288 Commits

Author SHA1 Message Date
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
Redkale
f9f2e080da 2017-04-30 19:25:33 +08:00
Redkale
027fa3a18b 2017-04-30 19:25:13 +08:00
Redkale
d9f50d63f0 2017-04-30 18:48:10 +08:00
Redkale
181486c348 2017-04-30 18:30:53 +08:00
Redkale
74adfdfc99 2017-04-30 17:33:15 +08:00
Redkale
c3783eb041 2017-04-30 17:08:59 +08:00
Redkale
77451561e6 2017-04-23 21:22:38 +08:00
Redkale
59d30b05f2 2017-04-23 21:08:18 +08:00
Redkale
2fcf0bb644 2017-04-23 21:03:08 +08:00
Redkale
71ab9c9728 去掉ServiceWrapper 2017-04-23 20:19:07 +08:00
Redkale
09165127e3 2017-04-23 16:47:24 +08:00
Redkale
c28310e0df 2017-04-23 16:03:24 +08:00
Redkale
51435a1c33 2017-04-23 14:19:34 +08:00
Redkale
83c70b9767 2017-04-20 22:03:07 +08:00
Redkale
5534dcd476 2017-04-20 15:03:32 +08:00
Redkale
d18a55deaf 2017-04-20 14:32:15 +08:00
Redkale
ae2d64991c 2017-04-20 14:28:59 +08:00
Redkale
ecb6b80e5e 2017-04-20 14:10:48 +08:00
Redkale
cf332fa67a 2017-04-20 14:08:13 +08:00
Redkale
ffa80c9212 2017-04-20 12:21:47 +08:00
Redkale
7463a8f6b5 2017-04-19 22:58:03 +08:00
Redkale
a168897784 2017-04-19 22:42:04 +08:00
Redkale
d39b3856ca 2017-04-19 18:52:20 +08:00
Redkale
ca9f74185b 2017-04-19 09:50:17 +08:00
Redkale
c35e421ba3 2017-04-19 07:55:45 +08:00
Redkale
65755e0787 2017-04-19 07:54:05 +08:00
Redkale
366c3becc4 2017-04-19 07:44:59 +08:00
Redkale
a1ac6ec543 2017-04-18 17:10:41 +08:00
Redkale
b27bbb7836 2017-03-31 08:11:46 +08:00
Redkale
a57574dd10 2017-03-30 23:19:54 +08:00
Redkale
a7dd22569c 2017-03-30 16:53:20 +08:00
Redkale
0bc0755fb3 2017-03-29 13:55:16 +08:00
Redkale
c2edb60218 2017-03-28 10:42:11 +08:00
Redkale
60c1a82a62 2017-03-27 10:03:55 +08:00
Redkale
e3205128b4 2017-03-27 08:23:22 +08:00
Redkale
a9dff0360f 2017-03-26 15:42:56 +08:00
Redkale
1e871cbee5 2017-03-26 15:02:54 +08:00
Redkale
41aadf33f3 2017-03-26 14:28:06 +08:00
Redkale
679567c85a 2017-03-26 14:15:36 +08:00
Redkale
b77050250c 2017-03-25 22:03:19 +08:00
Redkale
e178d1120b 2017-03-25 14:36:10 +08:00
Redkale
eca138b671 2017-03-24 11:28:01 +08:00
Redkale
0366aef672 2017-03-24 11:11:11 +08:00
Redkale
30103e5c8f 2017-03-23 14:20:35 +08:00
Redkale
cbba7701d8 2017-03-23 14:17:06 +08:00
Redkale
da53bd7db9 2017-03-23 11:20:43 +08:00
Redkale
9e7999da0f 2017-03-22 16:42:54 +08:00
Redkale
2c96f991d5 2017-03-22 16:36:09 +08:00
Redkale
67f8127452 2017-03-22 16:34:51 +08:00
Redkale
bce498885e 异步接口支持AsyncHandler子类 2017-03-22 16:05:10 +08:00
Redkale
eb57a25698 PrepareServlet.addServlet方法改为线程安全 2017-03-22 11:56:21 +08:00
Redkale
4e83e5bf71 2017-03-22 11:28:21 +08:00
Redkale
19a44ce8cf 2017-03-22 10:31:14 +08:00
Redkale
0bd0df3245 2017-03-21 17:16:28 +08:00
Redkale
4f0163736f 2017-03-21 13:09:11 +08:00
Redkale
8d03f52f09 2017-03-21 13:06:15 +08:00
Redkale
815267a590 2017-03-21 12:53:00 +08:00
Redkale
77f8d442b2 2017-03-21 12:45:41 +08:00
Redkale
aacda5d35e 2017-03-21 12:35:12 +08:00
Redkale
58d02f6471 2017-03-21 12:32:42 +08:00
Redkale
c735874cff 2017-03-21 12:02:54 +08:00
Redkale
045029b4a9 2017-03-21 11:55:57 +08:00
Redkale
f6b5882cd4 2017-03-21 11:45:40 +08:00
Redkale
63a9005e6b 2017-03-21 09:47:18 +08:00
Redkale
c4923f317b 2017-03-21 09:34:35 +08:00
Redkale
15e03c0459 2017-03-21 09:14:32 +08:00
Redkale
74f4ddf50b 2017-03-20 22:37:13 +08:00
Redkale
fdc868641d 2017-03-20 21:05:28 +08:00
Redkale
910eb88c55 2017-03-20 17:54:58 +08:00
Redkale
b597131de4 2017-03-20 17:33:32 +08:00
Redkale
56d5f97556 2017-03-20 17:08:54 +08:00
Redkale
b364dd5811 Service异步接口的返回类型强制约束为void,且必须存在对应的同步方法 2017-03-20 16:15:07 +08:00
Redkale
4fec27498c 2017-03-20 13:53:13 +08:00
Redkale
7a195ecf23 2017-03-20 13:37:40 +08:00
Redkale
95b7e819cd 2017-03-20 13:25:42 +08:00
Redkale
73d243aaf1 2017-03-20 10:59:08 +08:00
Redkale
998fecdd51 2017-03-19 21:36:16 +08:00
Redkale
b1ddc0e3a5 2017-03-18 19:21:05 +08:00
Redkale
641ff4709d 2017-03-18 19:16:06 +08:00
Redkale
13f2fbf7d6 2017-03-17 20:18:18 +08:00
Redkale
62f9882314 2017-03-17 20:08:24 +08:00
Redkale
7f270eb9d7 2017-03-17 20:04:09 +08:00
Redkale
974a6bfeaa 2017-03-17 18:18:58 +08:00
Redkale
4958b454af 2017-03-17 18:04:17 +08:00
Redkale
3531d0963d 2017-03-17 17:57:24 +08:00
Redkale
41e6497a2e 2017-03-17 16:49:55 +08:00
156 changed files with 10214 additions and 5747 deletions

32
assembly.xml Normal file
View File

@@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8"?>
<assembly xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/2.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/2.0.0 http://maven.apache.org/xsd/assembly-2.0.0.xsd">
<id>redkale</id>
<formats>
<format>tar.gz</format>
</formats>
<includeBaseDirectory>false</includeBaseDirectory>
<fileSets>
<fileSet>
<directory>${project.basedir}/bin</directory>
<outputDirectory>bin</outputDirectory>
</fileSet>
<fileSet>
<directory>${project.basedir}/conf</directory>
<outputDirectory>conf</outputDirectory>
</fileSet>
<fileSet>
<directory>${project.basedir}/logs</directory>
<outputDirectory>logs</outputDirectory>
</fileSet>
</fileSets>
<dependencySets>
<dependencySet>
<useProjectArtifact>true</useProjectArtifact>
<useTransitiveDependencies>false</useTransitiveDependencies>
<outputDirectory>lib</outputDirectory>
<scope>runtime</scope>
</dependencySet>
</dependencySets>
</assembly>

View File

@@ -1,6 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<application port="5050"> <application port="5050">
<!-- 详细配置说明见: http://redkale.org/redkale.html#redkale_confxml -->
<resources> <resources>
<!-- <!--
<properties> <properties>
@@ -10,24 +13,14 @@
</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>
<remoteaddr value="request.headers.X-RemoteAddress"/>
</request>
<response>
<addheader name="X-Node" value="system.property.APP_NODE" />
<addheader name="Access-Control-Allow-Origin" value="request.headers.Origin" />
<setheader name="Access-Control-Allow-Credentials" value="true"/>
</response>
-->
<services autoload="true"/> <services autoload="true"/>
<servlets path="/pipes" autoload="true" >
<!-- <!-- base指定的自定义HttpServlet子类必须标记@HttpUserType, 不设置base则视为没有当前用户信息设置 -->
<resource-servlet> <rest path="/pipes" base="org.redkale.net.http.HttpServlet"/>
<caches limit="0"/>
<rewrite type="location" match="^/([^-]+)(-[^-\.]+)+\.html(.*)" forward="/$1.html"/> <servlets path="/pipes" autoload="true" />
</resource-servlet>
-->
</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-%u.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

36
pom.xml
View File

@@ -6,7 +6,7 @@
<packaging>jar</packaging> <packaging>jar</packaging>
<url>http://redkale.org</url> <url>http://redkale.org</url>
<description>redkale -- java framework</description> <description>redkale -- java framework</description>
<version>1.4.0-SNAPSHOT</version> <version>1.6.2</version>
<licenses> <licenses>
<license> <license>
<name>Apache 2</name> <name>Apache 2</name>
@@ -122,19 +122,29 @@
</execution> </execution>
</executions> </executions>
</plugin> </plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<version>3.0.0</version>
<configuration>
<appendAssemblyId>false</appendAssemblyId>
<descriptors>
<descriptor>assembly.xml</descriptor>
</descriptors>
</configuration>
<executions>
<execution>
<id>redkale</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins> </plugins>
</build> </build>
<dependencies>
<dependency>
<groupId>org.codehaus.plexus</groupId>
<artifactId>plexus-compiler-javac</artifactId>
<version>2.7</version>
</dependency>
<dependency>
<groupId>org.codehaus.plexus</groupId>
<artifactId>plexus-utils</artifactId>
<version>3.0.22</version>
</dependency>
</dependencies>
</project> </project>

View File

@@ -30,6 +30,7 @@
所有服务所需的资源 所有服务所需的资源
--> -->
<resources> <resources>
<!-- <!--
【节点全局唯一】 【节点全局唯一】
transport节点只能有一个用于配置所有Transport的池参数没配置该节点将自动创建一个。 transport节点只能有一个用于配置所有Transport的池参数没配置该节点将自动创建一个。
@@ -38,8 +39,9 @@
bufferPoolSize ByteBuffer池的大小默认: <group>节点数*CPU核数*8 bufferPoolSize ByteBuffer池的大小默认: <group>节点数*CPU核数*8
--> -->
<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,9 +59,21 @@
--> -->
<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") 进行注入, 被注解的字段类型只能是String、primitive class 全局的参数配置, 可以通过@Resource(name="property.xxxxxx") 进行注入<property>的信息, 被注解的字段类型只能是String、primitive class
如果name是system.property.开头的值将会在进程启动时进行System.setProperty("yyyy", "YYYYYY")操作。 如果name是system.property.开头的值将会在进程启动时进行System.setProperty("yyyy", "YYYYYY")操作。
如果name是mimetype.property.开头的值将会在进程启动时进行MimeType.add("yyyy", "YYYYYY")操作。 如果name是mimetype.property.开头的值将会在进程启动时进行MimeType.add("yyyy", "YYYYYY")操作。
load: 加载文件,多个用;隔开。 load: 加载文件,多个用;隔开。
@@ -70,6 +84,8 @@
System.setProperty("convert.bson.pool.size", "128"); System.setProperty("convert.bson.pool.size", "128");
System.setProperty("convert.json.writer.buffer.defsize", "4096"); System.setProperty("convert.json.writer.buffer.defsize", "4096");
System.setProperty("convert.bson.writer.buffer.defsize", "4096"); System.setProperty("convert.bson.writer.buffer.defsize", "4096");
<properties>节点下也可包含非<property>节点,其节点可以通过@Resource(name="properties.xxxxxx")进行注入, 被注解的字段类型只能是AnyValue、AnyValue[]
--> -->
<properties load="config.properties"> <properties load="config.properties">
<property name="system.property.yyyy" value="YYYYYY"/> <property name="system.property.yyyy" value="YYYYYY"/>
@@ -77,10 +93,11 @@
<property name="xxxxxx" value="XXXXXXXX"/> <property name="xxxxxx" value="XXXXXXXX"/>
<property name="xxxxxx" value="XXXXXXXX"/> <property name="xxxxxx" value="XXXXXXXX"/>
</properties> </properties>
</resources> </resources>
<!-- <!--
protocol: required server所启动的协议Redkale内置的有HTTP、SNCPSNCP使用TCP实现; protocol: required server所启动的协议Redkale内置的有HTTP、SNCP、WATCHSNCP使用TCP实现;
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
@@ -90,7 +107,7 @@
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 表示永久不超时
@@ -128,21 +145,46 @@
</service> </service>
</services> </services>
<!--
加载所有的Filter服务;
autoload="true" 默认值.
autoload="false" 需要显著的指定Filter类
includes 当autoload="true" 拉取类名与includes中的正则表达式匹配的类, 多个正则表达式用分号;隔开
excludes 当autoload="true" 排除类名与excludes中的正则表达式匹配的类, 多个正则表达式用分号;隔开
-->
<filters autoload="true" includes="" excludes="">
<!-- 显著加载指定的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>
<!-- <!--

View File

@@ -17,7 +17,7 @@ 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-%u.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-%u.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

@@ -6,6 +6,11 @@
<!-- 为NONE表示不启动缓存@Cacheable 失效; 非NONE值(通常用ALL)表示开启缓存。 --> <!-- 为NONE表示不启动缓存@Cacheable 失效; 非NONE值(通常用ALL)表示开启缓存。 -->
<shared-cache-mode>NONE</shared-cache-mode> <shared-cache-mode>NONE</shared-cache-mode>
<properties> <properties>
<!--
DataSource的实现类没有设置默认为org.redkale.source.DataJdbcSource的实现使用常规基于JDBC的数据库驱动一般无需设置
-->
<property name="javax.persistence.datasource" value="org.redkale.source.DataJdbcSource"/>
<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"/>
<!-- <!--
javax.persistence.jdbc.driver在JPA的值是JDBC驱动Redkale有所不同值应该是javax.sql.DataSource的子类。 javax.persistence.jdbc.driver在JPA的值是JDBC驱动Redkale有所不同值应该是javax.sql.DataSource的子类。

View File

@@ -16,24 +16,25 @@ import org.redkale.util.*;
/** /**
* API接口文档生成类作用生成Application实例中所有HttpServer的可用HttpServlet的API接口方法 <br> * API接口文档生成类作用生成Application实例中所有HttpServer的可用HttpServlet的API接口方法 <br>
继承 HttpBaseServlet 是为了获取 WebMapping 信息 * 继承 HttpBaseServlet 是为了获取 WebMapping 信息
* *
* <p> * <p>
* 详情见: https://redkale.org * 详情见: https://redkale.org
* *
* @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,14 +52,14 @@ 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[] mappings = ws.value(); String[] urlregs = ws.value();
if (prefix != null && !prefix.isEmpty()) { if (prefix != null && !prefix.isEmpty()) {
for (int i = 0; i < mappings.length; i++) { for (int i = 0; i < urlregs.length; i++) {
mappings[i] = prefix + mappings[i]; urlregs[i] = prefix + urlregs[i];
} }
} }
servletmap.put("mappings", mappings); servletmap.put("urlregs", urlregs);
servletmap.put("moduleid", ws.moduleid()); servletmap.put("moduleid", ws.moduleid());
servletmap.put("name", ws.name()); servletmap.put("name", ws.name());
servletmap.put("comment", ws.comment()); servletmap.put("comment", ws.comment());
@@ -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;
} }
@@ -179,9 +180,9 @@ public class ApiDocs extends HttpBaseServlet {
servletsList.add(servletmap); servletsList.add(servletmap);
} }
servletsList.sort((o1, o2) -> { servletsList.sort((o1, o2) -> {
String[] mappings1 = (String[]) o1.get("mappings"); String[] urlregs1 = (String[]) o1.get("urlregs");
String[] mappings2 = (String[]) o2.get("mappings"); String[] urlregs2 = (String[]) o2.get("urlregs");
return mappings1.length > 0 ? (mappings2.length > 0 ? mappings1[0].compareTo(mappings2[0]) : 1) : -1; return urlregs1.length > 0 ? (urlregs2.length > 0 ? urlregs1[0].compareTo(urlregs2[0]) : 1) : -1;
}); });
} }
Map<String, Object> resultmap = new LinkedHashMap<>(); Map<String, Object> resultmap = new LinkedHashMap<>();
@@ -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 boolean authenticate(int moduleid, int actionid, HttpRequest request, HttpResponse response) throws IOException {
return true;
}
} }

View File

@@ -5,8 +5,9 @@
*/ */
package org.redkale.boot; package org.redkale.boot;
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 +16,19 @@ 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.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 +37,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 +76,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,43 +102,42 @@ 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();
//服务配置项
final AnyValue config;
//临时计数器 //临时计数器
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;
//日志 //日志
private final Logger logger; private final Logger logger;
//服务配置项
private final AnyValue config;
//服务启动时间 //服务启动时间
private final long startTime = System.currentTimeMillis(); private final long startTime = System.currentTimeMillis();
//Server启动的计数器用于确保所有Server都启动完后再进行下一步处理 //Server启动的计数器用于确保所有Server都启动完后再进行下一步处理
private final CountDownLatch serversLatch; private final CountDownLatch serversLatch;
//根ClassLoader
private final NodeClassLoader classLoader;
private Application(final AnyValue config) { private Application(final AnyValue config) {
this(false, config); this(false, config);
} }
@@ -232,7 +227,7 @@ 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 -------------------------------"); logger.log(Level.INFO, "------------------------------- Redkale " + Redkale.getDotedVersion() + " -------------------------------");
//------------------配置 <transport> 节点 ------------------ //------------------配置 <transport> 节点 ------------------
ObjectPool<ByteBuffer> transportPool = null; ObjectPool<ByteBuffer> transportPool = null;
ExecutorService transportExec = null; ExecutorService transportExec = null;
@@ -244,8 +239,8 @@ public final class Application {
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 = transportConf.getIntValue("bufferCapacity", 8 * 1024);
final int bufferPoolSize = transportConf.getIntValue("bufferPoolSize", groupsize * Runtime.getRuntime().availableProcessors() * 8); final int bufferPoolSize = transportConf.getIntValue("bufferPoolSize", groupsize * Runtime.getRuntime().availableProcessors() * 8);
final int threads = transportConf.getIntValue("threads", groupsize * Runtime.getRuntime().availableProcessors() * 8); final int threads = transportConf.getIntValue("threads", groupsize * Runtime.getRuntime().availableProcessors() * 8);
@@ -271,17 +266,25 @@ 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);
this.transportExecutor = transportExec; this.classLoader = new NodeClassLoader(Thread.currentThread().getContextClassLoader());
this.transportChannelGroup = transportGroup; Thread.currentThread().setContextClassLoader(this.classLoader);
} }
public ResourceFactory getResourceFactory() { public ResourceFactory getResourceFactory() {
return resourceFactory; return resourceFactory;
} }
public WatchFactory getWatchFactory() { public TransportFactory getTransportFactory() {
return watchFactory; return transportFactory;
}
public NodeClassLoader getNodeClassLoader() {
return classLoader;
}
public List<NodeServer> getNodeServers() {
return new ArrayList<>(servers);
} }
public File getHome() { public File getHome() {
@@ -292,10 +295,6 @@ public final class Application {
return startTime; return startTime;
} }
private void initLogging() {
}
public void init() throws Exception { public void init() throws Exception {
System.setProperty("java.util.concurrent.ForkJoinPool.common.parallelism", "" + Runtime.getRuntime().availableProcessors() * 4); System.setProperty("java.util.concurrent.ForkJoinPool.common.parallelism", "" + Runtime.getRuntime().availableProcessors() * 4);
System.setProperty("convert.bson.tiny", "true"); System.setProperty("convert.bson.tiny", "true");
@@ -307,12 +306,12 @@ public final class Application {
File persist = new File(this.home, "conf/persistence.xml"); File persist = new File(this.home, "conf/persistence.xml");
final String homepath = this.home.getCanonicalPath(); final String homepath = this.home.getCanonicalPath();
if (persist.isFile()) System.setProperty(DataDefaultSource.DATASOURCE_CONFPATH, persist.getCanonicalPath()); if (persist.isFile()) System.setProperty(DataSources.DATASOURCE_CONFPATH, persist.getCanonicalPath());
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(logger, lib);
initLogging();
//------------------------------------------------------------------------ //------------------------------------------------------------------------
final AnyValue resources = config.getAnyValue("resources"); final AnyValue resources = config.getAnyValue("resources");
if (resources != null) { if (resources != null) {
@@ -338,6 +337,7 @@ public final class Application {
String name = prop.getValue("name"); String name = prop.getValue("name");
String value = prop.getValue("value"); String value = prop.getValue("value");
if (name == null || value == null) continue; if (name == null || value == null) continue;
value = value.replace("${APP_HOME}", home.getCanonicalPath()).replace('\\', '/');
if (name.startsWith("system.property.")) { if (name.startsWith("system.property.")) {
System.setProperty(name.substring("system.property.".length()), value); System.setProperty(name.substring("system.property.".length()), value);
} else if (name.startsWith("mimetype.property.")) { } else if (name.startsWith("mimetype.property.")) {
@@ -350,15 +350,71 @@ public final class Application {
} }
} }
} }
if (this.localAddress != null && this.resourceFactory.find("property.datasource.nodeid", String.class) == null) {
byte[] bs = this.localAddress.getAddress();
int v = (0xff & bs[bs.length - 2]) % 10 * 100 + (0xff & bs[bs.length - 1]);
this.resourceFactory.register("property.datasource.nodeid", "" + v);
}
this.resourceFactory.register(BsonFactory.root()); this.resourceFactory.register(BsonFactory.root());
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());
//只有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 (!(src instanceof WatchService) || Sncp.isRemote((Service) src)) return; //远程模式不得注入
Class type = field.getType();
if (type == Application.class) {
field.set(src, application);
} 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, TransportFactory.class, NodeSncpServer.class, NodeHttpServer.class, NodeWatchServer.class);
//--------------------------------------------------------------------------
initResources(); initResources();
} }
@@ -367,25 +423,18 @@ 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);
} }
} }
//------------------------------------------------------------------------ //------------------------------------------------------------------------
@@ -435,7 +484,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();
@@ -480,7 +529,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;
} }
@@ -494,20 +543,25 @@ 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);
} }
} }
this.watching = !watchs.isEmpty();
//单向SNCP服务不需要对等group //单向SNCP服务不需要对等group
//if (!sncps.isEmpty() && globalNodes.isEmpty()) throw new RuntimeException("found SNCP Server node but not found <group> node info."); //if (!sncps.isEmpty() && globalNodes.isEmpty()) throw new RuntimeException("found SNCP Server node but not found <group> node info.");
runServers(timecd, sncps); //必须确保sncp都启动后再启动其他协议 runServers(timecd, sncps); //必须确保sncp都启动后再启动其他协议
runServers(timecd, others); runServers(timecd, others);
runServers(timecd, watchs); //必须在所有server都启动后再启动
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();
} }
@@ -534,13 +588,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(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) {
@@ -550,7 +613,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);
} }
} }
@@ -627,17 +692,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())) {
@@ -647,11 +701,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 {
@@ -664,26 +713,22 @@ 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 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,19 @@ public final class ClassFilter<T> {
private AnyValue conf; //基本配置信息, 当符合条件时将conf的属性赋值到FilterEntry中去。 private AnyValue conf; //基本配置信息, 当符合条件时将conf的属性赋值到FilterEntry中去。
public ClassFilter(Class<? extends Annotation> annotationClass, Class superClass) { public ClassFilter(Class<? extends Annotation> annotationClass, Class superClass, Class[] excludeSuperClasses) {
this(annotationClass, superClass, null); this(annotationClass, superClass, excludeSuperClasses, null);
} }
public ClassFilter(Class<? extends Annotation> annotationClass, Class superClass, AnyValue conf) { public ClassFilter(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;
} }
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, 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);
@@ -247,7 +250,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 +278,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 +314,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;
} }

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

@@ -0,0 +1,28 @@
/*
* 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.*;
/**
*
* @author zhangjx
*/
public class NodeClassLoader extends URLClassLoader {
public NodeClassLoader(ClassLoader parent) {
super(new URL[0], parent);
}
public Class<?> loadClass(String name, byte[] b) {
return defineClass(name, b, 0, b.length);
}
@Override
public void addURL(URL url) {
super.addURL(url);
}
}

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, (Transport) null, (Collection<Transport>) null); nodeService = Sncp.createLocalService(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());
@@ -109,13 +164,6 @@ public class NodeHttpServer extends NodeServer {
final String[] mappings = ws.value(); final String[] mappings = ws.value();
String pref = ws.repair() ? prefix : ""; String pref = ws.repair() ? prefix : "";
DefaultAnyValue servletConf = (DefaultAnyValue) en.getProperty(); DefaultAnyValue servletConf = (DefaultAnyValue) en.getProperty();
WebInitParam[] webparams = ws.initParams();
if (webparams.length > 0) {
if (servletConf == null) servletConf = new DefaultAnyValue();
for (WebInitParam webparam : webparams) {
servletConf.addValue(webparam.name(), webparam.value());
}
}
this.httpServer.addHttpServlet(servlet, pref, servletConf, mappings); this.httpServer.addHttpServlet(servlet, pref, servletConf, mappings);
if (ss != null) { if (ss != null) {
for (int i = 0; i < mappings.length; i++) { for (int i = 0; i < mappings.length; i++) {
@@ -124,9 +172,9 @@ public class NodeHttpServer extends NodeServer {
ss.add(new AbstractMap.SimpleEntry<>(clazz.getName(), mappings)); ss.add(new AbstractMap.SimpleEntry<>(clazz.getName(), mappings));
} }
} }
int max = 0;
if (ss != null && sb != null) { if (ss != null && 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;
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();
} }
@@ -138,64 +186,123 @@ public class NodeHttpServer extends NodeServer {
sb.append(" mapping to ").append(Arrays.toString(as.getValue())).append(LINE_SEPARATOR); sb.append(" mapping to ").append(Arrays.toString(as.getValue())).append(LINE_SEPARATOR);
} }
} }
if (sb != null && sb.length() > 0) logger.log(Level.INFO, sb.toString());
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); loadRestServlet(webSocketFilter, restConf, restedObjects, sb);
} }
} }
if (sb != null && sb.length() > 0) logger.log(Level.INFO, sb.toString().trim());
} }
protected void loadRestServlet(final String prefix, final AnyValue restConf) 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服务
final StringBuilder sb = logger.isLoggable(Level.INFO) ? new StringBuilder() : null;
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 Set<String> includeValues = new HashSet<>(); final Class userType = userTypeStr == null ? null : Class.forName(userTypeStr);
final Set<String> excludeValues = new HashSet<>(); final Class baseServletType = Class.forName(restConf.getValue("base", HttpServlet.class.getName()));
for (AnyValue item : restConf.getAnyValues("service")) { final Set<String> includeValues = new HashSet<>();
if (item.getBoolValue("ignore", false)) { final Set<String> excludeValues = new HashSet<>();
excludeValues.add(item.getValue("value", "")); for (AnyValue item : restConf.getAnyValues("service")) {
} else { if (item.getBoolValue("ignore", false)) {
includeValues.add(item.getValue("value", "")); excludeValues.add(item.getValue("value", ""));
} } else {
} includeValues.add(item.getValue("value", ""));
final ClassFilter restFilter = ClassFilter.create(restConf.getValue("includes", ""), restConf.getValue("excludes", ""), includeValues, excludeValues);
super.interceptorServiceWrappers.forEach((wrapper) -> {
final Class stype = wrapper.getType();
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(wrapper.getName(), stype, wrapper.getService(), baseServletClass, prefix, (AnyValue) null);
resourceFactory.inject(servlet, NodeHttpServer.this);
if (finest) logger.finest("Create RestServlet[resource=" + wrapper.getName() + "] = " + 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];
} }
if (servlet.getClass().getSimpleName().charAt(0) != '_') { }
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(service, userType, baseServletType, prefix);
if (servlet == null) return; //没有HttpMapping方法的HttpServlet调用Rest.createRestServlet就会返回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 (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(stype, prefix, en.getProperty());
if (servlet == null) return; //没有RestOnMessage方法的HttpServlet调用Rest.createRestWebSocketServlet就会返回null
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] = prefix + mappings[i];
}
ss.add(new AbstractMap.SimpleEntry<>(servlet.getClass().getName(), mappings)); 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) {
@@ -209,6 +316,5 @@ public class NodeHttpServer extends NodeServer {
sb.append(" mapping to ").append(Arrays.toString(as.getValue())).append(LINE_SEPARATOR); sb.append(" mapping to ").append(Arrays.toString(as.getValue())).append(LINE_SEPARATOR);
} }
} }
if (sb != null && sb.length() > 0) logger.log(Level.INFO, sb.toString());
} }
} }

View File

@@ -5,9 +5,6 @@
*/ */
package org.redkale.boot; package org.redkale.boot;
import java.util.Objects;
import org.redkale.service.Service;
/** /**
* NodeServer的拦截类 * NodeServer的拦截类
* *
@@ -38,63 +35,4 @@ public class NodeInterceptor {
} }
public static class InterceptorServiceWrapper<T extends Service> {
private String name;
private Class<T> type;
private T service;
public InterceptorServiceWrapper() {
}
public InterceptorServiceWrapper(String name, Class<T> type, T service) {
this.name = name;
this.type = type;
this.service = service;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Class<T> getType() {
return type;
}
public void setType(Class<T> type) {
this.type = type;
}
public T getService() {
return service;
}
public void setService(T service) {
this.service = service;
}
@Override
public int hashCode() {
int hash = 7;
hash = 97 * hash + Objects.hashCode(this.name);
hash = 97 * hash + Objects.hashCode(this.type);
return hash;
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null) return false;
if (getClass() != obj.getClass()) return false;
final InterceptorServiceWrapper<?> other = (InterceptorServiceWrapper<?>) obj;
return Objects.equals(this.name, other.name) && Objects.equals(this.type, other.type);
}
}
} }

View File

@@ -8,24 +8,24 @@ package org.redkale.boot;
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 +44,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,14 +53,17 @@ public abstract class NodeServer {
//当前Server对象 //当前Server对象
protected final Server server; protected final Server server;
//ClassLoader
protected final NodeClassLoader classLoader;
//当前Server的SNCP协议的组 //当前Server的SNCP协议的组
private String sncpGroup = null; protected String sncpGroup = null;
//SNCP服务的地址 非SNCP为null //SNCP服务的地址 非SNCP为null
private InetSocketAddress sncpAddress; private InetSocketAddress sncpAddress;
//加载Service时的处理函数 //加载Service时的处理函数
protected Consumer<ServiceWrapper> consumer; protected Consumer<Service> consumer;
//server节点的配置 //server节点的配置
protected AnyValue serverConf; protected AnyValue serverConf;
@@ -78,22 +72,25 @@ public abstract class NodeServer {
protected NodeInterceptor interceptor; protected NodeInterceptor interceptor;
//供interceptor使用的Service对象集合 //供interceptor使用的Service对象集合
protected final Set<NodeInterceptor.InterceptorServiceWrapper> interceptorServiceWrappers = new LinkedHashSet<>(); protected final Set<Service> interceptorServices = new LinkedHashSet<>();
//本地模式的Service对象集合 //本地模式的Service对象集合
protected final Set<ServiceWrapper> localServiceWrappers = new LinkedHashSet<>(); protected final Set<Service> localServices = new LinkedHashSet<>();
//远程模式的Service对象集合 //远程模式的Service对象集合
protected final Set<ServiceWrapper> remoteServiceWrappers = new LinkedHashSet<>(); protected final Set<Service> remoteServices = new LinkedHashSet<>();
private volatile int maxClassNameLength = 0;
private volatile int maxNameLength = 0;
public NodeServer(Application application, Server server) { public NodeServer(Application application, Server server) {
this.application = application; this.application = application;
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.classLoader = new NodeClassLoader(Thread.currentThread().getContextClassLoader());
this.finer = logger.isLoggable(Level.FINER); Thread.currentThread().setContextClassLoader(this.classLoader);
this.finest = logger.isLoggable(Level.FINEST);
} }
protected Consumer<Runnable> getExecutor() throws Exception { protected Consumer<Runnable> getExecutor() throws Exception {
@@ -116,7 +113,7 @@ public abstract class NodeServer {
if (context == null) { if (context == null) {
t.run(); t.run();
} else { } else {
context.submit(t); context.submitAsync(t);
} }
} }
@@ -136,13 +133,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");
@@ -169,151 +170,212 @@ public abstract class NodeServer {
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 = Class.forName(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);
//暂时不支持DataSource通过<resources>设置
logger.log(Level.SEVERE, "load application source resource, but not CacheSource error: " + 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 --------------------------------------------------------
resourceFactory.register((ResourceFactory rf, final Object src, String resourceName, Field field, final Object attachment) -> {
try {
Resource res = field.getAnnotation(Resource.class);
if (res == null || !res.name().startsWith("properties.")) return;
if ((src instanceof Service) && Sncp.isRemote((Service) src)) return; //远程模式不得注入 DataSource
Class type = field.getType();
if (type != AnyValue.class && type != AnyValue[].class) return;
Object resource = null;
final AnyValue properties = resources == null ? null : resources.getAnyValue("properties");
if (properties != null && type == AnyValue.class) {
resource = properties.getAnyValue(res.name().substring("properties.".length()));
appResFactory.register(resourceName, AnyValue.class, resource);
} else if (properties != null && type == AnyValue[].class) {
resource = properties.getAnyValues(res.name().substring("properties.".length()));
appResFactory.register(resourceName, AnyValue[].class, resource);
}
field.set(src, resource);
} catch (Exception e) {
logger.log(Level.SEVERE, "Resource inject error", e);
}
}, AnyValue.class, AnyValue[].class);
//------------------------------------- 注册DataSource --------------------------------------------------------
resourceFactory.register((ResourceFactory rf, final Object src, String resourceName, Field field, final Object attachment) -> { resourceFactory.register((ResourceFactory rf, final Object src, String resourceName, Field field, final Object attachment) -> {
try { try {
if (field.getAnnotation(Resource.class) == null) return; if (field.getAnnotation(Resource.class) == null) return;
if ((src instanceof Service) && Sncp.isRemote((Service) src)) return; //远程模式不得注入 DataSource if ((src instanceof Service) && Sncp.isRemote((Service) src)) return; //远程模式不得注入 DataSource
DataSource source = new DataDefaultSource(resourceName); DataSource source = DataSources.createDataSource(resourceName);
application.dataSources.add(source); application.dataSources.add(source);
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
Service cacheListenerService = Sncp.createLocalService(resourceName, getExecutor(), appResFactory, DataCacheListenerService.class, sncpAddr, sameGroupTransport, diffGroupTransports);
appResFactory.register(resourceName, DataCacheListener.class, cacheListenerService);
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<>();
ServiceWrapper wrapper = new ServiceWrapper(DataCacheListenerService.class, cacheListenerService, resourceName, sncpServer.getSncpGroup(), gs, null); if (client != null && client.getSameGroup() != null) groups.add(client.getSameGroup());
localServiceWrappers.add(wrapper); if (client != null && client.getDiffGroups() != null) groups.addAll(client.getDiffGroups());
sncpServer.consumerAccept(wrapper); Service cacheListenerService = Sncp.createLocalService(resourceName, DataCacheListenerService.class, appResFactory, appTranFactory, sncpAddr, groups, Sncp.getConf((Service) src));
appResFactory.register(resourceName, DataCacheListener.class, cacheListenerService);
localServices.add(cacheListenerService);
sncpServer.consumerAccept(cacheListenerService);
rf.inject(cacheListenerService, self); rf.inject(cacheListenerService, self);
logger.info("[" + Thread.currentThread().getName() + "] Load Service " + wrapper.getService()); logger.info("[" + Thread.currentThread().getName() + "] Load Service " + cacheListenerService);
} }
field.set(src, source); field.set(src, source);
rf.inject(source, self); // 给 "datasource.nodeid" 赋值; 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);
resourceFactory.register((ResourceFactory rf, final Object src, final String resourceName, Field field, final Object attachment) -> {
try {
if (field.getAnnotation(Resource.class) == null) return;
if ((src instanceof Service) && Sncp.isRemote((Service) src)) return; //远程模式不得注入 CacheSource
SncpClient client = Sncp.getSncpClient((Service) src); //------------------------------------- 注册CacheSource --------------------------------------------------------
Transport sameGroupTransport = Sncp.getSameGroupTransport((Service) src); resourceFactory.register(new ResourceFactory.ResourceLoader() {
Transport[] dts = Sncp.getDiffGroupTransports((Service) src); public void load(ResourceFactory rf, final Object src, final String resourceName, Field field, final Object attachment) {
List<Transport> diffGroupTransports = dts == null ? new ArrayList<>() : Arrays.asList(dts); try {
final InetSocketAddress sncpAddr = client == null ? null : client.getClientAddress(); if (field.getAnnotation(Resource.class) == null) return;
final CacheSourceService source = Sncp.createLocalService(resourceName, getExecutor(), appResFactory, CacheSourceService.class, sncpAddr, sameGroupTransport, diffGroupTransports); if ((src instanceof Service) && Sncp.isRemote((Service) src)) return; //远程模式不需要注入 CacheSource
Type genericType = field.getGenericType(); final Service srcService = (Service) src;
ParameterizedType pt = (genericType instanceof ParameterizedType) ? (ParameterizedType) genericType : null; SncpClient client = Sncp.getSncpClient(srcService);
Type valType = pt == null ? null : pt.getActualTypeArguments()[1]; final InetSocketAddress sncpAddr = client == null ? null : client.getClientAddress();
source.setStoreType(pt == null ? Serializable.class : (Class) pt.getActualTypeArguments()[0], valType instanceof Class ? (Class) valType : Object.class); final AnyValue sourceConf = cacheResource.get(resourceName);
if (field.getAnnotation(Transient.class) != null) source.setNeedStore(false); //必须在setStoreType之后 final Class sourceType = sourceConf == null ? CacheMemorySource.class : Class.forName(sourceConf.getValue("type"));
application.cacheSources.add(source); final Set<String> groups = new HashSet<>();
appResFactory.register(resourceName, genericType, source); if (client != null && client.getSameGroup() != null) groups.add(client.getSameGroup());
appResFactory.register(resourceName, CacheSource.class, source); if (client != null && client.getDiffGroups() != null) groups.addAll(client.getDiffGroups());
field.set(src, source); final CacheSource source = (CacheSource) Sncp.createLocalService(resourceName, sourceType, appResFactory, appTranFactory, sncpAddr, groups, Sncp.getConf(srcService));
rf.inject(source, self); // Type genericType = field.getGenericType();
((Service) source).init(null); 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(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服务注入CacheSourceService if ((src instanceof WebSocketNodeService) && sncpAddr != null) { //只有WebSocketNodeService的服务才需要给SNCP服务注入CacheMemorySource
NodeSncpServer sncpServer = application.findNodeSncpServer(sncpAddr); NodeSncpServer sncpServer = application.findNodeSncpServer(sncpAddr);
Set<String> gs = application.findSncpGroups(sameGroupTransport, diffGroupTransports); sncpServer.getSncpServer().addSncpServlet((Service) source);
ServiceWrapper wrapper = new ServiceWrapper(CacheSourceService.class, (Service) source, resourceName, sncpServer.getSncpGroup(), gs, null); //logger.info("[" + Thread.currentThread().getName() + "] Load Service " + source);
sncpServer.getSncpServer().addSncpServlet(wrapper); }
logger.info("[" + Thread.currentThread().getName() + "] Load Service " + wrapper.getService()); 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();
final Class<? extends Service> type = entry.getType(); for (FilterEntry<? extends Service> entry : entrys) { //service实现类
if (Modifier.isFinal(type.getModifiers())) continue; //修饰final的类跳过 final Class<? extends Service> serviceImplClass = entry.getType();
if (!Modifier.isPublic(type.getModifiers())) continue; if (Modifier.isFinal(serviceImplClass.getModifiers())) continue; //修饰final的类跳过
if (!Modifier.isPublic(serviceImplClass.getModifiers())) continue;
if (entry.isExpect()) { if (entry.isExpect()) {
if (Modifier.isAbstract(type.getModifiers())) continue; //修饰abstract的类跳过 if (Modifier.isAbstract(serviceImplClass.getModifiers())) continue; //修饰abstract的类跳过
if (DataSource.class.isAssignableFrom(type)) continue; if (DataSource.class.isAssignableFrom(serviceImplClass)) continue;
if (CacheSource.class.isAssignableFrom(type)) continue; if (CacheSource.class.isAssignableFrom(serviceImplClass)) continue;
if (DataCacheListener.class.isAssignableFrom(type)) continue; if (DataCacheListener.class.isAssignableFrom(serviceImplClass)) continue;
if (WebSocketNode.class.isAssignableFrom(type)) 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(), type); Service oldother = resourceFactory.find(entry.getName(), serviceImplClass);
if (oldother != null) { //Server加载Service时需要判断是否已经加载过了。 if (oldother != null) { //Server加载Service时需要判断是否已经加载过了。
interceptorServiceWrappers.add(new NodeInterceptor.InterceptorServiceWrapper(entry.getName(), type, oldother)); interceptorServices.add(oldother);
continue; continue;
} }
final HashSet<String> groups = entry.getGroups(); //groups.isEmpty()表示<services>没有配置groups属性。 final HashSet<String> groups = entry.getGroups(); //groups.isEmpty()表示<services>没有配置groups属性。
if (groups.isEmpty() && isSNCP() && this.sncpGroup != null) groups.add(this.sncpGroup); if (groups.isEmpty() && isSNCP() && this.sncpGroup != null) groups.add(this.sncpGroup);
final boolean localed = (this.sncpAddress == null && entry.isEmptyGroups() && !type.isInterface() && !Modifier.isAbstract(type.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配置
|| type.getAnnotation(LocalService.class) != null;//本地模式 || serviceImplClass.getAnnotation(Local.class) != null;//本地模式
if (localed && (type.isInterface() || Modifier.isAbstract(type.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(), type, if (ws || localed) { //本地模式
NodeServer.this.sncpAddress, loadTransport(NodeServer.this.sncpGroup), loadTransports(groups)); service = Sncp.createLocalService(resourceName, serviceImplClass, appResourceFactory, appTransportFactory, NodeServer.this.sncpAddress, groups, entry.getProperty());
} else { } else {
service = Sncp.createRemoteService(entry.getName(), getExecutor(), type, NodeServer.this.sncpAddress, loadTransport(groups)); service = Sncp.createRemoteService(resourceName, serviceImplClass, appTransportFactory, NodeServer.this.sncpAddress, groups, entry.getProperty());
} }
if (SncpClient.parseMethod(type).isEmpty()) return; //class没有可用的方法 通常为BaseService if (SncpClient.parseMethod(serviceImplClass).isEmpty()) return; //class没有可用的方法 通常为BaseService
final ServiceWrapper wrapper = new ServiceWrapper(type, service, entry.getName(), localed ? NodeServer.this.sncpGroup : null, groups, entry.getProperty());
for (final Class restype : wrapper.getTypes()) { final Class restype = Sncp.getResourceType(service);
if (resourceFactory.find(wrapper.getName(), restype) == null) { if (rf.find(resourceName, restype) == null) {
regFactory.register(wrapper.getName(), restype, wrapper.getService()); regFactory.register(resourceName, restype, service);
if (needinject) rf.inject(wrapper.getService()); //动态加载的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(ServiceWrapper.class.getSimpleName() + "(class:" + type.getName() + ", name:" + entry.getName() + ", group:" + groups + ") is repeat.");
}
} }
if (wrapper.isRemote()) { if (Sncp.isRemote(service)) {
remoteServiceWrappers.add(wrapper); remoteServices.add(service);
} else { } else {
localServiceWrappers.add(wrapper); if (field != null) rf.inject(service); //动态加载的Service也存在按需加载的注入资源
interceptorServiceWrappers.add(new NodeInterceptor.InterceptorServiceWrapper(entry.getName(), type, service)); localServices.add(service);
if (consumer != null) consumer.accept(wrapper); interceptorServices.add(service);
if (consumer != null) consumer.accept(service);
} }
} catch (RuntimeException ex) { } catch (RuntimeException ex) {
throw ex; throw ex;
@@ -322,14 +384,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) -> { ResourceType rty = entry.getType().getAnnotation(ResourceType.class);
runner.accept(rf, true); resourceFactory.register(resourceLoader, rty == null ? entry.getType() : rty.value());
};
for (final Class restype : ServiceWrapper.parseTypes(entry.getType())) {
resourceFactory.register(resourceLoader, restype);
}
} else { } else {
runner.accept(resourceFactory, false); resourceLoader.load(resourceFactory, null, entry.getName(), null, false);
} }
} }
@@ -339,30 +397,38 @@ public abstract class NodeServer {
final StringBuilder sb = logger.isLoggable(Level.INFO) ? new StringBuilder() : null; final StringBuilder sb = logger.isLoggable(Level.INFO) ? new StringBuilder() : null;
//---------------- inject ---------------- //---------------- inject ----------------
new ArrayList<>(localServiceWrappers).forEach(y -> { new ArrayList<>(localServices).forEach(y -> {
resourceFactory.inject(y.getService(), NodeServer.this); resourceFactory.inject(y, NodeServer.this);
calcMaxLength(y);
}); });
new ArrayList<>(remoteServiceWrappers).forEach(y -> { new ArrayList<>(remoteServices).forEach(y -> {
resourceFactory.inject(y.getService(), NodeServer.this); resourceFactory.inject(y, NodeServer.this);
calcMaxLength(y);
}); });
if (sb != null) { if (sb != null) {
remoteServiceWrappers.forEach(y -> { remoteServices.forEach(y -> {
sb.append(threadName).append(y.toSimpleString()).append(" load and inject").append(LINE_SEPARATOR); sb.append(threadName).append(Sncp.toSimpleString(y, maxNameLength, maxClassNameLength)).append(" load and inject").append(LINE_SEPARATOR);
}); });
} }
//----------------- init ----------------- //----------------- init -----------------
List<ServiceWrapper> swlist = new ArrayList<>(localServiceWrappers); List<Service> swlist = new ArrayList<>(localServices);
Collections.sort(swlist); Collections.sort(swlist, (o1, o2) -> {
localServiceWrappers.clear(); int rs = Sncp.getResourceType(o1).getName().compareTo(Sncp.getResourceType(o2).getName());
localServiceWrappers.addAll(swlist); if (rs == 0) rs = Sncp.getResourceName(o1).compareTo(Sncp.getResourceName(o2));
return rs;
});
localServices.clear();
localServices.addAll(swlist);
final List<String> slist = sb == null ? null : new CopyOnWriteArrayList<>(); final List<String> slist = sb == null ? null : new CopyOnWriteArrayList<>();
CountDownLatch clds = new CountDownLatch(localServiceWrappers.size()); CountDownLatch clds = new CountDownLatch(localServices.size());
localServiceWrappers.parallelStream().forEach(y -> { localServices.parallelStream().forEach(y -> {
try { try {
long s = System.currentTimeMillis(); long s = System.currentTimeMillis();
y.getService().init(y.getConf()); y.init(Sncp.getConf(y));
long e = System.currentTimeMillis() - s; long e = System.currentTimeMillis() - s;
if (slist != null) slist.add(new StringBuilder().append(threadName).append(y.toSimpleString()).append(" load and init in ").append(e).append(" ms").append(LINE_SEPARATOR).toString()); String serstr = Sncp.toSimpleString(y, maxNameLength, maxClassNameLength);
if (slist != null) slist.add(new StringBuilder().append(threadName).append(serstr).append(" load and init in ").append(e).append(" ms").append(LINE_SEPARATOR).toString());
} finally { } finally {
clds.countDown(); clds.countDown();
} }
@@ -378,76 +444,95 @@ public abstract class NodeServer {
if (sb != null && sb.length() > 0) logger.log(Level.INFO, sb.toString()); if (sb != null && sb.length() > 0) logger.log(Level.INFO, sb.toString());
} }
protected List<Transport> loadTransports(final HashSet<String> groups) { private void calcMaxLength(Service y) { //计算toString中的长度
if (groups == null) return null; maxNameLength = Math.max(maxNameLength, Sncp.getResourceName(y).length());
final List<Transport> transports = new ArrayList<>(); maxClassNameLength = Math.max(maxClassNameLength, Sncp.getResourceType(y).getName().length() + 1);
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; * protected List<Transport> loadTransports(final HashSet<String> groups) {
final String groupid = new ArrayList<>(groups).stream().sorted().collect(Collectors.joining(";")); //按字母排列顺序 * if (groups == null) return null;
Transport transport = application.resourceFactory.find(groupid, Transport.class); * final List<Transport> transports = new ArrayList<>();
if (transport != null) return transport; * for (String group : groups) {
final List<Transport> transports = new ArrayList<>(); * if (this.sncpGroup == null || !this.sncpGroup.equals(group)) {
for (String group : groups) { * transports.add(loadTransport(group));
transports.add(loadTransport(group)); * }
} * }
Set<InetSocketAddress> addrs = new HashSet(); * return transports;
transports.forEach(t -> addrs.addAll(Arrays.asList(t.getRemoteAddresses()))); * }
Transport first = transports.get(0); * protected Transport loadTransport(final HashSet<String> groups) {
GroupInfo ginfo = application.findGroupInfo(first.getName()); * if (groups == null || groups.isEmpty()) return null;
Transport newTransport = new Transport(groupid, ginfo.getProtocol(), application.getWatchFactory(), * final String groupid = new ArrayList<>(groups).stream().sorted().collect(Collectors.joining(";")); //按字母排列顺序
ginfo.getSubprotocol(), application.transportBufferPool, application.transportChannelGroup, this.sncpAddress, addrs); * Transport transport = application.resourceFactory.find(groupid, Transport.class);
synchronized (application.resourceFactory) { * if (transport != null) return transport;
transport = application.resourceFactory.find(groupid, Transport.class); * final List<Transport> transports = new ArrayList<>();
if (transport == null) { * for (String group : groups) {
transport = newTransport; * transports.add(loadTransport(group));
application.resourceFactory.register(groupid, transport); * }
} * Set<InetSocketAddress> addrs = new HashSet();
} * transports.forEach(t -> addrs.addAll(Arrays.asList(t.getRemoteAddresses())));
return transport; * Transport first = transports.get(0);
} * TransportGroupInfo ginfo = application.transportFactory.findGroupInfo(first.getName());
* Transport newTransport = new Transport(groupid, ginfo.getProtocol(),
protected Transport loadTransport(final String group) { * ginfo.getSubprotocol(), application.transportBufferPool, application.transportChannelGroup, this.sncpAddress, addrs);
if (group == null) return null; * synchronized (application.resourceFactory) {
Transport transport; * transport = application.resourceFactory.find(groupid, Transport.class);
synchronized (application.resourceFactory) { * if (transport == null) {
transport = application.resourceFactory.find(group, Transport.class); * transport = newTransport;
if (transport != null) { * application.resourceFactory.register(groupid, transport);
if (this.sncpAddress != null && !this.sncpAddress.equals(transport.getClientAddress())) { * }
throw new RuntimeException(transport + "repeat create on newClientAddress = " + this.sncpAddress + ", oldClientAddress = " + transport.getClientAddress()); * }
} * return transport;
return transport; * }
} *
GroupInfo ginfo = application.findGroupInfo(group); * protected Transport loadTransport(final String group) {
Set<InetSocketAddress> addrs = ginfo.copyAddrs(); * if (group == null) return null;
if (addrs == null) throw new RuntimeException("Not found <group> = " + group + " on <resources> "); * Transport transport;
transport = new Transport(group, ginfo.getProtocol(), application.getWatchFactory(), * synchronized (application.resourceFactory) {
ginfo.getSubprotocol(), application.transportBufferPool, application.transportChannelGroup, this.sncpAddress, addrs); * transport = application.resourceFactory.find(group, Transport.class);
application.resourceFactory.register(group, transport); * if (transport != null) {
} * if (this.sncpAddress != null && !this.sncpAddress.equals(transport.getClientAddress())) {
return transport; * throw new RuntimeException(transport + "repeat create on newClientAddress = " + this.sncpAddress + ", oldClientAddress = " + transport.getClientAddress());
} * }
* return transport;
* }
* TransportGroupInfo ginfo = application.findGroupInfo(group);
* Set<InetSocketAddress> addrs = ginfo.copyAddresses();
* if (addrs == null) throw new RuntimeException("Not found <group> = " + group + " on <resources> ");
* transport = new Transport(group, ginfo.getProtocol(), ginfo.getSubprotocol(), application.transportBufferPool, application.transportChannelGroup, this.sncpAddress, addrs);
* application.resourceFactory.register(group, transport);
* }
* return transport;
* }
*/
protected abstract ClassFilter<Filter> createFilterClassFilter();
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(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;
@@ -461,20 +546,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(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());
} }
} }
@@ -508,6 +593,14 @@ public abstract class NodeServer {
return false; return false;
} }
public ResourceFactory getResourceFactory() {
return resourceFactory;
}
public NodeClassLoader getNodeClassLoader() {
return classLoader;
}
public InetSocketAddress getSncpAddress() { public InetSocketAddress getSncpAddress() {
return sncpAddress; return sncpAddress;
} }
@@ -532,12 +625,12 @@ public abstract class NodeServer {
public void shutdown() throws IOException { public void shutdown() throws IOException {
if (interceptor != null) interceptor.preShutdown(this); if (interceptor != null) interceptor.preShutdown(this);
final StringBuilder sb = logger.isLoggable(Level.INFO) ? new StringBuilder() : null; final StringBuilder sb = logger.isLoggable(Level.INFO) ? new StringBuilder() : null;
localServiceWrappers.forEach(y -> { localServices.forEach(y -> {
long s = System.currentTimeMillis(); long s = System.currentTimeMillis();
y.getService().destroy(y.getConf()); y.destroy(Sncp.getConf(y));
long e = System.currentTimeMillis() - s; long e = System.currentTimeMillis() - s;
if (e > 2 && sb != null) { if (e > 2 && sb != null) {
sb.append(y.toSimpleString()).append(" destroy ").append(e).append("ms").append(LINE_SEPARATOR); sb.append(Sncp.toSimpleString(y, maxNameLength, maxClassNameLength)).append(" destroy ").append(e).append("ms").append(LINE_SEPARATOR);
} }
}); });
if (sb != null && sb.length() > 0) logger.log(Level.INFO, sb.toString()); if (sb != null && sb.length() > 0) logger.log(Level.INFO, sb.toString());
@@ -548,16 +641,16 @@ public abstract class NodeServer {
return (T) server; return (T) server;
} }
public Set<NodeInterceptor.InterceptorServiceWrapper> getInterceptorServiceWrappers() { public Set<Service> getInterceptorServices() {
return new LinkedHashSet<>(interceptorServiceWrappers); return new LinkedHashSet<>(interceptorServices);
} }
public Set<ServiceWrapper> getLocalServiceWrappers() { public Set<Service> getLocalServices() {
return new LinkedHashSet<>(localServiceWrappers); return new LinkedHashSet<>(localServices);
} }
public Set<ServiceWrapper> getRemoteServiceWrappers() { public Set<Service> getRemoteServices() {
return new LinkedHashSet<>(remoteServiceWrappers); return new LinkedHashSet<>(remoteServices);
} }
} }

View File

@@ -5,12 +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.util.*; import org.redkale.util.*;
import org.redkale.util.AnyValue.DefaultAnyValue;
/** /**
* SNCP Server节点的配置Server * SNCP Server节点的配置Server
@@ -40,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
@@ -48,8 +52,8 @@ public class NodeSncpServer extends NodeServer {
return sncpServer == null ? null : sncpServer.getSocketAddress(); return sncpServer == null ? null : sncpServer.getSocketAddress();
} }
public void consumerAccept(ServiceWrapper wrapper) { public void consumerAccept(Service service) {
if (this.consumer != null) this.consumer.accept(wrapper); if (this.consumer != null) this.consumer.accept(service);
} }
@Override @Override
@@ -77,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, null, null, "filters", "filter");
} }
@Override @Override

View File

@@ -0,0 +1,40 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.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");
}
}

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,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);
@@ -184,12 +189,68 @@ 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 ? Class.forName(name) : clazz;
@@ -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

@@ -11,7 +11,7 @@ import java.lang.reflect.Type;
import java.util.Map; import java.util.Map;
/** /**
* Map的反序列化操作类 * Map的反序列化操作类 <br>
* *
* <p> * <p>
* 详情见: https://redkale.org * 详情见: https://redkale.org

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

@@ -61,7 +61,7 @@ public final class BsonConvert extends Convert<BsonReader, BsonWriter> {
//------------------------------ 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) {
@@ -116,7 +116,12 @@ public final class BsonConvert extends Convert<BsonReader, BsonWriter> {
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));
}
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) {

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

@@ -236,11 +236,11 @@ public class BsonWriter extends Writer {
return; return;
} }
char[] chars = Utility.charArray(value); char[] chars = Utility.charArray(value);
if (chars.length > 255) throw new ConvertException("'" + value + "' has very long length"); if (chars.length > 255) throw new ConvertException("'" + value + "' have very long length");
byte[] bytes = new byte[chars.length + 1]; byte[] bytes = new byte[chars.length + 1];
bytes[0] = (byte) chars.length; bytes[0] = (byte) chars.length;
for (int i = 0; i < chars.length; i++) { for (int i = 0; i < chars.length; i++) {
if (chars[i] > Byte.MAX_VALUE) throw new ConvertException("'" + value + "' has double-word"); if (chars[i] > Byte.MAX_VALUE) throw new ConvertException("'" + value + "' have double-word");
bytes[i + 1] = (byte) chars[i]; bytes[i + 1] = (byte) chars[i];
} }
writeTo(bytes); writeTo(bytes);

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

@@ -38,7 +38,7 @@ public class TypeSimpledCoder<R extends Reader, W extends Writer> extends Simple
if (str == null) return null; if (str == null) return null;
try { try {
return Class.forName(str); return Class.forName(str);
} catch (Exception 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());
} }
} }
} }

View File

@@ -48,7 +48,7 @@ public final class JsonConvert extends Convert<JsonReader, JsonWriter> {
//------------------------------ 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) {
@@ -111,7 +111,12 @@ public final class JsonConvert extends Convert<JsonReader, JsonWriter> {
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));
}
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) {

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();
@@ -109,8 +103,12 @@ public class Context {
return charset; return charset;
} }
public void submit(Runnable r) { public Future<?> submitAsync(Runnable r) {
executor.submit(r); return executor.submit(r);
}
public void runAsync(Runnable r) {
executor.execute(r);
} }
public int getBufferCapacity() { public int getBufferCapacity() {
@@ -129,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,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;
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, Resourcable {
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) {
}
@Override
public String resourceName() {
return "";
}
/**
* 值越小越靠前执行
*
* @return int
*/
public int getIndex() {
return 0;
}
@Override
public 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.*;
@@ -34,9 +35,135 @@ public abstract class PrepareServlet<K extends Serializable, C extends Context,
protected final AtomicLong illRequestCounter = new AtomicLong(); //错误请求次数 protected final AtomicLong illRequestCounter = new AtomicLong(); //错误请求次数
protected final Set<S> servlets = new HashSet<>(); private final Object lock1 = new Object();
protected final Map<K, S> mappings = new HashMap<>(); private Set<S> servlets = new HashSet<>();
private final Object lock2 = new Object();
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) {
synchronized (lock1) {
Set<S> newservlets = new HashSet<>(servlets);
newservlets.add(servlet);
this.servlets = newservlets;
}
}
protected void removeServlet(S servlet) {
synchronized (lock1) {
Set<S> newservlets = new HashSet<>(servlets);
newservlets.remove(servlet);
this.servlets = newservlets;
}
}
protected void putMapping(K key, S servlet) {
synchronized (lock2) {
Map<K, S> newmappings = new HashMap<>(mappings);
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;
}
}
protected S mappingServlet(K 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);
}
}
public Filter<C, R, P> removeFilter(Class<? extends Filter<C, R, P>> filterClass) {
return removeFilter(f -> filterClass.equals(f.getClass()));
}
public Filter<C, R, P> removeFilter(String filterName) {
return removeFilter(f -> filterName.equals(f.resourceName()));
}
public Filter<C, R, P> removeFilter(Predicate<Filter<C, R, P>> predicate) {
if (this.headFilter == null || predicate == null) return null;
synchronized (filters) {
Filter filter = this.headFilter;
Filter prev = null;
do {
if (predicate.test(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 filter;
}
}
public abstract void addServlet(S servlet, Object attachment, AnyValue conf, K... mappings); public abstract void addServlet(S servlet, Object attachment, AnyValue conf, K... mappings);
@@ -50,7 +177,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);
@@ -67,7 +196,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);

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);
@@ -92,7 +116,7 @@ public abstract class ProtocolServer {
SocketAddress address = serchannel.receive(buffer); SocketAddress address = serchannel.receive(buffer);
buffer.flip(); buffer.flip();
AsyncConnection conn = AsyncConnection.create(serchannel, address, false, readTimeoutSecond, writeTimeoutSecond); AsyncConnection conn = AsyncConnection.create(serchannel, address, false, readTimeoutSecond, writeTimeoutSecond);
context.submit(new PrepareRunner(context, conn, buffer)); context.runAsync(new PrepareRunner(context, conn, buffer));
} catch (Exception e) { } catch (Exception e) {
context.offerBuffer(buffer); context.offerBuffer(buffer);
} }
@@ -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.submit(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,6 +5,7 @@
*/ */
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;
@@ -34,6 +35,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
@@ -114,10 +119,12 @@ public abstract class Response<C extends Context, R extends Request<C>> {
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) {
this.context.submit(new PrepareRunner(context, channel, null)); this.context.runAsync(new PrepareRunner(context, channel, null));
} else { } else {
try { try {
if (channel.isOpen()) channel.close(); if (channel.isOpen()) channel.close();
@@ -140,10 +147,45 @@ public abstract class Response<C extends Context, R extends Request<C>> {
this.request.createtime = System.currentTimeMillis(); this.request.createtime = System.currentTimeMillis();
} }
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 recycleListener) 代替
*
* @param recycleListener BiConsumer
*
* @deprecated
*/
@Deprecated
public void setRecycleListener(BiConsumer<R, Response<C, R>> recycleListener) { public void setRecycleListener(BiConsumer<R, Response<C, R>> recycleListener) {
this.recycleListener = recycleListener; this.recycleListener = recycleListener;
} }
public void recycleListener(BiConsumer<R, Response<C, R>> recycleListener) {
this.recycleListener = recycleListener;
}
public Object getOutput() { public Object getOutput() {
return output; return output;
} }
@@ -162,12 +204,14 @@ public abstract class Response<C extends Context, R extends Request<C>> {
} }
public void finish(boolean kill) { public void finish(boolean kill) {
if (!this.inited) return; //避免重复关闭
//System.println("耗时: " + (System.currentTimeMillis() - request.createtime)); //System.println("耗时: " + (System.currentTimeMillis() - request.createtime));
if (kill) refuseAlive(); if (kill) refuseAlive();
this.context.responsePool.offer(this); this.context.responsePool.offer(this);
} }
public void finish(final byte[] bs) { public void finish(final byte[] bs) {
if (!this.inited) return; //避免重复关闭
if (this.context.bufferCapacity == bs.length) { if (this.context.bufferCapacity == bs.length) {
ByteBuffer buffer = this.context.pollBuffer(); ByteBuffer buffer = this.context.pollBuffer();
buffer.put(bs); buffer.put(bs);
@@ -179,19 +223,23 @@ public abstract class Response<C extends Context, R extends Request<C>> {
} }
public void finish(ByteBuffer buffer) { public void finish(ByteBuffer buffer) {
if (!this.inited) return; //避免重复关闭
this.channel.write(buffer, buffer, finishHandler); this.channel.write(buffer, buffer, finishHandler);
} }
public void finish(boolean kill, ByteBuffer buffer) { public void finish(boolean kill, ByteBuffer buffer) {
if (!this.inited) return; //避免重复关闭
if (kill) refuseAlive(); if (kill) refuseAlive();
this.channel.write(buffer, buffer, finishHandler); this.channel.write(buffer, buffer, finishHandler);
} }
public void finish(ByteBuffer... buffers) { public void finish(ByteBuffer... buffers) {
if (!this.inited) return; //避免重复关闭
this.channel.write(buffers, buffers, finishHandler2); this.channel.write(buffers, buffers, finishHandler2);
} }
public void finish(boolean kill, ByteBuffer... buffers) { public void finish(boolean kill, ByteBuffer... buffers) {
if (!this.inited) return; //避免重复关闭
if (kill) refuseAlive(); if (kill) refuseAlive();
this.channel.write(buffers, buffers, finishHandler2); this.channel.write(buffers, buffers, finishHandler2);
} }

View File

@@ -15,7 +15,6 @@ 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.AnyValue;
import org.redkale.watch.WatchFactory;
/** /**
* *
@@ -39,9 +38,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 +89,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 {
@@ -109,7 +104,8 @@ public abstract class Server<K extends Serializable, C extends Context, R extend
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.maxbody = config.getIntValue("maxbody", 64 * 1024);
this.bufferCapacity = config.getIntValue("bufferCapacity", 8 * 1024); int bufCapacity = config.getIntValue("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);
@@ -161,7 +157,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 +185,30 @@ 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");
} }
/**
* 销毁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";

View File

@@ -13,7 +13,6 @@ import java.util.concurrent.*;
import java.util.function.Supplier; import java.util.function.Supplier;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import org.redkale.util.ObjectPool; import org.redkale.util.ObjectPool;
import org.redkale.watch.WatchFactory;
/** /**
* 传输客户端 * 传输客户端
@@ -50,8 +49,6 @@ 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;
@@ -62,15 +59,14 @@ public final class Transport {
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, final Collection<InetSocketAddress> addresses) {
this(name, DEFAULT_PROTOCOL, watch, subprotocol, transportBufferPool, transportChannelGroup, clientAddress, addresses); this(name, DEFAULT_PROTOCOL, subprotocol, transportBufferPool, transportChannelGroup, clientAddress, addresses);
} }
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) {
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);
@@ -92,7 +88,7 @@ public final class Transport {
if (first == null) throw new NullPointerException("Collection<Transport> is null or empty"); if (first == null) throw new NullPointerException("Collection<Transport> is null or empty");
//必须按字母排列顺序确保相同内容的transport列表组合的name相同而不会因为list的顺序不同产生不同的name //必须按字母排列顺序确保相同内容的transport列表组合的name相同而不会因为list的顺序不同产生不同的name
this.name = tmpgroup.stream().sorted().collect(Collectors.joining(";")); this.name = tmpgroup.stream().sorted().collect(Collectors.joining(";"));
this.watch = first.watch; //this.watch = first.watch;
this.subprotocol = first.subprotocol; this.subprotocol = first.subprotocol;
this.protocol = first.protocol; this.protocol = first.protocol;
this.tcp = "TCP".equalsIgnoreCase(first.protocol); this.tcp = "TCP".equalsIgnoreCase(first.protocol);
@@ -165,7 +161,7 @@ public final class Transport {
public AsyncConnection pollConnection(SocketAddress addr) { public AsyncConnection pollConnection(SocketAddress addr) {
if (addr == null && remoteAddres.length == 1) addr = remoteAddres[0]; if (addr == null && remoteAddres.length == 1) addr = remoteAddres[0];
final boolean rand = addr == null; final boolean rand = addr == null;
if (rand && remoteAddres.length < 1) throw new RuntimeException("Transport (" + this.name + ") has no remoteAddress list"); if (rand && remoteAddres.length < 1) throw new RuntimeException("Transport (" + this.name + ") have no remoteAddress list");
try { try {
if (tcp) { if (tcp) {
AsynchronousSocketChannel channel = null; AsynchronousSocketChannel channel = null;

View File

@@ -0,0 +1,165 @@
/*
* 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 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<>();
public TransportFactory(ExecutorService executor, ObjectPool<ByteBuffer> bufferPool, AsynchronousChannelGroup channelGroup) {
this.executor = executor;
this.bufferPool = bufferPool;
this.channelGroup = channelGroup;
}
public String findGroupName(InetSocketAddress addr) {
if (addr == null) return null;
return groupAddrs.get(addr);
}
public TransportGroupInfo findGroupInfo2(String group) {
if (group == null) return null;
return groupInfos.get(group);
}
public TransportFactory addGroupInfo(String name, InetSocketAddress... addrs) {
addGroupInfo(new TransportGroupInfo(name, addrs));
return this;
}
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("remotes", info.protocol, info.subprotocol, this.bufferPool, this.channelGroup, sncpAddress, addresses);
}
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);
}
public ExecutorService getExecutor() {
return executor;
}
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,113 @@
/*
* 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) {
if (this.addresses == null) return false;
return this.addresses.contains(addr);
}
public void putAddress(InetSocketAddress addr) {
if (addr == null) return;
if (this.addresses == null) this.addresses = new HashSet<>();
this.addresses.add(addr);
}
public void putAddress(Set<InetSocketAddress> addrs) {
if (addrs == null) return;
if (this.addresses == null) this.addresses = new HashSet<>();
this.addresses.addAll(addrs);
}
@Override
public String toString() {
return JsonConvert.root().convertTo(this);
}
}

View File

@@ -25,8 +25,8 @@ public class WorkThread extends Thread {
this.setDaemon(true); this.setDaemon(true);
} }
public void submit(Runnable runner) { public void runAsync(Runnable runner) {
executor.submit(runner); executor.execute(runner);
} }
public ExecutorService getExecutor() { public ExecutorService getExecutor() {

View File

@@ -8,12 +8,14 @@ package org.redkale.net.http;
import java.io.IOException; import java.io.IOException;
/** /**
* 默认Servlet, 没有配置RestHttpServlet实现类则使用该默认类 * 默认Servlet, 没有配置RestServlet实现类则使用该默认类
* <p> * <p>
* 详情见: https://redkale.org * 详情见: https://redkale.org
* *
* @deprecated
* @author zhangjx * @author zhangjx
*/ */
@Deprecated
public class DefaultRestServlet extends RestHttpServlet<Object> { public class DefaultRestServlet extends RestHttpServlet<Object> {
@Override @Override
@@ -21,9 +23,4 @@ public class DefaultRestServlet extends RestHttpServlet<Object> {
return new Object(); return new Object();
} }
@Override
public boolean authenticate(int moduleid, int actionid, HttpRequest request, HttpResponse response) throws IOException {
return true;
}
} }

View File

@@ -22,31 +22,31 @@ import static jdk.internal.org.objectweb.asm.Opcodes.*;
import org.redkale.service.RetResult; import org.redkale.service.RetResult;
/** /**
* 直接只用HttpServlet代替, 将在1.9版本移除
* *
* <p> * <p>
* 详情见: https://redkale.org * 详情见: https://redkale.org
* *
* @deprecated
* @author zhangjx * @author zhangjx
*/ */
@Deprecated
public abstract class HttpBaseServlet extends HttpServlet { 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 使用。 * 使用 org.redkale.util.AuthIgnore 代替
* 当标记为 &#64;AuthIgnore 的方法在执行execute之前不会调用authenticate 方法。
* *
* <p> * <p>
* 详情见: https://redkale.org * 详情见: https://redkale.org
* *
* @deprecated
* @author zhangjx * @author zhangjx
*/ */
@Inherited @Inherited
@Documented @Documented
@Target({METHOD, TYPE}) @Target({METHOD, TYPE})
@Retention(RUNTIME) @Retention(RUNTIME)
@Deprecated
protected @interface AuthIgnore { protected @interface AuthIgnore {
} }
@@ -66,18 +66,20 @@ public abstract class HttpBaseServlet extends HttpServlet {
} }
/** /**
* 配合 &#64;WebMapping 使用。 *
* 用于对&#64;WebMapping方法中参数描述 * 使用 org.redkale.net.http.HttpParam 代替
* *
* <p> * <p>
* 详情见: https://redkale.org * 详情见: https://redkale.org
* *
* @deprecated
* @author zhangjx * @author zhangjx
*/ */
@Documented @Documented
@Target({METHOD}) @Target({METHOD})
@Retention(RUNTIME) @Retention(RUNTIME)
@Repeatable(WebParams.class) @Repeatable(WebParams.class)
@Deprecated
protected @interface WebParam { protected @interface WebParam {
String name(); //参数名 String name(); //参数名
@@ -102,16 +104,17 @@ public abstract class HttpBaseServlet extends HttpServlet {
} }
/** /**
* 使用 WebMapping 替代。 * 使用 org.redkale.net.http.HttpMapping 替代。
* <p> * <p>
* 详情见: https://redkale.org * 详情见: https://redkale.org
* *
* @deprecated
* @author zhangjx * @author zhangjx
*/ */
@Deprecated
@Documented @Documented
@Target({METHOD}) @Target({METHOD})
@Retention(RUNTIME) @Retention(RUNTIME)
@Deprecated
protected @interface WebAction { protected @interface WebAction {
int actionid() default 0; int actionid() default 0;
@@ -130,17 +133,18 @@ public abstract class HttpBaseServlet extends HttpServlet {
} }
/** /**
* 配合 HttpBaseServlet 使用 * 使用 org.redkale.net.http.HttpMapping 替代
* 用于对&#64;WebServlet对应的url进行细分。 其url必须是包含WebServlet中定义的前缀 且不能是正则表达式
* *
* <p> * <p>
* 详情见: https://redkale.org * 详情见: https://redkale.org
* *
* @deprecated
* @author zhangjx * @author zhangjx
*/ */
@Documented @Documented
@Target({METHOD}) @Target({METHOD})
@Retention(RUNTIME) @Retention(RUNTIME)
@Deprecated
protected @interface WebMapping { protected @interface WebMapping {
int actionid() default 0; int actionid() default 0;
@@ -159,18 +163,15 @@ public abstract class HttpBaseServlet extends HttpServlet {
} }
/** /**
* 配合 HttpBaseServlet 使用 * 使用 org.redkale.net.http.HttpMapping.cacheseconds 替代
* 当标记为 &#64;HttpCacheable 的方法使用response.finish的参数将被缓存一段时间(默认值 seconds=15秒)。
* 通常情况下 &#64;HttpCacheable 需要与 &#64;AuthIgnore 一起使用,没有标记&#64;AuthIgnore的方法一般输出的结果与当前用户信息有关。
*
* <p>
* 详情见: https://redkale.org
* *
* @deprecated
* @author zhangjx * @author zhangjx
*/ */
@Documented @Documented
@Target({METHOD}) @Target({METHOD})
@Retention(RUNTIME) @Retention(RUNTIME)
@Deprecated
protected @interface HttpCacheable { protected @interface HttpCacheable {
/** /**
@@ -183,37 +184,122 @@ public abstract class HttpBaseServlet extends HttpServlet {
private Map.Entry<String, Entry>[] mappings; private Map.Entry<String, Entry>[] mappings;
public boolean preExecute(HttpRequest request, HttpResponse response) throws IOException { private final HttpServlet authSuccessServlet = new HttpServlet() {
@Override
public void execute(HttpRequest request, HttpResponse response) throws IOException {
Entry entry = (Entry) request.getAttribute("_redkale_entry");
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.setAttribute("_redkale_entry", entry);
if (entry.ignore) {
authSuccessServlet.execute(request, response);
} else {
HttpBaseServlet.this.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; 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 @Override
public final void execute(HttpRequest request, HttpResponse response) throws IOException { public final void execute(HttpRequest request, HttpResponse response) throws IOException {
if (!preExecute(request, response)) return; preExecute(request, response, preSuccessServlet);
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;
}
if (entry.ignore || authenticate(entry.moduleid, entry.actionid, request, response)) {
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);
}
return;
}
}
throw new IOException(this.getClass().getName() + " not found method for URI(" + request.getRequestURI() + ")");
} }
public final void preInit(HttpContext context, AnyValue config) { public final void preInit(HttpContext context, AnyValue config) {
@@ -233,8 +319,6 @@ public abstract class HttpBaseServlet extends HttpServlet {
public final void postDestroy(HttpContext context, AnyValue config) { public final void postDestroy(HttpContext context, AnyValue config) {
} }
public abstract boolean authenticate(int moduleid, int actionid, HttpRequest request, HttpResponse response) throws IOException;
protected void setHeader(HttpRequest request, String name, Serializable value) { protected void setHeader(HttpRequest request, String name, Serializable value) {
request.header.setValue(name, String.valueOf(value)); request.header.setValue(name, String.valueOf(value));
} }
@@ -280,7 +364,7 @@ public abstract class HttpBaseServlet extends HttpServlet {
final String[] methods = mapping == null ? action.methods() : mapping.methods(); final String[] methods = mapping == null ? action.methods() : mapping.methods();
if (nameset.containsKey(name)) { if (nameset.containsKey(name)) {
if (nameset.get(name) != clz) continue; if (nameset.get(name) != clz) continue;
throw new RuntimeException(this.getClass().getSimpleName() + " has two same " + WebMapping.class.getSimpleName() + "(" + name + ")"); throw new RuntimeException(this.getClass().getSimpleName() + " have two same " + WebMapping.class.getSimpleName() + "(" + name + ")");
} }
nameset.put(name, clz); nameset.put(name, clz);
map.put(name, new Entry(typeIgnore, serviceid, actionid, name, methods, method, createHttpServlet(method))); map.put(name, new Entry(typeIgnore, serviceid, actionid, name, methods, method, createHttpServlet(method)));
@@ -304,7 +388,7 @@ public abstract class HttpBaseServlet extends HttpServlet {
try { try {
Class.forName(newDynName.replace('/', '.')); Class.forName(newDynName.replace('/', '.'));
newDynName += "_" + (++i); newDynName += "_" + (++i);
} catch (Exception ex) { } catch (Throwable ex) {
break; break;
} }
} }

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,28 +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) {
this.servlets.forEach(s -> { super.init(context, config); //必须要执行
if (s instanceof WebSocketServlet) { Collection<HttpServlet> servlets = getServlets();
((WebSocketServlet) s).preInit(context, getServletConf(s)); servlets.forEach(s -> {
} else if (s instanceof HttpBaseServlet) { s.preInit(context, getServletConf(s));
((HttpBaseServlet) s).preInit(context, getServletConf(s));
}
s.init(context, getServletConf(s)); s.init(context, getServletConf(s));
}); });
final WatchFactory watch = context.getWatchFactory();
if (watch != null) {
this.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 +223,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) Class.forName(resServlet).newInstance();
} catch (Exception 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);
} }
@@ -82,17 +234,48 @@ 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 = this.mappings.isEmpty() ? null : this.mappings.get(uri); HttpServlet servlet;
if (servlet == null && this.regArray != null) { if (request.isWebSocket()) {
for (SimpleEntry<Predicate<String>, HttpServlet> en : regArray) { servlet = wsmappings.get(uri);
if (en.getKey().test(uri)) { if (servlet == null && this.regWsArray != null) {
servlet = en.getValue(); 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);
@@ -131,14 +314,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()) {
super.mappings.put(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);
@@ -148,7 +345,7 @@ public class HttpPrepareServlet extends PrepareServlet<String, HttpContext, Http
} }
setServletConf(servlet, conf); setServletConf(servlet, conf);
servlet._prefix = prefix.toString(); servlet._prefix = prefix.toString();
this.servlets.add(servlet); putServlet(servlet);
} }
} }
@@ -174,16 +371,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);
this.servlets.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>
@@ -34,6 +34,7 @@ public class HttpRequest extends Request<HttpContext> {
protected static final String SESSIONID_NAME = "JSESSIONID"; protected 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,13 +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; //仅供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;
} }
@@ -129,10 +143,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":
@@ -230,6 +244,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
* *
@@ -276,6 +335,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对象
* *
@@ -289,7 +386,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}";
@@ -323,7 +420,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;
@@ -335,6 +432,11 @@ 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.header.clear(); this.header.clear();
this.params.clear(); this.params.clear();
@@ -393,7 +495,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;
} }

View File

@@ -13,6 +13,7 @@ import java.nio.channels.*;
import java.nio.file.*; import java.nio.file.*;
import java.text.*; import java.text.*;
import java.util.*; import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicLong;
import java.util.logging.Level; import java.util.logging.Level;
import org.redkale.convert.json.JsonConvert; import org.redkale.convert.json.JsonConvert;
@@ -47,6 +48,8 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
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);
@@ -177,6 +180,11 @@ 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;
}
/** /**
* 增加Cookie值 * 增加Cookie值
* *
@@ -202,7 +210,7 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
} }
/** /**
* 创建AsyncHandler实例将非字符串对象以JSON格式输出字符串以文本输出 * 创建AsyncHandler实例
* *
* @return AsyncHandler * @return AsyncHandler
*/ */
@@ -221,6 +229,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格式输出
* *
@@ -311,12 +334,109 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
finish(convert.convertTo(context.getBufferSupplier(), ret)); finish(convert.convertTo(context.getBufferSupplier(), ret));
} }
/**
* 将CompletableFuture的结果对象以JSON格式输出
*
* @param future 输出对象的句柄
*/
public void finishJson(final CompletableFuture future) {
finishJson(request.getJsonConvert(), future);
}
/**
* 将CompletableFuture的结果对象以JSON格式输出
*
* @param convert 指定的JsonConvert
* @param future 输出对象的句柄
*/
public void finishJson(final JsonConvert convert, final CompletableFuture future) {
future.whenComplete((v, e) -> {
if (e != null) {
context.getLogger().log(Level.WARNING, "Servlet occur, forece to close channel. request = " + request, e);
finish(500, null);
return;
}
if (v instanceof CharSequence) {
finish(v.toString());
} else if (v instanceof org.redkale.service.RetResult) {
finishJson(convert, (org.redkale.service.RetResult) v);
} else {
finishJson(convert, v);
}
});
}
/**
* 将CompletableFuture的结果对象以JSON格式输出
*
* @param convert 指定的JsonConvert
* @param type 指定的类型
* @param future 输出对象的句柄
*/
public void finishJson(final JsonConvert convert, final Type type, final CompletableFuture future) {
future.whenComplete((v, e) -> {
if (e != null) {
context.getLogger().log(Level.WARNING, "Servlet occur, forece to close channel. request = " + request, e);
finish(500, null);
return;
}
if (v instanceof CharSequence) {
finish(v.toString());
} else if (v instanceof HttpResult) {
finishJson(convert, (HttpResult) v);
} else if (v instanceof org.redkale.service.RetResult) {
finishJson(convert, (org.redkale.service.RetResult) v);
} else {
finishJson(convert, type, v);
}
});
}
/**
* 将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());
}
}
/** /**
* 将指定字符串以响应结果输出 * 将指定字符串以响应结果输出
* *
* @param obj 输出内容 * @param obj 输出内容
*/ */
public void finish(String obj) { public void finish(String obj) {
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()) {
final ByteBuffer headbuf = createHeader(); final ByteBuffer headbuf = createHeader();
@@ -358,6 +478,7 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
* @param message 输出内容 * @param message 输出内容
*/ */
public void finish(int status, String message) { public void finish(int status, String message) {
if (isClosed()) return;
this.status = status; this.status = status;
if (status != 200) super.refuseAlive(); if (status != 200) super.refuseAlive();
finish(message); finish(message);
@@ -384,6 +505,7 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
*/ */
@Override @Override
public void finish(final byte[] bs) { public void finish(final byte[] bs) {
if (isClosed()) return; //避免重复关闭
if (this.context.getBufferCapacity() == bs.length) { if (this.context.getBufferCapacity() == bs.length) {
ByteBuffer buffer = this.context.pollBuffer(); ByteBuffer buffer = this.context.pollBuffer();
buffer.put(bs); buffer.put(bs);
@@ -412,6 +534,7 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
*/ */
@Override @Override
public void finish(boolean kill, ByteBuffer buffer) { public void finish(boolean kill, ByteBuffer buffer) {
if (isClosed()) return; //避免重复关闭
if (!this.headsended) { if (!this.headsended) {
this.contentLength = buffer == null ? 0 : buffer.remaining(); this.contentLength = buffer == null ? 0 : buffer.remaining();
ByteBuffer headbuf = createHeader(); ByteBuffer headbuf = createHeader();
@@ -444,6 +567,7 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
*/ */
@Override @Override
public void finish(boolean kill, ByteBuffer... buffers) { public void finish(boolean kill, ByteBuffer... buffers) {
if (isClosed()) return; //避免重复关闭
if (bufferHandler != null) { if (bufferHandler != null) {
ByteBuffer[] bufs = bufferHandler.execute(this, buffers); ByteBuffer[] bufs = bufferHandler.execute(this, buffers);
if (bufs != null) buffers = bufs; if (bufs != null) buffers = bufs;
@@ -493,6 +617,37 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
} }
} }
/**
* 异步输出指定内容
*
* @param <A> 泛型
* @param buffers 输出内容
* @param attachment 异步回调参数
* @param handler 异步回调函数
*/
public <A> void sendBody(ByteBuffer[] buffers, A attachment, AsyncHandler<Integer, A> handler) {
if (!this.headsended) {
if (this.contentLength < 0) {
int len = 0;
if (buffers != null && buffers.length > 0) {
for (ByteBuffer b : buffers) {
len += b.remaining();
}
}
this.contentLength = len;
}
ByteBuffer headbuf = createHeader();
headbuf.flip();
if (buffers == null || buffers.length == 0) {
super.send(headbuf, attachment, handler);
} else {
super.send(Utility.unshift(buffers, headbuf), attachment, handler);
}
} else {
super.send(buffers, attachment, handler);
}
}
/** /**
* 将指定文件按响应结果输出 * 将指定文件按响应结果输出
* *
@@ -605,6 +760,8 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
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) {

View File

@@ -0,0 +1,128 @@
/*
* 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.Serializable;
import java.net.HttpCookie;
import java.util.*;
import org.redkale.convert.json.JsonConvert;
/**
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
* @param <T> 结果对象的类型
*/
public class HttpResult<T> {
public static final String SESSIONID_COOKIENAME = HttpRequest.SESSIONID_NAME;
private Map<String, String> headers;
private List<HttpCookie> cookies;
private String contentType;
private T result;
private int status = 0; //不设置则为 200
private String message;
public HttpResult() {
}
public HttpResult(T result) {
this.result = result;
}
public HttpResult<T> header(String name, Serializable value) {
if (this.headers == null) this.headers = new HashMap<>();
this.headers.put(name, String.valueOf(value));
return this;
}
public HttpResult<T> cookie(HttpCookie cookie) {
if (this.cookies == null) this.cookies = new ArrayList<>();
this.cookies.add(cookie);
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() {
return headers;
}
public void setHeaders(Map<String, String> headers) {
this.headers = headers;
}
public List<HttpCookie> getCookies() {
return cookies;
}
public void setCookies(List<HttpCookie> cookies) {
this.cookies = cookies;
}
public String getContentType() {
return contentType;
}
public void setContentType(String contentType) {
this.contentType = contentType;
}
public T getResult() {
return result;
}
public void setResult(T result) {
this.result = result;
}
public int getStatus() {
return status;
}
public void setStatus(int status) {
this.status = status;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
@Override
public String toString() {
return JsonConvert.root().convertTo(this);
}
}

View File

@@ -11,9 +11,9 @@ import java.nio.ByteBuffer;
import java.util.*; import java.util.*;
import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicLong;
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 +23,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
@@ -47,6 +47,87 @@ public final class HttpServer extends Server<String, HttpContext, HttpRequest, H
return ((HttpPrepareServlet) this.prepare).resourceHttpServlet; return ((HttpPrepareServlet) this.prepare).resourceHttpServlet;
} }
/**
* 删除HttpFilter
*
* @param filterName HttpFilter名称
*
* @return HttpFilter
*/
public HttpFilter removeFilter(String filterName) {
return (HttpFilter) this.prepare.removeFilter(filterName);
}
/**
* 删除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 removeFilter(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;
}
/** /**
* 添加HttpServlet * 添加HttpServlet
* *
@@ -90,46 +171,63 @@ 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 webSocketType WebSocket的类型
* @param serviceType Service的类型 * @param prefix url前缀
* @param service Service对象 * @param conf 配置信息
* @param baseServletClass RestHttpServlet基类
* @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 Class<S> webSocketType, final String prefix, final AnyValue conf) {
return addRestServlet(name, serviceType, service, baseServletClass, prefix, null); T servlet = Rest.createRestWebSocketServlet(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 service Service对象
* @param serviceType Service的类型 * @param userType 用户数据类型
* @param service Service对象 * @param baseServletType RestServlet基类
* @param baseServletClass RestHttpServlet基类 * @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 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(null, service, userType, baseServletType, prefix);
RestHttpServlet servlet = null; }
/**
* 添加RestServlet
*
* @param <S> Service
* @param <T> HttpServlet
* @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 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) {
@@ -138,7 +236,8 @@ public final class HttpServer extends Server<String, HttpContext, HttpRequest, H
} }
} }
final boolean first = servlet == null; final boolean first = servlet == null;
if (servlet == null) servlet = Rest.createRestServlet(baseServletClass, serviceType); if (servlet == null) servlet = Rest.createRestServlet(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,
@@ -249,11 +348,11 @@ public final class HttpServer extends Server<String, HttpContext, HttpRequest, H
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 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));
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 = 1800_0001;
public static final int RET_METHOD_ERROR = 1800_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 {
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(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,12 +5,15 @@
*/ */
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 static java.lang.annotation.RetentionPolicy.*; import java.lang.annotation.*;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
/** /**
* RestMapping 的多用类 * 配合 HttpServlet 使用
* 用于指定HttpRequest.currentUser的数据类型<br>
* 注意 数据类型是JavaBean则必须要用javax.persistence.Id标记主键字段用于确定用户ID
*
* <p> * <p>
* 详情见: https://redkale.org * 详情见: https://redkale.org
* *
@@ -18,9 +21,10 @@ import static java.lang.annotation.RetentionPolicy.*;
*/ */
@Inherited @Inherited
@Documented @Documented
@Target({METHOD}) @Target({TYPE})
@Retention(RUNTIME) @Retention(RUNTIME)
public @interface RestMappings { public @interface HttpUserType {
Class value();
RestMapping[] 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

@@ -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

@@ -8,32 +8,19 @@ package org.redkale.net.http;
import java.io.*; import java.io.*;
/** /**
* 使用 RestServlet 代替
* *
* 详情见: https://redkale.org * 详情见: https://redkale.org
* *
* @author zhangjx * @author zhangjx
* @deprecated
* @param <T> 当前用户对象类型 * @param <T> 当前用户对象类型
*/ */
public abstract class RestHttpServlet<T> extends HttpBaseServlet { @Deprecated
public class RestHttpServlet<T> extends HttpServlet {
protected abstract T currentUser(HttpRequest req) throws IOException; protected T currentUser(HttpRequest req) throws IOException {
return 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());
if (output.getResult() instanceof File) {
response.finish((File) output.getResult());
} else if (output.getResult() instanceof String) {
response.finish((String) output.getResult());
} else {
response.finishJson(output.getResult());
}
} }
} }

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

@@ -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

@@ -10,13 +10,16 @@ import java.net.HttpCookie;
import java.util.*; import java.util.*;
/** /**
* 使用 HttpResult 代替
* *
* <p> * <p>
* 详情见: https://redkale.org * 详情见: https://redkale.org
* *
* @deprecated
* @author zhangjx * @author zhangjx
* @param <T> 结果对象的类型 * @param <T> 结果对象的类型
*/ */
@Deprecated
public class RestOutput<T> { public class RestOutput<T> {
private Map<String, String> headers; private Map<String, String> headers;
@@ -27,6 +30,10 @@ public class RestOutput<T> {
private T result; private T result;
private int status = 0; //不设置则为 200
private String message;
public RestOutput() { public RestOutput() {
} }
@@ -34,14 +41,16 @@ public class RestOutput<T> {
this.result = result; this.result = result;
} }
public void addHeader(String name, Serializable value) { public RestOutput<T> addHeader(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;
} }
public void addCookie(HttpCookie cookie) { public RestOutput<T> addCookie(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;
} }
public Map<String, String> getHeaders() { public Map<String, String> getHeaders() {
@@ -76,4 +85,20 @@ public class RestOutput<T> {
this.result = result; this.result = result;
} }
public int getStatus() {
return status;
}
public void setStatus(int status) {
this.status = status;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
} }

View File

@@ -32,6 +32,7 @@ public @interface RestParam {
* name='&#38;'表示当前用户; <br> * name='&#38;'表示当前用户; <br>
* name='#'表示截取uri最后一段; <br> * name='#'表示截取uri最后一段; <br>
* name='#xxx:'表示从uri中/pipes/xxx:v/截取xxx:的值 <br> * name='#xxx:'表示从uri中/pipes/xxx:v/截取xxx:的值 <br>
* 若方法名以find、delete开头且方法的参数只有一个且参数类型是基本数据类型或String则默认值为"#" <br>
* *
* @return String * @return String
*/ */

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,74 @@
/*
* 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 single() default true;
/**
* WebScoket服务器给客户端进行ping操作的间隔时间, 单位: 秒, 默认值60秒
*
* @return int
*/
int liveinterval() default 60;
/**
* 是否屏蔽该类的转换
*
* @return 默认false
*/
boolean ignore() default false;
/**
* 同&#64;WebServlet的repair属性
*
* @return 默认true
*/
boolean repair() default true;
/**
* 备注描述
*
* @return 备注描述
*/
String comment() default "";
}

View File

@@ -1,43 +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.*;
/**
* 功能同JSR 315 (java-servlet 3.0) 规范中的 @WebInitParam
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*/
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface WebInitParam {
/**
* 参数名
*
* @return String
*/
String name();
/**
* 参数值
*
* @return String
*/
String value();
/**
* 参数描述
*
* @return String
*/
String description() default "";
}

View File

@@ -49,13 +49,6 @@ public @interface WebServlet {
*/ */
int moduleid() default 0; int moduleid() default 0;
/**
* 参数
*
* @return WebInitParam[]
*/
WebInitParam[] initParams() default {};
/** /**
* 备注描述 * 备注描述
* *

View File

@@ -9,7 +9,9 @@ import org.redkale.net.http.WebSocketPacket.FrameType;
import java.io.*; import java.io.*;
import java.net.*; import java.net.*;
import java.util.*; import java.util.*;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.*;
import java.util.stream.Stream;
import org.redkale.convert.json.JsonConvert;
import org.redkale.net.*; import org.redkale.net.*;
import org.redkale.util.Comment; import org.redkale.util.Comment;
@@ -19,7 +21,7 @@ import org.redkale.util.Comment;
* WebSocket 有两种模式: * WebSocket 有两种模式:
* 1) 普通模式: 协议上符合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被关闭后回调此方法。
@@ -27,7 +29,7 @@ import org.redkale.util.Comment;
* *
* 2) 原始二进制模式: 此模式有别于HTML5规范可以视为原始的TCP连接。通常用于音频视频通讯场景。其流程顺序如下: * 2) 原始二进制模式: 此模式有别于HTML5规范可以视为原始的TCP连接。通常用于音频视频通讯场景。其流程顺序如下:
* 2.1 onOpen 若返回null视为WebSocket的连接不合法强制关闭WebSocket连接通常用于判断登录态。 * 2.1 onOpen 若返回null视为WebSocket的连接不合法强制关闭WebSocket连接通常用于判断登录态。
* 2.2 createGroupid 若返回null视为WebSocket的连接不合法强制关闭WebSocket连接通常用于判断用户权限是否符合。 * 2.2 createWebSocketid 若返回null视为WebSocket的连接不合法强制关闭WebSocket连接通常用于判断用户权限是否符合。
* 2.3 onRead WebSocket成功连接后回调此方法 由此方法处理原始的TCP连接 需要业务代码去控制WebSocket的关闭。 * 2.3 onRead WebSocket成功连接后回调此方法 由此方法处理原始的TCP连接 需要业务代码去控制WebSocket的关闭。
* 二进制模式下 以上方法都应该被重载。 * 二进制模式下 以上方法都应该被重载。
* </pre></blockquote> * </pre></blockquote>
@@ -35,8 +37,10 @@ import org.redkale.util.Comment;
* 详情见: 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
@@ -66,72 +70,37 @@ 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;//不可能为空
private final long createtime = System.currentTimeMillis(); JsonConvert _jsonConvert; //不可能为空
private final Map<String, Object> attributes = new ConcurrentHashMap<>(); java.lang.reflect.Type _messageTextType; //不可能为空
private long createtime = System.currentTimeMillis();
private Map<String, Object> attributes = new HashMap<>(); //非线程安全
protected WebSocket() { protected WebSocket() {
} }
//---------------------------------------------------------------- //----------------------------------------------------------------
/** public final CompletableFuture<Integer> sendPing() {
* 给自身发送消息体, 包含二进制/文本
*
* @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)); 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() {
@@ -139,179 +108,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); return send(message, true);
} }
/** /**
* 给自身发送二进制消息 * 给自身发送消息, 消息类型是String或byte[]或可JavaBean对象
* *
* @param data 不可为空 * @param message 不可为空, 只能是String或byte[]或可JavaBean对象
* @param last 是否最后一条
*
* @return 0表示成功 非0表示错误码
*/
public final int send(byte[] data, boolean last) {
return send(new WebSocketPacket(data, last));
}
/**
* 给自身发送消息, 消息类型是String或byte[]
*
* @param message 不可为空, 只能是String或者byte[]
* @param last 是否最后一条 * @param last 是否最后一条
* *
* @return 0表示成功 非0表示错误码 * @return 0表示成功 非0表示错误码
*/ */
public final int send(Serializable message, boolean last) { public final CompletableFuture<Integer> send(Object message, boolean last) {
return send(new WebSocketPacket(message, last)); if (message instanceof CompletableFuture) {
return ((CompletableFuture) message).thenCompose((json) -> {
if (json == null || json instanceof CharSequence || json instanceof byte[]) {
return sendPacket(new WebSocketPacket((Serializable) json, last));
} else if (message instanceof WebSocketPacket) {
return sendPacket((WebSocketPacket) message);
} else {
return sendPacket(new WebSocketPacket(_jsonConvert, 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(_jsonConvert, message, last));
}
}
/**
* 给自身发送消息, 消息类型是JavaBean对象
*
* @param convert JsonConvert
* @param message 不可为空, 只能是JSON对象
*
* @return 0表示成功 非0表示错误码
*/
public final CompletableFuture<Integer> send(JsonConvert convert, Object message) {
return send(convert, message, true);
}
/**
* 给自身发送消息, 消息类型是JavaBean对象
*
* @param convert JsonConvert
* @param message 不可为空, 只能是JavaBean对象
* @param last 是否最后一条
*
* @return 0表示成功 非0表示错误码
*/
public final CompletableFuture<Integer> send(JsonConvert convert, Object message, boolean last) {
if (message instanceof CompletableFuture) {
return ((CompletableFuture) message).thenCompose((json) -> sendPacket(new WebSocketPacket(convert == null ? _jsonConvert : convert, json, last)));
}
return sendPacket(new WebSocketPacket(convert == null ? _jsonConvert : 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:" + userid() + " 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 message 不可为空
* @param text 不可为空 * @param userids Serializable[]
* *
* @return 为0表示成功 其他值表示异常 * @return 为0表示成功 其他值表示异常
*/ */
public final int sendEachMessage(Serializable groupid, String text) { public final CompletableFuture<Integer> sendMessage(Object message, G... userids) {
return sendEachMessage(groupid, text, true); return sendMessage(message, true, userids);
} }
/** /**
* 给指定groupid的WebSocketGroup下所有WebSocket节点发送二进制消息 * 给指定userid的WebSocket节点发送 二进制消息/文本消息/JavaBean对象消息
* *
* @param groupid groupid * @param message 不可为空
* @param data 不可为空
*
* @return 为0表示成功 其他值表示异常
*/
public final int sendEachMessage(Serializable groupid, byte[] data) {
return WebSocket.this.sendEachMessage(groupid, data, true);
}
/**
* 给指定groupid的WebSocketGroup下所有WebSocket节点发送文本消息
*
* @param groupid groupid
* @param text 不可为空
* @param last 是否最后一条 * @param last 是否最后一条
* @param userids Serializable[]
* *
* @return 为0表示成功 其他值表示异常 * @return 为0表示成功 其他值表示异常
*/ */
public final int sendEachMessage(Serializable groupid, String text, boolean last) { public final CompletableFuture<Integer> sendMessage(Object message, boolean last, G... userids) {
return sendMessage(groupid, false, text, 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(" + _jsonConvert.convertTo(message) + ")");
* @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节点发送文本消息
*
* @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节点发送文本消息
*
* @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);
}
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; 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(" + _jsonConvert.convertTo(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 属性名
@@ -320,11 +286,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 属性名
@@ -332,26 +298,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 userid() {
return _groupid; return _userid;
} }
/** /**
@@ -359,7 +326,7 @@ public abstract class WebSocket {
* *
* @return sessionid * @return sessionid
*/ */
public final Serializable getSessionid() { public final String getSessionid() {
return _sessionid; return _sessionid;
} }
@@ -383,52 +350,56 @@ public abstract class WebSocket {
//------------------------------------------------------------------- //-------------------------------------------------------------------
/** /**
* 获取当前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();
} }
//------------------------------------------------------------------- //-------------------------------------------------------------------
/** /**
* 返回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才需要重写此方法 * 标记为WebSocketBinary才需要重写此方法
@@ -438,30 +409,64 @@ public abstract class WebSocket {
public void onRead(AsyncConnection channel) { public void onRead(AsyncConnection channel) {
} }
/**
* WebSokcet连接成功后的回调方法
*/
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) { /**
} * 接收到二进制消息的回调方法
*
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) {
} }
/**
* 获取最后一次发送消息的时间
*
* @return long
*/
public long getLastSendTime() {
return this._runner == null ? 0 : this._runner.lastSendTime;
}
/** /**
* 显式地关闭WebSocket * 显式地关闭WebSocket
*/ */
@@ -471,6 +476,6 @@ public abstract class WebSocket {
@Override @Override
public String toString() { public String toString() {
return "ws" + Objects.hashCode(this) + "@" + _remoteAddr; return this.userid() + "@" + _remoteAddr;
} }
} }

View File

@@ -11,88 +11,237 @@ 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 java.util.stream.*;
import org.redkale.convert.json.JsonConvert;
import static org.redkale.net.http.WebSocket.RETCODE_GROUP_EMPTY;
import org.redkale.util.*; import org.redkale.util.*;
/** /**
* *
* <p> 详情见: https://redkale.org * <p>
* 详情见: https://redkale.org
*
* @author zhangjx * @author zhangjx
*/ */
public final class WebSocketEngine { public final class WebSocketEngine {
//全局自增长ID
private static final AtomicInteger sequence = new AtomicInteger(); private static final AtomicInteger sequence = new AtomicInteger();
//Engine自增长序号ID
private final int index; private final int index;
//当前WebSocket对应的Engine
private final String engineid; private final String engineid;
//当前WebSocket对应的Node
protected final WebSocketNode node; protected final WebSocketNode node;
private final Map<Serializable, WebSocketGroup> containers = new ConcurrentHashMap<>(); //HttpContext
protected final HttpContext context;
//JsonConvert
protected final JsonConvert convert;
protected final boolean single; //是否单用户单连接
//在线用户ID对应的WebSocket组用于单用户单连接模式
private final Map<Serializable, WebSocket> websockets = new ConcurrentHashMap<>();
//在线用户ID对应的WebSocket组用于单用户多连接模式
private final Map<Serializable, List<WebSocket>> websockets2 = new ConcurrentHashMap<>();
//用于PING的定时器
private ScheduledThreadPoolExecutor scheduler; private ScheduledThreadPoolExecutor scheduler;
//日志
protected final Logger logger; protected final Logger logger;
//FINEST日志级别
protected final boolean finest; protected final boolean finest;
protected WebSocketEngine(String engineid, WebSocketNode node, Logger logger) { private int liveinterval;
protected WebSocketEngine(String engineid, boolean single, HttpContext context, int liveinterval, WebSocketNode node, Logger logger) {
this.engineid = engineid; this.engineid = engineid;
this.single = single;
this.context = context;
this.convert = context.getJsonConvert();
this.node = node; this.node = node;
this.liveinterval = liveinterval;
this.logger = logger; this.logger = logger;
this.index = sequence.getAndIncrement(); this.index = sequence.getAndIncrement();
this.finest = logger.isLoggable(Level.FINEST); this.finest = logger.isLoggable(Level.FINEST);
} }
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;
scheduler.scheduleWithFixedDelay(() -> { scheduler.scheduleWithFixedDelay(() -> {
getWebSocketGroups().stream().forEach(x -> x.sendEachPing()); getLocalWebSockets().forEach(x -> x.sendPing());
}, delay, liveinterval, TimeUnit.SECONDS); }, delay, interval, TimeUnit.SECONDS);
if (finest) logger.finest(this.getClass().getSimpleName() + "(" + engineid + ")" + " start keeplive(delay:" + delay + ", interval:" + liveinterval + "s) scheduler executor"); if (finest) logger.finest(this.getClass().getSimpleName() + "(" + engineid + ")" + " start keeplive(delay:" + delay + ", interval:" + interval + "s) scheduler executor");
}
void destroy(AnyValue conf) {
if (scheduler != null) scheduler.shutdownNow();
} }
void add(WebSocket socket) { void add(WebSocket socket) {
WebSocketGroup group = containers.get(socket._groupid); if (single) {
if (group == null) { websockets.put(socket._userid, socket);
group = new WebSocketGroup(socket._groupid); } else { //非线程安全, 在常规场景中无需锁
containers.putIfAbsent(socket._groupid, group); List<WebSocket> list = websockets2.get(socket._userid);
if (list == null) {
list = new CopyOnWriteArrayList<>();
websockets2.put(socket._userid, list);
}
list.add(socket);
} }
group.add(socket); if (node != null) node.connect(socket._userid);
if (node != null) node.connect(socket._groupid, engineid, socket.toString());
} }
void remove(WebSocket socket) { void remove(WebSocket socket) {
final WebSocketGroup group = containers.get(socket._groupid); Serializable userid = socket._userid;
if (group == null) { if (single) {
if (node != null) node.disconnect(socket._groupid, engineid); websockets.remove(userid);
return; if (node != null) node.disconnect(userid);
} } else { //非线程安全, 在常规场景中无需锁
group.remove(socket); List<WebSocket> list = websockets2.get(userid);
if (group.isEmpty()) { if (list != null) {
containers.remove(socket._groupid); list.remove(socket);
if (node != null) node.disconnect(socket._groupid, engineid); if (list.isEmpty()) {
websockets2.remove(userid);
if (node != null) node.disconnect(userid);
}
}
} }
} }
Collection<WebSocketGroup> getWebSocketGroups() { public CompletableFuture<Integer> broadcastMessage(final Object message, final boolean last) {
return containers.values(); if (message instanceof CompletableFuture) {
return ((CompletableFuture) message).thenCompose((json) -> broadcastMessage(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.convert, message, last));
packet.setSendBuffers(packet.encode(context.getBufferSupplier()));
CompletableFuture<Integer> future = null;
if (single) {
for (WebSocket websocket : websockets.values()) {
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) {
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()) {
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) {
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;
}
} }
public WebSocketGroup getWebSocketGroup(Serializable groupid) { public CompletableFuture<Integer> sendMessage(final Object message, final boolean last, final Serializable... userids) {
return containers.get(groupid); 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.convert, 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;
}
} }
void close() { Collection<WebSocket> getLocalWebSockets() {
if (scheduler != null) scheduler.shutdownNow(); if (single) return websockets.values();
List<WebSocket> list = new ArrayList<>();
websockets2.values().forEach(x -> list.addAll(x));
return list;
}
//适用于单用户单连接模式
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);
}
//适用于单用户多连接模式
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() {

View File

@@ -1,139 +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, Serializable message, boolean last) {
if (recent) {
return recentWebSocket.send(message, last);
} else {
return sendEach(message, last);
}
}
public final int sendEach(Serializable 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(Serializable message) {
return sendRecent(message, true);
}
public final int sendRecent(WebSocketPacket packet) {
return recentWebSocket.send(packet);
}
public final int sendEach(Serializable message, boolean last) {
int rs = 0;
for (WebSocket s : list) {
rs |= s.send(message, last);
}
return rs;
}
public final int sendRecent(Serializable message, boolean last) {
return recentWebSocket.send(message, last);
}
@Override
public String toString() {
return "{groupid: " + groupid + ", list.size: " + (list == null ? -1 : list.size()) + "}";
}
}

View File

@@ -30,208 +30,206 @@ public abstract class WebSocketNode {
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> source; //如果不是分布式(没有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.userid()));
if (sncpNodeAddresses != null && localSncpAddress != null) sncpNodeAddresses.removeSetItem("redkale_sncpnodes", localSncpAddress);
}
protected abstract int sendMessage(@RpcTargetAddress InetSocketAddress targetAddress, Serializable groupid, boolean recent, Serializable 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 source == null ? null : source.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, Serializable 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;
if ((recent && rscode == 0) || remoteNode == null || source == null) { });
if (finest) {
if ((recent && rscode == 0)) {
logger.finest("websocket want send recent message success");
} else {
logger.finest("websocket remote node is null");
}
}
return rscode;
}
//-----------------------发送远程的-----------------------------
Collection<InetSocketAddress> addrs = source.getCollection(groupid);
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) { public final CompletableFuture<Integer> sendMessage(Object message, final Serializable... userids) {
return sendMessage(groupid, false, text); return sendMessage(message, true, userids);
} }
public final int sendEachMessage(Serializable groupid, String text, boolean last) { /**
return sendMessage(groupid, false, text, last); * 向指定用户发送消息,先发送本地连接,再发送远程连接 <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) { /**
return sendMessage(groupid, true, text); * 广播消息, 给所有人发消息
*
* @param message 消息内容
*
* @return 为0表示成功 其他值表示部分发送异常
*/
public final CompletableFuture<Integer> broadcastMessage(final Object message) {
return broadcastMessage(message, true);
} }
public final int sendRecentMessage(Serializable groupid, String text, boolean last) { /**
return sendMessage(groupid, true, text, last); * 广播消息, 给所有人发消息
*
* @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) { private CompletableFuture<Integer> sendOneMessage(final Object message, final boolean last, final Serializable userid) {
return sendMessage(groupid, recent, text, true); 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);
public final int sendMessage(Serializable groupid, boolean recent, String text, boolean last) { if (this.sncpNodeAddresses == null || this.remoteNode == null) {
return sendMessage(groupid, recent, (Serializable) text, last); if (finest) logger.finest("websocket remote node is null");
} //没有CacheSource就不会有分布式节点
return localFuture == null ? CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY) : localFuture;
//-------------------------------------------------------------------------------- }
public final int sendEachMessage(Serializable groupid, byte[] data) { //远程节点发送消息
return sendMessage(groupid, false, data); 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);
public final int sendEachMessage(Serializable groupid, byte[] data, boolean last) { if (addrs == null || addrs.isEmpty()) return CompletableFuture.completedFuture(0);
return sendMessage(groupid, false, data, last); CompletableFuture<Integer> future = null;
} for (InetSocketAddress addr : addrs) {
if (addr == null || addr.equals(localSncpAddress)) continue;
public final int sendRecentMessage(Serializable groupid, byte[] data) { future = future == null ? remoteNode.sendMessage(addr, message, last, userid)
return sendMessage(groupid, true, data); : future.thenCombine(remoteNode.sendMessage(addr, message, last, userid), (a, b) -> a | b);
} }
return future == null ? CompletableFuture.completedFuture(0) : future;
public final int sendRecentMessage(Serializable groupid, byte[] data, boolean last) { });
return sendMessage(groupid, true, data, last); return localFuture == null ? remoteFuture : localFuture.thenCombine(remoteFuture, (a, b) -> a | b);
}
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, (Serializable) data, last);
} }
} }

View File

@@ -7,10 +7,17 @@ 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.ConvertMask;
import org.redkale.convert.json.JsonConvert;
/** /**
* *
* <p> 详情见: https://redkale.org * <p>
* 详情见: https://redkale.org
*
* @author zhangjx * @author zhangjx
*/ */
public final class WebSocketPacket { public final class WebSocketPacket {
@@ -51,6 +58,16 @@ public final class WebSocketPacket {
protected boolean last = true; protected boolean last = true;
protected Object sendJson;
JsonConvert sendConvert;
ByteBuffer[] sendBuffers;
ConvertMask receiveMasker;
ByteBuffer[] receiveBuffers;
public WebSocketPacket() { public WebSocketPacket() {
} }
@@ -76,6 +93,31 @@ public final class WebSocketPacket {
this.last = fin; this.last = fin;
} }
public WebSocketPacket(JsonConvert convert, Object json, boolean fin) {
this.type = FrameType.TEXT;
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();
}
return rs;
}
public WebSocketPacket(byte[] data) { public WebSocketPacket(byte[] data) {
this(FrameType.BINARY, data, true); this(FrameType.BINARY, data, true);
} }
@@ -120,4 +162,226 @@ public final class WebSocketPacket {
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 + ']') : "") + "]";
} }
/**
* 消息编码
*
* @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,22 @@ 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.json.JsonConvert;
/** /**
* 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,34 +38,34 @@ 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 boolean wsbinary; private final boolean wsbinary;
public WebSocketRunner(Context context, WebSocket webSocket, AsyncConnection channel, final boolean wsbinary) { private final BiConsumer<WebSocket, Object> restMessageConsumer; //主要供RestWebSocket使用
protected long lastSendTime;
protected final JsonConvert convert;
WebSocketRunner(Context context, WebSocket webSocket, BiConsumer<WebSocket, Object> messageConsumer, AsyncConnection channel, final boolean wsbinary) {
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; 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(); this.convert = context.getJsonConvert();
} }
@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分钟
@@ -93,48 +97,90 @@ public class WebSocketRunner implements Runnable {
return; return;
} }
readBuffer.flip(); readBuffer.flip();
ByteBuffer[] exBuffers = null;
if (!readBuffers.isEmpty()) {
exBuffers = readBuffers.toArray(new ByteBuffer[readBuffers.size()]);
readBuffers.clear();
recentExBuffer = null;
for (ByteBuffer b : exBuffers) {
b.flip();
}
}
try { try {
ByteBuffer[] exBuffers = null; WebSocketPacket packet = new WebSocketPacket().decode(context.getLogger(), readBuffer, exBuffers);
if (!readBuffers.isEmpty()) {
exBuffers = readBuffers.toArray(new ByteBuffer[readBuffers.size()]);
readBuffers.clear();
recentExBuffer = null;
for (ByteBuffer b : exBuffers) {
b.flip();
}
}
WebSocketPacket packet = coder.decode(readBuffer, exBuffers);
if (exBuffers != null) {
for (ByteBuffer b : exBuffers) {
context.offerBuffer(b);
}
}
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); Object message = convert.convertFrom(webSocket._messageTextType, packet.receiveMasker, packet.receiveBuffers);
} if (readBuffer != null) {
webSocket._group.setRecentWebSocket(webSocket); readBuffer.clear();
try { channel.read(readBuffer, null, this);
if (packet.type == FrameType.TEXT) { }
webSocket.onMessage(packet.getPayload()); try {
} else if (packet.type == FrameType.BINARY) { if (restMessageConsumer != null) { //主要供RestWebSocket使用
webSocket.onMessage(packet.getBytes()); restMessageConsumer.accept(webSocket, message);
} else if (packet.type == FrameType.PONG) { } else {
webSocket.onPong(packet.getBytes()); webSocket.onMessage(message, packet.last);
} else if (packet.type == FrameType.PING) { }
webSocket.onPing(packet.getBytes()); } catch (Exception e) {
context.getLogger().log(Level.SEVERE, "WebSocket onTextMessage error (" + packet + ")", e);
}
} else if (packet.type == FrameType.BINARY) {
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 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 +202,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 = packet.sendBuffers != null ? packet.duplicateSendBuffers() : 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 +270,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 +278,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 +297,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,13 +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.service.WebSocketNodeService; import org.redkale.convert.json.JsonConvert;
import org.redkale.service.*;
import org.redkale.util.*; import org.redkale.util.*;
/** /**
@@ -27,10 +31,7 @@ import org.redkale.util.*;
* / \ * / \
* / \ * / \
* / \ * / \
* WebSocketGroup1 WebSocketGroup2 * WebSocket1 WebSocket2
* / \ / \
* / \ / \
* WebSocket1 WebSocket2 WebSocket3 WebSocket4
* *
* </pre></blockquote> * </pre></blockquote>
* *
@@ -39,7 +40,7 @@ import org.redkale.util.*;
* *
* @author zhangjx * @author zhangjx
*/ */
public abstract class WebSocketServlet extends HttpServlet { public abstract class WebSocketServlet extends HttpServlet implements Resourcable {
@Comment("WebScoket服务器给客户端进行ping操作的间隔时间, 单位: 秒") @Comment("WebScoket服务器给客户端进行ping操作的间隔时间, 单位: 秒")
public static final String WEBPARAM__LIVEINTERVAL = "liveinterval"; public static final String WEBPARAM__LIVEINTERVAL = "liveinterval";
@@ -51,6 +52,159 @@ public abstract class WebSocketServlet extends HttpServlet {
private final MessageDigest digest = getMessageDigest(); private final MessageDigest digest = getMessageDigest();
@Comment("是否用于二进制流传输")
protected final boolean wsbinary = getClass().getAnnotation(WebSocketBinary.class) != null;
private final BiConsumer<WebSocket, Object> restMessageConsumer = createRestOnMessageConsumer();
protected Type messageTextType; //RestWebSocket时会被修改
protected boolean single = true; //是否单用户单连接
protected int liveinterval = DEFAILT_LIVEINTERVAL;
@Resource
protected JsonConvert jsonConvert; //Rest.createRestWebSocketServlet 需要过滤掉已有的@Resource
@Resource(name = "$")
protected WebSocketNode node; //Rest.createRestWebSocketServlet 需要过滤掉已有的@Resource
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) {
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, 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._jsonConvert = jsonConvert;
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(), wsbinary);
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");
@@ -59,102 +213,4 @@ public abstract class WebSocketServlet extends HttpServlet {
} }
} }
@Comment("是否用于二进制流传输")
protected final boolean wsbinary = getClass().getAnnotation(WebSocketBinary.class) != null;
@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() + "-[" + name() + "]", 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();
}
public String name() {
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._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.submit(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

@@ -1,156 +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.sncp;
import java.util.*;
import java.util.stream.Collectors;
import org.redkale.service.Service;
import org.redkale.util.*;
/**
* Service对象的封装类
*
*
* 详情见: https://redkale.org
*
* @author zhangjx
* @param <T> Service的子类
*/
public final class ServiceWrapper<T extends Service> implements Comparable<ServiceWrapper> {
private static volatile int maxClassNameLength = 0;
private static volatile int maxNameLength = 0;
private final T service;
private final AnyValue conf;
private final String sncpGroup; //自身的组节点名 可能为null
private final Set<String> groups; //所有的组节点,包含自身
private final String name;
private final boolean remote;
private final Class[] types;
public ServiceWrapper(T service, String name, String sncpGroup, Set<String> groups, AnyValue conf) {
this(null, service, name, sncpGroup, groups, conf);
}
@SuppressWarnings("unchecked")
public ServiceWrapper(Class<T> type, T service, String name, String sncpGroup, Set<String> groups, AnyValue conf) {
this.service = service;
this.conf = conf;
this.sncpGroup = sncpGroup;
this.groups = groups;
this.name = name;
this.remote = Sncp.isRemote(service);
ResourceType rty = service.getClass().getAnnotation(ResourceType.class);
this.types = rty == null ? new Class[]{type == null ? (Class<T>) service.getClass() : type} : rty.value();
maxNameLength = Math.max(maxNameLength, name.length());
StringBuilder s = new StringBuilder();
if (this.types.length == 1) {
s.append(types[0].getName());
} else {
s.append('[');
s.append(Arrays.asList(this.types).stream().map((Class t) -> t.getName()).collect(Collectors.joining(",")));
s.append(']');
}
maxClassNameLength = Math.max(maxClassNameLength, s.length() + 1);
}
public static Class[] parseTypes(final Class<? extends Service> servicetype) {
ResourceType rty = servicetype.getAnnotation(ResourceType.class);
return rty == null ? new Class[]{servicetype} : rty.value();
}
@Override
public String toString() {
return toSimpleString();
}
public String toSimpleString() {
StringBuilder sb = new StringBuilder();
sb.append(remote ? "RemoteService" : "LocalService ");
int len;
if (types.length == 1) {
sb.append("(type= ").append(types[0].getName());
len = maxClassNameLength - types[0].getName().length();
} else {
StringBuilder s = new StringBuilder();
s.append('[');
s.append(Arrays.asList(this.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++) {
sb.append(' ');
}
sb.append(", name='").append(name).append("'");
for (int i = 0; i < maxNameLength - name.length(); i++) {
sb.append(' ');
}
sb.append(")");
return sb.toString();
}
@Override
public boolean equals(Object obj) {
if (obj == this) return true;
if (obj == null) return false;
if (!(obj instanceof ServiceWrapper)) return false;
ServiceWrapper other = (ServiceWrapper) obj;
return (this.types[0].equals(other.types[0]) && this.remote == other.remote && this.name.equals(other.name) && Objects.equals(this.sncpGroup, other.sncpGroup));
}
@Override
public int hashCode() {
int hash = 3;
hash = 67 * hash + Objects.hashCode(this.types[0]);
hash = 67 * hash + Objects.hashCode(this.sncpGroup);
hash = 67 * hash + Objects.hashCode(this.name);
hash = 67 * hash + (this.remote ? 1 : 0);
return hash;
}
@Override
public int compareTo(ServiceWrapper o) {
int rs = this.types[0].getName().compareTo(o.types[0].getName());
if (rs == 0) rs = this.name.compareTo(o.name);
return rs;
}
public Class[] getTypes() {
return types;
}
public Service getService() {
return service;
}
public AnyValue getConf() {
return conf;
}
public String getName() {
return name;
}
public boolean isRemote() {
return remote;
}
public Set<String> getGroups() {
return groups;
}
}

View File

@@ -10,15 +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 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.*;
@@ -55,11 +52,6 @@ public abstract class Sncp {
private Sncp() { private Sncp() {
} }
public static long nodeid(InetSocketAddress ip) {
byte[] bytes = ip.getAddress().getAddress();
return ((0L + ip.getPort()) << 32) | ((0xffffffff & bytes[0]) << 24) | ((0xffffff & bytes[1]) << 16) | ((0xffff & bytes[2]) << 8) | (0xff & bytes[3]);
}
public static DLong hash(final java.lang.reflect.Method method) { public static DLong hash(final java.lang.reflect.Method method) {
if (method == null) return DLong.ZERO; if (method == null) return DLong.ZERO;
StringBuilder sb = new StringBuilder(); //不能使用method.toString() 因为包含declaringClass信息导致接口与实现类的方法hash不一致 StringBuilder sb = new StringBuilder(); //不能使用method.toString() 因为包含declaringClass信息导致接口与实现类的方法hash不一致
@@ -97,6 +89,38 @@ 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) {
if (service == null) return null;
Resource res = service.getClass().getAnnotation(Resource.class);
return res == null ? null : res.name();
}
public static Class getServiceType(Service service) {
ResourceType rt = service.getClass().getAnnotation(ResourceType.class);
return rt == null ? service.getClass() : rt.value();
}
public static Class getResourceType(Service service) {
if (service == null) return null;
ResourceType type = service.getClass().getAnnotation(ResourceType.class);
return type == null ? getServiceType(service) : type.value();
}
public static AnyValue getConf(Service service) {
if (service == null) return null;
try {
Field ts = service.getClass().getDeclaredField(FIELDPREFIX + "_conf");
ts.setAccessible(true);
return (AnyValue) ts.get(service);
} catch (Exception e) {
throw new RuntimeException(service + " not found " + FIELDPREFIX + "_conf");
}
}
public static SncpClient getSncpClient(Service service) { public static SncpClient getSncpClient(Service service) {
if (service == null) return null; if (service == null) return null;
try { try {
@@ -108,26 +132,61 @@ public abstract class Sncp {
} }
} }
public static Transport getSameGroupTransport(Service service) { static void checkAsyncModifier(Class param, Method method) {
if (service == null) return null; if (param == AsyncHandler.class) return;
try { if (Modifier.isFinal(param.getModifiers())) {
Field ts = service.getClass().getDeclaredField(FIELDPREFIX + "_sameGroupTransport"); throw new RuntimeException("AsyncHandler Type Parameter on {" + method + "} cannot final modifier");
ts.setAccessible(true); }
return (Transport) ts.get(service); if (!Modifier.isPublic(param.getModifiers())) {
} catch (Exception e) { throw new RuntimeException("AsyncHandler Type Parameter on {" + method + "} must be public modifier");
throw new RuntimeException(service + " not found " + FIELDPREFIX + "_sameGroupTransport"); }
if (param.isInterface()) return;
boolean constructorflag = false;
for (Constructor c : param.getDeclaredConstructors()) {
if (c.getParameterCount() == 0) {
int mod = c.getModifiers();
if (Modifier.isPublic(mod) || Modifier.isProtected(mod)) {
constructorflag = true;
break;
}
}
}
if (param.getDeclaredConstructors().length == 0) constructorflag = true;
if (!constructorflag) throw new RuntimeException(param + " must have a empty parameter Constructor");
for (Method m : param.getMethods()) {
if (m.getName().equals("completed") && Modifier.isFinal(m.getModifiers())) {
throw new RuntimeException(param + "'s completed method cannot final modifier");
} else if (m.getName().equals("failed") && Modifier.isFinal(m.getModifiers())) {
throw new RuntimeException(param + "'s failed method cannot final modifier");
} else if (m.getName().equals("sncp_getParams") && Modifier.isFinal(m.getModifiers())) {
throw new RuntimeException(param + "'s sncp_getParams method cannot final modifier");
} else if (m.getName().equals("sncp_setParams") && Modifier.isFinal(m.getModifiers())) {
throw new RuntimeException(param + "'s sncp_setParams method cannot final modifier");
} else if (m.getName().equals("sncp_setFuture") && Modifier.isFinal(m.getModifiers())) {
throw new RuntimeException(param + "'s sncp_setFuture method cannot final modifier");
} else if (m.getName().equals("sncp_getFuture") && Modifier.isFinal(m.getModifiers())) {
throw new RuntimeException(param + "'s sncp_getFuture method cannot final modifier");
}
} }
} }
public static Transport[] getDiffGroupTransports(Service service) { public static String toSimpleString(final Service service, int maxNameLength, int maxClassNameLength) {
if (service == null) return null; StringBuilder sb = new StringBuilder();
try { sb.append(isRemote(service) ? "RemoteService" : "LocalService ");
Field ts = service.getClass().getDeclaredField(FIELDPREFIX + "_diffGroupTransports"); int len;
ts.setAccessible(true); Class type = getResourceType(service);
return (Transport[]) ts.get(service); String name = getResourceName(service);
} catch (Exception e) { sb.append("(type= ").append(type.getName());
throw new RuntimeException(service + " not found " + FIELDPREFIX + "_diffGroupTransports"); len = maxClassNameLength - type.getName().length();
for (int i = 0; i < len; i++) {
sb.append(' ');
} }
sb.append(", name='").append(name).append("'");
for (int i = 0; i < maxNameLength - name.length(); i++) {
sb.append(' ');
}
sb.append(")");
return sb.toString();
} }
/** /**
@@ -139,37 +198,27 @@ public abstract class Sncp {
* } * }
* *
* &#64;RpcMultiRun(selfrun = false) * &#64;RpcMultiRun(selfrun = false)
public void createSomeThing(TestBean bean){ * public void createSomeThing(TestBean bean){
//do something * //do something
} * }
*
&#64;RpcMultiRun * &#64;RpcMultiRun
public String updateSomeThing(String id){ * public String updateSomeThing(String id){
return "hello" + id; * return "hello" + id;
} * }
} * }
</pre></blockquote> * </pre></blockquote>
* *
* <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{
* *
* &#64;Resource * private AnyValue _redkale_conf;
* private BsonConvert _redkale_bsonConvert;
*
* &#64;Resource
* private JsonConvert _redkale_jsonConvert;
*
* 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;
@@ -184,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, _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, _diffGroupTransports, 0, true, true, false, bean); * if (diffrunnable) _redkale_client.remoteDiffGroup(0, true, true, false, bean);
* } * }
* *
* &#64;Override * &#64;Override
@@ -196,9 +245,9 @@ public abstract class Sncp {
* &#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){
* String rs = super.updateSomeThing(id); * String rs = super.updateSomeThing(id);
* if (_redkale_client== null) return; * if (_redkale_client== null) return rs;
* if (samerunnable) _redkale_client.remoteSameGroup(_redkale_bsonConvert, _redkale_jsonConvert, _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, _diffGroupTransports, 1, true, true, false, id); * if (diffrunnable) _redkale_client.remoteDiffGroup(1, true, true, false, id);
* return rs; * return rs;
* } * }
* } * }
@@ -206,40 +255,38 @@ public abstract class Sncp {
* *
* 创建Service的本地模式Class * 创建Service的本地模式Class
* *
* @param <T> Service子类 * @param <T> Service子类
* @param name 资源名 * @param name 资源名
* @param serviceClass 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> serviceClass) { protected static <T extends Service> Class<? extends T> createLocalServiceClass(final String name, final Class<T> serviceImplClass) {
if (serviceClass == null) return null; if (serviceImplClass == null) return null;
if (!Service.class.isAssignableFrom(serviceClass)) return serviceClass; if (!Service.class.isAssignableFrom(serviceImplClass)) return serviceImplClass;
int mod = serviceClass.getModifiers(); int mod = serviceImplClass.getModifiers();
if (!java.lang.reflect.Modifier.isPublic(mod)) return serviceClass; if (!java.lang.reflect.Modifier.isPublic(mod)) return serviceImplClass;
if (java.lang.reflect.Modifier.isAbstract(mod)) return serviceClass; if (java.lang.reflect.Modifier.isAbstract(mod)) return serviceImplClass;
final List<Method> methods = SncpClient.parseMethod(serviceClass); final List<Method> methods = SncpClient.parseMethod(serviceImplClass);
final String supDynName = serviceClass.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 anyValueDesc = Type.getDescriptor(AnyValue.class);
final String jsonConvertDesc = Type.getDescriptor(JsonConvert.class);
final String sncpDynDesc = Type.getDescriptor(SncpDyn.class); final String sncpDynDesc = Type.getDescriptor(SncpDyn.class);
final String transportDesc = Type.getDescriptor(Transport.class);
final String transportsDesc = Type.getDescriptor(Transport[].class);
ClassLoader loader = Sncp.class.getClassLoader(); ClassLoader loader = Sncp.class.getClassLoader();
String newDynName = supDynName.substring(0, supDynName.lastIndexOf('/') + 1) + LOCALPREFIX + serviceClass.getSimpleName(); String newDynName = supDynName.substring(0, supDynName.lastIndexOf('/') + 1) + LOCALPREFIX + serviceImplClass.getSimpleName();
if (!name.isEmpty()) { if (!name.isEmpty()) {
boolean normal = true; boolean normal = true;
for (char ch : name.toCharArray()) { for (char ch : name.toCharArray()) {
if (!((ch >= '0' && ch <= '9') || ch == '_' || (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z'))) normal = false; if (!((ch >= '0' && ch <= '9') || ch == '_' || (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z'))) normal = false;
} }
if (!normal) throw new RuntimeException(serviceImplClass + "'s resource name is illegal, must be 0-9 _ a-z A-Z");
newDynName += "_" + (normal ? name : hash(name)); newDynName += "_" + (normal ? name : hash(name));
} }
try { try {
return (Class<T>) Class.forName(newDynName.replace('/', '.')); return (Class<T>) Class.forName(newDynName.replace('/', '.'));
} catch (Exception ex) { } catch (Throwable ex) {
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
ClassWriter cw = new ClassWriter(COMPUTE_FRAMES); ClassWriter cw = new ClassWriter(COMPUTE_FRAMES);
@@ -259,57 +306,25 @@ public abstract class Sncp {
av0.visitEnd(); av0.visitEnd();
} }
{ //给新类加上 原有的Annotation { //给新类加上 原有的Annotation
for (Annotation ann : serviceClass.getAnnotations()) { for (Annotation ann : serviceImplClass.getAnnotations()) {
if (ann instanceof Resource || ann instanceof SncpDyn || ann instanceof ResourceType) continue; if (ann instanceof Resource || ann instanceof SncpDyn || ann instanceof ResourceType) continue;
visitAnnotation(cw.visitAnnotation(Type.getDescriptor(ann.annotationType()), true), ann); visitAnnotation(cw.visitAnnotation(Type.getDescriptor(ann.annotationType()), true), ann);
} }
} }
{ {
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 = serviceClass.getAnnotation(ResourceType.class);
if (rty == null) {
av1.visit(null, Type.getType(Type.getDescriptor(serviceClass)));
} 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, FIELDPREFIX + "_bsonConvert", bsonConvertDesc, null, null); fv = cw.visitField(ACC_PRIVATE, FIELDPREFIX + "_conf", anyValueDesc, 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 + "_sameGroupTransport", transportDesc, null, null);
fv.visitEnd();
}
{
fv = cw.visitField(ACC_PRIVATE, FIELDPREFIX + "_diffGroupTransports", transportsDesc, null, null);
fv.visitEnd(); 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_PUBLIC, "<init>", "()V", null, null)); mv = new AsmMethodVisitor(cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null));
//mv.setDebug(true); //mv.setDebug(true);
@@ -322,16 +337,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);
@@ -362,7 +379,13 @@ public abstract class Sncp {
mv.visitInsn(mrun.samerun() ? ICONST_1 : ICONST_0); mv.visitInsn(mrun.samerun() ? ICONST_1 : ICONST_0);
mv.visitInsn(mrun.diffrun() ? ICONST_1 : ICONST_0); mv.visitInsn(mrun.diffrun() ? ICONST_1 : ICONST_0);
int varindex = 0; int varindex = 0;
boolean handlerFuncFlag = false;
for (Class pt : paramtypes) { for (Class pt : paramtypes) {
if (AsyncHandler.class.isAssignableFrom(pt)) {
if (handlerFuncFlag) throw new RuntimeException(method + " have more than one AsyncHandler type parameter");
checkAsyncModifier(pt, method);
handlerFuncFlag = true;
}
if (pt.isPrimitive()) { if (pt.isPrimitive()) {
if (pt == long.class) { if (pt == long.class) {
mv.visitVarInsn(LLOAD, ++varindex); mv.visitVarInsn(LLOAD, ++varindex);
@@ -403,8 +426,15 @@ public abstract class Sncp {
//mv.setDebug(true); //mv.setDebug(true);
{ //给参数加上 Annotation { //给参数加上 Annotation
final Annotation[][] anns = method.getParameterAnnotations(); final Annotation[][] anns = method.getParameterAnnotations();
boolean handlerAttachFlag = false;
for (int k = 0; k < anns.length; k++) { for (int k = 0; k < anns.length; k++) {
for (Annotation ann : anns[k]) { for (Annotation ann : anns[k]) {
if (ann.annotationType() == RpcAttachment.class) {
if (handlerAttachFlag) {
throw new RuntimeException(method + " have more than one @RpcAttachment parameter");
}
handlerAttachFlag = true;
}
if (ann instanceof SncpDyn || ann instanceof RpcMultiRun) continue; //必须过滤掉 RpcMultiRun、SncpDyn否则生成远程模式Service时会出错 if (ann instanceof SncpDyn || ann instanceof RpcMultiRun) continue; //必须过滤掉 RpcMultiRun、SncpDyn否则生成远程模式Service时会出错
visitAnnotation(mv.visitParameterAnnotation(k, Type.getDescriptor(ann.annotationType()), true), ann); visitAnnotation(mv.visitParameterAnnotation(k, Type.getDescriptor(ann.annotationType()), true), ann);
} }
@@ -494,14 +524,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 = 4; //调用selfrunnable之前的参数个数; _client/_bsonConvert/_jsonConvert/_sameGroupTransport
if (index <= 5) { //第几个 SncpAction if (index <= 5) { //第几个 SncpAction
mv.visitInsn(ICONST_0 + index); mv.visitInsn(ICONST_0 + index);
@@ -524,13 +547,13 @@ public abstract class Sncp {
mv.visitInsn(DUP); mv.visitInsn(DUP);
mv.visitInsn(ICONST_1); mv.visitInsn(ICONST_1);
mv.visitInsn(ICONST_0); //第个参数 samerunnable mv.visitInsn(ICONST_0); //第个参数 samerunnable
mv.visitMethodInsn(INVOKESTATIC, "java/lang/Boolean", "valueOf", "(Z)Ljava/lang/Boolean;", false); mv.visitMethodInsn(INVOKESTATIC, "java/lang/Boolean", "valueOf", "(Z)Ljava/lang/Boolean;", false);
mv.visitInsn(AASTORE); mv.visitInsn(AASTORE);
mv.visitInsn(DUP); mv.visitInsn(DUP);
mv.visitInsn(ICONST_2); mv.visitInsn(ICONST_2);
mv.visitInsn(ICONST_0); //第个参数 diffrunnable mv.visitInsn(ICONST_0); //第个参数 diffrunnable
mv.visitMethodInsn(INVOKESTATIC, "java/lang/Boolean", "valueOf", "(Z)Ljava/lang/Boolean;", false); mv.visitMethodInsn(INVOKESTATIC, "java/lang/Boolean", "valueOf", "(Z)Ljava/lang/Boolean;", false);
mv.visitInsn(AASTORE); mv.visitInsn(AASTORE);
@@ -561,7 +584,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
@@ -570,12 +593,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);
@@ -635,7 +652,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) {
@@ -719,30 +736,40 @@ public abstract class Sncp {
} }
} }
public static <T extends Service> T createSimpleLocalService(final Class<T> serviceImplClass,
final TransportFactory transportFactory, final InetSocketAddress clientSncpAddress, final String... groups) {
return createLocalService("", serviceImplClass, ResourceFactory.root(), transportFactory, clientSncpAddress, Utility.ofSet(groups), null);
}
/** /**
* *
* 创建本地模式Service实例 * 创建本地模式Service实例
* *
* @param <T> Service泛型 * @param <T> Service泛型
* @param name 资源名 * @param name 资源名
* @param executor 线程池 * @param serviceImplClass Service类
* @param resourceFactory 资源容器 * @param resourceFactory ResourceFactory
* @param serviceClass Service类 * @param transportFactory TransportFactory
* @param clientAddress 本地IP地址 * @param clientSncpAddress 本地IP地址
* @param sameGroupTransport 同组的通信组件 * @param groups 所有的组节点,包含自身
* @param diffGroupTransports 异组的通信组件列表 * @param conf 启动配置项
* *
* @return Service的本地模式实例 * @return Service的本地模式实例
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public static <T extends Service> T createLocalService(final String name, final Consumer<Runnable> executor, final ResourceFactory resourceFactory, public static <T extends Service> T createLocalService(
final Class<T> serviceClass, final InetSocketAddress clientAddress, final Transport sameGroupTransport, final Collection<Transport> diffGroupTransports) { final String name,
final Class<T> serviceImplClass,
final ResourceFactory resourceFactory,
final TransportFactory transportFactory,
final InetSocketAddress clientSncpAddress,
final Set<String> groups,
final AnyValue conf) {
try { try {
final Class newClazz = createLocalServiceClass(name, serviceClass); final Class newClazz = createLocalServiceClass(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 {
@@ -752,25 +779,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(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, serviceClass, clientAddress, remoteTransport);
} }
if (remoteService != null) field.set(rs, remoteService); if (remoteService != null) field.set(rs, remoteService);
} }
@@ -781,53 +791,25 @@ 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, serviceClass, 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 + "_sameGroupTransport"); Field c = newClazz.getDeclaredField(FIELDPREFIX + "_conf");
c.setAccessible(true); c.setAccessible(true);
c.set(rs, sameGroupTransport); c.set(rs, conf);
}
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) {
@@ -835,114 +817,96 @@ public abstract class Sncp {
} catch (Exception ex) { } catch (Exception ex) {
throw new RuntimeException(ex); throw new RuntimeException(ex);
} }
}
public static <T extends Service> T createSimpleRemoteService(final Class<T> serviceImplClass,
final TransportFactory transportFactory, final InetSocketAddress clientSncpAddress, final String... groups) {
return createRemoteService("", 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{
* *
* &#64;Resource * private AnyValue _redkale_conf;
* private BsonConvert _redkale_bsonConvert;
*
* &#64;Resource
* private JsonConvert _redkale_jsonConvert;
*
* 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>
* *
* 创建远程模式的Service实例 * 创建远程模式的Service实例
* *
* @param <T> Service泛型 * @param <T> Service泛型
* @param name 资源名 * @param name 资源名
* @param executor 线程池 * @param serviceTypeOrImplClass Service类
* @param serviceClass Service类 * @param transportFactory TransportFactory
* @param clientAddress 本地IP地址 * @param clientAddress 本地IP地址
* @param transport 通信组件 * @param groups 所有的组节点,包含自身
* @param conf 启动配置项
* *
* @return Service的远程模式实例 * @return Service的远程模式实例
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public static <T extends Service> T createRemoteService(final String name, final Consumer<Runnable> executor, final Class<T> serviceClass,
final InetSocketAddress clientAddress, final Transport transport) { public static <T extends Service> T createRemoteService(
if (serviceClass == null) return null; final String name,
if (!Service.class.isAssignableFrom(serviceClass)) return null; final Class<T> serviceTypeOrImplClass,
int mod = serviceClass.getModifiers(); final TransportFactory transportFactory,
boolean realed = !(java.lang.reflect.Modifier.isAbstract(mod) || serviceClass.isInterface()); final InetSocketAddress clientAddress,
final Set<String> groups,
final AnyValue conf) {
if (serviceTypeOrImplClass == null) return null;
if (!Service.class.isAssignableFrom(serviceTypeOrImplClass)) return null;
int mod = serviceTypeOrImplClass.getModifiers();
boolean realed = !(java.lang.reflect.Modifier.isAbstract(mod) || serviceTypeOrImplClass.isInterface());
if (!java.lang.reflect.Modifier.isPublic(mod)) return null; if (!java.lang.reflect.Modifier.isPublic(mod)) return null;
final String supDynName = serviceClass.getName().replace('.', '/'); final String supDynName = serviceTypeOrImplClass.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 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 anyValueDesc = Type.getDescriptor(AnyValue.class); final String anyValueDesc = Type.getDescriptor(AnyValue.class);
ClassLoader loader = Sncp.class.getClassLoader(); ClassLoader loader = Sncp.class.getClassLoader();
String newDynName = supDynName.substring(0, supDynName.lastIndexOf('/') + 1) + REMOTEPREFIX + serviceClass.getSimpleName(); String newDynName = supDynName.substring(0, supDynName.lastIndexOf('/') + 1) + REMOTEPREFIX + serviceTypeOrImplClass.getSimpleName();
try { try {
Class newClazz = Class.forName(newDynName.replace('/', '.')); Class newClazz = Class.forName(newDynName.replace('/', '.'));
T rs = (T) newClazz.newInstance(); T rs = (T) newClazz.newInstance();
SncpClient client = new SncpClient(name, serviceClass, rs, executor, true, realed ? createLocalServiceClass(name, serviceClass) : serviceClass, clientAddress); SncpClient client = new SncpClient(name, serviceTypeOrImplClass, rs, transportFactory, true, realed ? createLocalServiceClass(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 (Exception ex) { } catch (Throwable ex) {
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
ClassWriter cw = new ClassWriter(COMPUTE_FRAMES); ClassWriter cw = new ClassWriter(COMPUTE_FRAMES);
@@ -950,7 +914,7 @@ public abstract class Sncp {
AsmMethodVisitor mv; AsmMethodVisitor mv;
AnnotationVisitor av0; AnnotationVisitor av0;
cw.visit(V1_8, ACC_PUBLIC + ACC_FINAL + ACC_SUPER, newDynName, null, serviceClass.isInterface() ? "java/lang/Object" : supDynName, serviceClass.isInterface() ? new String[]{supDynName} : null); cw.visit(V1_8, ACC_PUBLIC + ACC_FINAL + ACC_SUPER, newDynName, null, serviceTypeOrImplClass.isInterface() ? "java/lang/Object" : supDynName, serviceTypeOrImplClass.isInterface() ? new String[]{supDynName} : null);
{ {
av0 = cw.visitAnnotation("Ljavax/annotation/Resource;", true); av0 = cw.visitAnnotation("Ljavax/annotation/Resource;", true);
av0.visit("name", name); av0.visit("name", name);
@@ -958,18 +922,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 = serviceClass.getAnnotation(ResourceType.class);
if (rty == null) {
av1.visit(null, Type.getType(Type.getDescriptor(serviceClass)));
} else {
for (Class cl : rty.value()) {
av1.visit(null, Type.getType(Type.getDescriptor(cl)));
}
}
av1.visitEnd();
}
av0.visitEnd(); av0.visitEnd();
} }
{ {
@@ -978,40 +932,24 @@ public abstract class Sncp {
av0.visitEnd(); av0.visitEnd();
} }
{ //给新类加上 原有的Annotation { //给新类加上 原有的Annotation
for (Annotation ann : serviceClass.getAnnotations()) { for (Annotation ann : serviceTypeOrImplClass.getAnnotations()) {
if (ann instanceof Resource || ann instanceof SncpDyn || ann instanceof ResourceType) continue; if (ann instanceof Resource || ann instanceof SncpDyn || ann instanceof ResourceType) continue;
visitAnnotation(cw.visitAnnotation(Type.getDescriptor(ann.annotationType()), true), ann); visitAnnotation(cw.visitAnnotation(Type.getDescriptor(ann.annotationType()), true), ann);
} }
} }
{ {
fv = cw.visitField(ACC_PRIVATE, FIELDPREFIX + "_bsonConvert", bsonConvertDesc, null, null); fv = cw.visitField(ACC_PRIVATE, FIELDPREFIX + "_conf", anyValueDesc, 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 + "_transport", transportDesc, null, null);
fv.visitEnd(); 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_PUBLIC, "<init>", "()V", null, null)); mv = new AsmMethodVisitor(cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null));
//mv.setDebug(true); //mv.setDebug(true);
mv.visitVarInsn(ALOAD, 0); mv.visitVarInsn(ALOAD, 0);
mv.visitMethodInsn(INVOKESPECIAL, serviceClass.isInterface() ? "java/lang/Object" : supDynName, "<init>", "()V", false); mv.visitMethodInsn(INVOKESPECIAL, serviceTypeOrImplClass.isInterface() ? "java/lang/Object" : supDynName, "<init>", "()V", false);
mv.visitInsn(RETURN); mv.visitInsn(RETURN);
mv.visitMaxs(1, 1); mv.visitMaxs(1, 1);
mv.visitEnd(); mv.visitEnd();
@@ -1031,23 +969,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, serviceClass) : serviceClass)) { for (final SncpAction entry : SncpClient.getSncpActions(realed ? createLocalServiceClass(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;
{ {
@@ -1063,12 +1003,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 {
@@ -1113,7 +1048,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);
@@ -1156,26 +1091,20 @@ public abstract class Sncp {
}.loadClass(newDynName.replace('/', '.'), bytes); }.loadClass(newDynName.replace('/', '.'), bytes);
try { try {
T rs = (T) newClazz.newInstance(); T rs = (T) newClazz.newInstance();
Field c = newClazz.getDeclaredField(FIELDPREFIX + "_client"); SncpClient client = new SncpClient(name, serviceTypeOrImplClass, rs, transportFactory, true, realed ? createLocalServiceClass(name, serviceTypeOrImplClass) : serviceTypeOrImplClass, clientAddress);
c.setAccessible(true); client.setRemoteGroups(groups);
SncpClient client = new SncpClient(name, serviceClass, rs, executor, true, realed ? createLocalServiceClass(name, serviceClass) : serviceClass, clientAddress); client.setRemoteGroupTransport(transportFactory.loadRemoteTransport(clientAddress, groups));
c.set(rs, client);
Field t = newClazz.getDeclaredField(FIELDPREFIX + "_transport");
t.setAccessible(true);
t.set(rs, transport);
{ {
StringBuilder sb = new StringBuilder(); Field c = newClazz.getDeclaredField(FIELDPREFIX + "_client");
sb.append(newClazz.getName()).append("{name = '").append(name); c.setAccessible(true);
sb.append("', serviceid = ").append(client.getServiceid()); c.set(rs, client);
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());
} }
{
Field c = newClazz.getDeclaredField(FIELDPREFIX + "_conf");
c.setAccessible(true);
c.set(rs, conf);
}
transportFactory.addSncpService(rs);
return rs; return rs;
} catch (Exception ex) { } catch (Exception ex) {
throw new RuntimeException(ex); throw new RuntimeException(ex);

View File

@@ -0,0 +1,307 @@
/*
* 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.sncp;
import java.util.concurrent.CompletableFuture;
import java.util.logging.Level;
import jdk.internal.org.objectweb.asm.*;
import static jdk.internal.org.objectweb.asm.Opcodes.*;
import org.redkale.convert.bson.*;
import org.redkale.net.sncp.SncpDynServlet.SncpServletAction;
import org.redkale.util.*;
/**
* 异步回调函数 <br>
*
* public class _DyncSncpAsyncHandler extends XXXAsyncHandler implements SncpAsyncHandler
*
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
* @param <V> 结果对象的泛型
* @param <A> 附件对象的泛型
*/
public interface SncpAsyncHandler<V, A> extends AsyncHandler<V, A> {
public Object[] sncp_getParams();
public void sncp_setParams(Object... params);
public void sncp_setFuture(CompletableFuture future);
public CompletableFuture sncp_getFuture();
static class Factory {
/**
* <blockquote><pre>
*
* 考虑点:
* 1、AsyncHandler子类是接口且还有其他多个方法
* 2、AsyncHandler子类是类 需要继承,且必须有空参数构造函数
* 3、AsyncHandler子类无论是接口还是类都可能存在其他泛型
*
* public class XXXAsyncHandler_DyncSncpAsyncHandler_4323 extends XXXAsyncHandler implements SncpAsyncHandler {
*
* private SncpAsyncHandler sncphandler;
*
* private CompletableFuture sncpfuture;
*
* &#64;java.beans.ConstructorProperties({"sncphandler"})
* public XXXAsyncHandler_DyncSncpAsyncHandler_4323(SncpAsyncHandler sncphandler) {
* super();
* this.sncphandler = sncphandler;
* }
*
* &#64;Override
* public void completed(Object result, Object attachment) {
* sncphandler.completed(result, attachment);
* }
*
* &#64;Override
* public void failed(Throwable exc, Object attachment) {
* sncphandler.failed(exc, attachment);
* }
*
* &#64;Override
* public Object[] sncp_getParams() {
* return sncphandler.sncp_getParams();
* }
*
* &#64;Override
* public void sncp_setParams(Object... params) {
* sncphandler.sncp_setParams(params);
* }
*
* &#64;Override
* public void sncp_setFuture(CompletableFuture future) {
* this.sncpfuture = future;
* }
*
* &#64;Override
* public CompletableFuture sncp_getFuture() {
* return this.sncpfuture;
* }
* }
*
* </pre></blockquote>
*
* @param handlerClass AsyncHandler类型或子类
*
* @return Creator
*/
public static Creator<SncpAsyncHandler> createCreator(Class<? extends AsyncHandler> handlerClass) {
//-------------------------------------------------------------
final boolean handlerinterface = handlerClass.isInterface();
final String handlerClassName = handlerClass.getName().replace('.', '/');
final String sncpHandlerName = SncpAsyncHandler.class.getName().replace('.', '/');
final String sncpHandlerDesc = Type.getDescriptor(SncpAsyncHandler.class);
final String sncpFutureDesc = Type.getDescriptor(CompletableFuture.class);
final String newDynName = handlerClass.getName().replace('.', '/') + "_Dync" + SncpAsyncHandler.class.getSimpleName() + "_" + (System.currentTimeMillis() % 10000);
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, sncpHandlerName} : new String[]{sncpHandlerName});
{ //handler 属性
fv = cw.visitField(ACC_PRIVATE, "sncphandler", sncpHandlerDesc, null, null);
fv.visitEnd();
}
{ //future 属性
fv = cw.visitField(ACC_PRIVATE, "sncpfuture", sncpFutureDesc, null, null);
fv.visitEnd();
}
{//构造方法
mv = new AsmMethodVisitor(cw.visitMethod(ACC_PUBLIC, "<init>", "(" + sncpHandlerDesc + ")V", null, null));
//mv.setDebug(true);
{
av0 = mv.visitAnnotation("Ljava/beans/ConstructorProperties;", true);
{
AnnotationVisitor av1 = av0.visitArray("value");
av1.visit(null, "sncphandler");
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, "sncphandler", sncpHandlerDesc);
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, "sncphandler", sncpHandlerDesc);
mv.visitVarInsn(ALOAD, 1);
mv.visitVarInsn(ALOAD, 2);
mv.visitMethodInsn(INVOKEINTERFACE, sncpHandlerName, "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, "sncphandler", sncpHandlerDesc);
mv.visitVarInsn(ALOAD, 1);
mv.visitVarInsn(ALOAD, 2);
mv.visitMethodInsn(INVOKEINTERFACE, sncpHandlerName, "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();
}
}
{ // sncp_getParams
mv = new AsmMethodVisitor(cw.visitMethod(ACC_PUBLIC, "sncp_getParams", "()[Ljava/lang/Object;", null, null));
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, newDynName, "sncphandler", sncpHandlerDesc);
mv.visitMethodInsn(INVOKEINTERFACE, sncpHandlerName, "sncp_getParams", "()[Ljava/lang/Object;", true);
mv.visitInsn(ARETURN);
mv.visitMaxs(1, 1);
mv.visitEnd();
}
{ // sncp_setParams
mv = new AsmMethodVisitor(cw.visitMethod(ACC_PUBLIC + ACC_VARARGS, "sncp_setParams", "([Ljava/lang/Object;)V", null, null));
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, newDynName, "sncphandler", sncpHandlerDesc);
mv.visitVarInsn(ALOAD, 1);
mv.visitMethodInsn(INVOKEINTERFACE, sncpHandlerName, "sncp_setParams", "([Ljava/lang/Object;)V", true);
mv.visitInsn(RETURN);
mv.visitMaxs(2, 2);
mv.visitEnd();
}
{ // sncp_setFuture
mv = new AsmMethodVisitor(cw.visitMethod(ACC_PUBLIC, "sncp_setFuture", "(" + sncpFutureDesc + ")V", null, null));
mv.visitVarInsn(ALOAD, 0);
mv.visitVarInsn(ALOAD, 1);
mv.visitFieldInsn(PUTFIELD, newDynName, "sncpfuture", sncpFutureDesc);
mv.visitInsn(RETURN);
mv.visitMaxs(2, 2);
mv.visitEnd();
}
{ // sncp_getFuture
mv = new AsmMethodVisitor(cw.visitMethod(ACC_PUBLIC, "sncp_getFuture", "()" + sncpFutureDesc, null, null));
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, newDynName, "sncpfuture", sncpFutureDesc);
mv.visitInsn(ARETURN);
mv.visitMaxs(1, 1);
mv.visitEnd();
}
cw.visitEnd();
byte[] bytes = cw.toByteArray();
Class<SncpAsyncHandler> newHandlerClazz = (Class<SncpAsyncHandler>) 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.create(newHandlerClazz);
}
}
static class DefaultSncpAsyncHandler<V, A> implements SncpAsyncHandler<V, A> {
//为了在回调函数中调用_callParameter方法
protected Object[] params;
protected SncpServletAction action;
protected BsonReader in;
protected BsonWriter out;
protected SncpRequest request;
protected SncpResponse response;
protected CompletableFuture future;
public DefaultSncpAsyncHandler(SncpServletAction action, BsonReader in, BsonWriter out, SncpRequest request, SncpResponse response) {
this.action = action;
this.in = in;
this.out = out;
this.request = request;
this.response = response;
}
@Override
public void completed(Object result, Object attachment) {
try {
action._callParameter(out, sncp_getParams());
action.convert.convertTo(out, Object.class, result);
response.finish(0, out);
} catch (Exception e) {
failed(e, attachment);
} finally {
action.convert.offerBsonReader(in);
action.convert.offerBsonWriter(out);
}
}
@Override
public void failed(Throwable exc, Object attachment) {
response.getContext().getLogger().log(Level.INFO, "sncp execute error(" + request + ")", exc);
response.finish(SncpResponse.RETCODE_THROWEXCEPTION, null);
}
@Override
public Object[] sncp_getParams() {
return params;
}
@Override
public void sncp_setParams(Object... params) {
this.params = params;
}
@Override
public void sncp_setFuture(CompletableFuture future) {
this.future = future;
}
@Override
public CompletableFuture sncp_getFuture() {
return this.future;
}
}
}

View File

@@ -12,8 +12,8 @@ import java.nio.*;
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.function.*;
import java.util.logging.*; import java.util.logging.*;
import javax.annotation.Resource;
import org.redkale.convert.bson.*; import org.redkale.convert.bson.*;
import org.redkale.convert.json.*; import org.redkale.convert.json.*;
import org.redkale.net.*; import org.redkale.net.*;
@@ -31,98 +31,6 @@ import org.redkale.service.RpcCall;
*/ */
public final class SncpClient { public final class SncpClient {
protected static final class SncpAction {
protected final DLong actionid;
protected final Method method;
protected final Type resultTypes; //void 必须设为 null
protected final Type[] paramTypes;
protected final Attribute[] paramAttrs; // 为null表示无RpcCall处理index=0固定为null, 其他为参数标记的RpcCall回调方法
protected final int handlerFuncParamIndex;
protected final int handlerAttachParamIndex;
protected final int addressTargetParamIndex;
protected final int addressSourceParamIndex;
public SncpAction(Method method, DLong actionid) {
this.actionid = actionid;
Type rt = method.getGenericReturnType();
if (rt instanceof TypeVariable) {
TypeVariable tv = (TypeVariable) rt;
if (tv.getBounds().length == 1) rt = tv.getBounds()[0];
}
this.resultTypes = rt == void.class ? null : rt;
this.paramTypes = method.getGenericParameterTypes();
this.method = method;
Annotation[][] anns = method.getParameterAnnotations();
int targetAddrIndex = -1;
int sourceAddrIndex = -1;
int handlerAttachIndex = -1;
int handlerFuncIndex = -1;
boolean hasattr = false;
Attribute[] atts = new Attribute[paramTypes.length + 1];
if (anns.length > 0) {
Class<?>[] params = method.getParameterTypes();
for (int i = 0; i < params.length; i++) {
if (AsyncHandler.class.isAssignableFrom(params[i])) {
handlerFuncIndex = i;
break;
}
}
for (int i = 0; i < anns.length; i++) {
if (anns[i].length > 0) {
for (Annotation ann : anns[i]) {
if (ann.annotationType() == RpcAttachment.class) {
handlerAttachIndex = i;
} else if (ann.annotationType() == RpcTargetAddress.class && SocketAddress.class.isAssignableFrom(params[i])) {
targetAddrIndex = i;
} else if (ann.annotationType() == RpcSourceAddress.class && SocketAddress.class.isAssignableFrom(params[i])) {
sourceAddrIndex = i;
}
}
for (Annotation ann : anns[i]) {
if (ann.annotationType() == RpcCall.class) {
try {
atts[i + 1] = ((RpcCall) ann).value().newInstance();
hasattr = true;
} catch (Exception e) {
logger.log(Level.SEVERE, RpcCall.class.getSimpleName() + ".attribute cannot a newInstance for" + method, e);
}
break;
}
}
}
}
}
this.addressTargetParamIndex = targetAddrIndex;
this.addressSourceParamIndex = sourceAddrIndex;
this.handlerFuncParamIndex = handlerFuncIndex;
this.handlerAttachParamIndex = handlerAttachIndex;
this.paramAttrs = hasattr ? atts : null;
if (handlerFuncIndex > 0) {
Type handlerFuncType = this.paramTypes[handlerFuncIndex];
if (handlerFuncType instanceof ParameterizedType) {
ParameterizedType handlerpt = (ParameterizedType) handlerFuncType;
//后续可以添加验证, AsyncHandler的第一个泛型必须与方法返回值类型相同 第二个泛型必须与@RpcAttachment的参数类型相同
//需要考虑AsyncHandler的子类形态 有可能0、1、2、。。。多个泛型
}
this.paramTypes[handlerFuncIndex] = AsyncHandler.class;
}
}
@Override
public String toString() {
return "{" + actionid + "," + (method == null ? "null" : method.getName()) + "}";
}
}
protected static final Logger logger = Logger.getLogger(SncpClient.class.getSimpleName()); protected static final Logger logger = Logger.getLogger(SncpClient.class.getSimpleName());
protected final boolean finest = logger.isLoggable(Level.FINEST); protected final boolean finest = logger.isLoggable(Level.FINEST);
@@ -147,21 +55,48 @@ public final class SncpClient {
protected final SncpAction[] actions; protected final SncpAction[] actions;
protected final Consumer<Runnable> executor; protected final ExecutorService executor;
public <T extends Service> SncpClient(final String serviceName, final Class<T> serviceType, final T service, final Consumer<Runnable> executor, @Resource
protected JsonConvert jsonConvert;
@Resource
protected BsonConvert bsonConvert;
//远程模式
protected Set<String> remoteGroups;
//远程模式
protected Transport remoteGroupTransport;
//本地模式
protected String sameGroup;
//本地模式
protected Transport sameGroupTransport;
//本地模式
protected Set<String> diffGroups;
//本地模式
protected Transport[] diffGroupTransports;
public <T extends Service> SncpClient(final String serviceName, final Class<T> serviceTypeOrImplClass, final T service, final TransportFactory factory,
final boolean remote, final Class serviceClass, final InetSocketAddress clientAddress) { final boolean remote, final Class serviceClass, final InetSocketAddress clientAddress) {
this.remote = remote; this.remote = remote;
this.executor = executor; this.executor = factory.getExecutor();
this.serviceClass = serviceClass; this.serviceClass = serviceClass;
this.serviceversion = service.version(); this.serviceversion = 0;
this.clientAddress = clientAddress; this.clientAddress = clientAddress;
this.name = serviceName; this.name = serviceName;
this.serviceid = Sncp.hash(serviceType.getName() + ':' + serviceName); Class tn = serviceTypeOrImplClass;
ResourceType rt = (ResourceType) tn.getAnnotation(ResourceType.class);
if (rt != null) tn = rt.value();
this.serviceid = Sncp.hash(tn.getName() + ':' + serviceName);
final List<SncpAction> methodens = new ArrayList<>(); final List<SncpAction> methodens = new ArrayList<>();
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
for (java.lang.reflect.Method method : parseMethod(serviceClass)) { for (java.lang.reflect.Method method : parseMethod(serviceClass)) {
methodens.add(new SncpAction(method, Sncp.hash(method))); methodens.add(new SncpAction(serviceClass, method, Sncp.hash(method)));
} }
this.actions = methodens.toArray(new SncpAction[methodens.size()]); this.actions = methodens.toArray(new SncpAction[methodens.size()]);
this.addrBytes = clientAddress == null ? new byte[4] : clientAddress.getAddress().getAddress(); this.addrBytes = clientAddress == null ? new byte[4] : clientAddress.getAddress().getAddress();
@@ -172,7 +107,7 @@ public final class SncpClient {
final List<SncpAction> actions = new ArrayList<>(); final List<SncpAction> actions = new ArrayList<>();
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
for (java.lang.reflect.Method method : parseMethod(serviceClass)) { for (java.lang.reflect.Method method : parseMethod(serviceClass)) {
actions.add(new SncpAction(method, Sncp.hash(method))); actions.add(new SncpAction(serviceClass, method, Sncp.hash(method)));
} }
return actions; return actions;
} }
@@ -193,6 +128,54 @@ public final class SncpClient {
return actions.length; return actions.length;
} }
public Set<String> getRemoteGroups() {
return remoteGroups;
}
public void setRemoteGroups(Set<String> remoteGroups) {
this.remoteGroups = remoteGroups;
}
public Transport getRemoteGroupTransport() {
return remoteGroupTransport;
}
public void setRemoteGroupTransport(Transport remoteGroupTransport) {
this.remoteGroupTransport = remoteGroupTransport;
}
public String getSameGroup() {
return sameGroup;
}
public void setSameGroup(String sameGroup) {
this.sameGroup = sameGroup;
}
public Transport getSameGroupTransport() {
return sameGroupTransport;
}
public void setSameGroupTransport(Transport sameGroupTransport) {
this.sameGroupTransport = sameGroupTransport;
}
public Set<String> getDiffGroups() {
return diffGroups;
}
public void setDiffGroups(Set<String> diffGroups) {
this.diffGroups = diffGroups;
}
public Transport[] getDiffGroupTransports() {
return diffGroupTransports;
}
public void setDiffGroupTransports(Transport[] diffGroupTransports) {
this.diffGroupTransports = diffGroupTransports;
}
@Override @Override
public String toString() { public String toString() {
String service = serviceClass.getName(); String service = serviceClass.getName();
@@ -202,6 +185,26 @@ public final class SncpClient {
+ ", actions.size = " + actions.length + ")"; + ", actions.size = " + actions.length + ")";
} }
public String toSimpleString() { //给Sncp产生的Service用
String service = serviceClass.getName();
if (remote) service = service.replace(Sncp.LOCALPREFIX, Sncp.REMOTEPREFIX);
List<InetSocketAddress> diffaddrs = new ArrayList<>();
if (diffGroupTransports != null && diffGroupTransports.length > 1) {
for (Transport t : diffGroupTransports) {
diffaddrs.addAll(Utility.ofList(t.getRemoteAddresses()));
}
}
return service + "(name = '" + name + "', serviceid = " + serviceid + ", serviceversion = " + serviceversion
+ ", clientaddr = " + (clientAddress == null ? "" : (clientAddress.getHostString() + ":" + clientAddress.getPort()))
+ ((sameGroup == null || sameGroup.isEmpty()) ? "" : ", sameGroup = " + sameGroup)
+ (sameGroupTransport == null ? "" : ", sameGroupTransport = " + Arrays.toString(sameGroupTransport.getRemoteAddresses()))
+ ((diffGroups == null || diffGroups.isEmpty()) ? "" : ", diffGroups = " + diffGroups)
+ ((diffGroupTransports == null || diffGroupTransports.length < 1) ? "" : ", diffGroupTransports = " + diffaddrs)
+ ((remoteGroups == null || remoteGroups.isEmpty()) ? "" : ", remoteGroups = " + remoteGroups)
+ (remoteGroupTransport == null ? "" : ", remoteGroupTransport = " + Arrays.toString(remoteGroupTransport.getRemoteAddresses()))
+ ", actions.size = " + actions.length + ")";
}
public static List<Method> parseMethod(final Class serviceClass) { public static List<Method> parseMethod(final Class serviceClass) {
final List<Method> list = new ArrayList<>(); final List<Method> list = new ArrayList<>();
final List<Method> multis = new ArrayList<>(); final List<Method> multis = new ArrayList<>();
@@ -212,11 +215,12 @@ public final class SncpClient {
final int mod = method.getModifiers(); final int mod = method.getModifiers();
if (Modifier.isStatic(mod)) continue; if (Modifier.isStatic(mod)) continue;
if (Modifier.isFinal(mod)) continue; if (Modifier.isFinal(mod)) continue;
if (method.getAnnotation(Local.class) != null) continue;
if (method.getName().equals("getClass") || method.getName().equals("toString")) continue; if (method.getName().equals("getClass") || method.getName().equals("toString")) continue;
if (method.getName().equals("equals") || method.getName().equals("hashCode")) continue; if (method.getName().equals("equals") || method.getName().equals("hashCode")) continue;
if (method.getName().equals("notify") || method.getName().equals("notifyAll") || method.getName().equals("wait")) continue; if (method.getName().equals("notify") || method.getName().equals("notifyAll") || method.getName().equals("wait")) continue;
if (method.getName().equals("init") || method.getName().equals("destroy")) continue; if (method.getName().equals("init") || method.getName().equals("destroy")) continue;
if (method.getName().equals("version") || method.getName().equals("name")) continue; //if (method.getName().equals("version") || method.getName().equals("name")) continue;
//if (onlySncpDyn && method.getAnnotation(SncpDyn.class) == null) continue; //if (onlySncpDyn && method.getAnnotation(SncpDyn.class) == null) continue;
DLong actionid = Sncp.hash(method); DLong actionid = Sncp.hash(method);
Method old = actionids.get(actionid); Method old = actionids.get(actionid);
@@ -243,52 +247,79 @@ public final class SncpClient {
return multis; return multis;
} }
public void remoteSameGroup(final BsonConvert bsonConvert, final JsonConvert jsonConvert, Transport transport, final int index, final Object... params) { public void remoteSameGroup(final int index, final Object... params) {
if (sameGroupTransport == null) return;
final SncpAction action = actions[index]; final SncpAction action = actions[index];
if (action.handlerFuncParamIndex >= 0) params[action.handlerFuncParamIndex] = null; //不能让远程调用handler因为之前本地方法已经调用过了 if (action.handlerFuncParamIndex >= 0) params[action.handlerFuncParamIndex] = null; //不能让远程调用handler因为之前本地方法已经调用过了
for (InetSocketAddress addr : transport.getRemoteAddresses()) { for (InetSocketAddress addr : sameGroupTransport.getRemoteAddresses()) {
remote0(null, bsonConvert, jsonConvert, transport, addr, action, params); remote0(null, sameGroupTransport, addr, action, params);
} }
} }
public void asyncRemoteSameGroup(final BsonConvert bsonConvert, final JsonConvert jsonConvert, Transport transport, final int index, final Object... params) { public void asyncRemoteSameGroup(final int index, final Object... params) {
if (sameGroupTransport == null) return;
if (executor != null) { if (executor != null) {
executor.accept(() -> { executor.execute(() -> {
remoteSameGroup(bsonConvert, jsonConvert, transport, index, params); remoteSameGroup(index, params);
}); });
} else { } else {
remoteSameGroup(bsonConvert, jsonConvert, transport, index, params); remoteSameGroup(index, params);
} }
} }
public void remoteDiffGroup(final BsonConvert bsonConvert, final JsonConvert jsonConvert, Transport[] transports, final int index, final Object... params) { public void remoteDiffGroup(final int index, final Object... params) {
if (transports == null || transports.length < 1) return; if (diffGroupTransports == null || diffGroupTransports.length < 1) return;
final SncpAction action = actions[index]; final SncpAction action = actions[index];
if (action.handlerFuncParamIndex >= 0) params[action.handlerFuncParamIndex] = null; //不能让远程调用handler因为之前本地方法已经调用过了 if (action.handlerFuncParamIndex >= 0) params[action.handlerFuncParamIndex] = null; //不能让远程调用handler因为之前本地方法已经调用过了
for (Transport transport : transports) { for (Transport transport : diffGroupTransports) {
remote0(null, bsonConvert, jsonConvert, transport, null, action, params); remote0(null, transport, null, action, params);
} }
} }
public void asyncRemoteDiffGroup(final BsonConvert bsonConvert, final JsonConvert jsonConvert, Transport[] transports, final int index, final Object... params) { public void asyncRemoteDiffGroup(final int index, final Object... params) {
if (transports == null || transports.length < 1) return; if (diffGroupTransports == null || diffGroupTransports.length < 1) return;
if (executor != null) { if (executor != null) {
executor.accept(() -> { executor.execute(() -> {
remoteDiffGroup(bsonConvert, jsonConvert, transports, index, params); remoteDiffGroup(index, params);
}); });
} else { } else {
remoteDiffGroup(bsonConvert, jsonConvert, transports, index, params); remoteDiffGroup(index, params);
} }
} }
//只给远程模式调用的 //只给远程模式调用的
public <T> T remote(final BsonConvert bsonConvert, final JsonConvert jsonConvert, Transport transport, final int index, final Object... params) { public <T> T remote(final int index, final Object... params) {
final SncpAction action = actions[index]; final SncpAction action = actions[index];
final AsyncHandler handlerFunc = action.handlerFuncParamIndex >= 0 ? (AsyncHandler) params[action.handlerFuncParamIndex] : null; final AsyncHandler handlerFunc = action.handlerFuncParamIndex >= 0 ? (AsyncHandler) params[action.handlerFuncParamIndex] : null;
if (action.handlerFuncParamIndex >= 0) params[action.handlerFuncParamIndex] = null; if (action.handlerFuncParamIndex >= 0) params[action.handlerFuncParamIndex] = null;
SncpFuture<byte[]> future = remote0(handlerFunc, bsonConvert, jsonConvert, transport, null, action, params);
if (handlerFunc != null) return null;
final BsonReader reader = bsonConvert.pollBsonReader(); final BsonReader reader = bsonConvert.pollBsonReader();
CompletableFuture<byte[]> future = remote0(handlerFunc, remoteGroupTransport, null, action, params);
if (action.boolReturnTypeFuture) {
CompletableFuture result = action.futureCreator.create();
future.whenComplete((v, e) -> {
try {
if (e != null) {
result.completeExceptionally(e);
} else {
reader.setBytes(v);
byte i;
while ((i = reader.readByte()) != 0) {
final Attribute attr = action.paramAttrs[i];
attr.set(params[i - 1], bsonConvert.convertFrom(attr.type(), reader));
}
Object rs = bsonConvert.convertFrom(Object.class, reader);
result.complete(rs);
}
} catch (Exception exp) {
result.completeExceptionally(exp);
} finally {
bsonConvert.offerBsonReader(reader);
}
}); //需要获取 Executor
return (T) result;
}
if (handlerFunc != null) return null;
try { try {
reader.setBytes(future.get(5, TimeUnit.SECONDS)); reader.setBytes(future.get(5, TimeUnit.SECONDS));
byte i; byte i;
@@ -296,7 +327,7 @@ public final class SncpClient {
final Attribute attr = action.paramAttrs[i]; final Attribute attr = action.paramAttrs[i];
attr.set(params[i - 1], bsonConvert.convertFrom(attr.type(), reader)); attr.set(params[i - 1], bsonConvert.convertFrom(attr.type(), reader));
} }
return bsonConvert.convertFrom(action.resultTypes, reader); return bsonConvert.convertFrom(action.handlerFuncParamIndex >= 0 ? Object.class : action.resultTypes, reader);
} catch (InterruptedException | ExecutionException | TimeoutException e) { } catch (InterruptedException | ExecutionException | TimeoutException e) {
logger.log(Level.SEVERE, actions[index].method + " sncp (params: " + jsonConvert.convertTo(params) + ") remote error", e); logger.log(Level.SEVERE, actions[index].method + " sncp (params: " + jsonConvert.convertTo(params) + ") remote error", e);
throw new RuntimeException(actions[index].method + " sncp remote error", e); throw new RuntimeException(actions[index].method + " sncp remote error", e);
@@ -305,48 +336,36 @@ public final class SncpClient {
} }
} }
public <T> void remote(final BsonConvert bsonConvert, final JsonConvert jsonConvert, Transport[] transports, final int index, final Object... params) { private CompletableFuture<byte[]> remote0(final AsyncHandler handler, final Transport transport, final SocketAddress addr0, final SncpAction action, final Object... params) {
if (transports == null || transports.length < 1) return; final Type[] myparamtypes = action.paramTypes;
remote(bsonConvert, jsonConvert, transports[0], index, params); final Class[] myparamclass = action.paramClass;
for (int i = 1; i < transports.length; i++) {
remote0(null, bsonConvert, jsonConvert, transports[i], null, actions[index], params);
}
}
private SncpFuture<byte[]> remote0(final AsyncHandler handler, final BsonConvert bsonConvert, final JsonConvert jsonConvert, final Transport transport, final SocketAddress addr0, final SncpAction action, final Object... params) {
if ("rest".equalsIgnoreCase(transport.getSubprotocol())) {
return remoteRest0(handler, jsonConvert, transport, addr0, action, params);
}
return remoteSncp0(handler, bsonConvert, transport, addr0, action, params);
}
//尚未实现
private SncpFuture<byte[]> remoteRest0(final AsyncHandler handler, final JsonConvert jsonConvert, final Transport transport, final SocketAddress addr0, final SncpAction action, final Object... params) {
return null;
}
private SncpFuture<byte[]> remoteSncp0(final AsyncHandler handler, final BsonConvert bsonConvert, final Transport transport, final SocketAddress addr0, final SncpAction action, final Object... params) {
Type[] myparamtypes = action.paramTypes;
if (action.addressSourceParamIndex >= 0) params[action.addressSourceParamIndex] = this.clientAddress; if (action.addressSourceParamIndex >= 0) params[action.addressSourceParamIndex] = this.clientAddress;
final BsonWriter writer = bsonConvert.pollBsonWriter(transport.getBufferSupplier()); // 将head写入 final BsonWriter writer = bsonConvert.pollBsonWriter(transport.getBufferSupplier()); // 将head写入
writer.writeTo(DEFAULT_HEADER); writer.writeTo(DEFAULT_HEADER);
for (int i = 0; i < params.length; i++) { for (int i = 0; i < params.length; i++) { //params 可能包含: 3 个 boolean
bsonConvert.convertTo(writer, myparamtypes[i], params[i]); bsonConvert.convertTo(writer, AsyncHandler.class.isAssignableFrom(myparamclass[i]) ? AsyncHandler.class : myparamtypes[i], params[i]);
} }
final int reqBodyLength = writer.count() - HEADER_SIZE; //body总长度 final int reqBodyLength = writer.count() - HEADER_SIZE; //body总长度
final long seqid = System.nanoTime(); final long seqid = System.nanoTime();
final DLong actionid = action.actionid; final DLong actionid = action.actionid;
final SocketAddress addr = addr0 == null ? (action.addressTargetParamIndex >= 0 ? (SocketAddress) params[action.addressTargetParamIndex] : null) : addr0; final SocketAddress addr = addr0 == null ? (action.addressTargetParamIndex >= 0 ? (SocketAddress) params[action.addressTargetParamIndex] : null) : addr0;
final AsyncConnection conn = transport.pollConnection(addr); final CompletableFuture<byte[]> future = new CompletableFuture();
if (conn == null || !conn.isOpen()) { AsyncConnection conn0;
logger.log(Level.SEVERE, action.method + " sncp (params: " + convert.convertTo(params) + ") cannot connect " + (conn == null ? addr : conn.getRemoteAddress())); try {
throw new RuntimeException("sncp " + (conn == null ? addr : conn.getRemoteAddress()) + " cannot connect"); conn0 = transport.pollConnection(addr);
} catch (Exception e) {
future.completeExceptionally(e);
return future;
} }
if (conn0 == null || !conn0.isOpen()) {
future.completeExceptionally(new RuntimeException("sncp " + (conn0 == null ? addr : conn0.getRemoteAddress()) + " cannot connect"));
return future;
}
final AsyncConnection conn = conn0;
final ByteBuffer[] sendBuffers = writer.toBuffers(); final ByteBuffer[] sendBuffers = writer.toBuffers();
fillHeader(sendBuffers[0], seqid, actionid, reqBodyLength); fillHeader(sendBuffers[0], seqid, actionid, reqBodyLength);
final ByteBuffer buffer = transport.pollBuffer(); final ByteBuffer buffer = transport.pollBuffer();
final SncpFuture<byte[]> future = new SncpFuture(false);
conn.write(sendBuffers, sendBuffers, new CompletionHandler<Integer, ByteBuffer[]>() { conn.write(sendBuffers, sendBuffers, new CompletionHandler<Integer, ByteBuffer[]>() {
@Override @Override
@@ -380,7 +399,7 @@ public final class SncpClient {
@Override @Override
public void completed(Integer count, Void attachment2) { public void completed(Integer count, Void attachment2) {
if (count < 1 && buffer.remaining() == buffer.limit()) { //没有数据可读 if (count < 1 && buffer.remaining() == buffer.limit()) { //没有数据可读
future.set(new RuntimeException(action.method + " sncp[" + conn.getRemoteAddress() + "] remote no response data")); future.completeExceptionally(new RuntimeException(action.method + " sncp[" + conn.getRemoteAddress() + "] remote no response data"));
transport.offerBuffer(buffer); transport.offerBuffer(buffer);
transport.offerConnection(true, conn); transport.offerConnection(true, conn);
return; return;
@@ -425,7 +444,7 @@ public final class SncpClient {
} }
public void success() { public void success() {
future.set(this.body); future.complete(this.body);
transport.offerBuffer(buffer); transport.offerBuffer(buffer);
transport.offerConnection(false, conn); transport.offerConnection(false, conn);
if (handler != null) { if (handler != null) {
@@ -438,7 +457,7 @@ public final class SncpClient {
final Attribute attr = action.paramAttrs[i]; final Attribute attr = action.paramAttrs[i];
attr.set(params[i - 1], bsonConvert.convertFrom(attr.type(), reader)); attr.set(params[i - 1], bsonConvert.convertFrom(attr.type(), reader));
} }
Object rs = bsonConvert.convertFrom(action.resultTypes, reader); Object rs = bsonConvert.convertFrom(action.handlerFuncParamIndex >= 0 ? Object.class : action.resultTypes, reader);
handler.completed(rs, handlerAttach); handler.completed(rs, handlerAttach);
} catch (Exception e) { } catch (Exception e) {
handler.failed(e, handlerAttach); handler.failed(e, handlerAttach);
@@ -451,7 +470,7 @@ public final class SncpClient {
@Override @Override
public void failed(Throwable exc, Void attachment2) { public void failed(Throwable exc, Void attachment2) {
logger.log(Level.SEVERE, action.method + " sncp (params: " + convert.convertTo(params) + ") remote read exec failed", exc); logger.log(Level.SEVERE, action.method + " sncp (params: " + convert.convertTo(params) + ") remote read exec failed", exc);
future.set(new RuntimeException(action.method + " sncp remote exec failed")); future.completeExceptionally(new RuntimeException(action.method + " sncp remote exec failed"));
transport.offerBuffer(buffer); transport.offerBuffer(buffer);
transport.offerConnection(true, conn); transport.offerConnection(true, conn);
if (handler != null) { if (handler != null) {
@@ -481,7 +500,8 @@ public final class SncpClient {
int version = buffer.getInt(); int version = buffer.getInt();
if (version != this.serviceversion) throw new RuntimeException("sncp(" + action.method + ") response.serviceversion = " + serviceversion + ", but request.serviceversion =" + version); if (version != this.serviceversion) throw new RuntimeException("sncp(" + action.method + ") response.serviceversion = " + serviceversion + ", but request.serviceversion =" + version);
DLong raction = DLong.read(buffer); DLong raction = DLong.read(buffer);
if (!action.actionid.equals(raction)) throw new RuntimeException("sncp(" + action.method + ") response.actionid = " + action.actionid + ", but request.actionid =(" + raction + ")"); DLong actid = action.actionid;
if (!actid.equals(raction)) throw new RuntimeException("sncp(" + action.method + ") response.actionid = " + action.actionid + ", but request.actionid =(" + raction + ")");
buffer.getInt(); //地址 buffer.getInt(); //地址
buffer.getChar(); //端口 buffer.getChar(); //端口
} }
@@ -502,91 +522,108 @@ public final class SncpClient {
buffer.position(currentpos); buffer.position(currentpos);
} }
protected static final class SncpFuture<T> implements Future<T> { protected static final class SncpAction {
private volatile boolean done; protected final DLong actionid;
private T result; protected final Method method;
private RuntimeException ex; protected final Type resultTypes; //void 必须设为 null
private final boolean rest; protected final Type[] paramTypes;
public SncpFuture(boolean rest) { protected final Class[] paramClass;
this.rest = rest;
}
public SncpFuture(boolean rest, T result) { protected final Attribute[] paramAttrs; // 为null表示无RpcCall处理index=0固定为null, 其他为参数标记的RpcCall回调方法
this.rest = rest;
this.result = result;
this.done = true;
}
public boolean isRest() { protected final int handlerFuncParamIndex;
return this.rest;
}
public void set(T result) { protected final int handlerAttachParamIndex;
this.result = result;
this.done = true; protected final int addressTargetParamIndex;
synchronized (this) {
notifyAll(); protected final int addressSourceParamIndex;
protected final boolean boolReturnTypeFuture; // 返回结果类型是否为 CompletableFuture
protected final Creator<? extends CompletableFuture> futureCreator;
public SncpAction(final Class clazz, Method method, DLong actionid) {
this.actionid = actionid == null ? Sncp.hash(method) : actionid;
Type rt = method.getGenericReturnType();
if (rt instanceof TypeVariable) {
TypeVariable tv = (TypeVariable) rt;
if (tv.getBounds().length == 1) rt = tv.getBounds()[0];
} }
} this.resultTypes = rt == void.class ? null : rt;
this.boolReturnTypeFuture = CompletableFuture.class.isAssignableFrom(method.getReturnType());
public void set(RuntimeException ex) { this.futureCreator = boolReturnTypeFuture ? Creator.create((Class<? extends CompletableFuture>) method.getReturnType()) : null;
this.ex = ex; this.paramTypes = method.getGenericParameterTypes();
this.done = true; this.paramClass = method.getParameterTypes();
synchronized (this) { this.method = method;
notifyAll(); Annotation[][] anns = method.getParameterAnnotations();
int targetAddrIndex = -1;
int sourceAddrIndex = -1;
int handlerAttachIndex = -1;
int handlerFuncIndex = -1;
boolean hasattr = false;
Attribute[] atts = new Attribute[paramTypes.length + 1];
if (anns.length > 0) {
Class<?>[] params = method.getParameterTypes();
for (int i = 0; i < params.length; i++) {
if (AsyncHandler.class.isAssignableFrom(params[i])) {
if (boolReturnTypeFuture) {
throw new RuntimeException(method + " have both AsyncHandler and CompletableFuture");
}
if (handlerFuncIndex >= 0) {
throw new RuntimeException(method + " have more than one AsyncHandler type parameter");
}
Sncp.checkAsyncModifier(params[i], method);
handlerFuncIndex = i;
break;
}
}
for (int i = 0; i < anns.length; i++) {
if (anns[i].length > 0) {
for (Annotation ann : anns[i]) {
if (ann.annotationType() == RpcAttachment.class) {
if (handlerAttachIndex >= 0) {
throw new RuntimeException(method + " have more than one @RpcAttachment parameter");
}
handlerAttachIndex = i;
} else if (ann.annotationType() == RpcTargetAddress.class && SocketAddress.class.isAssignableFrom(params[i])) {
targetAddrIndex = i;
} else if (ann.annotationType() == RpcSourceAddress.class && SocketAddress.class.isAssignableFrom(params[i])) {
sourceAddrIndex = i;
}
}
for (Annotation ann : anns[i]) {
if (ann.annotationType() == RpcCall.class) {
try {
atts[i + 1] = ((RpcCall) ann).value().newInstance();
hasattr = true;
} catch (Exception e) {
logger.log(Level.SEVERE, RpcCall.class.getSimpleName() + ".attribute cannot a newInstance for" + method, e);
}
break;
}
}
}
}
}
this.addressTargetParamIndex = targetAddrIndex;
this.addressSourceParamIndex = sourceAddrIndex;
this.handlerFuncParamIndex = handlerFuncIndex;
this.handlerAttachParamIndex = handlerAttachIndex;
this.paramAttrs = hasattr ? atts : null;
if (this.handlerFuncParamIndex >= 0 && method.getReturnType() != void.class) {
throw new RuntimeException(method + " have AsyncHandler type parameter but return type is not void");
} }
} }
@Override @Override
public boolean cancel(boolean mayInterruptIfRunning) { public String toString() {
return false; return "{" + actionid + "," + (method == null ? "null" : method.getName()) + "}";
}
@Override
public boolean isCancelled() {
return false;
}
@Override
public boolean isDone() {
return done;
}
@Override
public T get() throws InterruptedException, ExecutionException {
if (done) {
if (ex != null) throw ex;
return result;
}
synchronized (this) {
if (!done) wait(10_000);
}
if (done) {
if (ex != null) throw ex;
return result;
}
throw new InterruptedException();
}
@Override
public T get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
if (done) {
if (ex != null) throw ex;
return result;
}
synchronized (this) {
if (!done) wait(unit.toMillis(timeout));
}
if (done) {
if (ex != null) throw ex;
return result;
}
throw new TimeoutException();
} }
} }
} }

View File

@@ -12,7 +12,6 @@ import java.util.concurrent.ExecutorService;
import java.util.logging.Logger; import java.util.logging.Logger;
import org.redkale.net.*; import org.redkale.net.*;
import org.redkale.util.ObjectPool; import org.redkale.util.ObjectPool;
import org.redkale.watch.WatchFactory;
/** /**
* <p> * <p>
@@ -23,9 +22,9 @@ import org.redkale.watch.WatchFactory;
public class SncpContext extends Context { public class SncpContext extends Context {
public SncpContext(long serverStartTime, Logger logger, ExecutorService executor, int bufferCapacity, ObjectPool<ByteBuffer> bufferPool, public SncpContext(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);
} }
} }

View File

@@ -11,6 +11,7 @@ import java.lang.annotation.*;
import java.lang.reflect.*; import java.lang.reflect.*;
import java.nio.*; import java.nio.*;
import java.util.*; import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.function.*; import java.util.function.*;
import java.util.logging.*; import java.util.logging.*;
import javax.annotation.*; import javax.annotation.*;
@@ -19,6 +20,7 @@ import static jdk.internal.org.objectweb.asm.ClassWriter.COMPUTE_FRAMES;
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.*; import org.redkale.convert.bson.*;
import org.redkale.net.sncp.SncpAsyncHandler.DefaultSncpAsyncHandler;
import org.redkale.service.*; import org.redkale.service.*;
import org.redkale.util.*; import org.redkale.util.*;
import org.redkale.service.RpcCall; import org.redkale.service.RpcCall;
@@ -40,19 +42,14 @@ public final class SncpDynServlet extends SncpServlet {
private final boolean finest = logger.isLoggable(Level.FINEST); private final boolean finest = logger.isLoggable(Level.FINEST);
private final Class<? extends Service> type;
private final String serviceName;
private final DLong serviceid; private final DLong serviceid;
private final HashMap<DLong, SncpServletAction> actions = new HashMap<>(); private final HashMap<DLong, SncpServletAction> actions = new HashMap<>();
private Supplier<ByteBuffer> bufferSupplier; private Supplier<ByteBuffer> bufferSupplier;
public SncpDynServlet(final BsonConvert convert, final String serviceName, final Class<? extends Service> type, final Service service) { public SncpDynServlet(final BsonConvert convert, final String serviceName, final Class serviceOrSourceType, final Service service) {
this.serviceName = serviceName; super(serviceName, serviceOrSourceType, service);
this.type = type;
this.serviceid = Sncp.hash(type.getName() + ':' + serviceName); this.serviceid = Sncp.hash(type.getName() + ':' + serviceName);
Set<DLong> actionids = new HashSet<>(); Set<DLong> actionids = new HashSet<>();
for (java.lang.reflect.Method method : service.getClass().getMethods()) { for (java.lang.reflect.Method method : service.getClass().getMethods()) {
@@ -63,7 +60,7 @@ public final class SncpDynServlet extends SncpServlet {
if (method.getName().equals("equals") || method.getName().equals("hashCode")) continue; if (method.getName().equals("equals") || method.getName().equals("hashCode")) continue;
if (method.getName().equals("notify") || method.getName().equals("notifyAll") || method.getName().equals("wait")) continue; if (method.getName().equals("notify") || method.getName().equals("notifyAll") || method.getName().equals("wait")) continue;
if (method.getName().equals("init") || method.getName().equals("destroy")) continue; if (method.getName().equals("init") || method.getName().equals("destroy")) continue;
if (method.getName().equals("version") || method.getName().equals("name")) continue; //if (method.getName().equals("version") || method.getName().equals("name")) continue;
final DLong actionid = Sncp.hash(method); final DLong actionid = Sncp.hash(method);
SncpServletAction action = SncpServletAction.create(service, actionid, method); SncpServletAction action = SncpServletAction.create(service, actionid, method);
action.convert = convert; action.convert = convert;
@@ -112,7 +109,7 @@ public final class SncpDynServlet extends SncpServlet {
if (bufferSupplier == null) { if (bufferSupplier == null) {
bufferSupplier = request.getContext().getBufferSupplier(); bufferSupplier = request.getContext().getBufferSupplier();
} }
SncpServletAction action = actions.get(request.getActionid()); final SncpServletAction action = actions.get(request.getActionid());
//if (finest) logger.log(Level.FINEST, "sncpdyn.execute: " + request + ", " + (action == null ? "null" : action.method)); //if (finest) logger.log(Level.FINEST, "sncpdyn.execute: " + request + ", " + (action == null ? "null" : action.method));
if (action == null) { if (action == null) {
response.finish(SncpResponse.RETCODE_ILLACTIONID, null); //无效actionid response.finish(SncpResponse.RETCODE_ILLACTIONID, null); //无效actionid
@@ -120,16 +117,52 @@ public final class SncpDynServlet extends SncpServlet {
BsonWriter out = action.convert.pollBsonWriter(bufferSupplier); BsonWriter out = action.convert.pollBsonWriter(bufferSupplier);
out.writeTo(DEFAULT_HEADER); out.writeTo(DEFAULT_HEADER);
BsonReader in = action.convert.pollBsonReader(); BsonReader in = action.convert.pollBsonReader();
SncpAsyncHandler handler = null;
try { try {
if (action.handlerFuncParamIndex >= 0) {
if (action.handlerFuncParamClass == AsyncHandler.class) {
handler = new DefaultSncpAsyncHandler(action, in, out, request, response);
} else {
Creator<SncpAsyncHandler> creator = action.handlerCreator;
if (creator == null) {
creator = SncpAsyncHandler.Factory.createCreator(action.handlerFuncParamClass);
action.handlerCreator = creator;
}
handler = creator.create(new DefaultSncpAsyncHandler(action, in, out, request, response));
}
} else if (action.boolReturnTypeFuture) {
handler = new DefaultSncpAsyncHandler(action, in, out, request, response);
}
in.setBytes(request.getBody()); in.setBytes(request.getBody());
action.action(in, out); action.action(in, out, handler);
response.finish(0, out); if (handler == null) {
response.finish(0, out);
action.convert.offerBsonReader(in);
action.convert.offerBsonWriter(out);
} else if (action.boolReturnTypeFuture) {
CompletableFuture future = handler.sncp_getFuture();
if (future == null) {
action._callParameter(out, handler.sncp_getParams());
action.convert.convertTo(out, Object.class, null);
} else {
Object[] sncpParams = handler.sncp_getParams();
future.whenComplete((v, e) -> {
if (e != null) {
response.getContext().getLogger().log(Level.INFO, "sncp CompleteAsync error(" + request + ")", e);
response.finish(SncpResponse.RETCODE_THROWEXCEPTION, null);
return;
}
action._callParameter(out, sncpParams);
action.convert.convertTo(out, Object.class, v);
response.finish(0, out);
action.convert.offerBsonReader(in);
action.convert.offerBsonWriter(out);
});
}
}
} catch (Throwable t) { } catch (Throwable t) {
response.getContext().getLogger().log(Level.INFO, "sncp execute error(" + request + ")", t); response.getContext().getLogger().log(Level.INFO, "sncp execute error(" + request + ")", t);
response.finish(SncpResponse.RETCODE_THROWEXCEPTION, null); response.finish(SncpResponse.RETCODE_THROWEXCEPTION, null);
} finally {
action.convert.offerBsonReader(in);
action.convert.offerBsonWriter(out);
} }
} }
} }
@@ -138,6 +171,8 @@ public final class SncpDynServlet extends SncpServlet {
public Method method; public Method method;
public Creator<SncpAsyncHandler> handlerCreator;
@Resource @Resource
protected BsonConvert convert; protected BsonConvert convert;
@@ -145,8 +180,15 @@ public final class SncpDynServlet extends SncpServlet {
protected java.lang.reflect.Type[] paramTypes; //index=0表示返回参数的type void的返回参数类型为null protected java.lang.reflect.Type[] paramTypes; //index=0表示返回参数的type void的返回参数类型为null
public abstract void action(final BsonReader in, final BsonWriter out) throws Throwable; protected int handlerFuncParamIndex = -1; //handlerFuncParamIndex>=0表示存在AsyncHandler参数
protected boolean boolReturnTypeFuture = false; // 返回结果类型是否为 CompletableFuture
protected Class handlerFuncParamClass; //AsyncHandler参数的类型
public abstract void action(final BsonReader in, final BsonWriter out, final SncpAsyncHandler handler) throws Throwable;
//只有同步方法才调用 (没有AsyncHandler、CompletableFuture)
public final void _callParameter(final BsonWriter out, final Object... params) { public final void _callParameter(final BsonWriter out, final Object... params) {
if (paramAttrs != null) { if (paramAttrs != null) {
for (int i = 1; i < paramAttrs.length; i++) { for (int i = 1; i < paramAttrs.length; i++) {
@@ -162,17 +204,29 @@ public final class SncpDynServlet extends SncpServlet {
/** /**
* <blockquote><pre> * <blockquote><pre>
* public class TestService implements Service { * public class TestService implements Service {
*
* public boolean change(TestBean bean, String name, int id) { * public boolean change(TestBean bean, String name, int id) {
* * return false;
* } * }
* }
* *
* public class DynActionTestService_change extends SncpServletAction { * public void insert(AsyncHandler&#60;Boolean, TestBean&#62; handler, TestBean bean, String name, int id) {
* }
*
* public void update(long show, short v2, AsyncHandler&#60;Boolean, TestBean&#62; handler, TestBean bean, String name, int id) {
* }
*
* public CompletableFuture&#60;String&#62; changeName(TestBean bean, String name, int id) {
* return null;
* }
* }
*
*
* class DynActionTestService_change extends SncpServletAction {
* *
* public TestService service; * public TestService service;
* *
* &#64;Override * &#064;Override
* public void action(final BsonReader in, final BsonWriter out) throws Throwable { * public void action(BsonReader in, BsonWriter out, SncpAsyncHandler handler) throws Throwable {
* TestBean arg1 = convert.convertFrom(paramTypes[1], in); * TestBean arg1 = convert.convertFrom(paramTypes[1], in);
* String arg2 = convert.convertFrom(paramTypes[2], in); * String arg2 = convert.convertFrom(paramTypes[2], in);
* int arg3 = convert.convertFrom(paramTypes[3], in); * int arg3 = convert.convertFrom(paramTypes[3], in);
@@ -180,7 +234,58 @@ public final class SncpDynServlet extends SncpServlet {
* _callParameter(out, arg1, arg2, arg3); * _callParameter(out, arg1, arg2, arg3);
* convert.convertTo(out, paramTypes[0], rs); * convert.convertTo(out, paramTypes[0], rs);
* } * }
* } * }
*
* class DynActionTestService_insert extends SncpServletAction {
*
* public TestService service;
*
* &#064;Override
* public void action(BsonReader in, BsonWriter out, SncpAsyncHandler handler) throws Throwable {
* SncpAsyncHandler arg0 = handler;
* convert.convertFrom(AsyncHandler.class, in);
* TestBean arg1 = convert.convertFrom(paramTypes[2], in);
* String arg2 = convert.convertFrom(paramTypes[3], in);
* int arg3 = convert.convertFrom(paramTypes[4], in);
* handler.sncp_setParams(arg0, arg1, arg2, arg3);
* service.insert(arg0, arg1, arg2, arg3);
* }
* }
*
* class DynActionTestService_update extends SncpServletAction {
*
* public TestService service;
*
* &#064;Override
* public void action(BsonReader in, BsonWriter out, SncpAsyncHandler handler) throws Throwable {
* long a1 = convert.convertFrom(paramTypes[1], in);
* short a2 = convert.convertFrom(paramTypes[2], in);
* SncpAsyncHandler a3 = handler;
* convert.convertFrom(AsyncHandler.class, in);
* TestBean arg1 = convert.convertFrom(paramTypes[4], in);
* String arg2 = convert.convertFrom(paramTypes[5], in);
* int arg3 = convert.convertFrom(paramTypes[6], in);
* handler.sncp_setParams(a1, a2, a3, arg1, arg2, arg3);
* service.update(a1, a2, a3, arg1, arg2, arg3);
* }
* }
*
*
* class DynActionTestService_changeName extends SncpServletAction {
*
* public TestService service;
*
* &#064;Override
* public void action(final BsonReader in, final BsonWriter out, final SncpAsyncHandler handler) throws Throwable {
* TestBean arg1 = convert.convertFrom(paramTypes[1], in);
* String arg2 = convert.convertFrom(paramTypes[2], in);
* int arg3 = convert.convertFrom(paramTypes[3], in);
* handler.sncp_setParams(arg1, arg2, arg3);
* CompletableFuture future = service.changeName(arg1, arg2, arg3);
* handler.sncp_setFuture(future);
* }
* }
*
* </pre></blockquote> * </pre></blockquote>
* *
* @param service Service * @param service Service
@@ -195,16 +300,19 @@ public final class SncpDynServlet extends SncpServlet {
final String supDynName = SncpServletAction.class.getName().replace('.', '/'); final String supDynName = SncpServletAction.class.getName().replace('.', '/');
final String serviceName = serviceClass.getName().replace('.', '/'); final String serviceName = serviceClass.getName().replace('.', '/');
final String convertName = BsonConvert.class.getName().replace('.', '/'); final String convertName = BsonConvert.class.getName().replace('.', '/');
final String handlerName = SncpAsyncHandler.class.getName().replace('.', '/');
final String asyncHandlerDesc = Type.getDescriptor(SncpAsyncHandler.class);
final String convertReaderDesc = Type.getDescriptor(BsonReader.class); final String convertReaderDesc = Type.getDescriptor(BsonReader.class);
final String convertWriterDesc = Type.getDescriptor(BsonWriter.class); final String convertWriterDesc = Type.getDescriptor(BsonWriter.class);
final String serviceDesc = Type.getDescriptor(serviceClass); final String serviceDesc = Type.getDescriptor(serviceClass);
final boolean boolReturnTypeFuture = CompletableFuture.class.isAssignableFrom(method.getReturnType());
String newDynName = serviceName.substring(0, serviceName.lastIndexOf('/') + 1) String newDynName = serviceName.substring(0, serviceName.lastIndexOf('/') + 1)
+ "DynAction" + serviceClass.getSimpleName() + "_" + method.getName() + "_" + actionid; + "DynAction" + serviceClass.getSimpleName() + "_" + method.getName() + "_" + actionid;
while (true) { while (true) {
try { try {
Class.forName(newDynName.replace('/', '.')); Class.forName(newDynName.replace('/', '.'));
newDynName += "_"; newDynName += "_";
} catch (Exception ex) { } catch (Throwable ex) {
break; break;
} }
} }
@@ -236,15 +344,42 @@ public final class SncpDynServlet extends SncpServlet {
} catch (Exception ex) { } catch (Exception ex) {
throw new RuntimeException(ex); //不可能会发生 throw new RuntimeException(ex); //不可能会发生
} }
int handlerFuncIndex = -1;
Class handlerFuncClass = null;
{ // action方法 { // action方法
mv = new AsmMethodVisitor(cw.visitMethod(ACC_PUBLIC, "action", "(" + convertReaderDesc + convertWriterDesc + ")V", null, new String[]{"java/lang/Throwable"})); mv = new AsmMethodVisitor(cw.visitMethod(ACC_PUBLIC, "action", "(" + convertReaderDesc + convertWriterDesc + asyncHandlerDesc + ")V", null, new String[]{"java/lang/Throwable"}));
//mv.setDebug(true); //mv.setDebug(true);
int iconst = ICONST_1; int iconst = ICONST_1;
int intconst = 1; int intconst = 1;
int store = 3; //action的参数个数+1 int store = 4; //action的参数个数+1
final Class[] paramClasses = method.getParameterTypes(); final Class[] paramClasses = method.getParameterTypes();
int[][] codes = new int[paramClasses.length][2]; int[][] codes = new int[paramClasses.length][2];
for (int i = 0; i < paramClasses.length; i++) { //参数 for (int i = 0; i < paramClasses.length; i++) { //反序列化方法的每个参数
if (AsyncHandler.class.isAssignableFrom(paramClasses[i])) {
if (boolReturnTypeFuture) {
throw new RuntimeException(method + " have both AsyncHandler and CompletableFuture");
}
if (handlerFuncIndex >= 0) {
throw new RuntimeException(method + " have more than one AsyncHandler type parameter");
}
Sncp.checkAsyncModifier(paramClasses[i], method);
handlerFuncIndex = i;
handlerFuncClass = paramClasses[i];
mv.visitVarInsn(ALOAD, 3);
mv.visitTypeInsn(CHECKCAST, paramClasses[i].getName().replace('.', '/'));
mv.visitVarInsn(ASTORE, store);
codes[i] = new int[]{ALOAD, store};
store++;
iconst++;
intconst++;
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, newDynName, "convert", Type.getDescriptor(BsonConvert.class));
mv.visitLdcInsn(Type.getType(Type.getDescriptor(AsyncHandler.class)));
mv.visitVarInsn(ALOAD, 1);
mv.visitMethodInsn(INVOKEVIRTUAL, convertName, "convertFrom", convertFromDesc, false);
mv.visitInsn(POP);
continue;
}
mv.visitVarInsn(ALOAD, 0); mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, newDynName, "convert", Type.getDescriptor(BsonConvert.class)); mv.visitFieldInsn(GETFIELD, newDynName, "convert", Type.getDescriptor(BsonConvert.class));
mv.visitVarInsn(ALOAD, 0); mv.visitVarInsn(ALOAD, 0);
@@ -296,6 +431,43 @@ public final class SncpDynServlet extends SncpServlet {
intconst++; intconst++;
store++; store++;
} }
if (boolReturnTypeFuture || handlerFuncIndex >= 0) { //调用SncpAsyncHandler.setParams(Object... params)
mv.visitVarInsn(ALOAD, 3);
if (paramClasses.length > 5) {
mv.visitIntInsn(BIPUSH, paramClasses.length);
} else {
mv.visitInsn(paramClasses.length + ICONST_0);
}
mv.visitTypeInsn(ANEWARRAY, "java/lang/Object");
int insn = 3; //action的参数个数
for (int j = 0; j < paramClasses.length; j++) {
final Class pt = paramClasses[j];
mv.visitInsn(DUP);
insn++;
if (j <= 5) {
mv.visitInsn(ICONST_0 + j);
} else {
mv.visitIntInsn(BIPUSH, j);
}
if (pt.isPrimitive()) {
if (pt == long.class) {
mv.visitVarInsn(LLOAD, insn++);
} else if (pt == float.class) {
mv.visitVarInsn(FLOAD, insn++);
} else if (pt == double.class) {
mv.visitVarInsn(DLOAD, insn++);
} else {
mv.visitVarInsn(ILOAD, insn);
}
Class bigclaz = java.lang.reflect.Array.get(java.lang.reflect.Array.newInstance(pt, 1), 0).getClass();
mv.visitMethodInsn(INVOKESTATIC, bigclaz.getName().replace('.', '/'), "valueOf", "(" + Type.getDescriptor(pt) + ")" + Type.getDescriptor(bigclaz), false);
} else {
mv.visitVarInsn(ALOAD, insn);
}
mv.visitInsn(AASTORE);
}
mv.visitMethodInsn(INVOKEINTERFACE, handlerName, "sncp_setParams", "([Ljava/lang/Object;)V", true);
}
{ //调用service { //调用service
mv.visitVarInsn(ALOAD, 0); mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, newDynName, "service", serviceDesc); mv.visitFieldInsn(GETFIELD, newDynName, "service", serviceDesc);
@@ -317,51 +489,57 @@ public final class SncpDynServlet extends SncpServlet {
} }
} }
mv.visitVarInsn(ASTORE, store); //11 mv.visitVarInsn(ASTORE, store); //11
} if (boolReturnTypeFuture) {
//------------------------- _callParameter 方法 -------------------------------- mv.visitVarInsn(ALOAD, 3);
mv.visitVarInsn(ALOAD, 0); mv.visitVarInsn(ALOAD, store);
mv.visitVarInsn(ALOAD, 2); mv.visitMethodInsn(INVOKEINTERFACE, handlerName, "sncp_setFuture", "(Ljava/util/concurrent/CompletableFuture;)V", true);
if (paramClasses.length <= 5) { //参数总数量
mv.visitInsn(ICONST_0 + paramClasses.length);
} else {
mv.visitIntInsn(BIPUSH, paramClasses.length);
}
mv.visitTypeInsn(ANEWARRAY, "java/lang/Object");
int insn = 2;
for (int j = 0; j < paramClasses.length; j++) {
final Class pt = paramClasses[j];
mv.visitInsn(DUP);
insn++;
if (j <= 5) {
mv.visitInsn(ICONST_0 + j);
} else {
mv.visitIntInsn(BIPUSH, j);
} }
if (pt.isPrimitive()) { }
if (pt == long.class) { if (!boolReturnTypeFuture && handlerFuncIndex < 0) { //同步方法
mv.visitVarInsn(LLOAD, insn++); //------------------------- _callParameter 方法 --------------------------------
} else if (pt == float.class) { mv.visitVarInsn(ALOAD, 0);
mv.visitVarInsn(FLOAD, insn++); mv.visitVarInsn(ALOAD, 2);
} else if (pt == double.class) { if (paramClasses.length <= 5) { //参数总数量
mv.visitVarInsn(DLOAD, insn++); mv.visitInsn(ICONST_0 + paramClasses.length);
} else {
mv.visitIntInsn(BIPUSH, paramClasses.length);
}
mv.visitTypeInsn(ANEWARRAY, "java/lang/Object");
int insn = 3;//action的参数个数
for (int j = 0; j < paramClasses.length; j++) {
final Class pt = paramClasses[j];
mv.visitInsn(DUP);
insn++;
if (j <= 5) {
mv.visitInsn(ICONST_0 + j);
} else { } else {
mv.visitVarInsn(ILOAD, insn); mv.visitIntInsn(BIPUSH, j);
} }
Class bigclaz = java.lang.reflect.Array.get(java.lang.reflect.Array.newInstance(pt, 1), 0).getClass(); if (pt.isPrimitive()) {
mv.visitMethodInsn(INVOKESTATIC, bigclaz.getName().replace('.', '/'), "valueOf", "(" + Type.getDescriptor(pt) + ")" + Type.getDescriptor(bigclaz), false); if (pt == long.class) {
} else { mv.visitVarInsn(LLOAD, insn++);
mv.visitVarInsn(ALOAD, insn); } else if (pt == float.class) {
mv.visitVarInsn(FLOAD, insn++);
} else if (pt == double.class) {
mv.visitVarInsn(DLOAD, insn++);
} else {
mv.visitVarInsn(ILOAD, insn);
}
Class bigclaz = java.lang.reflect.Array.get(java.lang.reflect.Array.newInstance(pt, 1), 0).getClass();
mv.visitMethodInsn(INVOKESTATIC, bigclaz.getName().replace('.', '/'), "valueOf", "(" + Type.getDescriptor(pt) + ")" + Type.getDescriptor(bigclaz), false);
} else {
mv.visitVarInsn(ALOAD, insn);
}
mv.visitInsn(AASTORE);
} }
mv.visitInsn(AASTORE); mv.visitMethodInsn(INVOKEVIRTUAL, newDynName, "_callParameter", "(" + convertWriterDesc + "[Ljava/lang/Object;)V", false);
} }
mv.visitMethodInsn(INVOKEVIRTUAL, newDynName, "_callParameter", "(" + convertWriterDesc + "[Ljava/lang/Object;)V", false);
//-------------------------直接返回 或者 调用convertTo方法 -------------------------------- //-------------------------直接返回 或者 调用convertTo方法 --------------------------------
int maxStack = codes.length > 0 ? codes[codes.length - 1][1] : 1; int maxStack = codes.length > 0 ? codes[codes.length - 1][1] : 1;
if (returnClass == void.class) { //返回 if (boolReturnTypeFuture || returnClass == void.class) { //返回
mv.visitInsn(RETURN); mv.visitInsn(RETURN);
maxStack = 8; maxStack = 8;
} else { } else { //同步方法调用
mv.visitVarInsn(ALOAD, 0); mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, newDynName, "convert", Type.getDescriptor(BsonConvert.class)); mv.visitFieldInsn(GETFIELD, newDynName, "convert", Type.getDescriptor(BsonConvert.class));
mv.visitVarInsn(ALOAD, 2); mv.visitVarInsn(ALOAD, 2);
@@ -398,6 +576,9 @@ public final class SncpDynServlet extends SncpServlet {
types[0] = rt; types[0] = rt;
System.arraycopy(ptypes, 0, types, 1, ptypes.length); System.arraycopy(ptypes, 0, types, 1, ptypes.length);
instance.paramTypes = types; instance.paramTypes = types;
instance.handlerFuncParamIndex = handlerFuncIndex;
instance.handlerFuncParamClass = handlerFuncClass;
instance.boolReturnTypeFuture = boolReturnTypeFuture;
org.redkale.util.Attribute[] atts = new org.redkale.util.Attribute[ptypes.length + 1]; org.redkale.util.Attribute[] atts = new org.redkale.util.Attribute[ptypes.length + 1];
Annotation[][] anns = method.getParameterAnnotations(); Annotation[][] anns = method.getParameterAnnotations();

View File

@@ -0,0 +1,16 @@
/*
* 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.sncp;
import org.redkale.net.Filter;
/**
*
* @author zhangjx
*/
public abstract class SncpFilter extends Filter<SncpContext, SncpRequest, SncpResponse> {
}

View File

@@ -10,6 +10,7 @@ import org.redkale.util.AnyValue;
import java.io.IOException; import java.io.IOException;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.util.*; import java.util.*;
import org.redkale.service.Service;
import org.redkale.util.*; import org.redkale.util.*;
/** /**
@@ -21,35 +22,53 @@ import org.redkale.util.*;
*/ */
public class SncpPrepareServlet extends PrepareServlet<DLong, SncpContext, SncpRequest, SncpResponse, SncpServlet> { public class SncpPrepareServlet extends PrepareServlet<DLong, SncpContext, SncpRequest, SncpResponse, SncpServlet> {
private final Object sncplock = new Object();
private static final ByteBuffer pongBuffer = ByteBuffer.wrap("PONG".getBytes()).asReadOnlyBuffer(); private static final ByteBuffer pongBuffer = ByteBuffer.wrap("PONG".getBytes()).asReadOnlyBuffer();
@Override @Override
public void addServlet(SncpServlet servlet, Object attachment, AnyValue conf, DLong... mappings) { public void addServlet(SncpServlet servlet, Object attachment, AnyValue conf, DLong... mappings) {
addServlet((SncpServlet) servlet, conf); synchronized (sncplock) {
} for (SncpServlet s : getServlets()) {
if (s.service == servlet.service) throw new RuntimeException(s.service + " repeat addSncpServlet");
public void addServlet(SncpServlet servlet, AnyValue conf) { }
setServletConf(servlet, conf); setServletConf(servlet, conf);
synchronized (mappings) { putMapping(servlet.getServiceid(), servlet);
mappings.put(servlet.getServiceid(), servlet); putServlet(servlet);
servlets.add(servlet);
} }
} }
public <T> SncpServlet removeSncpServlet(Service service) {
SncpServlet rs = null;
synchronized (sncplock) {
for (SncpServlet servlet : getServlets()) {
if (servlet.service == service) {
rs = servlet;
break;
}
}
if (rs != null) {
removeMapping(rs);
removeServlet(rs);
}
}
return rs;
}
public List<SncpServlet> getSncpServlets() { public List<SncpServlet> getSncpServlets() {
ArrayList<SncpServlet> list = new ArrayList<>(servlets.size()); return new ArrayList<>(getServlets());
servlets.forEach(x -> list.add((SncpServlet) x));
return list;
} }
@Override @Override
public void init(SncpContext context, AnyValue config) { public void init(SncpContext context, AnyValue config) {
servlets.forEach(s -> s.init(context, getServletConf(s))); super.init(context, config); //必须要执行
getServlets().forEach(s -> s.init(context, getServletConf(s)));
} }
@Override @Override
public void destroy(SncpContext context, AnyValue config) { public void destroy(SncpContext context, AnyValue config) {
servlets.forEach(s -> s.destroy(context, getServletConf(s))); super.destroy(context, config); //必须要执行
getServlets().forEach(s -> s.destroy(context, getServletConf(s)));
} }
@Override @Override
@@ -58,7 +77,7 @@ public class SncpPrepareServlet extends PrepareServlet<DLong, SncpContext, SncpR
response.finish(pongBuffer.duplicate()); response.finish(pongBuffer.duplicate());
return; return;
} }
SncpServlet servlet = (SncpServlet) mappings.get(request.getServiceid()); SncpServlet servlet = (SncpServlet) mappingServlet(request.getServiceid());
if (servlet == null) { if (servlet == null) {
response.finish(SncpResponse.RETCODE_ILLSERVICEID, null); //无效serviceid response.finish(SncpResponse.RETCODE_ILLSERVICEID, null); //无效serviceid
} else { } else {

View File

@@ -12,7 +12,6 @@ import org.redkale.convert.bson.*;
import org.redkale.net.*; import org.redkale.net.*;
import org.redkale.service.Service; import org.redkale.service.Service;
import org.redkale.util.*; import org.redkale.util.*;
import org.redkale.watch.*;
/** /**
* Service Node Communicate Protocol * Service Node Communicate Protocol
@@ -23,14 +22,14 @@ import org.redkale.watch.*;
* @author zhangjx * @author zhangjx
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public final class SncpServer extends Server<DLong, SncpContext, SncpRequest, SncpResponse, SncpServlet> { public class SncpServer extends Server<DLong, SncpContext, SncpRequest, SncpResponse, SncpServlet> {
public SncpServer() { public SncpServer() {
this(System.currentTimeMillis(), null); this(System.currentTimeMillis());
} }
public SncpServer(long serverStartTime, final WatchFactory watch) { public SncpServer(long serverStartTime) {
super(serverStartTime, "TCP", new SncpPrepareServlet(), watch); super(serverStartTime, "TCP", new SncpPrepareServlet());
} }
@Override @Override
@@ -38,16 +37,56 @@ public final class SncpServer extends Server<DLong, SncpContext, SncpRequest, Sn
super.init(config); super.init(config);
} }
public void addSncpServlet(ServiceWrapper entry) { /**
for (Class type : entry.getTypes()) { * 删除SncpFilter
SncpDynServlet sds = new SncpDynServlet(BsonFactory.root().getConvert(), entry.getName(), type, entry.getService()); *
this.prepare.addServlet(sds, null, entry.getConf()); * @param filterName SncpFilter名称
} *
* @return SncpFilter
*/
public SncpFilter removeFilter(String filterName) {
return (SncpFilter) this.prepare.removeFilter(filterName);
} }
public <T extends Service> void addSncpServlet(Class<T> serviceType, String name, T service, AnyValue conf) { /**
SncpDynServlet sds = new SncpDynServlet(BsonFactory.root().getConvert(), name, serviceType, service); * 删除SncpFilter
this.prepare.addServlet(sds, null, conf); *
* @param <T> 泛型
* @param filterClass SncpFilter类
*
* @return SncpFilter
*/
public <T extends SncpFilter> T removeFilter(Class<T> filterClass) {
return (T) this.prepare.removeFilter(filterClass);
}
/**
* 添加SncpFilter
*
* @param filter SncpFilter
* @param conf AnyValue
*
* @return SncpServer
*/
public SncpServer addSncpFilter(SncpFilter filter, AnyValue conf) {
this.prepare.addFilter(filter, conf);
return this;
}
/**
* 删除SncpServlet
*
* @param sncpService Service
*
* @return SncpServlet
*/
public SncpServlet removeSncpServlet(Service sncpService) {
return ((SncpPrepareServlet) this.prepare).removeSncpServlet(sncpService);
}
public void addSncpServlet(Service sncpService) {
SncpDynServlet sds = new SncpDynServlet(BsonFactory.root().getConvert(), Sncp.getResourceName(sncpService), Sncp.getResourceType(sncpService), sncpService);
this.prepare.addServlet(sds, null, Sncp.getConf(sncpService));
} }
public List<SncpServlet> getSncpServlets() { public List<SncpServlet> getSncpServlets() {
@@ -58,8 +97,8 @@ public final class SncpServer extends Server<DLong, SncpContext, SncpRequest, Sn
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
protected SncpContext createContext() { protected SncpContext createContext() {
final int port = this.address.getPort(); final int port = this.address.getPort();
AtomicLong createBufferCounter = watch == null ? new AtomicLong() : watch.createWatchNumber("SNCP_" + port + ".Buffer.creatCounter"); AtomicLong createBufferCounter = new AtomicLong();
AtomicLong cycleBufferCounter = watch == null ? new AtomicLong() : watch.createWatchNumber("SNCP_" + port + ".Buffer.cycleCounter"); AtomicLong cycleBufferCounter = new AtomicLong();
final int rcapacity = Math.max(this.bufferCapacity, 4 * 1024); final int rcapacity = Math.max(this.bufferCapacity, 4 * 1024);
ObjectPool<ByteBuffer> bufferPool = new ObjectPool<>(createBufferCounter, cycleBufferCounter, this.bufferPoolSize, ObjectPool<ByteBuffer> bufferPool = new ObjectPool<>(createBufferCounter, cycleBufferCounter, this.bufferPoolSize,
(Object... params) -> ByteBuffer.allocateDirect(rcapacity), null, (e) -> { (Object... params) -> ByteBuffer.allocateDirect(rcapacity), null, (e) -> {
@@ -67,11 +106,11 @@ public final class SncpServer extends Server<DLong, SncpContext, SncpRequest, Sn
e.clear(); e.clear();
return true; return true;
}); });
AtomicLong createResponseCounter = watch == null ? new AtomicLong() : watch.createWatchNumber("SNCP_" + port + ".Response.creatCounter"); AtomicLong createResponseCounter = new AtomicLong();
AtomicLong cycleResponseCounter = watch == null ? new AtomicLong() : watch.createWatchNumber("SNCP_" + port + ".Response.cycleCounter"); AtomicLong cycleResponseCounter = new AtomicLong();
ObjectPool<Response> responsePool = SncpResponse.createPool(createResponseCounter, cycleResponseCounter, this.responsePoolSize, null); ObjectPool<Response> responsePool = SncpResponse.createPool(createResponseCounter, cycleResponseCounter, this.responsePoolSize, null);
SncpContext sncpcontext = new SncpContext(this.serverStartTime, this.logger, executor, rcapacity, bufferPool, responsePool, SncpContext sncpcontext = new SncpContext(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 SncpResponse(sncpcontext, new SncpRequest(sncpcontext))); responsePool.setCreator((Object... params) -> new SncpResponse(sncpcontext, new SncpRequest(sncpcontext)));
return sncpcontext; return sncpcontext;
} }

View File

@@ -6,7 +6,9 @@
package org.redkale.net.sncp; package org.redkale.net.sncp;
import java.util.Objects; import java.util.Objects;
import java.util.concurrent.*;
import org.redkale.net.*; import org.redkale.net.*;
import org.redkale.service.Service;
import org.redkale.util.*; import org.redkale.util.*;
/** /**
@@ -18,8 +20,36 @@ import org.redkale.util.*;
*/ */
public abstract class SncpServlet extends Servlet<SncpContext, SncpRequest, SncpResponse> implements Comparable<SncpServlet> { public abstract class SncpServlet extends Servlet<SncpContext, SncpRequest, SncpResponse> implements Comparable<SncpServlet> {
protected final Class type;
protected final String serviceName;
protected final Service service;
protected SncpServlet(String serviceName, Class serviceOrSourceType, Service service) {
this.type = serviceOrSourceType;
this.service = service;
this.serviceName = serviceName;
}
public Service getService() {
return service;
}
public String getServiceName() {
return serviceName;
}
public abstract DLong getServiceid(); public abstract DLong getServiceid();
protected ExecutorService getExecutor() {
Thread thread = Thread.currentThread();
if (thread instanceof WorkThread) {
return ((WorkThread) thread).getExecutor();
}
return ForkJoinPool.commonPool();
}
@Override @Override
public final boolean equals(Object obj) { public final boolean equals(Object obj) {
if (!(obj instanceof SncpServlet)) return false; if (!(obj instanceof SncpServlet)) return 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.service;
import java.util.concurrent.*;
import org.redkale.net.WorkThread;
/**
*
* @author zhangjx
*/
public abstract class AbstractService implements Service {
protected void runAsync(Runnable runner) {
Thread thread = Thread.currentThread();
if (thread instanceof WorkThread) {
((WorkThread) thread).runAsync(runner);
} else {
ForkJoinPool.commonPool().execute(runner);
}
}
protected ExecutorService getExecutor() {
Thread thread = Thread.currentThread();
if (thread instanceof WorkThread) {
return ((WorkThread) thread).getExecutor();
}
return ForkJoinPool.commonPool();
}
}

View File

@@ -1,501 +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.service;
import java.beans.*;
import java.io.*;
import java.lang.reflect.*;
import java.util.*;
import java.util.concurrent.*;
import java.util.function.*;
import java.util.logging.*;
import javax.annotation.*;
import org.redkale.convert.*;
import org.redkale.convert.json.*;
import org.redkale.net.sncp.*;
import org.redkale.source.*;
import org.redkale.util.*;
/**
*
* @param <K> key类型
* @param <V> value类型
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*/
@AutoLoad(false)
@ResourceType({CacheSourceService.class, CacheSource.class})
public class CacheSourceService<K extends Serializable, V extends Object> implements CacheSource<K, V>, Service, AutoCloseable {
@Resource(name = "APP_HOME")
private File home;
@Resource
private JsonConvert convert;
private boolean needStore;
private Class keyType;
private Type objValueType;
private Type setValueType;
private Type listValueType;
private ScheduledThreadPoolExecutor scheduler;
private Consumer<CacheEntry> expireHandler;
private final Logger logger = Logger.getLogger(this.getClass().getSimpleName());
protected final ConcurrentHashMap<K, CacheEntry<K, ?>> container = new ConcurrentHashMap<>();
public CacheSourceService() {
}
public final CacheSourceService setStoreType(Class keyType, Class valueType) {
this.keyType = keyType;
this.objValueType = valueType;
this.setValueType = TypeToken.createParameterizedType(null, CopyOnWriteArraySet.class, valueType);
this.listValueType = TypeToken.createParameterizedType(null, ConcurrentLinkedQueue.class, valueType);
this.setNeedStore(this.keyType != null && this.keyType != Serializable.class && this.objValueType != null);
return this;
}
public final void setNeedStore(boolean needStore) {
this.needStore = needStore;
}
@Override
public void init(AnyValue conf) {
final CacheSourceService self = this;
AnyValue prop = conf == null ? null : conf.getAnyValue("property");
if (keyType == null && prop != null) {
String storeKeyStr = prop.getValue("key-type");
String storeValueStr = prop.getValue("value-type");
if (storeKeyStr != null && storeValueStr != null) {
try {
this.setStoreType(Class.forName(storeKeyStr), Class.forName(storeValueStr));
} catch (Exception e) {
logger.log(Level.SEVERE, self.getClass().getSimpleName() + " load key & value store class (" + storeKeyStr + ", " + storeValueStr + ") error", e);
}
}
if (prop.getBoolValue("store-ignore", false)) setNeedStore(false);
}
String expireHandlerClass = prop == null ? null : prop.getValue("expirehandler");
if (expireHandlerClass != null) {
try {
this.expireHandler = (Consumer<CacheEntry>) Class.forName(expireHandlerClass).newInstance();
} catch (Exception e) {
logger.log(Level.SEVERE, self.getClass().getSimpleName() + " new expirehandler class (" + expireHandlerClass + ") instance error", e);
}
}
if (scheduler == null) {
this.scheduler = new ScheduledThreadPoolExecutor(1, (Runnable r) -> {
final Thread t = new Thread(r, self.getClass().getSimpleName() + "-Expirer-Thread");
t.setDaemon(true);
return t;
});
final List<K> keys = new ArrayList<>();
scheduler.scheduleWithFixedDelay(() -> {
keys.clear();
int now = (int) (System.currentTimeMillis() / 1000);
container.forEach((k, x) -> {
if (x.expireSeconds > 0 && (now > (x.lastAccessed + x.expireSeconds))) {
keys.add(x.key);
}
});
for (K key : keys) {
CacheEntry entry = container.remove(key);
if (expireHandler != null && entry != null) expireHandler.accept(entry);
}
}, 10, 10, TimeUnit.SECONDS);
logger.finest(self.getClass().getSimpleName() + ":" + self.name() + " start schedule expire executor");
}
if (Sncp.isRemote(self)) return;
boolean datasync = false; //是否从远程同步过数据
//----------同步数据……-----------
// TODO
if (!this.needStore) return;
try {
File store = new File(home, "cache/" + name());
if (!store.isFile() || !store.canRead()) return;
LineNumberReader reader = new LineNumberReader(new FileReader(store));
if (this.keyType == null) this.keyType = Serializable.class;
if (this.objValueType == null) {
this.objValueType = Object.class;
this.setValueType = TypeToken.createParameterizedType(null, CopyOnWriteArraySet.class, this.objValueType);
this.listValueType = TypeToken.createParameterizedType(null, ConcurrentLinkedQueue.class, this.objValueType);
}
final Type storeObjType = TypeToken.createParameterizedType(null, CacheEntry.class, keyType, objValueType);
final Type storeSetType = TypeToken.createParameterizedType(null, CacheEntry.class, keyType, setValueType);
final Type storeListType = TypeToken.createParameterizedType(null, CacheEntry.class, keyType, listValueType);
String line;
while ((line = reader.readLine()) != null) {
if (line.isEmpty()) continue;
CacheEntry<K, ?> entry = convert.convertFrom(line.startsWith(CacheEntry.JSON_SET_KEY) ? storeSetType : (line.startsWith(CacheEntry.JSON_LIST_KEY) ? storeListType : storeObjType), line);
if (entry.isExpired()) continue;
if (datasync && container.containsKey(entry.key)) continue; //已经同步了
container.put(entry.key, entry);
}
reader.close();
store.delete();
} catch (Exception e) {
logger.log(Level.SEVERE, CacheSource.class.getSimpleName() + "(" + name() + ") load store file error ", e);
}
}
@Override
public void close() throws Exception { //给Application 关闭时调用
destroy(null);
}
public String name() {
return this.getClass().getAnnotation(Resource.class).name();
}
@Override
public void destroy(AnyValue conf) {
if (scheduler != null) scheduler.shutdownNow();
if (!this.needStore || Sncp.isRemote(this) || container.isEmpty()) return;
try {
File store = new File(home, "cache/" + name());
store.getParentFile().mkdirs();
PrintStream stream = new PrintStream(store, "UTF-8");
final Type storeObjType = TypeToken.createParameterizedType(null, CacheEntry.class, keyType, objValueType);
final Type storeSetType = TypeToken.createParameterizedType(null, CacheEntry.class, keyType, setValueType);
final Type storeListType = TypeToken.createParameterizedType(null, CacheEntry.class, keyType, listValueType);
Collection<CacheEntry<K, ?>> entrys = (Collection<CacheEntry<K, ?>>) container.values();
for (CacheEntry entry : entrys) {
stream.println(convert.convertTo(entry.isSetCacheType() ? storeSetType : (entry.isListCacheType() ? storeListType : storeObjType), entry));
}
container.clear();
stream.close();
} catch (Exception e) {
logger.log(Level.SEVERE, CacheSource.class.getSimpleName() + "(" + name() + ") store to file error ", e);
}
}
@Override
public boolean exists(K key) {
if (key == null) return false;
CacheEntry entry = container.get(key);
if (entry == null) return false;
return !entry.isExpired();
}
@Override
public boolean exists(final AsyncHandler<Boolean, K> handler, @RpcAttachment final K key) {
boolean rs = exists(key);
if (handler != null) handler.completed(rs, key);
return rs;
}
@Override
public V get(K key) {
if (key == null) return null;
CacheEntry entry = container.get(key);
if (entry == null || entry.isExpired() || entry.value == null) return null;
if (entry.isListCacheType()) return (V) new ArrayList((Collection) entry.value);
if (entry.isSetCacheType()) return (V) new HashSet((Collection) entry.value);
return (V) entry.getValue();
}
@Override
public V get(final AsyncHandler<V, K> handler, @RpcAttachment final K key) {
V rs = get(key);
if (handler != null) handler.completed(rs, key);
return rs;
}
@Override
@RpcMultiRun
public V getAndRefresh(K key, final int expireSeconds) {
if (key == null) return null;
CacheEntry entry = container.get(key);
if (entry == null || entry.isExpired() || entry.value == null) return null;
entry.lastAccessed = (int) (System.currentTimeMillis() / 1000);
entry.expireSeconds = expireSeconds;
if (entry.isListCacheType()) return (V) new ArrayList((Collection) entry.value);
if (entry.isSetCacheType()) return (V) new HashSet((Collection) entry.value);
return (V) entry.getValue();
}
@Override
public V getAndRefresh(final AsyncHandler<V, K> handler, @RpcAttachment final K key, final int expireSeconds) {
V rs = getAndRefresh(key, expireSeconds);
if (handler != null) handler.completed(rs, key);
return rs;
}
@Override
@RpcMultiRun
public void refresh(K key, final int expireSeconds) {
if (key == null) return;
CacheEntry entry = container.get(key);
if (entry == null) return;
entry.lastAccessed = (int) (System.currentTimeMillis() / 1000);
entry.expireSeconds = expireSeconds;
}
@Override
public void refresh(final AsyncHandler<Void, K> handler, @RpcAttachment final K key, final int expireSeconds) {
refresh(key, expireSeconds);
if (handler != null) handler.completed(null, key);
}
@Override
@RpcMultiRun
public void set(K key, V value) {
if (key == null) return;
CacheEntry entry = container.get(key);
if (entry == null) {
entry = new CacheEntry(CacheEntryType.OBJECT, key, value);
container.putIfAbsent(key, entry);
} else {
entry.expireSeconds = 0;
entry.value = value;
entry.lastAccessed = (int) (System.currentTimeMillis() / 1000);
}
}
@Override
public void set(final AsyncHandler<Void, K> handler, @RpcAttachment final K key, final V value) {
set(key, value);
if (handler != null) handler.completed(null, key);
}
@Override
@RpcMultiRun
public void set(int expireSeconds, K key, V value) {
if (key == null) return;
CacheEntry entry = container.get(key);
if (entry == null) {
entry = new CacheEntry(CacheEntryType.OBJECT, expireSeconds, key, value);
container.putIfAbsent(key, entry);
} else {
if (expireSeconds > 0) entry.expireSeconds = expireSeconds;
entry.lastAccessed = (int) (System.currentTimeMillis() / 1000);
entry.value = value;
}
}
@Override
public void set(final AsyncHandler<Void, K> handler, final int expireSeconds, @RpcAttachment final K key, final V value) {
set(expireSeconds, key, value);
if (handler != null) handler.completed(null, key);
}
@Override
@RpcMultiRun
public void setExpireSeconds(K key, int expireSeconds) {
if (key == null) return;
CacheEntry entry = container.get(key);
if (entry == null) return;
entry.expireSeconds = expireSeconds;
}
@Override
public void setExpireSeconds(final AsyncHandler<Void, K> handler, @RpcAttachment final K key, final int expireSeconds) {
setExpireSeconds(key, expireSeconds);
if (handler != null) handler.completed(null, key);
}
@Override
@RpcMultiRun
public void remove(K key) {
if (key == null) return;
container.remove(key);
}
@Override
public void remove(final AsyncHandler<Void, K> handler, @RpcAttachment final K key) {
remove(key);
if (handler != null) handler.completed(null, key);
}
@Override
public Collection<V> getCollection(final K key) {
return (Collection<V>) get(key);
}
@Override
public Collection<V> getCollection(final AsyncHandler<Collection<V>, K> handler, @RpcAttachment final K key) {
Collection<V> rs = getCollection(key);
if (handler != null) handler.completed(rs, key);
return rs;
}
@Override
public Collection<V> getCollectionAndRefresh(final K key, final int expireSeconds) {
return (Collection<V>) getAndRefresh(key, expireSeconds);
}
@Override
public Collection<V> getCollectionAndRefresh(final AsyncHandler<Collection<V>, K> handler, @RpcAttachment final K key, final int expireSeconds) {
Collection<V> rs = getCollectionAndRefresh(key, expireSeconds);
if (handler != null) handler.completed(rs, key);
return rs;
}
@Override
@RpcMultiRun
public void appendListItem(K key, V value) {
if (key == null) return;
CacheEntry entry = container.get(key);
if (entry == null || !entry.isListCacheType()) {
Collection<V> list = new ConcurrentLinkedQueue<>();
entry = new CacheEntry(CacheEntryType.LIST, key, list);
CacheEntry old = container.putIfAbsent(key, entry);
if (old != null) list = (Collection) old.value;
list.add(value);
} else {
((Collection) entry.getValue()).add(value);
}
}
@Override
public void appendListItem(final AsyncHandler<Void, K> handler, @RpcAttachment final K key, final V value) {
appendListItem(key, value);
if (handler != null) handler.completed(null, key);
}
@Override
@RpcMultiRun
public void removeListItem(K key, V value) {
if (key == null) return;
CacheEntry entry = container.get(key);
if (entry == null || !entry.isListCacheType()) return;
((Collection) entry.getValue()).remove(value);
}
@Override
public void removeListItem(final AsyncHandler<Void, K> handler, @RpcAttachment final K key, final V value) {
removeListItem(key, value);
if (handler != null) handler.completed(null, key);
}
@Override
@RpcMultiRun
public void appendSetItem(K key, V value) {
if (key == null) return;
CacheEntry entry = container.get(key);
if (entry == null || !entry.isSetCacheType()) {
Collection<V> set = new CopyOnWriteArraySet();
entry = new CacheEntry(CacheEntryType.SET, key, set);
CacheEntry old = container.putIfAbsent(key, entry);
if (old != null) set = (Collection) old.value;
set.add(value);
} else {
((Collection) entry.getValue()).add(value);
}
}
@Override
public void appendSetItem(final AsyncHandler<Void, K> handler, @RpcAttachment final K key, final V value) {
appendSetItem(key, value);
if (handler != null) handler.completed(null, key);
}
@Override
@RpcMultiRun
public void removeSetItem(K key, V value) {
if (key == null) return;
CacheEntry entry = container.get(key);
if (entry == null || !(entry.value instanceof Set)) return;
((Set) entry.getValue()).remove(value);
}
@Override
public void removeSetItem(final AsyncHandler<Void, K> handler, @RpcAttachment final K key, final V value) {
removeSetItem(key, value);
if (handler != null) handler.completed(null, key);
}
public static enum CacheEntryType {
OBJECT, SET, LIST;
}
public static final class CacheEntry<K extends Serializable, T> {
public static final String JSON_SET_KEY = "{\"cacheType\":\"" + CacheEntryType.SET + "\"";
public static final String JSON_LIST_KEY = "{\"cacheType\":\"" + CacheEntryType.LIST + "\"";
private final CacheEntryType cacheType;
private final K key;
//<=0表示永久保存
private int expireSeconds;
private volatile int lastAccessed; //最后刷新时间
private T value;
public CacheEntry(CacheEntryType cacheType, K key, T value) {
this(cacheType, 0, key, value);
}
public CacheEntry(CacheEntryType cacheType, int expireSeconds, K key, T value) {
this(cacheType, expireSeconds, (int) (System.currentTimeMillis() / 1000), key, value);
}
@ConstructorProperties({"cacheType", "expireSeconds", "lastAccessed", "key", "value"})
public CacheEntry(CacheEntryType cacheType, int expireSeconds, int lastAccessed, K key, T value) {
this.cacheType = cacheType;
this.expireSeconds = expireSeconds;
this.lastAccessed = lastAccessed;
this.key = key;
this.value = value;
}
@Override
public String toString() {
return JsonFactory.root().getConvert().convertTo(this);
}
@ConvertColumn(ignore = true)
public boolean isListCacheType() {
return cacheType == CacheEntryType.LIST;
}
@ConvertColumn(ignore = true)
public boolean isSetCacheType() {
return cacheType == CacheEntryType.SET;
}
@ConvertColumn(ignore = true)
public boolean isExpired() {
return (expireSeconds > 0 && lastAccessed + expireSeconds < (System.currentTimeMillis() / 1000));
}
public CacheEntryType getCacheType() {
return cacheType;
}
public int getExpireSeconds() {
return expireSeconds;
}
public int getLastAccessed() {
return lastAccessed;
}
public T getValue() {
return value;
}
public K getKey() {
return key;
}
}
}

View File

@@ -11,6 +11,7 @@ import org.redkale.source.*;
import org.redkale.util.*; import org.redkale.util.*;
/** /**
* 实现进程间DataSource的缓存数据同步
* *
* <p> * <p>
* 详情见: https://redkale.org * 详情见: https://redkale.org
@@ -18,7 +19,7 @@ import org.redkale.util.*;
* @author zhangjx * @author zhangjx
*/ */
@AutoLoad(false) @AutoLoad(false)
@ResourceType({DataCacheListenerService.class, DataCacheListener.class}) @ResourceType(DataCacheListener.class)
public class DataCacheListenerService implements DataCacheListener, Service { public class DataCacheListenerService implements DataCacheListener, Service {
@Resource(name = "$") @Resource(name = "$")
@@ -26,20 +27,23 @@ public class DataCacheListenerService implements DataCacheListener, Service {
@Override @Override
@RpcMultiRun(selfrun = false, async = true) @RpcMultiRun(selfrun = false, async = true)
public <T> void insertCache(Class<T> clazz, T... entitys) { public <T> int insertCache(Class<T> clazz, T... entitys) {
((DataDefaultSource) source).insertCache(clazz, entitys); if (!(source instanceof DataCacheListener)) return -2;
return ((DataCacheListener) source).insertCache(clazz, entitys);
} }
@Override @Override
@RpcMultiRun(selfrun = false, async = true) @RpcMultiRun(selfrun = false, async = true)
public <T> void updateCache(Class<T> clazz, T... entitys) { public <T> int updateCache(Class<T> clazz, T... entitys) {
((DataDefaultSource) source).updateCache(clazz, entitys); if (!(source instanceof DataCacheListener)) return -2;
return ((DataCacheListener) source).updateCache(clazz, entitys);
} }
@Override @Override
@RpcMultiRun(selfrun = false, async = true) @RpcMultiRun(selfrun = false, async = true)
public <T> void deleteCache(Class<T> clazz, Serializable... ids) { public <T> int deleteCache(Class<T> clazz, Serializable... ids) {
((DataDefaultSource) source).deleteCache(clazz, ids); if (!(source instanceof DataCacheListener)) return -2;
return ((DataCacheListener) source).deleteCache(clazz, ids);
} }
} }

View File

@@ -1,849 +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.service;
import java.io.*;
import java.sql.*;
import java.util.*;
import java.util.function.*;
import javax.annotation.*;
import org.redkale.source.*;
import org.redkale.util.*;
/**
* DataSource对应的Service类 该类主要特点是将所有含FilterBean参数的方法重载成FilterNode对应的方法。
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*/
@AutoLoad(false)
@ResourceType({DataSourceService.class, DataSource.class})
public class DataSourceService implements DataSource, Service, AutoCloseable {
@Resource(name = "$")
private DataSource source;
@Override
public <T> void insert(@RpcCall(DataCallArrayAttribute.class) T... values) {
source.insert(values);
}
@Override
public <T> void insert(final AsyncHandler<Void, T[]> handler, @RpcAttachment @RpcCall(DataCallArrayAttribute.class) final T... values) {
source.insert(values);
if (handler != null) handler.completed(null, values);
}
@Override
public <T> int delete(T... values) {
return source.delete(values);
}
@Override
public <T> int delete(final AsyncHandler<Integer, T[]> handler, @RpcAttachment final T... values) {
int rs = source.delete(values);
if (handler != null) handler.completed(rs, values);
return rs;
}
@Override
public <T> int delete(final Class<T> clazz, final Serializable... ids) {
return source.delete(clazz, ids);
}
@Override
public <T> int delete(final AsyncHandler<Integer, Serializable[]> handler, final Class<T> clazz, @RpcAttachment final Serializable... ids) {
int rs = source.delete(clazz, ids);
if (handler != null) handler.completed(rs, ids);
return rs;
}
@Override
public <T> int delete(final Class<T> clazz, FilterNode node) {
return source.delete(clazz, node);
}
@Override
public <T> int delete(final AsyncHandler<Integer, FilterNode> handler, final Class<T> clazz, @RpcAttachment final FilterNode node) {
int rs = source.delete(clazz, node);
if (handler != null) handler.completed(rs, node);
return rs;
}
@Override
public <T> int delete(final Class<T> clazz, final Flipper flipper, FilterNode node) {
return source.delete(clazz, flipper, node);
}
@Override
public <T> int delete(final AsyncHandler<Integer, FilterNode> handler, final Class<T> clazz, final Flipper flipper, @RpcAttachment FilterNode node) {
int rs = source.delete(clazz, flipper, node);
if (handler != null) handler.completed(rs, node);
return rs;
}
@Override
public <T> int update(T... values) {
return source.update(values);
}
@Override
public <T> int update(final AsyncHandler<Integer, T[]> handler, @RpcAttachment final T... values) {
int rs = source.update(values);
if (handler != null) handler.completed(rs, values);
return rs;
}
@Override
public <T> int updateColumn(final Class<T> clazz, final Serializable id, final String column, final Serializable value) {
return source.updateColumn(clazz, id, column, value);
}
@Override
public <T> int updateColumn(final AsyncHandler<Integer, Serializable> handler, final Class<T> clazz, @RpcAttachment final Serializable id, final String column, final Serializable value) {
int rs = source.updateColumn(clazz, id, column, value);
if (handler != null) handler.completed(rs, id);
return rs;
}
@Override
public <T> int updateColumn(final Class<T> clazz, final String column, final Serializable value, final FilterNode node) {
return source.updateColumn(clazz, column, value, node);
}
@Override
public <T> int updateColumn(final AsyncHandler<Integer, FilterNode> handler, final Class<T> clazz, final String column, final Serializable value, @RpcAttachment final FilterNode node) {
int rs = source.updateColumn(clazz, column, value, node);
if (handler != null) handler.completed(rs, node);
return rs;
}
@Override
public <T> int updateColumn(final Class<T> clazz, final Serializable id, final ColumnValue... values) {
return source.updateColumn(clazz, id, values);
}
@Override
public <T> int updateColumn(final AsyncHandler<Integer, Serializable> handler, final Class<T> clazz, @RpcAttachment final Serializable id, final ColumnValue... values) {
int rs = source.updateColumn(clazz, id, values);
if (handler != null) handler.completed(rs, id);
return rs;
}
@Override
public <T> int updateColumn(final Class<T> clazz, final FilterNode node, final ColumnValue... values) {
return source.updateColumn(clazz, node, values);
}
@Override
public <T> int updateColumn(final AsyncHandler<Integer, FilterNode> handler, final Class<T> clazz, @RpcAttachment final FilterNode node, final ColumnValue... values) {
int rs = source.updateColumn(clazz, node, values);
if (handler != null) handler.completed(rs, node);
return rs;
}
@Override
public <T> int updateColumn(final Class<T> clazz, final FilterNode node, final Flipper flipper, final ColumnValue... values) {
return source.updateColumn(clazz, node, flipper, values);
}
@Override
public <T> int updateColumn(final AsyncHandler<Integer, FilterNode> handler, final Class<T> clazz, @RpcAttachment final FilterNode node, final Flipper flipper, final ColumnValue... values) {
int rs = source.updateColumn(clazz, node, flipper, values);
if (handler != null) handler.completed(rs, node);
return rs;
}
@Override
public <T> int updateColumn(T bean, final String... columns) {
return source.updateColumn(bean, columns);
}
@Override
public <T> int updateColumn(final AsyncHandler<Integer, T> handler, @RpcAttachment final T bean, final String... columns) {
int rs = source.updateColumn(bean, columns);
if (handler != null) handler.completed(rs, bean);
return rs;
}
@Override
public <T> int updateColumn(T bean, final FilterNode node, final String... columns) {
return source.updateColumn(bean, node, columns);
}
@Override
public <T> int updateColumn(final AsyncHandler<Integer, FilterNode> handler, final T bean, @RpcAttachment final FilterNode node, final String... columns) {
int rs = source.updateColumn(bean, node, columns);
if (handler != null) handler.completed(rs, node);
return rs;
}
@Override
public <T> int updateColumn(T bean, final SelectColumn selects) {
return source.updateColumn(bean, selects);
}
@Override
public <T> int updateColumn(final AsyncHandler<Integer, T> handler, @RpcAttachment final T bean, final SelectColumn selects) {
int rs = source.updateColumn(bean, selects);
if (handler != null) handler.completed(rs, bean);
return rs;
}
@Override
public <T> int updateColumn(T bean, final FilterNode node, final SelectColumn selects) {
return source.updateColumn(bean, node, selects);
}
@Override
public <T> int updateColumn(final AsyncHandler<Integer, FilterNode> handler, final T bean, @RpcAttachment final FilterNode node, final SelectColumn selects) {
int rs = source.updateColumn(bean, node, selects);
if (handler != null) handler.completed(rs, node);
return rs;
}
@Override
public Number getNumberResult(final Class entityClass, FilterFunc func, final String column) {
return source.getNumberResult(entityClass, func, column);
}
@Override
public Number getNumberResult(final AsyncHandler<Number, String> handler, final Class entityClass, final FilterFunc func, @RpcAttachment final String column) {
Number rs = source.getNumberResult(entityClass, func, column);
if (handler != null) handler.completed(rs, column);
return rs;
}
@Override
public final Number getNumberResult(final Class entityClass, FilterFunc func, final String column, FilterBean bean) {
return getNumberResult(entityClass, func, column, FilterNodeBean.createFilterNode(bean));
}
@Override
public <B extends FilterBean> Number getNumberResult(final AsyncHandler<Number, B> handler, final Class entityClass, final FilterFunc func, final String column, @RpcAttachment final B bean) {
Number rs = source.getNumberResult(entityClass, func, column, bean);
if (handler != null) handler.completed(rs, bean);
return rs;
}
@Override
public Number getNumberResult(final Class entityClass, FilterFunc func, final String column, FilterNode node) {
return source.getNumberResult(entityClass, func, column, node);
}
@Override
public Number getNumberResult(final AsyncHandler<Number, FilterNode> handler, final Class entityClass, final FilterFunc func, final String column, @RpcAttachment final FilterNode node) {
Number rs = source.getNumberResult(entityClass, func, column, node);
if (handler != null) handler.completed(rs, node);
return rs;
}
@Override
public Number getNumberResult(final Class entityClass, FilterFunc func, final Number defVal, final String column) {
return source.getNumberResult(entityClass, func, defVal, column);
}
@Override
public Number getNumberResult(final AsyncHandler<Number, String> handler, final Class entityClass, final FilterFunc func, final Number defVal, @RpcAttachment final String column) {
Number rs = source.getNumberResult(entityClass, func, defVal, column);
if (handler != null) handler.completed(rs, column);
return rs;
}
@Override
public final Number getNumberResult(final Class entityClass, FilterFunc func, final Number defVal, final String column, FilterBean bean) {
return getNumberResult(entityClass, func, defVal, column, FilterNodeBean.createFilterNode(bean));
}
@Override
public Number getNumberResult(final AsyncHandler<Number, String> handler, final Class entityClass, final FilterFunc func, final Number defVal, @RpcAttachment final String column, final FilterBean bean) {
Number rs = source.getNumberResult(entityClass, func, defVal, column, bean);
if (handler != null) handler.completed(rs, column);
return rs;
}
@Override
public Number getNumberResult(final Class entityClass, FilterFunc func, final Number defVal, final String column, FilterNode node) {
return source.getNumberResult(entityClass, func, defVal, column, node);
}
@Override
public Number getNumberResult(final AsyncHandler<Number, String> handler, final Class entityClass, final FilterFunc func, final Number defVal, @RpcAttachment final String column, final FilterNode node) {
Number rs = source.getNumberResult(entityClass, func, defVal, column, node);
if (handler != null) handler.completed(rs, column);
return rs;
}
@Override
public <N extends Number> Map<String, N> getNumberMap(final Class entityClass, final FilterFuncColumn... columns) {
return source.getNumberMap(entityClass, columns);
}
@Override
public <N extends Number> Map<String, N> getNumberMap(final AsyncHandler<Map<String, N>, FilterFuncColumn[]> handler, final Class entityClass, @RpcAttachment final FilterFuncColumn... columns) {
Map<String, N> rs = source.getNumberMap(entityClass, columns);
if (handler != null) handler.completed(rs, columns);
return rs;
}
@Override
public <N extends Number> Map<String, N> getNumberMap(final Class entityClass, final FilterBean bean, final FilterFuncColumn... columns) {
return source.getNumberMap(entityClass, bean, columns);
}
@Override
public <N extends Number, B extends FilterBean> Map<String, N> getNumberMap(final AsyncHandler<Map<String, N>, B> handler, final Class entityClass, @RpcAttachment final B bean, final FilterFuncColumn... columns) {
Map<String, N> rs = source.getNumberMap(entityClass, bean, columns);
if (handler != null) handler.completed(rs, bean);
return rs;
}
@Override
public <N extends Number> Map<String, N> getNumberMap(final Class entityClass, final FilterNode node, final FilterFuncColumn... columns) {
return source.getNumberMap(entityClass, node, columns);
}
@Override
public <N extends Number> Map<String, N> getNumberMap(final AsyncHandler<Map<String, N>, FilterNode> handler, final Class entityClass, @RpcAttachment final FilterNode node, final FilterFuncColumn... columns) {
Map<String, N> rs = source.getNumberMap(entityClass, node, columns);
if (handler != null) handler.completed(rs, node);
return rs;
}
@Override
public <T, K extends Serializable, N extends Number> Map<K, N> queryColumnMap(final Class<T> entityClass, final String keyColumn, FilterFunc func, final String funcColumn) {
return source.queryColumnMap(entityClass, keyColumn, func, funcColumn);
}
@Override
public <T, K extends Serializable, N extends Number> Map<K, N> queryColumnMap(final AsyncHandler<Map<K, N>, String> handler, final Class<T> entityClass, @RpcAttachment final String keyColumn, final FilterFunc func, final String funcColumn) {
Map<K, N> rs = source.queryColumnMap(entityClass, keyColumn, func, funcColumn);
if (handler != null) handler.completed(rs, keyColumn);
return rs;
}
@Override
public final <T, K extends Serializable, N extends Number> Map<K, N> queryColumnMap(final Class<T> entityClass, final String keyColumn, FilterFunc func, final String funcColumn, FilterBean bean) {
return queryColumnMap(entityClass, keyColumn, func, funcColumn, FilterNodeBean.createFilterNode(bean));
}
@Override
public <T, K extends Serializable, N extends Number> Map<K, N> queryColumnMap(final AsyncHandler<Map<K, N>, String> handler, final Class<T> entityClass, @RpcAttachment final String keyColumn, final FilterFunc func, final String funcColumn, final FilterBean bean) {
Map<K, N> rs = source.queryColumnMap(entityClass, keyColumn, func, funcColumn, bean);
if (handler != null) handler.completed(rs, keyColumn);
return rs;
}
@Override
public <T, K extends Serializable, N extends Number> Map<K, N> queryColumnMap(final Class<T> entityClass, final String keyColumn, FilterFunc func, final String funcColumn, FilterNode node) {
return source.queryColumnMap(entityClass, keyColumn, func, funcColumn, node);
}
@Override
public <T, K extends Serializable, N extends Number> Map<K, N> queryColumnMap(final AsyncHandler<Map<K, N>, String> handler, final Class<T> entityClass, @RpcAttachment final String keyColumn, final FilterFunc func, final String funcColumn, final FilterNode node) {
Map<K, N> rs = source.queryColumnMap(entityClass, keyColumn, func, funcColumn, node);
if (handler != null) handler.completed(rs, keyColumn);
return rs;
}
@Override
public <T> T find(final Class<T> clazz, final Serializable pk) {
return source.find(clazz, pk);
}
@Override
public <T> T find(final AsyncHandler<T, Serializable> handler, final Class<T> clazz, @RpcAttachment final Serializable pk) {
T rs = source.find(clazz, pk);
if (handler != null) handler.completed(rs, pk);
return rs;
}
@Override
public <T> T find(final Class<T> clazz, SelectColumn selects, final Serializable pk) {
return source.find(clazz, selects, pk);
}
@Override
public <T> T find(final AsyncHandler<T, Serializable> handler, final Class<T> clazz, SelectColumn selects, @RpcAttachment final Serializable pk) {
T rs = source.find(clazz, selects, pk);
if (handler != null) handler.completed(rs, pk);
return rs;
}
@Override
public <T> T find(final Class<T> clazz, final String column, final Serializable key) {
return source.find(clazz, column, key);
}
@Override
public <T> T find(final AsyncHandler<T, Serializable> handler, final Class<T> clazz, final String column, @RpcAttachment final Serializable key) {
T rs = source.find(clazz, column, key);
if (handler != null) handler.completed(rs, key);
return rs;
}
@Override
public final <T> T find(final Class<T> clazz, FilterBean bean) {
return find(clazz, FilterNodeBean.createFilterNode(bean));
}
@Override
public <T, B extends FilterBean> T find(final AsyncHandler<T, B> handler, final Class<T> clazz, @RpcAttachment final B bean) {
T rs = source.find(clazz, bean);
if (handler != null) handler.completed(rs, bean);
return rs;
}
@Override
public <T> T find(final Class<T> clazz, FilterNode node) {
return source.find(clazz, node);
}
@Override
public <T> T find(final AsyncHandler<T, FilterNode> handler, final Class<T> clazz, @RpcAttachment final FilterNode node) {
T rs = source.find(clazz, node);
if (handler != null) handler.completed(rs, node);
return rs;
}
@Override
public final <T> T find(final Class<T> clazz, final SelectColumn selects, FilterBean bean) {
return find(clazz, selects, FilterNodeBean.createFilterNode(bean));
}
@Override
public <T, B extends FilterBean> T find(final AsyncHandler<T, B> handler, final Class<T> clazz, final SelectColumn selects, @RpcAttachment final B bean) {
T rs = source.find(clazz, selects, bean);
if (handler != null) handler.completed(rs, bean);
return rs;
}
@Override
public <T> T find(final Class<T> clazz, final SelectColumn selects, final FilterNode node) {
return source.find(clazz, selects, node);
}
@Override
public <T> T find(final AsyncHandler<T, FilterNode> handler, final Class<T> clazz, final SelectColumn selects, @RpcAttachment final FilterNode node) {
T rs = source.find(clazz, selects, node);
if (handler != null) handler.completed(rs, node);
return rs;
}
@Override
public <T> Serializable findColumn(final Class<T> clazz, final String column, final Serializable pk) {
return source.findColumn(clazz, column, pk);
}
@Override
public <T> Serializable findColumn(final AsyncHandler<Serializable, Serializable> handler, final Class<T> clazz, final String column, @RpcAttachment final Serializable pk) {
Serializable rs = source.findColumn(clazz, column, pk);
if (handler != null) handler.completed(rs, pk);
return rs;
}
@Override
public <T> Serializable findColumn(final Class<T> clazz, final String column, final FilterBean bean) {
return source.findColumn(clazz, column, bean);
}
@Override
public <T, B extends FilterBean> Serializable findColumn(final AsyncHandler<Serializable, B> handler, final Class<T> clazz, final String column, @RpcAttachment final B bean) {
Serializable rs = source.findColumn(clazz, column, bean);
if (handler != null) handler.completed(rs, bean);
return rs;
}
@Override
public <T> Serializable findColumn(final Class<T> clazz, final String column, final FilterNode node) {
return source.findColumn(clazz, column, node);
}
@Override
public <T> Serializable findColumn(final AsyncHandler<Serializable, FilterNode> handler, final Class<T> clazz, final String column, @RpcAttachment final FilterNode node) {
Serializable rs = source.findColumn(clazz, column, node);
if (handler != null) handler.completed(rs, node);
return rs;
}
@Override
public <T> Serializable findColumn(final Class<T> clazz, final String column, final Serializable defValue, final Serializable pk) {
return source.findColumn(clazz, column, defValue, pk);
}
@Override
public <T> Serializable findColumn(final AsyncHandler<Serializable, Serializable> handler, final Class<T> clazz, final String column, final Serializable defValue, @RpcAttachment final Serializable pk) {
Serializable rs = source.findColumn(clazz, column, defValue, pk);
if (handler != null) handler.completed(rs, pk);
return rs;
}
@Override
public <T> Serializable findColumn(final Class<T> clazz, final String column, final Serializable defValue, final FilterBean bean) {
return source.findColumn(clazz, column, defValue, bean);
}
@Override
public <T, B extends FilterBean> Serializable findColumn(final AsyncHandler<Serializable, B> handler, final Class<T> clazz, final String column, final Serializable defValue, @RpcAttachment final B bean) {
Serializable rs = source.findColumn(clazz, column, defValue, bean);
if (handler != null) handler.completed(rs, bean);
return rs;
}
@Override
public <T> Serializable findColumn(final Class<T> clazz, final String column, final Serializable defValue, final FilterNode node) {
return source.findColumn(clazz, column, defValue, node);
}
@Override
public <T> Serializable findColumn(final AsyncHandler<Serializable, FilterNode> handler, final Class<T> clazz, final String column, final Serializable defValue, @RpcAttachment final FilterNode node) {
Serializable rs = source.findColumn(clazz, column, defValue, node);
if (handler != null) handler.completed(rs, node);
return rs;
}
@Override
public <T> boolean exists(final Class<T> clazz, final Serializable pk) {
return source.exists(clazz, pk);
}
@Override
public <T> boolean exists(final AsyncHandler<Boolean, Serializable> handler, final Class<T> clazz, @RpcAttachment final Serializable pk) {
boolean rs = source.exists(clazz, pk);
if (handler != null) handler.completed(rs, pk);
return rs;
}
@Override
public final <T> boolean exists(final Class<T> clazz, FilterBean bean) {
return exists(clazz, FilterNodeBean.createFilterNode(bean));
}
@Override
public <T, B extends FilterBean> boolean exists(final AsyncHandler<Boolean, B> handler, final Class<T> clazz, @RpcAttachment final B bean) {
boolean rs = source.exists(clazz, bean);
if (handler != null) handler.completed(rs, bean);
return rs;
}
@Override
public <T> boolean exists(final Class<T> clazz, FilterNode node) {
return source.exists(clazz, node);
}
@Override
public <T> boolean exists(final AsyncHandler<Boolean, FilterNode> handler, final Class<T> clazz, @RpcAttachment final FilterNode node) {
boolean rs = source.exists(clazz, node);
if (handler != null) handler.completed(rs, node);
return rs;
}
@Override
public <T, V extends Serializable> HashSet<V> queryColumnSet(String selectedColumn, Class<T> clazz, final String column, final Serializable key) {
return source.queryColumnSet(selectedColumn, clazz, column, key);
}
@Override
public <T, V extends Serializable> HashSet<V> queryColumnSet(final AsyncHandler<HashSet<V>, String> handler, final String selectedColumn, final Class<T> clazz, @RpcAttachment final String column, final Serializable key) {
HashSet<V> rs = source.queryColumnSet(selectedColumn, clazz, column, key);
if (handler != null) handler.completed(rs, column);
return rs;
}
@Override
public final <T, V extends Serializable> HashSet<V> queryColumnSet(String selectedColumn, Class<T> clazz, FilterBean bean) {
return queryColumnSet(selectedColumn, clazz, FilterNodeBean.createFilterNode(bean));
}
@Override
public <T, V extends Serializable, B extends FilterBean> HashSet<V> queryColumnSet(final AsyncHandler<HashSet<V>, B> handler, final String selectedColumn, final Class<T> clazz, @RpcAttachment final B bean) {
HashSet<V> rs = source.queryColumnSet(selectedColumn, clazz, bean);
if (handler != null) handler.completed(rs, bean);
return rs;
}
@Override
public <T, V extends Serializable> HashSet<V> queryColumnSet(String selectedColumn, Class<T> clazz, FilterNode node) {
return source.queryColumnSet(selectedColumn, clazz, node);
}
@Override
public <T, V extends Serializable> HashSet<V> queryColumnSet(final AsyncHandler<HashSet<V>, FilterNode> handler, final String selectedColumn, final Class<T> clazz, @RpcAttachment final FilterNode node) {
HashSet<V> rs = source.queryColumnSet(selectedColumn, clazz, node);
if (handler != null) handler.completed(rs, node);
return rs;
}
@Override
public <T, V extends Serializable> List<V> queryColumnList(String selectedColumn, Class<T> clazz, final String column, final Serializable key) {
return source.queryColumnList(selectedColumn, clazz, column, key);
}
@Override
public <T, V extends Serializable> List<V> queryColumnList(final AsyncHandler<List<V>, Serializable> handler, final String selectedColumn, final Class<T> clazz, final String column, @RpcAttachment final Serializable key) {
List<V> rs = source.queryColumnList(selectedColumn, clazz, column, key);
if (handler != null) handler.completed(rs, key);
return rs;
}
@Override
public final <T, V extends Serializable> List<V> queryColumnList(String selectedColumn, Class<T> clazz, FilterBean bean) {
return queryColumnList(selectedColumn, clazz, FilterNodeBean.createFilterNode(bean));
}
@Override
public <T, V extends Serializable, B extends FilterBean> List<V> queryColumnList(final AsyncHandler<List<V>, B> handler, String selectedColumn, Class<T> clazz, @RpcAttachment final B bean) {
List<V> rs = source.queryColumnList(selectedColumn, clazz, bean);
if (handler != null) handler.completed(rs, bean);
return rs;
}
@Override
public <T, V extends Serializable> List<V> queryColumnList(String selectedColumn, Class<T> clazz, FilterNode node) {
return source.queryColumnList(selectedColumn, clazz, node);
}
@Override
public <T, V extends Serializable> List<V> queryColumnList(final AsyncHandler<List<V>, FilterNode> handler, final String selectedColumn, final Class<T> clazz, @RpcAttachment final FilterNode node) {
List<V> rs = source.queryColumnList(selectedColumn, clazz, node);
if (handler != null) handler.completed(rs, node);
return rs;
}
@Override
public final <T, V extends Serializable> List<V> queryColumnList(String selectedColumn, Class<T> clazz, Flipper flipper, FilterBean bean) {
return queryColumnList(selectedColumn, clazz, flipper, FilterNodeBean.createFilterNode(bean));
}
@Override
public <T, V extends Serializable, B extends FilterBean> List<V> queryColumnList(final AsyncHandler<List<V>, B> handler, String selectedColumn, Class<T> clazz, Flipper flipper, @RpcAttachment final B bean) {
List<V> rs = source.queryColumnList(selectedColumn, clazz, flipper, bean);
if (handler != null) handler.completed(rs, bean);
return rs;
}
@Override
public <T, V extends Serializable> List<V> queryColumnList(String selectedColumn, Class<T> clazz, Flipper flipper, FilterNode node) {
return source.queryColumnList(selectedColumn, clazz, flipper, node);
}
@Override
public <T, V extends Serializable> List<V> queryColumnList(final AsyncHandler<List<V>, FilterNode> handler, final String selectedColumn, final Class<T> clazz, Flipper flipper, @RpcAttachment final FilterNode node) {
List<V> rs = source.queryColumnList(selectedColumn, clazz, flipper, node);
if (handler != null) handler.completed(rs, node);
return rs;
}
@Override
public final <T, V extends Serializable> Sheet<V> queryColumnSheet(String selectedColumn, Class<T> clazz, Flipper flipper, FilterBean bean) {
return queryColumnSheet(selectedColumn, clazz, flipper, FilterNodeBean.createFilterNode(bean));
}
@Override
public <T, V extends Serializable, B extends FilterBean> Sheet<V> queryColumnSheet(final AsyncHandler<Sheet<V>, B> handler, String selectedColumn, Class<T> clazz, Flipper flipper, @RpcAttachment B bean) {
Sheet<V> rs = source.queryColumnSheet(selectedColumn, clazz, flipper, bean);
if (handler != null) handler.completed(rs, bean);
return rs;
}
@Override
public <T, V extends Serializable> Sheet<V> queryColumnSheet(String selectedColumn, Class<T> clazz, Flipper flipper, FilterNode node) {
return source.queryColumnSheet(selectedColumn, clazz, flipper, node);
}
@Override
public <T, V extends Serializable> Sheet<V> queryColumnSheet(final AsyncHandler<Sheet<V>, FilterNode> handler, final String selectedColumn, final Class<T> clazz, final Flipper flipper, @RpcAttachment final FilterNode node) {
Sheet<V> rs = source.queryColumnSheet(selectedColumn, clazz, flipper, node);
if (handler != null) handler.completed(rs, node);
return rs;
}
@Override
public <T> List<T> queryList(final Class<T> clazz, final String column, final Serializable key) {
return source.queryList(clazz, column, key);
}
@Override
public <T> List<T> queryList(final AsyncHandler<List<T>, Serializable> handler, final Class<T> clazz, final String column, @RpcAttachment final Serializable key) {
List<T> rs = source.queryList(clazz, column, key);
if (handler != null) handler.completed(rs, key);
return rs;
}
@Override
public final <T> List<T> queryList(final Class<T> clazz, final FilterBean bean) {
return queryList(clazz, FilterNodeBean.createFilterNode(bean));
}
@Override
public <T, B extends FilterBean> List<T> queryList(final AsyncHandler<List<T>, B> handler, final Class<T> clazz, @RpcAttachment final B bean) {
List<T> rs = source.queryList(clazz, bean);
if (handler != null) handler.completed(rs, bean);
return rs;
}
@Override
public <T> List<T> queryList(final Class<T> clazz, final FilterNode node) {
return source.queryList(clazz, node);
}
@Override
public <T> List<T> queryList(final AsyncHandler<List<T>, FilterNode> handler, final Class<T> clazz, @RpcAttachment final FilterNode node) {
List<T> rs = source.queryList(clazz, node);
if (handler != null) handler.completed(rs, node);
return rs;
}
@Override
public final <T> List<T> queryList(final Class<T> clazz, final SelectColumn selects, final FilterBean bean) {
return queryList(clazz, FilterNodeBean.createFilterNode(bean));
}
@Override
public <T, B extends FilterBean> List<T> queryList(final AsyncHandler<List<T>, B> handler, final Class<T> clazz, final SelectColumn selects, @RpcAttachment final B bean) {
List<T> rs = source.queryList(clazz, selects, bean);
if (handler != null) handler.completed(rs, bean);
return rs;
}
@Override
public <T> List<T> queryList(final Class<T> clazz, final SelectColumn selects, final FilterNode node) {
return source.queryList(clazz, selects, node);
}
@Override
public <T> List<T> queryList(final AsyncHandler<List<T>, FilterNode> handler, final Class<T> clazz, final SelectColumn selects, @RpcAttachment final FilterNode node) {
List<T> rs = source.queryList(clazz, selects, node);
if (handler != null) handler.completed(rs, node);
return rs;
}
@Override
public <T> List<T> queryList(final Class<T> clazz, final Flipper flipper, final String column, final Serializable key) {
return source.queryList(clazz, flipper, column, key);
}
@Override
public <T> List<T> queryList(final AsyncHandler<List<T>, Serializable> handler, final Class<T> clazz, final Flipper flipper, final String column, @RpcAttachment final Serializable key) {
List<T> rs = source.queryList(clazz, flipper, column, key);
if (handler != null) handler.completed(rs, key);
return rs;
}
@Override
public final <T> List<T> queryList(final Class<T> clazz, final Flipper flipper, final FilterBean bean) {
return queryList(clazz, flipper, FilterNodeBean.createFilterNode(bean));
}
@Override
public <T, B extends FilterBean> List<T> queryList(final AsyncHandler<List<T>, B> handler, final Class<T> clazz, final Flipper flipper, @RpcAttachment final B bean) {
List<T> rs = source.queryList(clazz, flipper, bean);
if (handler != null) handler.completed(rs, bean);
return rs;
}
@Override
public <T> List<T> queryList(final Class<T> clazz, final Flipper flipper, final FilterNode node) {
return source.queryList(clazz, flipper, node);
}
@Override
public <T> List<T> queryList(final AsyncHandler<List<T>, FilterNode> handler, final Class<T> clazz, final Flipper flipper, @RpcAttachment final FilterNode node) {
List<T> rs = source.queryList(clazz, flipper, node);
if (handler != null) handler.completed(rs, node);
return rs;
}
@Override
public final <T> List<T> queryList(final Class<T> clazz, final SelectColumn selects, final Flipper flipper, final FilterBean bean) {
return queryList(clazz, selects, flipper, FilterNodeBean.createFilterNode(bean));
}
@Override
public <T, B extends FilterBean> List<T> queryList(final AsyncHandler<List<T>, B> handler, final Class<T> clazz, final SelectColumn selects, final Flipper flipper, @RpcAttachment final B bean) {
List<T> rs = source.queryList(clazz, selects, flipper, bean);
if (handler != null) handler.completed(rs, bean);
return rs;
}
@Override
public <T> List<T> queryList(final Class<T> clazz, final SelectColumn selects, final Flipper flipper, final FilterNode node) {
return source.queryList(clazz, selects, flipper, node);
}
@Override
public <T> List<T> queryList(final AsyncHandler<List<T>, FilterNode> handler, final Class<T> clazz, final SelectColumn selects, final Flipper flipper, @RpcAttachment final FilterNode node) {
List<T> rs = source.queryList(clazz, selects, flipper, node);
if (handler != null) handler.completed(rs, node);
return rs;
}
@Override
public final <T> Sheet<T> querySheet(final Class<T> clazz, final Flipper flipper, final FilterBean bean) {
return querySheet(clazz, flipper, FilterNodeBean.createFilterNode(bean));
}
@Override
public <T, B extends FilterBean> Sheet<T> querySheet(final AsyncHandler<Sheet<T>, B> handler, final Class<T> clazz, final Flipper flipper, @RpcAttachment final B bean) {
Sheet<T> rs = source.querySheet(clazz, flipper, bean);
if (handler != null) handler.completed(rs, bean);
return rs;
}
@Override
public <T> Sheet<T> querySheet(final Class<T> clazz, final Flipper flipper, final FilterNode node) {
return source.querySheet(clazz, flipper, node);
}
@Override
public <T> Sheet<T> querySheet(final AsyncHandler<Sheet<T>, FilterNode> handler, final Class<T> clazz, final Flipper flipper, @RpcAttachment final FilterNode node) {
Sheet<T> rs = source.querySheet(clazz, flipper, node);
if (handler != null) handler.completed(rs, node);
return rs;
}
@Override
public final <T> Sheet<T> querySheet(final Class<T> clazz, final SelectColumn selects, final Flipper flipper, final FilterBean bean) {
return querySheet(clazz, selects, flipper, FilterNodeBean.createFilterNode(bean));
}
@Override
public <T, B extends FilterBean> Sheet<T> querySheet(final AsyncHandler<Sheet<T>, B> handler, final Class<T> clazz, final SelectColumn selects, final Flipper flipper, @RpcAttachment final B bean) {
Sheet<T> rs = source.querySheet(clazz, selects, flipper, bean);
if (handler != null) handler.completed(rs, bean);
return rs;
}
@Override
public <T> Sheet<T> querySheet(final Class<T> clazz, final SelectColumn selects, final Flipper flipper, final FilterNode node) {
return source.querySheet(clazz, selects, flipper, node);
}
@Override
public <T> Sheet<T> querySheet(final AsyncHandler<Sheet<T>, FilterNode> handler, final Class<T> clazz, final SelectColumn selects, final Flipper flipper, @RpcAttachment final FilterNode node) {
Sheet<T> rs = source.querySheet(clazz, selects, flipper, node);
if (handler != null) handler.completed(rs, node);
return rs;
}
@Override
public void close() throws Exception {
source.getClass().getMethod("close").invoke(source);
}
@Override
public final void directQuery(String sql, Consumer<ResultSet> consumer) {
source.directQuery(sql, consumer);
}
@Override
public final int[] directExecute(String... sqls) {
return source.directExecute(sqls);
}
}

View File

@@ -6,12 +6,12 @@
package org.redkale.service; package org.redkale.service;
import java.lang.annotation.*; import java.lang.annotation.*;
import static java.lang.annotation.ElementType.TYPE; import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.RUNTIME; import static java.lang.annotation.RetentionPolicy.RUNTIME;
/** /**
* 本地模式注解 * 本地模式注解
* 声明为LocalService的Service只能以本地模式存在 即使配置文件中配置成远程模式也将被忽略 * 声明为Local的Service只能以本地模式存在 即使配置文件中配置成远程模式也将被忽略
* *
* <p> * <p>
* 详情见: https://redkale.org * 详情见: https://redkale.org
@@ -20,9 +20,9 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME;
*/ */
@Inherited @Inherited
@Documented @Documented
@Target({TYPE}) @Target({TYPE, METHOD, PARAMETER})
@Retention(RUNTIME) @Retention(RUNTIME)
public @interface LocalService { public @interface Local {
String comment() default ""; //备注描述 String comment() default ""; //备注描述
} }

View File

@@ -8,8 +8,8 @@ package org.redkale.service;
import org.redkale.util.*; import org.redkale.util.*;
/** /**
* 所有Service的实现类不得声明为final 允许远程模式的public方法都不能声明为final。 * 所有Service的实现类不得声明为final 允许远程模式的public方法都不能声明为final。<br>
* 注意: "$"是一个很特殊的Service.name值 。 被标记为@Resource(name = "$") 的Service的资源名与所属父Service的资源名一致。 * 注意: "$"是一个很特殊的Service.name值 。 被标记为&#64;Resource(name = "$") 的Service的资源名与所属父Service的资源名一致。<br>
* *
* <blockquote><pre> * <blockquote><pre>
* Service的资源类型 * Service的资源类型
@@ -19,6 +19,16 @@ import org.redkale.util.*;
* 第二种方式需要在具体实现类上使用&#64;ResourceType指明资源注入的类型。 * 第二种方式需要在具体实现类上使用&#64;ResourceType指明资源注入的类型。
* </pre></blockquote> * </pre></blockquote>
* *
* <blockquote><pre>
* 异步方法:
* Service编写异步方法
* 1、异步方法有且仅有一个类型为AsyncHandler的参数 返回类型必须是void。若参数类型为AsyncHandler子类必须保证其子类可被继承且completed、failed可被重载且包含空参数的构造函数。
* 2、异步方法返回类型是CompletableFuture。
* 例如:
* public void insertRecord(AsyncHandler&#60;Integer, Record&#62; handler, String name, &#64;RpcAttachment Record record);
*
* </pre></blockquote>
*
* <p> * <p>
* 详情见: https://redkale.org * 详情见: https://redkale.org
* *
@@ -46,14 +56,4 @@ public interface Service {
} }
/**
* Service的接口版本号
* <b>注: public方法的参数或返回类型或参数类型内部变更后改值必须进行改变</b>
*
* @return 接口版本号
*/
default int version() {
return 0;
}
} }

View File

@@ -9,6 +9,7 @@ import static org.redkale.net.http.WebSocket.*;
import java.io.*; import java.io.*;
import java.net.*; import java.net.*;
import java.util.*; import java.util.*;
import java.util.concurrent.CompletableFuture;
import org.redkale.net.http.*; import org.redkale.net.http.*;
import org.redkale.util.*; import org.redkale.util.*;
@@ -20,7 +21,7 @@ import org.redkale.util.*;
* @author zhangjx * @author zhangjx
*/ */
@AutoLoad(false) @AutoLoad(false)
@ResourceType({WebSocketNodeService.class, WebSocketNode.class}) @ResourceType(WebSocketNode.class)
public class WebSocketNodeService extends WebSocketNode implements Service { public class WebSocketNodeService extends WebSocketNode implements Service {
@Override @Override
@@ -34,49 +35,56 @@ public class WebSocketNodeService extends WebSocketNode implements Service {
} }
@Override @Override
public List<String> getOnlineRemoteAddresses(@RpcTargetAddress InetSocketAddress targetAddress, Serializable groupid) { public CompletableFuture<List<String>> getWebSocketAddresses(final @RpcTargetAddress InetSocketAddress targetAddress, final Serializable groupid) {
if (localSncpAddress == null || !localSncpAddress.equals(targetAddress)) return remoteOnlineRemoteAddresses(targetAddress, groupid); if (localSncpAddress == null || !localSncpAddress.equals(targetAddress)) return remoteWebSocketAddresses(targetAddress, groupid);
final Set<String> engineids = localNodes.get(groupid); if (this.localEngine == null) return CompletableFuture.completedFuture(new ArrayList<>());
if (engineids == null || engineids.isEmpty()) return null; return CompletableFuture.supplyAsync(() -> {
final List<String> rs = new ArrayList<>(); final List<String> rs = new ArrayList<>();
for (String engineid : engineids) { this.localEngine.getLocalWebSockets(groupid).forEach(x -> rs.add(x.getRemoteAddr()));
final WebSocketEngine engine = engines.get(engineid); return rs;
if (engine == null) continue; });
final WebSocketGroup group = engine.getWebSocketGroup(groupid);
group.getWebSockets().forEach(x -> rs.add("ws" + Objects.hashCode(x) + '@' + x.getRemoteAddr()));
}
return rs;
} }
@Override @Override
public int sendMessage(@RpcTargetAddress InetSocketAddress addr, Serializable groupid, boolean recent, Serializable message, boolean last) { public CompletableFuture<Integer> sendMessage(@RpcTargetAddress InetSocketAddress addr, Object message, boolean last, Serializable userid) {
final Set<String> engineids = localNodes.get(groupid); if (this.localEngine == null) return CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY);
if (engineids == null || engineids.isEmpty()) return RETCODE_GROUP_EMPTY; return this.localEngine.sendMessage(message, last, userid);
int code = RETCODE_GROUP_EMPTY;
for (String engineid : engineids) {
final WebSocketEngine engine = engines.get(engineid);
if (engine != null) { //在本地
final WebSocketGroup group = engine.getWebSocketGroup(groupid);
if (group == null || group.isEmpty()) {
if (finest) logger.finest("receive websocket message {engineid:'" + engineid + "', groupid:" + groupid + ", content:'" + message + "'} from " + addr + " but send result is " + RETCODE_GROUP_EMPTY);
return RETCODE_GROUP_EMPTY;
}
code = group.send(recent, message, last);
if (finest) logger.finest("websocket node send message (" + message + ") from " + addr + " result is " + code);
}
}
return code;
} }
@Override @Override
public void connect(Serializable groupid, InetSocketAddress addr) { public CompletableFuture<Integer> broadcastMessage(@RpcTargetAddress InetSocketAddress addr, Object message, boolean last) {
source.appendSetItem(groupid, addr); if (this.localEngine == null) return CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY);
if (finest) logger.finest(WebSocketNodeService.class.getSimpleName() + ".event: " + groupid + " connect from " + addr); return this.localEngine.broadcastMessage(message, last);
} }
/**
* 当用户连接到节点需要更新到CacheSource
*
* @param userid String
* @param sncpAddr InetSocketAddress
*
* @return 无返回值
*/
@Override @Override
public void disconnect(Serializable groupid, InetSocketAddress addr) { public CompletableFuture<Void> connect(Serializable userid, InetSocketAddress sncpAddr) {
source.removeSetItem(groupid, addr); CompletableFuture<Void> future = sncpNodeAddresses.appendSetItemAsync(userid, sncpAddr);
if (finest) logger.finest(WebSocketNodeService.class.getSimpleName() + ".event: " + groupid + " disconnect from " + addr); future = future.thenAccept((a) -> sncpNodeAddresses.appendSetItemAsync("redkale_sncpnodes", sncpAddr));
if (finest) logger.finest(WebSocketNodeService.class.getSimpleName() + ".event: " + userid + " connect from " + sncpAddr);
return future;
}
/**
* 当用户从一个节点断掉了所有的连接需要从CacheSource中删除
*
* @param userid String
* @param sncpAddr InetSocketAddress
*
* @return 无返回值
*/
@Override
public CompletableFuture<Void> disconnect(Serializable userid, InetSocketAddress sncpAddr) {
CompletableFuture<Void> future = sncpNodeAddresses.removeSetItemAsync(userid, sncpAddr);
if (finest) logger.finest(WebSocketNodeService.class.getSimpleName() + ".event: " + userid + " disconnect from " + sncpAddr);
return future;
} }
} }

View File

@@ -0,0 +1,458 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.source;
import java.io.*;
import java.lang.reflect.Type;
import java.util.*;
import java.util.concurrent.*;
import java.util.function.Consumer;
import java.util.logging.*;
import javax.annotation.Resource;
import org.redkale.convert.json.*;
import org.redkale.net.sncp.Sncp;
import org.redkale.service.*;
import org.redkale.util.*;
/**
* CacheSource的默认实现--内存缓存
*
* @param <K> key类型
* @param <V> value类型
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*/
@Local
@AutoLoad(false)
@ResourceType(CacheSource.class)
public class CacheMemorySource<K extends Serializable, V extends Object> extends AbstractService implements CacheSource<K, V>, Service, AutoCloseable, Resourcable {
@Resource(name = "APP_HOME")
private File home;
@Resource
private JsonConvert convert;
private boolean needStore;
private Class keyType;
private Type objValueType;
private Type setValueType;
private Type listValueType;
private ScheduledThreadPoolExecutor scheduler;
private Consumer<CacheEntry> expireHandler;
private final Logger logger = Logger.getLogger(this.getClass().getSimpleName());
protected final ConcurrentHashMap<K, CacheEntry<K, Object>> container = new ConcurrentHashMap<>();
@RpcRemote
protected CacheSource<K, V> remoteSource;
public CacheMemorySource() {
}
public final CacheMemorySource setStoreType(Class keyType, Class valueType) {
this.keyType = keyType;
this.objValueType = valueType;
this.setValueType = TypeToken.createParameterizedType(null, CopyOnWriteArraySet.class, valueType);
this.listValueType = TypeToken.createParameterizedType(null, ConcurrentLinkedQueue.class, valueType);
this.setNeedStore(this.keyType != null && this.keyType != Serializable.class && this.objValueType != null);
return this;
}
public final void setNeedStore(boolean needStore) {
this.needStore = needStore;
}
@Override
public void init(AnyValue conf) {
final CacheMemorySource self = this;
AnyValue prop = conf == null ? null : conf.getAnyValue("property");
if (keyType == null && prop != null) {
String storeKeyStr = prop.getValue("key-type");
String storeValueStr = prop.getValue("value-type");
if (storeKeyStr != null && storeValueStr != null) {
try {
this.setStoreType(Class.forName(storeKeyStr), Class.forName(storeValueStr));
} catch (Throwable e) {
logger.log(Level.SEVERE, self.getClass().getSimpleName() + " load key & value store class (" + storeKeyStr + ", " + storeValueStr + ") error", e);
}
}
if (prop.getBoolValue("store-ignore", false)) setNeedStore(false);
}
String expireHandlerClass = prop == null ? null : prop.getValue("expirehandler");
if (expireHandlerClass != null) {
try {
this.expireHandler = (Consumer<CacheEntry>) Class.forName(expireHandlerClass).newInstance();
} catch (Throwable e) {
logger.log(Level.SEVERE, self.getClass().getSimpleName() + " new expirehandler class (" + expireHandlerClass + ") instance error", e);
}
}
if (scheduler == null) {
this.scheduler = new ScheduledThreadPoolExecutor(1, (Runnable r) -> {
final Thread t = new Thread(r, self.getClass().getSimpleName() + "-Expirer-Thread");
t.setDaemon(true);
return t;
});
final List<K> keys = new ArrayList<>();
scheduler.scheduleWithFixedDelay(() -> {
keys.clear();
int now = (int) (System.currentTimeMillis() / 1000);
container.forEach((k, x) -> {
if (x.expireSeconds > 0 && (now > (x.lastAccessed + x.expireSeconds))) {
keys.add(x.key);
}
});
for (K key : keys) {
CacheEntry entry = container.remove(key);
if (expireHandler != null && entry != null) expireHandler.accept(entry);
}
}, 10, 10, TimeUnit.SECONDS);
logger.finest(self.getClass().getSimpleName() + ":" + self.resourceName() + " start schedule expire executor");
}
if (Sncp.isRemote(self)) return;
boolean datasync = false; //是否从远程同步过数据
//----------同步数据……-----------
// TODO
if (this.needStore) {
try {
File store = new File(home, "cache/" + resourceName());
if (!store.isFile() || !store.canRead()) return;
LineNumberReader reader = new LineNumberReader(new FileReader(store));
if (this.keyType == null) this.keyType = Serializable.class;
if (this.objValueType == null) {
this.objValueType = Object.class;
this.setValueType = TypeToken.createParameterizedType(null, CopyOnWriteArraySet.class, this.objValueType);
this.listValueType = TypeToken.createParameterizedType(null, ConcurrentLinkedQueue.class, this.objValueType);
}
final Type storeObjType = TypeToken.createParameterizedType(null, CacheEntry.class, keyType, objValueType);
final Type storeSetType = TypeToken.createParameterizedType(null, CacheEntry.class, keyType, setValueType);
final Type storeListType = TypeToken.createParameterizedType(null, CacheEntry.class, keyType, listValueType);
String line;
while ((line = reader.readLine()) != null) {
if (line.isEmpty()) continue;
CacheEntry<K, Object> entry = convert.convertFrom(line.startsWith(CacheEntry.JSON_SET_KEY) ? storeSetType : (line.startsWith(CacheEntry.JSON_LIST_KEY) ? storeListType : storeObjType), line);
if (entry.isExpired()) continue;
if (datasync && container.containsKey(entry.key)) continue; //已经同步了
container.put(entry.key, entry);
}
reader.close();
store.delete();
} catch (Exception e) {
logger.log(Level.SEVERE, CacheSource.class.getSimpleName() + "(" + resourceName() + ") load store file error ", e);
}
}
if (remoteSource != null && !Sncp.isRemote(this)) {
super.runAsync(() -> {
try {
CompletableFuture<List<CacheEntry<K, Object>>> listFuture = remoteSource.queryListAsync();
listFuture.whenComplete((list, exp) -> {
if (exp != null) {
logger.log(Level.FINEST, CacheSource.class.getSimpleName() + "(" + resourceName() + ") queryListAsync error", exp);
} else {
for (CacheEntry<K, Object> entry : list) {
container.put(entry.key, entry);
}
}
});
} catch (Exception e) {
logger.log(Level.FINEST, CacheSource.class.getSimpleName() + "(" + resourceName() + ") queryListAsync error, maybe remote node connot connect ", e);
}
});
}
}
@Override
public void close() throws Exception { //给Application 关闭时调用
destroy(null);
}
@Override
public String resourceName() {
Resource res = this.getClass().getAnnotation(Resource.class);
return res == null ? null : res.name();
}
@Override
public void destroy(AnyValue conf) {
if (scheduler != null) scheduler.shutdownNow();
if (!this.needStore || Sncp.isRemote(this) || container.isEmpty()) return;
try {
File store = new File(home, "cache/" + resourceName());
store.getParentFile().mkdirs();
PrintStream stream = new PrintStream(store, "UTF-8");
final Type storeObjType = TypeToken.createParameterizedType(null, CacheEntry.class, keyType, objValueType);
final Type storeSetType = TypeToken.createParameterizedType(null, CacheEntry.class, keyType, setValueType);
final Type storeListType = TypeToken.createParameterizedType(null, CacheEntry.class, keyType, listValueType);
Collection<CacheEntry<K, Object>> entrys = container.values();
for (CacheEntry entry : entrys) {
stream.println(convert.convertTo(entry.isSetCacheType() ? storeSetType : (entry.isListCacheType() ? storeListType : storeObjType), entry));
}
container.clear();
stream.close();
} catch (Exception e) {
logger.log(Level.SEVERE, CacheSource.class.getSimpleName() + "(" + resourceName() + ") store to file error ", e);
}
}
@Override
public boolean exists(K key) {
if (key == null) return false;
CacheEntry entry = container.get(key);
if (entry == null) return false;
return !entry.isExpired();
}
@Override
public CompletableFuture<Boolean> existsAsync(final K key) {
return CompletableFuture.supplyAsync(() -> exists(key), getExecutor());
}
@Override
public V get(K key) {
if (key == null) return null;
CacheEntry entry = container.get(key);
if (entry == null || entry.isExpired()) return null;
if (entry.isListCacheType()) return (V) (entry.listValue == null ? null : new ArrayList(entry.listValue));
if (entry.isSetCacheType()) return (V) (entry.setValue == null ? null : new HashSet(entry.setValue));
return (V) entry.objectValue;
}
@Override
public CompletableFuture<V> getAsync(final K key) {
return CompletableFuture.supplyAsync(() -> get(key), getExecutor());
}
@Override
@RpcMultiRun
public V getAndRefresh(K key, final int expireSeconds) {
if (key == null) return null;
CacheEntry entry = container.get(key);
if (entry == null || entry.isExpired()) return null;
entry.lastAccessed = (int) (System.currentTimeMillis() / 1000);
entry.expireSeconds = expireSeconds;
if (entry.isListCacheType()) return (V) (entry.listValue == null ? null : new ArrayList(entry.listValue));
if (entry.isSetCacheType()) return (V) (entry.setValue == null ? null : new HashSet(entry.setValue));
return (V) entry.objectValue;
}
@Override
public CompletableFuture<V> getAndRefreshAsync(final K key, final int expireSeconds) {
return CompletableFuture.supplyAsync(() -> getAndRefresh(key, expireSeconds), getExecutor());
}
@Override
@RpcMultiRun
public void refresh(K key, final int expireSeconds) {
if (key == null) return;
CacheEntry entry = container.get(key);
if (entry == null) return;
entry.lastAccessed = (int) (System.currentTimeMillis() / 1000);
entry.expireSeconds = expireSeconds;
}
@Override
public CompletableFuture<Void> refreshAsync(final K key, final int expireSeconds) {
return CompletableFuture.runAsync(() -> refresh(key, expireSeconds), getExecutor());
}
@Override
@RpcMultiRun
public void set(K key, V value) {
if (key == null) return;
CacheEntry entry = container.get(key);
if (entry == null) {
entry = new CacheEntry(CacheEntryType.OBJECT, key, value, null, null);
container.putIfAbsent(key, entry);
} else {
entry.expireSeconds = 0;
entry.objectValue = value;
entry.lastAccessed = (int) (System.currentTimeMillis() / 1000);
}
}
@Override
public CompletableFuture<Void> setAsync(K key, V value) {
return CompletableFuture.runAsync(() -> set(key, value), getExecutor());
}
@Override
@RpcMultiRun
public void set(int expireSeconds, K key, V value) {
if (key == null) return;
CacheEntry entry = container.get(key);
if (entry == null) {
entry = new CacheEntry(CacheEntryType.OBJECT, expireSeconds, key, value, null, null);
container.putIfAbsent(key, entry);
} else {
if (expireSeconds > 0) entry.expireSeconds = expireSeconds;
entry.lastAccessed = (int) (System.currentTimeMillis() / 1000);
entry.objectValue = value;
}
}
@Override
public CompletableFuture<Void> setAsync(int expireSeconds, K key, V value) {
return CompletableFuture.runAsync(() -> set(expireSeconds, key, value), getExecutor());
}
@Override
@RpcMultiRun
public void setExpireSeconds(K key, int expireSeconds) {
if (key == null) return;
CacheEntry entry = container.get(key);
if (entry == null) return;
entry.expireSeconds = expireSeconds;
}
@Override
public CompletableFuture<Void> setExpireSecondsAsync(final K key, final int expireSeconds) {
return CompletableFuture.runAsync(() -> setExpireSeconds(key, expireSeconds), getExecutor());
}
@Override
@RpcMultiRun
public void remove(K key) {
if (key == null) return;
container.remove(key);
}
@Override
public CompletableFuture<Void> removeAsync(final K key) {
return CompletableFuture.runAsync(() -> remove(key), getExecutor());
}
@Override
public Collection<V> getCollection(final K key) {
return (Collection<V>) get(key);
}
@Override
public CompletableFuture<Collection<V>> getCollectionAsync(final K key) {
return CompletableFuture.supplyAsync(() -> getCollection(key), getExecutor());
}
@Override
public long getCollectionSize(final K key) {
Collection<V> collection = (Collection<V>) get(key);
return collection == null ? 0 : collection.size();
}
@Override
public CompletableFuture<Long> getCollectionSizeAsync(final K key) {
return CompletableFuture.supplyAsync(() -> getCollectionSize(key), getExecutor());
}
@Override
public Collection<V> getCollectionAndRefresh(final K key, final int expireSeconds) {
return (Collection<V>) getAndRefresh(key, expireSeconds);
}
@Override
public CompletableFuture<Collection<V>> getCollectionAndRefreshAsync(final K key, final int expireSeconds) {
return CompletableFuture.supplyAsync(() -> getCollectionAndRefresh(key, expireSeconds), getExecutor());
}
@Override
@RpcMultiRun
public void appendListItem(K key, V value) {
if (key == null) return;
CacheEntry entry = container.get(key);
if (entry == null || !entry.isListCacheType() || entry.listValue == null) {
ConcurrentLinkedQueue<V> list = new ConcurrentLinkedQueue();
entry = new CacheEntry(CacheEntryType.LIST, key, null, null, list);
CacheEntry old = container.putIfAbsent(key, entry);
if (old != null) list = old.listValue;
if (list != null) list.add(value);
} else {
entry.listValue.add(value);
}
}
@Override
public CompletableFuture<Void> appendListItemAsync(final K key, final V value) {
return CompletableFuture.runAsync(() -> appendListItem(key, value), getExecutor());
}
@Override
@RpcMultiRun
public void removeListItem(K key, V value) {
if (key == null) return;
CacheEntry entry = container.get(key);
if (entry == null || entry.listValue == null) return;
entry.listValue.remove(value);
}
@Override
public CompletableFuture<Void> removeListItemAsync(final K key, final V value) {
return CompletableFuture.runAsync(() -> removeListItem(key, value), getExecutor());
}
@Override
@RpcMultiRun
public void appendSetItem(K key, V value) {
if (key == null) return;
CacheEntry entry = container.get(key);
if (entry == null || !entry.isSetCacheType() || entry.setValue == null) {
CopyOnWriteArraySet<V> set = new CopyOnWriteArraySet();
entry = new CacheEntry(CacheEntryType.SET, key, null, set, null);
CacheEntry old = container.putIfAbsent(key, entry);
if (old != null) set = old.setValue;
if (set != null) set.add(value);
} else {
entry.setValue.add(value);
}
}
@Override
public CompletableFuture<Void> appendSetItemAsync(final K key, final V value) {
return CompletableFuture.runAsync(() -> appendSetItem(key, value), getExecutor());
}
@Override
@RpcMultiRun
public void removeSetItem(K key, V value) {
if (key == null) return;
CacheEntry entry = container.get(key);
if (entry == null || entry.setValue == null) return;
entry.setValue.remove(value);
}
@Override
public CompletableFuture<Void> removeSetItemAsync(final K key, final V value) {
return CompletableFuture.runAsync(() -> removeSetItem(key, value), getExecutor());
}
@Override
public List<K> queryKeys() {
return new ArrayList<>(container.keySet());
}
@Override
public CompletableFuture<List<CacheEntry<K, Object>>> queryListAsync() {
return CompletableFuture.completedFuture(new ArrayList<>(container.values()));
}
@Override
public List<CacheEntry<K, Object>> queryList() {
return new ArrayList<>(container.values());
}
@Override
public CompletableFuture<List<K>> queryKeysAsync() {
return CompletableFuture.completedFuture(new ArrayList<>(container.keySet()));
}
}

View File

@@ -5,9 +5,12 @@
*/ */
package org.redkale.source; package org.redkale.source;
import java.beans.ConstructorProperties;
import java.io.*; import java.io.*;
import java.util.*; import java.util.*;
import org.redkale.util.*; import java.util.concurrent.*;
import org.redkale.convert.ConvertColumn;
import org.redkale.convert.json.JsonFactory;
/** /**
* *
@@ -42,6 +45,8 @@ public interface CacheSource<K extends Serializable, V extends Object> {
public Collection<V> getCollection(final K key); public Collection<V> getCollection(final K key);
public long getCollectionSize(final K key);
public Collection<V> getCollectionAndRefresh(final K key, final int expireSeconds); public Collection<V> getCollectionAndRefresh(final K key, final int expireSeconds);
public void appendListItem(final K key, final V value); public void appendListItem(final K key, final V value);
@@ -52,37 +57,140 @@ public interface CacheSource<K extends Serializable, V extends Object> {
public void removeSetItem(final K key, final V value); public void removeSetItem(final K key, final V value);
//----------------------异步版--------------------------------- public List<K> queryKeys();
public boolean exists(final AsyncHandler<Boolean, K> handler, final K key);
public V get(final AsyncHandler<V, K> handler, final K key); public List<CacheEntry<K, Object>> queryList();
public V getAndRefresh(final AsyncHandler<V, K> handler, final K key, final int expireSeconds); //---------------------- CompletableFuture 异步版 ---------------------------------
public CompletableFuture<Boolean> existsAsync(final K key);
public void refresh(final AsyncHandler<Void, K> handler, final K key, final int expireSeconds); public CompletableFuture<V> getAsync(final K key);
public void set(final AsyncHandler<Void, K> handler, final K key, final V value); public CompletableFuture<V> getAndRefreshAsync(final K key, final int expireSeconds);
public void set(final AsyncHandler<Void, K> handler, final int expireSeconds, final K key, final V value); public CompletableFuture<Void> refreshAsync(final K key, final int expireSeconds);
public void setExpireSeconds(final AsyncHandler<Void, K> handler, final K key, final int expireSeconds); public CompletableFuture<Void> setAsync(final K key, final V value);
public void remove(final AsyncHandler<Void, K> handler, final K key); public CompletableFuture<Void> setAsync(final int expireSeconds, final K key, final V value);
public Collection<V> getCollection(final AsyncHandler<Collection<V>, K> handler, final K key); public CompletableFuture<Void> setExpireSecondsAsync(final K key, final int expireSeconds);
public Collection<V> getCollectionAndRefresh(final AsyncHandler<Collection<V>, K> handler, final K key, final int expireSeconds); public CompletableFuture<Void> removeAsync(final K key);
public void appendListItem(final AsyncHandler<Void, K> handler, final K key, final V value); public CompletableFuture<Collection<V>> getCollectionAsync(final K key);
public void removeListItem(final AsyncHandler<Void, K> handler, final K key, final V value); public CompletableFuture<Long> getCollectionSizeAsync(final K key);
public void appendSetItem(final AsyncHandler<Void, K> handler, final K key, final V value); public CompletableFuture<Collection<V>> getCollectionAndRefreshAsync(final K key, final int expireSeconds);
public void removeSetItem(final AsyncHandler<Void, K> handler, final K key, final V value); public CompletableFuture<Void> appendListItemAsync(final K key, final V value);
public CompletableFuture<Void> removeListItemAsync(final K key, final V value);
public CompletableFuture<Void> appendSetItemAsync(final K key, final V value);
public CompletableFuture<Void> removeSetItemAsync(final K key, final V value);
public CompletableFuture<List<K>> queryKeysAsync();
public CompletableFuture<List<CacheEntry<K, Object>>> queryListAsync();
default CompletableFuture<Boolean> isOpenAsync() {
return CompletableFuture.completedFuture(true);
}
public static enum CacheEntryType {
OBJECT, SET, LIST;
}
public static final class CacheEntry<K extends Serializable, T> {
static final String JSON_SET_KEY = "{\"cacheType\":\"" + CacheEntryType.SET + "\"";
static final String JSON_LIST_KEY = "{\"cacheType\":\"" + CacheEntryType.LIST + "\"";
final CacheEntryType cacheType;
final K key;
//<=0表示永久保存
int expireSeconds;
volatile int lastAccessed; //最后刷新时间
T objectValue;
CopyOnWriteArraySet<T> setValue;
ConcurrentLinkedQueue<T> listValue;
public CacheEntry(CacheEntryType cacheType, K key, T objectValue, CopyOnWriteArraySet<T> setValue, ConcurrentLinkedQueue<T> listValue) {
this(cacheType, 0, key, objectValue, setValue, listValue);
}
public CacheEntry(CacheEntryType cacheType, int expireSeconds, K key, T objectValue, CopyOnWriteArraySet<T> setValue, ConcurrentLinkedQueue<T> listValue) {
this(cacheType, expireSeconds, (int) (System.currentTimeMillis() / 1000), key, objectValue, setValue, listValue);
}
@ConstructorProperties({"cacheType", "expireSeconds", "lastAccessed", "key", "objectValue", "setValue", "listValue"})
public CacheEntry(CacheEntryType cacheType, int expireSeconds, int lastAccessed, K key, T objectValue, CopyOnWriteArraySet<T> setValue, ConcurrentLinkedQueue<T> listValue) {
this.cacheType = cacheType;
this.expireSeconds = expireSeconds;
this.lastAccessed = lastAccessed;
this.key = key;
this.objectValue = objectValue;
this.setValue = setValue;
this.listValue = listValue;
}
@Override
public String toString() {
return JsonFactory.root().getConvert().convertTo(this);
}
@ConvertColumn(ignore = true)
public boolean isListCacheType() {
return cacheType == CacheEntryType.LIST;
}
@ConvertColumn(ignore = true)
public boolean isSetCacheType() {
return cacheType == CacheEntryType.SET;
}
@ConvertColumn(ignore = true)
public boolean isExpired() {
return (expireSeconds > 0 && lastAccessed + expireSeconds < (System.currentTimeMillis() / 1000));
}
public CacheEntryType getCacheType() {
return cacheType;
}
public int getExpireSeconds() {
return expireSeconds;
}
public int getLastAccessed() {
return lastAccessed;
}
public K getKey() {
return key;
}
public T getObjectValue() {
return objectValue;
}
public CopyOnWriteArraySet<T> getSetValue() {
return setValue;
}
public ConcurrentLinkedQueue<T> getListValue() {
return listValue;
}
default boolean isOpen(final AsyncHandler<Boolean, Void> handler) {
if (handler != null) handler.completed(Boolean.TRUE, null);
return true;
} }
} }

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