320 Commits
1.7.0 ... 1.8.2

Author SHA1 Message Date
Redkale
4931c66868 2017-08-28 18:11:55 +08:00
Redkale
fcff1c3a4b 修复ByteBuffer的缓存区域会重复的BUG 2017-08-28 17:50:09 +08:00
Redkale
2005bf7e3b 2017-08-27 17:18:13 +08:00
Redkale
cb07a38f04 2017-08-27 14:37:06 +08:00
Redkale
6085cd5eef 2017-08-25 12:54:23 +08:00
Redkale
086275c135 2017-08-22 15:55:25 +08:00
Redkale
a449a96ef9 2017-08-22 15:10:17 +08:00
Redkale
bc3209a09c 2017-08-21 14:40:36 +08:00
Redkale
63d1ef985d 2017-08-18 14:48:21 +08:00
Redkale
24505564c8 2017-08-14 16:22:46 +08:00
Redkale
93a7bd63cf WebSocket 增加 onOccurException 方法 2017-08-12 07:56:42 +08:00
Redkale
0b9b5baa49 HTTP服务增加OPTIONS配置项功能 2017-08-10 16:19:58 +08:00
Redkale
5c4100e762 2017-08-10 15:05:46 +08:00
Redkale
eb861014c4 2017-08-10 10:24:37 +08:00
Redkale
e62f7ea63d 2017-08-06 12:25:56 +08:00
Redkale
d6df2055b2 2017-08-03 14:30:03 +08:00
Redkale
570aac947a 2017-08-03 12:16:14 +08:00
Redkale
1fdc33b565 2017-08-03 10:21:16 +08:00
Redkale
af8d0e978e 2017-07-27 16:52:00 +08:00
Redkale
c58022a81e Update README.md 2017-07-25 11:32:57 +08:00
Redkale
f4cf828993 Update README.md 2017-07-25 11:31:23 +08:00
Redkale
c4dc0de5fe Redkale 1.8.2 开始 2017-07-25 11:24:04 +08:00
Redkale
44507a97a6 兼容Oracle的复制表结构 2017-07-25 11:05:49 +08:00
Redkale
f4a7f1cff6 修复负载均衡无法正确切换节点的BUG 2017-07-24 18:29:40 +08:00
Redkale
a5fcb45a88 2017-07-24 17:34:07 +08:00
Redkale
bc8b68526d 2017-07-24 17:32:45 +08:00
Redkale
180f201dc0 修复多个<services>节点且其下指定service时无法获取所有service实例的BUG 2017-07-24 17:20:16 +08:00
Redkale
9ab315a405 2017-07-24 16:10:13 +08:00
Redkale
27b4742b6d 增加 TransportStrategy 功能 2017-07-21 16:07:13 +08:00
Redkale
702220d18e 2017-07-19 10:51:17 +08:00
Redkale
414489da8e 2017-07-15 22:24:10 +08:00
Redkale
77057df25d 2017-07-11 11:59:12 +08:00
Redkale
2f98cd1ab5 2017-07-11 11:29:57 +08:00
Redkale
8809fe8ec9 2017-07-11 10:09:49 +08:00
Redkale
f9702a9517 WebSocket时request.keepAlive设置为false 2017-07-11 10:07:42 +08:00
Redkale
29e46b9b68 暂时屏蔽304 2017-07-11 09:47:40 +08:00
Redkale
f838e35413 返回304的响应中增加Content-Length值 2017-07-11 09:30:27 +08:00
Redkale
f3bb77c49b 增加获取最后一次ping的时间点 2017-07-10 22:46:16 +08:00
Redkale
12fa033e15 增加判断用户是否在线和获取在线用户总数接口 2017-07-05 15:57:22 +08:00
Redkale
f4abfafea2 2017-07-05 15:42:54 +08:00
Redkale
0918af71d2 CacheSource增加getKeySize方法 2017-07-05 15:18:16 +08:00
Redkale
275befa330 private方法改成protected,方便重载 2017-07-05 10:28:55 +08:00
Redkale
ab4cd8bcb6 2017-07-02 17:30:08 +08:00
Redkale
36c109b32f 2017-07-02 17:29:22 +08:00
Redkale
73a915665d 2017-07-02 17:00:59 +08:00
Redkale
bd6d71c94a 修复@RestWebSocket对应的WebSocketServlet.resourceName没有重载的BUG 2017-07-01 16:31:00 +08:00
Redkale
842e93507c WebSocketEngine.broadcastMessage 增加 Predicate<WebSocket> 参数 2017-07-01 15:46:51 +08:00
Redkale
76df1108d7 WebSocketEngine.broadcastMessage 增加 Predicate<WebSocket> 参数 2017-07-01 15:38:47 +08:00
Redkale
941d09cde2 2017-07-01 15:29:26 +08:00
Redkale
9dd3e1da07 允许本地模式的Service通过@Resource可以获取Application、ResourceFactory等资源 2017-06-30 13:35:44 +08:00
Redkale
2bf73245ec Redkale 1.8.1 开始 2017-06-26 09:40:30 +08:00
Redkale
b0ab792f72 2017-06-24 22:45:19 +08:00
Redkale
0df9a940c5 2017-06-24 16:24:13 +08:00
Redkale
fc35fc5abc 2017-06-24 15:26:52 +08:00
Redkale
eaa0a99933 2017-06-24 12:33:48 +08:00
Redkale
83bdb97842 2017-06-24 12:31:20 +08:00
Redkale
a71a4d0fed 2017-06-24 10:47:41 +08:00
Redkale
835435c220 2017-06-24 10:38:41 +08:00
Redkale
c524ba1797 2017-06-24 10:32:00 +08:00
Redkale
f254b48693 2017-06-24 10:09:26 +08:00
Redkale
922697eb4d 2017-06-22 14:37:42 +08:00
Redkale
450e3e3ea2 2017-06-22 09:42:24 +08:00
Redkale
b31f75f4f6 2017-06-22 00:54:51 +08:00
Redkale
1d1f18b046 2017-06-21 23:05:40 +08:00
Redkale
73f942746b 2017-06-21 17:35:18 +08:00
Redkale
b6cefe8c2d 2017-06-21 17:34:08 +08:00
Redkale
ebc0e4eb41 2017-06-21 17:32:22 +08:00
Redkale
179a7b22ea 2017-06-21 16:27:18 +08:00
Redkale
0b87d9a261 2017-06-21 16:10:15 +08:00
Redkale
685a686ead 2017-06-20 21:58:20 +08:00
Redkale
568e1cf62d 2017-06-20 21:57:29 +08:00
Redkale
1d121bd2ab 2017-06-20 13:56:45 +08:00
Redkale
2ca4bdaaec 2017-06-19 13:46:32 +08:00
Redkale
878fda30f6 2017-06-19 13:45:16 +08:00
Redkale
abb611382c 2017-06-19 13:41:43 +08:00
Redkale
b53510a26f 2017-06-19 10:07:59 +08:00
Redkale
2aee84d477 2017-06-19 09:53:29 +08:00
Redkale
0ba2e25f2e 2017-06-19 08:20:52 +08:00
Redkale
98e8a7eb05 2017-06-18 22:31:13 +08:00
Redkale
62139efca9 2017-06-18 21:58:28 +08:00
Redkale
2b62cbe455 2017-06-18 21:51:20 +08:00
Redkale
e44602fe3b 2017-06-15 23:27:56 +08:00
Redkale
276cb4da92 2017-06-15 08:08:38 +08:00
Redkale
7081f94afc 2017-06-12 14:43:32 +08:00
Redkale
2d6cefeb43 2017-06-07 09:38:57 +08:00
Redkale
ea6c703ac6 2017-06-07 07:02:16 +08:00
Redkale
d1f14962fd 2017-06-07 06:59:51 +08:00
Redkale
79ca63bf81 2017-06-06 09:44:51 +08:00
Redkale
6421bc2851 2017-06-06 09:43:31 +08:00
Redkale
43f9f50f4c 2017-06-06 09:35:41 +08:00
Redkale
5f140a8ce9 2017-06-06 09:31:27 +08:00
Redkale
c0f8cdf902 2017-06-06 09:30:46 +08:00
Redkale
8d66b1b4a7 2017-06-06 08:35:57 +08:00
Redkale
8c7ee4136c 2017-06-06 08:20:47 +08:00
Redkale
d9498c9a6c 2017-06-05 16:09:06 +08:00
Redkale
08060a8c86 2017-06-05 09:18:05 +08:00
Redkale
2bdf0e4a50 2017-06-05 08:55:37 +08:00
Redkale
eb184df100 2017-06-04 11:30:07 +08:00
Redkale
cf545a731c 2017-06-04 08:03:03 +08:00
Redkale
95e18dfd48 2017-06-04 08:00:13 +08:00
Redkale
023a9abdef 2017-06-03 22:32:47 +08:00
Redkale
7f3776c224 2017-06-03 22:12:29 +08:00
Redkale
7b15ba33e0 2017-06-03 22:05:53 +08:00
Redkale
a6c105d63d 2017-06-03 18:41:08 +08:00
Redkale
10e22b0873 2017-06-03 16:50:40 +08:00
Redkale
53a35e6397 2017-06-03 10:20:36 +08:00
Redkale
b5a3c39f4f 2017-06-03 09:51:53 +08:00
Redkale
99381d4842 2017-06-02 15:24:53 +08:00
Redkale
5f2c2a9f2c 2017-06-02 14:00:14 +08:00
Redkale
dc3f318949 2017-06-02 09:46:58 +08:00
Redkale
cfae61faea 2017-06-01 20:06:46 +08:00
Redkale
dd7626b1a3 2017-06-01 15:21:28 +08:00
Redkale
2171aa1232 2017-06-01 13:23:35 +08:00
Redkale
801ad489d2 2017-06-01 11:38:49 +08:00
Redkale
c88d0b402d 2017-05-31 21:19:51 +08:00
Redkale
542bb4353b 2017-05-31 14:07:21 +08:00
Redkale
fe1e0a845a 2017-05-31 13:41:10 +08:00
Redkale
f320f4c550 2017-05-31 12:17:10 +08:00
Redkale
0ec2bf211a 2017-05-31 10:51:02 +08:00
Redkale
b3c54e4db5 2017-05-31 10:50:10 +08:00
Redkale
52838f04a9 2017-05-31 08:31:40 +08:00
Redkale
fa5bd95a2b 2017-05-30 17:12:51 +08:00
Redkale
b2e73d378c 2017-05-30 16:42:10 +08:00
Redkale
6895b31ad0 2017-05-30 16:11:29 +08:00
Redkale
56637ff7ef 2017-05-30 16:04:25 +08:00
Redkale
40b46f9c7f 2017-05-30 14:57:58 +08:00
Redkale
0aed26652d 2017-05-30 11:12:42 +08:00
Redkale
14238a0203 2017-05-30 07:22:45 +08:00
Redkale
369a70e857 2017-05-29 21:20:30 +08:00
Redkale
6ede9b0f31 2017-05-29 21:19:52 +08:00
Redkale
6c039dc8f4 2017-05-29 21:01:56 +08:00
Redkale
1e69a6755f 2017-05-29 19:02:43 +08:00
Redkale
f6c617574c 2017-05-29 17:22:12 +08:00
Redkale
2291beb5e7 2017-05-29 16:35:11 +08:00
Redkale
d8e091f888 2017-05-29 15:13:46 +08:00
Redkale
6a42ae7570 2017-05-29 15:05:39 +08:00
Redkale
756e4634d9 2017-05-29 15:03:27 +08:00
Redkale
06773ccdc0 2017-05-29 14:03:11 +08:00
Redkale
0da74be2fa 2017-05-29 09:17:54 +08:00
Redkale
1d1b732a74 2017-05-28 15:04:10 +08:00
Redkale
ab90c80785 2017-05-28 13:30:50 +08:00
Redkale
6cd232efd2 2017-05-28 10:19:56 +08:00
Redkale
96e4b8834d 2017-05-27 21:56:34 +08:00
Redkale
7ef5ddfd46 2017-05-27 21:41:24 +08:00
Redkale
73a973e0ed 2017-05-27 21:05:17 +08:00
Redkale
e0411a94f6 2017-05-27 20:44:47 +08:00
Redkale
054e853074 2017-05-27 19:34:35 +08:00
Redkale
88672b5522 2017-05-27 18:20:49 +08:00
Redkale
0a60b81a98 2017-05-27 18:08:14 +08:00
Redkale
37f8208b1b 2017-05-27 17:54:55 +08:00
Redkale
a8b9cc9753 2017-05-27 15:31:35 +08:00
Redkale
a99c7d3454 2017-05-27 15:07:24 +08:00
Redkale
b88987dd98 2017-05-27 14:59:52 +08:00
Redkale
54f4f8e35d 2017-05-27 13:18:32 +08:00
Redkale
97670261e6 2017-05-27 12:43:03 +08:00
Redkale
82a2a513f5 2017-05-27 12:31:58 +08:00
Redkale
174a8a2a0c 2017-05-27 11:54:28 +08:00
Redkale
92b3d0bbd4 2017-05-27 10:25:53 +08:00
Redkale
24c90b015a 2017-05-26 14:19:34 +08:00
Redkale
2742e935cb 2017-05-26 13:57:50 +08:00
Redkale
b7770c89b8 2017-05-26 13:52:36 +08:00
Redkale
779a9cca4d 2017-05-26 13:26:39 +08:00
Redkale
493c27beb5 2017-05-26 13:25:57 +08:00
Redkale
c68d988d51 2017-05-26 11:15:04 +08:00
Redkale
ceeb924d4d 2017-05-26 10:18:55 +08:00
Redkale
4d0a16d35d 2017-05-26 07:39:41 +08:00
Redkale
1e60adf5bb 2017-05-26 00:40:51 +08:00
Redkale
59b08684a8 2017-05-25 23:51:27 +08:00
Redkale
c326b7ed05 2017-05-25 23:06:27 +08:00
Redkale
d9bf5c8412 2017-05-25 17:54:14 +08:00
Redkale
39d4e6405f 2017-05-25 17:43:22 +08:00
Redkale
03bcea30df 2017-05-25 17:18:20 +08:00
Redkale
27b67cde0e 2017-05-25 16:50:46 +08:00
Redkale
31fca5630b 2017-05-25 16:49:01 +08:00
Redkale
3504d735c1 2017-05-25 16:01:11 +08:00
Redkale
74f554fe33 2017-05-25 15:57:18 +08:00
Redkale
b02d4e6731 2017-05-25 15:06:15 +08:00
Redkale
23fd72b116 2017-05-24 23:05:14 +08:00
Redkale
4cd1b10c35 2017-05-24 22:54:11 +08:00
Redkale
71c0763304 2017-05-24 17:57:23 +08:00
Redkale
0ea4ddb5eb 2017-05-24 17:50:49 +08:00
Redkale
9bc266ca61 2017-05-24 12:36:38 +08:00
Redkale
b202de4916 2017-05-24 10:53:51 +08:00
Redkale
7d26198e88 2017-05-24 09:29:14 +08:00
Redkale
d98e249fd7 2017-05-24 09:21:30 +08:00
Redkale
7a8b6cac9d 2017-05-23 23:23:08 +08:00
Redkale
822ac078b5 2017-05-23 23:19:16 +08:00
Redkale
5e3718af19 2017-05-23 23:16:37 +08:00
Redkale
713fdefb56 2017-05-23 23:14:30 +08:00
Redkale
804e0e27e7 2017-05-23 22:14:03 +08:00
Redkale
2653808f8f 2017-05-23 22:12:55 +08:00
Redkale
ba12df2cba 2017-05-23 20:12:32 +08:00
Redkale
d2bfa9ab56 2017-05-23 20:03:09 +08:00
Redkale
0fb108bd9b 2017-05-23 19:59:14 +08:00
Redkale
145527db38 2017-05-23 19:45:52 +08:00
Redkale
d7c50532cf 2017-05-23 18:59:56 +08:00
Redkale
6562ac0a2b 2017-05-23 14:25:24 +08:00
Redkale
6f4c9dca48 2017-05-23 13:15:13 +08:00
Redkale
f18ef4f94d 2017-05-23 12:19:42 +08:00
Redkale
4786e17243 2017-05-23 11:23:58 +08:00
Redkale
96beb9cef4 2017-05-23 11:04:54 +08:00
Redkale
a59bf92ee7 2017-05-23 09:32:52 +08:00
Redkale
cdc3dbf9ea 2017-05-22 22:21:17 +08:00
Redkale
a957a18e32 2017-05-22 22:18:03 +08:00
Redkale
95c53f99e0 2017-05-22 20:20:29 +08:00
Redkale
03ac849451 2017-05-22 20:01:52 +08:00
Redkale
d9946ceb64 2017-05-22 19:32:26 +08:00
Redkale
fda9c30dc4 2017-05-22 19:22:24 +08:00
Redkale
205162ce38 2017-05-22 17:13:16 +08:00
Redkale
f5379df63b 2017-05-22 16:47:53 +08:00
Redkale
859e56af4d 2017-05-22 16:46:59 +08:00
Redkale
a29cc94f32 2017-05-22 16:00:25 +08:00
Redkale
9e6840f5cb 2017-05-22 15:27:53 +08:00
Redkale
52d559ea4a 2017-05-22 14:41:03 +08:00
Redkale
95a2b752af 2017-05-22 13:19:12 +08:00
Redkale
33da94960c 2017-05-22 13:05:50 +08:00
Redkale
dee2002cf3 2017-05-22 12:43:09 +08:00
Redkale
d9a318bba8 2017-05-22 09:32:14 +08:00
Redkale
05925b4f78 2017-05-21 21:21:56 +08:00
Redkale
45fe7cb3e9 2017-05-21 21:00:21 +08:00
Redkale
58271c803b 2017-05-21 20:56:57 +08:00
Redkale
88942c61b5 2017-05-21 19:31:54 +08:00
Redkale
88c4824c4f 2017-05-21 19:29:56 +08:00
Redkale
62b0be802e 2017-05-21 19:26:27 +08:00
Redkale
cdec316312 2017-05-21 18:33:39 +08:00
Redkale
8dcb999444 2017-05-21 14:18:47 +08:00
Redkale
ee460b4196 2017-05-21 14:08:55 +08:00
Redkale
d049b3f9ea 2017-05-21 13:47:31 +08:00
Redkale
c244c4edab 2017-05-21 13:19:32 +08:00
Redkale
7c533ce8d3 2017-05-21 12:52:01 +08:00
Redkale
f68686114d 2017-05-21 12:29:58 +08:00
Redkale
0859dee201 2017-05-20 13:43:02 +08:00
Redkale
46ccd83acc 2017-05-20 13:40:58 +08:00
Redkale
d5e44787c0 2017-05-20 13:15:23 +08:00
Redkale
4029b09d81 2017-05-20 13:14:00 +08:00
Redkale
e939241a8c 临时 2017-05-20 13:01:43 +08:00
Redkale
cee2c47d9a 2017-05-19 13:22:51 +08:00
Redkale
ccceaa2607 2017-05-19 13:21:09 +08:00
Redkale
358a50ecc7 2017-05-19 12:53:28 +08:00
Redkale
6aabae849d 2017-05-19 12:34:00 +08:00
Redkale
60cbd9b37d 2017-05-19 11:43:46 +08:00
Redkale
df2ee8273d 2017-05-19 11:39:10 +08:00
Redkale
8bbee0aff8 2017-05-19 11:26:21 +08:00
Redkale
01ea7f07f5 2017-05-18 16:52:44 +08:00
Redkale
ba928b389b 2017-05-18 14:22:44 +08:00
Redkale
39ba0f86f6 2017-05-17 19:10:51 +08:00
Redkale
4d523d1ca4 2017-05-17 18:35:37 +08:00
Redkale
2f60fe795c 2017-05-17 18:24:04 +08:00
Redkale
c3560b0ef0 2017-05-17 18:11:44 +08:00
Redkale
804b4dc07d 2017-05-17 18:00:15 +08:00
Redkale
bb87774ba9 临时修改 2017-05-17 02:32:36 +08:00
Redkale
f76ab977d1 2017-05-17 02:17:03 +08:00
Redkale
8003252cf3 2017-05-16 21:11:18 +08:00
Redkale
95c0d0f5cf 2017-05-16 21:09:28 +08:00
Redkale
3dcc6ea28f 2017-05-16 20:14:35 +08:00
Redkale
85c708b075 2017-05-16 20:11:45 +08:00
Redkale
278c51e26b 2017-05-16 17:13:51 +08:00
Redkale
7c0e60d191 2017-05-14 20:41:29 +08:00
Redkale
d1d10f90b9 2017-05-14 20:04:40 +08:00
Redkale
3b601979f4 2017-05-14 19:42:08 +08:00
Redkale
69cc09e76d 2017-05-14 16:05:41 +08:00
Redkale
ef28e32e04 2017-05-14 15:52:15 +08:00
Redkale
9954eaf469 2017-05-14 13:29:45 +08:00
Redkale
5ce5f53ed8 2017-05-14 13:27:07 +08:00
Redkale
8e36a7b450 2017-05-14 13:07:02 +08:00
Redkale
f15754386b 2017-05-14 12:27:27 +08:00
Redkale
6cc90ac7fe 2017-05-14 11:49:53 +08:00
Redkale
3c7f10d657 2017-05-14 11:49:31 +08:00
Redkale
21c84865b9 2017-05-14 11:47:00 +08:00
Redkale
853e823a8d 2017-05-14 11:34:15 +08:00
Redkale
6d74355fc0 2017-05-13 22:37:54 +08:00
Redkale
987914e748 2017-05-13 22:35:16 +08:00
Redkale
6e66ee0c99 2017-05-13 21:15:53 +08:00
Redkale
4f7145319f 2017-05-13 18:54:39 +08:00
Redkale
ab2656cde6 2017-05-13 18:47:03 +08:00
Redkale
27468d9f0c 2017-05-13 18:44:32 +08:00
Redkale
bb8462af2a 2017-05-13 18:17:36 +08:00
Redkale
e1df150a37 2017-05-13 16:49:21 +08:00
Redkale
bee4f31323 2017-05-13 16:40:26 +08:00
Redkale
df3a1ef84d 2017-05-13 15:39:58 +08:00
Redkale
242b13fff0 2017-05-13 15:27:34 +08:00
Redkale
c3cc9de5b5 2017-05-13 14:33:44 +08:00
Redkale
72e16b88f7 2017-05-13 14:19:23 +08:00
Redkale
f1f0227dca 2017-05-13 14:18:46 +08:00
Redkale
63fe26f9cb 2017-05-13 14:16:51 +08:00
Redkale
02fcb7b089 2017-05-13 13:56:02 +08:00
Redkale
9db9db4f5e 2017-05-13 11:23:14 +08:00
Redkale
e5ce250304 2017-05-13 11:20:18 +08:00
Redkale
a5756c0b4d 2017-05-13 10:50:52 +08:00
Redkale
0be3d8a4fa 2017-05-13 09:38:28 +08:00
Redkale
017ab1ae84 2017-05-12 22:02:19 +08:00
Redkale
c622e2437d 2017-05-12 21:46:10 +08:00
Redkale
3a1f20d438 2017-05-12 21:38:30 +08:00
Redkale
2a9b64e53e 2017-05-12 21:34:46 +08:00
Redkale
225745a282 2017-05-12 21:30:18 +08:00
Redkale
83680c46e8 2017-05-12 19:56:09 +08:00
Redkale
7484b80fb2 2017-05-12 17:57:17 +08:00
Redkale
77674ac8d2 2017-05-12 17:51:27 +08:00
Redkale
564067602f 2017-05-12 14:58:52 +08:00
Redkale
26778c58c9 2017-05-12 14:21:47 +08:00
Redkale
77cd24fa42 2017-05-12 14:19:06 +08:00
Redkale
4feea0e784 重构HttpServlet 2017-05-12 13:42:10 +08:00
Redkale
681faa415f 2017-05-12 08:09:05 +08:00
Redkale
3b54484832 改造HttpBaseServlet 的preExecute 和 authenticate 方法 2017-05-11 14:56:48 +08:00
Redkale
fa2513d934 2017-05-11 13:58:33 +08:00
Redkale
5e2be5e926 2017-05-11 13:56:36 +08:00
Redkale
4c071b0a1d 2017-05-11 13:49:52 +08:00
Redkale
9e7949d9eb 2017-05-11 13:35:09 +08:00
Redkale
29843a9812 2017-05-11 13:34:33 +08:00
Redkale
48a08f27a7 2017-05-11 13:07:37 +08:00
Redkale
4f1a0849ec 2017-05-11 12:55:40 +08:00
Redkale
6676c3fd37 REST 增加 @RestBody 特性, 获取请求内容, 参数可以是String 或 byte[] 2017-05-10 23:35:47 +08:00
Redkale
6daa45ff05 2017-05-10 17:40:56 +08:00
Redkale
28f95a89df 2017-05-03 13:41:08 +08:00
Redkale
44d11dae34 2017-05-02 08:20:54 +08:00
Redkale
a1b39ba99b 2017-05-02 08:19:32 +08:00
Redkale
abcfb8f10c 2017-05-02 08:12:19 +08:00
Redkale
33f49c7632 2017-05-02 08:10:51 +08:00
Redkale
d800a33ded 2017-05-02 08:09:01 +08:00
Redkale
f587e13bdc 2017-05-02 08:08:25 +08:00
160 changed files with 8706 additions and 6406 deletions

View File

@@ -22,3 +22,7 @@
&nbsp;&nbsp;&nbsp;由于RedKale使用了JDK 8 内置的ASM包所以需要在源码工程中的编译器选项中加入 <b>-XDignore.symbol.file=true</b> &nbsp;&nbsp;&nbsp;由于RedKale使用了JDK 8 内置的ASM包所以需要在源码工程中的编译器选项中加入 <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> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<h5>详情请访问:&nbsp;&nbsp;&nbsp;&nbsp;<a href='https://redkale.org' target='_blank'>https://redkale.org</a></h5>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<h5>基本文档:&nbsp;&nbsp;&nbsp;&nbsp;<a href='https://redkale.org/articles.html' target='_blank'>https://redkale.org/articles.html</a></h5>
&nbsp;

View File

@@ -16,6 +16,10 @@
<directory>${project.basedir}/conf</directory> <directory>${project.basedir}/conf</directory>
<outputDirectory>conf</outputDirectory> <outputDirectory>conf</outputDirectory>
</fileSet> </fileSet>
<fileSet>
<directory>${project.basedir}/libs</directory>
<outputDirectory>libs</outputDirectory>
</fileSet>
<fileSet> <fileSet>
<directory>${project.basedir}/logs</directory> <directory>${project.basedir}/logs</directory>
<outputDirectory>logs</outputDirectory> <outputDirectory>logs</outputDirectory>

View File

@@ -4,7 +4,7 @@ export LC_ALL="zh_CN.UTF-8"
APP_HOME=`dirname "$0"` APP_HOME=`dirname "$0"`
if [ ! -a "$APP_HOME"/conf/application.xml ]; then if [ ! -f "$APP_HOME"/conf/application.xml ]; then
APP_HOME="$APP_HOME"/.. APP_HOME="$APP_HOME"/..
fi fi

View File

@@ -4,7 +4,7 @@ export LC_ALL="zh_CN.UTF-8"
APP_HOME=`dirname "$0"` APP_HOME=`dirname "$0"`
if [ ! -a "$APP_HOME"/conf/application.xml ]; then if [ ! -f "$APP_HOME"/conf/application.xml ]; then
APP_HOME="$APP_HOME"/.. APP_HOME="$APP_HOME"/..
fi fi

View File

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

@@ -8,7 +8,7 @@ export LC_ALL="zh_CN.UTF-8"
APP_HOME=`dirname "$0"` APP_HOME=`dirname "$0"`
if [ ! -a "$APP_HOME"/conf/application.xml ]; then if [ ! -f "$APP_HOME"/conf/application.xml ]; then
APP_HOME="$APP_HOME"/.. APP_HOME="$APP_HOME"/..
fi fi

View File

@@ -5,33 +5,29 @@
<!-- 详细配置说明见: http://redkale.org/redkale.html#redkale_confxml --> <!-- 详细配置说明见: http://redkale.org/redkale.html#redkale_confxml -->
<resources> <resources>
<!--
<properties>
<property name="system.property.convert.json.tiny" value="true"/>
</properties>
-->
</resources> </resources>
<server protocol="HTTP" host="0.0.0.0" port="6060" root="root"> <server protocol="HTTP" host="0.0.0.0" port="6060" root="root">
<!--
<request> <request>
<remoteaddr value="request.headers.X-RemoteAddress"/> <remoteaddr value="request.headers.X-RemoteAddress"/>
</request> </request>
<response> <response>
<addheader name="X-Node" value="system.property.APP_NODE" /> <defcookie domain="" path=""/>
<addheader name="Access-Control-Allow-Origin" value="request.headers.Origin" /> <addheader name="Access-Control-Allow-Origin" value="request.headers.Origin" />
<setheader name="Access-Control-Allow-Credentials" value="true"/> <setheader name="Access-Control-Allow-Credentials" value="true"/>
</response> </response>
-->
<services autoload="true"/> <services autoload="true"/>
<servlets path="/pipes" autoload="true" >
<!-- <filters autoload="true"/>
<resource-servlet>
<caches limit="0"/> <rest path="/pipes" /> <!-- base指定的自定义HttpServlet子类必须标记@HttpUserType, 不设置base则视为没有当前用户信息设置 -->
<rewrite type="location" match="^/([^-]+)(-[^-\.]+)+\.html(.*)" forward="/$1.html"/>
</resource-servlet> <servlets path="/pipes" autoload="true" />
-->
</servlets>
</server> </server>
</application> </application>

View File

@@ -3,7 +3,7 @@
handlers = java.util.logging.ConsoleHandler handlers = java.util.logging.ConsoleHandler
############################################################ ############################################################
.level = FINE .level = FINER
java.level = INFO java.level = INFO
javax.level = INFO javax.level = INFO
@@ -12,12 +12,13 @@ sun.level = INFO
jdk.level = INFO jdk.level = INFO
java.util.logging.FileHandler.level = FINE java.util.logging.FileHandler.level = FINER
#10M #10M
java.util.logging.FileHandler.limit = 10485760 java.util.logging.FileHandler.limit = 10485760
java.util.logging.FileHandler.count = 10000 java.util.logging.FileHandler.count = 10000
java.util.logging.FileHandler.encoding = UTF-8 java.util.logging.FileHandler.encoding = UTF-8
java.util.logging.FileHandler.pattern = ${APP_HOME}/logs-%m/log-%d.log java.util.logging.FileHandler.pattern = ${APP_HOME}/logs-%m/log-%d.log
java.util.logging.FileHandler.unusual = ${APP_HOME}/logs-%m/log-warnerr-%d.log
java.util.logging.FileHandler.append = true java.util.logging.FileHandler.append = true
java.util.logging.ConsoleHandler.level = FINE java.util.logging.ConsoleHandler.level = FINER

View File

@@ -16,9 +16,9 @@
<persistence-unit name="user.read" transaction-type="RESOURCE_LOCAL"> <persistence-unit name="user.read" transaction-type="RESOURCE_LOCAL">
<shared-cache-mode>ALL</shared-cache-mode> <shared-cache-mode>ALL</shared-cache-mode>
<properties> <properties>
<property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/user?autoReconnect=true&amp;characterEncoding=utf8"/> <property name="javax.persistence.jdbc.url" value="jdbc:oracle:thin:@localhost:1521:orcl"/>
<property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver"/> <property name="javax.persistence.jdbc.driver" value="oracle.jdbc.driver.OracleDriver"/>
<property name="javax.persistence.jdbc.user" value="root"/> <property name="javax.persistence.jdbc.user" value="system"/>
<property name="javax.persistence.jdbc.password" value="1234"/> <property name="javax.persistence.jdbc.password" value="1234"/>
</properties> </properties>
</persistence-unit> </persistence-unit>

View File

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

1
libs/readme.txt Normal file
View File

@@ -0,0 +1 @@
<EFBFBD>Լ<EFBFBD><EFBFBD><EFBFBD>ҵ<EFBFBD><EFBFBD>jarĬ<EFBFBD>Ϸ<EFBFBD><EFBFBD>ڴ˴<EFBFBD>

View File

@@ -30,16 +30,19 @@
所有服务所需的资源 所有服务所需的资源
--> -->
<resources> <resources>
<!-- <!--
【节点全局唯一】 【节点全局唯一】
transport节点只能有一个用于配置所有Transport的池参数没配置该节点将自动创建一个。 transport节点只能有一个用于配置所有Transport的池参数没配置该节点将自动创建一个。
threads 线程总数, 默认: <group>节点数*CPU核数*8 threads 线程总数, 默认: <group>节点数*CPU核数*8
bufferCapacity: ByteBuffer的初始化大小 默认: 8K; bufferCapacity: ByteBuffer的初始化大小 默认: 8K;
bufferPoolSize ByteBuffer池的大小默认: <group>节点数*CPU核数*8 bufferPoolSize ByteBuffer池的大小默认: <group>节点数*CPU核数*8
strategy: 远程请求的负载均衡策略, 必须是org.redkale.net.TransportStrategy的实现类
--> -->
<transport bufferCapacity="8K" bufferPoolSize="32" threads="32"/> <transport bufferCapacity="8K" bufferPoolSize="32" threads="32"/>
<!-- <!--
一个组包含多个NODE 同一Service服务可以由多个进程提供这些进程称为一个GROUP且同一GROUP内的进程必须在同一机房或局域网内 一个组包含多个node 同一Service服务可以由多个进程提供这些进程称为一个GROUP且同一GROUP内的进程必须在同一机房或局域网内
一个group节点对应一个 Transport 对象。 一个group节点对应一个 Transport 对象。
name: 服务组ID长度不能超过11个字节. 默认为空字符串。 注意: name不能包含$符号。 name: 服务组ID长度不能超过11个字节. 默认为空字符串。 注意: name不能包含$符号。
protocol值范围UDP TCP 默认TCP protocol值范围UDP TCP 默认TCP
@@ -57,6 +60,18 @@
--> -->
<node addr="127.0.0.1" port="7070"/> <node addr="127.0.0.1" port="7070"/>
</group> </group>
<!--
全局的数据源设置, 可以是CacheSource、DataSource JDBC的DataSource通常通过persistence.xml配置此处多用于CacheSource的配置
name: 资源名,用于依赖注入。
value类名必须是CacheSource或DataSource的子类且必须实现Service接口。
groups: 指定groups。
xxx: 其他属性与子节点通过Service.init方法传入的AnyValue获取。
-->
<source name="redis" value="org.redkalex.cache.RedisCacheSource" xxx="16">
<node addr="127.0.0.1" port="7070"/>
</source>
<!-- <!--
【节点全局唯一】 【节点全局唯一】
全局的参数配置, 可以通过@Resource(name="property.xxxxxx") 进行注入<property>的信息, 被注解的字段类型只能是String、primitive class 全局的参数配置, 可以通过@Resource(name="property.xxxxxx") 进行注入<property>的信息, 被注解的字段类型只能是String、primitive class
@@ -82,18 +97,18 @@
</resources> </resources>
<!-- <!--
protocol: required server所启动的协议Redkale内置的有HTTP、SNCPSNCP使用TCP实现; protocol: required server所启动的协议Redkale内置的有HTTP、SNCP、WATCH。协议均使用TCP实现; WATCH服务只能存在一个。
name: 服务的名称用于监控识别一个配置文件中的server.name不能重复,命名规则: 字母、数字、下划线、减号 name: 服务的名称用于监控识别一个配置文件中的server.name不能重复,命名规则: 字母、数字、下划线
host: 服务所占address 默认: 0.0.0.0 host: 服务所占address 默认: 0.0.0.0
port: required 服务所占端口 port: required 服务所占端口
root: 如果是web类型服务则包含页面 默认:{APP_HOME}/root root: 如果是web类型服务则包含页面 默认:{APP_HOME}/root
lib: server额外的class目录 默认为 lib: server额外的class目录 默认为${APP_HOME}/libs/*;
excludelibs: 排除lib.path与excludes中的正则表达式匹配的路径, 多个正则表达式用分号;隔开 excludelibs: 排除lib.path与excludes中的正则表达式匹配的路径, 多个正则表达式用分号;隔开
charset: 文本编码, 默认: UTF-8 charset: 文本编码, 默认: UTF-8
backlog: 默认10K backlog: 默认10K
threads 线程总数, 默认: CPU核数*16 threads 线程总数, 默认: CPU核数*16
maxbody: request.body最大值 默认: 64K maxbody: request.body最大值 默认: 64K
bufferCapacity: ByteBuffer的初始化大小 默认: 8K; 如果是HTTP协议则默认: 16K + 8B (兼容HTTP 2.0) bufferCapacity: ByteBuffer的初始化大小 默认: 8K; 如果是HTTP协议则默认: 16K + 16B (兼容HTTP 2.0、WebSocket)
bufferPoolSize ByteBuffer池的大小默认: CPU核数*512 bufferPoolSize ByteBuffer池的大小默认: CPU核数*512
responsePoolSize Response池的大小默认: CPU核数*256 responsePoolSize Response池的大小默认: CPU核数*256
readTimeoutSecond: 读操作超时秒数, 默认0 表示永久不超时 readTimeoutSecond: 读操作超时秒数, 默认0 表示永久不超时
@@ -105,8 +120,7 @@
<!-- <!--
加载所有的Service服务; 加载所有的Service服务;
在同一个进程中同一个name同一类型的Service将共用同一个实例 在同一个进程中同一个name同一类型的Service将共用同一个实例
autoload="true" 默认值. 自动加载以下目录(如果存在的话)下所有的Service类: autoload="true" 默认值. 自动加载classpath下所有的Service类
server.lib; server.lib/*; server.classes;
autoload="false" 需要显著的指定Service类 autoload="false" 需要显著的指定Service类
includes 当autoload="true" 拉取类名与includes中的正则表达式匹配的类, 多个正则表达式用分号;隔开 includes 当autoload="true" 拉取类名与includes中的正则表达式匹配的类, 多个正则表达式用分号;隔开
excludes 当autoload="true" 排除类名与excludes中的正则表达式匹配的类, 多个正则表达式用分号;隔开 excludes 当autoload="true" 排除类名与excludes中的正则表达式匹配的类, 多个正则表达式用分号;隔开
@@ -121,6 +135,7 @@
<!-- <!--
name: 显式指定name覆盖默认的空字符串值。 注意: name不能包含$符号。 name: 显式指定name覆盖默认的空字符串值。 注意: name不能包含$符号。
groups: 显式指定groups覆盖<services>节点的groups默认值。 groups: 显式指定groups覆盖<services>节点的groups默认值。
ignore: 是否禁用, 默认为false。
--> -->
<service value="com.xxx.XXX2Service" name="" groups="xxx;yyy"/> <service value="com.xxx.XXX2Service" name="" groups="xxx;yyy"/>
<!-- 给Service增加配置属性 --> <!-- 给Service增加配置属性 -->
@@ -131,21 +146,51 @@
</service> </service>
</services> </services>
<!--
加载所有的Filter服务;
autoload="true" 默认值.
autoload="false" 需要显著的指定Filter类
includes 当autoload="true" 拉取类名与includes中的正则表达式匹配的类, 多个正则表达式用分号;隔开
excludes 当autoload="true" 排除类名与excludes中的正则表达式匹配的类, 多个正则表达式用分号;隔开
-->
<filters autoload="true" includes="" excludes="">
<!--
显著加载指定的Filter类
value=: Filter类名。必须与Server的协议层相同HTTP必须是HttpFilter
ignore: 是否禁用, 默认为false。
-->
<!-- 显著加载指定的Filter类 -->
<filter value="com.xxx.XXX1Filter"/>
<!-- 给Filter增加配置属性 -->
<filter value="com.xxx.XXX12Filter">
<!-- property节点值在 public void init(AnyValue conf) 方法中可以通过 AnyValue properties = conf.getAnyValue("properties");获取 -->
<property name="xxxxxx" value="XXXXXXXX"/>
<property name="xxxxxx" value="XXXXXXXX"/>
</filter>
</filters>
<!-- <!--
REST的核心配置项 REST的核心配置项
当Server为HTTP协议时, rest节点才有效。存在[rest]节点则Server启动时会加载REST服务, 节点可以多个 当Server为HTTP协议时, rest节点才有效。存在[rest]节点则Server启动时会加载REST服务, 节点可以多个,(WATCH协议不需要设置系统会自动生成)
base: REST服务的BaseServlet必须是 org.redkale.net.http.RestHttpServlet 的子类,该属性值默认值为 org.redkale.net.http.DefaultRestServlet。 path: servlet的ContextPath前缀 默认为空
base: REST服务的BaseServlet必须是 org.redkale.net.http.HttpServlet 的子类,且子类必须标记@HttpUserType。
autoload默认值"true" 默认值. 加载当前server所能使用的Servce对象; autoload默认值"true" 默认值. 加载当前server所能使用的Servce对象;
mustsign默认值"true" 是否只加载标记为RestService的Service类默认只加载标记RestService且ignore=false的Service
includes当autoload="true" 拉取类名与includes中的正则表达式匹配的类, 多个正则表达式用分号;隔开 includes当autoload="true" 拉取类名与includes中的正则表达式匹配的类, 多个正则表达式用分号;隔开
excludes当autoload="true" 排除类名与excludes中的正则表达式匹配的类, 多个正则表达式用分号;隔开 excludes当autoload="true" 排除类名与excludes中的正则表达式匹配的类, 多个正则表达式用分号;隔开
--> -->
<rest base="org.redkale.net.http.DefaultRestServlet" mustsign="true" autoload="true" includes="" excludes=""> <rest path="/pipes" base="org.redkale.net.http.HttpServlet" autoload="true" includes="" excludes="">
<!-- <!--
value: Service类名列出的表示必须被加载的Service对象 value: Service类名列出的表示必须被加载的Service对象
ignore: 是否忽略设置为true则不会加载该Service对象默认值为false ignore: 是否忽略设置为true则不会加载该Service对象默认值为false
--> -->
<service value="com.xxx.XXXXService"/> <service value="com.xxx.XXXXService"/>
<!--
value: WebSocket类名列出的表示必须被加载且标记为@RestWebSocket的WebSocket对象
ignore: 是否忽略设置为true则不会加载该RestWebSocket对象默认值为false
-->
<websocket value="com.xxx.XXXXRestWebSocket"/>
</rest> </rest>
<!-- <!--
@@ -166,11 +211,14 @@
如果addheader、setheader 的value值以request.parameters.开头则表示从request.parameters中获取对应的parameter值 如果addheader、setheader 的value值以request.parameters.开头则表示从request.parameters中获取对应的parameter值
如果addheader、setheader 的value值以request.headers.开头则表示从request.headers中获取对应的header值 如果addheader、setheader 的value值以request.headers.开头则表示从request.headers中获取对应的header值
例如下面例子是在Response输出header时添加两个header一个addHeader 一个setHeader 例如下面例子是在Response输出header时添加两个header一个addHeader 一个setHeader
options 节点: 设置了该节点却auto=true当request的method=OPTIONS自动设置addheader、setheader并返回200状态码
--> -->
<response> <response>
<defcookie domain="" path=""/> <defcookie domain="" path=""/>
<addheader name="Access-Control-Allow-Origin" value="request.headers.Origin" /> <addheader name="Access-Control-Allow-Origin" value="request.headers.Origin" />
<setheader name="Access-Control-Allow-Headers" value="request.headers.Access-Control-Request-Headers"/>
<setheader name="Access-Control-Allow-Credentials" value="true"/> <setheader name="Access-Control-Allow-Credentials" value="true"/>
<options auto="true" />
</response> </response>
<!-- <!--
@@ -201,14 +249,17 @@
<!-- <!--
加载所有的Servlet服务; 加载所有的Servlet服务;
path: servlet的ContextPath前缀 默认为空 path: servlet的ContextPath前缀 默认为空
autoload="true" 默认值. 自动加载以下目录(如果存在的话)下所有的Servlet类: autoload="true" 默认值. 自动加载classpath下所有的Servlet类
${APP_HOME}/lib; ${APP_HOME}/root/lib/*; ${APP_HOME}/root/classes;
autoload="false" 需要显著的指定Service类 autoload="false" 需要显著的指定Service类
includes 当autoload="true" 拉取类名与includes中的正则表达式匹配的类, 多个正则表达式用分号;隔开 includes 当autoload="true" 拉取类名与includes中的正则表达式匹配的类, 多个正则表达式用分号;隔开
excludes 当autoload="true" 排除类名与excludes中的正则表达式匹配的类, 多个正则表达式用分号;隔开 excludes 当autoload="true" 排除类名与excludes中的正则表达式匹配的类, 多个正则表达式用分号;隔开
--> -->
<servlets path="/pipes" autoload="true" includes="" excludes=""> <servlets path="/pipes" autoload="true" includes="" excludes="">
<!-- 显著加载指定的Servlet --> <!--
显著加载指定的Servlet类
value=: Servlet类名。必须与Server的协议层相同HTTP必须是HttpServlet
ignore: 是否禁用, 默认为false。
-->
<servlet value="com.xxx.XXX1Servlet" /> <servlet value="com.xxx.XXX1Servlet" />
<servlet value="com.xxx.XXX2Servlet" /> <servlet value="com.xxx.XXX2Servlet" />
<servlet value="com.xxx.XXX3Servlet" > <servlet value="com.xxx.XXX3Servlet" >

View File

@@ -15,9 +15,9 @@ com.sun.level = INFO
java.util.logging.FileHandler.limit = 10485760 java.util.logging.FileHandler.limit = 10485760
java.util.logging.FileHandler.count = 100 java.util.logging.FileHandler.count = 100
java.util.logging.FileHandler.encoding = UTF-8 java.util.logging.FileHandler.encoding = UTF-8
java.util.logging.FileHandler.pattern = ${APP_HOME}/logs-%m/log-%u.log 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 \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-error-%u.log java.util.logging.FileHandler.unusual = ${APP_HOME}/logs-%m/log-warnerr-%d.log
java.util.logging.FileHandler.append = true java.util.logging.FileHandler.append = true
#java.util.logging.ConsoleHandler.level = FINE #java.util.logging.ConsoleHandler.level = FINE

View File

@@ -3,13 +3,15 @@
<persistence> <persistence>
<!-- 系统基本库 --> <!-- 系统基本库 -->
<persistence-unit name="demouser"> <persistence-unit name="demouser">
<!-- 为NONE表示不启动缓存@Cacheable 失效; 非NONE值(通常用ALL)表示开启缓存。 -->
<shared-cache-mode>NONE</shared-cache-mode>
<properties> <properties>
<!-- <!--
DataSource的实现类没有设置默认为org.redkale.source.DataJdbcSource的实现使用常规基于JDBC的数据库驱动一般无需设置 DataSource的实现类没有设置默认为org.redkale.source.DataJdbcSource的实现使用常规基于JDBC的数据库驱动一般无需设置
--> -->
<property name="javax.persistence.datasource" value="org.redkale.source.DataJdbcSource"/> <property name="javax.persistence.datasource" value="org.redkale.source.DataJdbcSource"/>
<!--
是否开启缓存(标记为@Cacheable的Entity类),值目前只支持两种: ALL: 所有开启缓存。 NONE: 关闭所有缓存
-->
<property name="javax.persistence.cachemode" value="ALL"/>
<property name="javax.persistence.jdbc.url" value="jdbc:mysql://127.0.0.1:3306/dbuser?characterEncoding=utf8"/> <property name="javax.persistence.jdbc.url" value="jdbc:mysql://127.0.0.1:3306/dbuser?characterEncoding=utf8"/>
<!-- <!--
@@ -42,7 +44,6 @@
</persistence-unit> </persistence-unit>
<!-- IM消息库 --> <!-- IM消息库 -->
<persistence-unit name="demoim"> <persistence-unit name="demoim">
<shared-cache-mode>NONE</shared-cache-mode>
<properties> <properties>
<!-- jdbc:mysql://127.0.0.1:3306/dbim?autoReconnect=true&amp;autoReconnectForPools=true&amp;characterEncoding=utf8 --> <!-- jdbc:mysql://127.0.0.1:3306/dbim?autoReconnect=true&amp;autoReconnectForPools=true&amp;characterEncoding=utf8 -->
<property name="javax.persistence.jdbc.url" value="jdbc:mysql://127.0.0.1:3306/dbim?characterEncoding=utf8"/> <property name="javax.persistence.jdbc.url" value="jdbc:mysql://127.0.0.1:3306/dbim?characterEncoding=utf8"/>

View File

@@ -23,17 +23,18 @@ import org.redkale.util.*;
* *
* @author zhangjx * @author zhangjx
*/ */
public class ApiDocs extends HttpBaseServlet { public final class ApiDocsService {
private final Application app; //Application全局对象 private final Application app; //Application全局对象
public ApiDocs(Application app) { public ApiDocsService(Application app) {
this.app = app; this.app = app;
} }
public void run() throws Exception { public void run() throws Exception {
List<Map> serverList = new ArrayList<>(); List<Map> serverList = new ArrayList<>();
Field __prefix = HttpServlet.class.getDeclaredField("_prefix");
__prefix.setAccessible(true);
Map<String, Map<String, Map<String, Object>>> typesmap = new LinkedHashMap<>(); Map<String, Map<String, Map<String, Object>>> typesmap = new LinkedHashMap<>();
for (NodeServer node : app.servers) { for (NodeServer node : app.servers) {
if (!(node instanceof NodeHttpServer)) continue; if (!(node instanceof NodeHttpServer)) continue;
@@ -51,7 +52,7 @@ public class ApiDocs extends HttpBaseServlet {
continue; continue;
} }
final Map<String, Object> servletmap = new LinkedHashMap<>(); final Map<String, Object> servletmap = new LinkedHashMap<>();
String prefix = _prefix(servlet); String prefix = (String) __prefix.get(servlet);
String[] urlregs = ws.value(); String[] urlregs = ws.value();
if (prefix != null && !prefix.isEmpty()) { if (prefix != null && !prefix.isEmpty()) {
for (int i = 0; i < urlregs.length; i++) { for (int i = 0; i < urlregs.length; i++) {
@@ -72,14 +73,14 @@ public class ApiDocs extends HttpBaseServlet {
if (Modifier.isAbstract(clz.getModifiers())) break; if (Modifier.isAbstract(clz.getModifiers())) break;
for (Method method : clz.getMethods()) { for (Method method : clz.getMethods()) {
if (method.getParameterCount() != 2) continue; if (method.getParameterCount() != 2) continue;
WebMapping action = method.getAnnotation(WebMapping.class); HttpMapping action = method.getAnnotation(HttpMapping.class);
if (action == null) continue; if (action == null) continue;
if (!action.inherited() && selfClz != clz) continue; //忽略不被继承的方法 if (!action.inherited() && selfClz != clz) continue; //忽略不被继承的方法
final Map<String, Object> mappingmap = new LinkedHashMap<>(); final Map<String, Object> mappingmap = new LinkedHashMap<>();
if (actionurls.contains(action.url())) continue; if (actionurls.contains(action.url())) continue;
mappingmap.put("url", prefix + action.url()); mappingmap.put("url", prefix + action.url());
actionurls.add(action.url()); actionurls.add(action.url());
mappingmap.put("auth", method.getAnnotation(AuthIgnore.class) == null); mappingmap.put("auth", action.auth());
mappingmap.put("actionid", action.actionid()); mappingmap.put("actionid", action.actionid());
mappingmap.put("comment", action.comment()); mappingmap.put("comment", action.comment());
List<Map> paramsList = new ArrayList<>(); List<Map> paramsList = new ArrayList<>();
@@ -112,7 +113,7 @@ public class ApiDocs extends HttpBaseServlet {
} }
fieldmap.put("primary", !filter && (field.getAnnotation(Id.class) != null)); fieldmap.put("primary", !filter && (field.getAnnotation(Id.class) != null));
fieldmap.put("updatable", (filter || col == null || col.updatable())); fieldmap.put("updatable", (filter || col == null || col.updatable()));
if (servlet.getClass().getAnnotation(Rest.RestDynamic.class) != null) { if (servlet.getClass().getAnnotation(Rest.RestDyn.class) != null) {
if (field.getAnnotation(RestAddress.class) != null) continue; if (field.getAnnotation(RestAddress.class) != null) continue;
} }
@@ -122,7 +123,7 @@ public class ApiDocs extends HttpBaseServlet {
typesmap.put(rtype.getName(), typemap); typesmap.put(rtype.getName(), typemap);
} }
mappingmap.put("results", results); mappingmap.put("results", results);
for (WebParam param : method.getAnnotationsByType(WebParam.class)) { for (HttpParam param : method.getAnnotationsByType(HttpParam.class)) {
final Map<String, Object> parammap = new LinkedHashMap<>(); final Map<String, Object> parammap = new LinkedHashMap<>();
final boolean isarray = param.type().isArray(); final boolean isarray = param.type().isArray();
final Class ptype = isarray ? param.type().getComponentType() : param.type(); final Class ptype = isarray ? param.type().getComponentType() : param.type();
@@ -161,7 +162,7 @@ public class ApiDocs extends HttpBaseServlet {
fieldmap.put("primary", !filter && (field.getAnnotation(Id.class) != null)); fieldmap.put("primary", !filter && (field.getAnnotation(Id.class) != null));
fieldmap.put("updatable", (filter || col == null || col.updatable())); fieldmap.put("updatable", (filter || col == null || col.updatable()));
if (servlet.getClass().getAnnotation(Rest.RestDynamic.class) != null) { if (servlet.getClass().getAnnotation(Rest.RestDyn.class) != null) {
if (field.getAnnotation(RestAddress.class) != null) continue; if (field.getAnnotation(RestAddress.class) != null) continue;
} }
@@ -196,7 +197,7 @@ public class ApiDocs extends HttpBaseServlet {
if (doctemplate.isFile() && doctemplate.canRead()) { if (doctemplate.isFile() && doctemplate.canRead()) {
in = new FileInputStream(doctemplate); in = new FileInputStream(doctemplate);
} }
if (in == null) in = ApiDocs.class.getResourceAsStream("apidoc-template.html"); if (in == null) in = ApiDocsService.class.getResourceAsStream("apidoc-template.html");
String content = Utility.read(in).replace("'${content}'", json); String content = Utility.read(in).replace("'${content}'", json);
in.close(); in.close();
FileOutputStream outhtml = new FileOutputStream(new File(app.getHome(), "apidoc.html")); FileOutputStream outhtml = new FileOutputStream(new File(app.getHome(), "apidoc.html"));
@@ -204,8 +205,4 @@ public class ApiDocs extends HttpBaseServlet {
outhtml.close(); outhtml.close();
} }
@Override
public void authenticate(int moduleid, int actionid, HttpRequest request, HttpResponse response, HttpServlet next) throws IOException {
next.execute(request, response);
}
} }

View File

@@ -5,8 +5,10 @@
*/ */
package org.redkale.boot; package org.redkale.boot;
import org.redkale.util.RedkaleClassLoader;
import org.redkale.net.TransportGroupInfo;
import java.io.*; import java.io.*;
import java.lang.reflect.Modifier; import java.lang.reflect.*;
import java.net.*; import java.net.*;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.nio.channels.*; import java.nio.channels.*;
@@ -15,18 +17,20 @@ import java.util.*;
import java.util.concurrent.*; import java.util.concurrent.*;
import java.util.concurrent.atomic.*; import java.util.concurrent.atomic.*;
import java.util.logging.*; import java.util.logging.*;
import javax.annotation.Resource;
import javax.xml.parsers.*; import javax.xml.parsers.*;
import org.redkale.boot.ClassFilter.FilterEntry; import org.redkale.boot.ClassFilter.FilterEntry;
import org.redkale.convert.Convert;
import org.redkale.convert.bson.BsonFactory; import org.redkale.convert.bson.BsonFactory;
import org.redkale.convert.json.JsonFactory; import org.redkale.convert.json.JsonFactory;
import org.redkale.net.*; import org.redkale.net.*;
import org.redkale.net.http.MimeType; import org.redkale.net.http.MimeType;
import org.redkale.net.sncp.SncpClient; import org.redkale.net.sncp.*;
import org.redkale.service.Service; import org.redkale.service.Service;
import org.redkale.source.*; import org.redkale.source.*;
import org.redkale.util.AnyValue.DefaultAnyValue; import org.redkale.util.AnyValue.DefaultAnyValue;
import org.redkale.util.*; import org.redkale.util.*;
import org.redkale.watch.WatchFactory; import org.redkale.watch.*;
import org.w3c.dom.*; import org.w3c.dom.*;
/** /**
@@ -35,8 +39,8 @@ import org.w3c.dom.*;
* <pre> * <pre>
* 程序启动执行步骤: * 程序启动执行步骤:
* 1、读取application.xml * 1、读取application.xml
* 2、进行classpath扫描动态加载Service与Servlet * 2、进行classpath扫描动态加载Service、WebSocket与Servlet
* 3、优先加载所有SNCP协议的服务再加载其他协议服务 * 3、优先加载所有SNCP协议的服务再加载其他协议服务 最后加载WATCH协议的服务
* 4、最后进行Service、Servlet与其他资源之间的依赖注入 * 4、最后进行Service、Servlet与其他资源之间的依赖注入
* </pre> * </pre>
* <p> * <p>
@@ -74,26 +78,20 @@ public final class Application {
public static final String RESNAME_APP_ADDR = "APP_ADDR"; public static final String RESNAME_APP_ADDR = "APP_ADDR";
/** /**
* 当前Service的IP地址+端口 类型: SocketAddress、InetSocketAddress、String * 当前Service所属的SNCP Server的地址 类型: SocketAddress、InetSocketAddress、String <br>
*/ */
public static final String RESNAME_SERVER_ADDR = "SERVER_ADDR"; public static final String RESNAME_SNCP_ADDR = "SNCP_ADDR";
/** /**
* 当前SNCP Server所属的组 类型: String * 当前Service所属的SNCP Server所属的组 类型: String<br>
*/ */
public static final String RESNAME_SERVER_GROUP = "SERVER_GROUP"; public static final String RESNAME_SNCP_GROUP = "SNCP_GROUP";
/** /**
* 当前Server的ROOT目录 类型String、File、Path * "SERVER_ROOT" 当前Server的ROOT目录类型String、File、Path
*/ */
public static final String RESNAME_SERVER_ROOT = Server.RESNAME_SERVER_ROOT; public static final String RESNAME_SERVER_ROOT = Server.RESNAME_SERVER_ROOT;
//每个地址对应的Group名
final Map<InetSocketAddress, String> globalNodes = new HashMap<>();
//协议地址的Group集合
final Map<String, GroupInfo> globalGroups = new HashMap<>();
//本地IP地址 //本地IP地址
final InetAddress localAddress; final InetAddress localAddress;
@@ -106,14 +104,8 @@ public final class Application {
//NodeServer 资源 //NodeServer 资源
final List<NodeServer> servers = new CopyOnWriteArrayList<>(); final List<NodeServer> servers = new CopyOnWriteArrayList<>();
//传输端的ByteBuffer对象池 //传输端的TransportFactory
final ObjectPool<ByteBuffer> transportBufferPool; final TransportFactory transportFactory;
//传输端的线程池
final ExecutorService transportExecutor;
//传输端的ChannelGroup
final AsynchronousChannelGroup transportChannelGroup;
//全局根ResourceFactory //全局根ResourceFactory
final ResourceFactory resourceFactory = ResourceFactory.root(); final ResourceFactory resourceFactory = ResourceFactory.root();
@@ -124,13 +116,15 @@ public final class Application {
//临时计数器 //临时计数器
CountDownLatch servicecdl; //会出现两次赋值 CountDownLatch servicecdl; //会出现两次赋值
//是否启动了WATCH协议服务
boolean watching;
//-------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------
//是否用于main方法运行 //是否用于main方法运行
private final boolean singletonrun; private final boolean singletonrun;
//根WatchFactory //根WatchFactory
private final WatchFactory watchFactory = WatchFactory.root(); //private final WatchFactory watchFactory = WatchFactory.root();
//进程根目录 //进程根目录
private final File home; private final File home;
@@ -143,6 +137,12 @@ public final class Application {
//Server启动的计数器用于确保所有Server都启动完后再进行下一步处理 //Server启动的计数器用于确保所有Server都启动完后再进行下一步处理
private final CountDownLatch serversLatch; private final CountDownLatch serversLatch;
//根ClassLoader
private final RedkaleClassLoader classLoader;
//Server根ClassLoader
private final RedkaleClassLoader serverClassLoader;
private Application(final AnyValue config) { private Application(final AnyValue config) {
this(false, config); this(false, config);
} }
@@ -232,23 +232,25 @@ public final class Application {
} }
this.logger = Logger.getLogger(this.getClass().getSimpleName()); this.logger = Logger.getLogger(this.getClass().getSimpleName());
this.serversLatch = new CountDownLatch(config.getAnyValues("server").length + 1); this.serversLatch = new CountDownLatch(config.getAnyValues("server").length + 1);
logger.log(Level.INFO, "------------------------------- Redkale -------------------------------"); this.classLoader = new RedkaleClassLoader(Thread.currentThread().getContextClassLoader());
logger.log(Level.INFO, "------------------------------- Redkale " + Redkale.getDotedVersion() + " -------------------------------");
//------------------配置 <transport> 节点 ------------------ //------------------配置 <transport> 节点 ------------------
ObjectPool<ByteBuffer> transportPool = null; ObjectPool<ByteBuffer> transportPool = null;
ExecutorService transportExec = null; ExecutorService transportExec = null;
AsynchronousChannelGroup transportGroup = null; AsynchronousChannelGroup transportGroup = null;
final AnyValue resources = config.getAnyValue("resources"); final AnyValue resources = config.getAnyValue("resources");
TransportStrategy strategy = null;
if (resources != null) { if (resources != null) {
AnyValue transportConf = resources.getAnyValue("transport"); AnyValue transportConf = resources.getAnyValue("transport");
int groupsize = resources.getAnyValues("group").length; int groupsize = resources.getAnyValues("group").length;
if (groupsize > 0 && transportConf == null) transportConf = new DefaultAnyValue(); if (groupsize > 0 && transportConf == null) transportConf = new DefaultAnyValue();
if (transportConf != null) { if (transportConf != null) {
//--------------transportBufferPool----------- //--------------transportBufferPool-----------
AtomicLong createBufferCounter = watchFactory == null ? new AtomicLong() : watchFactory.createWatchNumber(Transport.class.getSimpleName() + ".Buffer.creatCounter"); AtomicLong createBufferCounter = new AtomicLong();
AtomicLong cycleBufferCounter = watchFactory == null ? new AtomicLong() : watchFactory.createWatchNumber(Transport.class.getSimpleName() + ".Buffer.cycleCounter"); AtomicLong cycleBufferCounter = new AtomicLong();
final int bufferCapacity = transportConf.getIntValue("bufferCapacity", 8 * 1024); final int bufferCapacity = Math.max(parseLenth(transportConf.getValue("bufferCapacity"), 8 * 1024), 4 * 1024);
final int bufferPoolSize = transportConf.getIntValue("bufferPoolSize", groupsize * Runtime.getRuntime().availableProcessors() * 8); final int bufferPoolSize = parseLenth(transportConf.getValue("bufferPoolSize"), groupsize * Runtime.getRuntime().availableProcessors() * 8);
final int threads = transportConf.getIntValue("threads", groupsize * Runtime.getRuntime().availableProcessors() * 8); final int threads = parseLenth(transportConf.getValue("threads"), groupsize * Runtime.getRuntime().availableProcessors() * 8);
transportPool = new ObjectPool<>(createBufferCounter, cycleBufferCounter, bufferPoolSize, transportPool = new ObjectPool<>(createBufferCounter, cycleBufferCounter, bufferPoolSize,
(Object... params) -> ByteBuffer.allocateDirect(bufferCapacity), null, (e) -> { (Object... params) -> ByteBuffer.allocateDirect(bufferCapacity), null, (e) -> {
if (e == null || e.isReadOnly() || e.capacity() != bufferCapacity) return false; if (e == null || e.isReadOnly() || e.capacity() != bufferCapacity) return false;
@@ -257,6 +259,10 @@ public final class Application {
}); });
//-----------transportChannelGroup-------------- //-----------transportChannelGroup--------------
try { try {
final String strategyClass = transportConf.getValue("strategy");
if (strategyClass != null && !strategyClass.isEmpty()) {
strategy = (TransportStrategy) classLoader.loadClass(strategyClass).newInstance();
}
final AtomicInteger counter = new AtomicInteger(); final AtomicInteger counter = new AtomicInteger();
transportExec = Executors.newFixedThreadPool(threads, (Runnable r) -> { transportExec = Executors.newFixedThreadPool(threads, (Runnable r) -> {
Thread t = new Thread(r); Thread t = new Thread(r);
@@ -271,17 +277,29 @@ public final class Application {
logger.log(Level.INFO, Transport.class.getSimpleName() + " configure bufferCapacity = " + bufferCapacity + "; bufferPoolSize = " + bufferPoolSize + "; threads = " + threads + ";"); logger.log(Level.INFO, Transport.class.getSimpleName() + " configure bufferCapacity = " + bufferCapacity + "; bufferPoolSize = " + bufferPoolSize + "; threads = " + threads + ";");
} }
} }
this.transportBufferPool = transportPool; this.transportFactory = new TransportFactory(transportExec, transportPool, transportGroup, strategy);
this.transportExecutor = transportExec; Thread.currentThread().setContextClassLoader(this.classLoader);
this.transportChannelGroup = transportGroup; this.serverClassLoader = new RedkaleClassLoader(this.classLoader);
} }
public ResourceFactory getResourceFactory() { public ResourceFactory getResourceFactory() {
return resourceFactory; return resourceFactory;
} }
public WatchFactory getWatchFactory() { public TransportFactory getTransportFactory() {
return watchFactory; return transportFactory;
}
public RedkaleClassLoader getClassLoader() {
return classLoader;
}
public RedkaleClassLoader getServerClassLoader() {
return serverClassLoader;
}
public List<NodeServer> getNodeServers() {
return new ArrayList<>(servers);
} }
public File getHome() { public File getHome() {
@@ -292,8 +310,8 @@ public final class Application {
return startTime; return startTime;
} }
private void initLogging() { public AnyValue getAppConfig() {
return config;
} }
public void init() throws Exception { public void init() throws Exception {
@@ -311,8 +329,8 @@ public final class Application {
logger.log(Level.INFO, RESNAME_APP_HOME + "= " + homepath + "\r\n" + RESNAME_APP_ADDR + "= " + this.localAddress.getHostAddress()); logger.log(Level.INFO, RESNAME_APP_HOME + "= " + homepath + "\r\n" + RESNAME_APP_ADDR + "= " + this.localAddress.getHostAddress());
String lib = config.getValue("lib", "").trim().replace("${APP_HOME}", homepath); String lib = config.getValue("lib", "").trim().replace("${APP_HOME}", homepath);
lib = lib.isEmpty() ? (homepath + "/conf") : (lib + ";" + homepath + "/conf"); lib = lib.isEmpty() ? (homepath + "/conf") : (lib + ";" + homepath + "/conf");
Server.loadLib(logger, lib); Server.loadLib(classLoader, logger, lib);
initLogging();
//------------------------------------------------------------------------ //------------------------------------------------------------------------
final AnyValue resources = config.getAnyValue("resources"); final AnyValue resources = config.getAnyValue("resources");
if (resources != null) { if (resources != null) {
@@ -355,6 +373,71 @@ public final class Application {
this.resourceFactory.register(JsonFactory.root()); this.resourceFactory.register(JsonFactory.root());
this.resourceFactory.register(BsonFactory.root().getConvert()); this.resourceFactory.register(BsonFactory.root().getConvert());
this.resourceFactory.register(JsonFactory.root().getConvert()); this.resourceFactory.register(JsonFactory.root().getConvert());
this.resourceFactory.register("bsonconvert", Convert.class, BsonFactory.root().getConvert());
this.resourceFactory.register("jsonconvert", Convert.class, JsonFactory.root().getConvert());
//只有WatchService才能加载Application、WatchFactory
final Application application = this;
this.resourceFactory.register(new ResourceFactory.ResourceLoader() {
@Override
public void load(ResourceFactory rf, final Object src, String resourceName, Field field, final Object attachment) {
try {
Resource res = field.getAnnotation(Resource.class);
if (res == null) return;
if (Sncp.isRemote((Service) src)) return; //远程模式不得注入
Class type = field.getType();
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));
} else if (type == TransportFactory.class) {
field.set(src, application.transportFactory);
} else if (type == NodeSncpServer.class) {
NodeServer server = null;
for (NodeServer ns : application.getNodeServers()) {
if (ns.getClass() == NodeSncpServer.class) continue;
if (res.name().equals(ns.server.getName())) {
server = ns;
break;
}
}
field.set(src, server);
} else if (type == NodeHttpServer.class) {
NodeServer server = null;
for (NodeServer ns : application.getNodeServers()) {
if (ns.getClass() == NodeHttpServer.class) continue;
if (res.name().equals(ns.server.getName())) {
server = ns;
break;
}
}
field.set(src, server);
} else if (type == NodeWatchServer.class) {
NodeServer server = null;
for (NodeServer ns : application.getNodeServers()) {
if (ns.getClass() == NodeWatchServer.class) continue;
if (res.name().equals(ns.server.getName())) {
server = ns;
break;
}
}
field.set(src, server);
}
// if (type == WatchFactory.class) {
// field.set(src, application.watchFactory);
// }
} catch (Exception e) {
logger.log(Level.SEVERE, "Resource inject error", e);
}
}
@Override
public boolean autoNone() {
return false;
}
}, Application.class, ResourceFactory.class, TransportFactory.class, NodeSncpServer.class, NodeHttpServer.class, NodeWatchServer.class);
//--------------------------------------------------------------------------
initResources(); initResources();
} }
@@ -363,30 +446,33 @@ public final class Application {
final AnyValue resources = config.getAnyValue("resources"); final AnyValue resources = config.getAnyValue("resources");
if (resources != null) { if (resources != null) {
//------------------------------------------------------------------------ //------------------------------------------------------------------------
for (AnyValue conf : resources.getAnyValues("group")) { for (AnyValue conf : resources.getAnyValues("group")) {
final String group = conf.getValue("name", ""); final String group = conf.getValue("name", "");
final String protocol = conf.getValue("protocol", Transport.DEFAULT_PROTOCOL).toUpperCase(); final String protocol = conf.getValue("protocol", Transport.DEFAULT_PROTOCOL).toUpperCase();
if (!"TCP".equalsIgnoreCase(protocol) && !"UDP".equalsIgnoreCase(protocol)) { if (!"TCP".equalsIgnoreCase(protocol) && !"UDP".equalsIgnoreCase(protocol)) {
throw new RuntimeException("Not supported Transport Protocol " + conf.getValue("protocol")); throw new RuntimeException("Not supported Transport Protocol " + conf.getValue("protocol"));
} }
GroupInfo ginfo = globalGroups.get(group); TransportGroupInfo ginfo = new TransportGroupInfo(group, protocol, conf.getValue("subprotocol", ""), new LinkedHashSet<>());
if (ginfo == null) {
ginfo = new GroupInfo(group, protocol, conf.getValue("subprotocol", ""), new LinkedHashSet<>());
globalGroups.put(group, ginfo);
}
for (AnyValue node : conf.getAnyValues("node")) { for (AnyValue node : conf.getAnyValues("node")) {
final InetSocketAddress addr = new InetSocketAddress(node.getValue("addr"), node.getIntValue("port")); final InetSocketAddress addr = new InetSocketAddress(node.getValue("addr"), node.getIntValue("port"));
ginfo.addrs.add(addr); ginfo.putAddress(addr);
String oldgroup = globalNodes.get(addr);
if (oldgroup != null) throw new RuntimeException(addr + " had one more group " + (globalNodes.get(addr)));
globalNodes.put(addr, group);
} }
transportFactory.addGroupInfo(ginfo);
} }
} }
//------------------------------------------------------------------------ //------------------------------------------------------------------------
} }
public void restoreConfig() throws IOException {
synchronized (this) {
File confFile = new File(this.home, "conf/application.xml");
confFile.renameTo(new File(this.home, "conf/application_" + String.format("%1$tY%1$tm%1$td%1$tH%1$tM%1$tS", System.currentTimeMillis()) + ".xml"));
final PrintStream ps = new PrintStream(new FileOutputStream(confFile));
ps.append(config.toXML("application"));
ps.close();
}
}
private void startSelfServer() throws Exception { private void startSelfServer() throws Exception {
final Application application = this; final Application application = this;
new Thread() { new Thread() {
@@ -431,7 +517,7 @@ public final class Application {
} }
} else if ("APIDOC".equalsIgnoreCase(new String(bytes))) { } else if ("APIDOC".equalsIgnoreCase(new String(bytes))) {
try { try {
new ApiDocs(application).run(); new ApiDocsService(application).run();
buffer.clear(); buffer.clear();
buffer.put("APIDOC OK".getBytes()); buffer.put("APIDOC OK".getBytes());
buffer.flip(); buffer.flip();
@@ -476,7 +562,7 @@ public final class Application {
final Application application = Application.create(true); final Application application = Application.create(true);
application.init(); application.init();
application.start(); application.start();
new ApiDocs(application).run(); new ApiDocsService(application).run();
logger.info("APIDOC OK"); logger.info("APIDOC OK");
return; return;
} }
@@ -490,20 +576,24 @@ public final class Application {
CountDownLatch timecd = new CountDownLatch(entrys.length); CountDownLatch timecd = new CountDownLatch(entrys.length);
final List<AnyValue> sncps = new ArrayList<>(); final List<AnyValue> sncps = new ArrayList<>();
final List<AnyValue> others = new ArrayList<>(); final List<AnyValue> others = new ArrayList<>();
final List<AnyValue> watchs = new ArrayList<>();
for (final AnyValue entry : entrys) { for (final AnyValue entry : entrys) {
if (entry.getValue("protocol", "").toUpperCase().startsWith("SNCP")) { if (entry.getValue("protocol", "").toUpperCase().startsWith("SNCP")) {
sncps.add(entry); sncps.add(entry);
} else if (entry.getValue("protocol", "").toUpperCase().startsWith("WATCH")) {
watchs.add(entry);
} else { } else {
others.add(entry); others.add(entry);
} }
} }
//单向SNCP服务不需要对等group if (watchs.size() > 1) throw new RuntimeException("Found one more WATCH Server");
//if (!sncps.isEmpty() && globalNodes.isEmpty()) throw new RuntimeException("found SNCP Server node but not found <group> node info."); this.watching = !watchs.isEmpty();
runServers(timecd, sncps); //必须确保sncp都启动后再启动其他协议 runServers(timecd, sncps); //必须确保SNCP服务都启动后再启动其他服务
runServers(timecd, others); runServers(timecd, others);
runServers(timecd, watchs); //必须在所有服务都启动后再启动WATCH服务
timecd.await(); timecd.await();
logger.info(this.getClass().getSimpleName() + " started in " + (System.currentTimeMillis() - startTime) + " ms"); logger.info(this.getClass().getSimpleName() + " started in " + (System.currentTimeMillis() - startTime) + " ms\r\n");
if (!singletonrun) this.serversLatch.await(); if (!singletonrun) this.serversLatch.await();
} }
@@ -530,13 +620,22 @@ public final class Application {
NodeServer server = null; NodeServer server = null;
if ("SNCP".equals(protocol)) { if ("SNCP".equals(protocol)) {
server = NodeSncpServer.createNodeServer(Application.this, serconf); server = NodeSncpServer.createNodeServer(Application.this, serconf);
} else if ("WATCH".equalsIgnoreCase(protocol)) {
DefaultAnyValue serconf2 = (DefaultAnyValue) serconf;
DefaultAnyValue rest = (DefaultAnyValue) serconf2.getAnyValue("rest");
if (rest == null) {
rest = new DefaultAnyValue();
serconf2.addValue("rest", rest);
}
rest.setValue("base", WatchServlet.class.getName());
server = new NodeWatchServer(Application.this, serconf);
} else if ("HTTP".equalsIgnoreCase(protocol)) { } else if ("HTTP".equalsIgnoreCase(protocol)) {
server = new NodeHttpServer(Application.this, serconf); server = new NodeHttpServer(Application.this, serconf);
} else { } else {
if (!inited.get()) { if (!inited.get()) {
synchronized (nodeClasses) { synchronized (nodeClasses) {
if (!inited.getAndSet(true)) { //加载自定义的协议SOCKS if (!inited.getAndSet(true)) { //加载自定义的协议SOCKS
ClassFilter profilter = new ClassFilter(NodeProtocol.class, NodeServer.class); ClassFilter profilter = new ClassFilter(classLoader, NodeProtocol.class, NodeServer.class, (Class[]) null);
ClassFilter.Loader.load(home, serconf.getValue("excludelibs", "").split(";"), profilter); ClassFilter.Loader.load(home, serconf.getValue("excludelibs", "").split(";"), profilter);
final Set<FilterEntry<NodeServer>> entrys = profilter.getFilterEntrys(); final Set<FilterEntry<NodeServer>> entrys = profilter.getFilterEntrys();
for (FilterEntry<NodeServer> entry : entrys) { for (FilterEntry<NodeServer> entry : entrys) {
@@ -546,7 +645,9 @@ public final class Application {
p = p.toUpperCase(); p = p.toUpperCase();
if ("SNCP".equals(p) || "HTTP".equals(p)) continue; if ("SNCP".equals(p) || "HTTP".equals(p)) continue;
final Class<? extends NodeServer> old = nodeClasses.get(p); final Class<? extends NodeServer> old = nodeClasses.get(p);
if (old != null && old != type) throw new RuntimeException("Protocol(" + p + ") had NodeServer-Class(" + old.getName() + ") but repeat NodeServer-Class(" + type.getName() + ")"); if (old != null && old != type) {
throw new RuntimeException("Protocol(" + p + ") had NodeServer-Class(" + old.getName() + ") but repeat NodeServer-Class(" + type.getName() + ")");
}
nodeClasses.put(p, type); nodeClasses.put(p, type);
} }
} }
@@ -623,17 +724,6 @@ public final class Application {
System.exit(0); System.exit(0);
} }
Set<String> findSncpGroups(Transport sameGroupTransport, Collection<Transport> diffGroupTransports) {
Set<String> gs = new HashSet<>();
if (sameGroupTransport != null) gs.add(sameGroupTransport.getName());
if (diffGroupTransports != null) {
for (Transport t : diffGroupTransports) {
gs.add(t.getName());
}
}
return gs;
}
NodeSncpServer findNodeSncpServer(final InetSocketAddress sncpAddr) { NodeSncpServer findNodeSncpServer(final InetSocketAddress sncpAddr) {
for (NodeServer node : servers) { for (NodeServer node : servers) {
if (node.isSNCP() && sncpAddr.equals(node.getSncpAddress())) { if (node.isSNCP() && sncpAddr.equals(node.getSncpAddress())) {
@@ -643,11 +733,6 @@ public final class Application {
return null; return null;
} }
GroupInfo findGroupInfo(String group) {
if (group == null) return null;
return globalGroups.get(group);
}
private void shutdown() throws Exception { private void shutdown() throws Exception {
servers.stream().forEach((server) -> { servers.stream().forEach((server) -> {
try { try {
@@ -660,26 +745,31 @@ public final class Application {
}); });
for (DataSource source : dataSources) { for (DataSource source : dataSources) {
if (source == null) continue;
try { try {
source.getClass().getMethod("close").invoke(source); source.getClass().getMethod("close").invoke(source);
} catch (Exception e) { } catch (Exception e) {
logger.log(Level.FINER, "close DataSource erroneous", e); logger.log(Level.FINER, source.getClass() + " close DataSource erroneous", e);
} }
} }
for (CacheSource source : cacheSources) { for (CacheSource source : cacheSources) {
if (source == null) continue;
try { try {
source.getClass().getMethod("close").invoke(source); source.getClass().getMethod("close").invoke(source);
} catch (Exception e) { } catch (Exception e) {
logger.log(Level.FINER, "close CacheSource erroneous", e); logger.log(Level.FINER, source.getClass() + " close CacheSource erroneous", e);
}
}
if (this.transportChannelGroup != null) {
try {
this.transportChannelGroup.shutdownNow();
} catch (Exception e) {
logger.log(Level.FINER, "close transportChannelGroup erroneous", e);
} }
} }
this.transportFactory.shutdownNow();
}
private 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);
} }
private static AnyValue load(final InputStream in0) { private static AnyValue load(final InputStream in0) {

View File

@@ -40,6 +40,8 @@ public final class ClassFilter<T> {
private Class superClass; //符合的父类型。不为空时扫描结果的class必须是superClass的子类 private Class superClass; //符合的父类型。不为空时扫描结果的class必须是superClass的子类
private Class[] excludeSuperClasses; //不符合的父类型。
private Class<? extends Annotation> annotationClass;//符合的注解。不为空时扫描结果的class必须包含该注解 private Class<? extends Annotation> annotationClass;//符合的注解。不为空时扫描结果的class必须包含该注解
private Pattern[] includePatterns; //符合的classname正则表达式 private Pattern[] includePatterns; //符合的classname正则表达式
@@ -56,18 +58,22 @@ public final class ClassFilter<T> {
private AnyValue conf; //基本配置信息, 当符合条件时将conf的属性赋值到FilterEntry中去。 private AnyValue conf; //基本配置信息, 当符合条件时将conf的属性赋值到FilterEntry中去。
public ClassFilter(Class<? extends Annotation> annotationClass, Class superClass) { private final ClassLoader classLoader;
this(annotationClass, superClass, null);
public ClassFilter(ClassLoader classLoader, Class<? extends Annotation> annotationClass, Class superClass, Class[] excludeSuperClasses) {
this(classLoader, annotationClass, superClass, excludeSuperClasses, null);
} }
public ClassFilter(Class<? extends Annotation> annotationClass, Class superClass, AnyValue conf) { public ClassFilter(ClassLoader classLoader, Class<? extends Annotation> annotationClass, Class superClass, Class[] excludeSuperClasses, AnyValue conf) {
this.annotationClass = annotationClass; this.annotationClass = annotationClass;
this.superClass = superClass; this.superClass = superClass;
this.excludeSuperClasses = excludeSuperClasses;
this.conf = conf; this.conf = conf;
this.classLoader = classLoader == null ? Thread.currentThread().getContextClassLoader() : classLoader;
} }
public static ClassFilter create(String includeregs, String excluderegs, Set<String> includeValues, Set<String> excludeValues) { public static ClassFilter create(Class[] excludeSuperClasses, String includeregs, String excluderegs, Set<String> includeValues, Set<String> excludeValues) {
ClassFilter filter = new ClassFilter(null, null); ClassFilter filter = new ClassFilter(null, null, null, excludeSuperClasses);
filter.setIncludePatterns(includeregs == null ? null : includeregs.split(";")); filter.setIncludePatterns(includeregs == null ? null : includeregs.split(";"));
filter.setExcludePatterns(excluderegs == null ? null : excluderegs.split(";")); filter.setExcludePatterns(excluderegs == null ? null : excluderegs.split(";"));
filter.setPrivilegeIncludes(includeValues); filter.setPrivilegeIncludes(includeValues);
@@ -93,7 +99,11 @@ public final class ClassFilter<T> {
* @return Set&lt;FilterEntry&lt;T&gt;&gt; * @return Set&lt;FilterEntry&lt;T&gt;&gt;
*/ */
public final Set<FilterEntry<T>> getFilterEntrys() { public final Set<FilterEntry<T>> getFilterEntrys() {
return entrys; HashSet<FilterEntry<T>> set = new HashSet<>();
set.addAll(entrys);
if (ors != null) ors.forEach(f -> set.addAll(f.getFilterEntrys()));
if (ands != null) ands.forEach(f -> set.addAll(f.getFilterEntrys()));
return set;
} }
/** /**
@@ -102,7 +112,11 @@ public final class ClassFilter<T> {
* @return Set&lt;FilterEntry&lt;T&gt;&gt; * @return Set&lt;FilterEntry&lt;T&gt;&gt;
*/ */
public final Set<FilterEntry<T>> getFilterExpectEntrys() { public final Set<FilterEntry<T>> getFilterExpectEntrys() {
return expectEntrys; HashSet<FilterEntry<T>> set = new HashSet<>();
set.addAll(expectEntrys);
if (ors != null) ors.forEach(f -> set.addAll(f.getFilterExpectEntrys()));
if (ands != null) ands.forEach(f -> set.addAll(f.getFilterExpectEntrys()));
return set;
} }
/** /**
@@ -112,8 +126,8 @@ public final class ClassFilter<T> {
*/ */
public final Set<FilterEntry<T>> getAllFilterEntrys() { public final Set<FilterEntry<T>> getAllFilterEntrys() {
HashSet<FilterEntry<T>> rs = new HashSet<>(); HashSet<FilterEntry<T>> rs = new HashSet<>();
rs.addAll(entrys); rs.addAll(getFilterEntrys());
rs.addAll(expectEntrys); rs.addAll(getFilterExpectEntrys());
return rs; return rs;
} }
@@ -153,7 +167,7 @@ public final class ClassFilter<T> {
} }
if (cf == null || clazzname.startsWith("sun.")) return; if (cf == null || clazzname.startsWith("sun.")) return;
try { try {
Class clazz = Class.forName(clazzname); Class clazz = classLoader.loadClass(clazzname);
if (!cf.accept(property, clazz, autoscan)) return; if (!cf.accept(property, clazz, autoscan)) return;
if (cf.conf != null) { if (cf.conf != null) {
if (property == null) { if (property == null) {
@@ -177,7 +191,7 @@ public final class ClassFilter<T> {
} catch (Throwable cfe) { } catch (Throwable cfe) {
if (finer && !clazzname.startsWith("sun.") && !clazzname.startsWith("javax.") if (finer && !clazzname.startsWith("sun.") && !clazzname.startsWith("javax.")
&& !clazzname.startsWith("com.sun.") && !clazzname.startsWith("jdk.")) { && !clazzname.startsWith("com.sun.") && !clazzname.startsWith("jdk.")) {
//logger.log(Level.FINEST, ClassFilter.class.getSimpleName() + " filter error", cfe); logger.log(Level.FINEST, ClassFilter.class.getSimpleName() + " filter error", cfe);
} }
} }
} }
@@ -247,7 +261,13 @@ public final class ClassFilter<T> {
public boolean accept(AnyValue property, Class clazz, boolean autoscan) { public boolean accept(AnyValue property, Class clazz, boolean autoscan) {
if (this.refused || !Modifier.isPublic(clazz.getModifiers())) return false; if (this.refused || !Modifier.isPublic(clazz.getModifiers())) return false;
if (annotationClass != null && clazz.getAnnotation(annotationClass) == null) return false; if (annotationClass != null && clazz.getAnnotation(annotationClass) == null) return false;
return superClass == null || (clazz != superClass && superClass.isAssignableFrom(clazz)); boolean rs = superClass == null || (clazz != superClass && superClass.isAssignableFrom(clazz));
if (rs && this.excludeSuperClasses != null && this.excludeSuperClasses.length > 0) {
for (Class c : this.excludeSuperClasses) {
if (c != null && (clazz == c || c.isAssignableFrom(clazz))) return false;
}
}
return rs;
} }
public static Pattern[] toPattern(String[] regs) { public static Pattern[] toPattern(String[] regs) {
@@ -269,6 +289,18 @@ public final class ClassFilter<T> {
this.superClass = superClass; this.superClass = superClass;
} }
public Class getSuperClass() {
return superClass;
}
public Class[] getExcludeSuperClasses() {
return excludeSuperClasses;
}
public void setExcludeSuperClasses(Class[] excludeSuperClasses) {
this.excludeSuperClasses = excludeSuperClasses;
}
public void setAnnotationClass(Class<? extends Annotation> annotationClass) { public void setAnnotationClass(Class<? extends Annotation> annotationClass) {
this.annotationClass = annotationClass; this.annotationClass = annotationClass;
} }
@@ -293,10 +325,6 @@ public final class ClassFilter<T> {
return annotationClass; return annotationClass;
} }
public Class getSuperClass() {
return superClass;
}
public boolean isRefused() { public boolean isRefused() {
return refused; return refused;
} }
@@ -428,12 +456,12 @@ public final class ClassFilter<T> {
* @throws IOException 异常 * @throws IOException 异常
*/ */
public static void load(final File excludeFile, final String[] excludeRegs, final ClassFilter... filters) throws IOException { public static void load(final File excludeFile, final String[] excludeRegs, final ClassFilter... filters) throws IOException {
URLClassLoader loader = (URLClassLoader) Thread.currentThread().getContextClassLoader(); RedkaleClassLoader loader = (RedkaleClassLoader) Thread.currentThread().getContextClassLoader();
List<URL> urlfiles = new ArrayList<>(2); List<URL> urlfiles = new ArrayList<>(2);
List<URL> urljares = new ArrayList<>(2); List<URL> urljares = new ArrayList<>(2);
final URL exurl = excludeFile != null ? excludeFile.toURI().toURL() : null; final URL exurl = excludeFile != null ? excludeFile.toURI().toURL() : null;
final Pattern[] excludePatterns = toPattern(excludeRegs); final Pattern[] excludePatterns = toPattern(excludeRegs);
for (URL url : loader.getURLs()) { for (URL url : loader.getAllURLs()) {
if (exurl != null && exurl.sameFile(url)) continue; if (exurl != null && exurl.sameFile(url)) continue;
if (excludePatterns != null) { if (excludePatterns != null) {
boolean skip = false; boolean skip = false;
@@ -465,6 +493,12 @@ public final class ClassFilter<T> {
if (entryname.endsWith(".class") && entryname.indexOf('$') < 0) { if (entryname.endsWith(".class") && entryname.indexOf('$') < 0) {
String classname = entryname.substring(0, entryname.length() - 6); String classname = entryname.substring(0, entryname.length() - 6);
if (classname.startsWith("javax.") || classname.startsWith("com.sun.")) continue; if (classname.startsWith("javax.") || classname.startsWith("com.sun.")) continue;
//常见的jar跳过
if (classname.startsWith("com.mysql.")) break;
if (classname.startsWith("org.mariadb.")) break;
if (classname.startsWith("oracle.jdbc.")) break;
if (classname.startsWith("org.postgresql.")) break;
if (classname.startsWith("com.microsoft.sqlserver.")) break;
classes.add(classname); classes.add(classname);
if (debug) debugstr.append(classname).append("\r\n"); if (debug) debugstr.append(classname).append("\r\n");
for (final ClassFilter filter : filters) { for (final ClassFilter filter : filters) {

View File

@@ -1,80 +0,0 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.boot;
import java.net.InetSocketAddress;
import java.util.*;
import org.redkale.convert.json.JsonConvert;
/**
* 协议地址组合对象, 对应application.xml 中 resources-&#62;group 节点信息
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*/
public class GroupInfo {
protected String name; //地址
protected String protocol; //协议 取值范围: TCP、UDP
protected String subprotocol; //子协议,预留使用
protected Set<InetSocketAddress> addrs; //地址列表, 对应 resources-&#62;group-&#62;node节点信息
public GroupInfo() {
}
public GroupInfo(String name, String protocol, String subprotocol, Set<InetSocketAddress> addrs) {
this.name = name;
this.protocol = protocol;
this.subprotocol = subprotocol;
this.addrs = addrs;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getProtocol() {
return protocol;
}
public void setProtocol(String protocol) {
this.protocol = protocol;
}
public String getSubprotocol() {
return subprotocol;
}
public void setSubprotocol(String subprotocol) {
this.subprotocol = subprotocol;
}
public Set<InetSocketAddress> getAddrs() {
return addrs;
}
public Set<InetSocketAddress> copyAddrs() {
return addrs == null ? null : new LinkedHashSet<>(addrs);
}
public void setAddrs(Set<InetSocketAddress> addrs) {
this.addrs = addrs;
}
@Override
public String toString() {
return JsonConvert.root().convertTo(this);
}
}

View File

@@ -5,6 +5,7 @@
*/ */
package org.redkale.boot; package org.redkale.boot;
import java.lang.annotation.Annotation;
import java.lang.reflect.*; import java.lang.reflect.*;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.util.*; import java.util.*;
@@ -17,6 +18,7 @@ import org.redkale.net.sncp.Sncp;
import org.redkale.service.*; import org.redkale.service.*;
import org.redkale.util.AnyValue.DefaultAnyValue; import org.redkale.util.AnyValue.DefaultAnyValue;
import org.redkale.util.*; import org.redkale.util.*;
import org.redkale.watch.*;
/** /**
* HTTP Server节点的配置Server * HTTP Server节点的配置Server
@@ -29,7 +31,7 @@ import org.redkale.util.*;
@NodeProtocol({"HTTP"}) @NodeProtocol({"HTTP"})
public class NodeHttpServer extends NodeServer { public class NodeHttpServer extends NodeServer {
protected final boolean rest; //是否加载REST服务 为true加载rest节点信息并将所有可REST化的Service生成RestHttpServlet protected final boolean rest; //是否加载REST服务 为true加载rest节点信息并将所有可REST化的Service生成RestServlet
protected final HttpServer httpServer; protected final HttpServer httpServer;
@@ -40,7 +42,11 @@ public class NodeHttpServer extends NodeServer {
} }
private static Server createServer(Application application, AnyValue serconf) { private static Server createServer(Application application, AnyValue serconf) {
return new HttpServer(application.getStartTime(), application.getWatchFactory()); return new HttpServer(application.getStartTime());
}
public HttpServer getHttpServer() {
return httpServer;
} }
@Override @Override
@@ -48,22 +54,42 @@ public class NodeHttpServer extends NodeServer {
return httpServer == null ? null : httpServer.getSocketAddress(); return httpServer == null ? null : httpServer.getSocketAddress();
} }
@Override
protected ClassFilter<Service> createServiceClassFilter() {
return createClassFilter(this.sncpGroup, null, Service.class, new Class[]{org.redkale.watch.WatchService.class}, Annotation.class, "services", "service");
}
@Override
protected ClassFilter<Filter> createFilterClassFilter() {
return createClassFilter(null, null, HttpFilter.class, new Class[]{WatchFilter.class}, null, "filters", "filter");
}
@Override @Override
protected ClassFilter<Servlet> createServletClassFilter() { protected ClassFilter<Servlet> createServletClassFilter() {
return createClassFilter(null, WebServlet.class, HttpServlet.class, null, "servlets", "servlet"); return createClassFilter(null, WebServlet.class, HttpServlet.class, new Class[]{WatchServlet.class}, null, "servlets", "servlet");
} }
@Override @Override
protected void loadServlet(ClassFilter<? extends Servlet> servletFilter) throws Exception { protected ClassFilter createOtherClassFilter() {
if (httpServer != null) loadHttpServlet(this.serverConf.getAnyValue("servlets"), servletFilter); return createClassFilter(null, RestWebSocket.class, WebSocket.class, null, null, "rest", "websocket");
} }
@Override @Override
protected void loadService(ClassFilter serviceFilter) throws Exception { protected void loadService(ClassFilter<? extends Service> serviceFilter, ClassFilter otherFilter) throws Exception {
super.loadService(serviceFilter); super.loadService(serviceFilter, otherFilter);
initWebSocketService(); initWebSocketService();
} }
@Override
protected void loadFilter(ClassFilter<? extends Filter> filterFilter, ClassFilter otherFilter) throws Exception {
if (httpServer != null) loadHttpFilter(this.serverConf.getAnyValue("filters"), filterFilter);
}
@Override
protected void loadServlet(ClassFilter<? extends Servlet> servletFilter, ClassFilter otherFilter) throws Exception {
if (httpServer != null) loadHttpServlet(servletFilter, otherFilter);
}
private void initWebSocketService() { private void initWebSocketService() {
final NodeServer self = this; final NodeServer self = this;
final ResourceFactory regFactory = application.getResourceFactory(); final ResourceFactory regFactory = application.getResourceFactory();
@@ -71,14 +97,23 @@ public class NodeHttpServer extends NodeServer {
try { try {
if (field.getAnnotation(Resource.class) == null) return; if (field.getAnnotation(Resource.class) == null) return;
if (!(src instanceof WebSocketServlet)) return; if (!(src instanceof WebSocketServlet)) return;
ResourceFactory.ResourceLoader loader = null;
ResourceFactory sncpResFactory = null;
for (NodeServer ns : application.servers) {
if (!ns.isSNCP()) continue;
sncpResFactory = ns.resourceFactory;
loader = sncpResFactory.findLoader(WebSocketNode.class, field);
if (loader != null) break;
}
if (loader != null) loader.load(sncpResFactory, src, resourceName, field, attachment);
synchronized (regFactory) { synchronized (regFactory) {
Service nodeService = (Service) rf.find(resourceName, WebSocketNode.class); Service nodeService = (Service) rf.find(resourceName, WebSocketNode.class);
if (nodeService == null) { if (nodeService == null) {
nodeService = Sncp.createLocalService(resourceName, getExecutor(), application.getResourceFactory(), WebSocketNodeService.class, (InetSocketAddress) null, (String) null, (Set<String>) null, (AnyValue) null, (Transport) null, (Collection<Transport>) null); nodeService = Sncp.createLocalService(serverClassLoader, resourceName, WebSocketNodeService.class, application.getResourceFactory(), application.getTransportFactory(), (InetSocketAddress) null, (Set<String>) null, (AnyValue) null);
regFactory.register(resourceName, WebSocketNode.class, nodeService); regFactory.register(resourceName, WebSocketNode.class, nodeService);
resourceFactory.inject(nodeService, self);
logger.fine("[" + Thread.currentThread().getName() + "] Load Service " + nodeService);
} }
resourceFactory.inject(nodeService, self);
logger.fine("[" + Thread.currentThread().getName() + "] Load Service " + nodeService);
field.set(src, nodeService); field.set(src, nodeService);
} }
} catch (Exception e) { } catch (Exception e) {
@@ -87,11 +122,31 @@ public class NodeHttpServer extends NodeServer {
}, WebSocketNode.class); }, WebSocketNode.class);
} }
protected void loadHttpServlet(final AnyValue servletsConf, final ClassFilter<? extends Servlet> filter) throws Exception { protected void loadHttpFilter(final AnyValue filtersConf, final ClassFilter<? extends Filter> classFilter) throws Exception {
final StringBuilder sb = logger.isLoggable(Level.INFO) ? new StringBuilder() : null; final StringBuilder sb = logger.isLoggable(Level.INFO) ? new StringBuilder() : null;
final String prefix = servletsConf == null ? "" : servletsConf.getValue("path", "");
final String threadName = "[" + Thread.currentThread().getName() + "] "; final String threadName = "[" + Thread.currentThread().getName() + "] ";
List<FilterEntry<? extends Servlet>> list = new ArrayList(filter.getFilterEntrys()); List<FilterEntry<? extends Filter>> list = new ArrayList(classFilter.getFilterEntrys());
for (FilterEntry<? extends Filter> en : list) {
Class<HttpFilter> clazz = (Class<HttpFilter>) en.getType();
if (Modifier.isAbstract(clazz.getModifiers())) continue;
final HttpFilter filter = clazz.newInstance();
resourceFactory.inject(filter, this);
DefaultAnyValue filterConf = (DefaultAnyValue) en.getProperty();
this.httpServer.addHttpFilter(filter, filterConf);
if (sb != null) sb.append(threadName).append(" Load ").append(clazz.getName()).append(LINE_SEPARATOR);
}
if (sb != null && sb.length() > 0) logger.log(Level.INFO, sb.toString());
}
protected void loadHttpServlet(final ClassFilter<? extends Servlet> servletFilter, ClassFilter<? extends WebSocket> webSocketFilter) throws Exception {
final AnyValue servletsConf = this.serverConf.getAnyValue("servlets");
final StringBuilder sb = logger.isLoggable(Level.INFO) ? new StringBuilder() : null;
String prefix0 = servletsConf == null ? "" : servletsConf.getValue("path", "");
if (!prefix0.isEmpty() && prefix0.charAt(prefix0.length() - 1) == '/') prefix0 = prefix0.substring(0, prefix0.length() - 1);
if (!prefix0.isEmpty() && prefix0.charAt(0) != '/') prefix0 = '/' + prefix0;
final String prefix = prefix0;
final String threadName = "[" + Thread.currentThread().getName() + "] ";
List<FilterEntry<? extends Servlet>> list = new ArrayList(servletFilter.getFilterEntrys());
list.sort((FilterEntry<? extends Servlet> o1, FilterEntry<? extends Servlet> o2) -> { //必须保证WebSocketServlet优先加载 因为要确保其他的HttpServlet可以注入本地模式的WebSocketNode list.sort((FilterEntry<? extends Servlet> o1, FilterEntry<? extends Servlet> o2) -> { //必须保证WebSocketServlet优先加载 因为要确保其他的HttpServlet可以注入本地模式的WebSocketNode
boolean ws1 = WebSocketServlet.class.isAssignableFrom(o1.getType()); boolean ws1 = WebSocketServlet.class.isAssignableFrom(o1.getType());
boolean ws2 = WebSocketServlet.class.isAssignableFrom(o2.getType()); boolean ws2 = WebSocketServlet.class.isAssignableFrom(o2.getType());
@@ -132,67 +187,134 @@ public class NodeHttpServer extends NodeServer {
} }
} }
if (rest && serverConf != null) { if (rest && serverConf != null) {
final List<Object> restedObjects = new ArrayList<>();
for (AnyValue restConf : serverConf.getAnyValues("rest")) { for (AnyValue restConf : serverConf.getAnyValues("rest")) {
loadRestServlet(prefix, restConf, sb); loadRestServlet(webSocketFilter, restConf, restedObjects, sb);
} }
} }
if (sb != null && sb.length() > 0) logger.log(Level.INFO, sb.toString()); if (sb != null && sb.length() > 0) logger.log(Level.INFO, sb.toString().trim());
} }
protected void loadRestServlet(final String prefix, final AnyValue restConf, final StringBuilder sb) throws Exception { protected void loadRestServlet(final ClassFilter<? extends WebSocket> webSocketFilter, final AnyValue restConf, final List<Object> restedObjects, final StringBuilder sb) throws Exception {
if (!rest) return; if (!rest) return;
if (restConf == null) return; //不存在REST服务 if (restConf == null) return; //不存在REST服务
String prefix0 = restConf.getValue("path", "");
if (!prefix0.isEmpty() && prefix0.charAt(prefix0.length() - 1) == '/') prefix0 = prefix0.substring(0, prefix0.length() - 1);
if (!prefix0.isEmpty() && prefix0.charAt(0) != '/') prefix0 = '/' + prefix0;
final String prefix = prefix0;
final String threadName = "[" + Thread.currentThread().getName() + "] "; final String threadName = "[" + Thread.currentThread().getName() + "] ";
final List<AbstractMap.SimpleEntry<String, String[]>> ss = sb == null ? null : new ArrayList<>(); final List<AbstractMap.SimpleEntry<String, String[]>> ss = sb == null ? null : new ArrayList<>();
final Class baseServletClass = Class.forName(restConf.getValue("base", DefaultRestServlet.class.getName()));
final boolean autoload = restConf.getBoolValue("autoload", true); final boolean autoload = restConf.getBoolValue("autoload", true);
final boolean mustsign = restConf.getBoolValue("mustsign", true); //是否只加载标记@RestService的Service类 { //加载RestService
String userTypeStr = restConf.getValue("usertype");
final Class userType = userTypeStr == null ? null : this.serverClassLoader.loadClass(userTypeStr);
final Set<String> includeValues = new HashSet<>(); final Class baseServletType = this.serverClassLoader.loadClass(restConf.getValue("base", HttpServlet.class.getName()));
final Set<String> excludeValues = new HashSet<>(); final Set<String> includeValues = new HashSet<>();
for (AnyValue item : restConf.getAnyValues("service")) { final Set<String> excludeValues = new HashSet<>();
if (item.getBoolValue("ignore", false)) { for (AnyValue item : restConf.getAnyValues("service")) {
excludeValues.add(item.getValue("value", "")); if (item.getBoolValue("ignore", false)) {
} else { excludeValues.add(item.getValue("value", ""));
includeValues.add(item.getValue("value", "")); } else {
includeValues.add(item.getValue("value", ""));
}
}
final ClassFilter restFilter = ClassFilter.create(null, restConf.getValue("includes", ""), restConf.getValue("excludes", ""), includeValues, excludeValues);
final boolean finest = logger.isLoggable(Level.FINEST);
super.interceptorServices.forEach((service) -> {
final Class stype = Sncp.getServiceType(service);
final String name = Sncp.getResourceName(service);
RestService rs = (RestService) stype.getAnnotation(RestService.class);
if (rs == null || rs.ignore()) return;
final String stypename = stype.getName();
if (!autoload && !includeValues.contains(stypename)) return;
if (!restFilter.accept(stypename)) return;
if (restedObjects.contains(service)) {
logger.log(Level.WARNING, stype.getName() + " repeat create rest servlet, so ignore");
return;
}
restedObjects.add(service); //避免重复创建Rest对象
HttpServlet servlet = httpServer.addRestServlet(serverClassLoader, service, userType, baseServletType, prefix);
if (servlet == null) return; //没有HttpMapping方法的HttpServlet调用Rest.createRestServlet就会返回null
String prefix2 = prefix;
WebServlet ws = servlet.getClass().getAnnotation(WebServlet.class);
if (ws != null && !ws.repair()) prefix2 = "";
resourceFactory.inject(servlet, NodeHttpServer.this);
if (finest) logger.finest(threadName + " Create RestServlet(resource.name='" + name + "') = " + servlet);
if (ss != null) {
String[] mappings = servlet.getClass().getAnnotation(WebServlet.class).value();
for (int i = 0; i < mappings.length; i++) {
mappings[i] = prefix2 + mappings[i];
}
ss.add(new AbstractMap.SimpleEntry<>(servlet.getClass().getName(), mappings));
}
});
}
if (webSocketFilter != null) { //加载RestWebSocket
final Set<String> includeValues = new HashSet<>();
final Set<String> excludeValues = new HashSet<>();
for (AnyValue item : restConf.getAnyValues("websocket")) {
if (item.getBoolValue("ignore", false)) {
excludeValues.add(item.getValue("value", ""));
} else {
includeValues.add(item.getValue("value", ""));
}
}
final ClassFilter restFilter = ClassFilter.create(null, restConf.getValue("includes", ""), restConf.getValue("excludes", ""), includeValues, excludeValues);
final boolean finest = logger.isLoggable(Level.FINEST);
List<FilterEntry<? extends WebSocket>> list = new ArrayList(webSocketFilter.getFilterEntrys());
for (FilterEntry<? extends WebSocket> en : list) {
Class<WebSocket> clazz = (Class<WebSocket>) en.getType();
if (Modifier.isAbstract(clazz.getModifiers())) {
logger.log(Level.FINE, clazz.getName() + " cannot abstract on rest websocket, so ignore");
continue;
}
if (Modifier.isFinal(clazz.getModifiers())) {
logger.log(Level.FINE, clazz.getName() + " cannot final on rest websocket, so ignore");
continue;
}
final Class<? extends WebSocket> stype = en.getType();
RestWebSocket rs = stype.getAnnotation(RestWebSocket.class);
if (rs == null || rs.ignore()) return;
final String stypename = stype.getName();
if (!autoload && !includeValues.contains(stypename)) return;
if (!restFilter.accept(stypename)) return;
if (restedObjects.contains(stype)) {
logger.log(Level.WARNING, stype.getName() + " repeat create rest websocket, so ignore");
return;
}
restedObjects.add(stype); //避免重复创建Rest对象
HttpServlet servlet = httpServer.addRestWebSocketServlet(serverClassLoader, stype, prefix, en.getProperty());
if (servlet == null) return; //没有RestOnMessage方法的HttpServlet调用Rest.createRestWebSocketServlet就会返回null
String prefix2 = prefix;
WebServlet ws = servlet.getClass().getAnnotation(WebServlet.class);
if (ws != null && !ws.repair()) prefix2 = "";
resourceFactory.inject(servlet, NodeHttpServer.this);
if (finest) logger.finest(threadName + " " + stype.getName() + " create RestWebSocketServlet " + servlet);
if (ss != null) {
String[] mappings = servlet.getClass().getAnnotation(WebServlet.class).value();
for (int i = 0; i < mappings.length; i++) {
mappings[i] = prefix2 + mappings[i];
}
ss.add(new AbstractMap.SimpleEntry<>(servlet.getClass().getName(), mappings));
}
} }
} }
final ClassFilter restFilter = ClassFilter.create(restConf.getValue("includes", ""), restConf.getValue("excludes", ""), includeValues, excludeValues);
super.interceptorServices.forEach((service) -> {
final Class stype = Sncp.getServiceType(service);
final String name = Sncp.getResourceName(service);
RestService rs = (RestService) stype.getAnnotation(RestService.class);
if (rs != null && rs.ignore()) return;
if (mustsign && rs == null) return;
if (stype.getAnnotation(LocalService.class) != null && rs == null) return;
final String stypename = stype.getName();
if (!autoload && !includeValues.contains(stypename)) return;
if (!restFilter.accept(stypename)) return;
RestHttpServlet servlet = httpServer.addRestServlet(name, stype, service, baseServletClass, prefix, (AnyValue) null);
resourceFactory.inject(servlet, NodeHttpServer.this);
if (finest) logger.finest(threadName + " Create RestServlet(resource.name='" + name + "') = " + servlet);
if (ss != null) {
String[] mappings = servlet.getClass().getAnnotation(WebServlet.class).value();
for (int i = 0; i < mappings.length; i++) {
mappings[i] = prefix + mappings[i];
}
ss.add(new AbstractMap.SimpleEntry<>(servlet.getClass().getName(), mappings));
}
});
//输出信息 //输出信息
if (ss != null && sb != null) { if (ss != null && !ss.isEmpty() && sb != null) {
Collections.sort(ss, (AbstractMap.SimpleEntry<String, String[]> o1, AbstractMap.SimpleEntry<String, String[]> o2) -> o1.getKey().compareTo(o2.getKey())); Collections.sort(ss, (AbstractMap.SimpleEntry<String, String[]> o1, AbstractMap.SimpleEntry<String, String[]> o2) -> o1.getKey().compareTo(o2.getKey()));
int max = 0; int max = 0;
for (AbstractMap.SimpleEntry<String, String[]> as : ss) { for (AbstractMap.SimpleEntry<String, String[]> as : ss) {
if (as.getKey().length() > max) max = as.getKey().length(); if (as.getKey().length() > max) max = as.getKey().length();
} }
sb.append(threadName).append(" ").append(LINE_SEPARATOR);
for (AbstractMap.SimpleEntry<String, String[]> as : ss) { for (AbstractMap.SimpleEntry<String, String[]> as : ss) {
sb.append(threadName).append(" Load ").append(as.getKey()); sb.append(threadName).append(" Load ").append(as.getKey());
for (int i = 0; i < max - as.getKey().length(); i++) { for (int i = 0; i < max - as.getKey().length(); i++) {

View File

@@ -5,27 +5,28 @@
*/ */
package org.redkale.boot; package org.redkale.boot;
import org.redkale.util.RedkaleClassLoader;
import java.io.*; import java.io.*;
import java.lang.annotation.Annotation; import java.lang.annotation.Annotation;
import java.lang.reflect.*; import java.lang.reflect.*;
import java.net.InetSocketAddress; import java.net.*;
import java.nio.file.Path; import java.nio.file.*;
import java.util.*; import java.util.*;
import java.util.concurrent.*; import java.util.concurrent.*;
import java.util.function.*; import java.util.function.*;
import java.util.logging.*; import java.util.logging.*;
import java.util.stream.Collectors;
import javax.annotation.Resource; import javax.annotation.Resource;
import javax.persistence.Transient; import javax.persistence.Transient;
import static org.redkale.boot.Application.*; import static org.redkale.boot.Application.*;
import org.redkale.boot.ClassFilter.FilterEntry; import org.redkale.boot.ClassFilter.FilterEntry;
import org.redkale.net.Filter;
import org.redkale.net.*; import org.redkale.net.*;
import org.redkale.net.http.WebSocketNode; import org.redkale.net.http.WebSocketServlet;
import org.redkale.net.sncp.*; import org.redkale.net.sncp.*;
import org.redkale.service.*; import org.redkale.service.*;
import org.redkale.source.*; import org.redkale.source.*;
import org.redkale.util.AnyValue.DefaultAnyValue;
import org.redkale.util.*; import org.redkale.util.*;
import org.redkale.util.AnyValue.DefaultAnyValue;
/** /**
* Server节点的初始化配置类 * Server节点的初始化配置类
@@ -44,15 +45,6 @@ public abstract class NodeServer {
//日志输出对象 //日志输出对象
protected final Logger logger; protected final Logger logger;
//日志是否为FINE级别
protected final boolean fine;
//日志是否为FINER级别
protected final boolean finer;
//日志是否为FINEST级别
protected final boolean finest;
//进程主类 //进程主类
protected final Application application; protected final Application application;
@@ -62,8 +54,13 @@ public abstract class NodeServer {
//当前Server对象 //当前Server对象
protected final Server server; protected final Server server;
//ClassLoader
protected RedkaleClassLoader serverClassLoader;
protected final Thread serverThread;
//当前Server的SNCP协议的组 //当前Server的SNCP协议的组
private String sncpGroup = null; protected String sncpGroup = null;
//SNCP服务的地址 非SNCP为null //SNCP服务的地址 非SNCP为null
private InetSocketAddress sncpAddress; private InetSocketAddress sncpAddress;
@@ -95,36 +92,9 @@ public abstract class NodeServer {
this.resourceFactory = application.getResourceFactory().createChild(); this.resourceFactory = application.getResourceFactory().createChild();
this.server = server; this.server = server;
this.logger = Logger.getLogger(this.getClass().getSimpleName()); this.logger = Logger.getLogger(this.getClass().getSimpleName());
this.fine = logger.isLoggable(Level.FINE); this.serverClassLoader = new RedkaleClassLoader(application.getServerClassLoader());
this.finer = logger.isLoggable(Level.FINER); Thread.currentThread().setContextClassLoader(this.serverClassLoader);
this.finest = logger.isLoggable(Level.FINEST); this.serverThread = Thread.currentThread();
}
protected Consumer<Runnable> getExecutor() throws Exception {
if (server == null) return null;
final Field field = Server.class.getDeclaredField("context");
field.setAccessible(true);
return new Consumer<Runnable>() {
private Context context;
@Override
public void accept(Runnable t) {
if (context == null && server != null) {
try {
this.context = (Context) field.get(server);
} catch (Exception e) {
logger.log(Level.SEVERE, "Server (" + server.getSocketAddress() + ") cannot find Context", e);
}
}
if (context == null) {
t.run();
} else {
context.submitAsync(t);
}
}
};
} }
public static <T extends NodeServer> NodeServer create(Class<T> clazz, Application application, AnyValue serconf) { public static <T extends NodeServer> NodeServer create(Class<T> clazz, Application application, AnyValue serconf) {
@@ -140,13 +110,17 @@ public abstract class NodeServer {
if (isSNCP()) { // SNCP协议 if (isSNCP()) { // SNCP协议
String host = this.serverConf.getValue("host", "0.0.0.0").replace("0.0.0.0", ""); String host = this.serverConf.getValue("host", "0.0.0.0").replace("0.0.0.0", "");
this.sncpAddress = new InetSocketAddress(host.isEmpty() ? application.localAddress.getHostAddress() : host, this.serverConf.getIntValue("port")); this.sncpAddress = new InetSocketAddress(host.isEmpty() ? application.localAddress.getHostAddress() : host, this.serverConf.getIntValue("port"));
this.sncpGroup = application.globalNodes.get(this.sncpAddress); this.sncpGroup = application.transportFactory.findGroupName(this.sncpAddress);
//单向SNCP服务不需要对等group //单向SNCP服务不需要对等group
//if (this.sncpGroup == null) throw new RuntimeException("Server (" + String.valueOf(config).replaceAll("\\s+", " ") + ") not found <group> info"); //if (this.sncpGroup == null) throw new RuntimeException("Server (" + String.valueOf(config).replaceAll("\\s+", " ") + ") not found <group> info");
} }
//单点服务不会有 sncpAddress、sncpGroup //单点服务不会有 sncpAddress、sncpGroup
if (this.sncpAddress != null) this.resourceFactory.register(RESNAME_SERVER_ADDR, this.sncpAddress); if (this.sncpAddress != null) {
if (this.sncpGroup != null) this.resourceFactory.register(RESNAME_SERVER_GROUP, this.sncpGroup); this.resourceFactory.register(RESNAME_SNCP_ADDR, this.sncpAddress);
this.resourceFactory.register(RESNAME_SNCP_ADDR, SocketAddress.class, this.sncpAddress);
this.resourceFactory.register(RESNAME_SNCP_ADDR, String.class, this.sncpAddress.getHostString() + ":" + this.sncpAddress.getPort());
}
if (this.sncpGroup != null) this.resourceFactory.register(RESNAME_SNCP_GROUP, this.sncpGroup);
{ {
//设置root文件夹 //设置root文件夹
String webroot = this.serverConf.getValue("root", "root"); String webroot = this.serverConf.getValue("root", "root");
@@ -159,9 +133,9 @@ public abstract class NodeServer {
resourceFactory.register(Server.RESNAME_SERVER_ROOT, File.class, myroot.getCanonicalFile()); resourceFactory.register(Server.RESNAME_SERVER_ROOT, File.class, myroot.getCanonicalFile());
resourceFactory.register(Server.RESNAME_SERVER_ROOT, Path.class, myroot.toPath()); resourceFactory.register(Server.RESNAME_SERVER_ROOT, Path.class, myroot.toPath());
final String homepath = myroot.getCanonicalPath();
//加入指定的classpath //加入指定的classpath
Server.loadLib(logger, this.serverConf.getValue("lib", "").replace("${APP_HOME}", homepath) + ";" + homepath + "/lib/*;" + homepath + "/classes"); Server.loadLib(serverClassLoader, logger, this.serverConf.getValue("lib", "${APP_HOME}/libs/*").replace("${APP_HOME}", application.getHome().getPath().replace('\\', '/')));
this.serverThread.setContextClassLoader(this.serverClassLoader);
} }
//必须要进行初始化, 构建Service时需要使用Context中的ExecutorService //必须要进行初始化, 构建Service时需要使用Context中的ExecutorService
server.init(this.serverConf); server.init(this.serverConf);
@@ -169,32 +143,55 @@ public abstract class NodeServer {
initResource(); //给 DataSource、CacheSource 注册依赖注入时的监听回调事件。 initResource(); //给 DataSource、CacheSource 注册依赖注入时的监听回调事件。
String interceptorClass = this.serverConf.getValue("interceptor", ""); String interceptorClass = this.serverConf.getValue("interceptor", "");
if (!interceptorClass.isEmpty()) { if (!interceptorClass.isEmpty()) {
Class clazz = Class.forName(interceptorClass); Class clazz = serverClassLoader.loadClass(interceptorClass);
this.interceptor = (NodeInterceptor) clazz.newInstance(); this.interceptor = (NodeInterceptor) clazz.newInstance();
} }
ClassFilter<Servlet> servletFilter = createServletClassFilter();
ClassFilter<Service> serviceFilter = createServiceClassFilter(); ClassFilter<Service> serviceFilter = createServiceClassFilter();
ClassFilter<Filter> filterFilter = createFilterClassFilter();
ClassFilter<Servlet> servletFilter = createServletClassFilter();
ClassFilter otherFilter = createOtherClassFilter();
long s = System.currentTimeMillis(); long s = System.currentTimeMillis();
if (servletFilter == null) { ClassFilter.Loader.load(application.getHome(), serverConf.getValue("excludelibs", "").split(";"), serviceFilter, filterFilter, servletFilter, otherFilter);
ClassFilter.Loader.load(application.getHome(), serverConf.getValue("excludelibs", "").split(";"), serviceFilter);
} else {
ClassFilter.Loader.load(application.getHome(), serverConf.getValue("excludelibs", "").split(";"), serviceFilter, servletFilter);
}
long e = System.currentTimeMillis() - s; long e = System.currentTimeMillis() - s;
logger.info(this.getClass().getSimpleName() + " load filter class in " + e + " ms"); logger.info(this.getClass().getSimpleName() + " load filter class in " + e + " ms");
loadService(serviceFilter); //必须在servlet之前 loadService(serviceFilter, otherFilter); //必须在servlet之前
loadServlet(servletFilter); loadFilter(filterFilter, otherFilter);
loadServlet(servletFilter, otherFilter);
if (this.interceptor != null) this.resourceFactory.inject(this.interceptor); if (this.interceptor != null) this.resourceFactory.inject(this.interceptor);
} }
protected abstract void loadServlet(ClassFilter<? extends Servlet> servletFilter) throws Exception; protected abstract void loadFilter(ClassFilter<? extends Filter> filterFilter, ClassFilter otherFilter) throws Exception;
protected abstract void loadServlet(ClassFilter<? extends Servlet> servletFilter, ClassFilter otherFilter) throws Exception;
private void initResource() { private void initResource() {
final NodeServer self = this; final NodeServer self = this;
//--------------------------------------------------------------------------------------------- //---------------------------------------------------------------------------------------------
final ResourceFactory appResFactory = application.getResourceFactory(); final ResourceFactory appResFactory = application.getResourceFactory();
final TransportFactory appTranFactory = application.getTransportFactory();
final AnyValue resources = application.config.getAnyValue("resources");
final Map<String, AnyValue> cacheResource = new HashMap<>();
final Map<String, AnyValue> dataResources = new HashMap<>();
if (resources != null) {
for (AnyValue sourceConf : resources.getAnyValues("source")) {
try {
Class type = serverClassLoader.loadClass(sourceConf.getValue("value"));
if (!Service.class.isAssignableFrom(type)) {
logger.log(Level.SEVERE, "load application source resource, but not Service error: " + sourceConf);
} else if (CacheSource.class.isAssignableFrom(type)) {
cacheResource.put(sourceConf.getValue("name", ""), sourceConf);
} else if (DataSource.class.isAssignableFrom(type)) {
dataResources.put(sourceConf.getValue("name", ""), sourceConf);
} else {
logger.log(Level.SEVERE, "load application source resource, but not CacheSource error: " + sourceConf);
}
} catch (Exception e) {
logger.log(Level.SEVERE, "load application source resource error: " + sourceConf, e);
}
}
}
//------------------------------------- 注册Resource -------------------------------------------------------- //------------------------------------- 注册Resource --------------------------------------------------------
resourceFactory.register((ResourceFactory rf, final Object src, String resourceName, Field field, final Object attachment) -> { resourceFactory.register((ResourceFactory rf, final Object src, String resourceName, Field field, final Object attachment) -> {
try { try {
@@ -204,7 +201,6 @@ public abstract class NodeServer {
Class type = field.getType(); Class type = field.getType();
if (type != AnyValue.class && type != AnyValue[].class) return; if (type != AnyValue.class && type != AnyValue[].class) return;
Object resource = null; Object resource = null;
final AnyValue resources = application.config.getAnyValue("resources");
final AnyValue properties = resources == null ? null : resources.getAnyValue("properties"); final AnyValue properties = resources == null ? null : resources.getAnyValue("properties");
if (properties != null && type == AnyValue.class) { if (properties != null && type == AnyValue.class) {
resource = properties.getAnyValue(res.name().substring("properties.".length())); resource = properties.getAnyValue(res.name().substring("properties.".length()));
@@ -229,13 +225,13 @@ public abstract class NodeServer {
appResFactory.register(resourceName, DataSource.class, source); appResFactory.register(resourceName, DataSource.class, source);
SncpClient client = Sncp.getSncpClient((Service) src); SncpClient client = Sncp.getSncpClient((Service) src);
Transport sameGroupTransport = Sncp.getSameGroupTransport((Service) src);
List<Transport> diffGroupTransports = Arrays.asList(Sncp.getDiffGroupTransports((Service) src));
final InetSocketAddress sncpAddr = client == null ? null : client.getClientAddress(); final InetSocketAddress sncpAddr = client == null ? null : client.getClientAddress();
if ((src instanceof DataSource) && sncpAddr != null && resourceFactory.find(resourceName, DataCacheListener.class) == null) { //只有DataSourceService 才能赋值 DataCacheListener if ((src instanceof DataSource) && sncpAddr != null && resourceFactory.find(resourceName, DataCacheListener.class) == null) { //只有DataSourceService 才能赋值 DataCacheListener
final NodeSncpServer sncpServer = application.findNodeSncpServer(sncpAddr); final NodeSncpServer sncpServer = application.findNodeSncpServer(sncpAddr);
Set<String> gs = application.findSncpGroups(sameGroupTransport, diffGroupTransports); final Set<String> groups = new HashSet<>();
Service cacheListenerService = Sncp.createLocalService(resourceName, getExecutor(), appResFactory, DataCacheListenerService.class, sncpAddr, sncpServer.getSncpGroup(), gs, Sncp.getConf((Service) src), sameGroupTransport, diffGroupTransports); if (client != null && client.getSameGroup() != null) groups.add(client.getSameGroup());
if (client != null && client.getDiffGroups() != null) groups.addAll(client.getDiffGroups());
Service cacheListenerService = Sncp.createLocalService(serverClassLoader, resourceName, DataCacheListenerService.class, appResFactory, appTranFactory, sncpAddr, groups, Sncp.getConf((Service) src));
appResFactory.register(resourceName, DataCacheListener.class, cacheListenerService); appResFactory.register(resourceName, DataCacheListener.class, cacheListenerService);
localServices.add(cacheListenerService); localServices.add(cacheListenerService);
sncpServer.consumerAccept(cacheListenerService); sncpServer.consumerAccept(cacheListenerService);
@@ -244,56 +240,78 @@ public abstract class NodeServer {
} }
field.set(src, source); field.set(src, source);
rf.inject(source, self); // 给其可能包含@Resource的字段赋值; rf.inject(source, self); // 给其可能包含@Resource的字段赋值;
//NodeServer.this.watchFactory.inject(src);
if (source instanceof Service) ((Service) source).init(null);
} catch (Exception e) { } catch (Exception e) {
logger.log(Level.SEVERE, "DataSource inject error", e); logger.log(Level.SEVERE, "DataSource inject error", e);
} }
}, DataSource.class); }, DataSource.class);
//------------------------------------- 注册CacheSource -------------------------------------------------------- //------------------------------------- 注册CacheSource --------------------------------------------------------
resourceFactory.register((ResourceFactory rf, final Object src, final String resourceName, Field field, final Object attachment) -> { resourceFactory.register(new ResourceFactory.ResourceLoader() {
try { public void load(ResourceFactory rf, final Object src, final String resourceName, Field field, final Object attachment) {
if (field.getAnnotation(Resource.class) == null) return; try {
if ((src instanceof Service) && Sncp.isRemote((Service) src)) return; //远程模式不需要注入 CacheSource if (field.getAnnotation(Resource.class) == null) return;
final Service srcService = (Service) src; if ((src instanceof Service) && Sncp.isRemote((Service) src)) return; //远程模式不需要注入 CacheSource
SncpClient client = Sncp.getSncpClient(srcService); final Service srcService = (Service) src;
Transport sameGroupTransport = Sncp.getSameGroupTransport(srcService); SncpClient client = Sncp.getSncpClient(srcService);
Transport[] dts = Sncp.getDiffGroupTransports((Service) src); final InetSocketAddress sncpAddr = client == null ? null : client.getClientAddress();
List<Transport> diffGroupTransports = dts == null ? new ArrayList<>() : Arrays.asList(dts); final Set<String> groups = new HashSet<>();
final InetSocketAddress sncpAddr = client == null ? null : client.getClientAddress(); if (client != null && client.getSameGroup() != null) groups.add(client.getSameGroup());
final CacheMemorySource source = Sncp.createLocalService(resourceName, getExecutor(), appResFactory, CacheMemorySource.class, sncpAddr, Sncp.getSncpGroup(srcService), Sncp.getGroups(srcService), Sncp.getConf(srcService), sameGroupTransport, diffGroupTransports); if (client != null && client.getDiffGroups() != null) groups.addAll(client.getDiffGroups());
Type genericType = field.getGenericType();
ParameterizedType pt = (genericType instanceof ParameterizedType) ? (ParameterizedType) genericType : null;
Type valType = pt == null ? null : pt.getActualTypeArguments()[1];
source.setStoreType(pt == null ? Serializable.class : (Class) pt.getActualTypeArguments()[0], valType instanceof Class ? (Class) valType : Object.class);
if (field.getAnnotation(Transient.class) != null) source.setNeedStore(false); //必须在setStoreType之后
application.cacheSources.add(source);
appResFactory.register(resourceName, genericType, source);
appResFactory.register(resourceName, CacheSource.class, source);
field.set(src, source);
rf.inject(source, self); //
((Service) source).init(null);
if ((src instanceof WebSocketNodeService) && sncpAddr != null) { //只有WebSocketNodeService的服务才需要给SNCP服务注入CacheMemorySource AnyValue sourceConf = cacheResource.get(resourceName);
NodeSncpServer sncpServer = application.findNodeSncpServer(sncpAddr); if (sourceConf == null) sourceConf = dataResources.get(resourceName);
Set<String> gs = application.findSncpGroups(sameGroupTransport, diffGroupTransports); final Class sourceType = sourceConf == null ? CacheMemorySource.class : serverClassLoader.loadClass(sourceConf.getValue("value"));
sncpServer.getSncpServer().addSncpServlet((Service) source); Object source;
logger.info("[" + Thread.currentThread().getName() + "] Load Service " + source); if (DataSource.class.isAssignableFrom(sourceType)) { // DataSource
source = (DataSource) Sncp.createLocalService(serverClassLoader, resourceName, sourceType, appResFactory, appTranFactory, sncpAddr, groups, Sncp.getConf(srcService));
application.dataSources.add((DataSource) source);
appResFactory.register(resourceName, DataSource.class, source);
} else { // CacheSource
source = (CacheSource) Sncp.createLocalService(serverClassLoader, resourceName, sourceType, appResFactory, appTranFactory, sncpAddr, groups, Sncp.getConf(srcService));
Type genericType = field.getGenericType();
ParameterizedType pt = (genericType instanceof ParameterizedType) ? (ParameterizedType) genericType : null;
Type valType = pt == null ? null : pt.getActualTypeArguments()[1];
if (sourceType == CacheMemorySource.class) {
CacheMemorySource memorySource = (CacheMemorySource) source;
memorySource.setStoreType(pt == null ? Serializable.class : (Class) pt.getActualTypeArguments()[0], valType instanceof Class ? (Class) valType : Object.class);
if (field.getAnnotation(Transient.class) != null) memorySource.setNeedStore(false); //必须在setStoreType之后
}
application.cacheSources.add((CacheSource) source);
appResFactory.register(resourceName, genericType, source);
appResFactory.register(resourceName, CacheSource.class, source);
}
field.set(src, source);
rf.inject(source, self); //
if (source instanceof Service) ((Service) source).init(sourceConf);
if ((src instanceof WebSocketNodeService) && sncpAddr != null) { //只有WebSocketNodeService的服务才需要给SNCP服务注入CacheMemorySource
NodeSncpServer sncpServer = application.findNodeSncpServer(sncpAddr);
sncpServer.getSncpServer().addSncpServlet((Service) source);
//logger.info("[" + Thread.currentThread().getName() + "] Load Service " + source);
}
logger.info("[" + Thread.currentThread().getName() + "] Load Source " + source);
} catch (Exception e) {
logger.log(Level.SEVERE, "DataSource inject error", e);
} }
logger.info("[" + Thread.currentThread().getName() + "] Load Source " + source); }
} catch (Exception e) {
logger.log(Level.SEVERE, "DataSource inject error", e); public boolean autoNone() {
return false;
} }
}, CacheSource.class); }, CacheSource.class);
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
protected void loadService(ClassFilter serviceFilter) throws Exception { protected void loadService(ClassFilter<? extends Service> serviceFilter, ClassFilter otherFilter) throws Exception {
if (serviceFilter == null) return; if (serviceFilter == null) return;
final String threadName = "[" + Thread.currentThread().getName() + "] "; final String threadName = "[" + Thread.currentThread().getName() + "] ";
final Set<FilterEntry<Service>> entrys = serviceFilter.getAllFilterEntrys(); final Set<FilterEntry<? extends Service>> entrys = (Set) serviceFilter.getAllFilterEntrys();
ResourceFactory regFactory = isSNCP() ? application.getResourceFactory() : resourceFactory; ResourceFactory regFactory = isSNCP() ? application.getResourceFactory() : resourceFactory;
final ResourceFactory appResourceFactory = application.getResourceFactory();
for (FilterEntry<Service> entry : entrys) { //service实现类 final TransportFactory appTransportFactory = application.getTransportFactory();
for (FilterEntry<? extends Service> entry : entrys) { //service实现类
final Class<? extends Service> serviceImplClass = entry.getType(); final Class<? extends Service> serviceImplClass = entry.getType();
if (Modifier.isFinal(serviceImplClass.getModifiers())) continue; //修饰final的类跳过 if (Modifier.isFinal(serviceImplClass.getModifiers())) continue; //修饰final的类跳过
if (!Modifier.isPublic(serviceImplClass.getModifiers())) continue; if (!Modifier.isPublic(serviceImplClass.getModifiers())) continue;
@@ -302,7 +320,7 @@ public abstract class NodeServer {
if (DataSource.class.isAssignableFrom(serviceImplClass)) continue; if (DataSource.class.isAssignableFrom(serviceImplClass)) continue;
if (CacheSource.class.isAssignableFrom(serviceImplClass)) continue; if (CacheSource.class.isAssignableFrom(serviceImplClass)) continue;
if (DataCacheListener.class.isAssignableFrom(serviceImplClass)) continue; if (DataCacheListener.class.isAssignableFrom(serviceImplClass)) continue;
if (WebSocketNode.class.isAssignableFrom(serviceImplClass)) continue; //if (WebSocketNode.class.isAssignableFrom(serviceImplClass)) continue;
} }
if (entry.getName().contains("$")) throw new RuntimeException("<name> value cannot contains '$' in " + entry.getProperty()); if (entry.getName().contains("$")) throw new RuntimeException("<name> value cannot contains '$' in " + entry.getProperty());
Service oldother = resourceFactory.find(entry.getName(), serviceImplClass); Service oldother = resourceFactory.find(entry.getName(), serviceImplClass);
@@ -316,30 +334,29 @@ public abstract class NodeServer {
final boolean localed = (this.sncpAddress == null && entry.isEmptyGroups() && !serviceImplClass.isInterface() && !Modifier.isAbstract(serviceImplClass.getModifiers())) //非SNCP的Server通常是单点服务 final boolean localed = (this.sncpAddress == null && entry.isEmptyGroups() && !serviceImplClass.isInterface() && !Modifier.isAbstract(serviceImplClass.getModifiers())) //非SNCP的Server通常是单点服务
|| groups.contains(this.sncpGroup) //本地IP含在内的 || groups.contains(this.sncpGroup) //本地IP含在内的
|| (this.sncpGroup == null && entry.isEmptyGroups()) //空的SNCP配置 || (this.sncpGroup == null && entry.isEmptyGroups()) //空的SNCP配置
|| serviceImplClass.getAnnotation(LocalService.class) != null;//本地模式 || serviceImplClass.getAnnotation(Local.class) != null;//本地模式
if (localed && (serviceImplClass.isInterface() || Modifier.isAbstract(serviceImplClass.getModifiers()))) continue; //本地模式不能实例化接口和抽象类的Service类 if (localed && (serviceImplClass.isInterface() || Modifier.isAbstract(serviceImplClass.getModifiers()))) continue; //本地模式不能实例化接口和抽象类的Service类
final BiConsumer<ResourceFactory, Boolean> runner = (ResourceFactory rf, Boolean needinject) -> { final ResourceFactory.ResourceLoader resourceLoader = (ResourceFactory rf, final Object src, final String resourceName, Field field, final Object attachment) -> {
try { try {
Service service; Service service;
if (localed) { //本地模式 boolean ws = src instanceof WebSocketServlet;
service = Sncp.createLocalService(entry.getName(), getExecutor(), application.getResourceFactory(), serviceImplClass, if (ws || localed) { //本地模式
NodeServer.this.sncpAddress, NodeServer.this.sncpGroup, groups, entry.getProperty(), loadTransport(NodeServer.this.sncpGroup), loadTransports(groups)); service = Sncp.createLocalService(serverClassLoader, resourceName, serviceImplClass, appResourceFactory, appTransportFactory, NodeServer.this.sncpAddress, groups, entry.getProperty());
} else { } else {
service = Sncp.createRemoteService(entry.getName(), getExecutor(), serviceImplClass, NodeServer.this.sncpAddress, null, groups, entry.getProperty(), loadTransport(groups)); service = Sncp.createRemoteService(serverClassLoader, resourceName, serviceImplClass, appTransportFactory, NodeServer.this.sncpAddress, groups, entry.getProperty());
} }
if (SncpClient.parseMethod(serviceImplClass).isEmpty()) return; //class没有可用的方法 通常为BaseService if (SncpClient.parseMethod(serviceImplClass).isEmpty()) return; //class没有可用的方法 通常为BaseService
//final ServiceWrapper wrapper = new ServiceWrapper(serviceImplClass, service, entry.getName(), localed ? NodeServer.this.sncpGroup : null, groups, entry.getProperty());
for (final Class restype : Sncp.getResourceTypes(service)) { final Class restype = Sncp.getResourceType(service);
if (resourceFactory.find(entry.getName(), restype) == null) { if (rf.find(resourceName, restype) == null) {
regFactory.register(entry.getName(), restype, service); regFactory.register(resourceName, restype, service);
if (needinject) rf.inject(service); //动态加载的Service也存在按需加载的注入资源 } else if (isSNCP() && !entry.isAutoload()) {
} else if (isSNCP() && !entry.isAutoload()) { throw new RuntimeException(restype.getSimpleName() + "(class:" + serviceImplClass.getName() + ", name:" + resourceName + ", group:" + groups + ") is repeat.");
throw new RuntimeException(restype.getSimpleName() + "(class:" + serviceImplClass.getName() + ", name:" + entry.getName() + ", group:" + groups + ") is repeat.");
}
} }
if (Sncp.isRemote(service)) { if (Sncp.isRemote(service)) {
remoteServices.add(service); remoteServices.add(service);
} else { } else {
if (field != null) rf.inject(service); //动态加载的Service也存在按需加载的注入资源
localServices.add(service); localServices.add(service);
interceptorServices.add(service); interceptorServices.add(service);
if (consumer != null) consumer.accept(service); if (consumer != null) consumer.accept(service);
@@ -351,16 +368,10 @@ public abstract class NodeServer {
} }
}; };
if (entry.isExpect()) { if (entry.isExpect()) {
ResourceFactory.ResourceLoader resourceLoader = (ResourceFactory rf, final Object src, final String resourceName, Field field, final Object attachment) -> {
runner.accept(rf, true);
};
ResourceType rty = entry.getType().getAnnotation(ResourceType.class); ResourceType rty = entry.getType().getAnnotation(ResourceType.class);
Class[] resTypes = rty == null ? new Class[]{} : rty.value(); resourceFactory.register(resourceLoader, rty == null ? entry.getType() : rty.value());
for (final Class restype : resTypes) {
resourceFactory.register(resourceLoader, restype);
}
} else { } else {
runner.accept(resourceFactory, false); resourceLoader.load(resourceFactory, null, entry.getName(), null, false);
} }
} }
@@ -387,7 +398,7 @@ public abstract class NodeServer {
//----------------- init ----------------- //----------------- init -----------------
List<Service> swlist = new ArrayList<>(localServices); List<Service> swlist = new ArrayList<>(localServices);
Collections.sort(swlist, (o1, o2) -> { Collections.sort(swlist, (o1, o2) -> {
int rs = Sncp.getResourceTypes(o1)[0].getName().compareTo(Sncp.getResourceTypes(o2)[0].getName()); int rs = Sncp.getResourceType(o1).getName().compareTo(Sncp.getResourceType(o2).getName());
if (rs == 0) rs = Sncp.getResourceName(o1).compareTo(Sncp.getResourceName(o2)); if (rs == 0) rs = Sncp.getResourceName(o1).compareTo(Sncp.getResourceName(o2));
return rs; return rs;
}); });
@@ -419,88 +430,37 @@ public abstract class NodeServer {
private void calcMaxLength(Service y) { //计算toString中的长度 private void calcMaxLength(Service y) { //计算toString中的长度
maxNameLength = Math.max(maxNameLength, Sncp.getResourceName(y).length()); maxNameLength = Math.max(maxNameLength, Sncp.getResourceName(y).length());
StringBuilder s = new StringBuilder(); maxClassNameLength = Math.max(maxClassNameLength, Sncp.getResourceType(y).getName().length() + 1);
Class[] types = Sncp.getResourceTypes(y);
if (types.length == 1) {
s.append(types[0].getName());
} else {
s.append('[');
s.append(Arrays.asList(types).stream().map((Class t) -> t.getName()).collect(Collectors.joining(",")));
s.append(']');
}
maxClassNameLength = Math.max(maxClassNameLength, s.length() + 1);
} }
protected List<Transport> loadTransports(final HashSet<String> groups) { protected abstract ClassFilter<Filter> createFilterClassFilter();
if (groups == null) return null;
final List<Transport> transports = new ArrayList<>();
for (String group : groups) {
if (this.sncpGroup == null || !this.sncpGroup.equals(group)) {
transports.add(loadTransport(group));
}
}
return transports;
}
protected Transport loadTransport(final HashSet<String> groups) {
if (groups == null || groups.isEmpty()) return null;
final String groupid = new ArrayList<>(groups).stream().sorted().collect(Collectors.joining(";")); //按字母排列顺序
Transport transport = application.resourceFactory.find(groupid, Transport.class);
if (transport != null) return transport;
final List<Transport> transports = new ArrayList<>();
for (String group : groups) {
transports.add(loadTransport(group));
}
Set<InetSocketAddress> addrs = new HashSet();
transports.forEach(t -> addrs.addAll(Arrays.asList(t.getRemoteAddresses())));
Transport first = transports.get(0);
GroupInfo ginfo = application.findGroupInfo(first.getName());
Transport newTransport = new Transport(groupid, ginfo.getProtocol(), application.getWatchFactory(),
ginfo.getSubprotocol(), application.transportBufferPool, application.transportChannelGroup, this.sncpAddress, addrs);
synchronized (application.resourceFactory) {
transport = application.resourceFactory.find(groupid, Transport.class);
if (transport == null) {
transport = newTransport;
application.resourceFactory.register(groupid, transport);
}
}
return transport;
}
protected Transport loadTransport(final String group) {
if (group == null) return null;
Transport transport;
synchronized (application.resourceFactory) {
transport = application.resourceFactory.find(group, Transport.class);
if (transport != null) {
if (this.sncpAddress != null && !this.sncpAddress.equals(transport.getClientAddress())) {
throw new RuntimeException(transport + "repeat create on newClientAddress = " + this.sncpAddress + ", oldClientAddress = " + transport.getClientAddress());
}
return transport;
}
GroupInfo ginfo = application.findGroupInfo(group);
Set<InetSocketAddress> addrs = ginfo.copyAddrs();
if (addrs == null) throw new RuntimeException("Not found <group> = " + group + " on <resources> ");
transport = new Transport(group, ginfo.getProtocol(), application.getWatchFactory(),
ginfo.getSubprotocol(), application.transportBufferPool, application.transportChannelGroup, this.sncpAddress, addrs);
application.resourceFactory.register(group, transport);
}
return transport;
}
protected abstract ClassFilter<Servlet> createServletClassFilter(); protected abstract ClassFilter<Servlet> createServletClassFilter();
protected ClassFilter createOtherClassFilter() {
return null;
}
protected ClassFilter<Service> createServiceClassFilter() { protected ClassFilter<Service> createServiceClassFilter() {
return createClassFilter(this.sncpGroup, null, Service.class, Annotation.class, "services", "service"); return createClassFilter(this.sncpGroup, null, Service.class, (!isSNCP() && application.watching) ? null : new Class[]{org.redkale.watch.WatchService.class}, Annotation.class, "services", "service");
} }
protected ClassFilter createClassFilter(final String localGroup, Class<? extends Annotation> ref, protected ClassFilter createClassFilter(final String localGroup, Class<? extends Annotation> ref,
Class inter, Class<? extends Annotation> ref2, String properties, String property) { Class inter, Class[] excludeSuperClasses, Class<? extends Annotation> ref2, String properties, String property) {
ClassFilter cf = new ClassFilter(ref, inter, null); ClassFilter cf = new ClassFilter(this.serverClassLoader, ref, inter, excludeSuperClasses, null);
if (properties == null && properties == null) return cf; if (properties == null && properties == null) {
if (this.serverConf == null) return cf; cf.setRefused(true);
return cf;
}
if (this.serverConf == null) {
cf.setRefused(true);
return cf;
}
AnyValue[] proplist = this.serverConf.getAnyValues(properties); AnyValue[] proplist = this.serverConf.getAnyValues(properties);
if (proplist == null || proplist.length < 1) return cf; if (proplist == null || proplist.length < 1) {
cf.setRefused(true);
return cf;
}
cf = null; cf = null;
for (AnyValue list : proplist) { for (AnyValue list : proplist) {
DefaultAnyValue prop = null; DefaultAnyValue prop = null;
@@ -514,20 +474,20 @@ public abstract class NodeServer {
prop = new AnyValue.DefaultAnyValue(); prop = new AnyValue.DefaultAnyValue();
prop.addValue("groups", sc); prop.addValue("groups", sc);
} }
ClassFilter filter = new ClassFilter(ref, inter, prop); ClassFilter filter = new ClassFilter(this.serverClassLoader, ref, inter, excludeSuperClasses, prop);
for (AnyValue av : list.getAnyValues(property)) { // <service><servlet> 节点 for (AnyValue av : list.getAnyValues(property)) { // <service>、<filter>、<servlet> 节点
final AnyValue[] items = av.getAnyValues("property"); final AnyValue[] items = av.getAnyValues("property");
if (av instanceof DefaultAnyValue && items.length > 0) { //存在 <property>节点 if (av instanceof DefaultAnyValue && items.length > 0) { //存在 <property>节点
DefaultAnyValue dav = DefaultAnyValue.create(); DefaultAnyValue dav = DefaultAnyValue.create();
final AnyValue.Entry<String>[] strings = av.getStringEntrys(); final AnyValue.Entry<String>[] strings = av.getStringEntrys();
if (strings != null) { //将<service><servlet>节点的属性值传给dav if (strings != null) { //将<service>、<filter>、<servlet>节点的属性值传给dav
for (AnyValue.Entry<String> en : strings) { for (AnyValue.Entry<String> en : strings) {
dav.addValue(en.name, en.getValue()); dav.addValue(en.name, en.getValue());
} }
} }
final AnyValue.Entry<AnyValue>[] anys = av.getAnyEntrys(); final AnyValue.Entry<AnyValue>[] anys = av.getAnyEntrys();
if (anys != null) { if (anys != null) {
for (AnyValue.Entry<AnyValue> en : anys) { //将<service><servlet>节点的非property属性节点传给dav for (AnyValue.Entry<AnyValue> en : anys) { //将<service>、<filter>、<servlet>节点的非property属性节点传给dav
if (!"property".equals(en.name)) dav.addValue(en.name, en.getValue()); if (!"property".equals(en.name)) dav.addValue(en.name, en.getValue());
} }
} }
@@ -538,7 +498,9 @@ public abstract class NodeServer {
dav.addValue("properties", ps); dav.addValue("properties", ps);
av = dav; av = dav;
} }
filter.filter(av, av.getValue("value"), false); if (!av.getBoolValue("ignore", false)) {
filter.filter(av, av.getValue("value"), false);
}
} }
if (list.getBoolValue("autoload", true)) { if (list.getBoolValue("autoload", true)) {
String includes = list.getValue("includes", ""); String includes = list.getValue("includes", "");
@@ -561,6 +523,24 @@ public abstract class NodeServer {
return false; return false;
} }
public boolean isWATCH() {
return false;
}
public ResourceFactory getResourceFactory() {
return resourceFactory;
}
public RedkaleClassLoader getServerClassLoader() {
return serverClassLoader;
}
public void setServerClassLoader(RedkaleClassLoader serverClassLoader) {
Objects.requireNonNull(this.serverClassLoader);
this.serverClassLoader = serverClassLoader;
this.serverThread.setContextClassLoader(serverClassLoader);
}
public InetSocketAddress getSncpAddress() { public InetSocketAddress getSncpAddress() {
return sncpAddress; return sncpAddress;
} }

View File

@@ -5,13 +5,16 @@
*/ */
package org.redkale.boot; package org.redkale.boot;
import java.lang.reflect.Modifier;
import java.net.*; import java.net.*;
import java.util.*; import java.util.*;
import java.util.logging.*; import java.util.logging.Level;
import org.redkale.boot.ClassFilter.FilterEntry;
import org.redkale.net.*; import org.redkale.net.*;
import org.redkale.net.sncp.*; import org.redkale.net.sncp.*;
import org.redkale.service.Service; import org.redkale.service.Service;
import org.redkale.util.*; import org.redkale.util.*;
import org.redkale.util.AnyValue.DefaultAnyValue;
/** /**
* SNCP Server节点的配置Server * SNCP Server节点的配置Server
@@ -41,7 +44,7 @@ public class NodeSncpServer extends NodeServer {
} }
private static Server createServer(Application application, AnyValue serconf) { private static Server createServer(Application application, AnyValue serconf) {
return new SncpServer(application.getStartTime(), application.getWatchFactory()); return new SncpServer(application.getStartTime());
} }
@Override @Override
@@ -78,7 +81,33 @@ public class NodeSncpServer extends NodeServer {
} }
@Override @Override
protected void loadServlet(ClassFilter<? extends Servlet> servletFilter) throws Exception { protected void loadFilter(ClassFilter<? extends Filter> filterFilter, ClassFilter otherFilter) throws Exception {
if (sncpServer != null) loadSncpFilter(this.serverConf.getAnyValue("fliters"), filterFilter);
}
protected void loadSncpFilter(final AnyValue servletsConf, final ClassFilter<? extends Filter> classFilter) throws Exception {
final StringBuilder sb = logger.isLoggable(Level.INFO) ? new StringBuilder() : null;
final String threadName = "[" + Thread.currentThread().getName() + "] ";
List<FilterEntry<? extends Filter>> list = new ArrayList(classFilter.getFilterEntrys());
for (FilterEntry<? extends Filter> en : list) {
Class<SncpFilter> clazz = (Class<SncpFilter>) en.getType();
if (Modifier.isAbstract(clazz.getModifiers())) continue;
final SncpFilter filter = clazz.newInstance();
resourceFactory.inject(filter, this);
DefaultAnyValue filterConf = (DefaultAnyValue) en.getProperty();
this.sncpServer.addSncpFilter(filter, filterConf);
if (sb != null) sb.append(threadName).append(" Load ").append(clazz.getName()).append(LINE_SEPARATOR);
}
if (sb != null && sb.length() > 0) logger.log(Level.INFO, sb.toString());
}
@Override
protected void loadServlet(ClassFilter<? extends Servlet> servletFilter, ClassFilter otherFilter) throws Exception {
}
@Override
protected ClassFilter<Filter> createFilterClassFilter() {
return createClassFilter(null, null, SncpFilter.class, new Class[]{org.redkale.watch.WatchFilter.class}, null, "filters", "filter");
} }
@Override @Override

View File

@@ -0,0 +1,50 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.boot;
import java.lang.annotation.Annotation;
import org.redkale.net.*;
import org.redkale.net.http.*;
import org.redkale.service.Service;
import org.redkale.util.AnyValue;
import org.redkale.watch.*;
/**
*
* @author zhangjx
*/
@NodeProtocol({"WATCH"})
public class NodeWatchServer extends NodeHttpServer {
public NodeWatchServer(Application application, AnyValue serconf) {
super(application, serconf);
}
@Override
protected ClassFilter<Service> createServiceClassFilter() {
return createClassFilter(this.sncpGroup, null, WatchService.class, null, Annotation.class, "services", "service");
}
@Override
protected ClassFilter<Filter> createFilterClassFilter() {
return createClassFilter(null, null, WatchFilter.class, null, null, "filters", "filter");
}
@Override
protected ClassFilter<Servlet> createServletClassFilter() {
return createClassFilter(null, WebServlet.class, WatchServlet.class, null, null, "servlets", "servlet");
}
@Override
protected ClassFilter createOtherClassFilter() {
return null;
}
@Override
public boolean isWATCH() {
return true;
}
}

View File

@@ -0,0 +1,17 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.boot.watch;
import org.redkale.service.AbstractService;
import org.redkale.watch.WatchService;
/**
*
* @author zhangjx
*/
public abstract class AbstractWatchService extends AbstractService implements WatchService {
}

View File

@@ -0,0 +1,50 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.boot.watch;
import java.io.IOException;
import java.util.List;
import javax.annotation.Resource;
import org.redkale.boot.*;
import org.redkale.net.http.*;
import org.redkale.service.RetResult;
import org.redkale.util.Comment;
/**
*
* @author zhangjx
*/
@RestService(name = "filter", catalog = "watch", repair = false)
public class FilterWatchService extends AbstractWatchService {
@Comment("Filter类名不存在")
public static final int RET_FILTER_TYPE_NOT_EXISTS = 1601_0002;
@Comment("Filter类名不合法")
public static final int RET_FILTER_TYPE_ILLEGAL = 1601_0003;
@Comment("Filter类名已存在")
public static final int RET_FILTER_EXISTS = 1601_0004;
@Comment("Filter的JAR包不存在")
public static final int RET_FILTER_JAR_ILLEGAL = 1601_0005;
@Resource
private Application application;
@RestMapping(name = "addfilter", auth = false, comment = "动态增加Filter")
public RetResult addFilter(@RestUploadFile(maxLength = 10 * 1024 * 1024, fileNameReg = "\\.jar$") byte[] jar,
@RestParam(name = "server", comment = "Server节点名") final String serverName,
@RestParam(name = "type", comment = "Filter类名") final String filterType) throws IOException {
if (filterType == null) return new RetResult(RET_FILTER_TYPE_NOT_EXISTS, "Not found Filter Type (" + filterType + ")");
if (jar == null) return new RetResult(RET_FILTER_JAR_ILLEGAL, "Not found jar file");
List<NodeServer> nodes = application.getNodeServers();
for (NodeServer node : nodes) {
if (node.getServer().containsFilter(filterType)) return new RetResult(RET_FILTER_EXISTS, "Filter(" + filterType + ") exists");
}
return RetResult.success();
}
}

View File

@@ -0,0 +1,17 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.boot.watch;
import org.redkale.net.http.RestService;
/**
*
* @author zhangjx
*/
@RestService(name = "server", catalog = "watch", repair = false)
public class ServerWatchService extends AbstractWatchService {
}

View File

@@ -0,0 +1,39 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.boot.watch;
import javax.annotation.Resource;
import org.redkale.boot.Application;
import org.redkale.net.TransportFactory;
import org.redkale.net.http.*;
/**
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*/
@RestService(name = "service", catalog = "watch", repair = false)
public class ServiceWatchService extends AbstractWatchService {
@Resource
private Application application;
@Resource
private TransportFactory transportFactory;
// @RestMapping(name = "load", auth = false, comment = "动态增加Service")
// public RetResult loadService(String type, @RestUploadFile(maxLength = 10 * 1024 * 1024, fileNameReg = "\\.jar$") byte[] jar) {
// //待开发
// return RetResult.success();
// }
//
// @RestMapping(name = "stop", auth = false, comment = "动态停止Service")
// public RetResult stopService(String name, String type) {
// //待开发
// return RetResult.success();
// }
}

View File

@@ -0,0 +1,39 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.boot.watch;
import javax.annotation.Resource;
import org.redkale.boot.Application;
import org.redkale.net.TransportFactory;
import org.redkale.net.http.*;
/**
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*/
@RestService(name = "servlet", catalog = "watch", repair = false)
public class ServletWatchService extends AbstractWatchService {
@Resource
private Application application;
@Resource
private TransportFactory transportFactory;
//
// @RestMapping(name = "load", auth = false, comment = "动态增加Servlet")
// public RetResult loadServlet(String type, @RestUploadFile(maxLength = 10 * 1024 * 1024, fileNameReg = "\\.jar$") byte[] jar) {
// //待开发
// return RetResult.success();
// }
//
// @RestMapping(name = "stop", auth = false, comment = "动态停止Servlet")
// public RetResult stopServlet(String type) {
// //待开发
// return RetResult.success();
// }
}

View File

@@ -0,0 +1,26 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.boot.watch;
import javax.annotation.Resource;
import org.redkale.boot.Application;
import org.redkale.net.TransportFactory;
import org.redkale.net.http.RestService;
/**
*
* @author zhangjx
*/
@RestService(name = "source", catalog = "watch", repair = false)
public class SourceWatchService extends AbstractWatchService {
@Resource
private Application application;
@Resource
private TransportFactory transportFactory;
}

View File

@@ -0,0 +1,138 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.boot.watch;
import java.io.IOException;
import java.net.*;
import java.nio.channels.AsynchronousSocketChannel;
import java.util.List;
import java.util.concurrent.TimeUnit;
import javax.annotation.Resource;
import org.redkale.boot.Application;
import org.redkale.net.*;
import org.redkale.net.http.*;
import org.redkale.net.sncp.*;
import org.redkale.service.*;
import org.redkale.util.*;
import org.redkale.util.AnyValue.DefaultAnyValue;
/**
*
* @author zhangjx
*/
@RestService(name = "transport", catalog = "watch", repair = false)
public class TransportWatchService extends AbstractWatchService {
@Comment("不存在的Group节点")
public static final int RET_TRANSPORT_GROUP_NOT_EXISTS = 1606_0001;
@Comment("非法的Node节点IP地址")
public static final int RET_TRANSPORT_ADDR_ILLEGAL = 1606_0002;
@Comment("Node节点IP地址已存在")
public static final int RET_TRANSPORT_ADDR_EXISTS = 1606_0003;
@Resource
private Application application;
@Resource
private TransportFactory transportFactory;
@RestMapping(name = "listnodes", auth = false, comment = "获取所有Node节点")
public List<TransportGroupInfo> listNodes() {
return transportFactory.getGroupInfos();
}
@RestMapping(name = "addnode", auth = false, comment = "动态增加指定Group的Node节点")
public RetResult addNode(@RestParam(name = "group", comment = "Group节点名") final String group,
@RestParam(name = "addr", comment = "节点IP") final String addr,
@RestParam(name = "port", comment = "节点端口") final int port) throws IOException {
InetSocketAddress address;
try {
address = new InetSocketAddress(addr, port);
AsynchronousSocketChannel channel = AsynchronousSocketChannel.open();
channel.connect(address).get(2, TimeUnit.SECONDS); //连接超时2秒
channel.close();
} catch (Exception e) {
return new RetResult(RET_TRANSPORT_ADDR_ILLEGAL, "InetSocketAddress(addr=" + addr + ", port=" + port + ") is illegal or cannot connect");
}
if (transportFactory.findGroupName(address) != null) return new RetResult(RET_TRANSPORT_ADDR_ILLEGAL, "InetSocketAddress(addr=" + addr + ", port=" + port + ") is exists");
synchronized (this) {
if (transportFactory.findGroupInfo(group) == null) {
return new RetResult(RET_TRANSPORT_GROUP_NOT_EXISTS, "not found group (" + group + ")");
}
transportFactory.addGroupInfo(group, address);
for (Service service : transportFactory.getServices()) {
if (!Sncp.isSncpDyn(service)) continue;
SncpClient client = Sncp.getSncpClient(service);
if (Sncp.isRemote(service)) {
if (client.getRemoteGroups() != null && client.getRemoteGroups().contains(group)) {
client.getRemoteGroupTransport().addRemoteAddresses(address);
}
} else {
if (group.equals(client.getSameGroup())) {
client.getSameGroupTransport().addRemoteAddresses(address);
}
if (client.getDiffGroups() != null && client.getDiffGroups().contains(group)) {
for (Transport transport : client.getDiffGroupTransports()) {
transport.addRemoteAddresses(address);
}
}
}
}
DefaultAnyValue node = DefaultAnyValue.create("addr", addr).addValue("port", port);
for (AnyValue groupconf : application.getAppConfig().getAnyValue("resources").getAnyValues("group")) {
if (group.equals(groupconf.getValue("name"))) {
((DefaultAnyValue) groupconf).addValue("node", node);
break;
}
}
application.restoreConfig();
}
return RetResult.success();
}
@RestMapping(name = "removenode", auth = false, comment = "动态删除指定Group的Node节点")
public RetResult removeNode(@RestParam(name = "group", comment = "Group节点名") final String group,
@RestParam(name = "addr", comment = "节点IP") final String addr,
@RestParam(name = "port", comment = "节点端口") final int port) throws IOException {
if (group == null) return new RetResult(RET_TRANSPORT_GROUP_NOT_EXISTS, "not found group (" + group + ")");
final InetSocketAddress address = new InetSocketAddress(addr, port);
if (!group.equals(transportFactory.findGroupName(address))) return new RetResult(RET_TRANSPORT_ADDR_ILLEGAL, "InetSocketAddress(addr=" + addr + ", port=" + port + ") not belong to group(" + group + ")");
synchronized (this) {
if (transportFactory.findGroupInfo(group) == null) {
return new RetResult(RET_TRANSPORT_GROUP_NOT_EXISTS, "not found group (" + group + ")");
}
transportFactory.removeGroupInfo(group, address);
for (Service service : transportFactory.getServices()) {
if (!Sncp.isSncpDyn(service)) continue;
SncpClient client = Sncp.getSncpClient(service);
if (Sncp.isRemote(service)) {
if (client.getRemoteGroups() != null && client.getRemoteGroups().contains(group)) {
client.getRemoteGroupTransport().removeRemoteAddresses(address);
}
} else {
if (group.equals(client.getSameGroup())) {
client.getSameGroupTransport().removeRemoteAddresses(address);
}
if (client.getDiffGroups() != null && client.getDiffGroups().contains(group)) {
for (Transport transport : client.getDiffGroupTransports()) {
transport.removeRemoteAddresses(address);
}
}
}
}
for (AnyValue groupconf : application.getAppConfig().getAnyValue("resources").getAnyValues("group")) {
if (group.equals(groupconf.getValue("name"))) {
((DefaultAnyValue) groupconf).removeValue("node", DefaultAnyValue.create("addr", addr).addValue("port", port));
break;
}
}
application.restoreConfig();
}
return RetResult.success();
}
}

View File

@@ -44,6 +44,11 @@ public final class CollectionDecoder<T> implements Decodeable<Reader, Collection
this.creator = factory.loadCreator((Class) pt.getRawType()); this.creator = factory.loadCreator((Class) pt.getRawType());
factory.register(type, this); factory.register(type, this);
this.decoder = factory.loadDecoder(this.componentType); this.decoder = factory.loadDecoder(this.componentType);
} else if(factory.isReversible()){
this.componentType = Object.class;
this.creator = factory.loadCreator(Object.class);
factory.register(type, this);
this.decoder = factory.loadDecoder(this.componentType);
} else { } else {
throw new ConvertException("collectiondecoder not support the type (" + type + ")"); throw new ConvertException("collectiondecoder not support the type (" + type + ")");
} }

View File

@@ -5,6 +5,10 @@
*/ */
package org.redkale.convert; package org.redkale.convert;
import java.lang.reflect.Type;
import java.nio.ByteBuffer;
import java.util.function.Supplier;
/** /**
* 序列化/反序列化操作类 * 序列化/反序列化操作类
* *
@@ -26,4 +30,14 @@ public abstract class Convert<R extends Reader, W extends Writer> {
public ConvertFactory<R, W> getFactory() { public ConvertFactory<R, W> getFactory() {
return this.factory; return this.factory;
} }
public abstract boolean isBinary();
public abstract <T> T convertFrom(final Type type, final ByteBuffer... buffers);
public abstract <T> T convertFrom(final Type type, final ConvertMask mask, final ByteBuffer... buffers);
public abstract ByteBuffer[] convertTo(final Supplier<ByteBuffer> supplier, final Object value);
public abstract ByteBuffer[] convertTo(final Supplier<ByteBuffer> supplier, final Type type, final Object value);
} }

View File

@@ -21,7 +21,7 @@ import static java.lang.annotation.RetentionPolicy.*;
@Documented @Documented
@Target({METHOD, FIELD}) @Target({METHOD, FIELD})
@Retention(RUNTIME) @Retention(RUNTIME)
@Repeatable(ConvertColumns.class) @Repeatable(ConvertColumn.ConvertColumns.class)
public @interface ConvertColumn { public @interface ConvertColumn {
/** /**
@@ -31,6 +31,13 @@ public @interface ConvertColumn {
*/ */
String name() default ""; String name() default "";
/**
* 给字段取个序号ID值小靠前
*
* @return 字段排序ID
*/
int index() default 0;
/** /**
* 解析/序列化时是否屏蔽该字段 * 解析/序列化时是否屏蔽该字段
* *
@@ -44,4 +51,21 @@ public @interface ConvertColumn {
* @return JSON or BSON or ALL * @return JSON or BSON or ALL
*/ */
ConvertType type() default ConvertType.ALL; ConvertType type() default ConvertType.ALL;
/**
* ConvertColumn 的多用类
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*/
@Inherited
@Documented
@Target({METHOD, FIELD})
@Retention(RUNTIME)
public static @interface ConvertColumns {
ConvertColumn[] value();
}
} }

View File

@@ -8,11 +8,15 @@ package org.redkale.convert;
/** /**
* ConvertColumn 对应的实体类 * ConvertColumn 对应的实体类
* *
* <p> 详情见: https://redkale.org * <p>
* 详情见: https://redkale.org
*
* @author zhangjx * @author zhangjx
*/ */
public final class ConvertColumnEntry { public final class ConvertColumnEntry {
private int index;
private String name = ""; private String name = "";
private boolean ignore; private boolean ignore;
@@ -25,6 +29,7 @@ public final class ConvertColumnEntry {
public ConvertColumnEntry(ConvertColumn column) { public ConvertColumnEntry(ConvertColumn column) {
if (column == null) return; if (column == null) return;
this.name = column.name(); this.name = column.name();
this.index = column.index();
this.ignore = column.ignore(); this.ignore = column.ignore();
this.convertType = column.type(); this.convertType = column.type();
} }
@@ -45,6 +50,13 @@ public final class ConvertColumnEntry {
this.convertType = convertType; this.convertType = convertType;
} }
public ConvertColumnEntry(String name, int index, boolean ignore, ConvertType convertType) {
this.name = name;
this.index = index;
this.ignore = ignore;
this.convertType = convertType;
}
public String name() { public String name() {
return name == null ? "" : name; return name == null ? "" : name;
} }
@@ -69,4 +81,12 @@ public final class ConvertColumnEntry {
this.convertType = convertType; this.convertType = convertType;
} }
public int getIndex() {
return index;
}
public void setIndex(int index) {
this.index = index;
}
} }

View File

@@ -1,25 +0,0 @@
/*
* 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.*;
/**
* ConvertColumn 的多用类
*
* <p> 详情见: https://redkale.org
* @author zhangjx
*/
@Inherited
@Documented
@Target({METHOD, FIELD})
@Retention(RUNTIME)
public @interface ConvertColumns {
ConvertColumn[] value();
}

View File

@@ -5,13 +5,16 @@
*/ */
package org.redkale.convert; package org.redkale.convert;
import java.io.File;
import java.lang.reflect.*; import java.lang.reflect.*;
import java.math.BigInteger; import java.math.BigInteger;
import java.net.*; import java.net.*;
import java.nio.ByteBuffer;
import java.nio.channels.CompletionHandler; import java.nio.channels.CompletionHandler;
import java.util.*; import java.util.*;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import java.util.stream.Stream;
import org.redkale.convert.ext.InetAddressSimpledCoder.InetSocketAddressSimpledCoder; import org.redkale.convert.ext.InetAddressSimpledCoder.InetSocketAddressSimpledCoder;
import org.redkale.convert.ext.*; import org.redkale.convert.ext.*;
import org.redkale.util.*; import org.redkale.util.*;
@@ -91,11 +94,13 @@ public abstract class ConvertFactory<R extends Reader, W extends Writer> {
this.register(Class.class, TypeSimpledCoder.instance); this.register(Class.class, TypeSimpledCoder.instance);
this.register(InetSocketAddress.class, InetSocketAddressSimpledCoder.instance); this.register(InetSocketAddress.class, InetSocketAddressSimpledCoder.instance);
this.register(Pattern.class, PatternSimpledCoder.instance); this.register(Pattern.class, PatternSimpledCoder.instance);
this.register(File.class, FileSimpledCoder.instance);
this.register(CompletionHandler.class, CompletionHandlerSimpledCoder.instance); this.register(CompletionHandler.class, CompletionHandlerSimpledCoder.instance);
this.register(AsyncHandler.class, AsyncHandlerSimpledCoder.instance); this.register(AsyncHandler.class, AsyncHandlerSimpledCoder.instance);
this.register(URL.class, URLSimpledCoder.instance); this.register(URL.class, URLSimpledCoder.instance);
this.register(URI.class, URISimpledCoder.instance); this.register(URI.class, URISimpledCoder.instance);
//--------------------------------------------------------- //---------------------------------------------------------
this.register(ByteBuffer.class, ByteBufferSimpledCoder.instance);
this.register(boolean[].class, BoolArraySimpledCoder.instance); this.register(boolean[].class, BoolArraySimpledCoder.instance);
this.register(byte[].class, ByteArraySimpledCoder.instance); this.register(byte[].class, ByteArraySimpledCoder.instance);
this.register(short[].class, ShortArraySimpledCoder.instance); this.register(short[].class, ShortArraySimpledCoder.instance);
@@ -124,7 +129,7 @@ public abstract class ConvertFactory<R extends Reader, W extends Writer> {
public abstract ConvertType getConvertType(); public abstract ConvertType getConvertType();
public abstract boolean isReversible(); public abstract boolean isReversible(); //是否可逆的
public abstract ConvertFactory createChild(); public abstract ConvertFactory createChild();
@@ -184,15 +189,71 @@ public abstract class ConvertFactory<R extends Reader, W extends Writer> {
} }
final String getEntityAlias(Class clazz) { final String getEntityAlias(Class clazz) {
if (clazz == String.class) return "A";
if (clazz == int.class) return "I";
if (clazz == Integer.class) return "i";
if (clazz == long.class) return "J";
if (clazz == Long.class) return "j";
if (clazz == byte.class) return "B";
if (clazz == Byte.class) return "b";
if (clazz == boolean.class) return "Z";
if (clazz == Boolean.class) return "z";
if (clazz == short.class) return "S";
if (clazz == Short.class) return "s";
if (clazz == char.class) return "C";
if (clazz == Character.class) return "c";
if (clazz == float.class) return "F";
if (clazz == Float.class) return "f";
if (clazz == double.class) return "D";
if (clazz == Double.class) return "d";
if (clazz == String[].class) return "[A";
if (clazz == int[].class) return "[I";
if (clazz == long[].class) return "[J";
if (clazz == byte[].class) return "[B";
if (clazz == boolean[].class) return "[Z";
if (clazz == short[].class) return "[S";
if (clazz == char[].class) return "[C";
if (clazz == float[].class) return "[F";
if (clazz == double[].class) return "[D";
ConvertEntity ce = (ConvertEntity) clazz.getAnnotation(ConvertEntity.class); ConvertEntity ce = (ConvertEntity) clazz.getAnnotation(ConvertEntity.class);
if (ce != null && findEntityAlias(ce.value()) == null) entitys.put(ce.value(), clazz); if (ce != null && findEntityAlias(ce.value()) == null) entitys.put(ce.value(), clazz);
return ce == null ? clazz.getName() : ce.value(); return ce == null ? clazz.getName() : ce.value();
} }
final Class getEntityAlias(String name) { final Class getEntityAlias(String name) {
if ("A".equals(name)) return String.class;
if ("I".equals(name)) return int.class;
if ("i".equals(name)) return Integer.class;
if ("J".equals(name)) return long.class;
if ("j".equals(name)) return Long.class;
if ("B".equals(name)) return byte.class;
if ("b".equals(name)) return Byte.class;
if ("Z".equals(name)) return boolean.class;
if ("z".equals(name)) return Boolean.class;
if ("S".equals(name)) return short.class;
if ("s".equals(name)) return Short.class;
if ("C".equals(name)) return char.class;
if ("c".equals(name)) return Character.class;
if ("F".equals(name)) return float.class;
if ("f".equals(name)) return Float.class;
if ("D".equals(name)) return double.class;
if ("d".equals(name)) return Double.class;
if ("[A".equals(name)) return String[].class;
if ("[I".equals(name)) return int[].class;
if ("[J".equals(name)) return long[].class;
if ("[B".equals(name)) return byte[].class;
if ("[Z".equals(name)) return boolean[].class;
if ("[S".equals(name)) return short[].class;
if ("[C".equals(name)) return char[].class;
if ("[F".equals(name)) return float[].class;
if ("[D".equals(name)) return double[].class;
Class clazz = findEntityAlias(name); Class clazz = findEntityAlias(name);
try { try {
return clazz == null ? Class.forName(name) : clazz; return clazz == null ? Thread.currentThread().getContextClassLoader().loadClass(name) : clazz;
} catch (Exception ex) { } catch (Exception ex) {
throw new ConvertException("convert entity is " + name, ex); throw new ConvertException("convert entity is " + name, ex);
} }
@@ -393,6 +454,8 @@ public abstract class ConvertFactory<R extends Reader, W extends Writer> {
decoder = new ArrayDecoder(this, type); decoder = new ArrayDecoder(this, type);
} else if (Collection.class.isAssignableFrom(clazz)) { } else if (Collection.class.isAssignableFrom(clazz)) {
decoder = new CollectionDecoder(this, type); decoder = new CollectionDecoder(this, type);
} else if (Stream.class.isAssignableFrom(clazz)) {
decoder = new StreamDecoder(this, type);
} else if (Map.class.isAssignableFrom(clazz)) { } else if (Map.class.isAssignableFrom(clazz)) {
decoder = new MapDecoder(this, type); decoder = new MapDecoder(this, type);
} else if (clazz == Object.class) { } else if (clazz == Object.class) {
@@ -476,6 +539,8 @@ public abstract class ConvertFactory<R extends Reader, W extends Writer> {
encoder = new ArrayEncoder(this, type); encoder = new ArrayEncoder(this, type);
} else if (Collection.class.isAssignableFrom(clazz)) { } else if (Collection.class.isAssignableFrom(clazz)) {
encoder = new CollectionEncoder(this, type); encoder = new CollectionEncoder(this, type);
} else if (Stream.class.isAssignableFrom(clazz)) {
encoder = new StreamEncoder(this, type);
} else if (Map.class.isAssignableFrom(clazz)) { } else if (Map.class.isAssignableFrom(clazz)) {
encoder = new MapEncoder(this, type); encoder = new MapEncoder(this, type);
} else if (clazz == Object.class) { } else if (clazz == Object.class) {

View File

@@ -0,0 +1,25 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.convert;
/**
* Mask接口
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*/
public interface ConvertMask {
default byte mask(byte value) {
return value;
}
default byte unmask(byte value) {
return value;
}
}

View File

@@ -22,6 +22,8 @@ import org.redkale.util.Attribute;
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public final class DeMember<R extends Reader, T, F> implements Comparable<DeMember<R, T, F>> { public final class DeMember<R extends Reader, T, F> implements Comparable<DeMember<R, T, F>> {
protected int index;
protected final Attribute<T, F> attribute; protected final Attribute<T, F> attribute;
protected Decodeable<R, F> decoder; protected Decodeable<R, F> decoder;
@@ -68,9 +70,14 @@ public final class DeMember<R extends Reader, T, F> implements Comparable<DeMemb
return this.attribute; return this.attribute;
} }
public int getIndex() {
return this.index;
}
@Override @Override
public final int compareTo(DeMember<R, T, F> o) { public final int compareTo(DeMember<R, T, F> o) {
if (o == null) return 1; if (o == null) return -1;
if (this.index != o.index) return (this.index == 0 ? Integer.MAX_VALUE : this.index) - (o.index == 0 ? Integer.MAX_VALUE : o.index);
return this.attribute.field().compareTo(o.attribute.field()); return this.attribute.field().compareTo(o.attribute.field());
} }

View File

@@ -31,6 +31,8 @@ public final class EnMember<W extends Writer, T, F> implements Comparable<EnMemb
//final boolean isnumber; //final boolean isnumber;
final boolean isbool; final boolean isbool;
protected int index;
public EnMember(Attribute<T, F> attribute, Encodeable<W, F> encoder) { public EnMember(Attribute<T, F> attribute, Encodeable<W, F> encoder) {
this.attribute = attribute; this.attribute = attribute;
this.encoder = encoder; this.encoder = encoder;
@@ -61,9 +63,14 @@ public final class EnMember<W extends Writer, T, F> implements Comparable<EnMemb
return attribute.field().equals(name); return attribute.field().equals(name);
} }
public int getIndex() {
return this.index;
}
@Override @Override
public final int compareTo(EnMember<W, T, F> o) { public final int compareTo(EnMember<W, T, F> o) {
if (o == null) return 1; if (o == null) return -1;
if (this.index != o.index) return (this.index == 0 ? Integer.MAX_VALUE : this.index) - (o.index == 0 ? Integer.MAX_VALUE : o.index);
return this.attribute.field().compareTo(o.attribute.field()); return this.attribute.field().compareTo(o.attribute.field());
} }

View File

@@ -78,7 +78,9 @@ public final class ObjectDecoder<R extends Reader, T> implements Decodeable<R, T
ref = factory.findRef(field); ref = factory.findRef(field);
if (ref != null && ref.ignore()) continue; if (ref != null && ref.ignore()) continue;
Type t = TypeToken.createClassType(field.getGenericType(), this.type); Type t = TypeToken.createClassType(field.getGenericType(), this.type);
list.add(new DeMember(ObjectEncoder.createAttribute(factory, clazz, field, null, null), factory.loadDecoder(t))); DeMember member = new DeMember(ObjectEncoder.createAttribute(factory, clazz, field, null, null), factory.loadDecoder(t));
if (ref != null) member.index = ref.getIndex();
list.add(member);
} }
final boolean reversible = factory.isReversible(); final boolean reversible = factory.isReversible();
for (final Method method : clazz.getMethods()) { for (final Method method : clazz.getMethods()) {
@@ -101,7 +103,9 @@ public final class ObjectDecoder<R extends Reader, T> implements Decodeable<R, T
ref = factory.findRef(method); ref = factory.findRef(method);
if (ref != null && ref.ignore()) continue; if (ref != null && ref.ignore()) continue;
Type t = TypeToken.createClassType(method.getGenericParameterTypes()[0], this.type); Type t = TypeToken.createClassType(method.getGenericParameterTypes()[0], this.type);
list.add(new DeMember(ObjectEncoder.createAttribute(factory, clazz, null, null, method), factory.loadDecoder(t))); DeMember member = new DeMember(ObjectEncoder.createAttribute(factory, clazz, null, null, method), factory.loadDecoder(t));
if (ref != null) member.index = ref.getIndex();
list.add(member);
} }
if (cps != null) { //可能存在某些构造函数中的字段名不存在setter方法 if (cps != null) { //可能存在某些构造函数中的字段名不存在setter方法
for (final String constructorField : cps) { for (final String constructorField : cps) {

View File

@@ -69,7 +69,9 @@ public final class ObjectEncoder<W extends Writer, T> implements Encodeable<W, T
ref = factory.findRef(field); ref = factory.findRef(field);
if (ref != null && ref.ignore()) continue; if (ref != null && ref.ignore()) continue;
Type t = TypeToken.createClassType(field.getGenericType(), this.type); Type t = TypeToken.createClassType(field.getGenericType(), this.type);
list.add(new EnMember(createAttribute(factory, clazz, field, null, null), factory.loadEncoder(t))); EnMember member = new EnMember(createAttribute(factory, clazz, field, null, null), factory.loadEncoder(t));
if (ref != null) member.index = ref.getIndex();
list.add(member);
} }
for (final Method method : clazz.getMethods()) { for (final Method method : clazz.getMethods()) {
if (Modifier.isStatic(method.getModifiers())) continue; if (Modifier.isStatic(method.getModifiers())) continue;
@@ -92,7 +94,9 @@ public final class ObjectEncoder<W extends Writer, T> implements Encodeable<W, T
ref = factory.findRef(method); ref = factory.findRef(method);
if (ref != null && ref.ignore()) continue; if (ref != null && ref.ignore()) continue;
Type t = TypeToken.createClassType(method.getGenericReturnType(), this.type); Type t = TypeToken.createClassType(method.getGenericReturnType(), this.type);
list.add(new EnMember(createAttribute(factory, clazz, null, method, null), factory.loadEncoder(t))); EnMember member = new EnMember(createAttribute(factory, clazz, null, method, null), factory.loadEncoder(t));
if (ref != null) member.index = ref.getIndex();
list.add(member);
} }
this.members = list.toArray(new EnMember[list.size()]); this.members = list.toArray(new EnMember[list.size()]);
Arrays.sort(this.members); Arrays.sort(this.members);

View File

@@ -0,0 +1,94 @@
/*
* 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 org.redkale.util.Creator;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.*;
import java.util.stream.Stream;
/**
* Stream的反序列化操作类 <br>
* 支持一定程度的泛型。 <br>
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
* @param <T> 反解析的集合元素类型
*/
@SuppressWarnings("unchecked")
public final class StreamDecoder<T> implements Decodeable<Reader, Stream<T>> {
private final Type type;
private final Type componentType;
protected Creator<Stream<T>> creator;
protected final Decodeable<Reader, T> decoder;
private boolean inited = false;
private final Object lock = new Object();
public StreamDecoder(final ConvertFactory factory, final Type type) {
this.type = type;
try {
if (type instanceof ParameterizedType) {
final ParameterizedType pt = (ParameterizedType) type;
this.componentType = pt.getActualTypeArguments()[0];
this.creator = factory.loadCreator((Class) pt.getRawType());
factory.register(type, this);
this.decoder = factory.loadDecoder(this.componentType);
} else {
throw new ConvertException("StreamDecoder not support the type (" + type + ")");
}
} finally {
inited = true;
synchronized (lock) {
lock.notifyAll();
}
}
}
@Override
public Stream<T> convertFrom(Reader in) {
final int len = in.readArrayB();
if (len == Reader.SIGN_NULL) return null;
if (this.decoder == null) {
if (!this.inited) {
synchronized (lock) {
try {
lock.wait();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
final Decodeable<Reader, T> localdecoder = this.decoder;
final List<T> result = new ArrayList();
if (len == Reader.SIGN_NOLENGTH) {
while (in.hasNext()) {
result.add(localdecoder.convertFrom(in));
}
} else {
for (int i = 0; i < len; i++) {
result.add(localdecoder.convertFrom(in));
}
}
in.readArrayE();
return result.stream();
}
@Override
public Type getType() {
return type;
}
}

View File

@@ -0,0 +1,90 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.convert;
import java.lang.reflect.*;
import java.util.stream.Stream;
/**
* Stream的序列化操作类 <br>
* 支持一定程度的泛型。 <br>
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
* @param <T> 序列化的集合元素类型
*/
@SuppressWarnings("unchecked")
public final class StreamEncoder<T> implements Encodeable<Writer, Stream<T>> {
private final Type type;
private final Encodeable<Writer, Object> encoder;
private boolean inited = false;
private final Object lock = new Object();
public StreamEncoder(final ConvertFactory factory, final Type type) {
this.type = type;
try {
if (type instanceof ParameterizedType) {
Type t = ((ParameterizedType) type).getActualTypeArguments()[0];
if (t instanceof TypeVariable) {
this.encoder = factory.getAnyEncoder();
} else {
this.encoder = factory.loadEncoder(t);
}
} else {
this.encoder = factory.getAnyEncoder();
}
} finally {
inited = true;
synchronized (lock) {
lock.notifyAll();
}
}
}
@Override
public void convertTo(Writer out, Stream<T> value) {
if (value == null) {
out.writeNull();
return;
}
Object[] array = value.toArray();
if (array.length == 0) {
out.writeArrayB(0);
out.writeArrayE();
return;
}
if (this.encoder == null) {
if (!this.inited) {
synchronized (lock) {
try {
lock.wait();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
out.writeArrayB(array.length);
boolean first = true;
for (Object v : array) {
if (!first) out.writeArrayMark();
encoder.convertTo(out, v);
if (first) first = false;
}
out.writeArrayE();
}
@Override
public Type getType() {
return type;
}
}

View File

@@ -25,7 +25,10 @@ public class BsonByteBufferReader extends BsonReader {
private ByteBuffer currentBuffer; private ByteBuffer currentBuffer;
protected BsonByteBufferReader(ByteBuffer... buffers) { protected ConvertMask mask;
protected BsonByteBufferReader(ConvertMask mask, ByteBuffer... buffers) {
this.mask = mask;
this.buffers = buffers; this.buffers = buffers;
if (buffers != null && buffers.length > 0) this.currentBuffer = buffers[currentIndex]; if (buffers != null && buffers.length > 0) this.currentBuffer = buffers[currentIndex];
} }
@@ -36,12 +39,13 @@ public class BsonByteBufferReader extends BsonReader {
this.currentIndex = 0; this.currentIndex = 0;
this.currentBuffer = null; this.currentBuffer = null;
this.buffers = null; this.buffers = null;
this.mask = null;
return false; return false;
} }
@Override @Override
protected byte currentByte() { protected byte currentByte() {
return currentBuffer.get(currentBuffer.position()); return mask == null ? currentBuffer.get(currentBuffer.position()) : mask.unmask(currentBuffer.get(currentBuffer.position()));
} }
/** /**
@@ -67,13 +71,13 @@ public class BsonByteBufferReader extends BsonReader {
public byte readByte() { public byte readByte() {
if (this.currentBuffer.hasRemaining()) { if (this.currentBuffer.hasRemaining()) {
this.position++; this.position++;
return this.currentBuffer.get(); return mask == null ? this.currentBuffer.get() : mask.unmask(this.currentBuffer.get());
} }
for (;;) { for (;;) {
this.currentBuffer = this.buffers[++this.currentIndex]; this.currentBuffer = this.buffers[++this.currentIndex];
if (this.currentBuffer.hasRemaining()) { if (this.currentBuffer.hasRemaining()) {
this.position++; this.position++;
return this.currentBuffer.get(); return mask == null ? this.currentBuffer.get() : mask.unmask(this.currentBuffer.get());
} }
} }
} }
@@ -84,7 +88,11 @@ public class BsonByteBufferReader extends BsonReader {
int remain = this.currentBuffer.remaining(); int remain = this.currentBuffer.remaining();
if (remain >= 2) { if (remain >= 2) {
this.position += 2; this.position += 2;
return this.currentBuffer.getChar(); if (mask == null) {
return this.currentBuffer.getChar();
} else {
return (char) ((0xff00 & (mask.unmask(this.currentBuffer.get()) << 8)) | (0xff & mask.unmask(this.currentBuffer.get())));
}
} }
} }
return (char) ((0xff00 & (readByte() << 8)) | (0xff & readByte())); return (char) ((0xff00 & (readByte() << 8)) | (0xff & readByte()));
@@ -96,7 +104,11 @@ public class BsonByteBufferReader extends BsonReader {
int remain = this.currentBuffer.remaining(); int remain = this.currentBuffer.remaining();
if (remain >= 2) { if (remain >= 2) {
this.position += 2; this.position += 2;
return this.currentBuffer.getShort(); if (mask == null) {
return this.currentBuffer.getShort();
} else {
return (short) ((0xff00 & (mask.unmask(this.currentBuffer.get()) << 8)) | (0xff & mask.unmask(this.currentBuffer.get())));
}
} }
} }
return (short) ((0xff00 & (readByte() << 8)) | (0xff & readByte())); return (short) ((0xff00 & (readByte() << 8)) | (0xff & readByte()));
@@ -108,7 +120,14 @@ public class BsonByteBufferReader extends BsonReader {
int remain = this.currentBuffer.remaining(); int remain = this.currentBuffer.remaining();
if (remain >= 4) { if (remain >= 4) {
this.position += 4; this.position += 4;
return this.currentBuffer.getInt(); if (mask == null) {
return this.currentBuffer.getInt();
} else {
return ((mask.unmask(this.currentBuffer.get()) & 0xff) << 24)
| ((mask.unmask(this.currentBuffer.get()) & 0xff) << 16)
| ((mask.unmask(this.currentBuffer.get()) & 0xff) << 8)
| (mask.unmask(this.currentBuffer.get()) & 0xff);
}
} }
} }
return ((readByte() & 0xff) << 24) | ((readByte() & 0xff) << 16) | ((readByte() & 0xff) << 8) | (readByte() & 0xff); return ((readByte() & 0xff) << 24) | ((readByte() & 0xff) << 16) | ((readByte() & 0xff) << 8) | (readByte() & 0xff);
@@ -120,7 +139,18 @@ public class BsonByteBufferReader extends BsonReader {
int remain = this.currentBuffer.remaining(); int remain = this.currentBuffer.remaining();
if (remain >= 8) { if (remain >= 8) {
this.position += 8; this.position += 8;
return this.currentBuffer.getLong(); if (mask == null) {
return this.currentBuffer.getLong();
} else {
return ((((long) mask.unmask(this.currentBuffer.get()) & 0xff) << 56)
| (((long) mask.unmask(this.currentBuffer.get()) & 0xff) << 48)
| (((long) mask.unmask(this.currentBuffer.get()) & 0xff) << 40)
| (((long) mask.unmask(this.currentBuffer.get()) & 0xff) << 32)
| (((long) mask.unmask(this.currentBuffer.get()) & 0xff) << 24)
| (((long) mask.unmask(this.currentBuffer.get()) & 0xff) << 16)
| (((long) mask.unmask(this.currentBuffer.get()) & 0xff) << 8)
| (((long) mask.unmask(this.currentBuffer.get()) & 0xff)));
}
} }
} }
return ((((long) readByte() & 0xff) << 56) return ((((long) readByte() & 0xff) << 56)
@@ -150,9 +180,19 @@ public class BsonByteBufferReader extends BsonReader {
if (remain >= len) { if (remain >= len) {
this.position += len; this.position += len;
this.currentBuffer.get(bs, pos, len); this.currentBuffer.get(bs, pos, len);
if (mask != null) {
for (int i = pos, end = pos + len; i < end; i++) {
bs[i] = mask.unmask(bs[i]);
}
}
return; return;
} }
this.currentBuffer.get(bs, pos, remain); this.currentBuffer.get(bs, pos, remain);
if (mask != null) {
for (int i = pos, end = pos + remain; i < end; i++) {
bs[i] = mask.unmask(bs[i]);
}
}
this.position += remain; this.position += remain;
this.currentBuffer = this.buffers[++this.currentIndex]; this.currentBuffer = this.buffers[++this.currentIndex];
read(bs, pos + remain); read(bs, pos + remain);

View File

@@ -59,9 +59,14 @@ public final class BsonConvert extends Convert<BsonReader, BsonWriter> {
return BsonFactory.root().getConvert(); return BsonFactory.root().getConvert();
} }
@Override
public boolean isBinary() {
return true;
}
//------------------------------ reader ----------------------------------------------------------- //------------------------------ reader -----------------------------------------------------------
public BsonReader pollBsonReader(final ByteBuffer... buffers) { public BsonReader pollBsonReader(final ByteBuffer... buffers) {
return new BsonByteBufferReader(buffers); return new BsonByteBufferReader((ConvertMask) null, buffers);
} }
public BsonReader pollBsonReader(final InputStream in) { public BsonReader pollBsonReader(final InputStream in) {
@@ -114,9 +119,16 @@ public final class BsonConvert extends Convert<BsonReader, BsonWriter> {
return (T) factory.loadDecoder(type).convertFrom(new BsonStreamReader(in)); return (T) factory.loadDecoder(type).convertFrom(new BsonStreamReader(in));
} }
@Override
public <T> T convertFrom(final Type type, final ByteBuffer... buffers) { public <T> T convertFrom(final Type type, final ByteBuffer... buffers) {
if (type == null || buffers.length < 1) return null; if (type == null || buffers.length < 1) return null;
return (T) factory.loadDecoder(type).convertFrom(new BsonByteBufferReader(buffers)); return (T) factory.loadDecoder(type).convertFrom(new BsonByteBufferReader((ConvertMask) null, buffers));
}
@Override
public <T> T convertFrom(final Type type, final ConvertMask mask, final ByteBuffer... buffers) {
if (type == null || buffers.length < 1) return null;
return (T) factory.loadDecoder(type).convertFrom(new BsonByteBufferReader(mask, buffers));
} }
public <T> T convertFrom(final Type type, final BsonReader reader) { public <T> T convertFrom(final Type type, final BsonReader reader) {
@@ -164,17 +176,7 @@ public final class BsonConvert extends Convert<BsonReader, BsonWriter> {
} }
} }
public ByteBuffer[] convertTo(final Supplier<ByteBuffer> supplier, final Type type, final Object value) { @Override
if (supplier == null || type == null) return null;
BsonByteBufferWriter out = new BsonByteBufferWriter(tiny, supplier);
if (value == null) {
out.writeNull();
} else {
factory.loadEncoder(type).convertTo(out, value);
}
return out.toBuffers();
}
public ByteBuffer[] convertTo(final Supplier<ByteBuffer> supplier, final Object value) { public ByteBuffer[] convertTo(final Supplier<ByteBuffer> supplier, final Object value) {
if (supplier == null) return null; if (supplier == null) return null;
BsonByteBufferWriter out = new BsonByteBufferWriter(tiny, supplier); BsonByteBufferWriter out = new BsonByteBufferWriter(tiny, supplier);
@@ -186,6 +188,18 @@ public final class BsonConvert extends Convert<BsonReader, BsonWriter> {
return out.toBuffers(); return out.toBuffers();
} }
@Override
public ByteBuffer[] convertTo(final Supplier<ByteBuffer> supplier, final Type type, final Object value) {
if (supplier == null || type == null) return null;
BsonByteBufferWriter out = new BsonByteBufferWriter(tiny, supplier);
if (value == null) {
out.writeNull();
} else {
factory.loadEncoder(type).convertTo(out, value);
}
return out.toBuffers();
}
public void convertTo(final BsonWriter writer, final Object value) { public void convertTo(final BsonWriter writer, final Object value) {
if (value == null) { if (value == null) {
writer.writeNull(); writer.writeNull();

View File

@@ -21,6 +21,7 @@ class BsonStreamReader extends BsonByteBufferReader {
private byte currByte; private byte currByte;
protected BsonStreamReader(InputStream in) { protected BsonStreamReader(InputStream in) {
super((ConvertMask) null);
this.in = in; this.in = in;
} }

View File

@@ -0,0 +1,70 @@
/*
* 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.ext;
import java.nio.ByteBuffer;
import org.redkale.convert.Reader;
import org.redkale.convert.SimpledCoder;
import org.redkale.convert.Writer;
/**
* ByteBuffer 的SimpledCoder实现
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
* @param <R> Reader输入的子类型
* @param <W> Writer输出的子类型
*/
public final class ByteBufferSimpledCoder<R extends Reader, W extends Writer> extends SimpledCoder<R, W, ByteBuffer> {
public static final ByteBufferSimpledCoder instance = new ByteBufferSimpledCoder();
@Override
public void convertTo(W out, ByteBuffer value) {
if (value == null) {
out.writeNull();
return;
}
out.writeArrayB(value.remaining());
boolean flag = false;
for (byte v : value.array()) {
if (flag) out.writeArrayMark();
out.writeByte(v);
flag = true;
}
out.writeArrayE();
}
@Override
public ByteBuffer convertFrom(R in) {
int len = in.readArrayB();
if (len == Reader.SIGN_NULL) return null;
if (len == Reader.SIGN_NOLENGTH) {
int size = 0;
byte[] data = new byte[8];
while (in.hasNext()) {
if (size >= data.length) {
byte[] newdata = new byte[data.length + 4];
System.arraycopy(data, 0, newdata, 0, size);
data = newdata;
}
data[size++] = in.readByte();
}
in.readArrayE();
return ByteBuffer.wrap(data, 0, size);
} else {
byte[] values = new byte[len];
for (int i = 0; i < values.length; i++) {
values[i] = in.readByte();
}
in.readArrayE();
return ByteBuffer.wrap(values);
}
}
}

View File

@@ -0,0 +1,41 @@
/*
* 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.ext;
import java.io.File;
import org.redkale.convert.*;
/**
* 文件 的SimpledCoder实现
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
* @param <R> Reader输入的子类型
* @param <W> Writer输出的子类型
*/
public class FileSimpledCoder<R extends Reader, W extends Writer> extends SimpledCoder<R, W, File> {
public static final PatternSimpledCoder instance = new PatternSimpledCoder();
@Override
public void convertTo(W out, File value) {
if (value == null) {
out.writeNull();
} else {
out.writeString(value.getPath());
}
}
@Override
public File convertFrom(R in) {
String value = in.readString();
if (value == null) return null;
return new File(value);
}
}

View File

@@ -37,7 +37,7 @@ public class TypeSimpledCoder<R extends Reader, W extends Writer> extends Simple
String str = in.readSmallString(); String str = in.readSmallString();
if (str == null) return null; if (str == null) return null;
try { try {
return Class.forName(str); return Thread.currentThread().getContextClassLoader().loadClass(str);
} catch (Throwable e) { } catch (Throwable e) {
return null; return null;
} }

View File

@@ -29,7 +29,10 @@ public class JsonByteBufferReader extends JsonReader {
private ByteBuffer currentBuffer; private ByteBuffer currentBuffer;
protected JsonByteBufferReader(ByteBuffer... buffers) { protected ConvertMask mask;
protected JsonByteBufferReader(ConvertMask mask, ByteBuffer... buffers) {
this.mask = mask;
this.buffers = buffers; this.buffers = buffers;
if (buffers != null && buffers.length > 0) this.currentBuffer = buffers[currentIndex]; if (buffers != null && buffers.length > 0) this.currentBuffer = buffers[currentIndex];
} }
@@ -41,19 +44,20 @@ public class JsonByteBufferReader extends JsonReader {
this.currentChar = 0; this.currentChar = 0;
this.currentBuffer = null; this.currentBuffer = null;
this.buffers = null; this.buffers = null;
this.mask = null;
return false; return false;
} }
protected byte nextByte() { protected byte nextByte() {
if (this.currentBuffer.hasRemaining()) { if (this.currentBuffer.hasRemaining()) {
this.position++; this.position++;
return this.currentBuffer.get(); return mask == null ? this.currentBuffer.get() : mask.unmask(this.currentBuffer.get());
} }
for (;;) { for (;;) {
this.currentBuffer = this.buffers[++this.currentIndex]; this.currentBuffer = this.buffers[++this.currentIndex];
if (this.currentBuffer.hasRemaining()) { if (this.currentBuffer.hasRemaining()) {
this.position++; this.position++;
return this.currentBuffer.get(); return mask == null ? this.currentBuffer.get() : mask.unmask(this.currentBuffer.get());
} }
} }
} }
@@ -158,7 +162,7 @@ public class JsonByteBufferReader extends JsonReader {
public final boolean hasNext() { public final boolean hasNext() {
char ch = nextGoodChar(); char ch = nextGoodChar();
if (ch == ',') return true; if (ch == ',') return true;
if (ch == '}' || ch == ']') return false; if (ch == '}' || ch == ']' || ch == 0) return false;
backChar(ch); // { [ 交由 readObjectB 或 readMapB 或 readArrayB 读取 backChar(ch); // { [ 交由 readObjectB 或 readMapB 或 readArrayB 读取
return true; return true;
} }
@@ -249,6 +253,7 @@ public class JsonByteBufferReader extends JsonReader {
throw new ConvertException("illegal escape(" + c + ") (position = " + this.position + ")"); throw new ConvertException("illegal escape(" + c + ") (position = " + this.position + ")");
} }
} else if (ch == ',' || ch == ']' || ch == '}' || ch <= ' ' || ch == ':') { // ch <= ' ' 包含 0 } else if (ch == ',' || ch == ']' || ch == '}' || ch <= ' ' || ch == ':') { // ch <= ' ' 包含 0
backChar(ch);
break; break;
} else { } else {
sb.append(ch); sb.append(ch);

View File

@@ -46,9 +46,14 @@ public final class JsonConvert extends Convert<JsonReader, JsonWriter> {
return JsonFactory.root().getConvert(); return JsonFactory.root().getConvert();
} }
@Override
public boolean isBinary() {
return false;
}
//------------------------------ reader ----------------------------------------------------------- //------------------------------ reader -----------------------------------------------------------
public JsonReader pollJsonReader(final ByteBuffer... buffers) { public JsonReader pollJsonReader(final ByteBuffer... buffers) {
return new JsonByteBufferReader(buffers); return new JsonByteBufferReader((ConvertMask) null, buffers);
} }
public JsonReader pollJsonReader(final InputStream in) { public JsonReader pollJsonReader(final InputStream in) {
@@ -109,9 +114,16 @@ public final class JsonConvert extends Convert<JsonReader, JsonWriter> {
return (T) factory.loadDecoder(type).convertFrom(new JsonStreamReader(in)); return (T) factory.loadDecoder(type).convertFrom(new JsonStreamReader(in));
} }
@Override
public <T> T convertFrom(final Type type, final ByteBuffer... buffers) { public <T> T convertFrom(final Type type, final ByteBuffer... buffers) {
if (type == null || buffers == null || buffers.length == 0) return null; if (type == null || buffers == null || buffers.length == 0) return null;
return (T) factory.loadDecoder(type).convertFrom(new JsonByteBufferReader(buffers)); return (T) factory.loadDecoder(type).convertFrom(new JsonByteBufferReader((ConvertMask) null, buffers));
}
@Override
public <T> T convertFrom(final Type type, final ConvertMask mask, final ByteBuffer... buffers) {
if (type == null || buffers == null || buffers.length == 0) return null;
return (T) factory.loadDecoder(type).convertFrom(new JsonByteBufferReader(mask, buffers));
} }
public <T> T convertFrom(final Type type, final JsonReader reader) { public <T> T convertFrom(final Type type, final JsonReader reader) {
@@ -154,6 +166,7 @@ public final class JsonConvert extends Convert<JsonReader, JsonWriter> {
} }
} }
@Override
public ByteBuffer[] convertTo(final Supplier<ByteBuffer> supplier, final Object value) { public ByteBuffer[] convertTo(final Supplier<ByteBuffer> supplier, final Object value) {
if (supplier == null) return null; if (supplier == null) return null;
JsonByteBufferWriter out = new JsonByteBufferWriter(tiny, null, supplier); JsonByteBufferWriter out = new JsonByteBufferWriter(tiny, null, supplier);
@@ -165,6 +178,7 @@ public final class JsonConvert extends Convert<JsonReader, JsonWriter> {
return out.toBuffers(); return out.toBuffers();
} }
@Override
public ByteBuffer[] convertTo(final Supplier<ByteBuffer> supplier, final Type type, final Object value) { public ByteBuffer[] convertTo(final Supplier<ByteBuffer> supplier, final Type type, final Object value) {
if (supplier == null || type == null) return null; if (supplier == null || type == null) return null;
JsonByteBufferWriter out = new JsonByteBufferWriter(tiny, null, supplier); JsonByteBufferWriter out = new JsonByteBufferWriter(tiny, null, supplier);

View File

@@ -404,6 +404,7 @@ public class JsonReader extends Reader {
@Override @Override
public final DeMember readFieldName(final DeMember[] members) { public final DeMember readFieldName(final DeMember[] members) {
final String exceptedfield = this.readSmallString(); final String exceptedfield = this.readSmallString();
if(exceptedfield == null) return null;
final int len = members.length; final int len = members.length;
if (this.fieldIndex >= len) this.fieldIndex = 0; if (this.fieldIndex >= len) this.fieldIndex = 0;
for (int k = this.fieldIndex; k < len; k++) { for (int k = this.fieldIndex; k < len; k++) {

View File

@@ -19,6 +19,7 @@ class JsonStreamReader extends JsonByteBufferReader {
private InputStream in; private InputStream in;
protected JsonStreamReader(InputStream in) { protected JsonStreamReader(InputStream in) {
super((ConvertMask) null);
this.in = in; this.in = in;
} }

View File

@@ -11,6 +11,7 @@ import java.nio.ByteBuffer;
import java.nio.channels.*; import java.nio.channels.*;
import java.util.*; import java.util.*;
import java.util.concurrent.*; import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicLong;
/** /**
* *
@@ -25,6 +26,12 @@ public abstract class AsyncConnection implements AsynchronousByteChannel, AutoCl
protected Object subobject; //用于存储绑定在Connection上的对象 同attributes 只绑定单个对象时尽量使用subobject而非attributes protected Object subobject; //用于存储绑定在Connection上的对象 同attributes 只绑定单个对象时尽量使用subobject而非attributes
//关闭数
AtomicLong closedCounter = new AtomicLong();
//在线数
AtomicLong livingCounter = new AtomicLong();
public abstract boolean isTCP(); public abstract boolean isTCP();
public abstract SocketAddress getRemoteAddress(); public abstract SocketAddress getRemoteAddress();
@@ -43,7 +50,7 @@ public abstract class AsyncConnection implements AsynchronousByteChannel, AutoCl
write(srcs, 0, srcs.length, attachment, handler); write(srcs, 0, srcs.length, attachment, handler);
} }
protected abstract <A> void write(ByteBuffer[] srcs, int offset, int length, A attachment, CompletionHandler<Integer, ? super A> handler); public abstract <A> void write(ByteBuffer[] srcs, int offset, int length, A attachment, CompletionHandler<Integer, ? super A> handler);
public void dispose() {//同close 只是去掉throws IOException public void dispose() {//同close 只是去掉throws IOException
try { try {
@@ -54,6 +61,14 @@ public abstract class AsyncConnection implements AsynchronousByteChannel, AutoCl
@Override @Override
public void close() throws IOException { public void close() throws IOException {
if (closedCounter != null) {
closedCounter.incrementAndGet();
closedCounter = null;
}
if (livingCounter != null) {
livingCounter.decrementAndGet();
livingCounter = null;
}
if (attributes == null) return; if (attributes == null) return;
try { try {
for (Object obj : attributes.values()) { for (Object obj : attributes.values()) {
@@ -107,11 +122,12 @@ public abstract class AsyncConnection implements AsynchronousByteChannel, AutoCl
* @param group 连接AsynchronousChannelGroup * @param group 连接AsynchronousChannelGroup
* @param readTimeoutSecond0 读取超时秒数 * @param readTimeoutSecond0 读取超时秒数
* @param writeTimeoutSecond0 写入超时秒数 * @param writeTimeoutSecond0 写入超时秒数
*
* @return 连接 * @return 连接
* @throws java.io.IOException 异常 * @throws java.io.IOException 异常
*/ */
public static AsyncConnection create(final String protocol, final AsynchronousChannelGroup group, final SocketAddress address, public static AsyncConnection create(final String protocol, final AsynchronousChannelGroup group, final SocketAddress address,
final int readTimeoutSecond0, final int writeTimeoutSecond0) throws IOException { final int readTimeoutSecond0, final int writeTimeoutSecond0) throws IOException {
if ("TCP".equalsIgnoreCase(protocol)) { if ("TCP".equalsIgnoreCase(protocol)) {
AsynchronousSocketChannel channel = AsynchronousSocketChannel.open(group); AsynchronousSocketChannel channel = AsynchronousSocketChannel.open(group);
try { try {
@@ -143,7 +159,7 @@ public abstract class AsyncConnection implements AsynchronousByteChannel, AutoCl
private final boolean client; private final boolean client;
public BIOUDPAsyncConnection(final DatagramChannel ch, SocketAddress addr, public BIOUDPAsyncConnection(final DatagramChannel ch, SocketAddress addr,
final boolean client0, final int readTimeoutSecond0, final int writeTimeoutSecond0) { final boolean client0, final int readTimeoutSecond0, final int writeTimeoutSecond0) {
this.channel = ch; this.channel = ch;
this.client = client0; this.client = client0;
this.readTimeoutSecond = readTimeoutSecond0; this.readTimeoutSecond = readTimeoutSecond0;
@@ -186,7 +202,7 @@ public abstract class AsyncConnection implements AsynchronousByteChannel, AutoCl
} }
@Override @Override
protected <A> void write(ByteBuffer[] srcs, int offset, int length, A attachment, CompletionHandler<Integer, ? super A> handler) { public <A> void write(ByteBuffer[] srcs, int offset, int length, A attachment, CompletionHandler<Integer, ? super A> handler) {
try { try {
int rs = 0; int rs = 0;
for (int i = offset; i < offset + length; i++) { for (int i = offset; i < offset + length; i++) {
@@ -213,7 +229,7 @@ public abstract class AsyncConnection implements AsynchronousByteChannel, AutoCl
public Future<Integer> read(ByteBuffer dst) { public Future<Integer> read(ByteBuffer dst) {
try { try {
int rs = channel.read(dst); int rs = channel.read(dst);
return new SimpleFuture(rs); return CompletableFuture.completedFuture(rs);
} catch (IOException e) { } catch (IOException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
@@ -233,7 +249,7 @@ public abstract class AsyncConnection implements AsynchronousByteChannel, AutoCl
public Future<Integer> write(ByteBuffer src) { public Future<Integer> write(ByteBuffer src) {
try { try {
int rs = channel.send(src, remoteAddress); int rs = channel.send(src, remoteAddress);
return new SimpleFuture(rs); return CompletableFuture.completedFuture(rs);
} catch (IOException e) { } catch (IOException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
@@ -257,45 +273,10 @@ public abstract class AsyncConnection implements AsynchronousByteChannel, AutoCl
} }
public static AsyncConnection create(final DatagramChannel ch, SocketAddress addr, public static AsyncConnection create(final DatagramChannel ch, SocketAddress addr,
final boolean client0, final int readTimeoutSecond0, final int writeTimeoutSecond0) { final boolean client0, final int readTimeoutSecond0, final int writeTimeoutSecond0) {
return new BIOUDPAsyncConnection(ch, addr, client0, readTimeoutSecond0, writeTimeoutSecond0); return new BIOUDPAsyncConnection(ch, addr, client0, readTimeoutSecond0, writeTimeoutSecond0);
} }
private static class SimpleFuture implements Future<Integer> {
private final int rs;
public SimpleFuture(int rs) {
this.rs = rs;
}
@Override
public boolean cancel(boolean mayInterruptIfRunning) {
return true;
}
@Override
public boolean isCancelled() {
return false;
}
@Override
public boolean isDone() {
return true;
}
@Override
public Integer get() throws InterruptedException, ExecutionException {
return rs;
}
@Override
public Integer get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
return rs;
}
}
private static class BIOTCPAsyncConnection extends AsyncConnection { private static class BIOTCPAsyncConnection extends AsyncConnection {
private int readTimeoutSecond; private int readTimeoutSecond;
@@ -372,7 +353,7 @@ public abstract class AsyncConnection implements AsynchronousByteChannel, AutoCl
} }
@Override @Override
protected <A> void write(ByteBuffer[] srcs, int offset, int length, A attachment, CompletionHandler<Integer, ? super A> handler) { public <A> void write(ByteBuffer[] srcs, int offset, int length, A attachment, CompletionHandler<Integer, ? super A> handler) {
try { try {
int rs = 0; int rs = 0;
for (int i = offset; i < offset + length; i++) { for (int i = offset; i < offset + length; i++) {
@@ -398,7 +379,7 @@ public abstract class AsyncConnection implements AsynchronousByteChannel, AutoCl
public Future<Integer> read(ByteBuffer dst) { public Future<Integer> read(ByteBuffer dst) {
try { try {
int rs = readChannel.read(dst); int rs = readChannel.read(dst);
return new SimpleFuture(rs); return CompletableFuture.completedFuture(rs);
} catch (IOException e) { } catch (IOException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
@@ -418,7 +399,7 @@ public abstract class AsyncConnection implements AsynchronousByteChannel, AutoCl
public Future<Integer> write(ByteBuffer src) { public Future<Integer> write(ByteBuffer src) {
try { try {
int rs = writeChannel.write(src); int rs = writeChannel.write(src);
return new SimpleFuture(rs); return CompletableFuture.completedFuture(rs);
} catch (IOException e) { } catch (IOException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
@@ -440,6 +421,7 @@ public abstract class AsyncConnection implements AsynchronousByteChannel, AutoCl
* 通常用于 ssl socket * 通常用于 ssl socket
* *
* @param socket Socket对象 * @param socket Socket对象
*
* @return 连接对象 * @return 连接对象
*/ */
public static AsyncConnection create(final Socket socket) { public static AsyncConnection create(final Socket socket) {

View File

@@ -14,7 +14,6 @@ import java.util.logging.*;
import org.redkale.convert.bson.*; import org.redkale.convert.bson.*;
import org.redkale.convert.json.*; import org.redkale.convert.json.*;
import org.redkale.util.*; import org.redkale.util.*;
import org.redkale.watch.*;
/** /**
* 服务器上下文对象 * 服务器上下文对象
@@ -70,12 +69,8 @@ public class Context {
//JSON操作工厂 //JSON操作工厂
protected final JsonFactory jsonFactory; protected final JsonFactory jsonFactory;
//监控对象
protected final WatchFactory watch;
public Context(long serverStartTime, Logger logger, ExecutorService executor, int bufferCapacity, ObjectPool<ByteBuffer> bufferPool, ObjectPool<Response> responsePool, public Context(long serverStartTime, Logger logger, ExecutorService executor, int bufferCapacity, ObjectPool<ByteBuffer> bufferPool, ObjectPool<Response> responsePool,
final int maxbody, Charset charset, InetSocketAddress address, final PrepareServlet prepare, final WatchFactory watch, final int maxbody, Charset charset, InetSocketAddress address, final PrepareServlet prepare, final int readTimeoutSecond, final int writeTimeoutSecond) {
final int readTimeoutSecond, final int writeTimeoutSecond) {
this.serverStartTime = serverStartTime; this.serverStartTime = serverStartTime;
this.logger = logger; this.logger = logger;
this.executor = executor; this.executor = executor;
@@ -86,7 +81,6 @@ public class Context {
this.charset = UTF8.equals(charset) ? null : charset; this.charset = UTF8.equals(charset) ? null : charset;
this.address = address; this.address = address;
this.prepare = prepare; this.prepare = prepare;
this.watch = watch;
this.readTimeoutSecond = readTimeoutSecond; this.readTimeoutSecond = readTimeoutSecond;
this.writeTimeoutSecond = writeTimeoutSecond; this.writeTimeoutSecond = writeTimeoutSecond;
this.jsonFactory = JsonFactory.root(); this.jsonFactory = JsonFactory.root();
@@ -133,6 +127,13 @@ public class Context {
bufferPool.offer(buffer); bufferPool.offer(buffer);
} }
public void offerBuffer(ByteBuffer... buffers) {
if (buffers == null) return;
for (ByteBuffer buffer : buffers) {
bufferPool.offer(buffer);
}
}
public Logger getLogger() { public Logger getLogger() {
return logger; return logger;
} }

View File

@@ -0,0 +1,50 @@
/*
* 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 org.redkale.util.*;
/**
* 协议拦截器类
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
* @param <C> Context的子类型
* @param <R> Request的子类型
* @param <P> Response的子类型
*/
public abstract class Filter<C extends Context, R extends Request<C>, P extends Response<C, R>> implements Comparable {
AnyValue _conf; //当前Filter的配置
Filter<C, R, P> _next; //下一个Filter
public void init(C context, AnyValue config) {
}
public abstract void doFilter(R request, P response) throws IOException;
public void destroy(C context, AnyValue config) {
}
/**
* 值越小越靠前执行
*
* @return int
*/
public int getIndex() {
return 0;
}
@Override
public final int compareTo(Object o) {
if (!(o instanceof Filter)) return 1;
return this.getIndex() - ((Filter) o).getIndex();
}
}

View File

@@ -10,6 +10,7 @@ import java.nio.*;
import java.nio.channels.*; import java.nio.channels.*;
import java.util.*; import java.util.*;
import java.util.concurrent.atomic.*; import java.util.concurrent.atomic.*;
import java.util.function.Predicate;
import java.util.logging.*; import java.util.logging.*;
import org.redkale.util.*; import org.redkale.util.*;
@@ -42,6 +43,10 @@ public abstract class PrepareServlet<K extends Serializable, C extends Context,
private Map<K, S> mappings = new HashMap<>(); private Map<K, S> mappings = new HashMap<>();
private final List<Filter<C, R, P>> filters = new ArrayList<>();
protected Filter<C, R, P> headFilter;
protected void putServlet(S servlet) { protected void putServlet(S servlet) {
synchronized (lock1) { synchronized (lock1) {
Set<S> newservlets = new HashSet<>(servlets); Set<S> newservlets = new HashSet<>(servlets);
@@ -50,10 +55,60 @@ public abstract class PrepareServlet<K extends Serializable, C extends Context,
} }
} }
protected void putMapping(K key, S value) { protected void removeServlet(S servlet) {
synchronized (lock1) {
Set<S> newservlets = new HashSet<>(servlets);
newservlets.remove(servlet);
this.servlets = newservlets;
}
}
public boolean containsServlet(Class<? extends S> servletClass) {
synchronized (lock1) {
for (S servlet : new HashSet<>(servlets)) {
if (servlet.getClass().equals(servletClass)) return true;
}
return false;
}
}
public boolean containsServlet(String servletClassName) {
synchronized (lock1) {
for (S servlet : new HashSet<>(servlets)) {
if (servlet.getClass().getName().equals(servletClassName)) return true;
}
return false;
}
}
protected void putMapping(K key, S servlet) {
synchronized (lock2) { synchronized (lock2) {
Map<K, S> newmappings = new HashMap<>(mappings); Map<K, S> newmappings = new HashMap<>(mappings);
newmappings.put(key, value); newmappings.put(key, servlet);
this.mappings = newmappings;
}
}
protected void removeMapping(K key) {
synchronized (lock2) {
if (mappings.containsKey(key)) {
Map<K, S> newmappings = new HashMap<>(mappings);
newmappings.remove(key);
this.mappings = newmappings;
}
}
}
protected void removeMapping(S servlet) {
synchronized (lock2) {
List<K> keys = new ArrayList<>();
Map<K, S> newmappings = new HashMap<>(mappings);
for (Map.Entry<K, S> en : newmappings.entrySet()) {
if (en.getValue().equals(servlet)) {
keys.add(en.getKey());
}
}
for (K key : keys) newmappings.remove(key);
this.mappings = newmappings; this.mappings = newmappings;
} }
} }
@@ -62,6 +117,91 @@ public abstract class PrepareServlet<K extends Serializable, C extends Context,
return mappings.get(key); return mappings.get(key);
} }
@Override
public void init(C context, AnyValue config) {
synchronized (filters) {
if (!filters.isEmpty()) {
Collections.sort(filters);
for (Filter<C, R, P> filter : filters) {
filter.init(context, config);
}
this.headFilter = filters.get(0);
Filter<C, R, P> filter = this.headFilter;
for (int i = 1; i < filters.size(); i++) {
filter._next = filters.get(i);
filter = filter._next;
}
}
}
}
@Override
public void destroy(C context, AnyValue config) {
synchronized (filters) {
if (!filters.isEmpty()) {
for (Filter filter : filters) {
filter.destroy(context, config);
}
}
}
}
public void addFilter(Filter<C, R, P> filter, AnyValue conf) {
filter._conf = conf;
synchronized (filters) {
this.filters.add(filter);
Collections.sort(this.filters);
}
}
public <T extends Filter<C, R, P>> T removeFilter(Class<T> filterClass) {
return removeFilter(f -> filterClass.equals(f.getClass()));
}
public boolean containsFilter(Class<? extends Filter> filterClass) {
if (this.headFilter == null || filterClass == null) return false;
Filter filter = this.headFilter;
do {
if (filter.getClass().equals(filterClass)) return true;
} while ((filter = filter._next) != null);
return false;
}
public boolean containsFilter(String filterClassName) {
if (this.headFilter == null || filterClassName == null) return false;
Filter filter = this.headFilter;
do {
if (filter.getClass().getName().equals(filterClassName)) return true;
} while ((filter = filter._next) != null);
return false;
}
public <T extends Filter<C, R, P>> T removeFilter(Predicate<T> predicate) {
if (this.headFilter == null || predicate == null) return null;
synchronized (filters) {
Filter filter = this.headFilter;
Filter prev = null;
do {
if (predicate.test((T) filter)) break;
prev = filter;
} while ((filter = filter._next) != null);
if (filter != null) {
if (prev == null) {
this.headFilter = filter._next;
} else {
prev._next = filter._next;
}
filter._next = null;
this.filters.remove(filter);
}
return (T) filter;
}
}
public <T extends Filter<C, R, P>> List<T> getFilters() {
return (List) new ArrayList<>(filters);
}
public abstract void addServlet(S servlet, Object attachment, AnyValue conf, K... mappings); public abstract void addServlet(S servlet, Object attachment, AnyValue conf, K... mappings);
public final void prepare(final ByteBuffer buffer, final R request, final P response) throws IOException { public final void prepare(final ByteBuffer buffer, final R request, final P response) throws IOException {
@@ -74,7 +214,9 @@ public abstract class PrepareServlet<K extends Serializable, C extends Context,
} else if (rs == 0) { } else if (rs == 0) {
response.context.offerBuffer(buffer); response.context.offerBuffer(buffer);
request.prepare(); request.prepare();
this.execute(request, response); response.filter = this.headFilter;
response.servlet = this;
response.nextEvent();
} else { } else {
buffer.clear(); buffer.clear();
final AtomicInteger ai = new AtomicInteger(rs); final AtomicInteger ai = new AtomicInteger(rs);
@@ -91,7 +233,9 @@ public abstract class PrepareServlet<K extends Serializable, C extends Context,
response.context.offerBuffer(buffer); response.context.offerBuffer(buffer);
request.prepare(); request.prepare();
try { try {
execute(request, response); response.filter = PrepareServlet.this.headFilter;
response.servlet = PrepareServlet.this;
response.nextEvent();
} catch (Exception e) { } catch (Exception e) {
illRequestCounter.incrementAndGet(); illRequestCounter.incrementAndGet();
response.finish(true); response.finish(true);
@@ -119,7 +263,7 @@ public abstract class PrepareServlet<K extends Serializable, C extends Context,
servlet._conf = conf; servlet._conf = conf;
} }
public Set<S> getServlets() { public List<S> getServlets() {
return new LinkedHashSet<>(servlets); return new ArrayList<>(servlets);
} }
} }

View File

@@ -11,15 +11,27 @@ import java.nio.ByteBuffer;
import java.nio.channels.*; import java.nio.channels.*;
import java.util.*; import java.util.*;
import java.util.concurrent.*; import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicLong;
/** /**
* 协议底层Server * 协议底层Server
* *
* <p> 详情见: https://redkale.org * <p>
* 详情见: https://redkale.org
*
* @author zhangjx * @author zhangjx
*/ */
public abstract class ProtocolServer { public abstract class ProtocolServer {
//创建数
protected final AtomicLong createCounter = new AtomicLong();
//关闭数
protected final AtomicLong closedCounter = new AtomicLong();
//在线数
protected final AtomicLong livingCounter = new AtomicLong();
public abstract void open() throws IOException; public abstract void open() throws IOException;
public abstract void bind(SocketAddress local, int backlog) throws IOException; public abstract void bind(SocketAddress local, int backlog) throws IOException;
@@ -34,6 +46,18 @@ public abstract class ProtocolServer {
public abstract AsynchronousChannelGroup getChannelGroup(); public abstract AsynchronousChannelGroup getChannelGroup();
public long getCreateCount() {
return createCounter.longValue();
}
public long getClosedCount() {
return closedCounter.longValue();
}
public long getLivingCount() {
return livingCounter.longValue();
}
//--------------------------------------------------------------------- //---------------------------------------------------------------------
public static ProtocolServer create(String protocol, Context context) { public static ProtocolServer create(String protocol, Context context) {
if ("TCP".equalsIgnoreCase(protocol)) return new ProtocolTCPServer(context); if ("TCP".equalsIgnoreCase(protocol)) return new ProtocolTCPServer(context);
@@ -117,6 +141,20 @@ public abstract class ProtocolServer {
return null; 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 static final class ProtocolTCPServer extends ProtocolServer {
@@ -160,7 +198,12 @@ public abstract class ProtocolServer {
@Override @Override
public void completed(final AsynchronousSocketChannel channel, Void attachment) { public void completed(final AsynchronousSocketChannel channel, Void attachment) {
serchannel.accept(null, this); serchannel.accept(null, this);
context.submitAsync(new PrepareRunner(context, AsyncConnection.create(channel, null, context.readTimeoutSecond, context.writeTimeoutSecond), null)); 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 @Override

View File

@@ -5,9 +5,11 @@
*/ */
package org.redkale.net; package org.redkale.net;
import java.io.IOException;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.nio.channels.CompletionHandler; import java.nio.channels.CompletionHandler;
import java.util.function.BiConsumer; import java.util.function.BiConsumer;
import java.util.logging.Level;
/** /**
* 协议响应对象 * 协议响应对象
@@ -34,6 +36,10 @@ public abstract class Response<C extends Context, R extends Request<C>> {
protected BiConsumer<R, Response<C, R>> recycleListener; protected BiConsumer<R, Response<C, R>> recycleListener;
protected Filter<C, R, ? extends Response<C, R>> filter;
protected Servlet<C, R, ? extends Response<C, R>> servlet;
private final CompletionHandler finishHandler = new CompletionHandler<Integer, ByteBuffer>() { private final CompletionHandler finishHandler = new CompletionHandler<Integer, ByteBuffer>() {
@Override @Override
@@ -108,12 +114,13 @@ public abstract class Response<C extends Context, R extends Request<C>> {
try { try {
recycleListener.accept(request, this); recycleListener.accept(request, this);
} catch (Exception e) { } catch (Exception e) {
System.err.println(request); context.logger.log(Level.WARNING, "Response.recycleListener error, request = " + request, e);
e.printStackTrace();
} }
recycleListener = null; recycleListener = null;
} }
this.output = null; this.output = null;
this.filter = null;
this.servlet = null;
request.recycle(); request.recycle();
if (channel != null) { if (channel != null) {
if (keepAlive) { if (keepAlive) {
@@ -140,7 +147,30 @@ public abstract class Response<C extends Context, R extends Request<C>> {
this.request.createtime = System.currentTimeMillis(); this.request.createtime = System.currentTimeMillis();
} }
public void setRecycleListener(BiConsumer<R, Response<C, R>> recycleListener) { protected void setFilter(Filter<C, R, Response<C, R>> filter) {
this.filter = filter;
}
protected void thenEvent(Servlet servlet) {
this.servlet = servlet;
}
@SuppressWarnings("unchecked")
public void nextEvent() throws IOException {
if (this.filter != null) {
Filter runner = this.filter;
this.filter = this.filter._next;
runner.doFilter(request, this);
return;
}
if (this.servlet != null) {
Servlet s = this.servlet;
this.servlet = null;
s.execute(request, this);
}
}
public void recycleListener(BiConsumer<R, Response<C, R>> recycleListener) {
this.recycleListener = recycleListener; this.recycleListener = recycleListener;
} }

View File

@@ -6,7 +6,6 @@
package org.redkale.net; package org.redkale.net;
import java.io.*; import java.io.*;
import java.lang.reflect.Method;
import java.net.*; import java.net.*;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import java.text.*; import java.text.*;
@@ -14,8 +13,7 @@ import java.util.*;
import java.util.concurrent.*; import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Logger; import java.util.logging.Logger;
import org.redkale.util.AnyValue; import org.redkale.util.*;
import org.redkale.watch.WatchFactory;
/** /**
* *
@@ -39,9 +37,6 @@ public abstract class Server<K extends Serializable, C extends Context, R extend
//服务的启动时间 //服务的启动时间
protected final long serverStartTime; protected final long serverStartTime;
//监控对象
protected final WatchFactory watch;
//服务的名称 //服务的名称
protected String name; protected String name;
@@ -93,11 +88,10 @@ public abstract class Server<K extends Serializable, C extends Context, R extend
//IO写入 的超时秒数小于1视为不设置 //IO写入 的超时秒数小于1视为不设置
protected int writeTimeoutSecond; protected int writeTimeoutSecond;
protected Server(long serverStartTime, String protocol, PrepareServlet<K, C, R, P, S> servlet, final WatchFactory watch) { protected Server(long serverStartTime, String protocol, PrepareServlet<K, C, R, P, S> servlet) {
this.serverStartTime = serverStartTime; this.serverStartTime = serverStartTime;
this.protocol = protocol; this.protocol = protocol;
this.prepare = servlet; this.prepare = servlet;
this.watch = watch;
} }
public void init(final AnyValue config) throws Exception { public void init(final AnyValue config) throws Exception {
@@ -105,11 +99,12 @@ public abstract class Server<K extends Serializable, C extends Context, R extend
this.config = config; this.config = config;
this.address = new InetSocketAddress(config.getValue("host", "0.0.0.0"), config.getIntValue("port", 80)); this.address = new InetSocketAddress(config.getValue("host", "0.0.0.0"), config.getIntValue("port", 80));
this.charset = Charset.forName(config.getValue("charset", "UTF-8")); this.charset = Charset.forName(config.getValue("charset", "UTF-8"));
this.backlog = config.getIntValue("backlog", 8 * 1024);
this.readTimeoutSecond = config.getIntValue("readTimeoutSecond", 0); this.readTimeoutSecond = config.getIntValue("readTimeoutSecond", 0);
this.writeTimeoutSecond = config.getIntValue("writeTimeoutSecond", 0); this.writeTimeoutSecond = config.getIntValue("writeTimeoutSecond", 0);
this.maxbody = config.getIntValue("maxbody", 64 * 1024); this.backlog = parseLenth(config.getValue("backlog"), 8 * 1024);
this.bufferCapacity = config.getIntValue("bufferCapacity", 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.threads = config.getIntValue("threads", Runtime.getRuntime().availableProcessors() * 16);
this.bufferPoolSize = config.getIntValue("bufferPoolSize", Runtime.getRuntime().availableProcessors() * 512); this.bufferPoolSize = config.getIntValue("bufferPoolSize", Runtime.getRuntime().availableProcessors() * 512);
this.responsePoolSize = config.getIntValue("responsePoolSize", Runtime.getRuntime().availableProcessors() * 256); this.responsePoolSize = config.getIntValue("responsePoolSize", Runtime.getRuntime().availableProcessors() * 256);
@@ -125,6 +120,24 @@ 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);
}
protected static long parseLenth(String value, long defValue) {
if (value == null) return defValue;
value = value.toUpperCase().replace("B", "");
if (value.endsWith("G")) return Long.decode(value.replace("G", "")) * 1024 * 1024 * 1024;
if (value.endsWith("M")) return Long.decode(value.replace("M", "")) * 1024 * 1024;
if (value.endsWith("K")) return Long.decode(value.replace("K", "")) * 1024;
return Long.decode(value);
}
public void destroy(final AnyValue config) throws Exception { public void destroy(final AnyValue config) throws Exception {
this.prepare.destroy(context, config); this.prepare.destroy(context, config);
} }
@@ -161,7 +174,6 @@ public abstract class Server<K extends Serializable, C extends Context, R extend
public void start() throws IOException { public void start() throws IOException {
this.context = this.createContext(); this.context = this.createContext();
this.prepare.init(this.context, config); this.prepare.init(this.context, config);
if (this.watch != null) this.watch.inject(this.prepare);
this.serverChannel = ProtocolServer.create(this.protocol, context); this.serverChannel = ProtocolServer.create(this.protocol, context);
this.serverChannel.open(); this.serverChannel.open();
if (this.serverChannel.supportedOptions().contains(StandardSocketOptions.TCP_NODELAY)) { if (this.serverChannel.supportedOptions().contains(StandardSocketOptions.TCP_NODELAY)) {
@@ -190,6 +202,76 @@ public abstract class Server<K extends Serializable, C extends Context, R extend
logger.info(this.getClass().getSimpleName() + " shutdown in " + e + " ms"); logger.info(this.getClass().getSimpleName() + " shutdown in " + e + " ms");
} }
/**
* 判断是否存在Filter
*
* @param <T> 泛型
* @param filterClass Filter类
*
* @return boolean
*/
public <T extends Filter> boolean containsFilter(Class<T> filterClass) {
return this.prepare.containsFilter(filterClass);
}
/**
* 判断是否存在Filter
*
* @param <T> 泛型
* @param filterClassName Filter类
*
* @return boolean
*/
public <T extends Filter> boolean containsFilter(String filterClassName) {
return this.prepare.containsFilter(filterClassName);
}
/**
* 判断是否存在Servlet
*
* @param servletClass Servlet类
*
* @return boolean
*/
public boolean containsServlet(Class<? extends S> servletClass) {
return this.prepare.containsServlet(servletClass);
}
/**
* 判断是否存在Servlet
*
* @param servletClassName Servlet类
*
* @return boolean
*/
public boolean containsServlet(String servletClassName) {
return this.prepare.containsServlet(servletClassName);
}
/**
* 销毁Servlet
*
* @param servlet Servlet
*/
public void destroyServlet(S servlet) {
servlet.destroy(context, this.prepare.getServletConf(servlet));
}
//创建数
public long getCreateConnectionCount() {
return serverChannel == null ? -1 : serverChannel.getCreateCount();
}
//关闭数
public long getClosedConnectionCount() {
return serverChannel == null ? -1 : serverChannel.getClosedCount();
}
//在线数
public long getLivingConnectionCount() {
return serverChannel == null ? -1 : serverChannel.getLivingCount();
}
protected Format createFormat() { protected Format createFormat() {
String sf = "0"; String sf = "0";
if (this.threads > 10) sf = "00"; if (this.threads > 10) sf = "00";
@@ -198,7 +280,7 @@ public abstract class Server<K extends Serializable, C extends Context, R extend
return new DecimalFormat(sf); return new DecimalFormat(sf);
} }
public static URL[] loadLib(final Logger logger, final String lib) throws Exception { public static URL[] loadLib(final RedkaleClassLoader classLoader, final Logger logger, final String lib) throws Exception {
if (lib == null || lib.isEmpty()) return new URL[0]; if (lib == null || lib.isEmpty()) return new URL[0];
final Set<URL> set = new HashSet<>(); final Set<URL> set = new HashSet<>();
for (String s : lib.split(";")) { for (String s : lib.split(";")) {
@@ -216,17 +298,8 @@ public abstract class Server<K extends Serializable, C extends Context, R extend
} }
} }
if (set.isEmpty()) return new URL[0]; if (set.isEmpty()) return new URL[0];
ClassLoader cl = Thread.currentThread().getContextClassLoader(); for (URL url : set) {
if (cl instanceof URLClassLoader) { classLoader.addURL(url);
URLClassLoader loader = (URLClassLoader) cl;
final Method method = URLClassLoader.class.getDeclaredMethod("addURL", URL.class);
method.setAccessible(true);
for (URL url : set) {
method.invoke(loader, url);
//if (logger != null) logger.log(Level.INFO, "Server found ClassPath({0})", url);
}
} else {
Thread.currentThread().setContextClassLoader(new URLClassLoader(set.toArray(new URL[set.size()]), cl));
} }
List<URL> list = new ArrayList<>(set); List<URL> list = new ArrayList<>(set);
Collections.sort(list, (URL o1, URL o2) -> o1.getFile().compareTo(o2.getFile())); Collections.sort(list, (URL o1, URL o2) -> o1.getFile().compareTo(o2.getFile()));

View File

@@ -11,9 +11,9 @@ import java.nio.channels.*;
import java.util.*; import java.util.*;
import java.util.concurrent.*; import java.util.concurrent.*;
import java.util.function.Supplier; import java.util.function.Supplier;
import java.util.stream.Collectors; import org.redkale.convert.*;
import org.redkale.util.ObjectPool; import org.redkale.convert.json.JsonConvert;
import org.redkale.watch.WatchFactory; import org.redkale.util.*;
/** /**
* 传输客户端 * 传输客户端
@@ -50,71 +50,79 @@ public final class Transport {
protected final String protocol; protected final String protocol;
protected final WatchFactory watch;
protected final AsynchronousChannelGroup group; protected final AsynchronousChannelGroup group;
protected final InetSocketAddress clientAddress; protected final InetSocketAddress clientAddress;
protected InetSocketAddress[] remoteAddres = new InetSocketAddress[0]; protected TransportAddress[] transportAddres = new TransportAddress[0];
protected final ObjectPool<ByteBuffer> bufferPool; protected final ObjectPool<ByteBuffer> bufferPool;
//负载均衡策略
protected final TransportStrategy strategy;
protected final ConcurrentHashMap<SocketAddress, BlockingQueue<AsyncConnection>> connPool = new ConcurrentHashMap<>(); protected final ConcurrentHashMap<SocketAddress, BlockingQueue<AsyncConnection>> connPool = new ConcurrentHashMap<>();
public Transport(String name, WatchFactory watch, String subprotocol, final ObjectPool<ByteBuffer> transportBufferPool, public Transport(String name, String subprotocol, final ObjectPool<ByteBuffer> transportBufferPool,
final AsynchronousChannelGroup transportChannelGroup, final InetSocketAddress clientAddress, final Collection<InetSocketAddress> addresses) { final AsynchronousChannelGroup transportChannelGroup, final InetSocketAddress clientAddress,
this(name, DEFAULT_PROTOCOL, watch, subprotocol, transportBufferPool, transportChannelGroup, clientAddress, addresses); final Collection<InetSocketAddress> addresses, final TransportStrategy strategy) {
this(name, DEFAULT_PROTOCOL, subprotocol, transportBufferPool, transportChannelGroup, clientAddress, addresses, strategy);
} }
public Transport(String name, String protocol, WatchFactory watch, String subprotocol, final ObjectPool<ByteBuffer> transportBufferPool, public Transport(String name, String protocol, String subprotocol, final ObjectPool<ByteBuffer> transportBufferPool,
final AsynchronousChannelGroup transportChannelGroup, final InetSocketAddress clientAddress, final Collection<InetSocketAddress> addresses) { final AsynchronousChannelGroup transportChannelGroup, final InetSocketAddress clientAddress,
final Collection<InetSocketAddress> addresses, final TransportStrategy strategy) {
this.name = name; this.name = name;
this.watch = watch;
this.subprotocol = subprotocol == null ? "" : subprotocol.trim(); this.subprotocol = subprotocol == null ? "" : subprotocol.trim();
this.protocol = protocol; this.protocol = protocol;
this.tcp = "TCP".equalsIgnoreCase(protocol); this.tcp = "TCP".equalsIgnoreCase(protocol);
this.group = transportChannelGroup; this.group = transportChannelGroup;
this.bufferPool = transportBufferPool; this.bufferPool = transportBufferPool;
this.clientAddress = clientAddress; this.clientAddress = clientAddress;
this.strategy = strategy;
updateRemoteAddresses(addresses); updateRemoteAddresses(addresses);
} }
public Transport(final Collection<Transport> transports) {
Transport first = null;
List<String> tmpgroup = new ArrayList<>();
if (transports != null) {
for (Transport t : transports) {
if (first == null) first = t;
tmpgroup.add(t.name);
}
}
if (first == null) throw new NullPointerException("Collection<Transport> is null or empty");
//必须按字母排列顺序确保相同内容的transport列表组合的name相同而不会因为list的顺序不同产生不同的name
this.name = tmpgroup.stream().sorted().collect(Collectors.joining(";"));
this.watch = first.watch;
this.subprotocol = first.subprotocol;
this.protocol = first.protocol;
this.tcp = "TCP".equalsIgnoreCase(first.protocol);
this.group = first.group;
this.bufferPool = first.bufferPool;
this.clientAddress = first.clientAddress;
Set<InetSocketAddress> addrs = new HashSet<>();
transports.forEach(t -> addrs.addAll(Arrays.asList(t.getRemoteAddresses())));
updateRemoteAddresses(addrs);
}
public final InetSocketAddress[] updateRemoteAddresses(final Collection<InetSocketAddress> addresses) { public final InetSocketAddress[] updateRemoteAddresses(final Collection<InetSocketAddress> addresses) {
InetSocketAddress[] oldAddresses = this.remoteAddres; TransportAddress[] oldAddresses = this.transportAddres;
List<InetSocketAddress> list = new ArrayList<>(); List<TransportAddress> list = new ArrayList<>();
if (addresses != null) { if (addresses != null) {
for (InetSocketAddress addr : addresses) { for (InetSocketAddress addr : addresses) {
if (clientAddress != null && clientAddress.equals(addr)) continue; if (clientAddress != null && clientAddress.equals(addr)) continue;
list.add(addr); list.add(new TransportAddress(addr));
} }
} }
this.remoteAddres = list.toArray(new InetSocketAddress[list.size()]); this.transportAddres = list.toArray(new TransportAddress[list.size()]);
return oldAddresses;
InetSocketAddress[] rs = new InetSocketAddress[oldAddresses.length];
for (int i = 0; i < rs.length; i++) {
rs[i] = oldAddresses[i].getAddress();
}
return rs;
}
public final boolean addRemoteAddresses(final InetSocketAddress addr) {
if (addr == null) return false;
synchronized (this) {
if (this.transportAddres == null) {
this.transportAddres = new TransportAddress[]{new TransportAddress(addr)};
} else {
for (TransportAddress i : this.transportAddres) {
if (addr.equals(i.address)) return false;
}
this.transportAddres = Utility.append(transportAddres, new TransportAddress(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));
}
return true;
} }
public String getName() { public String getName() {
@@ -133,13 +141,25 @@ public final class Transport {
return clientAddress; return clientAddress;
} }
public TransportAddress[] getTransportAddresses() {
return transportAddres;
}
public InetSocketAddress[] getRemoteAddresses() { public InetSocketAddress[] getRemoteAddresses() {
return remoteAddres; InetSocketAddress[] rs = new InetSocketAddress[transportAddres.length];
for (int i = 0; i < rs.length; i++) {
rs[i] = transportAddres[i].getAddress();
}
return rs;
}
public ConcurrentHashMap<SocketAddress, BlockingQueue<AsyncConnection>> getAsyncConnectionPool() {
return connPool;
} }
@Override @Override
public String toString() { public String toString() {
return Transport.class.getSimpleName() + "{name = " + name + ", protocol = " + protocol + ", clientAddress = " + clientAddress + ", remoteAddres = " + Arrays.toString(remoteAddres) + "}"; return Transport.class.getSimpleName() + "{name = " + name + ", protocol = " + protocol + ", clientAddress = " + clientAddress + ", remoteAddres = " + Arrays.toString(transportAddres) + "}";
} }
public ByteBuffer pollBuffer() { public ByteBuffer pollBuffer() {
@@ -163,32 +183,57 @@ public final class Transport {
} }
public AsyncConnection pollConnection(SocketAddress addr) { public AsyncConnection pollConnection(SocketAddress addr) {
if (addr == null && remoteAddres.length == 1) addr = remoteAddres[0]; 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; final boolean rand = addr == null;
if (rand && remoteAddres.length < 1) throw new RuntimeException("Transport (" + this.name + ") have no remoteAddress list"); if (rand && this.transportAddres.length < 1) throw new RuntimeException("Transport (" + this.name + ") have no remoteAddress list");
try { try {
if (tcp) { if (tcp) {
AsynchronousSocketChannel channel = null; AsynchronousSocketChannel channel = null;
if (rand) { //取地址 if (rand) { //取地址
for (int i = 0; i < remoteAddres.length; i++) { TransportAddress transportAddr;
addr = remoteAddres[i]; boolean tryed = false;
BlockingQueue<AsyncConnection> queue = connPool.get(addr); for (int i = 0; i < transportAddres.length; i++) {
if (queue != null && !queue.isEmpty()) { transportAddr = transportAddres[i];
addr = transportAddr.address;
if (!transportAddr.enable) continue;
final BlockingQueue<AsyncConnection> queue = transportAddr.conns;
if (!queue.isEmpty()) {
AsyncConnection conn; AsyncConnection conn;
while ((conn = queue.poll()) != null) { while ((conn = queue.poll()) != null) {
if (conn.isOpen()) return conn; if (conn.isOpen()) return conn;
} }
} }
tryed = true;
if (channel == null) { if (channel == null) {
channel = AsynchronousSocketChannel.open(group); channel = AsynchronousSocketChannel.open(group);
if (supportTcpNoDelay) channel.setOption(StandardSocketOptions.TCP_NODELAY, true); if (supportTcpNoDelay) channel.setOption(StandardSocketOptions.TCP_NODELAY, true);
} }
try { try {
channel.connect(addr).get(2, TimeUnit.SECONDS); channel.connect(addr).get(2, TimeUnit.SECONDS);
transportAddr.enable = true;
break; break;
} catch (Exception iex) { } catch (Exception iex) {
iex.printStackTrace(); transportAddr.enable = false;
if (i == remoteAddres.length - 1) channel = null; 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 { } else {
@@ -199,7 +244,7 @@ public final class Transport {
if (channel == null) return null; if (channel == null) return null;
return AsyncConnection.create(channel, addr, 3000, 3000); return AsyncConnection.create(channel, addr, 3000, 3000);
} else { // UDP } else { // UDP
if (rand) addr = remoteAddres[0]; if (rand) addr = this.transportAddres[0].address;
DatagramChannel channel = DatagramChannel.open(); DatagramChannel channel = DatagramChannel.open();
channel.configureBlocking(true); channel.configureBlocking(true);
channel.connect(addr); channel.connect(addr);
@@ -261,4 +306,54 @@ public final class Transport {
}); });
} }
public static class TransportAddress {
protected InetSocketAddress address;
protected volatile boolean enable;
protected final BlockingQueue<AsyncConnection> conns = new ArrayBlockingQueue<>(MAX_POOL_LIMIT);
public TransportAddress(InetSocketAddress address) {
this.address = address;
this.enable = true;
}
@java.beans.ConstructorProperties({"address", "enable"})
public TransportAddress(InetSocketAddress address, boolean enable) {
this.address = address;
this.enable = enable;
}
public InetSocketAddress getAddress() {
return address;
}
public boolean isEnable() {
return enable;
}
@ConvertColumn(ignore = true)
public BlockingQueue<AsyncConnection> getConns() {
return conns;
}
@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;
return this.address.equals(other.address);
}
public String toString() {
return JsonConvert.root().convertTo(this);
}
}
} }

View File

@@ -0,0 +1,189 @@
/*
* 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.lang.ref.WeakReference;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousChannelGroup;
import java.util.*;
import java.util.concurrent.*;
import java.util.logging.*;
import java.util.stream.Collectors;
import org.redkale.service.Service;
import org.redkale.util.ObjectPool;
/**
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*/
public class TransportFactory {
protected static final Logger logger = Logger.getLogger(TransportFactory.class.getSimpleName());
//传输端的线程池
protected final ExecutorService executor;
//传输端的ByteBuffer对象池
protected final ObjectPool<ByteBuffer> bufferPool;
//传输端的ChannelGroup
protected final AsynchronousChannelGroup channelGroup;
//每个地址对应的Group名
protected final Map<InetSocketAddress, String> groupAddrs = new HashMap<>();
//协议地址的Group集合
protected final Map<String, TransportGroupInfo> groupInfos = new HashMap<>();
protected final List<WeakReference<Service>> services = new CopyOnWriteArrayList<>();
//负载均衡策略
protected final TransportStrategy strategy;
public TransportFactory(ExecutorService executor, ObjectPool<ByteBuffer> bufferPool, AsynchronousChannelGroup channelGroup,
final TransportStrategy strategy) {
this.executor = executor;
this.bufferPool = bufferPool;
this.channelGroup = channelGroup;
this.strategy = strategy;
}
public TransportFactory(ExecutorService executor, ObjectPool<ByteBuffer> bufferPool, AsynchronousChannelGroup channelGroup) {
this(executor, bufferPool, channelGroup, null);
}
public String findGroupName(InetSocketAddress addr) {
if (addr == null) return null;
return groupAddrs.get(addr);
}
public TransportGroupInfo findGroupInfo(String group) {
if (group == null) return null;
return groupInfos.get(group);
}
public boolean addGroupInfo(String groupName, InetSocketAddress... addrs) {
addGroupInfo(new TransportGroupInfo(groupName, addrs));
return true;
}
public boolean removeGroupInfo(String groupName, InetSocketAddress addr) {
if (groupName == null || groupName.isEmpty() || addr == null) return false;
if (!groupName.equals(groupAddrs.get(addr))) return false;
TransportGroupInfo group = groupInfos.get(groupName);
if (group == null) return false;
group.removeAddress(addr);
groupAddrs.remove(addr);
return true;
}
public TransportFactory addGroupInfo(String name, Set<InetSocketAddress> addrs) {
addGroupInfo(new TransportGroupInfo(name, addrs));
return this;
}
public boolean addGroupInfo(TransportGroupInfo info) {
if (info == null) throw new RuntimeException("TransportGroupInfo can not null");
if (info.addresses == null) throw new RuntimeException("TransportGroupInfo.addresses can not null");
if (!checkName(info.name)) throw new RuntimeException("Transport.group.name only 0-9 a-z A-Z _ cannot begin 0-9");
TransportGroupInfo old = groupInfos.get(info.name);
if (old != null && !old.protocol.equals(info.protocol)) throw new RuntimeException("Transport.group.name repeat but protocol is different");
if (old != null && !old.subprotocol.equals(info.subprotocol)) throw new RuntimeException("Transport.group.name repeat but subprotocol is different");
for (InetSocketAddress addr : info.addresses) {
if (!groupAddrs.getOrDefault(addr, info.name).equals(info.name)) throw new RuntimeException(addr + " repeat but different group.name");
}
if (old == null) {
groupInfos.put(info.name, info);
} else {
old.putAddress(info.addresses);
}
for (InetSocketAddress addr : info.addresses) {
groupAddrs.put(addr, info.name);
}
return true;
}
public Transport loadSameGroupTransport(InetSocketAddress sncpAddress) {
return loadTransport(groupAddrs.get(sncpAddress), sncpAddress);
}
public Transport[] loadDiffGroupTransports(InetSocketAddress sncpAddress, final Set<String> diffGroups) {
if (diffGroups == null) return null;
final String sncpGroup = groupAddrs.get(sncpAddress);
final List<Transport> transports = new ArrayList<>();
for (String group : diffGroups) {
if (sncpGroup == null || !sncpGroup.equals(group)) {
transports.add(loadTransport(group, sncpAddress));
}
}
return transports.toArray(new Transport[transports.size()]);
}
public Transport loadRemoteTransport(InetSocketAddress sncpAddress, final Set<String> groups) {
if (groups == null) return null;
Set<InetSocketAddress> addresses = new HashSet<>();
TransportGroupInfo info = null;
for (String group : groups) {
info = groupInfos.get(group);
if (info == null) continue;
addresses.addAll(info.addresses);
}
if (info == null) return null;
if (sncpAddress != null) addresses.remove(sncpAddress);
return new Transport(groups.stream().sorted().collect(Collectors.joining(";")), info.protocol, info.subprotocol, this.bufferPool, this.channelGroup, 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.bufferPool, this.channelGroup, sncpAddress, info.addresses, this.strategy);
}
public ExecutorService getExecutor() {
return executor;
}
public List<TransportGroupInfo> getGroupInfos() {
return new ArrayList<>(this.groupInfos.values());
}
public void addSncpService(Service service) {
if (service == null) return;
services.add(new WeakReference<>(service));
}
public List<Service> getServices() {
List<Service> rs = new ArrayList<>();
for (WeakReference<Service> ref : services) {
Service service = ref.get();
if (service != null) rs.add(service);
}
return rs;
}
public void shutdownNow() {
try {
this.channelGroup.shutdownNow();
} catch (Exception e) {
logger.log(Level.FINER, "close transportChannelGroup erroneous", e);
}
}
private static boolean checkName(String name) { //不能含特殊字符
if (name.isEmpty()) return false;
if (name.charAt(0) >= '0' && name.charAt(0) <= '9') return false;
for (char ch : name.toCharArray()) {
if (!((ch >= '0' && ch <= '9') || ch == '_' || (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z'))) { //不能含特殊字符
return false;
}
}
return true;
}
}

View File

@@ -0,0 +1,127 @@
/*
* 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.net.InetSocketAddress;
import java.util.*;
import org.redkale.convert.json.JsonConvert;
import org.redkale.util.Utility;
/**
* 协议地址组合对象, 对应application.xml 中 resources-&#62;group 节点信息
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*/
public class TransportGroupInfo {
protected String name; //地址
protected String protocol; //协议 取值范围: TCP、UDP
protected String subprotocol; //子协议,预留使用
protected Set<InetSocketAddress> addresses; //地址列表, 对应 resources-&#62;group-&#62;node节点信息
public TransportGroupInfo() {
}
public TransportGroupInfo(String name, InetSocketAddress... addrs) {
this(name, "TCP", "", Utility.ofSet(addrs));
}
public TransportGroupInfo(String name, Set<InetSocketAddress> addrs) {
this(name, "TCP", "", addrs);
}
public TransportGroupInfo(String name, String protocol, String subprotocol, InetSocketAddress... addrs) {
this(name, protocol, subprotocol, Utility.ofSet(addrs));
}
public TransportGroupInfo(String name, String protocol, String subprotocol, Set<InetSocketAddress> addrs) {
Objects.requireNonNull(name, "Transport.group.name can not null");
this.name = name;
this.protocol = protocol == null ? "TCP" : protocol;
this.subprotocol = subprotocol == null ? "" : subprotocol;
this.addresses = addrs;
}
public String getName() {
return name;
}
public void setName(String name) {
Objects.requireNonNull(name, "Transport.group.name can not null");
this.name = name;
}
public String getProtocol() {
return protocol;
}
public void setProtocol(String protocol) {
this.protocol = protocol == null ? "TCP" : protocol;
}
public String getSubprotocol() {
return subprotocol;
}
public void setSubprotocol(String subprotocol) {
this.subprotocol = subprotocol == null ? "" : subprotocol;
this.subprotocol = subprotocol;
}
public Set<InetSocketAddress> getAddresses() {
return addresses;
}
public Set<InetSocketAddress> copyAddresses() {
return addresses == null ? null : new LinkedHashSet<>(addresses);
}
public void setAddresses(Set<InetSocketAddress> addresses) {
this.addresses = addresses;
}
public boolean containsAddress(InetSocketAddress addr) {
synchronized (this) {
if (this.addresses == null) return false;
return this.addresses.contains(addr);
}
}
public void removeAddress(InetSocketAddress addr) {
if (addr == null) return;
synchronized (this) {
if (this.addresses == null) return;
this.addresses.remove(addr);
}
}
public void putAddress(InetSocketAddress addr) {
if (addr == null) return;
synchronized (this) {
if (this.addresses == null) this.addresses = new HashSet<>();
this.addresses.add(addr);
}
}
public void putAddress(Set<InetSocketAddress> addrs) {
if (addrs == null) return;
synchronized (this) {
if (this.addresses == null) this.addresses = new HashSet<>();
this.addresses.addAll(addrs);
}
}
@Override
public String toString() {
return JsonConvert.root().convertTo(this);
}
}

View File

@@ -0,0 +1,21 @@
/*
* 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.net.SocketAddress;
/**
* 远程请求的负载均衡策略
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*/
public interface TransportStrategy {
public AsyncConnection pollConnection(SocketAddress addr, Transport transport);
}

View File

@@ -1,29 +0,0 @@
/*
* 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 java.io.IOException;
/**
* 默认Servlet, 没有配置RestHttpServlet实现类则使用该默认类
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*/
public class DefaultRestServlet extends RestHttpServlet<Object> {
@Override
protected Object currentUser(HttpRequest req) throws IOException {
return new Object();
}
@Override
public void authenticate(int moduleid, int actionid, HttpRequest request, HttpResponse response, HttpServlet next) throws IOException {
next.execute(request, response);
}
}

View File

@@ -1,537 +0,0 @@
/*
* 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 java.io.*;
import org.redkale.net.Response;
import org.redkale.net.Request;
import org.redkale.util.AnyValue;
import java.lang.annotation.*;
import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.reflect.*;
import java.nio.*;
import java.util.*;
import java.util.concurrent.*;
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 org.redkale.service.RetResult;
/**
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*/
public abstract class HttpBaseServlet extends HttpServlet {
public static final int RET_SERVER_ERROR = 1800_0001;
public static final int RET_METHOD_ERROR = 1800_0002;
/**
* 配合 HttpBaseServlet 使用。
* 当标记为 &#64;AuthIgnore 的方法在执行execute之前不会调用authenticate 方法。
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*/
@Inherited
@Documented
@Target({METHOD, TYPE})
@Retention(RUNTIME)
protected @interface AuthIgnore {
}
/**
* 配合 &#64;WebParam 使用。
* 用于对&#64;WebParam中参数的来源类型
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*/
protected enum ParamSourceType {
PARAMETER, HEADER, COOKIE;
}
/**
* 配合 &#64;WebMapping 使用。
* 用于对&#64;WebMapping方法中参数描述
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*/
@Documented
@Target({METHOD})
@Retention(RUNTIME)
@Repeatable(WebParams.class)
protected @interface WebParam {
String name(); //参数名
Class type(); //参数的数据类型
String comment() default ""; //备注描述
ParamSourceType src() default ParamSourceType.PARAMETER; //参数来源类型
int radix() default 10; //转换数字byte/short/int/long时所用的进制数 默认10进制
boolean required() default true; //参数是否必传
}
@Documented
@Target({METHOD})
@Retention(RUNTIME)
protected @interface WebParams {
WebParam[] value();
}
/**
* 使用 WebMapping 替代。
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*/
@Deprecated
@Documented
@Target({METHOD})
@Retention(RUNTIME)
protected @interface WebAction {
int actionid() default 0;
String url();
String[] methods() default {};//允许方法(不区分大小写),如:GET/POST/PUT,为空表示允许所有方法
String comment() default ""; //备注描述
boolean inherited() default true; //是否能被继承, 当 HttpBaseServlet 被继承后该方法是否能被子类继承
String result() default "Object"; //输出结果的数据类型
Class[] results() default {}; //输出结果的数据类型集合,由于结果类型可能是泛型而注解的参数值不支持泛型,因此加入明细数据类型集合
}
/**
* 配合 HttpBaseServlet 使用。
* 用于对&#64;WebServlet对应的url进行细分。 其url必须是包含WebServlet中定义的前缀 且不能是正则表达式
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*/
@Documented
@Target({METHOD})
@Retention(RUNTIME)
protected @interface WebMapping {
int actionid() default 0;
String url();
String[] methods() default {};//允许方法(不区分大小写),如:GET/POST/PUT,为空表示允许所有方法
String comment() default ""; //备注描述
boolean inherited() default true; //是否能被继承, 当 HttpBaseServlet 被继承后该方法是否能被子类继承
String result() default "Object"; //输出结果的数据类型
Class[] results() default {}; //输出结果的数据类型集合,由于结果类型可能是泛型而注解的参数值不支持泛型,因此加入明细数据类型集合
}
/**
* 配合 HttpBaseServlet 使用。
* 当标记为 &#64;HttpCacheable 的方法使用response.finish的参数将被缓存一段时间(默认值 seconds=15秒)。
* 通常情况下 &#64;HttpCacheable 需要与 &#64;AuthIgnore 一起使用,没有标记&#64;AuthIgnore的方法一般输出的结果与当前用户信息有关。
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*/
@Documented
@Target({METHOD})
@Retention(RUNTIME)
protected @interface HttpCacheable {
/**
* 超时的秒数
*
* @return 超时秒数
*/
int seconds() default 15;
}
private Map.Entry<String, Entry>[] mappings;
private final HttpServlet authSuccessServlet = new HttpServlet() {
@Override
public void execute(HttpRequest request, HttpResponse response) throws IOException {
Entry entry = (Entry) request.attachment;
if (entry.cacheseconds > 0) {//有缓存设置
CacheEntry ce = entry.cache.get(request.getRequestURI());
if (ce != null && ce.time + entry.cacheseconds > System.currentTimeMillis()) { //缓存有效
response.setStatus(ce.status);
response.setContentType(ce.contentType);
response.finish(ce.getBuffers());
return;
}
response.setBufferHandler(entry.cacheHandler);
}
entry.servlet.execute(request, response);
}
};
private final HttpServlet preSuccessServlet = new HttpServlet() {
@Override
public void execute(HttpRequest request, HttpResponse response) throws IOException {
for (Map.Entry<String, Entry> en : mappings) {
if (request.getRequestURI().startsWith(en.getKey())) {
Entry entry = en.getValue();
if (!entry.checkMethod(request.getMethod())) {
response.finishJson(new RetResult(RET_METHOD_ERROR, "Method(" + request.getMethod() + ") Error"));
return;
}
request.attachment = entry;
if (entry.ignore) {
authSuccessServlet.execute(request, response);
} else {
authenticate(entry.moduleid, entry.actionid, request, response, authSuccessServlet);
}
return;
}
}
throw new IOException(this.getClass().getName() + " not found method for URI(" + request.getRequestURI() + ")");
}
};
/**
* <p>
* 预执行方法在execute方法之前运行通常用于常规统计或基础检测例如 : <br>
* <blockquote><pre>
* &#64;Override
* public void preExecute(final HttpRequest request, final HttpResponse response, HttpServlet next) throws IOException {
* if (finer) response.setRecycleListener((req, resp) -&#62; { //记录处理时间比较长的请求
* long e = System.currentTimeMillis() - ((HttpRequest) req).getCreatetime();
* if (e &#62; 200) logger.finer("http-execute-cost-time: " + e + " ms. request = " + req);
* });
* next.execute(request, response);
* }
* </pre></blockquote>
* <p>
*
* @param request HttpRequest
* @param response HttpResponse
* @param next HttpServlet
*
* @throws IOException IOException
*/
public void preExecute(HttpRequest request, HttpResponse response, final HttpServlet next) throws IOException {
next.execute(request, response);
}
/**
* 使用 public void authenticate(int moduleid, int actionid, HttpRequest request, HttpResponse response, final HttpServlet next) throws IOException 代替
*
* @param moduleid int
* @param actionid int
* @param request HttpRequest
* @param response HttpResponse
*
* @return boolean
* @throws IOException IOException
* @deprecated
*/
@Deprecated
public boolean authenticate(int moduleid, int actionid, HttpRequest request, HttpResponse response) throws IOException {
return true;
}
/**
* <p>
* 用户登录或权限验证, 没有注解为&#64;AuthIgnore 的方法会执行authenticate方法, 若验证成功则必须调用next.execute(request, response);进行下一步操作, 例如: <br>
* <blockquote><pre>
* &#64;Override
* public void authenticate(int moduleid, int actionid, HttpRequest request, HttpResponse response, final HttpServlet next) throws IOException {
* UserInfo info = currentUser(request);
* if (info == null) {
* response.finishJson(RET_UNLOGIN);
* return;
* } else if (!info.checkAuth(module, actionid)) {
* response.finishJson(RET_AUTHILLEGAL);
* return;
* }
* next.execute(request, response);
* }
* </pre></blockquote>
* <p>
*
*
* @param moduleid 模块ID来自&#64;WebServlet.moduleid()
* @param actionid 操作ID来自&#64;WebMapping.actionid()
* @param request HttpRequest
* @param response HttpResponse
* @param next HttpServlet
*
* @throws IOException IOException
*/
public abstract void authenticate(int moduleid, int actionid, HttpRequest request, HttpResponse response, final HttpServlet next) throws IOException;
@Override
public final void execute(HttpRequest request, HttpResponse response) throws IOException {
preExecute(request, response, preSuccessServlet);
}
public final void preInit(HttpContext context, AnyValue config) {
String path = _prefix == null ? "" : _prefix;
WebServlet ws = this.getClass().getAnnotation(WebServlet.class);
if (ws != null && !ws.repair()) path = "";
HashMap<String, Entry> map = load();
this.mappings = new Map.Entry[map.size()];
int i = -1;
for (Map.Entry<String, Entry> en : map.entrySet()) {
mappings[++i] = new AbstractMap.SimpleEntry<>(path + en.getKey(), en.getValue());
}
//必须要倒排序, /query /query1 /query12 确保含子集的优先匹配 /query12 /query1 /query
Arrays.sort(mappings, (o1, o2) -> o2.getKey().compareTo(o1.getKey()));
}
public final void postDestroy(HttpContext context, AnyValue config) {
}
protected void setHeader(HttpRequest request, String name, Serializable value) {
request.header.setValue(name, String.valueOf(value));
}
protected void addHeader(HttpRequest request, String name, Serializable value) {
request.header.addValue(name, String.valueOf(value));
}
protected String _prefix(HttpServlet servlet) {
return servlet._prefix;
}
private HashMap<String, Entry> load() {
final boolean typeIgnore = this.getClass().getAnnotation(AuthIgnore.class) != null;
WebServlet module = this.getClass().getAnnotation(WebServlet.class);
final int serviceid = module == null ? 0 : module.moduleid();
final HashMap<String, Entry> map = new HashMap<>();
HashMap<String, Class> nameset = new HashMap<>();
final Class selfClz = this.getClass();
Class clz = this.getClass();
do {
if (Modifier.isAbstract(clz.getModifiers())) break;
for (final Method method : clz.getMethods()) {
//-----------------------------------------------
String methodname = method.getName();
if ("service".equals(methodname) || "preExecute".equals(methodname) || "execute".equals(methodname) || "authenticate".equals(methodname)) continue;
//-----------------------------------------------
Class[] paramTypes = method.getParameterTypes();
if (paramTypes.length != 2 || paramTypes[0] != HttpRequest.class
|| paramTypes[1] != HttpResponse.class) continue;
//-----------------------------------------------
Class[] exps = method.getExceptionTypes();
if (exps.length > 0 && (exps.length != 1 || exps[0] != IOException.class)) continue;
//-----------------------------------------------
final WebMapping mapping = method.getAnnotation(WebMapping.class);
final WebAction action = method.getAnnotation(WebAction.class);
if (mapping == null && action == null) continue;
final boolean inherited = mapping == null ? action.inherited() : mapping.inherited();
if (!inherited && selfClz != clz) continue; //忽略不被继承的方法
final int actionid = mapping == null ? action.actionid() : mapping.actionid();
final String name = mapping == null ? action.url().trim() : mapping.url().trim();
final String[] methods = mapping == null ? action.methods() : mapping.methods();
if (nameset.containsKey(name)) {
if (nameset.get(name) != clz) continue;
throw new RuntimeException(this.getClass().getSimpleName() + " have two same " + WebMapping.class.getSimpleName() + "(" + name + ")");
}
nameset.put(name, clz);
map.put(name, new Entry(typeIgnore, serviceid, actionid, name, methods, method, createHttpServlet(method)));
}
} while ((clz = clz.getSuperclass()) != HttpBaseServlet.class);
return map;
}
private HttpServlet createHttpServlet(final Method method) {
//------------------------------------------------------------------------------
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);
String newDynName = interName + "_Dyn_" + method.getName();
int i = 0;
for (;;) {
try {
Class.forName(newDynName.replace('/', '.'));
newDynName += "_" + (++i);
} catch (Throwable ex) {
break;
}
}
//------------------------------------------------------------------------------
ClassWriter cw = new ClassWriter(COMPUTE_FRAMES);
FieldVisitor fv;
MethodVisitor mv;
AnnotationVisitor av0;
final String factfield = "_factServlet";
cw.visit(V1_8, ACC_PUBLIC + ACC_FINAL + ACC_SUPER, newDynName, null, supDynName, null);
{
fv = cw.visitField(ACC_PUBLIC, factfield, interDesc, null, null);
fv.visitEnd();
}
{ //构造函数
mv = (cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null));
//mv.setDebug(true);
mv.visitVarInsn(ALOAD, 0);
mv.visitMethodInsn(INVOKESPECIAL, supDynName, "<init>", "()V", false);
mv.visitInsn(RETURN);
mv.visitMaxs(1, 1);
mv.visitEnd();
}
{
mv = (cw.visitMethod(ACC_PUBLIC, "execute", "(" + requestDesc + responseDesc + ")V", null, new String[]{"java/io/IOException"}));
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, newDynName, factfield, interDesc);
mv.visitVarInsn(ALOAD, 1);
mv.visitVarInsn(ALOAD, 2);
mv.visitMethodInsn(INVOKEVIRTUAL, interName, method.getName(), "(" + requestDesc + responseDesc + ")V", false);
mv.visitInsn(RETURN);
mv.visitMaxs(3, 3);
mv.visitEnd();
}
{
mv = cw.visitMethod(ACC_PUBLIC + ACC_BRIDGE + ACC_SYNTHETIC, "execute", "(" + requestSupDesc + responseSupDesc + ")V", null, new String[]{"java/io/IOException"});
mv.visitVarInsn(ALOAD, 0);
mv.visitVarInsn(ALOAD, 1);
mv.visitTypeInsn(CHECKCAST, HttpRequest.class.getName().replace('.', '/'));
mv.visitVarInsn(ALOAD, 2);
mv.visitTypeInsn(CHECKCAST, HttpResponse.class.getName().replace('.', '/'));
mv.visitMethodInsn(INVOKEVIRTUAL, newDynName, "execute", "(" + requestDesc + responseDesc + ")V", false);
mv.visitInsn(RETURN);
mv.visitMaxs(3, 3);
mv.visitEnd();
}
cw.visitEnd();
//------------------------------------------------------------------------------
byte[] bytes = cw.toByteArray();
Class<?> newClazz = new ClassLoader(this.getClass().getClassLoader()) {
public final Class<?> loadClass(String name, byte[] b) {
return defineClass(name, b, 0, b.length);
}
}.loadClass(newDynName.replace('/', '.'), bytes);
try {
HttpServlet instance = (HttpServlet) newClazz.newInstance();
instance.getClass().getField(factfield).set(instance, this);
return instance;
} catch (Exception ex) {
throw new RuntimeException(ex);
}
}
private static final class Entry {
public Entry(boolean typeIgnore, int moduleid, int actionid, String name, String[] methods, Method method, HttpServlet servlet) {
this.moduleid = moduleid;
this.actionid = actionid;
this.name = name;
this.methods = methods;
this.method = method;
this.servlet = servlet;
this.ignore = typeIgnore || method.getAnnotation(AuthIgnore.class) != null;
HttpCacheable hc = method.getAnnotation(HttpCacheable.class);
this.cacheseconds = hc == null ? 0 : hc.seconds() * 1000;
this.cache = cacheseconds > 0 ? new ConcurrentHashMap() : null;
this.cacheHandler = cacheseconds > 0 ? (HttpResponse response, ByteBuffer[] buffers) -> {
int status = response.getStatus();
if (status != 200) return null;
CacheEntry ce = new CacheEntry(response.getStatus(), response.getContentType(), buffers);
cache.put(response.getRequest().getRequestURI(), ce);
return ce.getBuffers();
} : null;
}
public boolean isNeedCheck() {
return this.moduleid != 0 || this.actionid != 0;
}
public boolean checkMethod(final String reqMethod) {
if (methods.length == 0) return true;
for (String m : methods) {
if (reqMethod.equalsIgnoreCase(m)) return true;
}
return false;
}
public final HttpResponse.BufferHandler cacheHandler;
public final ConcurrentHashMap<String, CacheEntry> cache;
public final int cacheseconds;
public final boolean ignore;
public final int moduleid;
public final int actionid;
public final String name;
public final String[] methods;
public final Method method;
public final HttpServlet servlet;
}
private static final class CacheEntry {
public final long time = System.currentTimeMillis();
private final ByteBuffer[] buffers;
private final int status;
private final String contentType;
public CacheEntry(int status, String contentType, ByteBuffer[] bufs) {
this.status = status;
this.contentType = contentType;
final ByteBuffer[] newBuffers = new ByteBuffer[bufs.length];
for (int i = 0; i < newBuffers.length; i++) {
newBuffers[i] = bufs[i].duplicate().asReadOnlyBuffer();
}
this.buffers = newBuffers;
}
public ByteBuffer[] getBuffers() {
final ByteBuffer[] newBuffers = new ByteBuffer[buffers.length];
for (int i = 0; i < newBuffers.length; i++) {
newBuffers[i] = buffers[i].duplicate();
}
return newBuffers;
}
}
}

View File

@@ -11,9 +11,10 @@ import java.nio.charset.*;
import java.security.*; import java.security.*;
import java.util.concurrent.*; import java.util.concurrent.*;
import java.util.logging.*; import java.util.logging.*;
import jdk.internal.org.objectweb.asm.*;
import static jdk.internal.org.objectweb.asm.Opcodes.*;
import org.redkale.net.*; import org.redkale.net.*;
import org.redkale.util.*; import org.redkale.util.*;
import org.redkale.watch.*;
/** /**
* HTTP服务的上下文对象 * HTTP服务的上下文对象
@@ -27,11 +28,13 @@ public class HttpContext extends Context {
protected final SecureRandom random = new SecureRandom(); protected final SecureRandom random = new SecureRandom();
protected final ConcurrentHashMap<Class, Creator> asyncHandlerCreators = new ConcurrentHashMap<>();
public HttpContext(long serverStartTime, Logger logger, ExecutorService executor, int bufferCapacity, ObjectPool<ByteBuffer> bufferPool, public HttpContext(long serverStartTime, Logger logger, ExecutorService executor, int bufferCapacity, ObjectPool<ByteBuffer> bufferPool,
ObjectPool<Response> responsePool, int maxbody, Charset charset, InetSocketAddress address, PrepareServlet prepare, ObjectPool<Response> responsePool, int maxbody, Charset charset, InetSocketAddress address, PrepareServlet prepare,
WatchFactory watch, int readTimeoutSecond, int writeTimeoutSecond) { int readTimeoutSecond, int writeTimeoutSecond) {
super(serverStartTime, logger, executor, bufferCapacity, bufferPool, responsePool, maxbody, charset, super(serverStartTime, logger, executor, bufferCapacity, bufferPool, responsePool, maxbody, charset,
address, prepare, watch, readTimeoutSecond, writeTimeoutSecond); address, prepare, readTimeoutSecond, writeTimeoutSecond);
random.setSeed(Math.abs(System.nanoTime())); random.setSeed(Math.abs(System.nanoTime()));
} }
@@ -42,10 +45,6 @@ public class HttpContext extends Context {
return new String(Utility.binToHex(bytes)); return new String(Utility.binToHex(bytes));
} }
protected WatchFactory getWatchFactory() {
return watch;
}
protected ExecutorService getExecutor() { protected ExecutorService getExecutor() {
return executor; return executor;
} }
@@ -54,4 +53,113 @@ public class HttpContext extends Context {
return responsePool; return responsePool;
} }
protected <H extends AsyncHandler> Creator<H> loadAsyncHandlerCreator(Class<H> handlerClass) {
Creator<H> creator = asyncHandlerCreators.get(handlerClass);
if (creator == null) {
creator = createAsyncHandlerCreator(handlerClass);
asyncHandlerCreators.put(handlerClass, creator);
}
return creator;
}
private <H extends AsyncHandler> Creator<H> createAsyncHandlerCreator(Class<H> handlerClass) {
//生成规则与SncpAsyncHandler.Factory 很类似
//-------------------------------------------------------------
final boolean handlerinterface = handlerClass.isInterface();
final String handlerClassName = handlerClass.getName().replace('.', '/');
final String handlerName = AsyncHandler.class.getName().replace('.', '/');
final String handlerDesc = Type.getDescriptor(AsyncHandler.class);
final String newDynName = handlerClass.getName().replace('.', '/') + "_Dync" + AsyncHandler.class.getSimpleName() + "_" + (System.currentTimeMillis() % 10000);
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
FieldVisitor fv;
AsmMethodVisitor 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});
{ //handler 属性
fv = cw.visitField(ACC_PRIVATE, "handler", handlerDesc, null, null);
fv.visitEnd();
}
{//构造方法
mv = new AsmMethodVisitor(cw.visitMethod(ACC_PUBLIC, "<init>", "(" + handlerDesc + ")V", null, null));
//mv.setDebug(true);
{
av0 = mv.visitAnnotation("Ljava/beans/ConstructorProperties;", true);
{
AnnotationVisitor av1 = av0.visitArray("value");
av1.visit(null, "handler");
av1.visitEnd();
}
av0.visitEnd();
}
mv.visitVarInsn(ALOAD, 0);
mv.visitMethodInsn(INVOKESPECIAL, handlerinterface ? "java/lang/Object" : handlerClassName, "<init>", "()V", false);
mv.visitVarInsn(ALOAD, 0);
mv.visitVarInsn(ALOAD, 1);
mv.visitFieldInsn(PUTFIELD, newDynName, "handler", handlerDesc);
mv.visitInsn(RETURN);
mv.visitMaxs(2, 2);
mv.visitEnd();
}
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.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, newDynName, "handler", handlerDesc);
mv.visitVarInsn(ALOAD, 1);
mv.visitVarInsn(ALOAD, 2);
mv.visitMethodInsn(INVOKEINTERFACE, handlerName, "completed", "(Ljava/lang/Object;Ljava/lang/Object;)V", true);
mv.visitInsn(RETURN);
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.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, newDynName, "handler", handlerDesc);
mv.visitVarInsn(ALOAD, 1);
mv.visitVarInsn(ALOAD, 2);
mv.visitMethodInsn(INVOKEINTERFACE, handlerName, "failed", "(Ljava/lang/Throwable;Ljava/lang/Object;)V", true);
mv.visitInsn(RETURN);
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));
Class returnType = method.getReturnType();
if (returnType == void.class) {
mv.visitInsn(RETURN);
mv.visitMaxs(0, 1);
} else if (returnType.isPrimitive()) {
mv.visitInsn(ICONST_0);
if (returnType == long.class) {
mv.visitInsn(LRETURN);
mv.visitMaxs(2, 1);
} else if (returnType == float.class) {
mv.visitInsn(FRETURN);
mv.visitMaxs(2, 1);
} else if (returnType == double.class) {
mv.visitInsn(DRETURN);
mv.visitMaxs(2, 1);
} else {
mv.visitInsn(IRETURN);
mv.visitMaxs(1, 1);
}
} else {
mv.visitInsn(ACONST_NULL);
mv.visitInsn(ARETURN);
mv.visitMaxs(1, 1);
}
mv.visitEnd();
}
}
cw.visitEnd();
byte[] bytes = cw.toByteArray();
Class<AsyncHandler> newHandlerClazz = (Class<AsyncHandler>) new ClassLoader(handlerClass.getClassLoader()) {
public final Class<?> loadClass(String name, byte[] b) {
return defineClass(name, b, 0, b.length);
}
}.loadClass(newDynName.replace('/', '.'), bytes);
return (Creator<H>) Creator.create(newHandlerClazz);
}
} }

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.http;
import org.redkale.net.Filter;
/**
* HTTP 过滤器 <br>
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*/
public abstract class HttpFilter extends Filter<HttpContext, HttpRequest, HttpResponse> {
}

View File

@@ -0,0 +1,85 @@
/*
* 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 java.lang.annotation.*;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
/**
* 配合 HttpServlet 使用。
* 用于对&#64;WebServlet对应的url进行细分。 其url必须是包含WebServlet中定义的前缀 且不能是正则表达式
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*/
@Documented
@Target({METHOD})
@Retention(RUNTIME)
public @interface HttpMapping {
/**
* 操作ID值鉴权时用到
*
* @return int
*/
int actionid() default 0;
String url();
/**
* 结果缓存的秒数, 为0表示不缓存 <br>
* * 当值大于0将被缓存一段时间(默认值 seconds=15秒)。 <br>
* 通常情况下需要 auth() == true 才使用没有标记auth==true方法一般输出的结果与当前用户信息有关。 <br>
*
* @return int
*/
int cacheseconds() default 0;
/**
* 是否鉴权,默认需要鉴权 <br>
*
* @return boolean
*/
boolean auth() default true;
/**
* 允许方法(不区分大小写),如:GET/POST/PUT,为空表示允许所有方法
*
* @return String[]
*/
String[] methods() default {};
/**
* 是否能被继承, 当 HttpServlet 被继承后该方法是否能被子类继承
*
* @return boolean
*/
boolean inherited() default true;
/**
* 输出结果的数据类型
*
* @return String
*/
String result() default "Object";
/**
* 输出结果的数据类型集合,由于结果类型可能是泛型而注解的参数值不支持泛型,因此加入明细数据类型集合
*
* @return Class[]
*/
Class[] results() default {};
/**
* 备注描述
*
* @return String
*/
String comment() default "";
}

View File

@@ -0,0 +1,61 @@
/*
* 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 java.lang.annotation.*;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
/**
* 配合 &#64;HttpMapping 使用。
* 用于对&#64;HttpMapping方法中参数描述
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*/
@Documented
@Target({METHOD})
@Retention(RUNTIME)
@Repeatable(HttpParam.HttpParams.class)
public @interface HttpParam {
String name(); //参数名
Class type(); //参数的数据类型
String comment() default ""; //备注描述
HttpParamSourceType src() default HttpParamSourceType.PARAMETER; //参数来源类型
int radix() default 10; //转换数字byte/short/int/long时所用的进制数 默认10进制
boolean required() default true; //参数是否必传
/**
* 配合 &#64;HttpParam 使用。
* 用于对&#64;HttpParam中参数的来源类型
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*/
public static enum HttpParamSourceType {
PARAMETER, HEADER, COOKIE;
}
@Documented
@Target({METHOD})
@Retention(RUNTIME)
@interface HttpParams {
HttpParam[] value();
}
}

View File

@@ -8,13 +8,13 @@ package org.redkale.net.http;
import org.redkale.util.AnyValue.DefaultAnyValue; import org.redkale.util.AnyValue.DefaultAnyValue;
import java.io.*; import java.io.*;
import java.util.*; import java.util.*;
import java.util.AbstractMap.SimpleEntry;
import java.util.function.*; import java.util.function.*;
import java.util.logging.*; import java.util.logging.*;
import java.util.regex.*; import java.util.regex.*;
import org.redkale.net.*; import org.redkale.net.*;
import org.redkale.net.http.Rest.RestDynSourceType;
import org.redkale.service.Service;
import org.redkale.util.*; import org.redkale.util.*;
import org.redkale.watch.*;
/** /**
* HTTP Servlet的总入口请求在HttpPrepareServlet中进行分流。 <br> * HTTP Servlet的总入口请求在HttpPrepareServlet中进行分流。 <br>
@@ -29,29 +29,180 @@ public class HttpPrepareServlet extends PrepareServlet<String, HttpContext, Http
protected final Logger logger = Logger.getLogger(this.getClass().getSimpleName()); protected final Logger logger = Logger.getLogger(this.getClass().getSimpleName());
protected SimpleEntry<Predicate<String>, HttpServlet>[] regArray = new SimpleEntry[0];
protected HttpServlet resourceHttpServlet = new HttpResourceServlet(); protected HttpServlet resourceHttpServlet = new HttpResourceServlet();
protected MappingEntry[] regArray = null; //regArray 包含 regWsArray
protected MappingEntry[] regWsArray = null;
protected Map<String, WebSocketServlet> wsmappings = new HashMap<>(); //super.mappings 包含 wsmappings
protected final Map<String, Class> allMapStrings = new HashMap<>(); protected final Map<String, Class> allMapStrings = new HashMap<>();
private final Object excludeLock = new Object();
private Map<String, BiPredicate<String, String>> forbidURIMaps; //禁用的URL的正则表达式, 必须与 forbidURIPredicates 保持一致
private BiPredicate<String, String>[] forbidURIPredicates; //禁用的URL的Predicate, 必须与 forbidURIMaps 保持一致
private List<HttpServlet> removeHttpServlet(final Predicate<MappingEntry> predicateEntry, final Predicate<Map.Entry<String, WebSocketServlet>> predicateFilter) {
List<HttpServlet> servlets = new ArrayList<>();
synchronized (allMapStrings) {
List<String> keys = new ArrayList<>();
if (regArray != null) {
for (MappingEntry me : regArray) {
if (predicateEntry.test(me)) {
servlets.add(me.servlet);
keys.add(me.mapping);
}
}
}
if (regWsArray != null) {
for (MappingEntry me : regWsArray) {
if (predicateEntry.test(me)) {
servlets.add(me.servlet);
keys.add(me.mapping);
}
}
}
Map<String, WebSocketServlet> newwsmappings = new HashMap<>();
for (Map.Entry<String, WebSocketServlet> en : wsmappings.entrySet()) {
if (predicateFilter.test(en)) {
servlets.add(en.getValue());
keys.add(en.getKey());
} else {
newwsmappings.put(en.getKey(), en.getValue());
}
}
if (newwsmappings.size() != wsmappings.size()) this.wsmappings = newwsmappings;
if (!keys.isEmpty()) {
this.regArray = Utility.remove(this.regArray, predicateEntry);
this.regWsArray = Utility.remove(this.regWsArray, predicateEntry);
for (HttpServlet rs : servlets) {
super.removeServlet(rs);
}
for (String key : keys) {
super.removeMapping(key);
allMapStrings.remove(key);
}
}
}
return servlets;
}
public HttpServlet removeHttpServlet(final HttpServlet servlet) {
Predicate<MappingEntry> predicateEntry = (t) -> t.servlet == servlet;
Predicate<Map.Entry<String, WebSocketServlet>> predicateFilter = (t) -> t.getValue() == servlet;
removeHttpServlet(predicateEntry, predicateFilter);
return servlet;
}
public <T extends HttpServlet> HttpServlet removeHttpServlet(Service service) {
Predicate<MappingEntry> predicateEntry = (t) -> {
if (!Rest.isRestDyn(t.servlet)) return false;
Service s = Rest.getService(t.servlet);
if (s == service) return true;
if (s != null) return false;
Map<String, Service> map = Rest.getServiceMap(t.servlet);
if (map == null) return false;
boolean rs = map.values().contains(service);
if (rs && map.size() == 1) return true;
if (rs && map.size() > 1) {
String key = null;
for (Map.Entry<String, Service> en : map.entrySet()) {
if (en.getValue() == service) {
key = en.getKey();
break;
}
}
if (key != null) map.remove(key);
return false; //还有其他Resouce.name 的Service
}
return rs;
};
Predicate<Map.Entry<String, WebSocketServlet>> predicateFilter = null;
List<HttpServlet> list = removeHttpServlet(predicateEntry, predicateFilter);
return list == null || list.isEmpty() ? null : list.get(0);
}
public <T extends WebSocket> HttpServlet removeHttpServlet(Class<T> websocketOrServletType) {
Predicate<MappingEntry> predicateEntry = (t) -> {
Class type = t.servlet.getClass();
if (type == websocketOrServletType) return true;
RestDynSourceType rdt = (RestDynSourceType) type.getAnnotation(RestDynSourceType.class);
return (rdt != null && rdt.value() == websocketOrServletType);
};
Predicate<Map.Entry<String, WebSocketServlet>> predicateFilter = (t) -> {
Class type = t.getValue().getClass();
if (type == websocketOrServletType) return true;
RestDynSourceType rdt = (RestDynSourceType) type.getAnnotation(RestDynSourceType.class);
return (rdt != null && rdt.value() == websocketOrServletType);
};
List<HttpServlet> list = removeHttpServlet(predicateEntry, predicateFilter);
return list == null || list.isEmpty() ? null : list.get(0);
}
public boolean addForbidURIReg(final String urlreg) {
if (urlreg == null || urlreg.isEmpty()) return false;
synchronized (excludeLock) {
if (forbidURIMaps != null && forbidURIMaps.containsKey(urlreg)) return false;
if (forbidURIMaps == null) forbidURIMaps = new HashMap<>();
String mapping = urlreg;
if (Utility.contains(mapping, '.', '*', '{', '[', '(', '|', '^', '$', '+', '?', '\\')) { //是否是正则表达式))
if (mapping.endsWith("/*")) {
mapping = mapping.substring(0, mapping.length() - 1) + ".*";
} else {
mapping = mapping + "$";
}
}
final String reg = mapping;
final boolean begin = mapping.charAt(0) == '^';
final Predicate regPredicate = Pattern.compile(reg).asPredicate();
BiPredicate<String, String> predicate = (prefix, uri) -> {
return begin || prefix.isEmpty() ? regPredicate.test(uri) : uri.matches(prefix + reg);
};
forbidURIMaps.put(urlreg, predicate);
forbidURIPredicates = Utility.append(forbidURIPredicates, predicate);
return true;
}
}
public boolean removeForbidURIReg(final String urlreg) {
if (urlreg == null || urlreg.isEmpty()) return false;
synchronized (excludeLock) {
if (forbidURIMaps == null || forbidURIPredicates == null || !forbidURIMaps.containsKey(urlreg)) return false;
BiPredicate<String, String> predicate = forbidURIMaps.get(urlreg);
forbidURIMaps.remove(urlreg);
int index = -1;
for (int i = 0; i < forbidURIPredicates.length; i++) {
if (forbidURIPredicates[i] == predicate) {
index = i;
break;
}
}
if (index > -1) {
if (forbidURIPredicates.length == 1) {
forbidURIPredicates = null;
} else {
int newlen = forbidURIPredicates.length - 1;
BiPredicate[] news = new BiPredicate[newlen];
System.arraycopy(forbidURIPredicates, 0, news, 0, index);
System.arraycopy(forbidURIPredicates, index + 1, news, index, newlen - index);
forbidURIPredicates = news;
}
}
return true;
}
}
@Override @Override
public void init(HttpContext context, AnyValue config) { public void init(HttpContext context, AnyValue config) {
super.init(context, config); //必须要执行
Collection<HttpServlet> servlets = getServlets(); Collection<HttpServlet> servlets = getServlets();
servlets.forEach(s -> { servlets.forEach(s -> {
if (s instanceof WebSocketServlet) { s.preInit(context, getServletConf(s));
((WebSocketServlet) s).preInit(context, getServletConf(s));
} else if (s instanceof HttpBaseServlet) {
((HttpBaseServlet) s).preInit(context, getServletConf(s));
}
s.init(context, getServletConf(s)); s.init(context, getServletConf(s));
}); });
final WatchFactory watch = context.getWatchFactory();
if (watch != null) {
servlets.forEach(s -> {
watch.inject(s);
});
}
AnyValue resConfig = config.getAnyValue("resource-servlet"); AnyValue resConfig = config.getAnyValue("resource-servlet");
if ((resConfig instanceof DefaultAnyValue) && resConfig.getValue("webroot", "").isEmpty()) { if ((resConfig instanceof DefaultAnyValue) && resConfig.getValue("webroot", "").isEmpty()) {
((DefaultAnyValue) resConfig).addValue("webroot", config.getValue("root")); ((DefaultAnyValue) resConfig).addValue("webroot", config.getValue("root"));
@@ -71,7 +222,7 @@ public class HttpPrepareServlet extends PrepareServlet<String, HttpContext, Http
} }
String resServlet = resConfig.getValue("servlet", HttpResourceServlet.class.getName()); String resServlet = resConfig.getValue("servlet", HttpResourceServlet.class.getName());
try { try {
this.resourceHttpServlet = (HttpServlet) Class.forName(resServlet).newInstance(); this.resourceHttpServlet = (HttpServlet) Thread.currentThread().getContextClassLoader().loadClass(resServlet).newInstance();
} catch (Throwable e) { } catch (Throwable e) {
this.resourceHttpServlet = new HttpResourceServlet(); this.resourceHttpServlet = new HttpResourceServlet();
logger.log(Level.WARNING, "init HttpResourceSerlvet(" + resServlet + ") error", e); logger.log(Level.WARNING, "init HttpResourceSerlvet(" + resServlet + ") error", e);
@@ -83,17 +234,52 @@ public class HttpPrepareServlet extends PrepareServlet<String, HttpContext, Http
public void execute(HttpRequest request, HttpResponse response) throws IOException { public void execute(HttpRequest request, HttpResponse response) throws IOException {
try { try {
final String uri = request.getRequestURI(); final String uri = request.getRequestURI();
Servlet<HttpContext, HttpRequest, HttpResponse> servlet = mappingServlet(uri); HttpServlet servlet;
if (servlet == null && this.regArray != null) { if (response.isAutoOptions() && "OPTIONS".equals(request.getMethod())) {
for (SimpleEntry<Predicate<String>, HttpServlet> en : regArray) { response.finish(200, null);
if (en.getKey().test(uri)) { return;
servlet = en.getValue(); }
if (request.isWebSocket()) {
servlet = wsmappings.get(uri);
if (servlet == null && this.regWsArray != null) {
for (MappingEntry en : regWsArray) {
if (en.predicate.test(uri)) {
servlet = en.servlet;
break;
}
}
}
if (servlet == null) {
response.finish(500, null);
return;
}
} else {
servlet = mappingServlet(uri);
if (servlet == null && this.regArray != null) {
for (MappingEntry en : regArray) {
if (en.predicate.test(uri)) {
servlet = en.servlet;
break;
}
}
}
//找不到匹配的HttpServlet则使用静态资源HttpResourceServlet
if (servlet == null) servlet = this.resourceHttpServlet;
}
boolean forbid = false;
BiPredicate<String, String>[] forbidUrlPredicates = this.forbidURIPredicates;
if (forbidUrlPredicates != null && forbidUrlPredicates.length > 0) {
for (BiPredicate<String, String> predicate : forbidUrlPredicates) {
if (predicate != null && predicate.test(servlet._prefix, uri)) {
forbid = true;
break; break;
} }
} }
} }
//找不到匹配的HttpServlet则使用静态资源HttpResourceServlet if (forbid) {
if (servlet == null) servlet = this.resourceHttpServlet; response.finish(403, response.getHttpCode(403));
return;
}
servlet.execute(request, response); servlet.execute(request, response);
} catch (Exception e) { } catch (Exception e) {
request.getContext().getLogger().log(Level.WARNING, "Servlet occur, forece to close channel. request = " + request, e); request.getContext().getLogger().log(Level.WARNING, "Servlet occur, forece to close channel. request = " + request, e);
@@ -132,14 +318,28 @@ public class HttpPrepareServlet extends PrepareServlet<String, HttpContext, Http
mapping = mapping + "$"; mapping = mapping + "$";
} }
if (regArray == null) { if (regArray == null) {
regArray = new SimpleEntry[1]; regArray = new MappingEntry[1];
regArray[0] = new SimpleEntry<>(Pattern.compile(mapping).asPredicate(), servlet); regArray[0] = new MappingEntry(mapping, Pattern.compile(mapping).asPredicate(), servlet);
} else { } else {
regArray = Arrays.copyOf(regArray, regArray.length + 1); regArray = Arrays.copyOf(regArray, regArray.length + 1);
regArray[regArray.length - 1] = new SimpleEntry<>(Pattern.compile(mapping).asPredicate(), servlet); regArray[regArray.length - 1] = new MappingEntry(mapping, Pattern.compile(mapping).asPredicate(), servlet);
}
if (servlet instanceof WebSocketServlet) {
if (regWsArray == null) {
regWsArray = new MappingEntry[1];
regWsArray[0] = new MappingEntry(mapping, Pattern.compile(mapping).asPredicate(), (WebSocketServlet) servlet);
} else {
regWsArray = Arrays.copyOf(regWsArray, regWsArray.length + 1);
regWsArray[regWsArray.length - 1] = new MappingEntry(mapping, Pattern.compile(mapping).asPredicate(), (WebSocketServlet) servlet);
}
} }
} else if (mapping != null && !mapping.isEmpty()) { } else if (mapping != null && !mapping.isEmpty()) {
putMapping(mapping, servlet); putMapping(mapping, servlet);
if (servlet instanceof WebSocketServlet) {
Map<String, WebSocketServlet> newmappings = new HashMap<>(wsmappings);
newmappings.put(mapping, (WebSocketServlet) servlet);
this.wsmappings = newmappings;
}
} }
if (this.allMapStrings.containsKey(mapping)) { if (this.allMapStrings.containsKey(mapping)) {
Class old = this.allMapStrings.get(mapping); Class old = this.allMapStrings.get(mapping);
@@ -175,16 +375,31 @@ public class HttpPrepareServlet extends PrepareServlet<String, HttpContext, Http
@Override @Override
public void destroy(HttpContext context, AnyValue config) { public void destroy(HttpContext context, AnyValue config) {
super.destroy(context, config); //必须要执行
this.resourceHttpServlet.destroy(context, config); this.resourceHttpServlet.destroy(context, config);
getServlets().forEach(s -> { getServlets().forEach(s -> {
s.destroy(context, getServletConf(s)); s.destroy(context, getServletConf(s));
if (s instanceof WebSocketServlet) { s.postDestroy(context, getServletConf(s));
((WebSocketServlet) s).postDestroy(context, getServletConf(s));
} else if (s instanceof HttpBaseServlet) {
((HttpBaseServlet) s).postDestroy(context, getServletConf(s));
}
}); });
this.allMapStrings.clear(); this.allMapStrings.clear();
this.wsmappings.clear();
this.regArray = null;
this.regWsArray = null;
} }
protected static class MappingEntry {
public final String mapping;
public final Predicate<String> predicate;
public final HttpServlet servlet;
public MappingEntry(String mapping, Predicate<String> predicate, HttpServlet servlet) {
this.mapping = mapping;
this.predicate = predicate;
this.servlet = servlet;
}
}
} }

View File

@@ -12,8 +12,8 @@ import java.nio.channels.Channels;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import org.redkale.convert.json.JsonConvert; import org.redkale.convert.json.JsonConvert;
import org.redkale.net.*; import org.redkale.net.*;
import org.redkale.util.*;
import org.redkale.util.AnyValue.DefaultAnyValue; import org.redkale.util.AnyValue.DefaultAnyValue;
import org.redkale.util.ByteArray;
/** /**
* Http请求包 与javax.servlet.http.HttpServletRequest 基本类似。 <br> * Http请求包 与javax.servlet.http.HttpServletRequest 基本类似。 <br>
@@ -32,8 +32,9 @@ public class HttpRequest extends Request<HttpContext> {
protected static final Charset UTF8 = Charset.forName("UTF-8"); protected static final Charset UTF8 = Charset.forName("UTF-8");
protected static final String SESSIONID_NAME = "JSESSIONID"; public static final String SESSIONID_NAME = "JSESSIONID";
@Comment("Method GET/POST/...")
private String method; private String method;
private String protocol; private String protocol;
@@ -48,7 +49,8 @@ public class HttpRequest extends Request<HttpContext> {
private String connection; private String connection;
protected String cookiestr; @Comment("原始的cookie字符串解析后值赋给HttpCookie[] cookies")
protected String cookie;
private HttpCookie[] cookies; private HttpCookie[] cookies;
@@ -64,15 +66,25 @@ public class HttpRequest extends Request<HttpContext> {
protected boolean boundary = false; protected boolean boundary = false;
protected int moduleid;
protected int actionid;
protected Object currentUser;
private final String remoteAddrHeader; private final String remoteAddrHeader;
Object attachment; //供 HttpBaseServlet传递Entry使用 Object attachment; //供HttpServlet传递Entry使用
public HttpRequest(HttpContext context, String remoteAddrHeader) { public HttpRequest(HttpContext context, String remoteAddrHeader) {
super(context); super(context);
this.remoteAddrHeader = remoteAddrHeader; this.remoteAddrHeader = remoteAddrHeader;
} }
protected boolean isWebSocket() {
return connection != null && connection.contains("Upgrade") && "GET".equalsIgnoreCase(method) && "websocket".equalsIgnoreCase(getHeader("Upgrade"));
}
protected void setKeepAlive(boolean keepAlive) { protected void setKeepAlive(boolean keepAlive) {
this.keepAlive = keepAlive; this.keepAlive = keepAlive;
} }
@@ -109,7 +121,6 @@ public class HttpRequest extends Request<HttpContext> {
} else { } else {
this.requestURI = array.toDecodeString(index, offset - index, charset).trim(); this.requestURI = array.toDecodeString(index, offset - index, charset).trim();
} }
if (this.requestURI.contains("../")) return -1;
index = ++offset; index = ++offset;
this.protocol = array.toString(index, array.size() - index, charset).trim(); this.protocol = array.toString(index, array.size() - index, charset).trim();
while (readLine(buffer, array)) { while (readLine(buffer, array)) {
@@ -131,10 +142,10 @@ public class HttpRequest extends Request<HttpContext> {
this.host = value; this.host = value;
break; break;
case "Cookie": case "Cookie":
if (this.cookiestr == null || this.cookiestr.isEmpty()) { if (this.cookie == null || this.cookie.isEmpty()) {
this.cookiestr = value; this.cookie = value;
} else { } else {
this.cookiestr += ";" + value; this.cookie += ";" + value;
} }
break; break;
case "Connection": case "Connection":
@@ -232,6 +243,51 @@ public class HttpRequest extends Request<HttpContext> {
return super.removeProperty(name); return super.removeProperty(name);
} }
/**
* 设置当前用户信息, 通常在HttpServlet.preExecute方法里设置currentUser <br>
* 数据类型由&#64;HttpUserType指定
*
* @param <T> 泛型
* @param user 用户信息
*
* @return HttpRequest
*/
public <T> HttpRequest setCurrentUser(T user) {
this.currentUser = user;
return this;
}
/**
* 获取当前用户信息<br>
* 数据类型由&#64;HttpUserType指定
*
* @param <T> &#64;HttpUserType指定的用户信息类型
*
* @return 用户信息
*/
@SuppressWarnings("unchecked")
public <T> T currentUser() {
return (T) this.currentUser;
}
/**
* 获取模块ID来自&#64;HttpServlet.moduleid()
*
* @return 模块ID
*/
public int getModuleid() {
return this.moduleid;
}
/**
* 获取操作ID来自&#64;HttpMapping.actionid()
*
* @return 模块ID
*/
public int getActionid() {
return this.actionid;
}
/** /**
* 获取客户端地址IP * 获取客户端地址IP
* *
@@ -278,6 +334,44 @@ public class HttpRequest extends Request<HttpContext> {
return array.toString(UTF8); return array.toString(UTF8);
} }
/**
* 获取请求内容的JavaBean对象
*
* @param <T> 泛型
* @param type 类型
*
* @return 内容
*/
public <T> T getBodyJson(java.lang.reflect.Type type) {
String str = array.toString(UTF8);
if (str == null || str.isEmpty()) return null;
return context.getJsonConvert().convertFrom(type, str);
}
/**
* 获取请求内容的JavaBean对象
*
* @param <T> 泛型
* @param convert JsonConvert
* @param type 类型
*
* @return 内容
*/
public <T> T getBodyJson(JsonConvert convert, java.lang.reflect.Type type) {
String str = array.toString(UTF8);
if (str == null || str.isEmpty()) return null;
return convert.convertFrom(type, str);
}
/**
* 获取请求内容的byte[]
*
* @return 内容
*/
public byte[] getBody() {
return array.getBytes();
}
/** /**
* 直接获取body对象 * 直接获取body对象
* *
@@ -291,7 +385,7 @@ public class HttpRequest extends Request<HttpContext> {
public String toString() { public String toString() {
parseBody(); parseBody();
return this.getClass().getSimpleName() + "{\r\n method: " + this.method + ", \r\n requestURI: " + this.requestURI return this.getClass().getSimpleName() + "{\r\n method: " + this.method + ", \r\n requestURI: " + this.requestURI
+ ", \r\n remoteAddr: " + this.getRemoteAddr() + ", \r\n cookies: " + this.cookiestr + ", \r\n contentType: " + this.contentType + ", \r\n remoteAddr: " + this.getRemoteAddr() + ", \r\n cookies: " + this.cookie + ", \r\n contentType: " + this.contentType
+ ", \r\n connection: " + this.connection + ", \r\n protocol: " + this.protocol + ", \r\n host: " + this.host + ", \r\n connection: " + this.connection + ", \r\n protocol: " + this.protocol + ", \r\n host: " + this.host
+ ", \r\n contentLength: " + this.contentLength + ", \r\n bodyLength: " + this.array.size() + (this.boundary || this.array.isEmpty() ? "" : (", \r\n bodyContent: " + this.getBodyUTF8())) + ", \r\n contentLength: " + this.contentLength + ", \r\n bodyLength: " + this.array.size() + (this.boundary || this.array.isEmpty() ? "" : (", \r\n bodyContent: " + this.getBodyUTF8()))
+ ", \r\n params: " + this.params.toString(4) + ", \r\n header: " + this.header.toString(4) + "\r\n}"; + ", \r\n params: " + this.params.toString(4) + ", \r\n header: " + this.header.toString(4) + "\r\n}";
@@ -325,7 +419,7 @@ public class HttpRequest extends Request<HttpContext> {
@Override @Override
protected void recycle() { protected void recycle() {
this.cookiestr = null; this.cookie = null;
this.cookies = null; this.cookies = null;
this.newsessionid = null; this.newsessionid = null;
this.method = null; this.method = null;
@@ -337,6 +431,9 @@ public class HttpRequest extends Request<HttpContext> {
this.contentLength = -1; this.contentLength = -1;
this.boundary = false; this.boundary = false;
this.bodyparsed = false; this.bodyparsed = false;
this.moduleid = 0;
this.actionid = 0;
this.currentUser = null;
this.attachment = null; this.attachment = null;
@@ -397,7 +494,7 @@ public class HttpRequest extends Request<HttpContext> {
* @return cookie对象数组 * @return cookie对象数组
*/ */
public HttpCookie[] getCookies() { public HttpCookie[] getCookies() {
if (this.cookies == null) this.cookies = parseCookies(this.cookiestr); if (this.cookies == null) this.cookies = parseCookies(this.cookie);
return this.cookies; return this.cookies;
} }
@@ -791,6 +888,15 @@ public class HttpRequest extends Request<HttpContext> {
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
/**
* 获取请求Header总对象
*
* @return AnyValue
*/
public AnyValue getHeaders() {
return header;
}
/** /**
* 获取所有的header名 * 获取所有的header名
* *
@@ -989,6 +1095,16 @@ public class HttpRequest extends Request<HttpContext> {
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
/**
* 获取请求参数总对象
*
* @return AnyValue
*/
public AnyValue getParameters() {
parseBody();
return params;
}
/** /**
* 获取所有参数名 * 获取所有参数名
* *

View File

@@ -162,6 +162,24 @@ public class HttpResourceServlet extends HttpServlet {
} }
} }
public void serRoot(String rootstr) {
if (rootstr == null) return;
try {
this.root = new File(rootstr).getCanonicalFile();
} catch (IOException ioe) {
this.root = new File(rootstr);
}
}
public void serRoot(File file) {
if (file == null) return;
try {
this.root = file.getCanonicalFile();
} catch (IOException ioe) {
this.root = file;
}
}
protected static long parseLenth(String value, long defValue) { protected static long parseLenth(String value, long defValue) {
if (value == null) return defValue; if (value == null) return defValue;
value = value.toUpperCase().replace("B", ""); value = value.toUpperCase().replace("B", "");
@@ -174,6 +192,11 @@ public class HttpResourceServlet extends HttpServlet {
@Override @Override
public void execute(HttpRequest request, HttpResponse response) throws IOException { public void execute(HttpRequest request, HttpResponse response) throws IOException {
String uri = request.getRequestURI(); String uri = request.getRequestURI();
if (uri.contains("../")) {
if (finest) logger.log(Level.FINEST, "Not found resource (404) be " + uri + ", request = " + request);
response.finish404();
return;
}
if (locationRewrites != null) { if (locationRewrites != null) {
for (SimpleEntry<Pattern, String> entry : locationRewrites) { for (SimpleEntry<Pattern, String> entry : locationRewrites) {
Matcher matcher = entry.getKey().matcher(uri); Matcher matcher = entry.getKey().matcher(uri);

View File

@@ -42,12 +42,14 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
public ByteBuffer[] execute(final HttpResponse response, final ByteBuffer[] buffers); public ByteBuffer[] execute(final HttpResponse response, final ByteBuffer[] buffers);
} }
private static final ByteBuffer buffer304 = ByteBuffer.wrap("HTTP/1.1 304 Not Modified\r\n\r\n".getBytes()).asReadOnlyBuffer(); 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(); 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[] LINE = new byte[]{'\r', '\n'}; protected static final byte[] LINE = new byte[]{'\r', '\n'};
protected static final byte[] serverNameBytes = ("Server: redkale/" + Redkale.getDotedVersion() + "\r\n").getBytes();
private static final Set<OpenOption> options = new HashSet<>(); private static final Set<OpenOption> options = new HashSet<>();
private static final DateFormat GMT_DATE_FORMAT = new SimpleDateFormat("EEE, dd-MMM-yyyy HH:mm:ss z", Locale.ENGLISH); private static final DateFormat GMT_DATE_FORMAT = new SimpleDateFormat("EEE, dd-MMM-yyyy HH:mm:ss z", Locale.ENGLISH);
@@ -123,17 +125,21 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
private final String[][] defaultSetHeaders; private final String[][] defaultSetHeaders;
private final boolean autoOptions;
private final HttpCookie defcookie; private final HttpCookie defcookie;
public static ObjectPool<Response> createPool(AtomicLong creatCounter, AtomicLong cycleCounter, int max, Creator<Response> creator) { 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()); 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) { public HttpResponse(HttpContext context, HttpRequest request, String[][] defaultAddHeaders, String[][] defaultSetHeaders,
HttpCookie defcookie, boolean autoOptions) {
super(context, request); super(context, request);
this.defaultAddHeaders = defaultAddHeaders; this.defaultAddHeaders = defaultAddHeaders;
this.defaultSetHeaders = defaultSetHeaders; this.defaultSetHeaders = defaultSetHeaders;
this.defcookie = defcookie; this.defcookie = defcookie;
this.autoOptions = autoOptions;
} }
@Override @Override
@@ -143,6 +149,7 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
@Override @Override
protected boolean recycle() { protected boolean recycle() {
boolean rs = super.recycle();
this.status = 200; this.status = 200;
this.contentLength = -1; this.contentLength = -1;
this.contentType = null; this.contentType = null;
@@ -150,7 +157,7 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
this.headsended = false; this.headsended = false;
this.header.clear(); this.header.clear();
this.bufferHandler = null; this.bufferHandler = null;
return super.recycle(); return rs;
} }
@Override @Override
@@ -178,6 +185,15 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
return v == null ? defValue : v; return v == null ? defValue : v;
} }
@Override
protected void thenEvent(Servlet servlet) {
this.servlet = servlet;
}
protected boolean isAutoOptions() {
return this.autoOptions;
}
/** /**
* 增加Cookie值 * 增加Cookie值
* *
@@ -203,7 +219,7 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
} }
/** /**
* 创建AsyncHandler实例将非字符串对象以JSON格式输出字符串以文本输出 * 创建AsyncHandler实例
* *
* @return AsyncHandler * @return AsyncHandler
*/ */
@@ -222,6 +238,21 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
}); });
} }
/**
* 创建AsyncHandler子类的实例 <br>
*
* 传入的AsyncHandler子类必须是public且保证其子类可被继承且completed、failed可被重载且包含空参数的构造函数。
*
* @param <H> 泛型
* @param handlerClass AsyncHandler子类
*
* @return AsyncHandler AsyncHandler
*/
public <H extends AsyncHandler> H createAsyncHandler(Class<H> handlerClass) {
if (handlerClass == null || handlerClass == AsyncHandler.class) return (H) createAsyncHandler();
return context.loadAsyncHandlerCreator(handlerClass).create(createAsyncHandler());
}
/** /**
* 将对象以JSON格式输出 * 将对象以JSON格式输出
* *
@@ -360,6 +391,8 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
} }
if (v instanceof CharSequence) { if (v instanceof CharSequence) {
finish(v.toString()); finish(v.toString());
} else if (v instanceof HttpResult) {
finishJson(convert, (HttpResult) v);
} else if (v instanceof org.redkale.service.RetResult) { } else if (v instanceof org.redkale.service.RetResult) {
finishJson(convert, (org.redkale.service.RetResult) v); finishJson(convert, (org.redkale.service.RetResult) v);
} else { } else {
@@ -368,6 +401,44 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
}); });
} }
/**
* 将HttpResult的结果对象以JSON格式输出
*
* @param result HttpResult对象
*/
public void finishJson(final HttpResult result) {
finishJson(request.getJsonConvert(), result);
}
/**
* 将HttpResult的结果对象以JSON格式输出
*
* @param convert 指定的JsonConvert
* @param result HttpResult对象
*/
public void finishJson(final JsonConvert convert, final HttpResult result) {
if (output == null) {
finish("");
return;
}
if (result.getContentType() != null) setContentType(result.getContentType());
addHeader(result.getHeaders()).addCookie(result.getCookies()).setStatus(result.getStatus() < 1 ? 200 : result.getStatus());
if (result.getResult() instanceof File) {
try {
finish((File) result.getResult());
} catch (IOException e) {
getContext().getLogger().log(Level.WARNING, "HttpServlet finishJson HttpResult File occur, forece to close channel. request = " + getRequest(), e);
finish(500, null);
}
} else if (result.getResult() instanceof String) {
finish((String) result.getResult());
} else if (result.getResult() == null) {
finish(result.getMessage());
} else {
finishJson(result.getResult());
}
}
/** /**
* 将指定字符串以响应结果输出 * 将指定字符串以响应结果输出
* *
@@ -377,6 +448,7 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
if (isClosed()) return; if (isClosed()) return;
if (this.recycleListener != null) this.output = obj; if (this.recycleListener != null) this.output = obj;
if (obj == null || obj.isEmpty()) { if (obj == null || obj.isEmpty()) {
this.contentLength = 0;
final ByteBuffer headbuf = createHeader(); final ByteBuffer headbuf = createHeader();
headbuf.flip(); headbuf.flip();
super.finish(headbuf); super.finish(headbuf);
@@ -642,8 +714,8 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
final String match = request.getHeader("If-None-Match"); final String match = request.getHeader("If-None-Match");
final String etag = (file == null ? 0L : file.lastModified()) + "-" + length; final String etag = (file == null ? 0L : file.lastModified()) + "-" + length;
if (match != null && etag.equals(match)) { if (match != null && etag.equals(match)) {
finish304(); //finish304();
return; //return;
} }
this.contentLength = length; this.contentLength = length;
if (filename != null && !filename.isEmpty() && file != null) { if (filename != null && !filename.isEmpty() && file != null) {
@@ -671,18 +743,20 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
ByteBuffer hbuffer = createHeader(); ByteBuffer hbuffer = createHeader();
hbuffer.flip(); hbuffer.flip();
if (fileBody == null) { if (fileBody == null) {
if (this.recycleListener != null) this.output = file;
finishFile(hbuffer, file, start, len); finishFile(hbuffer, file, start, len);
} else { } else {
if (start >= 0) { if (start >= 0) {
fileBody.position((int) start); fileBody.position((int) start);
if (len > 0) fileBody.limit((int) (fileBody.position() + len)); if (len > 0) fileBody.limit((int) (fileBody.position() + len));
} }
if (this.recycleListener != null) this.output = fileBody;
super.finish(hbuffer, fileBody); super.finish(hbuffer, fileBody);
} }
} }
private void finishFile(ByteBuffer hbuffer, File file, long offset, long length) throws IOException { private void finishFile(ByteBuffer hbuffer, File file, long offset, long length) throws IOException {
this.channel.write(hbuffer, hbuffer, new TransferFileHandler(AsynchronousFileChannel.open(file.toPath(), options, ((HttpContext) context).getExecutor()), offset, length)); this.channel.write(hbuffer, hbuffer, new TransferFileHandler(file, offset, length));
} }
private ByteBuffer createHeader() { private ByteBuffer createHeader() {
@@ -692,12 +766,14 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
buffer.put(("Content-Type: " + (this.contentType == null ? "text/plain; charset=utf-8" : this.contentType) + "\r\n").getBytes()); buffer.put(("Content-Type: " + (this.contentType == null ? "text/plain; charset=utf-8" : this.contentType) + "\r\n").getBytes());
if (this.contentLength > 0) { if (this.contentLength >= 0) {
buffer.put(("Content-Length: " + this.contentLength + "\r\n").getBytes()); buffer.put(("Content-Length: " + this.contentLength + "\r\n").getBytes());
} }
if (!this.request.isKeepAlive()) { if (!this.request.isKeepAlive()) {
buffer.put("Connection: close\r\n".getBytes()); buffer.put("Connection: close\r\n".getBytes());
} }
buffer.put(serverNameBytes);
if (this.defaultAddHeaders != null) { if (this.defaultAddHeaders != null) {
for (String[] headers : this.defaultAddHeaders) { for (String[] headers : this.defaultAddHeaders) {
if (headers.length > 3) { if (headers.length > 3) {
@@ -912,6 +988,8 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
protected final class TransferFileHandler implements AsyncHandler<Integer, ByteBuffer> { protected final class TransferFileHandler implements AsyncHandler<Integer, ByteBuffer> {
private final File file;
private final AsynchronousFileChannel filechannel; private final AsynchronousFileChannel filechannel;
private final long max; //需要读取的字节数, -1表示读到文件结尾 private final long max; //需要读取的字节数, -1表示读到文件结尾
@@ -924,23 +1002,36 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
private boolean read = true; private boolean read = true;
public TransferFileHandler(AsynchronousFileChannel channel) { public TransferFileHandler(File file) throws IOException {
this.filechannel = channel; this.file = file;
this.max = -1; this.filechannel = AsynchronousFileChannel.open(file.toPath(), options, ((HttpContext) context).getExecutor());
this.position = 0;
this.max = file.length();
} }
public TransferFileHandler(AsynchronousFileChannel channel, long offset, long len) { public TransferFileHandler(File file, long offset, long len) throws IOException {
this.filechannel = channel; this.file = file;
this.filechannel = AsynchronousFileChannel.open(file.toPath(), options, ((HttpContext) context).getExecutor());
this.position = offset <= 0 ? 0 : offset; this.position = offset <= 0 ? 0 : offset;
this.max = len; this.max = len <= 0 ? file.length() : len;
} }
@Override @Override
public void completed(Integer result, ByteBuffer attachment) { public void completed(Integer result, ByteBuffer attachment) {
if (result < 0 || (max > 0 && count >= max)) { //(Thread.currentThread().getName() + "-----------" + file + "-------------------result: " + result + ", max = " + max + ", count = " + count);
if (result < 0 || count >= max) {
failed(null, attachment); failed(null, attachment);
return; return;
} }
if (!next && attachment.hasRemaining()) { //Header还没写完
channel.write(attachment, attachment, this);
return;
}
if (next && read && attachment.hasRemaining()) { //Buffer还没写完
channel.write(attachment, attachment, this);
return;
}
if (read) { if (read) {
read = false; read = false;
if (next) { if (next) {
@@ -952,14 +1043,16 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
filechannel.read(attachment, position, attachment, this); filechannel.read(attachment, position, attachment, this);
} else { } else {
read = true; read = true;
if (max > 0) { count += result;
count += result; if (count > max) {
if (count > max) { attachment.limit((int) (attachment.position() + max - count));
attachment.limit((int) (attachment.position() + max - count));
}
} }
attachment.flip(); attachment.flip();
channel.write(attachment, attachment, this); if (attachment.hasRemaining()) {
channel.write(attachment, attachment, this);
} else {
failed(null, attachment);
}
} }
} }

View File

@@ -8,6 +8,7 @@ package org.redkale.net.http;
import java.io.Serializable; import java.io.Serializable;
import java.net.HttpCookie; import java.net.HttpCookie;
import java.util.*; import java.util.*;
import org.redkale.convert.json.JsonConvert;
/** /**
* *
@@ -17,7 +18,9 @@ import java.util.*;
* @author zhangjx * @author zhangjx
* @param <T> 结果对象的类型 * @param <T> 结果对象的类型
*/ */
public class RestOutput<T> { public class HttpResult<T> {
public static final String SESSIONID_COOKIENAME = HttpRequest.SESSIONID_NAME;
private Map<String, String> headers; private Map<String, String> headers;
@@ -31,25 +34,49 @@ public class RestOutput<T> {
private String message; private String message;
public RestOutput() { public HttpResult() {
} }
public RestOutput(T result) { public HttpResult(T result) {
this.result = result; this.result = result;
} }
public RestOutput<T> addHeader(String name, Serializable value) { public HttpResult<T> header(String name, Serializable value) {
if (this.headers == null) this.headers = new HashMap<>(); if (this.headers == null) this.headers = new HashMap<>();
this.headers.put(name, String.valueOf(value)); this.headers.put(name, String.valueOf(value));
return this; return this;
} }
public RestOutput<T> addCookie(HttpCookie cookie) { public HttpResult<T> cookie(String name, Serializable value) {
return cookie(new HttpCookie(name, String.valueOf(value)));
}
public HttpResult<T> cookie(HttpCookie cookie) {
if (this.cookies == null) this.cookies = new ArrayList<>(); if (this.cookies == null) this.cookies = new ArrayList<>();
this.cookies.add(cookie); this.cookies.add(cookie);
return this; return this;
} }
public HttpResult<T> contentType(String contentType) {
this.contentType = contentType;
return this;
}
public HttpResult<T> result(T result) {
this.result = result;
return this;
}
public HttpResult<T> status(int status) {
this.status = status;
return this;
}
public HttpResult<T> message(String message) {
this.message = message;
return this;
}
public Map<String, String> getHeaders() { public Map<String, String> getHeaders() {
return headers; return headers;
} }
@@ -98,4 +125,8 @@ public class RestOutput<T> {
this.message = message; this.message = message;
} }
@Override
public String toString() {
return JsonConvert.root().convertTo(this);
}
} }

View File

@@ -10,10 +10,11 @@ import java.net.HttpCookie;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.util.*; import java.util.*;
import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicLong;
import java.util.logging.Level;
import org.redkale.net.*; import org.redkale.net.*;
import org.redkale.net.sncp.Sncp;
import org.redkale.service.Service; import org.redkale.service.Service;
import org.redkale.util.*; import org.redkale.util.*;
import org.redkale.watch.WatchFactory;
/** /**
* Http服务器 * Http服务器
@@ -23,14 +24,14 @@ import org.redkale.watch.WatchFactory;
* *
* @author zhangjx * @author zhangjx
*/ */
public final class HttpServer extends Server<String, HttpContext, HttpRequest, HttpResponse, HttpServlet> { public class HttpServer extends Server<String, HttpContext, HttpRequest, HttpResponse, HttpServlet> {
public HttpServer() { public HttpServer() {
this(System.currentTimeMillis(), null); this(System.currentTimeMillis());
} }
public HttpServer(long serverStartTime, final WatchFactory watch) { public HttpServer(long serverStartTime) {
super(serverStartTime, "TCP", new HttpPrepareServlet(), watch); super(serverStartTime, "TCP", new HttpPrepareServlet());
} }
@Override @Override
@@ -38,13 +39,91 @@ public final class HttpServer extends Server<String, HttpContext, HttpRequest, H
super.init(config); super.init(config);
} }
public List<HttpServlet> getHttpServlets() {
return this.prepare.getServlets();
}
public List<HttpFilter> getHttpFilters() {
return this.prepare.getFilters();
}
/** /**
* 获取静态资源HttpServlet * 获取静态资源HttpServlet
* *
* @return HttpServlet * @return HttpServlet
*/ */
public HttpServlet getResourceServlet() { public HttpResourceServlet getResourceServlet() {
return ((HttpPrepareServlet) this.prepare).resourceHttpServlet; return (HttpResourceServlet) ((HttpPrepareServlet) this.prepare).resourceHttpServlet;
}
/**
* 删除HttpServlet
*
* @param service Service
*
* @return HttpServlet
*/
public HttpServlet removeHttpServlet(Service service) {
return ((HttpPrepareServlet) this.prepare).removeHttpServlet(service);
}
/**
* 删除HttpServlet
*
* @param <T> 泛型
* @param websocketOrServletType Class
*
* @return HttpServlet
*/
public <T extends WebSocket> HttpServlet removeHttpServlet(Class<T> websocketOrServletType) {
return ((HttpPrepareServlet) this.prepare).removeHttpServlet(websocketOrServletType);
}
/**
* 屏蔽请求URL的正则表达式
*
* @param urlreg 正则表达式
*
* @return 是否成功
*/
public boolean addForbidURIReg(final String urlreg) {
return ((HttpPrepareServlet) this.prepare).addForbidURIReg(urlreg);
}
/**
* 删除屏蔽请求URL的正则表达式
*
* @param urlreg 正则表达式
*
* @return 是否成功
*/
public boolean removeForbidURIReg(final String urlreg) {
return ((HttpPrepareServlet) this.prepare).removeForbidURIReg(urlreg);
}
/**
* 删除HttpFilter
*
* @param <T> 泛型
* @param filterClass HttpFilter类
*
* @return HttpFilter
*/
public <T extends HttpFilter> T removeHttpFilter(Class<T> filterClass) {
return (T) this.prepare.removeFilter(filterClass);
}
/**
* 添加HttpFilter
*
* @param filter HttpFilter
* @param conf AnyValue
*
* @return HttpServer
*/
public HttpServer addHttpFilter(HttpFilter filter, AnyValue conf) {
this.prepare.addFilter(filter, conf);
return this;
} }
/** /**
@@ -90,55 +169,75 @@ public final class HttpServer extends Server<String, HttpContext, HttpRequest, H
} }
/** /**
* 添加RestHttpServlet * 添加WebSocketServlet
* *
* @param <S> Service * @param <S> WebSocket
* @param <T> RestHttpServlet * @param <T> HttpServlet
* @param name Service的资源名 * @param classLoader ClassLoader
* @param serviceType Service的类型 * @param webSocketType WebSocket的类型
* @param service Service对象 * @param prefix url前缀
* @param baseServletClass RestHttpServlet基类 * @param conf 配置信息
* @param prefix url前缀
* *
* @return RestHttpServlet * @return RestServlet
*/ */
public <S extends Service, T extends RestHttpServlet> RestHttpServlet addRestServlet(String name, Class<S> serviceType, S service, Class<T> baseServletClass, String prefix) { public <S extends WebSocket, T extends HttpServlet> T addRestWebSocketServlet(final ClassLoader classLoader, final Class<S> webSocketType, final String prefix, final AnyValue conf) {
return addRestServlet(name, serviceType, service, baseServletClass, prefix, null); T servlet = Rest.createRestWebSocketServlet(classLoader, webSocketType);
if (servlet != null) this.prepare.addServlet(servlet, prefix, conf);
return servlet;
} }
/** /**
* 添加RestHttpServlet * 添加RestServlet
* *
* @param <S> Service * @param <S> Service
* @param <T> RestHttpServlet * @param <T> HttpServlet
* @param name Service的资源名 * @param classLoader ClassLoader
* @param serviceType Service的类型 * @param service Service对象
* @param service Service对象 * @param userType 用户数据类型
* @param baseServletClass RestHttpServlet基类 * @param baseServletType RestServlet基类
* @param prefix url前缀 * @param prefix url前缀
* @param conf 配置信息
* *
* @return RestHttpServlet * @return RestServlet
*/ */
public <S extends Service, T extends RestHttpServlet> RestHttpServlet addRestServlet( public <S extends Service, T extends HttpServlet> T addRestServlet(final ClassLoader classLoader, final S service, final Class userType, final Class<T> baseServletType, final String prefix) {
final String name, Class<S> serviceType, final S service, final Class<T> baseServletClass, final String prefix, AnyValue conf) { return addRestServlet(classLoader, null, service, userType, baseServletType, prefix);
RestHttpServlet servlet = null; }
/**
* 添加RestServlet
*
* @param <S> Service
* @param <T> HttpServlet
* @param classLoader ClassLoader
* @param name 资源名
* @param service Service对象
* @param userType 用户数据类型
* @param baseServletType RestServlet基类
* @param prefix url前缀
*
* @return RestServlet
*/
public <S extends Service, T extends HttpServlet> T addRestServlet(final ClassLoader classLoader, final String name, final S service, final Class userType, final Class<T> baseServletType, final String prefix) {
T servlet = null;
final boolean sncp = Sncp.isSncpDyn(service);
final String resname = name == null ? (sncp ? Sncp.getResourceName(service) : "") : name;
final Class<S> serviceType = Sncp.getServiceType(service);
for (final HttpServlet item : ((HttpPrepareServlet) this.prepare).getServlets()) { for (final HttpServlet item : ((HttpPrepareServlet) this.prepare).getServlets()) {
if (!(item instanceof RestHttpServlet)) continue; if (!(item instanceof HttpServlet)) continue;
if (item.getClass().getAnnotation(Rest.RestDynamic.class) == null) continue; if (item.getClass().getAnnotation(Rest.RestDyn.class) == null) continue;
try { try {
Field field = item.getClass().getDeclaredField(Rest.REST_SERVICE_FIELD_NAME); Field field = item.getClass().getDeclaredField(Rest.REST_SERVICE_FIELD_NAME);
if (serviceType.equals(field.getType())) { if (serviceType.equals(field.getType())) {
servlet = (RestHttpServlet) item; servlet = (T) item;
break; break;
} }
} catch (NoSuchFieldException | SecurityException e) { } catch (NoSuchFieldException | SecurityException e) {
System.err.println("serviceType = " + serviceType + ", servletClass = " + item.getClass()); logger.log(Level.SEVERE, "serviceType = " + serviceType + ", servletClass = " + item.getClass(), e);
e.printStackTrace();
} }
} }
final boolean first = servlet == null; final boolean first = servlet == null;
if (servlet == null) servlet = Rest.createRestServlet(baseServletClass, serviceType); if (servlet == null) servlet = Rest.createRestServlet(classLoader, userType, baseServletType, serviceType);
if (servlet == null) return null; //没有HttpMapping方法的HttpServlet调用Rest.createRestServlet就会返回null
try { //若提供动态变更Service服务功能则改Rest服务无法做出相应更新 try { //若提供动态变更Service服务功能则改Rest服务无法做出相应更新
Field field = servlet.getClass().getDeclaredField(Rest.REST_SERVICE_FIELD_NAME); Field field = servlet.getClass().getDeclaredField(Rest.REST_SERVICE_FIELD_NAME);
field.setAccessible(true); field.setAccessible(true);
@@ -147,21 +246,21 @@ public final class HttpServer extends Server<String, HttpContext, HttpRequest, H
mapfield.setAccessible(true); mapfield.setAccessible(true);
Service firstService = (Service) field.get(servlet); Service firstService = (Service) field.get(servlet);
if (name.isEmpty()) { if (resname.isEmpty()) {
field.set(servlet, service); field.set(servlet, service);
firstService = service; firstService = service;
} }
Map map = (Map) mapfield.get(servlet); Map map = (Map) mapfield.get(servlet);
if (map == null && !name.isEmpty()) map = new HashMap(); if (map == null && !resname.isEmpty()) map = new HashMap();
if (map != null) { if (map != null) {
map.put(name, service); map.put(resname, service);
if (firstService != null) map.put("", firstService); if (firstService != null) map.put("", firstService);
} }
mapfield.set(servlet, map); mapfield.set(servlet, map);
} catch (Exception e) { } catch (Exception e) {
throw new RuntimeException(serviceType + " generate rest servlet error", e); throw new RuntimeException(serviceType + " generate rest servlet error", e);
} }
if (first) this.prepare.addServlet(servlet, prefix, conf); if (first) this.prepare.addServlet(servlet, prefix, sncp ? Sncp.getConf(service) : null);
return servlet; return servlet;
} }
@@ -169,8 +268,8 @@ public final class HttpServer extends Server<String, HttpContext, HttpRequest, H
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
protected HttpContext createContext() { protected HttpContext createContext() {
final int port = this.address.getPort(); final int port = this.address.getPort();
AtomicLong createBufferCounter = watch == null ? new AtomicLong() : watch.createWatchNumber("HTTP_" + port + ".Buffer.creatCounter"); AtomicLong createBufferCounter = new AtomicLong();
AtomicLong cycleBufferCounter = watch == null ? new AtomicLong() : watch.createWatchNumber("HTTP_" + port + ".Buffer.cycleCounter"); AtomicLong cycleBufferCounter = new AtomicLong();
this.bufferCapacity = Math.max(this.bufferCapacity, 16 * 1024 + 16); //兼容 HTTP 2.0; this.bufferCapacity = Math.max(this.bufferCapacity, 16 * 1024 + 16); //兼容 HTTP 2.0;
final int rcapacity = this.bufferCapacity; final int rcapacity = this.bufferCapacity;
ObjectPool<ByteBuffer> bufferPool = new ObjectPool<>(createBufferCounter, cycleBufferCounter, this.bufferPoolSize, ObjectPool<ByteBuffer> bufferPool = new ObjectPool<>(createBufferCounter, cycleBufferCounter, this.bufferPoolSize,
@@ -181,6 +280,8 @@ public final class HttpServer extends Server<String, HttpContext, HttpRequest, H
}); });
final List<String[]> defaultAddHeaders = new ArrayList<>(); final List<String[]> defaultAddHeaders = new ArrayList<>();
final List<String[]> defaultSetHeaders = new ArrayList<>(); final List<String[]> defaultSetHeaders = new ArrayList<>();
boolean autoOptions = false;
HttpCookie defaultCookie = null; HttpCookie defaultCookie = null;
String remoteAddrHeader = null; String remoteAddrHeader = null;
if (config != null) { if (config != null) {
@@ -243,18 +344,23 @@ public final class HttpServer extends Server<String, HttpContext, HttpRequest, H
defaultCookie.setPath(path); defaultCookie.setPath(path);
} }
} }
AnyValue options = resps == null ? null : resps.getAnyValue("options");
autoOptions = options != null && options.getBoolValue("auto", false);
} }
} }
final String[][] addHeaders = defaultAddHeaders.isEmpty() ? null : defaultAddHeaders.toArray(new String[defaultAddHeaders.size()][]); 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 String[][] setHeaders = defaultSetHeaders.isEmpty() ? null : defaultSetHeaders.toArray(new String[defaultSetHeaders.size()][]);
final boolean options = autoOptions;
final HttpCookie defCookie = defaultCookie; final HttpCookie defCookie = defaultCookie;
final String addrHeader = remoteAddrHeader; final String addrHeader = remoteAddrHeader;
AtomicLong createResponseCounter = watch == null ? new AtomicLong() : watch.createWatchNumber("HTTP_" + port + ".Response.creatCounter"); AtomicLong createResponseCounter = new AtomicLong();
AtomicLong cycleResponseCounter = watch == null ? new AtomicLong() : watch.createWatchNumber("HTTP_" + port + ".Response.cycleCounter"); AtomicLong cycleResponseCounter = new AtomicLong();
ObjectPool<Response> responsePool = HttpResponse.createPool(createResponseCounter, cycleResponseCounter, this.responsePoolSize, null); ObjectPool<Response> responsePool = HttpResponse.createPool(createResponseCounter, cycleResponseCounter, this.responsePoolSize, null);
HttpContext httpcontext = new HttpContext(this.serverStartTime, this.logger, executor, rcapacity, bufferPool, responsePool, HttpContext httpcontext = new HttpContext(this.serverStartTime, this.logger, executor, rcapacity, bufferPool, responsePool,
this.maxbody, this.charset, this.address, this.prepare, this.watch, this.readTimeoutSecond, this.writeTimeoutSecond); 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)); responsePool.setCreator((Object... params) -> new HttpResponse(httpcontext, new HttpRequest(httpcontext, addrHeader), addHeaders, setHeaders, defCookie, options));
return httpcontext; return httpcontext;
} }

View File

@@ -5,17 +5,362 @@
*/ */
package org.redkale.net.http; package org.redkale.net.http;
import org.redkale.net.Servlet; import java.io.IOException;
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 org.redkale.net.*;
import org.redkale.service.RetResult;
import org.redkale.util.*;
/** /**
* HTTP版的Servlet 执行顺序 execute --&#62; preExecute --&#62; authenticate --&#62; HttpMapping对应的方法
* *
* <p> * <p>
* 详情见: https://redkale.org * 详情见: https://redkale.org
* *
* @author zhangjx * @author zhangjx
*/ */
public abstract class HttpServlet extends Servlet<HttpContext, HttpRequest, HttpResponse> { public class HttpServlet extends Servlet<HttpContext, HttpRequest, HttpResponse> {
public static final int RET_SERVER_ERROR = 1200_0001;
public static final int RET_METHOD_ERROR = 1200_0002;
String _prefix = ""; //当前HttpServlet的path前缀 String _prefix = ""; //当前HttpServlet的path前缀
private Map.Entry<String, Entry>[] mappings;
//这里不能直接使用HttpServlet会造成死循环初始化HttpServlet
private final Servlet<HttpContext, HttpRequest, HttpResponse> authSuccessServlet = new Servlet<HttpContext, HttpRequest, HttpResponse>() {
@Override
public void execute(HttpRequest request, HttpResponse response) throws IOException {
Entry entry = (Entry) request.attachment;
if (entry.cacheseconds > 0) {//有缓存设置
CacheEntry ce = entry.cache.get(request.getRequestURI());
if (ce != null && ce.time + entry.cacheseconds > System.currentTimeMillis()) { //缓存有效
response.setStatus(ce.status);
response.setContentType(ce.contentType);
response.finish(ce.getBuffers());
return;
}
response.setBufferHandler(entry.cacheHandler);
}
entry.servlet.execute(request, response);
}
};
//preExecute运行完后执行的Servlet
private final Servlet<HttpContext, HttpRequest, HttpResponse> preSuccessServlet = new Servlet<HttpContext, HttpRequest, HttpResponse>() {
@Override
public void execute(HttpRequest request, HttpResponse response) throws IOException {
for (Map.Entry<String, Entry> en : mappings) {
if (request.getRequestURI().startsWith(en.getKey())) {
Entry entry = en.getValue();
if (!entry.checkMethod(request.getMethod())) {
response.finishJson(new RetResult(RET_METHOD_ERROR, "Method(" + request.getMethod() + ") Error"));
return;
}
request.attachment = entry;
request.moduleid = entry.moduleid;
request.actionid = entry.actionid;
if (entry.ignore) {
authSuccessServlet.execute(request, response);
} else {
response.thenEvent(authSuccessServlet);
authenticate(request, response);
}
return;
}
}
throw new IOException(this.getClass().getName() + " not found method for URI(" + request.getRequestURI() + ")");
}
};
void preInit(HttpContext context, AnyValue config) {
String path = _prefix == null ? "" : _prefix;
WebServlet ws = this.getClass().getAnnotation(WebServlet.class);
if (ws != null && !ws.repair()) path = "";
HashMap<String, Entry> map = load();
this.mappings = new Map.Entry[map.size()];
int i = -1;
for (Map.Entry<String, Entry> en : map.entrySet()) {
mappings[++i] = new AbstractMap.SimpleEntry<>(path + en.getKey(), en.getValue());
}
//必须要倒排序, /query /query1 /query12 确保含子集的优先匹配 /query12 /query1 /query
Arrays.sort(mappings, (o1, o2) -> o2.getKey().compareTo(o1.getKey()));
}
void postDestroy(HttpContext context, AnyValue config) {
}
/**
* <p>
* 预执行方法在execute方法之前运行设置当前用户信息或者加入常规统计和基础检测例如 : <br>
* <blockquote><pre>
* &#64;Override
* public void preExecute(final HttpRequest request, final HttpResponse response) throws IOException {
* //设置当前用户信息
* final String sessionid = request.getSessionid(false);
* if (sessionid != null) request.setCurrentUser(userService.current(sessionid));
*
* if (finer) response.recycleListener((req, resp) -&#62; { //记录处理时间比较长的请求
* long e = System.currentTimeMillis() - ((HttpRequest) req).getCreatetime();
* if (e &#62; 200) logger.finer("http-execute-cost-time: " + e + " ms. request = " + req);
* });
* response.nextEvent();
* }
* </pre></blockquote>
* <p>
*
* @param request HttpRequest
* @param response HttpResponse
*
* @throws IOException IOException
*/
protected void preExecute(HttpRequest request, HttpResponse response) throws IOException {
response.nextEvent();
}
/**
* <p>
* 用户登录或权限验证, 注解为&#64;HttpMapping.auth == true 的方法会执行authenticate方法, 若验证成功则必须调用response.nextEvent();进行下一步操作, 例如: <br>
* <blockquote><pre>
* &#64;Override
* public void authenticate(HttpRequest request, HttpResponse response) throws IOException {
* UserInfo info = request.currentUser();
* if (info == null) {
* response.finishJson(RET_UNLOGIN);
* return;
* } else if (!info.checkAuth(request.getModuleid(), request.getActionid())) {
* response.finishJson(RET_AUTHILLEGAL);
* return;
* }
* response.nextEvent();
* }
* </pre></blockquote>
* <p>
*
*
* @param request HttpRequest
* @param response HttpResponse
*
* @throws IOException IOException
*/
protected void authenticate(HttpRequest request, HttpResponse response) throws IOException {
response.nextEvent();
}
@Override
public void execute(HttpRequest request, HttpResponse response) throws IOException {
response.thenEvent(preSuccessServlet);
preExecute(request, response);
}
private HashMap<String, Entry> load() {
WebServlet module = this.getClass().getAnnotation(WebServlet.class);
final int serviceid = module == null ? 0 : module.moduleid();
final HashMap<String, Entry> map = new HashMap<>();
HashMap<String, Class> nameset = new HashMap<>();
final Class selfClz = this.getClass();
Class clz = this.getClass();
do {
if (java.lang.reflect.Modifier.isAbstract(clz.getModifiers())) break;
for (final Method method : clz.getMethods()) {
//-----------------------------------------------
String methodname = method.getName();
if ("service".equals(methodname) || "preExecute".equals(methodname) || "execute".equals(methodname) || "authenticate".equals(methodname)) continue;
//-----------------------------------------------
Class[] paramTypes = method.getParameterTypes();
if (paramTypes.length != 2 || paramTypes[0] != HttpRequest.class
|| paramTypes[1] != HttpResponse.class) continue;
//-----------------------------------------------
Class[] exps = method.getExceptionTypes();
if (exps.length > 0 && (exps.length != 1 || exps[0] != IOException.class)) continue;
//-----------------------------------------------
final HttpMapping mapping = method.getAnnotation(HttpMapping.class);
if (mapping == null) continue;
final boolean inherited = mapping.inherited();
if (!inherited && selfClz != clz) continue; //忽略不被继承的方法
final int actionid = mapping.actionid();
final String name = mapping.url().trim();
final String[] methods = mapping.methods();
if (nameset.containsKey(name)) {
if (nameset.get(name) != clz) continue;
throw new RuntimeException(this.getClass().getSimpleName() + " have two same " + HttpMapping.class.getSimpleName() + "(" + name + ")");
}
nameset.put(name, clz);
map.put(name, new Entry(serviceid, actionid, name, methods, method, createHttpServlet(method)));
}
} while ((clz = clz.getSuperclass()) != HttpServlet.class);
return map;
}
private HttpServlet createHttpServlet(final Method method) {
//------------------------------------------------------------------------------
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);
String newDynName = interName + "_Dyn_" + method.getName();
int i = 0;
for (;;) {
try {
Thread.currentThread().getContextClassLoader().loadClass(newDynName.replace('/', '.'));
newDynName += "_" + (++i);
} catch (Throwable ex) {
break;
}
}
//------------------------------------------------------------------------------
ClassWriter cw = new ClassWriter(COMPUTE_FRAMES);
FieldVisitor fv;
MethodVisitor mv;
AnnotationVisitor av0;
final String factfield = "_factServlet";
cw.visit(V1_8, ACC_PUBLIC + ACC_FINAL + ACC_SUPER, newDynName, null, supDynName, null);
{
fv = cw.visitField(ACC_PUBLIC, factfield, interDesc, null, null);
fv.visitEnd();
}
{ //构造函数
mv = (cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null));
//mv.setDebug(true);
mv.visitVarInsn(ALOAD, 0);
mv.visitMethodInsn(INVOKESPECIAL, supDynName, "<init>", "()V", false);
mv.visitInsn(RETURN);
mv.visitMaxs(1, 1);
mv.visitEnd();
}
{
mv = (cw.visitMethod(ACC_PUBLIC, "execute", "(" + requestDesc + responseDesc + ")V", null, new String[]{"java/io/IOException"}));
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, newDynName, factfield, interDesc);
mv.visitVarInsn(ALOAD, 1);
mv.visitVarInsn(ALOAD, 2);
mv.visitMethodInsn(INVOKEVIRTUAL, interName, method.getName(), "(" + requestDesc + responseDesc + ")V", false);
mv.visitInsn(RETURN);
mv.visitMaxs(3, 3);
mv.visitEnd();
}
{
mv = cw.visitMethod(ACC_PUBLIC + ACC_BRIDGE + ACC_SYNTHETIC, "execute", "(" + requestSupDesc + responseSupDesc + ")V", null, new String[]{"java/io/IOException"});
mv.visitVarInsn(ALOAD, 0);
mv.visitVarInsn(ALOAD, 1);
mv.visitTypeInsn(CHECKCAST, HttpRequest.class.getName().replace('.', '/'));
mv.visitVarInsn(ALOAD, 2);
mv.visitTypeInsn(CHECKCAST, HttpResponse.class.getName().replace('.', '/'));
mv.visitMethodInsn(INVOKEVIRTUAL, newDynName, "execute", "(" + requestDesc + responseDesc + ")V", false);
mv.visitInsn(RETURN);
mv.visitMaxs(3, 3);
mv.visitEnd();
}
cw.visitEnd();
//------------------------------------------------------------------------------
byte[] bytes = cw.toByteArray();
Class<?> newClazz = new ClassLoader(this.getClass().getClassLoader()) {
public final Class<?> loadClass(String name, byte[] b) {
return defineClass(name, b, 0, b.length);
}
}.loadClass(newDynName.replace('/', '.'), bytes);
try {
HttpServlet instance = (HttpServlet) newClazz.newInstance();
instance.getClass().getField(factfield).set(instance, this);
return instance;
} catch (Exception ex) {
throw new RuntimeException(ex);
}
}
private static final class Entry {
public Entry(int moduleid, int actionid, String name, String[] methods, Method method, HttpServlet servlet) {
this.moduleid = moduleid;
this.actionid = actionid;
this.name = name;
this.methods = methods;
this.method = method;
this.servlet = servlet;
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.cacheHandler = cacheseconds > 0 ? (HttpResponse response, ByteBuffer[] buffers) -> {
int status = response.getStatus();
if (status != 200) return null;
CacheEntry ce = new CacheEntry(response.getStatus(), response.getContentType(), buffers);
cache.put(response.getRequest().getRequestURI(), ce);
return ce.getBuffers();
} : null;
}
public boolean isNeedCheck() {
return this.moduleid != 0 || this.actionid != 0;
}
public boolean checkMethod(final String reqMethod) {
if (methods.length == 0) return true;
for (String m : methods) {
if (reqMethod.equalsIgnoreCase(m)) return true;
}
return false;
}
public final HttpResponse.BufferHandler cacheHandler;
public final ConcurrentHashMap<String, CacheEntry> cache;
public final int cacheseconds;
public final boolean ignore;
public final int moduleid;
public final int actionid;
public final String name;
public final String[] methods;
public final Method method;
public final HttpServlet servlet;
}
private static final class CacheEntry {
public final long time = System.currentTimeMillis();
private final ByteBuffer[] buffers;
private final int status;
private final String contentType;
public CacheEntry(int status, String contentType, ByteBuffer[] bufs) {
this.status = status;
this.contentType = contentType;
final ByteBuffer[] newBuffers = new ByteBuffer[bufs.length];
for (int i = 0; i < newBuffers.length; i++) {
newBuffers[i] = bufs[i].duplicate().asReadOnlyBuffer();
}
this.buffers = newBuffers;
}
public ByteBuffer[] getBuffers() {
final ByteBuffer[] newBuffers = new ByteBuffer[buffers.length];
for (int i = 0; i < newBuffers.length; i++) {
newBuffers[i] = buffers[i].duplicate();
}
return newBuffers;
}
}
} }

View File

@@ -5,20 +5,26 @@
*/ */
package org.redkale.net.http; package org.redkale.net.http;
import java.lang.annotation.*;
import static java.lang.annotation.ElementType.*; import static java.lang.annotation.ElementType.*;
import java.lang.annotation.*;
import static java.lang.annotation.RetentionPolicy.RUNTIME; import static java.lang.annotation.RetentionPolicy.RUNTIME;
/** /**
* 被标记为 &#64;WebSocketBinary 的WebSocketServlet 使用原始的TCP传输, 通常用于类似音频/视频传输场景 * 配合 HttpServlet 使用
* 用于指定HttpRequest.currentUser的数据类型<br>
* 注意 数据类型是JavaBean则必须要用javax.persistence.Id标记主键字段用于确定用户ID
*
* <p>
* 详情见: https://redkale.org
* *
* <p> 详情见: https://redkale.org
* @author zhangjx * @author zhangjx
*/ */
@Inherited @Inherited
@Documented @Documented
@Target({TYPE}) @Target({TYPE})
@Retention(RUNTIME) @Retention(RUNTIME)
public @interface WebSocketBinary { public @interface HttpUserType {
Class value();
} }

View File

@@ -59,7 +59,7 @@ public final class MultiContext {
public MultiContext(final Charset charsetName, final String contentType, final DefaultAnyValue params, final InputStream in, String fielnameRegex) { public MultiContext(final Charset charsetName, final String contentType, final DefaultAnyValue params, final InputStream in, String fielnameRegex) {
this.charset = charsetName == null ? UTF8 : charsetName; this.charset = charsetName == null ? UTF8 : charsetName;
this.contentType = contentType.trim(); this.contentType = contentType == null ? "" : contentType.trim();
this.parameters = params; this.parameters = params;
this.boundary = parseBoundary(this.contentType); this.boundary = parseBoundary(this.contentType);
this.endboundarray = ("--" + this.boundary + "--").getBytes(); this.endboundarray = ("--" + this.boundary + "--").getBytes();
@@ -87,6 +87,107 @@ public final class MultiContext {
return this.boundary != null; return this.boundary != null;
} }
//或被 REST 用到
/**
* 获取第一个文件的二进制
*
* @param max 可接收的文件大小最大值
* @param filenameReg 可接收的文件名正则表达式
* @param contentTypeReg 可接收的ContentType正则表达式
*
* @return 二进制文件
* @throws IOException IOException
*/
public byte[] partsFirstBytes(final long max, final String filenameReg, final String contentTypeReg) throws IOException {
if (!isMultipart()) return null;
byte[] tmpfile = null;
boolean has = false;
for (MultiPart part : parts()) {
if (has) continue;//不遍历完后面getParameter可能获取不到值
has = true;
if (filenameReg != null && !filenameReg.isEmpty() && !part.getFilename().matches(filenameReg)) continue;
if (contentTypeReg != null && !contentTypeReg.isEmpty() && !part.getContentType().matches(contentTypeReg)) continue;
tmpfile = part.getContentBytes(max < 1 ? Long.MAX_VALUE : max);
}
return tmpfile;
}
//或被 REST 用到
/**
* 获取第一个文件
*
* @param home 进程目录
* @param max 可接收的文件大小最大值
* @param filenameReg 可接收的文件名正则表达式
* @param contentTypeReg 可接收的ContentType正则表达式
*
* @return 文件
* @throws IOException IOException
*/
public File partsFirstFile(final File home, final long max, final String filenameReg, final String contentTypeReg) throws IOException {
if (!isMultipart()) return null;
File tmpfile = null;
boolean has = false;
for (MultiPart part : parts()) {
if (has) continue; //不遍历完后面getParameter可能获取不到值
has = true;
if (filenameReg != null && !filenameReg.isEmpty() && !part.getFilename().matches(filenameReg)) continue;
if (contentTypeReg != null && !contentTypeReg.isEmpty() && !part.getContentType().matches(contentTypeReg)) continue;
String name = part.getFilename();
int pos = name.lastIndexOf('.');
if (pos > 0) {
int pos2 = name.lastIndexOf('.', pos - 1);
if (pos2 >= 0) pos = pos2;
}
File file = new File(home, "tmp/redkale_" + System.nanoTime() + (pos > 0 ? name.substring(pos) : name));
file.getParentFile().mkdirs();
boolean rs = part.save(max < 1 ? Long.MAX_VALUE : max, file);
if (!rs) {
file.delete();
} else {
tmpfile = file;
}
}
return tmpfile;
}
//或被 REST 用到
/**
* 获取所有文件
*
* @param home 进程目录
* @param max 可接收的文件大小最大值
* @param filenameReg 可接收的文件名正则表达式
* @param contentTypeReg 可接收的ContentType正则表达式
*
* @return 文件列表
* @throws IOException IOException
*/
public File[] partsFiles(final File home, final long max, final String filenameReg, final String contentTypeReg) throws IOException {
if (!isMultipart()) return null;
List<File> files = null;
for (MultiPart part : parts()) {
if (filenameReg != null && !filenameReg.isEmpty() && !part.getFilename().matches(filenameReg)) continue;
if (contentTypeReg != null && !contentTypeReg.isEmpty() && !part.getContentType().matches(contentTypeReg)) continue;
String name = part.getFilename();
int pos = name.lastIndexOf('.');
if (pos > 0) {
int pos2 = name.lastIndexOf('.', pos - 1);
if (pos2 >= 0) pos = pos2;
}
File file = new File(home, "tmp/redkale_" + System.nanoTime() + (pos > 0 ? name.substring(pos) : name));
file.getParentFile().mkdirs();
boolean rs = part.save(max < 1 ? Long.MAX_VALUE : max, file);
if (!rs) {
file.delete();
continue;
}
if (files == null) files = new ArrayList<>();
files.add(file);
}
return files == null ? null : files.toArray(new File[files.size()]);
}
/** /**
* 获取上传文件信息列表 * 获取上传文件信息列表
* *

File diff suppressed because it is too large Load Diff

View File

@@ -25,4 +25,10 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME;
@Retention(RUNTIME) @Retention(RUNTIME)
public @interface RestAddress { public @interface RestAddress {
/**
* 备注描述, 对应&#64;HttpParam.comment
*
* @return String
*/
String comment() default "";
} }

View File

@@ -0,0 +1,34 @@
/*
* 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 java.lang.annotation.*;
import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
/**
* 只能注解于RestService类的方法的String/byte[]/JavaBean参数或参数内的String/byte[]/JavaBean字段
* <p>
* 用于获取HTTP请求端的请求内容UTF-8编码字符串、byte[]、JavaBean
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*/
@Inherited
@Documented
@Target({PARAMETER, FIELD})
@Retention(RUNTIME)
public @interface RestBody {
/**
* 备注描述, 对应&#64;HttpParam.comment
*
* @return String
*/
String comment() default "";
}

View File

@@ -0,0 +1,43 @@
/*
* 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 java.lang.annotation.*;
import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
/**
* 只能依附在Service实现类的public方法上, 当方法的返回值以JSON输出时对指定类型的转换设定。 <br>
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*/
@Inherited
@Documented
@Target({METHOD})
@Retention(RUNTIME)
@Repeatable(RestConvert.RestConverts.class)
public @interface RestConvert {
boolean tiny() default true;
Class type();
String[] ignoreColumns() default {};
String[] convertColumns() default {};
@Inherited
@Documented
@Target({METHOD})
@Retention(RUNTIME)
@interface RestConverts {
RestConvert[] value();
}
}

View File

@@ -10,7 +10,7 @@ import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.RUNTIME; import static java.lang.annotation.RetentionPolicy.RUNTIME;
/** /**
* 只能注解于Service类的方法的String参数或参数内的String字段 * 只能注解于RestService类的方法的String参数或参数内的String字段
* <p> * <p>
* 详情见: https://redkale.org * 详情见: https://redkale.org
* *

View File

@@ -10,7 +10,7 @@ import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.RUNTIME; import static java.lang.annotation.RetentionPolicy.RUNTIME;
/** /**
* 只能注解于Service类的方法的参数或参数内的String字段 * 只能注解于RestService类的方法的参数或参数内的String字段
* <p> * <p>
* 详情见: https://redkale.org * 详情见: https://redkale.org
* *

View File

@@ -1,185 +0,0 @@
/*
* 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 java.io.*;
import java.util.concurrent.*;
import java.util.logging.Level;
import jdk.internal.org.objectweb.asm.*;
import static jdk.internal.org.objectweb.asm.Opcodes.*;
import org.redkale.util.*;
/**
*
* 详情见: https://redkale.org
*
* @author zhangjx
* @param <T> 当前用户对象类型
*/
public abstract class RestHttpServlet<T> extends HttpBaseServlet {
protected abstract T currentUser(HttpRequest req) throws IOException;
protected void finishJson(final HttpResponse response, CompletableFuture<RestOutput> future) throws IOException {
future.whenComplete((output, e) -> {
if (e != null) {
response.getContext().getLogger().log(Level.WARNING, "Servlet occur, forece to close channel. request = " + response.getRequest(), e);
response.finish(500, null);
return;
}
try {
finishJson(response, output);
} catch (IOException ioe) {
response.getContext().getLogger().log(Level.WARNING, "Servlet finish RestOutput occur, forece to close channel. request = " + response.getRequest(), ioe);
response.finish(500, null);
}
});
}
protected void finishJson(final HttpResponse response, RestOutput output) throws IOException {
if (output == null) {
response.finishJson(output);
return;
}
if (output.getContentType() != null) response.setContentType(output.getContentType());
response.addHeader(output.getHeaders());
response.addCookie(output.getCookies());
response.setStatus(output.getStatus() < 1 ? 200 : output.getStatus());
if (output.getResult() instanceof File) {
response.finish((File) output.getResult());
} else if (output.getResult() instanceof String) {
response.finish((String) output.getResult());
} else if (output.getResult() == null) {
response.finish(output.getMessage());
} else {
response.finishJson(output.getResult());
}
}
/**
* 创建AsyncHandler实例将非字符串对象以JSON格式输出字符串以文本输出 <br>
*
* 传入的AsyncHandler子类必须是public且保证其子类可被继承且completed、failed可被重载且包含空参数的构造函数。
*
* @param <H> AsyncHandler泛型
* @param response HttpResponse
* @param handlerClass Class
*
* @return AsyncHandler
*/
protected final <H extends AsyncHandler> H createAsyncHandler(HttpResponse response, final Class<H> handlerClass) {
if (handlerClass == null || handlerClass == AsyncHandler.class) return (H) response.createAsyncHandler();
Creator<H> creator = creators.get(handlerClass);
if (creator == null) {
creator = createCreator(handlerClass);
creators.put(handlerClass, creator);
}
return (H) creator.create(response.createAsyncHandler());
}
private static final ConcurrentHashMap<Class, Creator> creators = new ConcurrentHashMap<>();
private static <H extends AsyncHandler> Creator<H> createCreator(Class<H> handlerClass) {
//生成规则与SncpAsyncHandler.Factory 很类似
//-------------------------------------------------------------
final boolean handlerinterface = handlerClass.isInterface();
final String handlerClassName = handlerClass.getName().replace('.', '/');
final String handlerName = AsyncHandler.class.getName().replace('.', '/');
final String handlerDesc = Type.getDescriptor(AsyncHandler.class);
final String newDynName = handlerClass.getName().replace('.', '/') + "_Dync" + AsyncHandler.class.getSimpleName() + "_" + (System.currentTimeMillis() % 10000);
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
FieldVisitor fv;
AsmMethodVisitor 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});
{ //handler 属性
fv = cw.visitField(ACC_PRIVATE, "handler", handlerDesc, null, null);
fv.visitEnd();
}
{//构造方法
mv = new AsmMethodVisitor(cw.visitMethod(ACC_PUBLIC, "<init>", "(" + handlerDesc + ")V", null, null));
//mv.setDebug(true);
{
av0 = mv.visitAnnotation("Ljava/beans/ConstructorProperties;", true);
{
AnnotationVisitor av1 = av0.visitArray("value");
av1.visit(null, "handler");
av1.visitEnd();
}
av0.visitEnd();
}
mv.visitVarInsn(ALOAD, 0);
mv.visitMethodInsn(INVOKESPECIAL, handlerinterface ? "java/lang/Object" : handlerClassName, "<init>", "()V", false);
mv.visitVarInsn(ALOAD, 0);
mv.visitVarInsn(ALOAD, 1);
mv.visitFieldInsn(PUTFIELD, newDynName, "handler", handlerDesc);
mv.visitInsn(RETURN);
mv.visitMaxs(2, 2);
mv.visitEnd();
}
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.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, newDynName, "handler", handlerDesc);
mv.visitVarInsn(ALOAD, 1);
mv.visitVarInsn(ALOAD, 2);
mv.visitMethodInsn(INVOKEINTERFACE, handlerName, "completed", "(Ljava/lang/Object;Ljava/lang/Object;)V", true);
mv.visitInsn(RETURN);
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.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, newDynName, "handler", handlerDesc);
mv.visitVarInsn(ALOAD, 1);
mv.visitVarInsn(ALOAD, 2);
mv.visitMethodInsn(INVOKEINTERFACE, handlerName, "failed", "(Ljava/lang/Throwable;Ljava/lang/Object;)V", true);
mv.visitInsn(RETURN);
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));
Class returnType = method.getReturnType();
if (returnType == void.class) {
mv.visitInsn(RETURN);
mv.visitMaxs(0, 1);
} else if (returnType.isPrimitive()) {
mv.visitInsn(ICONST_0);
if (returnType == long.class) {
mv.visitInsn(LRETURN);
mv.visitMaxs(2, 1);
} else if (returnType == float.class) {
mv.visitInsn(FRETURN);
mv.visitMaxs(2, 1);
} else if (returnType == double.class) {
mv.visitInsn(DRETURN);
mv.visitMaxs(2, 1);
} else {
mv.visitInsn(IRETURN);
mv.visitMaxs(1, 1);
}
} else {
mv.visitInsn(ACONST_NULL);
mv.visitInsn(ARETURN);
mv.visitMaxs(1, 1);
}
mv.visitEnd();
}
}
cw.visitEnd();
byte[] bytes = cw.toByteArray();
Class<AsyncHandler> newHandlerClazz = (Class<AsyncHandler>) new ClassLoader(handlerClass.getClassLoader()) {
public final Class<?> loadClass(String name, byte[] b) {
return defineClass(name, b, 0, b.length);
}
}.loadClass(newDynName.replace('/', '.'), bytes);
return (Creator<H>) Creator.create(newHandlerClazz);
}
}

View File

@@ -21,11 +21,11 @@ import static java.lang.annotation.RetentionPolicy.*;
@Documented @Documented
@Target({METHOD}) @Target({METHOD})
@Retention(RUNTIME) @Retention(RUNTIME)
@Repeatable(RestMappings.class) @Repeatable(RestMapping.RestMappings.class)
public @interface RestMapping { public @interface RestMapping {
/** /**
* 是否屏蔽该方法转换 * 是否屏蔽该方法进行HttpMapping转换
* *
* @return boolean * @return boolean
*/ */
@@ -40,38 +40,46 @@ public @interface RestMapping {
String name() default ""; String name() default "";
/** /**
* 备注描述, 对应&#64;WebMapping.comment * 备注描述, 对应&#64;HttpMapping.comment
* *
* @return String * @return String
*/ */
String comment() default ""; String comment() default "";
/** /**
* 是否鉴权,默认鉴权, 对应&#64;AuthIgnore * 是否鉴权,默认需要鉴权, 对应&#64;HttpMapping.auth
* *
* @return boolean * @return boolean
*/ */
boolean auth() default false; boolean auth() default true;
/** /**
* 操作ID值鉴权时用到, 对应&#64;WebMapping.actionid * 操作ID值鉴权时用到, 对应&#64;HttpMapping.actionid
* *
* @return int * @return int
*/ */
int actionid() default 0; int actionid() default 0;
/** /**
* 结果缓存的秒数, 为0表示不缓存, 对应&#64;HttpCacheable.seconds * 结果缓存的秒数, 为0表示不缓存, 对应&#64;HttpMapping.cacheseconds
* *
* @return int * @return int
*/ */
int cacheseconds() default 0; int cacheseconds() default 0;
/** /**
* 允许方法(不区分大小写),如:GET/POST/PUT,为空表示允许所有方法, 对应&#64;WebMapping.methods * 允许方法(不区分大小写),如:GET/POST/PUT,为空表示允许所有方法, 对应&#64;HttpMapping.methods
* *
* @return String[] * @return String[]
*/ */
String[] methods() default {}; String[] methods() default {};
@Inherited
@Documented
@Target({METHOD})
@Retention(RUNTIME)
@interface RestMappings {
RestMapping[] value();
}
} }

View File

@@ -1,26 +0,0 @@
/*
* 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 java.lang.annotation.*;
import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.*;
/**
* RestMapping 的多用类
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*/
@Inherited
@Documented
@Target({METHOD})
@Retention(RUNTIME)
public @interface RestMappings {
RestMapping[] value();
}

View File

@@ -0,0 +1,39 @@
/*
* 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 java.lang.annotation.*;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
/**
* 标记在RestWebSocket的接收消息方法上
*
* <br><p>
* 详情见: https://redkale.org
*
* @author zhangjx
*/
@Inherited
@Documented
@Target({METHOD})
@Retention(RUNTIME)
public @interface RestOnMessage {
/**
* 请求的方法名, 不能含特殊字符,不能以数字开头(能作为变量名)
*
* @return String
*/
String name();
/**
* 备注描述
*
* @return String
*/
String comment() default "";
}

View File

@@ -29,6 +29,13 @@ public @interface RestService {
*/ */
String name() default ""; String name() default "";
/**
* 目录名, 不能含特殊字符, 只能小写字母+数字,且不能以数字开头
*
* @return 目录名
*/
String catalog() default "";
/** /**
* 模块ID值鉴权时用到, 对应&#64;WebServlet.moduleid * 模块ID值鉴权时用到, 对应&#64;WebServlet.moduleid
* *
@@ -36,6 +43,13 @@ public @interface RestService {
*/ */
int moduleid() default 0; int moduleid() default 0;
/**
* 没有标记&#64;RestMapping的方法是否转换 默认为false
*
* @return 默认false
*/
boolean automapping() default false;
/** /**
* 是否屏蔽该类的转换 * 是否屏蔽该类的转换
* *

View File

@@ -0,0 +1,33 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.net.http;
import java.lang.annotation.*;
import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
/**
* 只能注解于Service类的方法的String参数或参数内的String字段
* <p>
* 用于获取HTTP请求URL HttpRequest.getRequestURI
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*/
@Inherited
@Documented
@Target({PARAMETER, FIELD})
@Retention(RUNTIME)
public @interface RestURI {
/**
* 备注描述, 对应&#64;HttpParam.comment
*
* @return String
*/
String comment() default "";
}

View File

@@ -0,0 +1,55 @@
/*
* 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 java.lang.annotation.*;
import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
/**
*
* 依附在RestService类的方法的参数上, 用于接收上传文件 <br>
* 只能标记在byte[]/File/File[] 类型的参数上 <br>
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*/
@Inherited
@Documented
@Target({PARAMETER, FIELD})
@Retention(RUNTIME)
public @interface RestUploadFile {
/**
* 可接收的文件大小最大值, 小于1表示无大小限制
*
* @return int
*/
long maxLength() default 0;
/**
* 可接收的文件名正则表达式, 为空表示接收任何文件 <br>
*
* @return String
*/
String fileNameReg() default "";
/**
* 可接收的ContentType正则表达式, 为空表示接收任何文件类型 <br>
*
* @return String
*/
String contentTypeReg() default "";
/**
* 备注描述, 对应&#64;HttpParam.comment
*
* @return String
*/
String comment() default "";
}

View File

@@ -0,0 +1,81 @@
/*
* 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 java.lang.annotation.*;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
/**
* 只能依附在WebSocket类上name默认为Service的类名小写并去掉Service字样及后面的字符串 (如HelloWebSocket/HelloWebSocketImpl的默认路径为 hello)。 <br>
* <b>注意: </b> 被标记&#64;RestWebSocket的WebSocket不能被修饰为abstract或final且其内部标记为&#64;Resource的字段只能是protected或public且必须要有一个protected或public的空参数构造函数。 <br>
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*/
@Inherited
@Documented
@Target({TYPE})
@Retention(RUNTIME)
public @interface RestWebSocket {
/**
* 模块名, 只能是模块名,不能含特殊字符, 只能小写字母+数字,且不能以数字开头
*
* @return 模块名
*/
String name() default "";
/**
* 目录名, 不能含特殊字符, 只能小写字母+数字,且不能以数字开头
*
* @return 目录名
*/
String catalog() default "";
/**
* 是否为二进制消息, 默认为文本消息
*
* @return boolean
*/
boolean binary() default false;
/**
* 是否单用户单连接, 默认单用户单连接
*
* @return 是否单用户单连接
*/
boolean single() default true;
/**
* WebScoket服务器给客户端进行ping操作的间隔时间, 单位: 秒, 默认值15秒
*
* @return int
*/
int liveinterval() default WebSocketServlet.DEFAILT_LIVEINTERVAL;
/**
* 是否屏蔽该类的转换
*
* @return 默认false
*/
boolean ignore() default false;
/**
* 同&#64;WebServlet的repair属性
*
* @return 默认true
*/
boolean repair() default true;
/**
* 备注描述
*
* @return 备注描述
*/
String comment() default "";
}

View File

@@ -8,36 +8,34 @@ package org.redkale.net.http;
import org.redkale.net.http.WebSocketPacket.FrameType; import org.redkale.net.http.WebSocketPacket.FrameType;
import java.io.*; import java.io.*;
import java.net.*; import java.net.*;
import java.nio.ByteBuffer;
import java.util.*; import java.util.*;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.*;
import org.redkale.convert.json.JsonConvert; import java.util.function.Supplier;
import org.redkale.net.*; import java.util.logging.*;
import java.util.stream.Stream;
import org.redkale.convert.Convert;
import org.redkale.util.Comment; import org.redkale.util.Comment;
/** /**
* <blockquote><pre> * <blockquote><pre>
* 一个WebSocket连接对应一个WebSocket实体即一个WebSocket会绑定一个TCP连接。 * 一个WebSocket连接对应一个WebSocket实体即一个WebSocket会绑定一个TCP连接。
* WebSocket 有两种模式: * 协议上符合HTML5规范, 其流程顺序如下:
* 1) 普通模式: 协议上符合HTML5规范, 其流程顺序如下:
* 1.1 onOpen 若返回null视为WebSocket的连接不合法强制关闭WebSocket连接通常用于判断登录态。 * 1.1 onOpen 若返回null视为WebSocket的连接不合法强制关闭WebSocket连接通常用于判断登录态。
* 1.2 createGroupid 若返回null视为WebSocket的连接不合法强制关闭WebSocket连接通常用于判断用户权限是否符合。 * 1.2 createUserid 若返回null视为WebSocket的连接不合法强制关闭WebSocket连接通常用于判断用户权限是否符合。
* 1.3 onConnected WebSocket成功连接后在准备接收数据前回调此方法。 * 1.3 onConnected WebSocket成功连接后在准备接收数据前回调此方法。
* 1.4 onMessage/onFragment+ WebSocket接收到消息后回调此消息类方法。 * 1.4 onMessage/onFragment+ WebSocket接收到消息后回调此消息类方法。
* 1.5 onClose WebSocket被关闭后回调此方法。 * 1.5 onClose WebSocket被关闭后回调此方法。
* 普通模式下 以上方法都应该被重载。 * 普通模式下 以上方法都应该被重载。
*
* 2) 原始二进制模式: 此模式有别于HTML5规范可以视为原始的TCP连接。通常用于音频视频通讯场景。其流程顺序如下:
* 2.1 onOpen 若返回null视为WebSocket的连接不合法强制关闭WebSocket连接通常用于判断登录态。
* 2.2 createGroupid 若返回null视为WebSocket的连接不合法强制关闭WebSocket连接通常用于判断用户权限是否符合。
* 2.3 onRead WebSocket成功连接后回调此方法 由此方法处理原始的TCP连接 需要业务代码去控制WebSocket的关闭。
* 二进制模式下 以上方法都应该被重载。
* </pre></blockquote> * </pre></blockquote>
* <p> * <p>
* 详情见: https://redkale.org * 详情见: https://redkale.org
* *
* @author zhangjx * @author zhangjx
* @param <G> Groupid的泛型
* @param <T> Message的泛型
*/ */
public abstract class WebSocket { public abstract class WebSocket<G extends Serializable, T> {
@Comment("消息不合法") @Comment("消息不合法")
public static final int RETCODE_SEND_ILLPACKET = 1 << 1; //2 public static final int RETCODE_SEND_ILLPACKET = 1 << 1; //2
@@ -67,74 +65,45 @@ public abstract class WebSocket {
WebSocketEngine _engine; //不可能为空 WebSocketEngine _engine; //不可能为空
WebSocketGroup _group; //不可能为空 String _sessionid; //不可能为空
Serializable _sessionid; //不可能为空 G _userid; //不可能为空
Serializable _groupid; //不可能为空
SocketAddress _remoteAddress;//不可能为空 SocketAddress _remoteAddress;//不可能为空
String _remoteAddr;//不可能为空 String _remoteAddr;//不可能为空
JsonConvert _jsonConvert; //不可能为空 Convert _textConvert; //不可能为空
private final long createtime = System.currentTimeMillis(); Convert _binaryConvert; //可能为空
private final Map<String, Object> attributes = new ConcurrentHashMap<>(); Convert _sendConvert; //不可能为空
java.lang.reflect.Type _messageTextType; //不可能为空
private long createtime = System.currentTimeMillis();
private long pingtime;
private Map<String, Object> attributes = new HashMap<>(); //非线程安全
protected WebSocket() { protected WebSocket() {
} }
//---------------------------------------------------------------- //----------------------------------------------------------------
/** public final CompletableFuture<Integer> sendPing() {
* 给自身发送消息体, 包含二进制/文本 this.pingtime = System.currentTimeMillis();
*
* @param packet WebSocketPacket
*
* @return 0表示成功 非0表示错误码
*/
public final int send(WebSocketPacket packet) {
int rs = RETCODE_WSOCKET_CLOSED;
if (this._runner != null) rs = this._runner.sendMessage(packet);
if (_engine.finest) _engine.logger.finest("wsgroupid:" + getGroupid() + " send websocket result is " + rs + " on " + this + " by message(" + packet + ")");
return rs;
}
/**
* 给自身发送单一的文本消息
*
* @param text 不可为空
*
* @return 0表示成功 非0表示错误码
*/
public final int send(String text) {
return send(text, true);
}
/**
* 给自身发送文本消息
*
* @param text 不可为空
* @param last 是否最后一条
*
* @return 0表示成功 非0表示错误码
*/
public final int send(String text, boolean last) {
return send(new WebSocketPacket(text, last));
}
public final int sendPing() {
//if (_engine.finest) _engine.logger.finest(this + " on "+_engine.getEngineid()+" ping..."); //if (_engine.finest) _engine.logger.finest(this + " on "+_engine.getEngineid()+" ping...");
return send(WebSocketPacket.DEFAULT_PING_PACKET); return sendPacket(WebSocketPacket.DEFAULT_PING_PACKET);
} }
public final int sendPing(byte[] data) { public final CompletableFuture<Integer> sendPing(byte[] data) {
return send(new WebSocketPacket(FrameType.PING, data)); this.pingtime = System.currentTimeMillis();
return sendPacket(new WebSocketPacket(FrameType.PING, data));
} }
public final int sendPong(byte[] data) { public final CompletableFuture<Integer> sendPong(byte[] data) {
return send(new WebSocketPacket(FrameType.PONG, data)); return sendPacket(new WebSocketPacket(FrameType.PONG, data));
} }
public final long getCreatetime() { public final long getCreatetime() {
@@ -142,251 +111,176 @@ public abstract class WebSocket {
} }
/** /**
* 给自身发送单一的二进制消息 * 给自身发送消息, 消息类型是String或byte[]或可JavaBean对象
* *
* @param data byte[] * @param message 不可为空, 只能是String或byte[]或可JavaBean对象
* *
* @return 0表示成功 非0表示错误码 * @return 0表示成功 非0表示错误码
*/ */
public final int send(byte[] data) { public final CompletableFuture<Integer> send(Object message) {
return send(data, true);
}
/**
* 给自身发送二进制消息
*
* @param data 不可为空
* @param last 是否最后一条
*
* @return 0表示成功 非0表示错误码
*/
public final int send(byte[] data, boolean last) {
return send(new WebSocketPacket(data, last));
}
/**
* 给自身发送消息, 消息类型是String或byte[]或可JSON化对象
*
* @param message 不可为空, 只能是String或byte[]或可JSON化对象
*
* @return 0表示成功 非0表示错误码
*/
public final int send(Object message) {
return send(message, true); return send(message, true);
} }
/** /**
* 给自身发送消息, 消息类型是String或byte[]或可JSON化对象 * 给自身发送消息, 消息类型是String或byte[]或可JavaBean对象
* *
* @param message 不可为空, 只能是String或byte[]或可JSON化对象 * @param message 不可为空, 只能是String或byte[]或可JavaBean对象
* @param last 是否最后一条 * @param last 是否最后一条
* *
* @return 0表示成功 非0表示错误码 * @return 0表示成功 非0表示错误码
*/ */
public final int send(Object message, boolean last) { public final CompletableFuture<Integer> send(Object message, boolean last) {
if (message == null || message instanceof CharSequence || message instanceof byte[]) { if (message instanceof CompletableFuture) {
return send(new WebSocketPacket((Serializable) message, last)); return ((CompletableFuture) message).thenCompose((json) -> {
} else { if (json == null || json instanceof CharSequence || json instanceof byte[]) {
return send(new WebSocketPacket(_jsonConvert.convertTo(message), last)); return sendPacket(new WebSocketPacket((Serializable) json, last));
} else if (message instanceof WebSocketPacket) {
return sendPacket((WebSocketPacket) message);
} else {
return sendPacket(new WebSocketPacket(getSendConvert(), json, last));
}
});
} }
if (message == null || message instanceof CharSequence || message instanceof byte[]) {
return sendPacket(new WebSocketPacket((Serializable) message, last));
} else if (message instanceof WebSocketPacket) {
return sendPacket((WebSocketPacket) message);
} else {
return sendPacket(new WebSocketPacket(getSendConvert(), message, last));
}
}
/**
* 给自身发送消息, 消息类型是JavaBean对象
*
* @param convert Convert
* @param message 不可为空, 只能是JSON对象
*
* @return 0表示成功 非0表示错误码
*/
public final CompletableFuture<Integer> send(Convert convert, Object message) {
return send(convert, message, true);
}
/**
* 给自身发送消息, 消息类型是JavaBean对象
*
* @param convert Convert
* @param message 不可为空, 只能是JavaBean对象
* @param last 是否最后一条
*
* @return 0表示成功 非0表示错误码
*/
public final CompletableFuture<Integer> send(Convert convert, Object message, boolean last) {
if (message instanceof CompletableFuture) {
return ((CompletableFuture) message).thenCompose((json) -> sendPacket(new WebSocketPacket(convert == null ? getSendConvert() : convert, json, last)));
}
return sendPacket(new WebSocketPacket(convert == null ? getSendConvert() : convert, message, last));
}
/**
* 给自身发送消息体, 包含二进制/文本
*
* @param packet WebSocketPacket
*
* @return 0表示成功 非0表示错误码
*/
CompletableFuture<Integer> sendPacket(WebSocketPacket packet) {
CompletableFuture<Integer> rs = this._runner.sendMessage(packet);
if (_engine.finest) _engine.logger.finest("userid:" + getUserid() + " send websocket message(" + packet + ")" + " on " + this);
return rs == null ? CompletableFuture.completedFuture(RETCODE_WSOCKET_CLOSED) : rs;
} }
//---------------------------------------------------------------- //----------------------------------------------------------------
/** /**
* 给指定groupid的WebSocketGroup下所有WebSocket节点发送文本消息 * 给指定userid的WebSocket节点发送 二进制消息/文本消息/JavaBean对象消息
* *
* @param groupid groupid
* @param text 不可为空
*
* @return 为0表示成功 其他值表示异常
*/
public final int sendEachMessage(Serializable groupid, String text) {
return sendEachMessage(groupid, text, true);
}
/**
* 给指定groupid的WebSocketGroup下所有WebSocket节点发送二进制消息
*
* @param groupid groupid
* @param data 不可为空
*
* @return 为0表示成功 其他值表示异常
*/
public final int sendEachMessage(Serializable groupid, byte[] data) {
return sendEachMessage(groupid, data, true);
}
/**
* 给指定groupid的WebSocketGroup下所有WebSocket节点发送可JSON化对象消息
*
* @param groupid groupid
* @param message 不可为空 * @param message 不可为空
* @param userids Serializable[]
* *
* @return 为0表示成功 其他值表示异常 * @return 为0表示成功 其他值表示异常
*/ */
public final int sendEachMessage(Serializable groupid, Object message) { public final CompletableFuture<Integer> sendMessage(Object message, G... userids) {
return sendEachMessage(groupid, message, true); return sendMessage(message, true, userids);
} }
/** /**
* 给指定groupid的WebSocketGroup下所有WebSocket节点发送文本消息 * 给指定userid的WebSocket节点发送 二进制消息/文本消息/JavaBean对象消息
* *
* @param groupid groupid
* @param text 不可为空
* @param last 是否最后一条
*
* @return 为0表示成功 其他值表示异常
*/
public final int sendEachMessage(Serializable groupid, String text, boolean last) {
return sendMessage(groupid, false, text, last);
}
/**
* 给指定groupid的WebSocketGroup下所有WebSocket节点发送二进制消息
*
* @param groupid groupid
* @param data 不可为空
* @param last 是否最后一条
*
* @return 为0表示成功 其他值表示异常
*/
public final int sendEachMessage(Serializable groupid, byte[] data, boolean last) {
return sendMessage(groupid, false, data, last);
}
/**
* 给指定groupid的WebSocketGroup下所有WebSocket节点发送可JSON化对象消息
*
* @param groupid groupid
* @param message 不可为空 * @param message 不可为空
* @param last 是否最后一条 * @param last 是否最后一条
* @param userids Serializable[]
* *
* @return 为0表示成功 其他值表示异常 * @return 为0表示成功 其他值表示异常
*/ */
public final int sendEachMessage(Serializable groupid, Object message, boolean last) { public final CompletableFuture<Integer> sendMessage(Object message, boolean last, G... userids) {
return sendMessage(groupid, false, message, last); if (_engine.node == null) return CompletableFuture.completedFuture(RETCODE_NODESERVICE_NULL);
} if (message instanceof CompletableFuture) {
return ((CompletableFuture) message).thenCompose((json) -> _engine.node.sendMessage(json, last, userids));
/** }
* 给指定groupid的WebSocketGroup下最近接入的WebSocket节点发送文本消息 CompletableFuture<Integer> rs = _engine.node.sendMessage(message, last, userids);
* if (_engine.finest) _engine.logger.finest("userids:" + Arrays.toString(userids) + " send websocket message(" + message + ")");
* @param groupid groupid
* @param text 不可为空
*
* @return 为0表示成功 其他值表示异常
*/
public final int sendRecentMessage(Serializable groupid, String text) {
return sendRecentMessage(groupid, text, true);
}
/**
* 给指定groupid的WebSocketGroup下最近接入的WebSocket节点发送二进制消息
*
* @param groupid groupid
* @param data 不可为空
*
* @return 为0表示成功 其他值表示异常
*/
public final int sendRecentMessage(Serializable groupid, byte[] data) {
return sendRecentMessage(groupid, data, true);
}
/**
* 给指定groupid的WebSocketGroup下最近接入的WebSocket节点发送可JSON化对象消息
*
* @param groupid groupid
* @param message 不可为空
*
* @return 为0表示成功 其他值表示异常
*/
public final int sendRecentMessage(Serializable groupid, Object message) {
return sendMessage(groupid, true, message, true);
}
/**
* 给指定groupid的WebSocketGroup下最近接入的WebSocket节点发送文本消息
*
* @param groupid groupid
* @param text 不可为空
* @param last 是否最后一条
*
* @return 为0表示成功 其他值表示异常
*/
public final int sendRecentMessage(Serializable groupid, String text, boolean last) {
return sendMessage(groupid, true, text, last);
}
/**
* 给指定groupid的WebSocketGroup下最近接入的WebSocket节点发送二进制消息
*
* @param groupid groupid
* @param data 不可为空
* @param last 是否最后一条
*
* @return 为0表示成功 其他值表示异常
*/
public final int sendRecentMessage(Serializable groupid, byte[] data, boolean last) {
return sendMessage(groupid, true, data, last);
}
/**
* 给指定groupid的WebSocketGroup下最近接入的WebSocket节点发送可JSON化对象消息
*
* @param groupid groupid
* @param message 不可为空
* @param last 是否最后一条
*
* @return 为0表示成功 其他值表示异常
*/
public final int sendRecentMessage(Serializable groupid, Object message, boolean last) {
return sendMessage(groupid, true, message, last);
}
private int sendMessage(Serializable groupid, boolean recent, String text, boolean last) {
if (_engine.node == null) return RETCODE_NODESERVICE_NULL;
int rs = _engine.node.sendMessage(groupid, recent, text, last);
if (_engine.finest) _engine.logger.finest("wsgroupid:" + groupid + " " + (recent ? "recent " : "") + "send websocket result is " + rs + " on " + this + " by message(" + text + ")");
return rs;
}
private int sendMessage(Serializable groupid, boolean recent, byte[] data, boolean last) {
if (_engine.node == null) return RETCODE_NODESERVICE_NULL;
int rs = _engine.node.sendMessage(groupid, recent, data, last);
if (_engine.finest) _engine.logger.finest("wsgroupid:" + groupid + " " + (recent ? "recent " : "") + "send websocket result is " + rs + " on " + this + " by message(byte[" + data.length + "])");
return rs;
}
private int sendMessage(Serializable groupid, boolean recent, Object message, boolean last) {
if (_engine.node == null) return RETCODE_NODESERVICE_NULL;
int rs = _engine.node.sendMessage(groupid, recent, message, last);
if (_engine.finest) _engine.logger.finest("wsgroupid:" + groupid + " " + (recent ? "recent " : "") + "send websocket result is " + rs + " on " + this + " by message(" + _jsonConvert.convertTo(message) + ")");
return rs; return rs;
} }
/** /**
* 获取指定groupid在线用户的节点地址列表 * 广播消息, 给所有人发消息
* *
* @param groupid groupid * @param message 消息内容
*
* @return 为0表示成功 其他值表示部分发送异常
*/
public final CompletableFuture<Integer> broadcastMessage(final Object message) {
return broadcastMessage(message, true);
}
/**
* 广播消息, 给所有人发消息
*
* @param message 消息内容
* @param last 是否最后一条
*
* @return 为0表示成功 其他值表示部分发送异常
*/
public final CompletableFuture<Integer> broadcastMessage(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(json, last));
}
CompletableFuture<Integer> rs = _engine.node.broadcastMessage(message, last);
if (_engine.finest) _engine.logger.finest("broadcast send websocket message(" + message + ")");
return rs;
}
/**
* 获取用户在线的SNCP节点地址列表不是分布式则返回元素数量为1且元素值为null的列表<br>
* InetSocketAddress 为 SNCP节点地址
*
* @param userid Serializable
* *
* @return 地址列表 * @return 地址列表
*/ */
protected final Collection<InetSocketAddress> getOnlineNodes(Serializable groupid) { public CompletableFuture<Collection<InetSocketAddress>> getRpcNodeAddresses(final Serializable userid) {
return _engine.node.getOnlineNodes(groupid); if (_engine.node == null) return CompletableFuture.completedFuture(null);
return _engine.node.getRpcNodeAddresses(userid);
} }
/** /**
* 获取指定groupid在线用户的详细连接信息 * 获取在线用户的详细连接信息 <br>
* Map.key 为 SNCP节点地址, 含值为null的key表示没有分布式
* Map.value 为 用户客户端的IP
* *
* @param groupid groupid * @param userid Serializable
* *
* @return 地址集合 * @return 地址集合
*/ */
protected final Map<InetSocketAddress, List<String>> getOnlineRemoteAddress(Serializable groupid) { public CompletableFuture<Map<InetSocketAddress, List<String>>> getRpcNodeWebSocketAddresses(final Serializable userid) {
return _engine.node.getOnlineRemoteAddress(groupid); if (_engine.node == null) return CompletableFuture.completedFuture(null);
return _engine.node.getRpcNodeWebSocketAddresses(userid);
} }
/** /**
* 获取当前WebSocket下的属性 * 获取当前WebSocket下的属性,非线程安全
* *
* @param <T> 属性值的类型 * @param <T> 属性值的类型
* @param name 属性名 * @param name 属性名
@@ -395,11 +289,11 @@ public abstract class WebSocket {
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public final <T> T getAttribute(String name) { public final <T> T getAttribute(String name) {
return (T) attributes.get(name); return attributes == null ? null : (T) attributes.get(name);
} }
/** /**
* 移出当前WebSocket下的属性 * 移出当前WebSocket下的属性,非线程安全
* *
* @param <T> 属性值的类型 * @param <T> 属性值的类型
* @param name 属性名 * @param name 属性名
@@ -407,26 +301,27 @@ public abstract class WebSocket {
* @return 属性值 * @return 属性值
*/ */
public final <T> T removeAttribute(String name) { public final <T> T removeAttribute(String name) {
return (T) attributes.remove(name); return attributes == null ? null : (T) attributes.remove(name);
} }
/** /**
* 给当前WebSocket下的增加属性 * 给当前WebSocket下的增加属性,非线程安全
* *
* @param name 属性值 * @param name 属性值
* @param value 属性值 * @param value 属性值
*/ */
public final void setAttribute(String name, Object value) { public final void setAttribute(String name, Object value) {
if (attributes == null) attributes = new HashMap<>();
attributes.put(name, value); attributes.put(name, value);
} }
/** /**
* 获取当前WebSocket所属的groupid * 获取当前WebSocket所属的userid
* *
* @return groupid * @return userid
*/ */
public final Serializable getGroupid() { public final G getUserid() {
return _groupid; return _userid;
} }
/** /**
@@ -434,7 +329,7 @@ public abstract class WebSocket {
* *
* @return sessionid * @return sessionid
*/ */
public final Serializable getSessionid() { public final String getSessionid() {
return _sessionid; return _sessionid;
} }
@@ -456,87 +351,175 @@ public abstract class WebSocket {
return _remoteAddr; return _remoteAddr;
} }
protected Convert getTextConvert() {
return _textConvert;
}
protected Convert getBinaryConvert() {
return _binaryConvert;
}
protected Convert getSendConvert() {
return _sendConvert;
}
//------------------------------------------------------------------- //-------------------------------------------------------------------
/** /**
* 获取当前WebSocket所属的WebSocketGroup 不会为null * 获取指定userid的WebSocket数组, 没有返回null <br>
* 此方法用于单用户多连接模式
* *
* @return WebSocketGroup * @param userid Serializable
*
* @return WebSocket集合
*/ */
protected final WebSocketGroup getWebSocketGroup() { protected final Stream<WebSocket> getLocalWebSockets(G userid) {
return _group; return _engine.getLocalWebSockets(userid);
} }
/** /**
* 获取指定groupid的WebSocketGroup, 没有返回null * 获取指定userid的WebSocket数组, 没有返回null<br>
* 此方法用于单用户单连接模式
* *
* @param groupid groupid * @param userid Serializable
* *
* @return WebSocketGroup * @return WebSocket
*/ */
protected final WebSocketGroup getWebSocketGroup(Serializable groupid) { protected final WebSocket findLocalWebSocket(G userid) {
return _engine.getWebSocketGroup(groupid); return _engine.findLocalWebSocket(userid);
} }
/** /**
* 获取当前进程节点所有在线的WebSocketGroup * 获取当前进程节点所有在线的WebSocket
* *
* @return WebSocketGroup列表 * @return WebSocketGroup列表
*/ */
protected final Collection<WebSocketGroup> getWebSocketGroups() { protected final Collection<WebSocket> getLocalWebSockets() {
return _engine.getWebSocketGroups(); return _engine.getLocalWebSockets();
}
/**
* 获取ByteBuffer资源池
*
* @return Supplier
*/
protected Supplier<ByteBuffer> getByteBufferSupplier() {
return this._runner.context.getBufferSupplier();
} }
//------------------------------------------------------------------- //-------------------------------------------------------------------
/** /**
* 返回sessionid, null表示连接不合法或异常,默认实现是request.getSessionid(false),通常需要重写该方法 * 返回sessionid, null表示连接不合法或异常,默认实现是request.sessionid(true),通常需要重写该方法
* *
* @param request HttpRequest * @param request HttpRequest
* *
* @return sessionid * @return sessionid
*/ */
public Serializable onOpen(final HttpRequest request) { protected CompletableFuture<String> onOpen(final HttpRequest request) {
return request.getSessionid(false); return CompletableFuture.completedFuture(request.getSessionid(true));
} }
/** /**
* 创建groupid null表示异常 必须实现该方法 通常为用户ID为groupid * 创建userid null表示异常 必须实现该方法
* *
* @return groupid * @return userid
*/ */
protected abstract Serializable createGroupid(); protected abstract CompletableFuture<G> createUserid();
/** /**
* 标记为WebSocketBinary才需要重写此方法 * WebSokcet连接成功后的回调方法
*
* @param channel 请求连接
*/ */
public void onRead(AsyncConnection channel) {
}
public void onConnected() { public void onConnected() {
} }
public void onMessage(String text) { /**
} * ping后的回调方法
*
* @param bytes 数据
*/
public void onPing(byte[] bytes) { public void onPing(byte[] bytes) {
} }
/**
* pong后的回调方法
*
* @param bytes 数据
*/
public void onPong(byte[] bytes) { public void onPong(byte[] bytes) {
} }
public void onMessage(byte[] bytes) { /**
* 接收到消息的回调方法
*
* @param message 消息
* @param last 是否最后一条
*/
public void onMessage(T message, boolean last) {
} }
public void onFragment(String text, boolean last) { /**
* 接收到文本消息的回调方法
*
* @param text 消息
* @param last 是否最后一条
*/
public void onMessage(String text, boolean last) {
} }
public void onFragment(byte[] bytes, boolean last) { /**
* 接收到二进制消息的回调方法
*
* @param bytes 消息
* @param last 是否最后一条
*/
public void onMessage(byte[] bytes, boolean last) {
} }
/**
* 关闭的回调方法调用此方法时WebSocket已经被关闭
*
* @param code 结果码非0表示非正常关闭
* @param reason 关闭原因
*/
public void onClose(int code, String reason) { public void onClose(int code, String reason) {
} }
/**
* 发生异常时调用
*
* @param t 异常
* @param buffers ByteBuffer[]
*/
public void onOccurException(Throwable t, ByteBuffer[] buffers) {
this.getLogger().log(Level.SEVERE, "WebSocket receive or send Message error", t);
}
/**
* 获取Logger
*
* @return Logger Logger
*/
public Logger getLogger() {
return this._engine.logger;
}
/**
* 获取最后一次发送消息的时间
*
* @return long
*/
public long getLastSendTime() {
return this._runner == null ? 0 : this._runner.lastSendTime;
}
/**
* 获取最后一次发送PING消息的时间
*
* @return long
*/
public long getLastPingTime() {
return this.pingtime;
}
/** /**
* 显式地关闭WebSocket * 显式地关闭WebSocket
*/ */
@@ -546,6 +529,6 @@ public abstract class WebSocket {
@Override @Override
public String toString() { public String toString() {
return "ws" + Objects.hashCode(this) + "@" + _remoteAddr; return this.getUserid() + "@" + _remoteAddr;
} }
} }

View File

@@ -10,7 +10,11 @@ import java.io.*;
import java.util.*; import java.util.*;
import java.util.concurrent.*; import java.util.concurrent.*;
import java.util.concurrent.atomic.*; import java.util.concurrent.atomic.*;
import java.util.function.Predicate;
import java.util.logging.*; import java.util.logging.*;
import java.util.stream.*;
import org.redkale.convert.Convert;
import static org.redkale.net.http.WebSocket.RETCODE_GROUP_EMPTY;
import org.redkale.util.*; import org.redkale.util.*;
/** /**
@@ -20,83 +24,256 @@ import org.redkale.util.*;
* *
* @author zhangjx * @author zhangjx
*/ */
public final class WebSocketEngine { public class WebSocketEngine {
@Comment("全局自增长ID, 为了确保在一个进程里多个WebSocketEngine定时发送ping时不会同时进行")
private static final AtomicInteger sequence = new AtomicInteger(); private static final AtomicInteger sequence = new AtomicInteger();
@Comment("Engine自增长序号ID")
private final int index; private final int index;
@Comment("当前WebSocket对应的Engine")
private final String engineid; private final String engineid;
@Comment("当前WebSocket对应的Node")
protected final WebSocketNode node; protected final WebSocketNode node;
private final Map<Serializable, WebSocketGroup> containers = new ConcurrentHashMap<>(); //HttpContext
protected final HttpContext context;
//Convert
protected final Convert sendConvert;
@Comment("是否单用户单连接")
protected final boolean single;
@Comment("在线用户ID对应的WebSocket组用于单用户单连接模式")
private final Map<Serializable, WebSocket> websockets = new ConcurrentHashMap<>();
@Comment("在线用户ID对应的WebSocket组用于单用户多连接模式")
private final Map<Serializable, List<WebSocket>> websockets2 = new ConcurrentHashMap<>();
@Comment("用于PING的定时器")
private ScheduledThreadPoolExecutor scheduler; private ScheduledThreadPoolExecutor scheduler;
@Comment("日志")
protected final Logger logger; protected final Logger logger;
@Comment("日志级别")
protected final boolean finest; protected final boolean finest;
protected WebSocketEngine(String engineid, WebSocketNode node, Logger logger) { @Comment("PING的间隔秒数")
private int liveinterval;
protected WebSocketEngine(String engineid, boolean single, HttpContext context, int liveinterval, WebSocketNode node, Convert sendConvert, Logger logger) {
this.engineid = engineid; this.engineid = engineid;
this.single = single;
this.context = context;
this.sendConvert = sendConvert;
this.node = node; this.node = node;
this.liveinterval = liveinterval;
this.logger = logger; this.logger = logger;
this.index = sequence.getAndIncrement();
this.finest = logger.isLoggable(Level.FINEST); this.finest = logger.isLoggable(Level.FINEST);
this.index = sequence.getAndIncrement();
} }
void init(AnyValue conf) { void init(AnyValue conf) {
final int liveinterval = conf == null ? DEFAILT_LIVEINTERVAL : conf.getIntValue("liveinterval", DEFAILT_LIVEINTERVAL); final int interval = conf == null ? (liveinterval < 0 ? DEFAILT_LIVEINTERVAL : liveinterval) : conf.getIntValue("liveinterval", (liveinterval < 0 ? DEFAILT_LIVEINTERVAL : liveinterval));
if (liveinterval <= 0) return; if (interval <= 0) return;
if (scheduler != null) return; if (scheduler != null) return;
this.scheduler = new ScheduledThreadPoolExecutor(1, (Runnable r) -> { this.scheduler = new ScheduledThreadPoolExecutor(1, (Runnable r) -> {
final Thread t = new Thread(r, engineid + "-WebSocket-LiveInterval-Thread"); final Thread t = new Thread(r, engineid + "-WebSocket-LiveInterval-Thread");
t.setDaemon(true); t.setDaemon(true);
return t; return t;
}); });
long delay = (liveinterval - System.currentTimeMillis() / 1000 % liveinterval) + index * 5; long delay = (interval - System.currentTimeMillis() / 1000 % interval) + index * 5;
final int intervalms = interval * 1000;
scheduler.scheduleWithFixedDelay(() -> { scheduler.scheduleWithFixedDelay(() -> {
getWebSocketGroups().stream().forEach(x -> x.sendEachPing()); long now = System.currentTimeMillis();
}, delay, liveinterval, TimeUnit.SECONDS); getLocalWebSockets().stream().filter(x -> (now - x.getLastSendTime()) > intervalms).forEach(x -> x.sendPing());
if (finest) logger.finest(this.getClass().getSimpleName() + "(" + engineid + ")" + " start keeplive(delay:" + delay + ", interval:" + liveinterval + "s) scheduler executor"); }, delay, interval, TimeUnit.SECONDS);
if (finest) logger.finest(this.getClass().getSimpleName() + "(" + engineid + ")" + " start keeplive(delay:" + delay + ", interval:" + interval + "s) scheduler executor");
} }
void add(WebSocket socket) { void destroy(AnyValue conf) {
WebSocketGroup group = containers.get(socket._groupid);
if (group == null) {
group = new WebSocketGroup(socket._groupid);
containers.putIfAbsent(socket._groupid, group);
}
group.add(socket);
if (node != null) node.connect(socket._groupid, engineid, socket.toString());
}
void remove(WebSocket socket) {
final WebSocketGroup group = containers.get(socket._groupid);
if (group == null) {
if (node != null) node.disconnect(socket._groupid, engineid);
return;
}
group.remove(socket);
if (group.isEmpty()) {
containers.remove(socket._groupid);
if (node != null) node.disconnect(socket._groupid, engineid);
}
}
Collection<WebSocketGroup> getWebSocketGroups() {
return containers.values();
}
public WebSocketGroup getWebSocketGroup(Serializable groupid) {
return containers.get(groupid);
}
void close() {
if (scheduler != null) scheduler.shutdownNow(); if (scheduler != null) scheduler.shutdownNow();
} }
@Comment("添加WebSocket")
void add(WebSocket socket) {
if (single) {
websockets.put(socket._userid, socket);
} else { //非线程安全, 在常规场景中无需锁
List<WebSocket> list = websockets2.get(socket._userid);
if (list == null) {
list = new CopyOnWriteArrayList<>();
websockets2.put(socket._userid, list);
}
list.add(socket);
}
if (node != null) node.connect(socket._userid);
}
@Comment("从WebSocketEngine删除指定WebSocket")
void remove(WebSocket socket) {
Serializable userid = socket._userid;
if (single) {
websockets.remove(userid);
if (node != null) node.disconnect(userid);
} else { //非线程安全, 在常规场景中无需锁
List<WebSocket> list = websockets2.get(userid);
if (list != null) {
list.remove(socket);
if (list.isEmpty()) {
websockets2.remove(userid);
if (node != null) node.disconnect(userid);
}
}
}
}
@Comment("给所有连接用户发送消息")
public CompletableFuture<Integer> broadcastMessage(final Object message, final boolean last) {
return broadcastMessage(null, message, last);
}
@Comment("给指定WebSocket连接用户发送消息")
public CompletableFuture<Integer> broadcastMessage(final Predicate<WebSocket> predicate, final Object message, final boolean last) {
if (message instanceof CompletableFuture) {
return ((CompletableFuture) message).thenCompose((json) -> broadcastMessage(predicate, json, last));
}
final boolean more = (!(message instanceof WebSocketPacket) || ((WebSocketPacket) message).sendBuffers == null);
if (more) {
final WebSocketPacket packet = (message instanceof WebSocketPacket) ? (WebSocketPacket) message
: ((message == null || message instanceof CharSequence || message instanceof byte[])
? new WebSocketPacket((Serializable) message, last) : new WebSocketPacket(this.sendConvert, message, last));
packet.setSendBuffers(packet.encode(context.getBufferSupplier()));
CompletableFuture<Integer> future = null;
if (single) {
for (WebSocket websocket : websockets.values()) {
if (predicate != null && !predicate.test(websocket)) continue;
future = future == null ? websocket.sendPacket(packet) : future.thenCombine(websocket.sendPacket(packet), (a, b) -> a | (Integer) b);
}
} else {
for (List<WebSocket> list : websockets2.values()) {
for (WebSocket websocket : list) {
if (predicate != null && !predicate.test(websocket)) continue;
future = future == null ? websocket.sendPacket(packet) : future.thenCombine(websocket.sendPacket(packet), (a, b) -> a | (Integer) b);
}
}
}
if (future != null) future = future.whenComplete((rs, ex) -> context.offerBuffer(packet.sendBuffers));
return future == null ? CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY) : future;
} else {
CompletableFuture<Integer> future = null;
if (single) {
for (WebSocket websocket : websockets.values()) {
if (predicate != null && !predicate.test(websocket)) continue;
future = future == null ? websocket.send(message, last) : future.thenCombine(websocket.send(message, last), (a, b) -> a | (Integer) b);
}
} else {
for (List<WebSocket> list : websockets2.values()) {
for (WebSocket websocket : list) {
if (predicate != null && !predicate.test(websocket)) continue;
future = future == null ? websocket.send(message, last) : future.thenCombine(websocket.send(message, last), (a, b) -> a | (Integer) b);
}
}
}
return future == null ? CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY) : future;
}
}
@Comment("给指定用户组发送消息")
public CompletableFuture<Integer> sendMessage(final Object message, final boolean last, final Serializable... userids) {
if (message instanceof CompletableFuture) {
return ((CompletableFuture) message).thenCompose((json) -> sendMessage(json, last, userids));
}
final boolean more = (!(message instanceof WebSocketPacket) || ((WebSocketPacket) message).sendBuffers == null) && userids.length > 1;
if (more) {
final WebSocketPacket packet = (message instanceof WebSocketPacket) ? (WebSocketPacket) message
: ((message == null || message instanceof CharSequence || message instanceof byte[])
? new WebSocketPacket((Serializable) message, last) : new WebSocketPacket(this.sendConvert, message, last));
packet.setSendBuffers(packet.encode(context.getBufferSupplier()));
CompletableFuture<Integer> future = null;
if (single) {
for (Serializable userid : userids) {
WebSocket websocket = websockets.get(userid);
if (websocket == null) continue;
future = future == null ? websocket.sendPacket(packet) : future.thenCombine(websocket.sendPacket(packet), (a, b) -> a | (Integer) b);
}
} else {
for (Serializable userid : userids) {
List<WebSocket> list = websockets2.get(userid);
if (list == null) continue;
for (WebSocket websocket : list) {
future = future == null ? websocket.sendPacket(packet) : future.thenCombine(websocket.sendPacket(packet), (a, b) -> a | (Integer) b);
}
}
}
if (future != null) future = future.whenComplete((rs, ex) -> context.offerBuffer(packet.sendBuffers));
return future == null ? CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY) : future;
} else {
CompletableFuture<Integer> future = null;
if (single) {
for (Serializable userid : userids) {
WebSocket websocket = websockets.get(userid);
if (websocket == null) continue;
future = future == null ? websocket.send(message, last) : future.thenCombine(websocket.send(message, last), (a, b) -> a | (Integer) b);
}
} else {
for (Serializable userid : userids) {
List<WebSocket> list = websockets2.get(userid);
if (list == null) continue;
for (WebSocket websocket : list) {
future = future == null ? websocket.send(message, last) : future.thenCombine(websocket.send(message, last), (a, b) -> a | (Integer) b);
}
}
}
return future == null ? CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY) : future;
}
}
@Comment("获取所有连接")
public Collection<WebSocket> getLocalWebSockets() {
if (single) return websockets.values();
List<WebSocket> list = new ArrayList<>();
websockets2.values().forEach(x -> list.addAll(x));
return list;
}
@Comment("获取当前连接总数")
public int getLocalWebSocketSize() {
if (single) return websockets.size();
return (int) websockets2.values().stream().mapToInt(sublist -> sublist.size()).count();
}
@Comment("获取当前用户总数")
public int getLocalUserSize() {
return single ? websockets.size() : websockets2.size();
}
@Comment("适用于单用户单连接模式")
public WebSocket findLocalWebSocket(Serializable userid) {
if (single) return websockets.get(userid);
List<WebSocket> list = websockets2.get(userid);
return (list == null || list.isEmpty()) ? null : list.get(list.size() - 1);
}
@Comment("适用于单用户多连接模式")
public Stream<WebSocket> getLocalWebSockets(Serializable userid) {
if (single) {
WebSocket websocket = websockets.get(userid);
return websocket == null ? Stream.empty() : Stream.of(websocket);
} else {
List<WebSocket> list = websockets2.get(userid);
return list == null ? Stream.empty() : list.stream();
}
}
public boolean existsLocalWebSocket(Serializable userid) {
return single ? websockets.containsKey(userid) : websockets2.containsKey(userid);
}
public String getEngineid() { public String getEngineid() {
return engineid; return engineid;
} }

View File

@@ -1,142 +0,0 @@
/*
* 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 java.io.*;
import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.stream.Stream;
/**
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*/
public final class WebSocketGroup {
private final Serializable groupid;
private WebSocket recentWebSocket;
private final List<WebSocket> list = new CopyOnWriteArrayList<>();
private final Map<String, Object> attributes = new HashMap<>();
WebSocketGroup(Serializable groupid) {
this.groupid = groupid;
}
public Serializable getGroupid() {
return groupid;
}
public Stream<WebSocket> getWebSockets() {
return list.stream();
}
void remove(WebSocket socket) {
list.remove(socket);
}
void add(WebSocket socket) {
socket._group = this;
this.recentWebSocket = socket;
list.add(socket);
}
void setRecentWebSocket(WebSocket socket) {
this.recentWebSocket = socket;
}
public final boolean isEmpty() {
return list.isEmpty();
}
public final int size() {
return list.size();
}
/**
* 最近发送消息的WebSocket
*
* @return WebSocket
*/
public final WebSocket getRecentWebSocket() {
return recentWebSocket;
}
@SuppressWarnings("unchecked")
public final <T> T getAttribute(String name) {
return (T) attributes.get(name);
}
public final void removeAttribute(String name) {
attributes.remove(name);
}
public final void setAttribute(String name, Object value) {
attributes.put(name, value);
}
public final int send(boolean recent, Object message, boolean last) {
if (recent) {
return recentWebSocket.send(message, last);
} else {
return sendEach(message, last);
}
}
public final int sendEach(Object message) {
return sendEach(message, true);
}
public final int sendEach(WebSocketPacket packet) {
int rs = 0;
for (WebSocket s : list) {
rs |= s.send(packet);
}
return rs;
}
public final int sendEachPing() {
int rs = 0;
for (WebSocket s : list) {
rs |= s.sendPing();
}
return rs;
}
public final int sendRecent(Object message) {
return sendRecent(message, true);
}
public final int sendRecent(WebSocketPacket packet) {
return recentWebSocket.send(packet);
}
public final int sendEach(Object message, boolean last) {
if (message != null && !(message instanceof byte[]) && !(message instanceof CharSequence)) {
message = recentWebSocket._jsonConvert.convertTo(message);
}
int rs = 0;
for (WebSocket s : list) {
rs |= s.send(message, last);
}
return rs;
}
public final int sendRecent(Object message, boolean last) {
return recentWebSocket.send(message, last);
}
@Override
public String toString() {
return "{groupid: " + groupid + ", list.size: " + (list == null ? -1 : list.size()) + "}";
}
}

View File

@@ -26,234 +26,265 @@ import org.redkale.util.*;
*/ */
public abstract class WebSocketNode { public abstract class WebSocketNode {
@Comment("存储当前SNCP节点列表的key")
public static final String SOURCE_SNCP_NODES_KEY = "redkale_sncpnodes";
@Comment("存储当前用户数量的key")
public static final String SOURCE_USER_COUNT_KEY = "redkale_usercount";
protected final Logger logger = Logger.getLogger(this.getClass().getSimpleName()); protected final Logger logger = Logger.getLogger(this.getClass().getSimpleName());
protected final boolean finest = logger.isLoggable(Level.FINEST); protected final boolean finest = logger.isLoggable(Level.FINEST);
@Resource(name = Application.RESNAME_SERVER_ADDR) //"SNCP_ADDR" 如果不是分布式(没有SNCP) 值为null
@Resource(name = Application.RESNAME_SNCP_ADDR)
protected InetSocketAddress localSncpAddress; //为SncpServer的服务address protected InetSocketAddress localSncpAddress; //为SncpServer的服务address
//如果不是分布式(没有SNCP) 值为null
@RpcRemote @RpcRemote
protected WebSocketNode remoteNode; protected WebSocketNode remoteNode;
//存放所有用户分布在节点上的队列信息,Set<InetSocketAddress> 为 sncpnode 的集合 //存放所有用户分布在节点上的队列信息,Set<InetSocketAddress> 为 sncpnode 的集合 key: groupid
@Resource(name = "$") //集合包含 localSncpAddress
protected CacheSource<Serializable, InetSocketAddress> sncpNodes; //如果不是分布式(没有SNCP)sncpNodeAddresses 将不会被用到
@Resource(name = "$_nodes")
protected CacheSource<Serializable, InetSocketAddress> sncpNodeAddresses;
//存放本地节点上所有在线用户的队列信息,Set<String> 为 engineid 的集合 //当前节点的本地WebSocketEngine
protected final ConcurrentHashMap<Serializable, Set<String>> localNodes = new ConcurrentHashMap(); protected WebSocketEngine localEngine;
protected final ConcurrentHashMap<String, WebSocketEngine> engines = new ConcurrentHashMap();
public void init(AnyValue conf) { public void init(AnyValue conf) {
} }
public void destroy(AnyValue conf) { public void destroy(AnyValue conf) {
HashMap<Serializable, Set<String>> nodes = new HashMap<>(localNodes);
nodes.forEach((k, v) -> {
new HashSet<>(v).forEach(e -> {
if (engines.containsKey(e)) disconnect(k, e);
});
});
} }
protected abstract List<String> getOnlineRemoteAddresses(@RpcTargetAddress InetSocketAddress targetAddress, Serializable groupid); public final void postDestroy(AnyValue conf) {
if (this.localEngine == null) return;
//关掉所有本地本地WebSocket
this.localEngine.getLocalWebSockets().forEach(g -> disconnect(g.getUserid()));
if (sncpNodeAddresses != null && localSncpAddress != null) {
sncpNodeAddresses.removeSetItem(SOURCE_SNCP_NODES_KEY, localSncpAddress);
}
}
protected abstract int sendMessage(@RpcTargetAddress InetSocketAddress targetAddress, Serializable groupid, boolean recent, Object message, boolean last); protected abstract CompletableFuture<List<String>> getWebSocketAddresses(@RpcTargetAddress InetSocketAddress targetAddress, Serializable userid);
protected abstract void connect(Serializable groupid, InetSocketAddress addr); protected abstract CompletableFuture<Integer> sendMessage(@RpcTargetAddress InetSocketAddress targetAddress, Object message, boolean last, Serializable userid);
protected abstract void disconnect(Serializable groupid, InetSocketAddress addr); protected abstract CompletableFuture<Integer> broadcastMessage(@RpcTargetAddress InetSocketAddress targetAddress, Object message, boolean last);
protected abstract CompletableFuture<Void> connect(Serializable userid, InetSocketAddress addr);
protected abstract CompletableFuture<Void> disconnect(Serializable userid, InetSocketAddress addr);
//-------------------------------------------------------------------------------- //--------------------------------------------------------------------------------
protected List<String> remoteOnlineRemoteAddresses(@RpcTargetAddress InetSocketAddress targetAddress, Serializable groupid) { final CompletableFuture<Void> connect(final Serializable userid) {
if (remoteNode == null) return null; if (finest) logger.finest(localSncpAddress + " receive websocket connect event (" + userid + " on " + this.localEngine.getEngineid() + ").");
return connect(userid, localSncpAddress);
}
final CompletableFuture<Void> disconnect(final Serializable userid) {
if (finest) logger.finest(localSncpAddress + " receive websocket disconnect event (" + userid + " on " + this.localEngine.getEngineid() + ").");
return disconnect(userid, localSncpAddress);
}
//--------------------------------------------------------------------------------
/**
* 获取目标地址 <br>
* 该方法仅供内部调用
*
* @param targetAddress InetSocketAddress
* @param userid Serializable
*
* @return 客户端地址列表
*/
protected CompletableFuture<List<String>> remoteWebSocketAddresses(@RpcTargetAddress InetSocketAddress targetAddress, Serializable userid) {
if (remoteNode == null) return CompletableFuture.completedFuture(null);
try { try {
return remoteNode.getOnlineRemoteAddresses(targetAddress, groupid); return remoteNode.getWebSocketAddresses(targetAddress, userid);
} catch (Exception e) { } catch (Exception e) {
logger.log(Level.WARNING, "remote " + targetAddress + " websocket getOnlineRemoteAddresses error", e); logger.log(Level.WARNING, "remote " + targetAddress + " websocket getOnlineRemoteAddresses error", e);
return null; return CompletableFuture.completedFuture(null);
} }
} }
/** /**
* 获取在线用户的节点地址列表 * 获取用户在线的SNCP节点地址列表不是分布式则返回元素数量为1且元素值为null的列表<br>
* InetSocketAddress 为 SNCP节点地址
* *
* @param groupid groupid * @param userid Serializable
* *
* @return 地址列表 * @return 地址列表
*/ */
public Collection<InetSocketAddress> getOnlineNodes(final Serializable groupid) { public CompletableFuture<Collection<InetSocketAddress>> getRpcNodeAddresses(final Serializable userid) {
return sncpNodes == null ? null : sncpNodes.getCollection(groupid); if (this.sncpNodeAddresses != null) return this.sncpNodeAddresses.getCollectionAsync(userid);
List<InetSocketAddress> rs = new ArrayList<>();
rs.add(this.localSncpAddress);
return CompletableFuture.completedFuture(rs);
} }
/** /**
* 获取在线用户的详细连接信息 * 获取在线用户的详细连接信息 <br>
* Map.key 为 SNCP节点地址, 含值为null的key表示没有分布式
* Map.value 为 用户客户端的IP
* *
* @param groupid groupid * @param userid Serializable
* *
* @return 地址集合 * @return 地址集合
*/ */
public Map<InetSocketAddress, List<String>> getOnlineRemoteAddress(final Serializable groupid) { public CompletableFuture<Map<InetSocketAddress, List<String>>> getRpcNodeWebSocketAddresses(final Serializable userid) {
Collection<InetSocketAddress> nodes = getOnlineNodes(groupid); CompletableFuture<Collection<InetSocketAddress>> sncpFuture = getRpcNodeAddresses(userid);
if (nodes == null) return null; return sncpFuture.thenCompose((Collection<InetSocketAddress> addrs) -> {
final Map<InetSocketAddress, List<String>> map = new HashMap(); if (finest) logger.finest("websocket found userid:" + userid + " on " + addrs);
for (InetSocketAddress nodeAddress : nodes) { if (addrs == null || addrs.isEmpty()) return CompletableFuture.completedFuture(new HashMap<>());
List<String> list = getOnlineRemoteAddresses(nodeAddress, groupid); CompletableFuture<Map<InetSocketAddress, List<String>>> future = null;
if (list == null) list = new ArrayList(); for (final InetSocketAddress nodeAddress : addrs) {
map.put(nodeAddress, list); CompletableFuture<Map<InetSocketAddress, List<String>>> mapFuture = getWebSocketAddresses(nodeAddress, userid)
} .thenCompose((List<String> list) -> CompletableFuture.completedFuture(Utility.ofMap(nodeAddress, list)));
return map; future = future == null ? mapFuture : future.thenCombine(mapFuture, (a, b) -> Utility.merge(a, b));
}
public final void connect(Serializable groupid, String engineid, String wsinfo) {
if (finest) logger.finest(localSncpAddress + " receive websocket connect event (" + groupid + " on " + engineid + ").");
Set<String> engineids = localNodes.get(groupid);
if (engineids == null) {
engineids = new CopyOnWriteArraySet<>();
localNodes.putIfAbsent(groupid, engineids);
}
if (localSncpAddress != null && engineids.isEmpty()) connect(groupid, localSncpAddress);
engineids.add(engineid);
}
public final void disconnect(Serializable groupid, String engineid) {
if (finest) logger.finest(localSncpAddress + " receive websocket disconnect event (" + groupid + " on " + engineid + ").");
Set<String> engineids = localNodes.get(groupid);
if (engineids == null || engineids.isEmpty()) return;
engineids.remove(engineid);
if (engineids.isEmpty()) {
localNodes.remove(groupid);
if (localSncpAddress != null) disconnect(groupid, localSncpAddress);
}
}
final void putWebSocketEngine(WebSocketEngine engine) {
engines.put(engine.getEngineid(), engine);
}
public final int sendMessage(Serializable groupid, boolean recent, Object message, boolean last) {
final Set<String> engineids = localNodes.get(groupid);
if (finest) logger.finest("websocket want send message {groupid:" + groupid + ", content:'" + message + "'} from locale node to " + engineids);
int rscode = RETCODE_GROUP_EMPTY;
if (engineids != null && !engineids.isEmpty()) {
for (String engineid : engineids) {
final WebSocketEngine engine = engines.get(engineid);
if (engine != null) { //在本地
final WebSocketGroup group = engine.getWebSocketGroup(groupid);
if (group == null || group.isEmpty()) {
engineids.remove(engineid);
if (finest) logger.finest("websocket want send message {engineid:'" + engineid + "', groupid:" + groupid + ", content:'" + message + "'} but websocket group is empty ");
rscode = RETCODE_GROUP_EMPTY;
break;
}
rscode = group.send(recent, message, last);
}
} }
return future == null ? CompletableFuture.completedFuture(new HashMap<>()) : future;
});
}
/**
* 判断指定用户是否WebSocket在线
*
* @param userid Serializable
*
* @return boolean
*/
public CompletableFuture<Boolean> existsWebSocket(final Serializable userid) {
if (this.localEngine != null && this.sncpNodeAddresses == null) {
return CompletableFuture.completedFuture(this.localEngine.existsLocalWebSocket(userid));
} }
if ((recent && rscode == 0) || remoteNode == null || sncpNodes == null) { return this.sncpNodeAddresses.existsAsync(userid);
if (finest) { }
if ((recent && rscode == 0)) {
logger.finest("websocket want send recent message success"); /**
} else { * 获取在线用户总数
logger.finest("websocket remote node is null"); *
} *
} * @return boolean
return rscode; */
public CompletableFuture<Integer> getUserSize() {
if (this.localEngine != null && this.sncpNodeAddresses == null) {
return CompletableFuture.completedFuture(this.localEngine.getLocalUserSize());
} }
//-----------------------发送远程的----------------------------- return this.sncpNodeAddresses.getKeySizeAsync().thenCompose(count -> {
Collection<InetSocketAddress> addrs = sncpNodes.getCollection(groupid); return sncpNodeAddresses.existsAsync(SOURCE_SNCP_NODES_KEY).thenApply(exists -> exists ? (count - 1) : count);
if (finest) logger.finest("websocket found groupid:" + groupid + " on " + addrs); });
if (addrs != null && !addrs.isEmpty()) { //对方连接在远程节点(包含本地节点)所以正常情况下addrs不会为空。
if (recent) {
InetSocketAddress one = null;
for (InetSocketAddress addr : addrs) {
one = addr;
}
rscode = remoteNode.sendMessage(one, groupid, recent, message, last);
} else {
for (InetSocketAddress addr : addrs) {
if (!addr.equals(localSncpAddress)) {
rscode |= remoteNode.sendMessage(addr, groupid, recent, message, last);
}
}
}
} else {
rscode = RETCODE_GROUP_EMPTY;
}
return rscode;
} }
//-------------------------------------------------------------------------------- //--------------------------------------------------------------------------------
public final int sendEachMessage(Serializable groupid, String text) { /**
return sendMessage(groupid, false, (Object) text, true); * 获取本地的WebSocketEngine没有则返回null
*
*
* @return WebSocketEngine
*/
public final WebSocketEngine getLocalWebSocketEngine() {
return this.localEngine;
} }
public final int sendEachMessage(Serializable groupid, String text, boolean last) { /**
return sendMessage(groupid, false, (Object) text, last); * 向指定用户发送消息,先发送本地连接,再发送远程连接 <br>
* 如果当前WebSocketNode是远程模式此方法只发送远程连接
*
* @param message 消息内容
* @param userids Serializable[]
*
* @return 为0表示成功 其他值表示部分发送异常
*/
public final CompletableFuture<Integer> sendMessage(Object message, final Serializable... userids) {
return sendMessage(message, true, userids);
} }
public final int sendRecentMessage(Serializable groupid, String text) { /**
return sendMessage(groupid, true, (Object) text, true); * 向指定用户发送消息,先发送本地连接,再发送远程连接 <br>
* 如果当前WebSocketNode是远程模式此方法只发送远程连接
*
* @param message 消息内容
* @param last 是否最后一条
* @param userids Serializable[]
*
* @return 为0表示成功 其他值表示部分发送异常
*/
public final CompletableFuture<Integer> sendMessage(final Object message, final boolean last, final Serializable... userids) {
if (userids == null || userids.length < 1) return CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY);
if (this.localEngine != null && this.sncpNodeAddresses == null) { //本地模式且没有分布式
return this.localEngine.sendMessage(message, last, userids);
}
CompletableFuture<Integer> future = null;
for (Serializable userid : userids) {
future = future == null ? sendOneMessage(message, last, userid)
: future.thenCombine(sendOneMessage(message, last, userid), (a, b) -> a | b);
}
return future == null ? CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY) : future;
} }
public final int sendRecentMessage(Serializable groupid, String text, boolean last) { /**
return sendMessage(groupid, true, (Object) text, last); * 广播消息, 给所有人发消息
*
* @param message 消息内容
*
* @return 为0表示成功 其他值表示部分发送异常
*/
public final CompletableFuture<Integer> broadcastMessage(final Object message) {
return broadcastMessage(message, true);
} }
public final int sendMessage(Serializable groupid, boolean recent, String text) { /**
return sendMessage(groupid, recent, (Object) text, true); * 广播消息, 给所有人发消息
*
* @param message 消息内容
* @param last 是否最后一条
*
* @return 为0表示成功 其他值表示部分发送异常
*/
public final CompletableFuture<Integer> broadcastMessage(final Object message, final boolean last) {
if (this.localEngine != null && this.sncpNodeAddresses == null) { //本地模式且没有分布式
return this.localEngine.broadcastMessage(message, last);
}
CompletableFuture<Integer> localFuture = this.localEngine == null ? null : this.localEngine.broadcastMessage(message, last);
CompletableFuture<Collection<InetSocketAddress>> addrsFuture = sncpNodeAddresses.getCollectionAsync("redkale_sncpnodes");
CompletableFuture<Integer> remoteFuture = addrsFuture.thenCompose((Collection<InetSocketAddress> addrs) -> {
if (finest) logger.finest("websocket broadcast message on " + addrs);
if (addrs == null || addrs.isEmpty()) return CompletableFuture.completedFuture(0);
CompletableFuture<Integer> future = null;
for (InetSocketAddress addr : addrs) {
if (addr == null || addr.equals(localSncpAddress)) continue;
future = future == null ? remoteNode.broadcastMessage(addr, message, last)
: future.thenCombine(remoteNode.broadcastMessage(addr, message, last), (a, b) -> a | b);
}
return future == null ? CompletableFuture.completedFuture(0) : future;
});
return localFuture == null ? remoteFuture : localFuture.thenCombine(remoteFuture, (a, b) -> a | b);
} }
public final int sendMessage(Serializable groupid, boolean recent, String text, boolean last) { private CompletableFuture<Integer> sendOneMessage(final Object message, final boolean last, final Serializable userid) {
return sendMessage(groupid, recent, (Object) text, last); if (finest) logger.finest("websocket want send message {userid:" + userid + ", content:'" + message + "'} from locale node to locale engine");
CompletableFuture<Integer> localFuture = null;
if (this.localEngine != null) localFuture = localEngine.sendMessage(message, last, userid);
if (this.sncpNodeAddresses == null || this.remoteNode == null) {
if (finest) logger.finest("websocket remote node is null");
//没有CacheSource就不会有分布式节点
return localFuture == null ? CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY) : localFuture;
}
//远程节点发送消息
CompletableFuture<Collection<InetSocketAddress>> addrsFuture = sncpNodeAddresses.getCollectionAsync(userid);
CompletableFuture<Integer> remoteFuture = addrsFuture.thenCompose((Collection<InetSocketAddress> addrs) -> {
if (finest) logger.finest("websocket found userid:" + userid + " on " + addrs);
if (addrs == null || addrs.isEmpty()) return CompletableFuture.completedFuture(0);
CompletableFuture<Integer> future = null;
for (InetSocketAddress addr : addrs) {
if (addr == null || addr.equals(localSncpAddress)) continue;
future = future == null ? remoteNode.sendMessage(addr, message, last, userid)
: future.thenCombine(remoteNode.sendMessage(addr, message, last, userid), (a, b) -> a | b);
}
return future == null ? CompletableFuture.completedFuture(0) : future;
});
return localFuture == null ? remoteFuture : localFuture.thenCombine(remoteFuture, (a, b) -> a | b);
} }
//--------------------------------------------------------------------------------
public final int sendEachMessage(Serializable groupid, byte[] data) {
return sendMessage(groupid, false, (Object) data, true);
}
public final int sendEachMessage(Serializable groupid, byte[] data, boolean last) {
return sendMessage(groupid, false, (Object) data, last);
}
public final int sendRecentMessage(Serializable groupid, byte[] data) {
return sendMessage(groupid, true, (Object) data, true);
}
public final int sendRecentMessage(Serializable groupid, byte[] data, boolean last) {
return sendMessage(groupid, true, (Object) data, last);
}
public final int sendMessage(Serializable groupid, boolean recent, byte[] data) {
return sendMessage(groupid, recent, data, true);
}
public final int sendMessage(Serializable groupid, boolean recent, byte[] data, boolean last) {
return sendMessage(groupid, recent, (Object) data, last);
}
//--------------------------------------------------------------------------------
public final int sendEachMessage(Serializable groupid, Object message) {
return sendMessage(groupid, false, message, true);
}
public final int sendEachMessage(Serializable groupid, Object message, boolean last) {
return sendMessage(groupid, false, message, last);
}
public final int sendRecentMessage(Serializable groupid, Object message) {
return sendMessage(groupid, true, message, true);
}
public final int sendRecentMessage(Serializable groupid, Object message, boolean last) {
return sendMessage(groupid, true, message, last);
}
public final int sendMessage(Serializable groupid, boolean recent, Object message) {
return sendMessage(groupid, recent, message, true);
}
} }

View File

@@ -7,10 +7,16 @@ package org.redkale.net.http;
import org.redkale.util.Utility; import org.redkale.util.Utility;
import java.io.*; import java.io.*;
import java.nio.ByteBuffer;
import java.util.function.Supplier;
import java.util.logging.*;
import org.redkale.convert.*;
/** /**
* *
* <p> 详情见: https://redkale.org * <p>
* 详情见: https://redkale.org
*
* @author zhangjx * @author zhangjx
*/ */
public final class WebSocketPacket { public final class WebSocketPacket {
@@ -51,6 +57,16 @@ public final class WebSocketPacket {
protected boolean last = true; protected boolean last = true;
protected Object sendJson;
Convert sendConvert;
ByteBuffer[] sendBuffers;
ConvertMask receiveMasker;
ByteBuffer[] receiveBuffers;
public WebSocketPacket() { public WebSocketPacket() {
} }
@@ -76,6 +92,31 @@ public final class WebSocketPacket {
this.last = fin; this.last = fin;
} }
public WebSocketPacket(Convert convert, Object json, boolean fin) {
this.type = (convert == null || !convert.isBinary()) ? FrameType.TEXT : FrameType.BINARY;
this.sendConvert = convert;
this.sendJson = json;
this.last = fin;
}
WebSocketPacket(ByteBuffer[] sendBuffers, FrameType type, boolean fin) {
this.type = type;
this.last = fin;
this.setSendBuffers(sendBuffers);
}
void setSendBuffers(ByteBuffer[] sendBuffers) {
this.sendBuffers = sendBuffers;
}
ByteBuffer[] duplicateSendBuffers() {
ByteBuffer[] rs = new ByteBuffer[this.sendBuffers.length];
for (int i = 0; i < this.sendBuffers.length; i++) {
rs[i] = this.sendBuffers[i].duplicate().asReadOnlyBuffer();
}
return rs;
}
public WebSocketPacket(byte[] data) { public WebSocketPacket(byte[] data) {
this(FrameType.BINARY, data, true); this(FrameType.BINARY, data, true);
} }
@@ -118,6 +159,228 @@ public final class WebSocketPacket {
@Override @Override
public String toString() { public String toString() {
return this.getClass().getSimpleName() + "[type=" + type + ", last=" + last + (payload != null ? (", payload=" + payload) : "") + (bytes != null ? (", bytes=[" + bytes.length + ']') : "") + "]"; return this.getClass().getSimpleName() + "[type=" + type + ", last=" + last + (payload != null ? (", payload=" + payload) : "") + (bytes != null ? (", bytes=[" + bytes.length + ']') : "") + (sendJson != null ? (", json=" + sendJson) : "") + "]";
}
/**
* 消息编码
*
* @param supplier Supplier
*
* @return ByteBuffer[]
*/
ByteBuffer[] encode(final Supplier<ByteBuffer> supplier) {
final byte opcode = (byte) (this.type.getValue() | 0x80);
if (this.sendConvert != null) {
Supplier<ByteBuffer> newsupplier = new Supplier<ByteBuffer>() {
private ByteBuffer buf = supplier.get();
@Override
public ByteBuffer get() {
if (buf != null) {
ByteBuffer rs = buf;
rs.position(6);
this.buf = null;
return rs;
}
return supplier.get();
}
};
ByteBuffer[] buffers = this.sendConvert.convertTo(newsupplier, sendJson);
int len = 0;
for (ByteBuffer buf : buffers) {
len += buf.remaining();
}
int contentLength = len - 6;
ByteBuffer firstbuf = buffers[0];
if (contentLength <= 0x7D) { //125
firstbuf.put(4, opcode);
firstbuf.put(5, (byte) contentLength);
firstbuf.position(4);
} else if (contentLength <= 0xFFFF) {
firstbuf.put(2, opcode);
firstbuf.put(3, (byte) 0x7E); //126
firstbuf.putChar(4, (char) contentLength);
firstbuf.position(2);
} else {
firstbuf.put(0, opcode);
firstbuf.put(1, (byte) 0x7F); //127
firstbuf.putInt(2, contentLength);
}
return buffers;
}
ByteBuffer buffer = supplier.get(); //确保ByteBuffer的capacity不能小于128
final byte[] content = getContent();
final int len = content.length;
if (len <= 0x7D) { //125
buffer.put(opcode);
buffer.put((byte) len);
buffer.put(content);
buffer.flip();
return new ByteBuffer[]{buffer};
}
if (len <= 0xFFFF) { // 65535
buffer.put(opcode);
buffer.put((byte) 0x7E); //126
buffer.putChar((char) len);
} else {
buffer.put(opcode);
buffer.put((byte) 0x7F); //127
buffer.putInt(len);
}
int start = buffer.remaining();
int pend = len - buffer.remaining();
if (pend <= 0) {
buffer.put(content);
buffer.flip();
return new ByteBuffer[]{buffer};
}
buffer.put(content, 0, buffer.remaining());
buffer.flip();
final int capacity = buffer.capacity();
final ByteBuffer[] buffers = new ByteBuffer[(pend / capacity) + 1 + ((pend % capacity) > 0 ? 1 : 0)];
buffers[0] = buffer;
for (int i = 1; i < buffers.length; i++) {
ByteBuffer buf = supplier.get();
buf.put(content, start, Math.min(pend, capacity));
buf.flip();
buffers[i] = buf;
start += capacity;
pend -= capacity;
}
return buffers;
}
// public static void main(String[] args) throws Throwable {
// byte[] mask = new byte[]{(byte) 0x8f, (byte) 0xf8, (byte) 0x6d, (byte) 0x94};
// ByteBuffer buffer = ByteBuffer.wrap(new byte[]{(byte) 0x67, (byte) 0x47, (byte) 0xf4, (byte) 0x70, (byte) 0x37, (byte) 0x52, (byte) 0x8b, (byte) 0x0c, (byte) 0x20, (byte) 0x1e, (byte) 0xdb, (byte) 0x1c, (byte) 0x69, (byte) 0x79, (byte) 0xc2});
// ConvertMask masker = new ConvertMask() {
// private int index = 0;
//
// public byte unmask(byte value) {
// return (byte) (value ^ mask[index++ % 4]);
// }
// };
// String rs = JsonConvert.root().convertFrom(String.class, masker, buffer);
// System.out.println(rs);
// }
/**
* 消息解码 <br>
*
* 0 1 2 3
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
* +-+-+-+-+-------+-+-------------+-------------------------------+
* |F|R|R|R| opcode|M| Payload len | Extended payload length |
* |I|S|S|S| (4) |A| (7) | (16/64) |
* |N|V|V|V| |S| | (if payload len==126/127) |
* | |1|2|3| |K| | |
* +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
* | Extended payload length continued, if payload len == 127 |
* + - - - - - - - - - - - - - - - +-------------------------------+
* | |Masking-key, if MASK set to 1 |
* +-------------------------------+-------------------------------+
* | Masking-key (continued) | Payload Data |
* +-------------------------------- - - - - - - - - - - - - - - - +
* : Payload Data continued :
* + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
* | Payload Data continued |
* +-----------------------------------------------------------------------+
*
* @param buffer
* @param exbuffers
*
* @return
*/
WebSocketPacket decode(final Logger logger, final ByteBuffer buffer, ByteBuffer... exbuffers) {
final boolean debug = false; //调试开关
if (debug) {
int remain = buffer.remaining();
if (exbuffers != null) {
for (ByteBuffer b : exbuffers) {
remain += b == null ? 0 : b.remaining();
}
}
logger.log(Level.FINEST, "read websocket message's length = " + remain);
}
if (buffer.remaining() < 2) return null;
byte opcode = buffer.get();
this.last = (opcode & 0b1000_0000) != 0;
this.type = FrameType.valueOf(opcode & 0xF);
if (type == FrameType.CLOSE) {
if (debug) logger.log(Level.FINEST, " receive close command from websocket client");
return this;
}
final boolean checkrsv = false;//暂时不校验
if (checkrsv && (opcode & 0b0111_0000) != 0) {
if (debug) logger.log(Level.FINE, "rsv1 rsv2 rsv3 must be 0, but not (" + opcode + ")");
return null; //rsv1 rsv2 rsv3 must be 0
}
//0x00 表示一个后续帧
//0x01 表示一个文本帧
//0x02 表示一个二进制帧
//0x03-07 为以后的非控制帧保留
//0x8 表示一个连接关闭
//0x9 表示一个ping
//0xA 表示一个pong
//0x0B-0F 为以后的控制帧保留
final boolean control = (opcode & 0b0000_1000) != 0; //是否控制帧
byte lengthCode = buffer.get();
final boolean masked = (lengthCode & 0x80) == 0x80;
if (masked) lengthCode ^= 0x80; //mask
int length;
if (lengthCode <= 0x7D) { //125
length = lengthCode;
} else {
if (control) {
if (debug) logger.log(Level.FINE, " receive control command from websocket client");
return null;
}
if (lengthCode == 0x7E) {//0x7E=126
length = (int) buffer.getChar();
} else {
length = buffer.getInt();
}
}
if (masked) {
final byte[] masks = new byte[4];
buffer.get(masks);
this.receiveMasker = new ConvertMask() {
private int index = 0;
@Override
public byte unmask(byte value) {
return (byte) (value ^ masks[index++ % 4]);
}
};
}
this.receiveBuffers = Utility.append(new ByteBuffer[]{buffer}, exbuffers);
return this;
}
byte[] getReceiveBytes() {
if (this.receiveBuffers.length == 0) return new byte[0];
if (this.receiveBuffers.length == 1 && this.receiveBuffers[0].remaining() == 0) return new byte[0];
int count = 0;
for (ByteBuffer buf : this.receiveBuffers) {
count += buf.remaining();
}
byte[] bs = new byte[count];
int index = 0;
for (ByteBuffer buf : this.receiveBuffers) {
int r = buf.remaining();
buf.get(bs, index, r);
index += r;
}
ConvertMask mask = this.receiveMasker;
if (mask != null) {
for (int i = 0; i < bs.length; i++) {
bs[i] = mask.unmask(bs[i]);
}
}
return bs;
} }
} }

View File

@@ -11,18 +11,23 @@ import static org.redkale.net.http.WebSocket.*;
import org.redkale.net.http.WebSocketPacket.FrameType; import org.redkale.net.http.WebSocketPacket.FrameType;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.nio.channels.*; import java.nio.channels.*;
import java.security.SecureRandom;
import java.util.*; import java.util.*;
import java.util.concurrent.*; import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.BiConsumer;
import java.util.logging.*; import java.util.logging.*;
import org.redkale.convert.Convert;
import org.redkale.util.Utility;
/** /**
* WebSocket的消息接收发送器, 一个WebSocket对应一个WebSocketRunner
*
* <p>
* 详情见: https://redkale.org
* *
* <p> 详情见: https://redkale.org
* @author zhangjx * @author zhangjx
*/ */
public class WebSocketRunner implements Runnable { class WebSocketRunner implements Runnable {
private final WebSocketEngine engine; private final WebSocketEngine engine;
@@ -34,42 +39,32 @@ public class WebSocketRunner implements Runnable {
private ByteBuffer readBuffer; private ByteBuffer readBuffer;
private ByteBuffer writeBuffer; protected volatile boolean closed = false;
protected boolean closed = false;
private AtomicBoolean writing = new AtomicBoolean(); private AtomicBoolean writing = new AtomicBoolean();
private final Coder coder = new Coder(); private final BlockingQueue<QueueEntry> queue = new ArrayBlockingQueue(1024);
private final BlockingQueue<byte[]> queue = new ArrayBlockingQueue(1024); private final BiConsumer<WebSocket, Object> restMessageConsumer; //主要供RestWebSocket使用
private final boolean wsbinary; protected long lastSendTime;
public WebSocketRunner(Context context, WebSocket webSocket, AsyncConnection channel, final boolean wsbinary) { WebSocketRunner(Context context, WebSocket webSocket, BiConsumer<WebSocket, Object> messageConsumer, AsyncConnection channel) {
this.context = context; this.context = context;
this.engine = webSocket._engine; this.engine = webSocket._engine;
this.webSocket = webSocket; this.webSocket = webSocket;
this.restMessageConsumer = messageConsumer;
this.channel = channel; this.channel = channel;
this.wsbinary = wsbinary;
webSocket._runner = this;
this.coder.logger = context.getLogger();
this.coder.debugable = false;//context.getLogger().isLoggable(Level.FINEST);
this.readBuffer = context.pollBuffer(); this.readBuffer = context.pollBuffer();
this.writeBuffer = context.pollBuffer();
} }
@Override @Override
public void run() { public void run() {
final boolean debug = this.coder.debugable; final boolean debug = true;
try { try {
webSocket.onConnected(); webSocket.onConnected();
channel.setReadTimeoutSecond(300); //读取超时5分钟 channel.setReadTimeoutSecond(300); //读取超时5分钟
if (channel.isOpen()) { if (channel.isOpen()) {
if (wsbinary) {
webSocket.onRead(channel);
return;
}
channel.read(readBuffer, null, new CompletionHandler<Integer, Void>() { channel.read(readBuffer, null, new CompletionHandler<Integer, Void>() {
private ByteBuffer recentExBuffer; private ByteBuffer recentExBuffer;
@@ -93,48 +88,151 @@ public class WebSocketRunner implements Runnable {
return; return;
} }
readBuffer.flip(); readBuffer.flip();
try {
ByteBuffer[] exBuffers = null; ByteBuffer[] exBuffers = null;
if (!readBuffers.isEmpty()) { if (!readBuffers.isEmpty()) {
exBuffers = readBuffers.toArray(new ByteBuffer[readBuffers.size()]); exBuffers = readBuffers.toArray(new ByteBuffer[readBuffers.size()]);
readBuffers.clear(); readBuffers.clear();
recentExBuffer = null; recentExBuffer = null;
for (ByteBuffer b : exBuffers) { for (ByteBuffer b : exBuffers) {
b.flip(); b.flip();
}
} }
WebSocketPacket packet = coder.decode(readBuffer, exBuffers); }
if (exBuffers != null) {
for (ByteBuffer b : exBuffers) { try {
context.offerBuffer(b); WebSocketPacket packet;
try {
packet = new WebSocketPacket().decode(context.getLogger(), readBuffer, exBuffers);
} catch (Exception e) { //接收的消息体解析失败
webSocket.onOccurException(e, Utility.append(new ByteBuffer[]{readBuffer}, exBuffers == null ? new ByteBuffer[0] : exBuffers));
if (readBuffer != null) {
readBuffer.clear();
channel.read(readBuffer, null, this);
} }
return;
} }
if (packet == null) { if (packet == null) {
failed(null, attachment1); failed(null, attachment1);
if (debug) context.getLogger().log(Level.FINEST, "WebSocketRunner abort on decode WebSocketPacket, force to close channel, live " + (System.currentTimeMillis() - webSocket.getCreatetime()) / 1000 + " seconds"); if (debug) context.getLogger().log(Level.FINEST, "WebSocketRunner abort on decode WebSocketPacket, force to close channel, live " + (System.currentTimeMillis() - webSocket.getCreatetime()) / 1000 + " seconds");
return; return;
} }
if (readBuffer != null) {
readBuffer.clear(); if (packet.type == FrameType.TEXT) {
channel.read(readBuffer, null, this); Convert textConvert = webSocket.getTextConvert();
} if (textConvert == null) {
webSocket._group.setRecentWebSocket(webSocket); byte[] message = packet.getReceiveBytes();
try { if (readBuffer != null) {
if (packet.type == FrameType.TEXT) { readBuffer.clear();
webSocket.onMessage(packet.getPayload()); channel.read(readBuffer, null, this);
} else if (packet.type == FrameType.BINARY) { }
webSocket.onMessage(packet.getBytes()); try {
} else if (packet.type == FrameType.PONG) { webSocket.onMessage(new String(message, "UTF-8"), packet.last);
webSocket.onPong(packet.getBytes()); } catch (Exception e) {
} else if (packet.type == FrameType.PING) { context.getLogger().log(Level.SEVERE, "WebSocket onBinaryMessage error (" + packet + ")", e);
webSocket.onPing(packet.getBytes()); }
} else {
Object message;
try {
message = textConvert.convertFrom(webSocket._messageTextType, packet.receiveMasker, packet.receiveBuffers);
} catch (Exception e) { //接收的消息体解析失败
webSocket.onOccurException(e, packet.receiveBuffers);
if (readBuffer != null) {
readBuffer.clear();
channel.read(readBuffer, null, this);
}
return;
}
if (readBuffer != null) {
readBuffer.clear();
channel.read(readBuffer, null, this);
}
try {
if (restMessageConsumer != null) { //主要供RestWebSocket使用
restMessageConsumer.accept(webSocket, message);
} else {
webSocket.onMessage(message, packet.last);
}
} catch (Exception e) {
context.getLogger().log(Level.SEVERE, "WebSocket onTextMessage error (" + packet + ")", e);
}
}
} else if (packet.type == FrameType.BINARY) {
Convert binaryConvert = webSocket.getBinaryConvert();
if (binaryConvert == null) {
byte[] message = packet.getReceiveBytes();
if (readBuffer != null) {
readBuffer.clear();
channel.read(readBuffer, null, this);
}
try {
webSocket.onMessage(message, packet.last);
} catch (Exception e) {
context.getLogger().log(Level.SEVERE, "WebSocket onBinaryMessage error (" + packet + ")", e);
}
} else {
Object message;
try {
message = binaryConvert.convertFrom(webSocket._messageTextType, packet.receiveMasker, packet.receiveBuffers);
} catch (Exception e) { //接收的消息体解析失败
webSocket.onOccurException(e, packet.receiveBuffers);
if (readBuffer != null) {
readBuffer.clear();
channel.read(readBuffer, null, this);
}
return;
}
if (readBuffer != null) {
readBuffer.clear();
channel.read(readBuffer, null, this);
}
try {
if (restMessageConsumer != null) { //主要供RestWebSocket使用
restMessageConsumer.accept(webSocket, message);
} else {
webSocket.onMessage(message, packet.last);
}
} catch (Exception e) {
context.getLogger().log(Level.SEVERE, "WebSocket onTextMessage error (" + packet + ")", e);
}
}
} else if (packet.type == FrameType.PONG) {
byte[] message = packet.getReceiveBytes();
if (readBuffer != null) {
readBuffer.clear();
channel.read(readBuffer, null, this);
}
try {
webSocket.onPong(message);
} catch (Exception e) {
context.getLogger().log(Level.SEVERE, "WebSocket onPong error (" + packet + ")", e);
}
} else if (packet.type == FrameType.PING) {
byte[] message = packet.getReceiveBytes();
if (readBuffer != null) {
readBuffer.clear();
channel.read(readBuffer, null, this);
}
try {
webSocket.onPing(message);
} catch (Exception e) {
context.getLogger().log(Level.SEVERE, "WebSocket onPing error (" + packet + ")", e);
}
} else {
context.getLogger().log(Level.WARNING, "WebSocketRunner onMessage by unknown FrameType : " + packet);
if (readBuffer != null) {
readBuffer.clear();
channel.read(readBuffer, null, this);
} }
} catch (Exception e) {
context.getLogger().log(Level.INFO, "WebSocket onMessage error (" + packet + ")", e);
} }
} catch (Throwable t) { } catch (Throwable t) {
closeRunner(); closeRunner();
if (debug) context.getLogger().log(Level.FINEST, "WebSocketRunner abort on read WebSocketPacket, force to close channel, live " + (System.currentTimeMillis() - webSocket.getCreatetime()) / 1000 + " seconds", t); if (debug) context.getLogger().log(Level.FINEST, "WebSocketRunner abort on read WebSocketPacket, force to close channel, live " + (System.currentTimeMillis() - webSocket.getCreatetime()) / 1000 + " seconds", t);
} finally {
if (exBuffers != null) {
for (ByteBuffer b : exBuffers) {
context.offerBuffer(b);
}
}
} }
} }
@@ -156,54 +254,66 @@ public class WebSocketRunner implements Runnable {
} }
} }
public int sendMessage(WebSocketPacket packet) { public CompletableFuture<Integer> sendMessage(WebSocketPacket packet) {
if (packet == null) return RETCODE_SEND_ILLPACKET; if (packet == null) return CompletableFuture.completedFuture(RETCODE_SEND_ILLPACKET);
if (closed) return RETCODE_WSOCKET_CLOSED; if (closed) return CompletableFuture.completedFuture(RETCODE_WSOCKET_CLOSED);
final boolean debug = this.coder.debugable; boolean debug = true;
//System.out.println("推送消息"); //System.out.println("推送消息");
final byte[] bytes = coder.encode(packet); //if (debug) context.getLogger().log(Level.FINEST, "send web socket message: " + packet);
if (debug) context.getLogger().log(Level.FINEST, "send web socket message's length = " + bytes.length); final CompletableFuture<Integer> futureResult = new CompletableFuture<>();
if (writing.getAndSet(true)) { if (writing.getAndSet(true)) {
queue.add(bytes); queue.add(new QueueEntry(futureResult, packet));
return 0; return futureResult;
}
if (writeBuffer == null) return RETCODE_ILLEGALBUFFER;
ByteBuffer sendBuffer = null;
if (bytes.length <= writeBuffer.capacity()) {
writeBuffer.clear();
writeBuffer.put(bytes);
writeBuffer.flip();
sendBuffer = writeBuffer;
} else {
sendBuffer = ByteBuffer.wrap(bytes);
} }
ByteBuffer[] buffers = packet.sendBuffers != null ? packet.duplicateSendBuffers() : packet.encode(this.context.getBufferSupplier());
try { try {
channel.write(sendBuffer, sendBuffer, new CompletionHandler<Integer, ByteBuffer>() { this.lastSendTime = System.currentTimeMillis();
channel.write(buffers, buffers, new CompletionHandler<Integer, ByteBuffer[]>() {
private CompletableFuture<Integer> future = futureResult;
@Override @Override
public void completed(Integer result, ByteBuffer attachment) { public void completed(Integer result, ByteBuffer[] attachments) {
if (attachment == null || closed) return; if (attachments == null || closed) {
try { if (future != null) {
if (attachment.hasRemaining()) { future.complete(RETCODE_WSOCKET_CLOSED);
if (debug) context.getLogger().log(Level.FINEST, "WebSocketRunner write completed reemaining: " + attachment.remaining()); future = null;
channel.write(attachment, attachment, this); if (attachments != null) {
return; for (ByteBuffer buf : attachments) {
} context.offerBuffer(buf);
byte[] bs = queue.poll(); }
if (bs != null && writeBuffer != null) {
ByteBuffer sendBuffer;
if (bs.length <= writeBuffer.capacity()) {
writeBuffer.clear();
writeBuffer.put(bs);
writeBuffer.flip();
sendBuffer = writeBuffer;
} else {
sendBuffer = ByteBuffer.wrap(bs);
} }
channel.write(sendBuffer, sendBuffer, this); }
return;
}
try {
int index = -1;
for (int i = 0; i < attachments.length; i++) {
if (attachments[i].hasRemaining()) {
index = i;
break;
}
}
if (index >= 0) {
channel.write(attachments, index, attachments.length - index, attachments, this);
return; return;
} }
} catch (NullPointerException e) { if (future != null) {
future.complete(0);
future = null;
if (attachments != null) {
for (ByteBuffer buf : attachments) {
context.offerBuffer(buf);
}
}
}
QueueEntry entry = queue.poll();
if (entry != null) {
future = entry.future;
ByteBuffer[] buffers = entry.packet.sendBuffers != null ? entry.packet.duplicateSendBuffers() : entry.packet.encode(context.getBufferSupplier());
lastSendTime = System.currentTimeMillis();
channel.write(buffers, buffers, this);
}
} catch (Exception e) { } catch (Exception e) {
closeRunner(); closeRunner();
context.getLogger().log(Level.WARNING, "WebSocket sendMessage abort on rewrite, force to close channel, live " + (System.currentTimeMillis() - webSocket.getCreatetime()) / 1000 + " seconds", e); context.getLogger().log(Level.WARNING, "WebSocket sendMessage abort on rewrite, force to close channel, live " + (System.currentTimeMillis() - webSocket.getCreatetime()) / 1000 + " seconds", e);
@@ -212,7 +322,7 @@ public class WebSocketRunner implements Runnable {
} }
@Override @Override
public void failed(Throwable exc, ByteBuffer attachment) { public void failed(Throwable exc, ByteBuffer[] attachments) {
writing.set(false); writing.set(false);
closeRunner(); closeRunner();
if (exc != null) { if (exc != null) {
@@ -220,13 +330,13 @@ public class WebSocketRunner implements Runnable {
} }
} }
}); });
return 0;
} catch (Exception t) { } catch (Exception t) {
writing.set(false); writing.set(false);
closeRunner(); closeRunner();
context.getLogger().log(Level.FINE, "WebSocket sendMessage abort, force to close channel, live " + (System.currentTimeMillis() - webSocket.getCreatetime()) / 1000 + " seconds", t); context.getLogger().log(Level.FINE, "WebSocket sendMessage abort, force to close channel, live " + (System.currentTimeMillis() - webSocket.getCreatetime()) / 1000 + " seconds", t);
return RETCODE_SENDEXCEPTION; futureResult.complete(RETCODE_SENDEXCEPTION);
} }
return futureResult;
} }
public void closeRunner() { public void closeRunner() {
@@ -239,286 +349,21 @@ public class WebSocketRunner implements Runnable {
} catch (Throwable t) { } catch (Throwable t) {
} }
context.offerBuffer(readBuffer); context.offerBuffer(readBuffer);
context.offerBuffer(writeBuffer);
readBuffer = null; readBuffer = null;
writeBuffer = null;
engine.remove(webSocket); engine.remove(webSocket);
webSocket.onClose(0, null); webSocket.onClose(0, null);
} }
} }
private static final class Masker { private static final class QueueEntry {
public static final int MASK_SIZE = 4; public final CompletableFuture<Integer> future;
private ByteBuffer buffer; public final WebSocketPacket packet;
private ByteBuffer[] exbuffers; public QueueEntry(CompletableFuture<Integer> future, WebSocketPacket packet) {
this.future = future;
private byte[] mask; this.packet = packet;
private int index = 0;
public Masker(ByteBuffer buffer, ByteBuffer... exbuffers) {
this.buffer = buffer;
this.exbuffers = exbuffers == null || exbuffers.length == 0 ? null : exbuffers;
}
public Masker() {
generateMask();
}
public int remaining() {
int r = buffer.remaining();
if (exbuffers != null) {
for (ByteBuffer b : exbuffers) {
r += b.remaining();
}
}
return r;
}
public byte get() {
return buffer.get();
}
public byte[] get(final int size) {
byte[] bytes = new byte[size];
if (buffer.remaining() >= size) {
buffer.get(bytes);
} else { //必须有 exbuffers
int offset = buffer.remaining();
buffer.get(bytes, 0, buffer.remaining());
for (ByteBuffer b : exbuffers) {
b.get(bytes, offset, b.remaining());
offset += b.remaining();
}
}
return bytes;
}
public byte unmask() {
final byte b = get();
return mask == null ? b : (byte) (b ^ mask[index++ % MASK_SIZE]);
}
public byte[] unmask(int count) {
byte[] bytes = get(count);
if (mask != null) {
for (int i = 0; i < bytes.length; i++) {
bytes[i] ^= mask[index++ % MASK_SIZE];
}
}
return bytes;
}
public void generateMask() {
mask = new byte[MASK_SIZE];
new SecureRandom().nextBytes(mask);
}
public void mask(byte[] bytes, int location, byte b) {
bytes[location] = mask == null ? b : (byte) (b ^ mask[index++ % MASK_SIZE]);
}
public void mask(byte[] target, int location, byte[] bytes) {
if (bytes != null && target != null) {
for (int i = 0; i < bytes.length; i++) {
target[location + i] = mask == null ? bytes[i] : (byte) (bytes[i] ^ mask[index++ % MASK_SIZE]);
}
}
}
public byte[] maskAndPrepend(byte[] packet) {
byte[] masked = new byte[packet.length + MASK_SIZE];
System.arraycopy(getMask(), 0, masked, 0, MASK_SIZE);
mask(masked, MASK_SIZE, packet);
return masked;
}
public void setBuffer(ByteBuffer buffer) {
this.buffer = buffer;
}
public byte[] getMask() {
return mask;
}
public void readMask() {
mask = get(MASK_SIZE);
}
}
private static final class Coder {
protected byte inFragmentedType;
protected byte outFragmentedType;
protected final boolean maskData = false;
protected boolean processingFragment;
private boolean debugable;
private Logger logger;
/**
* 0 1 2 3
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
* +-+-+-+-+-------+-+-------------+-------------------------------+
* |F|R|R|R| opcode|M| Payload len | Extended payload length |
* |I|S|S|S| (4) |A| (7) | (16/64) |
* |N|V|V|V| |S| | (if payload len==126/127) |
* | |1|2|3| |K| | |
* +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
* | Extended payload length continued, if payload len == 127 |
* + - - - - - - - - - - - - - - - +-------------------------------+
* | |Masking-key, if MASK set to 1 |
* +-------------------------------+-------------------------------+
* | Masking-key (continued) | Payload Data |
* +-------------------------------- - - - - - - - - - - - - - - - +
* : Payload Data continued :
* + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
* | Payload Data continued |
* +-----------------------------------------------------------------------+
*
* @param buffer
* @param exbuffers
* @return
*/
public WebSocketPacket decode(final ByteBuffer buffer, ByteBuffer... exbuffers) {
final boolean debug = this.debugable;
if (debug) {
int remain = buffer.remaining();
if (exbuffers != null) {
for (ByteBuffer b : exbuffers) {
remain += b == null ? 0 : b.remaining();
}
}
logger.log(Level.FINEST, "read web socket message's length = " + remain);
}
if (buffer.remaining() < 2) return null;
byte opcode = buffer.get();
final boolean last = (opcode & 0b1000000) != 0;
final boolean checkrsv = false;//暂时不校验
if (checkrsv && (opcode & 0b01110000) != 0) {
if (debug) logger.log(Level.FINE, "rsv1 rsv2 rsv3 must be 0, but not (" + opcode + ")");
return null; //rsv1 rsv2 rsv3 must be 0
}
//0x00 表示一个后续帧
//0x01 表示一个文本帧
//0x02 表示一个二进制帧
//0x03-07 为以后的非控制帧保留
//0x8 表示一个连接关闭
//0x9 表示一个ping
//0xA 表示一个pong
//0x0B-0F 为以后的控制帧保留
final boolean control = (opcode & 0x08) == 0x08; //是否控制帧
//final boolean continuation = opcode == 0;
FrameType type = FrameType.valueOf(opcode & 0xf);
if (type == FrameType.CLOSE) {
if (debug) logger.log(Level.FINEST, " receive close command from websocket client");
return null;
}
byte lengthCode = buffer.get();
final Masker masker = new Masker(buffer, exbuffers);
final boolean masked = (lengthCode & 0x80) == 0x80;
if (masked) lengthCode ^= 0x80; //mask
int length;
if (lengthCode <= 125) {
length = lengthCode;
} else {
if (control) {
if (debug) logger.log(Level.FINE, " receive control command from websocket client");
return null;
}
final int lengthBytes = lengthCode == 126 ? 2 : 8;
if (buffer.remaining() < lengthBytes) {
if (debug) logger.log(Level.FINE, " read illegal message length from websocket, expect " + lengthBytes + " but " + buffer.remaining());
return null;
}
length = toInt(masker.unmask(lengthBytes));
}
if (masked) {
if (buffer.remaining() < Masker.MASK_SIZE) {
if (debug) logger.log(Level.FINE, " read illegal masker length from websocket, expect " + Masker.MASK_SIZE + " but " + buffer.remaining());
return null;
}
masker.readMask();
}
if (masker.remaining() < length) {
if (debug) logger.log(Level.FINE, " read illegal remaining length from websocket, expect " + length + " but " + masker.remaining());
return null;
}
final byte[] data = masker.unmask(length);
if (data.length != length) {
if (debug) logger.log(Level.FINE, " read illegal unmask length from websocket, expect " + length + " but " + data.length);
return null;
}
return new WebSocketPacket(type, data, last);
}
public byte[] encode(WebSocketPacket frame) {
byte opcode = (byte) (frame.type.getValue() | 0x80);
final byte[] bytes = frame.getContent();
final byte[] lengthBytes = encodeLength(bytes.length);
int length = 1 + lengthBytes.length + bytes.length + (maskData ? Masker.MASK_SIZE : 0);
int payloadStart = 1 + lengthBytes.length + (maskData ? Masker.MASK_SIZE : 0);
final byte[] packet = new byte[length];
packet[0] = opcode;
System.arraycopy(lengthBytes, 0, packet, 1, lengthBytes.length);
if (maskData) {
Masker masker = new Masker();
packet[1] |= 0x80;
masker.mask(packet, payloadStart, bytes);
System.arraycopy(masker.getMask(), 0, packet, payloadStart - Masker.MASK_SIZE, Masker.MASK_SIZE);
} else {
System.arraycopy(bytes, 0, packet, payloadStart, bytes.length);
}
return packet;
}
private static byte[] encodeLength(final int length) {
byte[] lengthBytes;
if (length <= 125) {
lengthBytes = new byte[1];
lengthBytes[0] = (byte) length;
} else {
byte[] b = toArray(length);
if (length <= 0xFFFF) {
lengthBytes = new byte[3];
lengthBytes[0] = 126;
System.arraycopy(b, 6, lengthBytes, 1, 2);
} else {
lengthBytes = new byte[9];
lengthBytes[0] = 127;
System.arraycopy(b, 0, lengthBytes, 1, 8);
}
}
return lengthBytes;
}
private static byte[] toArray(long length) {
long value = length;
byte[] b = new byte[8];
for (int i = 7; i >= 0 && value > 0; i--) {
b[i] = (byte) (value & 0xFF);
value >>= 8;
}
return b;
}
private static int toInt(byte[] bytes) {
int value = 0;
for (int i = 0; i < bytes.length; i++) {
value <<= 8;
value ^= (int) bytes[i] & 0xFF;
}
return value;
} }
} }

View File

@@ -6,14 +6,17 @@
package org.redkale.net.http; package org.redkale.net.http;
import java.io.*; import java.io.*;
import java.lang.reflect.*;
import java.net.*; import java.net.*;
import java.nio.*; import java.nio.*;
import java.security.*; import java.security.*;
import java.util.*; import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.function.BiConsumer;
import java.util.logging.*; import java.util.logging.*;
import javax.annotation.*; import javax.annotation.*;
import org.redkale.convert.json.JsonConvert; import org.redkale.convert.Convert;
import org.redkale.service.WebSocketNodeService; import org.redkale.service.*;
import org.redkale.util.*; import org.redkale.util.*;
/** /**
@@ -28,10 +31,7 @@ import org.redkale.util.*;
* / \ * / \
* / \ * / \
* / \ * / \
* WebSocketGroup1 WebSocketGroup2 * WebSocket1 WebSocket2
* / \ / \
* / \ / \
* WebSocket1 WebSocket2 WebSocket3 WebSocket4
* *
* </pre></blockquote> * </pre></blockquote>
* *
@@ -46,12 +46,176 @@ public abstract class WebSocketServlet extends HttpServlet implements Resourcabl
public static final String WEBPARAM__LIVEINTERVAL = "liveinterval"; public static final String WEBPARAM__LIVEINTERVAL = "liveinterval";
@Comment("WebScoket服务器给客户端进行ping操作的默认间隔时间, 单位: 秒") @Comment("WebScoket服务器给客户端进行ping操作的默认间隔时间, 单位: 秒")
public static final int DEFAILT_LIVEINTERVAL = 60; public static final int DEFAILT_LIVEINTERVAL = 15;
protected final Logger logger = Logger.getLogger(this.getClass().getSimpleName()); protected final Logger logger = Logger.getLogger(this.getClass().getSimpleName());
private final MessageDigest digest = getMessageDigest(); private final MessageDigest digest = getMessageDigest();
private final BiConsumer<WebSocket, Object> restMessageConsumer = createRestOnMessageConsumer();
protected Type messageTextType; //RestWebSocket时会被修改
protected boolean single = true; //是否单用户单连接
protected int liveinterval = DEFAILT_LIVEINTERVAL;
@Resource(name = "jsonconvert")
protected Convert jsonConvert;
@Resource(name = "$_textconvert")
protected Convert textConvert;
@Resource(name = "$_binaryconvert")
protected Convert binaryConvert;
@Resource(name = "$_sendconvert")
protected Convert sendConvert;
@Resource(name = "$")
protected WebSocketNode node;
protected WebSocketServlet() {
Type msgtype = String.class;
try {
for (Method method : this.getClass().getDeclaredMethods()) {
if (!method.getName().equals("createWebSocket")) continue;
if (method.getParameterCount() > 0) continue;
Type rt = method.getGenericReturnType();
if (rt instanceof ParameterizedType) {
msgtype = ((ParameterizedType) rt).getActualTypeArguments()[1];
}
if (msgtype == Object.class) msgtype = String.class;
break;
}
} catch (Exception e) {
logger.warning(this.getClass().getName() + " not designate text message type on createWebSocket Method");
}
this.messageTextType = msgtype;
}
@Override
final void preInit(HttpContext context, AnyValue conf) {
if (this.textConvert == null) this.textConvert = jsonConvert;
if (this.binaryConvert == null) this.binaryConvert = jsonConvert;
if (this.sendConvert == null) this.sendConvert = jsonConvert;
InetSocketAddress addr = context.getServerAddress();
if (this.node == null) this.node = createWebSocketNode();
if (this.node == null) { //没有部署SNCP即不是分布式
this.node = new WebSocketNodeService();
if (logger.isLoggable(Level.WARNING)) logger.warning("Not found WebSocketNode, create a default value for " + getClass().getName());
}
//存在WebSocketServlet则此WebSocketNode必须是本地模式Service
this.node.localEngine = new WebSocketEngine("WebSocketEngine-" + addr.getHostString() + ":" + addr.getPort() + "-[" + resourceName() + "]", this.single, context, liveinterval, this.node, this.sendConvert, logger);
this.node.init(conf);
this.node.localEngine.init(conf);
}
@Override
final void postDestroy(HttpContext context, AnyValue conf) {
this.node.postDestroy(conf);
super.destroy(context, conf);
this.node.localEngine.destroy(conf);
}
@Override
public String resourceName() {
return this.getClass().getSimpleName().replace("_Dyn", "").toLowerCase().replaceAll("websocket.*$", "").replaceAll("servlet.*$", "");
}
@Override
public final void execute(final HttpRequest request, final HttpResponse response) throws IOException {
final boolean debug = logger.isLoggable(Level.FINEST);
if (!request.isWebSocket()) {
if (debug) logger.finest("WebSocket connect abort, (Not GET Method) or (Connection != Upgrade) or (Upgrade != websocket). request=" + request);
response.finish(true);
return;
}
final String key = request.getHeader("Sec-WebSocket-Key");
if (key == null) {
if (debug) logger.finest("WebSocket connect abort, Not found Sec-WebSocket-Key header. request=" + request);
response.finish(true);
return;
}
final WebSocket webSocket = this.createWebSocket();
webSocket._engine = this.node.localEngine;
webSocket._messageTextType = this.messageTextType;
webSocket._textConvert = textConvert;
webSocket._binaryConvert = binaryConvert;
webSocket._sendConvert = sendConvert;
webSocket._remoteAddress = request.getRemoteAddress();
webSocket._remoteAddr = request.getRemoteAddr();
initRestWebSocket(webSocket);
CompletableFuture<String> sessionFuture = webSocket.onOpen(request);
if (sessionFuture == null) {
if (debug) logger.finest("WebSocket connect abort, Not found sessionid. request=" + request);
response.finish(true);
return;
}
sessionFuture.whenComplete((sessionid, ex) -> {
if (sessionid == null || ex != null) {
if (debug || ex != null) logger.log(ex == null ? Level.FINEST : Level.FINE, "WebSocket connect abort, Not found sessionid or occur error. request=" + request, ex);
response.finish(true);
return;
}
webSocket._sessionid = sessionid;
request.setKeepAlive(true);
byte[] bytes = (key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11").getBytes();
synchronized (digest) {
bytes = digest.digest(bytes);
}
response.setStatus(101);
response.setHeader("Connection", "Upgrade");
response.addHeader("Upgrade", "websocket");
response.addHeader("Sec-WebSocket-Accept", Base64.getEncoder().encodeToString(bytes));
response.sendBody((ByteBuffer) null, null, new AsyncHandler<Integer, Void>() {
@Override
public void completed(Integer result, Void attachment) {
HttpContext context = response.getContext();
CompletableFuture<Serializable> userFuture = webSocket.createUserid();
if (userFuture == null) {
if (debug) logger.finest("WebSocket connect abort, Create userid abort. request = " + request);
response.finish(true);
return;
}
userFuture.whenComplete((userid, ex2) -> {
if (userid == null || ex2 != null) {
if (debug || ex2 != null) logger.log(ex2 == null ? Level.FINEST : Level.FINE, "WebSocket connect abort, Create userid abort. request = " + request, ex2);
response.finish(true);
return;
}
webSocket._userid = userid;
WebSocketServlet.this.node.localEngine.add(webSocket);
WebSocketRunner runner = new WebSocketRunner(context, webSocket, restMessageConsumer, response.removeChannel());
webSocket._runner = runner;
context.runAsync(runner);
response.finish(true);
});
}
@Override
public void failed(Throwable exc, Void attachment) {
logger.log(Level.FINEST, "WebSocket connect abort, Response send abort. request = " + request, exc);
response.finish(true);
}
});
});
}
protected abstract <G extends Serializable, T> WebSocket<G, T> createWebSocket();
protected WebSocketNode createWebSocketNode() {
return null;
}
protected void initRestWebSocket(WebSocket websocket) { //设置WebSocket中的@Resource资源
}
protected BiConsumer<WebSocket, Object> createRestOnMessageConsumer() {
return null;
}
private static MessageDigest getMessageDigest() { private static MessageDigest getMessageDigest() {
try { try {
return MessageDigest.getInstance("SHA-1"); return MessageDigest.getInstance("SHA-1");
@@ -60,107 +224,4 @@ public abstract class WebSocketServlet extends HttpServlet implements Resourcabl
} }
} }
@Comment("是否用于二进制流传输")
protected final boolean wsbinary = getClass().getAnnotation(WebSocketBinary.class) != null;
@Resource
protected JsonConvert jsonConvert;
@Resource(name = "$")
protected WebSocketNode node;
protected WebSocketEngine engine;
public final void preInit(HttpContext context, AnyValue conf) {
InetSocketAddress addr = context.getServerAddress();
this.engine = new WebSocketEngine(addr.getHostString() + ":" + addr.getPort() + "-[" + resourceName() + "]", this.node, logger);
if (this.node == null) this.node = createWebSocketNode();
if (this.node == null) {
this.node = new WebSocketNodeService();
if (logger.isLoggable(Level.WARNING)) logger.warning("Not found WebSocketNode, create a default value for " + getClass().getName());
}
this.node.putWebSocketEngine(engine);
this.node.init(conf);
this.engine.init(conf);
}
public final void postDestroy(HttpContext context, AnyValue conf) {
this.node.destroy(conf);
super.destroy(context, conf);
engine.close();
}
@Override
public String resourceName() {
return this.getClass().getSimpleName().replace("Servlet", "").replace("WebSocket", "").toLowerCase();
}
@Override
public final void execute(final HttpRequest request, final HttpResponse response) throws IOException {
final boolean debug = logger.isLoggable(Level.FINEST);
if (!"GET".equalsIgnoreCase(request.getMethod())
|| !request.getConnection().contains("Upgrade")
|| !"websocket".equalsIgnoreCase(request.getHeader("Upgrade"))) {
if (debug) logger.finest("WebSocket connect abort, (Not GET Method) or (Connection != Upgrade) or (Upgrade != websocket). request=" + request);
response.finish(true);
return;
}
String key = request.getHeader("Sec-WebSocket-Key");
if (key == null) {
if (debug) logger.finest("WebSocket connect abort, Not found Sec-WebSocket-Key header. request=" + request);
response.finish(true);
return;
}
final WebSocket webSocket = this.createWebSocket();
webSocket._engine = engine;
webSocket._jsonConvert = jsonConvert;
webSocket._remoteAddress = request.getRemoteAddress();
webSocket._remoteAddr = request.getRemoteAddr();
Serializable sessionid = webSocket.onOpen(request);
if (sessionid == null) {
if (debug) logger.finest("WebSocket connect abort, Not found sessionid. request=" + request);
response.finish(true);
return;
}
webSocket._sessionid = sessionid;
request.setKeepAlive(true);
byte[] bytes = (key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11").getBytes();
synchronized (digest) {
bytes = digest.digest(bytes);
}
key = Base64.getEncoder().encodeToString(bytes);
response.setStatus(101);
response.setHeader("Connection", "Upgrade");
response.addHeader("Upgrade", "websocket");
response.addHeader("Sec-WebSocket-Accept", key);
response.sendBody((ByteBuffer) null, null, new AsyncHandler<Integer, Void>() {
@Override
public void completed(Integer result, Void attachment) {
HttpContext context = response.getContext();
Serializable groupid = webSocket.createGroupid();
if (groupid == null) {
if (debug) logger.finest("WebSocket connect abort, Create groupid abort. request = " + request);
response.finish(true);
return;
}
webSocket._groupid = groupid;
engine.add(webSocket);
context.runAsync(new WebSocketRunner(context, webSocket, response.removeChannel(), wsbinary));
response.finish(true);
}
@Override
public void failed(Throwable exc, Void attachment) {
logger.log(Level.FINEST, "WebSocket connect abort, Response send abort. request = " + request, exc);
response.finish(true);
}
});
}
protected WebSocketNode createWebSocketNode() {
return null;
}
protected abstract WebSocket createWebSocket();
} }

View File

@@ -10,16 +10,12 @@ import java.lang.reflect.*;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.security.*; import java.security.*;
import java.util.*; import java.util.*;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import javax.annotation.Resource; import javax.annotation.Resource;
import static jdk.internal.org.objectweb.asm.ClassWriter.COMPUTE_FRAMES; import static jdk.internal.org.objectweb.asm.ClassWriter.COMPUTE_FRAMES;
import jdk.internal.org.objectweb.asm.*; import jdk.internal.org.objectweb.asm.*;
import static jdk.internal.org.objectweb.asm.Opcodes.*; import static jdk.internal.org.objectweb.asm.Opcodes.*;
import jdk.internal.org.objectweb.asm.Type; import jdk.internal.org.objectweb.asm.Type;
import org.redkale.convert.bson.BsonConvert; import org.redkale.net.TransportFactory;
import org.redkale.convert.json.JsonConvert;
import org.redkale.net.Transport;
import org.redkale.net.sncp.SncpClient.SncpAction; import org.redkale.net.sncp.SncpClient.SncpAction;
import org.redkale.service.*; import org.redkale.service.*;
import org.redkale.util.*; import org.redkale.util.*;
@@ -93,6 +89,10 @@ public abstract class Sncp {
return dyn != null && dyn.remote(); return dyn != null && dyn.remote();
} }
public static boolean isSncpDyn(Service service) {
return service.getClass().getAnnotation(SncpDyn.class) != null;
}
public static String getResourceName(Service service) { public static String getResourceName(Service service) {
if (service == null) return null; if (service == null) return null;
Resource res = service.getClass().getAnnotation(Resource.class); Resource res = service.getClass().getAnnotation(Resource.class);
@@ -100,42 +100,14 @@ public abstract class Sncp {
} }
public static Class getServiceType(Service service) { public static Class getServiceType(Service service) {
if (service == null) return null; ResourceType rt = service.getClass().getAnnotation(ResourceType.class);
try { return rt == null ? service.getClass() : rt.value();
Field ts = service.getClass().getDeclaredField(FIELDPREFIX + "_service_type");
ts.setAccessible(true);
return (Class) ts.get(service);
} catch (Exception e) {
throw new RuntimeException(service + " not found " + FIELDPREFIX + "_service_type");
}
} }
public static Class[] getResourceTypes(Service service) { public static Class getResourceType(Service service) {
if (service == null) return null; if (service == null) return null;
ResourceType types = service.getClass().getAnnotation(ResourceType.class); ResourceType type = service.getClass().getAnnotation(ResourceType.class);
return types == null ? new Class[]{getServiceType(service)} : types.value(); return type == null ? getServiceType(service) : type.value();
}
public static String getSncpGroup(Service service) {
if (service == null) return null;
try {
Field ts = service.getClass().getDeclaredField(FIELDPREFIX + "_sncpGroup");
ts.setAccessible(true);
return (String) ts.get(service);
} catch (Exception e) {
throw new RuntimeException(service + " not found " + FIELDPREFIX + "_sncpGroup");
}
}
public static Set<String> getGroups(Service service) {
if (service == null) return null;
try {
Field ts = service.getClass().getDeclaredField(FIELDPREFIX + "_groups");
ts.setAccessible(true);
return (Set) ts.get(service);
} catch (Exception e) {
throw new RuntimeException(service + " not found " + FIELDPREFIX + "_groups");
}
} }
public static AnyValue getConf(Service service) { public static AnyValue getConf(Service service) {
@@ -160,28 +132,6 @@ public abstract class Sncp {
} }
} }
public static Transport getSameGroupTransport(Service service) {
if (service == null) return null;
try {
Field ts = service.getClass().getDeclaredField(FIELDPREFIX + "_sameGroupTransport");
ts.setAccessible(true);
return (Transport) ts.get(service);
} catch (Exception e) {
throw new RuntimeException(service + " not found " + FIELDPREFIX + "_sameGroupTransport");
}
}
public static Transport[] getDiffGroupTransports(Service service) {
if (service == null) return null;
try {
Field ts = service.getClass().getDeclaredField(FIELDPREFIX + "_diffGroupTransports");
ts.setAccessible(true);
return (Transport[]) ts.get(service);
} catch (Exception e) {
throw new RuntimeException(service + " not found " + FIELDPREFIX + "_diffGroupTransports");
}
}
static void checkAsyncModifier(Class param, Method method) { static void checkAsyncModifier(Class param, Method method) {
if (param == AsyncHandler.class) return; if (param == AsyncHandler.class) return;
if (Modifier.isFinal(param.getModifiers())) { if (Modifier.isFinal(param.getModifiers())) {
@@ -224,20 +174,10 @@ public abstract class Sncp {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
sb.append(isRemote(service) ? "RemoteService" : "LocalService "); sb.append(isRemote(service) ? "RemoteService" : "LocalService ");
int len; int len;
Class[] types = getResourceTypes(service); Class type = getResourceType(service);
String name = getResourceName(service); String name = getResourceName(service);
if (types.length == 1) { sb.append("(type= ").append(type.getName());
sb.append("(type= ").append(types[0].getName()); len = maxClassNameLength - type.getName().length();
len = maxClassNameLength - types[0].getName().length();
} else {
StringBuilder s = new StringBuilder();
s.append('[');
s.append(Arrays.asList(types).stream().map((Class t) -> t.getName()).collect(Collectors.joining(",")));
s.append(']');
sb.append("(types=").append(s);
len = maxClassNameLength - s.length();
}
for (int i = 0; i < len; i++) { for (int i = 0; i < len; i++) {
sb.append(' '); sb.append(' ');
} }
@@ -272,31 +212,13 @@ public abstract class Sncp {
* <blockquote><pre> * <blockquote><pre>
* &#64;Resource(name = "") * &#64;Resource(name = "")
* &#64;SncpDyn(remote = false) * &#64;SncpDyn(remote = false)
* &#64;ResourceType({TestService.class}) * &#64;ResourceType(TestService.class)
* public final class _DynLocalTestService extends TestService{ * public final class _DynLocalTestService extends TestService{
* *
* private static final Class _redkale_service_type = TestService.class;
*
* &#64;Resource
* private BsonConvert _redkale_bsonConvert;
*
* &#64;Resource
* private JsonConvert _redkale_jsonConvert;
*
* private AnyValue _redkale_conf; * private AnyValue _redkale_conf;
* *
* private String _redkale_sncpGroup; //自身的组节点名 可能为null
*
* private Set&lt;String&gt; groups; //所有的组节点,包含自身
*
* private Transport _redkale_sameGroupTransport;
*
* private Transport[] _redkale_diffGroupTransports;
*
* private SncpClient _redkale_client; * private SncpClient _redkale_client;
* *
* private String _redkale_selfstring;
*
* &#64;Override * &#64;Override
* public String toString() { * public String toString() {
* return _redkale_selfstring == null ? super.toString() : _redkale_selfstring; * return _redkale_selfstring == null ? super.toString() : _redkale_selfstring;
@@ -311,8 +233,8 @@ public abstract class Sncp {
* public void _redkale_createSomeThing(boolean selfrunnable, boolean samerunnable, boolean diffrunnable, TestBean bean){ * public void _redkale_createSomeThing(boolean selfrunnable, boolean samerunnable, boolean diffrunnable, TestBean bean){
* if(selfrunnable) super.createSomeThing(bean); * if(selfrunnable) super.createSomeThing(bean);
* if (_redkale_client== null) return; * if (_redkale_client== null) return;
* if (samerunnable) _redkale_client.remoteSameGroup(_redkale_bsonConvert, _redkale_jsonConvert, _redkale_sameGroupTransport, 0, true, false, false, bean); * if (samerunnable) _redkale_client.remoteSameGroup(0, true, false, false, bean);
* if (diffrunnable) _redkale_client.remoteDiffGroup(_redkale_bsonConvert, _redkale_jsonConvert, _redkale_diffGroupTransports, 0, true, true, false, bean); * if (diffrunnable) _redkale_client.remoteDiffGroup(0, true, true, false, bean);
* } * }
* *
* &#64;Override * &#64;Override
@@ -324,8 +246,8 @@ public abstract class Sncp {
* public String _redkale_updateSomeThing(boolean selfrunnable, boolean samerunnable, boolean diffrunnable, String id){ * public String _redkale_updateSomeThing(boolean selfrunnable, boolean samerunnable, boolean diffrunnable, String id){
* String rs = super.updateSomeThing(id); * String rs = super.updateSomeThing(id);
* if (_redkale_client== null) return rs; * if (_redkale_client== null) return rs;
* if (samerunnable) _redkale_client.remoteSameGroup(_redkale_bsonConvert, _redkale_jsonConvert, _redkale_sameGroupTransport, 1, true, false, false, id); * if (samerunnable) _redkale_client.remoteSameGroup(1, true, false, false, id);
* if (diffrunnable) _redkale_client.remoteDiffGroup(_redkale_bsonConvert, _redkale_jsonConvert, _redkale_diffGroupTransports, 1, true, true, false, id); * if (diffrunnable) _redkale_client.remoteDiffGroup(1, true, true, false, id);
* return rs; * return rs;
* } * }
* } * }
@@ -334,13 +256,14 @@ public abstract class Sncp {
* 创建Service的本地模式Class * 创建Service的本地模式Class
* *
* @param <T> Service子类 * @param <T> Service子类
* @param classLoader ClassLoader
* @param name 资源名 * @param name 资源名
* @param serviceImplClass Service类 * @param serviceImplClass Service类
* *
* @return Service实例 * @return Service实例
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
protected static <T extends Service> Class<? extends T> createLocalServiceClass(final String name, final Class<T> serviceImplClass) { protected static <T extends Service> Class<? extends T> createLocalServiceClass(ClassLoader classLoader, final String name, final Class<T> serviceImplClass) {
if (serviceImplClass == null) return null; if (serviceImplClass == null) return null;
if (!Service.class.isAssignableFrom(serviceImplClass)) return serviceImplClass; if (!Service.class.isAssignableFrom(serviceImplClass)) return serviceImplClass;
int mod = serviceImplClass.getModifiers(); int mod = serviceImplClass.getModifiers();
@@ -350,14 +273,9 @@ public abstract class Sncp {
final String supDynName = serviceImplClass.getName().replace('.', '/'); final String supDynName = serviceImplClass.getName().replace('.', '/');
final String clientName = SncpClient.class.getName().replace('.', '/'); final String clientName = SncpClient.class.getName().replace('.', '/');
final String clientDesc = Type.getDescriptor(SncpClient.class); final String clientDesc = Type.getDescriptor(SncpClient.class);
final String bsonConvertDesc = Type.getDescriptor(BsonConvert.class);
final String jsonConvertDesc = Type.getDescriptor(JsonConvert.class);
final String stringDesc = Type.getDescriptor(String.class);
final String anyValueDesc = Type.getDescriptor(AnyValue.class); final String anyValueDesc = Type.getDescriptor(AnyValue.class);
final String sncpDynDesc = Type.getDescriptor(SncpDyn.class); final String sncpDynDesc = Type.getDescriptor(SncpDyn.class);
final String transportDesc = Type.getDescriptor(Transport.class); ClassLoader loader = classLoader == null ? Thread.currentThread().getContextClassLoader() : classLoader;
final String transportsDesc = Type.getDescriptor(Transport[].class);
ClassLoader loader = Sncp.class.getClassLoader();
String newDynName = supDynName.substring(0, supDynName.lastIndexOf('/') + 1) + LOCALPREFIX + serviceImplClass.getSimpleName(); String newDynName = supDynName.substring(0, supDynName.lastIndexOf('/') + 1) + LOCALPREFIX + serviceImplClass.getSimpleName();
if (!name.isEmpty()) { if (!name.isEmpty()) {
boolean normal = true; boolean normal = true;
@@ -368,7 +286,7 @@ public abstract class Sncp {
newDynName += "_" + (normal ? name : hash(name)); newDynName += "_" + (normal ? name : hash(name));
} }
try { try {
return (Class<T>) Class.forName(newDynName.replace('/', '.')); return (Class<T>) loader.loadClass(newDynName.replace('/', '.'));
} catch (Throwable ex) { } catch (Throwable ex) {
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
@@ -396,73 +314,18 @@ public abstract class Sncp {
} }
{ {
av0 = cw.visitAnnotation(Type.getDescriptor(ResourceType.class), true); av0 = cw.visitAnnotation(Type.getDescriptor(ResourceType.class), true);
{ ResourceType rty = serviceImplClass.getAnnotation(ResourceType.class);
AnnotationVisitor av1 = av0.visitArray("value"); av0.visit("value", Type.getType(Type.getDescriptor(rty == null ? serviceImplClass : rty.value())));
ResourceType rty = serviceImplClass.getAnnotation(ResourceType.class);
if (rty == null) {
av1.visit(null, Type.getType(Type.getDescriptor(serviceImplClass)));
} else {
for (Class cl : rty.value()) {
av1.visit(null, Type.getType(Type.getDescriptor(cl)));
}
}
av1.visitEnd();
}
av0.visitEnd(); av0.visitEnd();
} }
{
fv = cw.visitField(ACC_PRIVATE + ACC_FINAL + ACC_STATIC, FIELDPREFIX + "_service_type", "Ljava/lang/Class;", null, null);
fv.visitEnd();
}
{
fv = cw.visitField(ACC_PRIVATE, FIELDPREFIX + "_bsonConvert", bsonConvertDesc, null, null);
av0 = fv.visitAnnotation("Ljavax/annotation/Resource;", true);
av0.visitEnd();
fv.visitEnd();
}
{
fv = cw.visitField(ACC_PRIVATE, FIELDPREFIX + "_jsonConvert", jsonConvertDesc, null, null);
av0 = fv.visitAnnotation("Ljavax/annotation/Resource;", true);
av0.visitEnd();
fv.visitEnd();
}
{ {
fv = cw.visitField(ACC_PRIVATE, FIELDPREFIX + "_conf", anyValueDesc, null, null); fv = cw.visitField(ACC_PRIVATE, FIELDPREFIX + "_conf", anyValueDesc, null, null);
fv.visitEnd(); fv.visitEnd();
} }
{
fv = cw.visitField(ACC_PRIVATE, FIELDPREFIX + "_sncpGroup", stringDesc, null, null);
fv.visitEnd();
}
{
fv = cw.visitField(ACC_PRIVATE, FIELDPREFIX + "_groups", "Ljava/util/Set;", "Ljava/util/Set<Ljava/lang/String;>;", null);
fv.visitEnd();
}
{
fv = cw.visitField(ACC_PRIVATE, FIELDPREFIX + "_sameGroupTransport", transportDesc, null, null);
fv.visitEnd();
}
{
fv = cw.visitField(ACC_PRIVATE, FIELDPREFIX + "_diffGroupTransports", transportsDesc, null, null);
fv.visitEnd();
}
{ {
fv = cw.visitField(ACC_PRIVATE, FIELDPREFIX + "_client", clientDesc, null, null); fv = cw.visitField(ACC_PRIVATE, FIELDPREFIX + "_client", clientDesc, null, null);
fv.visitEnd(); fv.visitEnd();
} }
{
fv = cw.visitField(ACC_PRIVATE, FIELDPREFIX + "_selfstring", "Ljava/lang/String;", null, null);
fv.visitEnd();
}
{//静态构造函数
mv = new AsmMethodVisitor(cw.visitMethod(ACC_STATIC, "<clinit>", "()V", null, null));
mv.visitLdcInsn(Type.getType(Type.getDescriptor(serviceImplClass)));
mv.visitFieldInsn(PUTSTATIC, newDynName, FIELDPREFIX + "_service_type", "Ljava/lang/Class;");
mv.visitInsn(RETURN);
mv.visitMaxs(1, 0);
mv.visitEnd();
}
{ //构造函数 { //构造函数
mv = new AsmMethodVisitor(cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null)); mv = new AsmMethodVisitor(cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null));
//mv.setDebug(true); //mv.setDebug(true);
@@ -475,16 +338,18 @@ public abstract class Sncp {
{ // toString() { // toString()
mv = new AsmMethodVisitor(cw.visitMethod(ACC_PUBLIC, "toString", "()Ljava/lang/String;", null, null)); mv = new AsmMethodVisitor(cw.visitMethod(ACC_PUBLIC, "toString", "()Ljava/lang/String;", null, null));
mv.visitVarInsn(ALOAD, 0); mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, newDynName, FIELDPREFIX + "_selfstring", "Ljava/lang/String;"); mv.visitFieldInsn(GETFIELD, newDynName, FIELDPREFIX + "_client", clientDesc);
Label l1 = new Label(); Label l1 = new Label();
mv.visitJumpInsn(IFNONNULL, l1); mv.visitJumpInsn(IFNONNULL, l1);
mv.visitVarInsn(ALOAD, 0); mv.visitVarInsn(ALOAD, 0);
mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "toString", "()Ljava/lang/String;", false); mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Object", "getClass", "()Ljava/lang/Class;", false);
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Class", "getName", "()Ljava/lang/String;", false);
Label l2 = new Label(); Label l2 = new Label();
mv.visitJumpInsn(GOTO, l2); mv.visitJumpInsn(GOTO, l2);
mv.visitLabel(l1); mv.visitLabel(l1);
mv.visitVarInsn(ALOAD, 0); mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, newDynName, FIELDPREFIX + "_selfstring", "Ljava/lang/String;"); mv.visitFieldInsn(GETFIELD, newDynName, FIELDPREFIX + "_client", clientDesc);
mv.visitMethodInsn(INVOKEVIRTUAL, clientName, "toSimpleString", "()Ljava/lang/String;", false);
mv.visitLabel(l2); mv.visitLabel(l2);
mv.visitInsn(ARETURN); mv.visitInsn(ARETURN);
mv.visitMaxs(1, 1); mv.visitMaxs(1, 1);
@@ -660,14 +525,7 @@ public abstract class Sncp {
mv.visitVarInsn(ALOAD, 0);//调用 _client mv.visitVarInsn(ALOAD, 0);//调用 _client
mv.visitFieldInsn(GETFIELD, newDynName, FIELDPREFIX + "_client", clientDesc); mv.visitFieldInsn(GETFIELD, newDynName, FIELDPREFIX + "_client", clientDesc);
mv.visitVarInsn(ALOAD, 0); //传递 _bsonConvert final int preparams = 3; //调用selfrunnable之前的参数个数; _client
mv.visitFieldInsn(GETFIELD, newDynName, FIELDPREFIX + "_bsonConvert", bsonConvertDesc);
mv.visitVarInsn(ALOAD, 0); //传递 _jsonConvert
mv.visitFieldInsn(GETFIELD, newDynName, FIELDPREFIX + "_jsonConvert", jsonConvertDesc);
mv.visitVarInsn(ALOAD, 0); //传递 _sameGroupTransport
mv.visitFieldInsn(GETFIELD, newDynName, FIELDPREFIX + "_sameGroupTransport", transportDesc);
final int preparams = 3; //调用selfrunnable之前的参数个数; _client/_bsonConvert/_jsonConvert/_sameGroupTransport
if (index <= 5) { //第几个 SncpAction if (index <= 5) { //第几个 SncpAction
mv.visitInsn(ICONST_0 + index); mv.visitInsn(ICONST_0 + index);
@@ -727,7 +585,7 @@ public abstract class Sncp {
} }
mv.visitInsn(AASTORE); mv.visitInsn(AASTORE);
} }
mv.visitMethodInsn(INVOKEVIRTUAL, clientName, mrun.async() ? "asyncRemoteSameGroup" : "remoteSameGroup", "(" + bsonConvertDesc + jsonConvertDesc + transportDesc + "I[Ljava/lang/Object;)V", false); mv.visitMethodInsn(INVOKEVIRTUAL, clientName, mrun.async() ? "asyncRemoteSameGroup" : "remoteSameGroup", "(I[Ljava/lang/Object;)V", false);
mv.visitLabel(sameLabel); mv.visitLabel(sameLabel);
//---------------------------- 调用diffrun --------------------------------- //---------------------------- 调用diffrun ---------------------------------
mv.visitVarInsn(ILOAD, 3); //读取 diffrunnable mv.visitVarInsn(ILOAD, 3); //读取 diffrunnable
@@ -736,12 +594,6 @@ public abstract class Sncp {
mv.visitVarInsn(ALOAD, 0); mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, newDynName, FIELDPREFIX + "_client", clientDesc); mv.visitFieldInsn(GETFIELD, newDynName, FIELDPREFIX + "_client", clientDesc);
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, newDynName, FIELDPREFIX + "_bsonConvert", bsonConvertDesc);
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, newDynName, FIELDPREFIX + "_jsonConvert", jsonConvertDesc);
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, newDynName, FIELDPREFIX + "_diffGroupTransports", transportsDesc);
if (index <= 5) { //第几个 SncpAction if (index <= 5) { //第几个 SncpAction
mv.visitInsn(ICONST_0 + index); mv.visitInsn(ICONST_0 + index);
@@ -801,7 +653,7 @@ public abstract class Sncp {
} }
mv.visitInsn(AASTORE); mv.visitInsn(AASTORE);
} }
mv.visitMethodInsn(INVOKEVIRTUAL, clientName, mrun.async() ? "asyncRemoteDiffGroup" : "remoteDiffGroup", "(" + bsonConvertDesc + jsonConvertDesc + transportsDesc + "I[Ljava/lang/Object;)V", false); mv.visitMethodInsn(INVOKEVIRTUAL, clientName, mrun.async() ? "asyncRemoteDiffGroup" : "remoteDiffGroup", "(I[Ljava/lang/Object;)V", false);
mv.visitLabel(diffLabel); mv.visitLabel(diffLabel);
if (returnType == void.class) { if (returnType == void.class) {
@@ -885,46 +737,42 @@ public abstract class Sncp {
} }
} }
public static <T extends Service> T createSimpleLocalService(final String name, final Class<T> serviceImplClass, final InetSocketAddress clientAddress, final Transport sameGroupTransport) { public static <T extends Service> T createSimpleLocalService(final Class<T> serviceImplClass,
return createLocalService(name, null, ResourceFactory.root(), serviceImplClass, clientAddress, null, new HashSet<>(), null, sameGroupTransport, null); final TransportFactory transportFactory, final InetSocketAddress clientSncpAddress, final String... groups) {
return createLocalService(null, "", serviceImplClass, ResourceFactory.root(), transportFactory, clientSncpAddress, Utility.ofSet(groups), null);
} }
/** /**
* *
* 创建本地模式Service实例 * 创建本地模式Service实例
* *
* @param <T> Service泛型 * @param <T> Service泛型
* @param name 资源名 * @param classLoader ClassLoader
* @param executor 线程池 * @param name 资源名
* @param resourceFactory 资源容器 * @param serviceImplClass Service类
* @param serviceImplClass Service类 * @param resourceFactory ResourceFactory
* @param clientAddress 本地IP地址 * @param transportFactory TransportFactory
* @param sncpGroup 自身的组节点名 可能为null * @param clientSncpAddress 本地IP地址
* @param groups 所有的组节点,包含自身 * @param groups 所有的组节点,包含自身
* @param conf 启动配置项 * @param conf 启动配置项
* @param sameGroupTransport 同组的通信组件
* @param diffGroupTransports 异组的通信组件列表
* *
* @return Service的本地模式实例 * @return Service的本地模式实例
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public static <T extends Service> T createLocalService( public static <T extends Service> T createLocalService(
final ClassLoader classLoader,
final String name, final String name,
final Consumer<Runnable> executor,
final ResourceFactory resourceFactory,
final Class<T> serviceImplClass, final Class<T> serviceImplClass,
final InetSocketAddress clientAddress, final ResourceFactory resourceFactory,
final String sncpGroup, final TransportFactory transportFactory,
final InetSocketAddress clientSncpAddress,
final Set<String> groups, final Set<String> groups,
final AnyValue conf, final AnyValue conf) {
final Transport sameGroupTransport,
final Collection<Transport> diffGroupTransports) {
try { try {
final Class newClazz = createLocalServiceClass(name, serviceImplClass); final Class newClazz = createLocalServiceClass(classLoader, name, serviceImplClass);
T rs = (T) newClazz.newInstance(); T rs = (T) newClazz.newInstance();
//-------------------------------------- //--------------------------------------
Service remoteService = null; Service remoteService = null;
Transport remoteTransport = null;
{ {
Class loop = newClazz; Class loop = newClazz;
do { do {
@@ -934,25 +782,8 @@ public abstract class Sncp {
if (field.getAnnotation(RpcRemote.class) == null) continue; if (field.getAnnotation(RpcRemote.class) == null) continue;
if (!field.getType().isAssignableFrom(newClazz)) continue; if (!field.getType().isAssignableFrom(newClazz)) continue;
field.setAccessible(true); field.setAccessible(true);
if (remoteTransport == null) { if (remoteService == null && clientSncpAddress != null) {
List<Transport> list = new ArrayList<>(); remoteService = createRemoteService(classLoader, name, serviceImplClass, transportFactory, clientSncpAddress, groups, conf);
if (sameGroupTransport != null) list.add(sameGroupTransport);
if (diffGroupTransports != null) list.addAll(diffGroupTransports);
if (!list.isEmpty()) {
Transport tmp = new Transport(list);
synchronized (resourceFactory) {
Transport old = resourceFactory.find(tmp.getName(), Transport.class);
if (old != null) {
remoteTransport = old;
} else {
remoteTransport = tmp;
resourceFactory.register(tmp.getName(), tmp);
}
}
}
}
if (remoteService == null && remoteTransport != null) {
remoteService = createRemoteService(name, executor, serviceImplClass, clientAddress, sncpGroup, groups, conf, remoteTransport);
} }
if (remoteService != null) field.set(rs, remoteService); if (remoteService != null) field.set(rs, remoteService);
} }
@@ -963,137 +794,73 @@ public abstract class Sncp {
try { try {
Field e = newClazz.getDeclaredField(FIELDPREFIX + "_client"); Field e = newClazz.getDeclaredField(FIELDPREFIX + "_client");
e.setAccessible(true); e.setAccessible(true);
client = new SncpClient(name, serviceImplClass, rs, executor, false, newClazz, clientAddress); client = new SncpClient(name, serviceImplClass, rs, transportFactory, false, newClazz, clientSncpAddress);
Set<String> diffGroups = groups == null ? new HashSet<>() : new HashSet<>(groups);
String sameGroup = transportFactory.findGroupName(clientSncpAddress);
if (sameGroup != null) diffGroups.remove(sameGroup);
client.setSameGroup(sameGroup);
client.setDiffGroups(diffGroups);
client.setSameGroupTransport(transportFactory.loadSameGroupTransport(clientSncpAddress));
client.setDiffGroupTransports(transportFactory.loadDiffGroupTransports(clientSncpAddress, diffGroups));
e.set(rs, client); e.set(rs, client);
transportFactory.addSncpService(rs);
} catch (NoSuchFieldException ne) { } catch (NoSuchFieldException ne) {
ne.printStackTrace();
} }
} }
{
StringBuilder sb = new StringBuilder();
sb.append(newClazz.getName()).append("{name = '").append(name).append("'");
if (client != null) {
sb.append(", serviceid = ").append(client.getServiceid());
sb.append(", serviceversion = ").append(client.getServiceversion());
sb.append(", action.size = ").append(client.getActionCount());
// List<String> groups = new ArrayList<>();
// if (sameGroupTransport != null) groups.add(sameGroupTransport.getName());
// if (diffGroupTransports != null) {
// for (Transport t : diffGroupTransports) {
// groups.add(t.getName());
// }
// }
sb.append(", address = ").append(clientAddress).append(", groups = ").append(groups);
sb.append(", sameaddrs = ").append(sameGroupTransport == null ? null : Arrays.asList(sameGroupTransport.getRemoteAddresses()));
List<InetSocketAddress> addrs = new ArrayList<>();
if (diffGroupTransports != null) {
for (Transport t : diffGroupTransports) {
addrs.addAll(Arrays.asList(t.getRemoteAddresses()));
}
}
sb.append(", diffaddrs = ").append(addrs);
} else {
sb.append(", ").append(RpcMultiRun.class.getSimpleName().toLowerCase()).append(" = false");
}
sb.append("}");
Field s = newClazz.getDeclaredField(FIELDPREFIX + "_selfstring");
s.setAccessible(true);
s.set(rs, sb.toString());
}
if (client == null) return rs; if (client == null) return rs;
{
Field c = newClazz.getDeclaredField(FIELDPREFIX + "_sncpGroup");
c.setAccessible(true);
c.set(rs, sncpGroup);
}
{
Field c = newClazz.getDeclaredField(FIELDPREFIX + "_groups");
c.setAccessible(true);
c.set(rs, groups);
}
{ {
Field c = newClazz.getDeclaredField(FIELDPREFIX + "_conf"); Field c = newClazz.getDeclaredField(FIELDPREFIX + "_conf");
c.setAccessible(true); c.setAccessible(true);
c.set(rs, conf); c.set(rs, conf);
} }
{
Field c = newClazz.getDeclaredField(FIELDPREFIX + "_sameGroupTransport");
c.setAccessible(true);
c.set(rs, sameGroupTransport);
}
if (diffGroupTransports != null) {
Field t = newClazz.getDeclaredField(FIELDPREFIX + "_diffGroupTransports");
t.setAccessible(true);
t.set(rs, diffGroupTransports.toArray(new Transport[diffGroupTransports.size()]));
}
return rs; return rs;
} catch (RuntimeException rex) { } catch (RuntimeException rex) {
throw rex; throw rex;
} catch (Exception ex) { } catch (Exception ex) {
throw new RuntimeException(ex); throw new RuntimeException(ex);
} }
} }
public static <T extends Service> T createSimpleRemoteService(final String name, final Class<T> serviceTypeOrImplClass, final InetSocketAddress clientAddress, final Transport transport) { public static <T extends Service> T createSimpleRemoteService(final Class<T> serviceImplClass,
return createRemoteService(name, null, serviceTypeOrImplClass, clientAddress, (String) null, new HashSet<>(), (AnyValue) null, transport); final TransportFactory transportFactory, final InetSocketAddress clientSncpAddress, final String... groups) {
return createRemoteService(null, "", serviceImplClass, transportFactory, clientSncpAddress, Utility.ofSet(groups), null);
} }
/** /**
* <blockquote><pre> * <blockquote><pre>
* &#64;Resource(name = "") * &#64;Resource(name = "")
* &#64;SncpDyn(remote = true) * &#64;SncpDyn(remote = true)
* &#64;ResourceType({TestService.class}) * &#64;ResourceType(TestService.class)
* public final class _DynRemoteTestService extends TestService{ * public final class _DynRemoteTestService extends TestService{
* *
* private static final Class _redkale_service_type = TestService.class;
*
* &#64;Resource
* private BsonConvert _redkale_bsonConvert;
*
* &#64;Resource
* private JsonConvert _redkale_jsonConvert;
*
* private String _redkale_sncpGroup; //自身的组节点名 可能为null
*
* private Set&lt;String&gt; groups; //所有的组节点,包含自身
*
* private AnyValue _redkale_conf; * private AnyValue _redkale_conf;
* *
* private Transport _redkale_transport;
*
* private SncpClient _redkale_client; * private SncpClient _redkale_client;
* *
* private String _redkale_selfstring;
*
* &#64;Override
* public String toString() {
* return _redkale_selfstring == null ? super.toString() : _redkale_selfstring;
* }
*
* &#64;SncpDyn(remote = false, index = 0) * &#64;SncpDyn(remote = false, index = 0)
* public void _redkale_createSomeThing(boolean selfrunnable, boolean samerunnable, boolean diffrunnable, TestBean bean){ * public void _redkale_createSomeThing(boolean selfrunnable, boolean samerunnable, boolean diffrunnable, TestBean bean){
* _redkale_client.remote(_redkale_bsonConvert, _redkale_jsonConvert, _redkale_transport, 0, selfrunnable, samerunnable, diffrunnable, bean); * _redkale_client.remote(0, selfrunnable, samerunnable, diffrunnable, bean);
* } * }
* *
* &#64;SncpDyn(remote = false, index = 1) * &#64;SncpDyn(remote = false, index = 1)
* public String _redkale_updateSomeThing(boolean selfrunnable, boolean samerunnable, boolean diffrunnable, String id){ * public String _redkale_updateSomeThing(boolean selfrunnable, boolean samerunnable, boolean diffrunnable, String id){
* return _redkale_client.remote(_redkale_bsonConvert, _redkale_jsonConvert, _redkale_transport, 1, selfrunnable, samerunnable, diffrunnable, id); * return _redkale_client.remote(1, selfrunnable, samerunnable, diffrunnable, id);
* } * }
* *
* &#64;Override * &#64;Override
* public void createSomeThing(TestBean bean){ * public void createSomeThing(TestBean bean){
* _redkale_client.remote(_redkale_bsonConvert, _redkale_jsonConvert, _redkale_transport, 2, bean); * _redkale_client.remote(2, bean);
* } * }
* *
* &#64;Override * &#64;Override
* public String findSomeThing(){ * public String findSomeThing(){
* return _redkale_client.remote(_redkale_bsonConvert, _redkale_jsonConvert, _redkale_transport, 3); * return _redkale_client.remote(3);
* } * }
* *
* &#64;Override * &#64;Override
* public String updateSomeThing(String id){ * public String updateSomeThing(String id){
* return _redkale_client.remote(_redkale_bsonConvert, _redkale_jsonConvert, _redkale_transport, 4, id); * return _redkale_client.remote(4, id);
* } * }
* } * }
* </pre></blockquote> * </pre></blockquote>
@@ -1101,27 +868,26 @@ public abstract class Sncp {
* 创建远程模式的Service实例 * 创建远程模式的Service实例
* *
* @param <T> Service泛型 * @param <T> Service泛型
* @param classLoader ClassLoader
* @param name 资源名 * @param name 资源名
* @param executor 线程池
* @param serviceTypeOrImplClass Service类 * @param serviceTypeOrImplClass Service类
* @param transportFactory TransportFactory
* @param clientAddress 本地IP地址 * @param clientAddress 本地IP地址
* @param sncpGroup 自身的组节点名 可能为null
* @param groups 所有的组节点,包含自身 * @param groups 所有的组节点,包含自身
* @param conf 启动配置项 * @param conf 启动配置项
* @param transport 通信组件
* *
* @return Service的远程模式实例 * @return Service的远程模式实例
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public static <T extends Service> T createRemoteService( public static <T extends Service> T createRemoteService(
final ClassLoader classLoader,
final String name, final String name,
final Consumer<Runnable> executor,
final Class<T> serviceTypeOrImplClass, final Class<T> serviceTypeOrImplClass,
final TransportFactory transportFactory,
final InetSocketAddress clientAddress, final InetSocketAddress clientAddress,
final String sncpGroup,
final Set<String> groups, final Set<String> groups,
final AnyValue conf, final AnyValue conf) {
final Transport transport) {
if (serviceTypeOrImplClass == null) return null; if (serviceTypeOrImplClass == null) return null;
if (!Service.class.isAssignableFrom(serviceTypeOrImplClass)) return null; if (!Service.class.isAssignableFrom(serviceTypeOrImplClass)) return null;
int mod = serviceTypeOrImplClass.getModifiers(); int mod = serviceTypeOrImplClass.getModifiers();
@@ -1131,36 +897,19 @@ public abstract class Sncp {
final String clientName = SncpClient.class.getName().replace('.', '/'); final String clientName = SncpClient.class.getName().replace('.', '/');
final String clientDesc = Type.getDescriptor(SncpClient.class); final String clientDesc = Type.getDescriptor(SncpClient.class);
final String sncpDynDesc = Type.getDescriptor(SncpDyn.class); final String sncpDynDesc = Type.getDescriptor(SncpDyn.class);
final String bsonConvertDesc = Type.getDescriptor(BsonConvert.class);
final String jsonConvertDesc = Type.getDescriptor(JsonConvert.class);
final String transportDesc = Type.getDescriptor(Transport.class);
final String stringDesc = Type.getDescriptor(String.class);
final String anyValueDesc = Type.getDescriptor(AnyValue.class); final String anyValueDesc = Type.getDescriptor(AnyValue.class);
ClassLoader loader = Sncp.class.getClassLoader(); ClassLoader loader = classLoader == null ? Thread.currentThread().getContextClassLoader() : classLoader;
String newDynName = supDynName.substring(0, supDynName.lastIndexOf('/') + 1) + REMOTEPREFIX + serviceTypeOrImplClass.getSimpleName(); String newDynName = supDynName.substring(0, supDynName.lastIndexOf('/') + 1) + REMOTEPREFIX + serviceTypeOrImplClass.getSimpleName();
try { try {
Class newClazz = Class.forName(newDynName.replace('/', '.')); Class newClazz = loader.loadClass(newDynName.replace('/', '.'));
T rs = (T) newClazz.newInstance(); T rs = (T) newClazz.newInstance();
SncpClient client = new SncpClient(name, serviceTypeOrImplClass, rs, executor, true, realed ? createLocalServiceClass(name, serviceTypeOrImplClass) : serviceTypeOrImplClass, clientAddress); SncpClient client = new SncpClient(name, serviceTypeOrImplClass, rs, transportFactory, true, realed ? createLocalServiceClass(loader, name, serviceTypeOrImplClass) : serviceTypeOrImplClass, clientAddress);
client.setRemoteGroups(groups);
client.setRemoteGroupTransport(transportFactory.loadRemoteTransport(clientAddress, groups));
Field c = newClazz.getDeclaredField(FIELDPREFIX + "_client"); Field c = newClazz.getDeclaredField(FIELDPREFIX + "_client");
c.setAccessible(true); c.setAccessible(true);
c.set(rs, client); c.set(rs, client);
Field t = newClazz.getDeclaredField(FIELDPREFIX + "_transport"); transportFactory.addSncpService(rs);
t.setAccessible(true);
t.set(rs, transport);
{
StringBuilder sb = new StringBuilder();
sb.append(newClazz.getName()).append("{name = '").append(name);
sb.append("', serviceid = '").append(client.getServiceid());
sb.append("', serviceversion = ").append(client.getServiceversion());
sb.append(", action.size = ").append(client.getActionCount());
sb.append(", address = ").append(clientAddress).append(", groups = ").append(transport == null ? null : transport.getName());
sb.append(", remoteaddrs = ").append(transport == null ? null : Arrays.asList(transport.getRemoteAddresses()));
sb.append("}");
Field s = newClazz.getDeclaredField(FIELDPREFIX + "_selfstring");
s.setAccessible(true);
s.set(rs, sb.toString());
}
return rs; return rs;
} catch (Throwable ex) { } catch (Throwable ex) {
} }
@@ -1178,18 +927,8 @@ public abstract class Sncp {
} }
{ {
av0 = cw.visitAnnotation(Type.getDescriptor(ResourceType.class), true); av0 = cw.visitAnnotation(Type.getDescriptor(ResourceType.class), true);
{ ResourceType rty = serviceTypeOrImplClass.getAnnotation(ResourceType.class);
AnnotationVisitor av1 = av0.visitArray("value"); av0.visit("value", Type.getType(Type.getDescriptor(rty == null ? serviceTypeOrImplClass : rty.value())));
ResourceType rty = serviceTypeOrImplClass.getAnnotation(ResourceType.class);
if (rty == null) {
av1.visit(null, Type.getType(Type.getDescriptor(serviceTypeOrImplClass)));
} else {
for (Class cl : rty.value()) {
av1.visit(null, Type.getType(Type.getDescriptor(cl)));
}
}
av1.visitEnd();
}
av0.visitEnd(); av0.visitEnd();
} }
{ {
@@ -1203,54 +942,14 @@ public abstract class Sncp {
visitAnnotation(cw.visitAnnotation(Type.getDescriptor(ann.annotationType()), true), ann); visitAnnotation(cw.visitAnnotation(Type.getDescriptor(ann.annotationType()), true), ann);
} }
} }
{
fv = cw.visitField(ACC_PRIVATE + ACC_FINAL + ACC_STATIC, FIELDPREFIX + "_service_type", "Ljava/lang/Class;", null, null);
fv.visitEnd();
}
{
fv = cw.visitField(ACC_PRIVATE, FIELDPREFIX + "_bsonConvert", bsonConvertDesc, null, null);
av0 = fv.visitAnnotation("Ljavax/annotation/Resource;", true);
av0.visitEnd();
fv.visitEnd();
}
{
fv = cw.visitField(ACC_PRIVATE, FIELDPREFIX + "_jsonConvert", jsonConvertDesc, null, null);
av0 = fv.visitAnnotation("Ljavax/annotation/Resource;", true);
av0.visitEnd();
fv.visitEnd();
}
{
fv = cw.visitField(ACC_PRIVATE, FIELDPREFIX + "_sncpGroup", stringDesc, null, null);
fv.visitEnd();
}
{
fv = cw.visitField(ACC_PRIVATE, FIELDPREFIX + "_groups", "Ljava/util/Set;", "Ljava/util/Set<Ljava/lang/String;>;", null);
fv.visitEnd();
}
{ {
fv = cw.visitField(ACC_PRIVATE, FIELDPREFIX + "_conf", anyValueDesc, null, null); fv = cw.visitField(ACC_PRIVATE, FIELDPREFIX + "_conf", anyValueDesc, null, null);
fv.visitEnd(); fv.visitEnd();
} }
{
fv = cw.visitField(ACC_PRIVATE, FIELDPREFIX + "_transport", transportDesc, null, null);
fv.visitEnd();
}
{ {
fv = cw.visitField(ACC_PRIVATE, FIELDPREFIX + "_client", clientDesc, null, null); fv = cw.visitField(ACC_PRIVATE, FIELDPREFIX + "_client", clientDesc, null, null);
fv.visitEnd(); fv.visitEnd();
} }
{
fv = cw.visitField(ACC_PRIVATE, FIELDPREFIX + "_selfstring", "Ljava/lang/String;", null, null);
fv.visitEnd();
}
{//静态构造函数
mv = new AsmMethodVisitor(cw.visitMethod(ACC_STATIC, "<clinit>", "()V", null, null));
mv.visitLdcInsn(Type.getType(Type.getDescriptor(serviceTypeOrImplClass)));
mv.visitFieldInsn(PUTSTATIC, newDynName, FIELDPREFIX + "_service_type", "Ljava/lang/Class;");
mv.visitInsn(RETURN);
mv.visitMaxs(1, 0);
mv.visitEnd();
}
{ //构造函数 { //构造函数
mv = new AsmMethodVisitor(cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null)); mv = new AsmMethodVisitor(cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null));
//mv.setDebug(true); //mv.setDebug(true);
@@ -1275,23 +974,25 @@ public abstract class Sncp {
{ // toString() { // toString()
mv = new AsmMethodVisitor(cw.visitMethod(ACC_PUBLIC, "toString", "()Ljava/lang/String;", null, null)); mv = new AsmMethodVisitor(cw.visitMethod(ACC_PUBLIC, "toString", "()Ljava/lang/String;", null, null));
mv.visitVarInsn(ALOAD, 0); mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, newDynName, FIELDPREFIX + "_selfstring", "Ljava/lang/String;"); mv.visitFieldInsn(GETFIELD, newDynName, FIELDPREFIX + "_client", clientDesc);
Label l1 = new Label(); Label l1 = new Label();
mv.visitJumpInsn(IFNONNULL, l1); mv.visitJumpInsn(IFNONNULL, l1);
mv.visitVarInsn(ALOAD, 0); mv.visitVarInsn(ALOAD, 0);
mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "toString", "()Ljava/lang/String;", false); mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Object", "getClass", "()Ljava/lang/Class;", false);
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Class", "getName", "()Ljava/lang/String;", false);
Label l2 = new Label(); Label l2 = new Label();
mv.visitJumpInsn(GOTO, l2); mv.visitJumpInsn(GOTO, l2);
mv.visitLabel(l1); mv.visitLabel(l1);
mv.visitVarInsn(ALOAD, 0); mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, newDynName, FIELDPREFIX + "_selfstring", "Ljava/lang/String;"); mv.visitFieldInsn(GETFIELD, newDynName, FIELDPREFIX + "_client", clientDesc);
mv.visitMethodInsn(INVOKEVIRTUAL, clientName, "toSimpleString", "()Ljava/lang/String;", false);
mv.visitLabel(l2); mv.visitLabel(l2);
mv.visitInsn(ARETURN); mv.visitInsn(ARETURN);
mv.visitMaxs(1, 1); mv.visitMaxs(1, 1);
mv.visitEnd(); mv.visitEnd();
} }
int i = -1; int i = -1;
for (final SncpAction entry : SncpClient.getSncpActions(realed ? createLocalServiceClass(name, serviceTypeOrImplClass) : serviceTypeOrImplClass)) { for (final SncpAction entry : SncpClient.getSncpActions(realed ? createLocalServiceClass(loader, name, serviceTypeOrImplClass) : serviceTypeOrImplClass)) {
final int index = ++i; final int index = ++i;
final java.lang.reflect.Method method = entry.method; final java.lang.reflect.Method method = entry.method;
{ {
@@ -1307,12 +1008,7 @@ public abstract class Sncp {
} }
mv.visitVarInsn(ALOAD, 0); mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, newDynName, FIELDPREFIX + "_client", clientDesc); mv.visitFieldInsn(GETFIELD, newDynName, FIELDPREFIX + "_client", clientDesc);
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, newDynName, FIELDPREFIX + "_bsonConvert", bsonConvertDesc);
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, newDynName, FIELDPREFIX + "_jsonConvert", jsonConvertDesc);
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, newDynName, FIELDPREFIX + "_transport", transportDesc);
if (index <= 5) { if (index <= 5) {
mv.visitInsn(ICONST_0 + index); mv.visitInsn(ICONST_0 + index);
} else { } else {
@@ -1357,7 +1053,7 @@ public abstract class Sncp {
} }
} }
mv.visitMethodInsn(INVOKEVIRTUAL, clientName, "remote", "(" + bsonConvertDesc + jsonConvertDesc + transportDesc + "I[Ljava/lang/Object;)Ljava/lang/Object;", false); mv.visitMethodInsn(INVOKEVIRTUAL, clientName, "remote", "(I[Ljava/lang/Object;)Ljava/lang/Object;", false);
//mv.visitMethodInsn(INVOKEVIRTUAL, convertName, "convertFrom", convertFromDesc, false); //mv.visitMethodInsn(INVOKEVIRTUAL, convertName, "convertFrom", convertFromDesc, false);
if (method.getGenericReturnType() == void.class) { if (method.getGenericReturnType() == void.class) {
mv.visitInsn(POP); mv.visitInsn(POP);
@@ -1400,7 +1096,9 @@ public abstract class Sncp {
}.loadClass(newDynName.replace('/', '.'), bytes); }.loadClass(newDynName.replace('/', '.'), bytes);
try { try {
T rs = (T) newClazz.newInstance(); T rs = (T) newClazz.newInstance();
SncpClient client = new SncpClient(name, serviceTypeOrImplClass, rs, executor, true, realed ? createLocalServiceClass(name, serviceTypeOrImplClass) : serviceTypeOrImplClass, clientAddress); SncpClient client = new SncpClient(name, serviceTypeOrImplClass, rs, transportFactory, true, realed ? createLocalServiceClass(loader, name, serviceTypeOrImplClass) : serviceTypeOrImplClass, clientAddress);
client.setRemoteGroups(groups);
client.setRemoteGroupTransport(transportFactory.loadRemoteTransport(clientAddress, groups));
{ {
Field c = newClazz.getDeclaredField(FIELDPREFIX + "_client"); Field c = newClazz.getDeclaredField(FIELDPREFIX + "_client");
c.setAccessible(true); c.setAccessible(true);
@@ -1411,34 +1109,7 @@ public abstract class Sncp {
c.setAccessible(true); c.setAccessible(true);
c.set(rs, conf); c.set(rs, conf);
} }
{ transportFactory.addSncpService(rs);
Field c = newClazz.getDeclaredField(FIELDPREFIX + "_sncpGroup");
c.setAccessible(true);
c.set(rs, sncpGroup);
}
{
Field c = newClazz.getDeclaredField(FIELDPREFIX + "_groups");
c.setAccessible(true);
c.set(rs, groups);
}
{
Field t = newClazz.getDeclaredField(FIELDPREFIX + "_transport");
t.setAccessible(true);
t.set(rs, transport);
}
{
StringBuilder sb = new StringBuilder();
sb.append(newClazz.getName()).append("{name = '").append(name);
sb.append("', serviceid = ").append(client.getServiceid());
sb.append(", serviceversion = ").append(client.getServiceversion());
sb.append(", action.size = ").append(client.getActionCount());
sb.append(", address = ").append(clientAddress).append(", groups = ").append(transport == null ? null : transport.getName());
sb.append(", remotes = ").append(transport == null ? null : Arrays.asList(transport.getRemoteAddresses()));
sb.append("}");
Field s = newClazz.getDeclaredField(FIELDPREFIX + "_selfstring");
s.setAccessible(true);
s.set(rs, sb.toString());
}
return rs; return rs;
} catch (Exception ex) { } catch (Exception ex) {
throw new RuntimeException(ex); throw new RuntimeException(ex);

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