305 Commits
2.0.0 ... 2.1.0

Author SHA1 Message Date
Redkale
75698d9390 2020-08-31 13:35:52 +08:00
Redkale
9d343694cc <tt>替换成<code> 2020-08-31 09:31:38 +08:00
Redkale
fddb2a3be0 2020-08-28 22:35:43 +08:00
Redkale
3b44eddbb9 2020-08-28 19:37:34 +08:00
Redkale
ba4100e22d 增加 ThreadHashExecutor 2020-08-28 15:15:26 +08:00
Redkale
72155e31dd 2020-08-19 23:03:35 +08:00
Redkale
60903c844e 2020-08-19 22:56:26 +08:00
Redkale
5c810b5ff6 2020-08-11 19:57:17 +08:00
Redkale
e3a8c2d392 2020-08-06 16:33:49 +08:00
Redkale
1f27d5f3e3 完善Rest的泛型识别 2020-08-06 16:06:46 +08:00
Redkale
abb790da54 2020-07-31 12:00:27 +08:00
Redkale
f4a2f2af94 2020-07-31 10:26:26 +08:00
Redkale
29a21d060b 2020-07-31 10:22:19 +08:00
Redkale
de19861974 2020-07-31 10:13:53 +08:00
Redkale
e5766e31dc 2020-07-31 09:50:59 +08:00
Redkale
fca727ecaf 2020-07-30 20:54:35 +08:00
Redkale
d009c9b2ed 2020-07-30 19:14:39 +08:00
Redkale
fd2e84f781 2020-07-30 18:02:29 +08:00
Redkale
2b7676449d 2020-07-30 17:57:31 +08:00
Redkale
4b2c6eba63 2020-07-30 17:35:59 +08:00
Redkale
84147280cf 2020-07-30 09:44:07 +08:00
Redkale
e62d9f8201 2020-07-30 09:11:49 +08:00
Redkale
4cec4e64f9 2020-07-30 09:10:08 +08:00
Redkale
9eb647428c 2020-07-29 21:20:12 +08:00
Redkale
b68808b325 加入spop系列方法 2020-07-29 20:08:47 +08:00
Redkale
a9dce0efbf 2020-07-27 16:43:56 +08:00
Redkale
de63f81238 CacheSource remove系列方法返回值由void改成int 2020-07-25 09:39:43 +08:00
Redkale
a05afe45c0 2020-07-24 09:41:43 +08:00
Redkale
236996caa1 2020-07-22 13:41:54 +08:00
Redkale
b808c93885 2020-07-22 11:53:16 +08:00
Redkale
7865b74359 2020-07-22 11:27:10 +08:00
Redkale
143e1880d4 2020-07-22 11:24:39 +08:00
Redkale
1be467cfe9 2020-07-22 11:09:49 +08:00
Redkale
7f8f157616 2020-07-21 19:49:32 +08:00
Redkale
c140ad6828 2020-07-20 11:45:51 +08:00
Redkale
50e41dba25 2020-07-19 21:36:19 +08:00
Redkale
c73fc5f266 2020-07-19 15:25:59 +08:00
Redkale
a5f3adc477 2020-07-19 13:42:26 +08:00
Redkale
b77764c1cf 2020-07-17 20:18:10 +08:00
Redkale
714b188560 2020-07-17 18:21:09 +08:00
Redkale
47315f188a 2020-07-17 17:37:02 +08:00
Redkale
384a8a3991 2020-07-17 16:57:41 +08:00
Redkale
7a80c00439 2020-07-17 12:13:45 +08:00
Redkale
43486ce1e2 2020-07-17 11:43:51 +08:00
Redkale
627c1e953e CacheSource增加hxxx方法 2020-07-17 10:41:41 +08:00
Redkale
da141cc6bd 2020-07-16 23:43:35 +08:00
Redkale
8f3a9a6297 2020-07-16 17:50:04 +08:00
Redkale
e8d496b0ad 2020-07-16 17:30:27 +08:00
Redkale
f5accfbe8f 2020-07-15 23:07:55 +08:00
Redkale
16e291036e 2020-07-15 21:02:00 +08:00
Redkale
a79b8b77b5 2020-07-15 20:58:03 +08:00
Redkale
8e2d6acdf8 2020-07-15 19:34:31 +08:00
Redkale
70c5123f7a 增加强大的HttpMessageClusterClient功能 2020-07-15 18:03:33 +08:00
Redkale
7cfbaee199 2020-07-14 23:23:34 +08:00
Redkale
fa8d78e18f 2020-07-13 22:57:21 +08:00
Redkale
4f5e72a31a 2020-07-13 22:56:42 +08:00
Redkale
7c5822fb7c 2020-07-12 17:56:58 +08:00
Redkale
60df140bfd 2020-07-11 20:22:44 +08:00
Redkale
f41e905842 RetResult增加CompletableFuture相关方法 2020-07-11 11:02:18 +08:00
Redkale
9824fe6da3 2020-07-11 10:45:12 +08:00
Redkale
1fb37f4b67 2020-07-10 17:50:48 +08:00
Redkale
a8a6861a21 2020-07-09 19:50:06 +08:00
Redkale
614222bdc3 2020-07-09 16:58:05 +08:00
Redkale
8e98dda795 2020-07-09 00:22:19 +08:00
Redkale
3686306505 2020-07-08 23:18:28 +08:00
Redkale
d29aa78022 2020-07-04 23:52:27 +08:00
Redkale
85fa406e1d 2020-07-03 09:21:49 +08:00
Redkale
d501e2016f 2020-07-01 18:08:39 +08:00
Redkale
cf0cd66ac9 2020-07-01 15:02:26 +08:00
Redkale
c921f657b1 2020-06-30 20:33:07 +08:00
Redkale
c93c1d84c5 2020-06-30 19:30:53 +08:00
Redkale
2e66b1a546 2020-06-29 20:12:37 +08:00
Redkale
48629146ed 2020-06-28 23:18:59 +08:00
Redkale
a6c8502416 2020-06-28 23:16:53 +08:00
Redkale
fc74dc33c1 2020-06-28 22:49:14 +08:00
Redkale
73de9dfc33 2020-06-28 22:22:53 +08:00
Redkale
68d78a4aa4 2020-06-28 19:28:56 +08:00
Redkale
b618e0e884 2020-06-28 09:45:53 +08:00
Redkale
2d1a6e0edc 2020-06-28 07:23:07 +08:00
Redkale
76a7e92787 2020-06-26 22:06:22 +08:00
Redkale
a2178b9a5f 2020-06-26 00:45:29 +08:00
Redkale
dcf0fecdb0 2020-06-26 00:38:24 +08:00
Redkale
f88db8abf9 2020-06-25 23:33:57 +08:00
Redkale
0c8b0f5e19 2020-06-25 20:55:17 +08:00
Redkale
b794872147 2020-06-25 17:34:04 +08:00
Redkale
86614f035b 2020-06-25 16:14:57 +08:00
Redkale
5f3a472c5e 2020-06-24 23:08:17 +08:00
Redkale
7eb2a405d3 2020-06-24 18:04:42 +08:00
Redkale
1d030183bb 2020-06-24 16:34:58 +08:00
Redkale
d211692306 2020-06-24 10:43:46 +08:00
Redkale
86c7d95c80 2020-06-24 10:05:02 +08:00
Redkale
e99d43b25c 2020-06-24 09:25:23 +08:00
Redkale
730fc0a911 2020-06-23 10:28:25 +08:00
Redkale
1ea92d7165 2020-06-23 10:13:12 +08:00
Redkale
0dfc0ca853 2020-06-22 11:07:26 +08:00
Redkale
474f82b3cf 2020-06-21 09:41:14 +08:00
Redkale
ae80109c77 2020-06-21 09:15:17 +08:00
Redkale
807fe63cbe HttpSimpleRequest 增加 rpc 功能 2020-06-21 08:57:59 +08:00
Redkale
44ed3259f9 2020-06-19 13:56:46 +08:00
Redkale
93b2b5fab8 2020-06-19 00:46:05 +08:00
Redkale
58e807439c 2020-06-19 00:20:57 +08:00
Redkale
ca2f34bfac 2020-06-19 00:18:38 +08:00
Redkale
b8719d7f76 去掉Service.stop 增加 @Command 功能 2020-06-18 15:35:40 +08:00
Redkale
be0fdd9045 2020-06-18 15:00:14 +08:00
Redkale
1d2b583c0c 2020-06-17 22:50:04 +08:00
Redkale
9def35e2e1 2020-06-17 22:49:27 +08:00
Redkale
a11127ea58 2020-06-17 13:39:08 +08:00
Redkale
af80864e08 2020-06-16 21:03:22 +08:00
Redkale
f6a4fbdf87 2020-06-16 20:57:09 +08:00
Redkale
61f48be2a6 2020-06-16 19:58:08 +08:00
Redkale
8516925537 Service 增加 stop 方法 2020-06-16 17:48:19 +08:00
Redkale
f828ec3d08 2020-06-16 15:33:49 +08:00
Redkale
1eb1706166 2020-06-16 11:12:51 +08:00
Redkale
ffd3b6daf6 2020-06-15 23:35:26 +08:00
Redkale
76d538264a 2020-06-15 23:16:17 +08:00
Redkale
02d0a06195 2020-06-15 20:57:59 +08:00
Redkale
c43325e402 2020-06-15 18:01:49 +08:00
Redkale
63bc4b2d00 CacheSource 增加get多个keys值得系列方法 2020-06-14 21:52:08 +08:00
Redkale
6e5da92263 2020-06-14 14:25:13 +08:00
Redkale
6358719255 2020-06-14 12:04:59 +08:00
Redkale
bdfd92299a 2020-06-14 12:00:03 +08:00
Redkale
aa4c9896ba 2020-06-14 01:21:26 +08:00
Redkale
1549f1feed 2020-06-14 01:13:08 +08:00
Redkale
2a9a713f3e 2020-06-14 01:12:26 +08:00
Redkale
0377dac71f 2020-06-14 01:01:38 +08:00
Redkale
13217a11c0 2020-06-13 09:38:25 +08:00
Redkale
146b81ee5e 2020-06-12 19:21:41 +08:00
Redkale
8502792aad 2020-06-12 18:10:00 +08:00
Redkale
3d9447327a 2020-06-12 17:20:36 +08:00
Redkale
288fc67621 2020-06-12 14:08:04 +08:00
Redkale
d4bc751a20 2020-06-12 09:35:41 +08:00
Redkale
4cf160e5d3 2020-06-11 23:30:43 +08:00
Redkale
98f29d6a6e 2020-06-11 19:24:16 +08:00
Redkale
92bb0a561b 2020-06-11 17:27:25 +08:00
Redkale
1329f6f0e1 2020-06-11 17:11:31 +08:00
Redkale
a3dbfaba88 2020-06-11 16:03:39 +08:00
Redkale
2032f39bf9 2020-06-11 14:51:22 +08:00
Redkale
8840415739 MessageAgent增加timeoutExecutor 2020-06-11 14:25:50 +08:00
Redkale
bdf2fd21c3 增加 @MessageMultiConsumer 功能 2020-06-11 14:06:25 +08:00
Redkale
bd1e326404 Rest配置中开启MQ时,path字段失效 2020-06-11 10:47:14 +08:00
Redkale
ebd31bc3d3 2020-06-11 10:16:48 +08:00
Redkale
7185191d91 2020-06-10 23:06:20 +08:00
Redkale
1a1dd44f34 2020-06-10 22:07:20 +08:00
Redkale
95fd147268 2020-06-10 20:37:24 +08:00
Redkale
88657fdf63 2020-06-10 20:30:50 +08:00
Redkale
03de684940 2020-06-10 17:25:52 +08:00
Redkale
b445b99572 2020-06-10 10:34:38 +08:00
Redkale
bd4343c5d8 2020-06-09 21:31:08 +08:00
Redkale
a58c13cd9f 2020-06-09 21:22:17 +08:00
Redkale
cf23ecc12c 2020-06-09 17:46:50 +08:00
Redkale
86754c2324 2020-06-09 13:10:16 +08:00
Redkale
e6f0a8fdf3 增加 @RestHeaders 功能 2020-06-09 09:53:32 +08:00
Redkale
fa9fc531d0 2020-06-08 22:51:14 +08:00
Redkale
30bf2c1ad3 2020-06-08 22:43:46 +08:00
Redkale
d326da9344 2020-06-08 22:27:30 +08:00
Redkale
8f6494163d 2020-06-08 21:57:55 +08:00
Redkale
490cabefb2 增加 <excludelibs/> 配置项 2020-06-08 21:45:19 +08:00
Redkale
66ff86d6fb 2020-06-08 20:13:26 +08:00
Redkale
0e5479d55e 增加 @RpcTargetTopic 2020-06-08 19:31:29 +08:00
Redkale
3eb3e0104d 2020-06-08 13:42:19 +08:00
Redkale
079e6116b2 2020-06-08 09:13:41 +08:00
Redkale
b6a3adb21c 2020-06-07 22:11:49 +08:00
Redkale
ecf831c0f5 2020-06-07 15:56:55 +08:00
Redkale
adfa0be79e 2020-06-07 11:27:40 +08:00
Redkale
7ec91802b2 2020-06-06 23:51:56 +08:00
Redkale
1e15eb31c7 2020-06-06 23:37:59 +08:00
Redkale
8d5e61a9a2 2020-06-06 22:04:18 +08:00
Redkale
64500b113a 2020-06-06 18:50:18 +08:00
Redkale
a7e5fad571 2020-06-05 18:44:51 +08:00
Redkale
531b00b6fd 2020-06-05 18:01:25 +08:00
Redkale
ce3fc5792b 2020-06-05 17:52:09 +08:00
Redkale
a477b2fb73 2020-06-05 13:50:58 +08:00
Redkale
6a5d121615 2020-06-05 12:01:32 +08:00
Redkale
90960e0574 2020-06-05 11:56:04 +08:00
Redkale
b6980f7cf8 2020-06-05 11:29:54 +08:00
Redkale
2fb3472de0 2020-06-05 10:06:44 +08:00
Redkale
6dacbafc29 2020-06-05 07:50:10 +08:00
Redkale
f1fcbd7396 2020-06-04 22:22:35 +08:00
Redkale
681e389f19 2020-06-04 21:32:18 +08:00
Redkale
06b7274364 2020-06-04 20:56:45 +08:00
Redkale
b4f7de4858 2020-06-04 20:14:16 +08:00
Redkale
6261232ce6 2020-06-04 19:53:41 +08:00
Redkale
eb35b90950 2020-06-04 18:03:02 +08:00
Redkale
958cea4e0d 2020-06-04 17:56:13 +08:00
Redkale
7f184bae50 2020-06-04 17:25:42 +08:00
Redkale
3d68103a19 2020-06-04 17:09:30 +08:00
Redkale
612851bce0 2020-06-04 16:21:28 +08:00
Redkale
e365dae9e4 2020-06-04 14:06:35 +08:00
Redkale
b6d5fc02dc 2020-06-04 10:11:14 +08:00
Redkale
59d139ace2 2020-06-04 09:44:00 +08:00
Redkale
1d7a72f992 2020-06-04 09:39:38 +08:00
Redkale
45f2ce261e 2020-06-04 09:29:04 +08:00
Redkale
f79e49db5a 2020-06-03 23:21:57 +08:00
Redkale
218aebaa0f 2020-06-03 22:34:10 +08:00
Redkale
f11870d6bc 2020-06-03 20:06:48 +08:00
Redkale
ebea6bb92c 2020-06-03 19:58:13 +08:00
Redkale
4806d6ada0 2020-06-03 19:46:25 +08:00
Redkale
6909748bda 2020-06-03 19:26:38 +08:00
Redkale
982fc8440c 2020-06-03 17:53:55 +08:00
Redkale
e4df07a00c 2020-06-03 17:02:23 +08:00
Redkale
0ee30aca7e 2020-06-03 16:45:28 +08:00
Redkale
b994bcbcfb 2020-06-03 16:23:16 +08:00
Redkale
cc23b44409 2020-06-03 15:48:40 +08:00
Redkale
de36ef697d 2020-06-03 15:23:45 +08:00
Redkale
e0d224a330 2020-06-03 14:58:44 +08:00
Redkale
adc2106bec 2020-06-03 14:15:42 +08:00
Redkale
6cdbe957a7 2020-06-03 14:03:46 +08:00
Redkale
14808cb01c 2020-06-03 13:55:08 +08:00
Redkale
1edadbfee8 2020-06-03 12:03:04 +08:00
Redkale
043a23ecdf 2020-06-03 09:29:54 +08:00
Redkale
383ef37989 2020-06-02 23:18:36 +08:00
Redkale
9cd9a8d3ea 2020-06-02 09:56:01 +08:00
Redkale
db8c94f433 2020-06-01 21:19:47 +08:00
Redkale
22fb3e5bef 2020-06-01 13:44:34 +08:00
Redkale
6329722f17 2020-06-01 11:04:03 +08:00
Redkale
2dee61223d 2020-06-01 10:04:20 +08:00
Redkale
f83fc52e9c 2020-05-31 11:59:11 +08:00
Redkale
2f8a04c15d 2020-05-31 08:42:51 +08:00
Redkale
a1501af7a7 2020-05-30 19:55:07 +08:00
Redkale
a1fdfc9cc9 2020-05-30 17:59:28 +08:00
Redkale
2dddf3c2a5 2020-05-30 17:53:59 +08:00
Redkale
84061cf60f 2020-05-30 17:42:28 +08:00
Redkale
557c2c7858 2020-05-30 16:40:33 +08:00
Redkale
cb2d355bc9 2020-05-30 15:57:56 +08:00
Redkale
ed1b642d5b 2020-05-30 14:50:07 +08:00
Redkale
411f5e1951 2020-05-30 11:55:34 +08:00
Redkale
450506ca96 2020-05-30 11:32:16 +08:00
Redkale
bf700aa88f 2020-05-30 10:23:36 +08:00
Redkale
c108ab196c 2020-05-30 10:18:00 +08:00
Redkale
cac8662c01 2020-05-30 09:31:38 +08:00
Redkale
197c58ef98 2020-05-30 09:24:40 +08:00
Redkale
d4c2723759 删除MessageStreams 2020-05-29 23:18:59 +08:00
Redkale
c34a6d8f49 2020-05-29 22:34:53 +08:00
Redkale
5de580c00b 2020-05-29 21:04:00 +08:00
Redkale
ed1fb151d7 2020-05-29 16:06:02 +08:00
Redkale
4c4913c5d0 2020-05-29 13:43:47 +08:00
Redkale
adb0dc5963 2020-05-29 11:15:56 +08:00
Redkale
740bc8ae31 增加@RestUserid 废弃 HttpRequest.currentUser() 方法, 建议 HttpRequest.currentUserid() 2020-05-28 20:20:08 +08:00
Redkale
30a0b12020 2020-05-28 19:11:28 +08:00
Redkale
b0b8b0db3b 2020-05-28 11:40:03 +08:00
Redkale
71244732f2 2020-05-28 11:22:30 +08:00
Redkale
820af60c19 2020-05-28 10:03:13 +08:00
Redkale
b3252359bd 2020-05-28 09:55:05 +08:00
Redkale
9ade568597 2020-05-28 09:51:57 +08:00
Redkale
cf5224a5f6 2020-05-28 09:38:18 +08:00
Redkale
6c4a83d14d 2020-05-27 20:32:57 +08:00
Redkale
0439b95139 2020-05-27 19:18:48 +08:00
Redkale
72a618a0b7 2020-05-27 18:02:51 +08:00
Redkale
0f7520e67b 2020-05-27 17:10:23 +08:00
Redkale
fcd69474a2 2020-05-27 15:22:41 +08:00
Redkale
3130e00bab 2020-05-27 15:08:12 +08:00
Redkale
1c063b57ec 2020-05-27 15:01:36 +08:00
Redkale
efeff3b720 2020-05-27 14:21:01 +08:00
Redkale
ea15634f47 2020-05-27 12:00:32 +08:00
Redkale
e744d001c6 2020-05-27 11:49:44 +08:00
Redkale
06e486e5e2 2020-05-27 11:32:29 +08:00
Redkale
95180ffdef 2020-05-27 11:31:29 +08:00
Redkale
e5bd85e9aa 2020-05-27 11:23:07 +08:00
Redkale
ec4ec2ff77 2020-05-26 22:22:44 +08:00
Redkale
5a1c9215bc 2020-05-26 19:11:12 +08:00
Redkale
53457b73ad 加入MQ功能 2020-05-26 17:59:00 +08:00
Redkale
8cb9fcf01a 2020-05-22 19:45:57 +08:00
Redkale
2c88b0e75e 2020-05-22 16:49:45 +08:00
Redkale
4daffdc31f 2020-05-21 20:23:56 +08:00
Redkale
16dec2cde5 2020-05-17 23:37:22 +08:00
Redkale
82e5fb593e 2020-05-17 22:25:47 +08:00
Redkale
4a60ecb3ff 2020-05-17 22:11:34 +08:00
Redkale
38307405ed 2020-05-17 20:34:36 +08:00
Redkale
fe332d0cbd 2020-05-17 19:08:43 +08:00
Redkale
e45f75c1d6 2020-05-17 16:39:51 +08:00
Redkale
a77f450757 2020-05-17 15:49:32 +08:00
Redkale
9f8e946ea3 2020-05-16 22:40:41 +08:00
Redkale
7633687665 2020-05-16 22:24:37 +08:00
Redkale
a1e37643d0 2020-05-16 22:12:45 +08:00
Redkale
8440b58d6c 2020-05-16 20:34:03 +08:00
Redkale
a97f8efe21 2020-05-16 20:10:21 +08:00
Redkale
d83e7c22ac 2020-05-16 19:59:49 +08:00
Redkale
01a5b32b22 ClusterAgent 2020-05-16 17:51:30 +08:00
Redkale
8834f57582 2020-05-16 17:22:52 +08:00
Redkale
57fd7b96b4 $cluster 2020-05-16 17:03:55 +08:00
Redkale
14fe2fbc84 2020-05-16 14:46:11 +08:00
Redkale
0b86edb654 2020-05-16 14:27:46 +08:00
Redkale
07ab74902c 2020-05-16 14:27:08 +08:00
Redkale
e4c6e860c1 DataCallAttribute移至RpcCallAttribute 2020-05-16 09:12:06 +08:00
Redkale
02cf587fcf 2020-05-16 09:09:43 +08:00
Redkale
081773163b 2020-05-16 09:03:18 +08:00
Redkale
292ff63699 移除RpcMultiRun功能 2020-05-15 22:02:54 +08:00
Redkale
f1a97c0219 移除DataCacheListener功能 2020-05-15 21:53:21 +08:00
Redkale
e545010034 2020-05-15 17:36:19 +08:00
Redkale
ef57fa9a25 2020-05-15 15:01:18 +08:00
Redkale
1bd0d891a4 2020-05-15 14:39:29 +08:00
Redkale
9a51c1de1d Application加入nodeid属性 2020-05-15 14:38:53 +08:00
Redkale
e4bb75dd8e 2020-05-15 14:09:49 +08:00
Redkale
66e1e08ae2 2020-05-05 19:54:13 +08:00
Redkale
708cd5a644 修复DeMember/EnMember中attribute.type值不能精准显示泛型的bug 2020-05-04 22:12:25 +08:00
Redkale
138c9aba97 2020-05-04 20:59:28 +08:00
Redkale
e5e17b4496 2020-05-04 20:52:00 +08:00
Redkale
cadaebefb9 2020-05-04 20:21:49 +08:00
Redkale
e2fb7935f6 2020-05-04 20:02:18 +08:00
Redkale
16b3dab600 2020-05-04 19:51:19 +08:00
Redkale
758972890e 2020-04-17 11:08:30 +08:00
Redkale
a88285d935 回滚Mask convertFrom 方法 2020-04-16 22:08:14 +08:00
Redkale
d7832fb931 Convert增加convertFrom(final Type type, final ConvertMask mask, final byte[] bytes) 方法 2020-04-16 21:42:52 +08:00
Redkale
f073c9a0bc 2020-04-16 21:08:00 +08:00
Redkale
63ef83ec62 WebSocket支持permessage-deflate单向功能 2020-04-16 20:19:49 +08:00
140 changed files with 7730 additions and 2087 deletions

7
bin/command.bat Normal file
View File

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

23
bin/command.sh Normal file
View File

@@ -0,0 +1,23 @@
#!/bin/sh
export LC_ALL="zh_CN.UTF-8"
APP_HOME=`dirname "$0"`
cd "$APP_HOME"/..
APP_HOME=`pwd`
if [ ! -f "$APP_HOME"/conf/application.xml ]; then
APP_HOME="$APP_HOME"/..
fi
lib="$APP_HOME"/lib
for jar in `ls $APP_HOME/lib/*.jar`
do
lib=$lib:$jar
done
export CLASSPATH=$CLASSPATH:$lib
echo "$APP_HOME"
java -DCMD=$1 -DAPP_HOME="$APP_HOME" org.redkale.boot.Application

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<application port="2121"> <application nodeid="10" port="2121">
<!-- 详细配置说明见: http://redkale.org/redkale.html#redkale_confxml --> <!-- 详细配置说明见: http://redkale.org/redkale.html#redkale_confxml -->

View File

@@ -61,7 +61,7 @@
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId> <artifactId>maven-compiler-plugin</artifactId>
<version>3.5.1</version> <version>3.8.0</version>
<configuration> <configuration>
<compilerArgument>-parameters</compilerArgument> <compilerArgument>-parameters</compilerArgument>
<encoding>UTF-8</encoding> <encoding>UTF-8</encoding>
@@ -74,7 +74,7 @@
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId> <artifactId>maven-jar-plugin</artifactId>
<version>2.6</version> <version>3.2.0</version>
<configuration> <configuration>
<archive> <archive>
<addMavenDescriptor>false</addMavenDescriptor> <addMavenDescriptor>false</addMavenDescriptor>
@@ -102,7 +102,7 @@
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId> <artifactId>maven-source-plugin</artifactId>
<version>3.0.0</version> <version>3.2.0</version>
<executions> <executions>
<execution> <execution>
<goals> <goals>
@@ -114,7 +114,7 @@
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId> <artifactId>maven-javadoc-plugin</artifactId>
<version>3.0.0</version> <version>3.2.0</version>
<executions> <executions>
<execution> <execution>
<goals> <goals>
@@ -127,7 +127,7 @@
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId> <artifactId>maven-assembly-plugin</artifactId>
<version>3.0.0</version> <version>3.2.0</version>
<configuration> <configuration>
<appendAssemblyId>false</appendAssemblyId> <appendAssemblyId>false</appendAssemblyId>
<descriptors> <descriptors>

View File

@@ -19,11 +19,13 @@
serviceid1_name1 serviceid1_name2 serviceid2_name1 serviceid2_name2 serviceid1_name1 serviceid1_name2 serviceid2_name1 serviceid2_name2
--> -->
<!-- <!--
nodeid: int 进程的节点ID用于分布式环境一个系统中节点ID必须全局唯一使用cluster时框架会进行唯一性校验
name: 进程的名称,用于监控识别,命名规则: 字母、数字、下划线
address: 本地局域网的IP地址 默认值为默认网卡的ip当不使用默认值需要指定值如192.168.1.22 address: 本地局域网的IP地址 默认值为默认网卡的ip当不使用默认值需要指定值如192.168.1.22
port: required 程序的管理Server的端口用于关闭或者与监管系统进行数据交互 port: required 程序的管理Server的端口用于关闭或者与监管系统进行数据交互
lib: 加上额外的lib路径,多个路径用分号;隔开; 默认为空。 例如: ${APP_HOME}/lib/a.jar;${APP_HOME}/lib2/b.jar; lib: 加上额外的lib路径,多个路径用分号;隔开; 默认为空。 例如: ${APP_HOME}/lib/a.jar;${APP_HOME}/lib2/b.jar;
--> -->
<application port="6560" lib=""> <application nodeid="1000" port="6560" lib="">
<!-- <!--
【节点全局唯一】 【节点全局唯一】
@@ -43,12 +45,52 @@
--> -->
<transport bufferCapacity="32K" bufferPoolSize="32" threads="32" readTimeoutSeconds="6" writeTimeoutSeconds="6"/> <transport bufferCapacity="32K" bufferPoolSize="32" threads="32" readTimeoutSeconds="6" writeTimeoutSeconds="6"/>
<!--
【节点全局唯一】
自动扫描时排除部分包路径
value 排除lib.path与excludes中的正则表达式匹配的路径, 多个正则表达式用分号;隔开
-->
<excludelibs value="^.*mysql.*$;^.*kafka.*$"/>
<!--
【节点全局唯一】
第三方服务发现管理接口
value 类名必须是org.redkale.cluster.ClusterAgent的子类
waits: 注销服务后是否需要等待检查周期时间后再进行Service销毁默认值为false
当一个Service进行服务注销后不能立刻销毁Service因为健康检测是有间隔时间差的
需要等待一个健康检测周期时间,让其他进程都更新完服务列表。
如果使用MQ可以设置为false如果对服务健壮性要求高建议设置为true
protocols: 服务发现可以处理的协议, 默认值为: SNCP, 多个协议用分号;隔开
ports: 服务发现可以处理的端口, 多个端口用分号;隔开
-->
<!--
<cluster value="org.redkalex.cluster.consul.ConsulClusterAgent" waits="false" protocols="SNCP" ports="7070;7071">
<property name="xxxxxx" value="XXXXXXXX"/>
</cluster>
-->
<!--
MQ管理接口配置
不同MQ节点所配置的MQ集群不能重复。
name: 服务的名称用于监控识别多个mq节点时只能有一个name为空的节点mq.name不能重复,命名规则: 字母、数字、下划线
value 实现类名必须是org.redkale.mq.MessageAgent的子类
MQ节点下的子节点配置没有固定格式, 根据MessageAgent实现方的定义来配置
-->
<!--
<mq name="" value="org.redkalex.mq.kafka.KafkaMessageAgent">
<servers value="127.0.0.1:9101"/>
<consumer>
<property name="xxxxxx" value="XXXXXXXX"/>
</consumer>
<producer>
<property name="xxxxxx" value="XXXXXXXX"/>
</producer>
</mq>
-->
<!-- <!--
一个组包含多个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
subprotocol: 子协议,预留字段。默认值为空
注意: 一个node只能所属一个group。只要存在protocol=SNCP的Server节点信息 就必须有group节点信息。 注意: 一个node只能所属一个group。只要存在protocol=SNCP的Server节点信息 就必须有group节点信息。
--> -->
<group name="" protocol="TCP"> <group name="" protocol="TCP">
@@ -146,11 +188,13 @@
在同一个进程中同一个name同一类型的Service将共用同一个实例 在同一个进程中同一个name同一类型的Service将共用同一个实例
autoload="true" 默认值. 自动加载classpath下所有的Service类 autoload="true" 默认值. 自动加载classpath下所有的Service类
autoload="false" 需要显著的指定Service类 autoload="false" 需要显著的指定Service类
mq: 所属的MQ管理器当 protocol == SNCP 时该值才有效, 存在该属性表示Service的SNCP协议采用消息总线代理模式
includes 当autoload="true" 拉取类名与includes中的正则表达式匹配的类, 多个正则表达式用分号;隔开 includes 当autoload="true" 拉取类名与includes中的正则表达式匹配的类, 多个正则表达式用分号;隔开
excludes 当autoload="true" 排除类名与excludes中的正则表达式匹配的类, 多个正则表达式用分号;隔开 excludes 当autoload="true" 排除类名与excludes中的正则表达式匹配的类, 多个正则表达式用分号;隔开
groups: 所属组的节点,多个节点值用;隔开如果配置文件中存在多个SNCP协议的Server节点需要显式指定group属性. groups: 所属组的节点,多个节点值用;隔开如果配置文件中存在多个SNCP协议的Server节点需要显式指定group属性.
当 protocol == SNCP 时 group表示当前Server与哪些节点组关联。 当 protocol == SNCP 时 group表示当前Server与哪些节点组关联。
当 protocol != SNCP 时 group只能是空或者一个group的节点值不能为多个节点值。 当 protocol != SNCP 时 group只能是空或者一个group的节点值不能为多个节点值。
特殊值"$cluster", 视为通过第三方服务注册发现管理工具来获取远程模式的ip端口信息
--> -->
<services autoload="true" includes="" excludes=""> <services autoload="true" includes="" excludes="">
@@ -198,8 +242,9 @@
<!-- <!--
REST的核心配置项 REST的核心配置项
当Server为HTTP协议时, rest节点才有效。存在[rest]节点则Server启动时会加载REST服务, 节点可以多个,(WATCH协议不需要设置系统会自动生成) 当Server为HTTP协议时, rest节点才有效。存在[rest]节点则Server启动时会加载REST服务, 节点可以多个,(WATCH协议不需要设置系统会自动生成)
path: servlet的ContextPath前缀 默认为空 path: servlet的ContextPath前缀 默认为空 【注: 开启MQ时,该字段失效】
base: REST服务的BaseServlet必须是 org.redkale.net.http.HttpServlet 的子类,且子类必须标记@HttpUserType。 base: REST服务的BaseServlet必须是 org.redkale.net.http.HttpServlet 的子类,且子类必须标记@HttpUserType。
mq: 所属的MQ管理器, 存在该属性表示RestService的请求来自于消息总线 【注: 开启MQ时,path字段失效】
autoload默认值"true" 默认值. 加载当前server所能使用的Servce对象; autoload默认值"true" 默认值. 加载当前server所能使用的Servce对象;
includes当autoload="true" 拉取类名与includes中的正则表达式匹配的类, 多个正则表达式用分号;隔开 includes当autoload="true" 拉取类名与includes中的正则表达式匹配的类, 多个正则表达式用分号;隔开
excludes当autoload="true" 排除类名与excludes中的正则表达式匹配的类, 多个正则表达式用分号;隔开 excludes当autoload="true" 排除类名与excludes中的正则表达式匹配的类, 多个正则表达式用分号;隔开

View File

@@ -22,6 +22,7 @@ module org.redkale {
exports org.redkale.convert.bson; exports org.redkale.convert.bson;
exports org.redkale.convert.ext; exports org.redkale.convert.ext;
exports org.redkale.convert.json; exports org.redkale.convert.json;
exports org.redkale.mq;
exports org.redkale.net; exports org.redkale.net;
exports org.redkale.net.http; exports org.redkale.net.http;
exports org.redkale.net.sncp; exports org.redkale.net.sncp;
@@ -30,6 +31,9 @@ module org.redkale {
exports org.redkale.util; exports org.redkale.util;
exports org.redkale.watch; exports org.redkale.watch;
uses org.redkale.cluster.ClusterAgent;
uses org.redkale.mq.MessageAgent;
uses org.redkale.source.CacheSource;
uses org.redkale.source.SourceLoader; uses org.redkale.source.SourceLoader;
uses org.redkale.util.ResourceInjectLoader; uses org.redkale.util.ResourceInjectLoader;
} }

View File

@@ -60,8 +60,8 @@ package org.redkale.asm;
/** /**
* A visitor to visit a Java annotation. The methods of this class must be * A visitor to visit a Java annotation. The methods of this class must be
* called in the following order: ( <tt>visit</tt> | <tt>visitEnum</tt> | * called in the following order: ( <code>visit</code> | <code>visitEnum</code> |
* <tt>visitAnnotation</tt> | <tt>visitArray</tt> )* <tt>visitEnd</tt>. * <code>visitAnnotation</code> | <code>visitArray</code> )* <code>visitEnd</code>.
* *
* @author Eric Bruneton * @author Eric Bruneton
* @author Eugene Kuleshov * @author Eugene Kuleshov
@@ -151,7 +151,7 @@ public abstract class AnnotationVisitor {
* @param desc * @param desc
* the class descriptor of the nested annotation class. * the class descriptor of the nested annotation class.
* @return a visitor to visit the actual nested annotation value, or * @return a visitor to visit the actual nested annotation value, or
* <tt>null</tt> if this visitor is not interested in visiting this * <code>null</code> if this visitor is not interested in visiting this
* nested annotation. <i>The nested annotation value must be fully * nested annotation. <i>The nested annotation value must be fully
* visited before calling other methods on this annotation * visited before calling other methods on this annotation
* visitor</i>. * visitor</i>.
@@ -172,7 +172,7 @@ public abstract class AnnotationVisitor {
* @param name * @param name
* the value name. * the value name.
* @return a visitor to visit the actual array value elements, or * @return a visitor to visit the actual array value elements, or
* <tt>null</tt> if this visitor is not interested in visiting these * <code>null</code> if this visitor is not interested in visiting these
* values. The 'name' parameters passed to the methods of this * values. The 'name' parameters passed to the methods of this
* visitor are ignored. <i>All the array values must be visited * visitor are ignored. <i>All the array values must be visited
* before calling other methods on this annotation visitor</i>. * before calling other methods on this annotation visitor</i>.

View File

@@ -77,7 +77,7 @@ final class AnnotationWriter extends AnnotationVisitor {
private int size; private int size;
/** /**
* <tt>true<tt> if values are named, <tt>false</tt> otherwise. Annotation * <code>true<code> if values are named, <code>false</code> otherwise. Annotation
* writers used for annotation default and annotation arrays use unnamed * writers used for annotation default and annotation arrays use unnamed
* values. * values.
*/ */
@@ -122,13 +122,13 @@ final class AnnotationWriter extends AnnotationVisitor {
* @param cw * @param cw
* the class writer to which this annotation must be added. * the class writer to which this annotation must be added.
* @param named * @param named
* <tt>true<tt> if values are named, <tt>false</tt> otherwise. * <code>true<code> if values are named, <code>false</code> otherwise.
* @param bv * @param bv
* where the annotation values must be stored. * where the annotation values must be stored.
* @param parent * @param parent
* where the number of annotation values must be stored. * where the number of annotation values must be stored.
* @param offset * @param offset
* where in <tt>parent</tt> the number of annotation values must * where in <code>parent</code> the number of annotation values must
* be stored. * be stored.
*/ */
AnnotationWriter(final ClassWriter cw, final boolean named, AnnotationWriter(final ClassWriter cw, final boolean named,
@@ -354,7 +354,7 @@ final class AnnotationWriter extends AnnotationVisitor {
* @param typePath * @param typePath
* the path to the annotated type argument, wildcard bound, array * the path to the annotated type argument, wildcard bound, array
* element type, or static inner type within 'typeRef'. May be * element type, or static inner type within 'typeRef'. May be
* <tt>null</tt> if the annotation targets 'typeRef' as a whole. * <code>null</code> if the annotation targets 'typeRef' as a whole.
* @param out * @param out
* where the type reference and type path must be put. * where the type reference and type path must be put.
*/ */

View File

@@ -77,7 +77,7 @@ class Attribute {
byte[] value; byte[] value;
/** /**
* The next attribute in this attribute list. May be <tt>null</tt>. * The next attribute in this attribute list. May be <code>null</code>.
*/ */
Attribute next; Attribute next;
@@ -92,19 +92,19 @@ class Attribute {
} }
/** /**
* Returns <tt>true</tt> if this type of attribute is unknown. The default * Returns <code>true</code> if this type of attribute is unknown. The default
* implementation of this method always returns <tt>true</tt>. * implementation of this method always returns <code>true</code>.
* *
* @return <tt>true</tt> if this type of attribute is unknown. * @return <code>true</code> if this type of attribute is unknown.
*/ */
public boolean isUnknown() { public boolean isUnknown() {
return true; return true;
} }
/** /**
* Returns <tt>true</tt> if this type of attribute is a code attribute. * Returns <code>true</code> if this type of attribute is a code attribute.
* *
* @return <tt>true</tt> if this type of attribute is a code attribute. * @return <code>true</code> if this type of attribute is a code attribute.
*/ */
public boolean isCodeAttribute() { public boolean isCodeAttribute() {
return false; return false;
@@ -113,7 +113,7 @@ class Attribute {
/** /**
* Returns the labels corresponding to this attribute. * Returns the labels corresponding to this attribute.
* *
* @return the labels corresponding to this attribute, or <tt>null</tt> if * @return the labels corresponding to this attribute, or <code>null</code> if
* this attribute is not a code attribute that contains labels. * this attribute is not a code attribute that contains labels.
*/ */
protected Label[] getLabels() { protected Label[] getLabels() {
@@ -123,7 +123,7 @@ class Attribute {
/** /**
* Reads a {@link #type type} attribute. This method must return a * Reads a {@link #type type} attribute. This method must return a
* <i>new</i> {@link Attribute} object, of type {@link #type type}, * <i>new</i> {@link Attribute} object, of type {@link #type type},
* corresponding to the <tt>len</tt> bytes starting at the given offset, in * corresponding to the <code>len</code> bytes starting at the given offset, in
* the given class reader. * the given class reader.
* *
* @param cr * @param cr
@@ -146,7 +146,7 @@ class Attribute {
* containing the type and the length of the attribute, are not * containing the type and the length of the attribute, are not
* taken into account here. * taken into account here.
* @param labels * @param labels
* the labels of the method's code, or <tt>null</tt> if the * the labels of the method's code, or <code>null</code> if the
* attribute to be read is not a code attribute. * attribute to be read is not a code attribute.
* @return a <i>new</i> {@link Attribute} object corresponding to the given * @return a <i>new</i> {@link Attribute} object corresponding to the given
* bytes. * bytes.
@@ -169,11 +169,11 @@ class Attribute {
* class the items that corresponds to this attribute. * class the items that corresponds to this attribute.
* @param code * @param code
* the bytecode of the method corresponding to this code * the bytecode of the method corresponding to this code
* attribute, or <tt>null</tt> if this attribute is not a code * attribute, or <code>null</code> if this attribute is not a code
* attributes. * attributes.
* @param len * @param len
* the length of the bytecode of the method corresponding to this * the length of the bytecode of the method corresponding to this
* code attribute, or <tt>null</tt> if this attribute is not a * code attribute, or <code>null</code> if this attribute is not a
* code attribute. * code attribute.
* @param maxStack * @param maxStack
* the maximum stack size of the method corresponding to this * the maximum stack size of the method corresponding to this
@@ -216,11 +216,11 @@ class Attribute {
* byte arrays, with the {@link #write write} method. * byte arrays, with the {@link #write write} method.
* @param code * @param code
* the bytecode of the method corresponding to these code * the bytecode of the method corresponding to these code
* attributes, or <tt>null</tt> if these attributes are not code * attributes, or <code>null</code> if these attributes are not code
* attributes. * attributes.
* @param len * @param len
* the length of the bytecode of the method corresponding to * the length of the bytecode of the method corresponding to
* these code attributes, or <tt>null</tt> if these attributes * these code attributes, or <code>null</code> if these attributes
* are not code attributes. * are not code attributes.
* @param maxStack * @param maxStack
* the maximum stack size of the method corresponding to these * the maximum stack size of the method corresponding to these
@@ -254,11 +254,11 @@ class Attribute {
* byte arrays, with the {@link #write write} method. * byte arrays, with the {@link #write write} method.
* @param code * @param code
* the bytecode of the method corresponding to these code * the bytecode of the method corresponding to these code
* attributes, or <tt>null</tt> if these attributes are not code * attributes, or <code>null</code> if these attributes are not code
* attributes. * attributes.
* @param len * @param len
* the length of the bytecode of the method corresponding to * the length of the bytecode of the method corresponding to
* these code attributes, or <tt>null</tt> if these attributes * these code attributes, or <code>null</code> if these attributes
* are not code attributes. * are not code attributes.
* @param maxStack * @param maxStack
* the maximum stack size of the method corresponding to these * the maximum stack size of the method corresponding to these

View File

@@ -332,7 +332,7 @@ public class ByteVector {
* automatically enlarged if necessary. * automatically enlarged if necessary.
* *
* @param b * @param b
* an array of bytes. May be <tt>null</tt> to put <tt>len</tt> * an array of bytes. May be <code>null</code> to put <code>len</code>
* null bytes into this byte vector. * null bytes into this byte vector.
* @param off * @param off
* index of the fist byte of b that must be copied. * index of the fist byte of b that must be copied.

View File

@@ -267,7 +267,7 @@ public class ClassReader {
* {@link Type#getInternalName() getInternalName}). For interfaces, the * {@link Type#getInternalName() getInternalName}). For interfaces, the
* super class is {@link Object}. * super class is {@link Object}.
* *
* @return the internal name of super class, or <tt>null</tt> for * @return the internal name of super class, or <code>null</code> for
* {@link Object} class. * {@link Object} class.
* *
* @see ClassVisitor#visit(int, int, String, String, String, String[]) * @see ClassVisitor#visit(int, int, String, String, String, String[])
@@ -281,7 +281,7 @@ public class ClassReader {
* {@link Type#getInternalName() getInternalName}). * {@link Type#getInternalName() getInternalName}).
* *
* @return the array of internal names for all implemented interfaces or * @return the array of internal names for all implemented interfaces or
* <tt>null</tt>. * <code>null</code>.
* *
* @see ClassVisitor#visit(int, int, String, String, String, String[]) * @see ClassVisitor#visit(int, int, String, String, String, String[])
*/ */
@@ -1932,7 +1932,7 @@ public class ClassReader {
* @param v * @param v
* start offset in {@link #b b} of the annotations to be read. * start offset in {@link #b b} of the annotations to be read.
* @param visible * @param visible
* <tt>true</tt> if the annotations to be read are visible at * <code>true</code> if the annotations to be read are visible at
* runtime. * runtime.
*/ */
private void readParameterAnnotations(final MethodVisitor mv, private void readParameterAnnotations(final MethodVisitor mv,
@@ -2474,9 +2474,9 @@ public class ClassReader {
* and the length of the attribute, are not taken into account * and the length of the attribute, are not taken into account
* here. * here.
* @param labels * @param labels
* the labels of the method's code, or <tt>null</tt> if the * the labels of the method's code, or <code>null</code> if the
* attribute to be read is not a code attribute. * attribute to be read is not a code attribute.
* @return the attribute that has been read, or <tt>null</tt> to skip this * @return the attribute that has been read, or <code>null</code> to skip this
* attribute. * attribute.
*/ */
private Attribute readAttribute(final Attribute[] attrs, final String type, private Attribute readAttribute(final Attribute[] attrs, final String type,

View File

@@ -60,11 +60,11 @@ package org.redkale.asm;
/** /**
* A visitor to visit a Java class. The methods of this class must be called in * A visitor to visit a Java class. The methods of this class must be called in
* the following order: <tt>visit</tt> [ <tt>visitSource</tt> ] [ * the following order: <code>visit</code> [ <code>visitSource</code> ] [
* <tt>visitModule</tt> ][ <tt>visitOuterClass</tt> ] ( <tt>visitAnnotation</tt> | * <code>visitModule</code> ][ <code>visitOuterClass</code> ] ( <code>visitAnnotation</code> |
* <tt>visitTypeAnnotation</tt> | <tt>visitAttribute</tt> )* ( * <code>visitTypeAnnotation</code> | <code>visitAttribute</code> )* (
* <tt>visitInnerClass</tt> | <tt>visitField</tt> | <tt>visitMethod</tt> )* * <code>visitInnerClass</code> | <code>visitField</code> | <code>visitMethod</code> )*
* <tt>visitEnd</tt>. * <code>visitEnd</code>.
* *
* @author Eric Bruneton * @author Eric Bruneton
*/ */
@@ -120,18 +120,18 @@ public abstract class ClassVisitor {
* the internal name of the class (see * the internal name of the class (see
* {@link Type#getInternalName() getInternalName}). * {@link Type#getInternalName() getInternalName}).
* @param signature * @param signature
* the signature of this class. May be <tt>null</tt> if the class * the signature of this class. May be <code>null</code> if the class
* is not a generic one, and does not extend or implement generic * is not a generic one, and does not extend or implement generic
* classes or interfaces. * classes or interfaces.
* @param superName * @param superName
* the internal of name of the super class (see * the internal of name of the super class (see
* {@link Type#getInternalName() getInternalName}). For * {@link Type#getInternalName() getInternalName}). For
* interfaces, the super class is {@link Object}. May be * interfaces, the super class is {@link Object}. May be
* <tt>null</tt>, but only for the {@link Object} class. * <code>null</code>, but only for the {@link Object} class.
* @param interfaces * @param interfaces
* the internal names of the class's interfaces (see * the internal names of the class's interfaces (see
* {@link Type#getInternalName() getInternalName}). May be * {@link Type#getInternalName() getInternalName}). May be
* <tt>null</tt>. * <code>null</code>.
*/ */
public void visit(int version, int access, String name, String signature, public void visit(int version, int access, String name, String signature,
String superName, String[] interfaces) { String superName, String[] interfaces) {
@@ -145,11 +145,11 @@ public abstract class ClassVisitor {
* *
* @param source * @param source
* the name of the source file from which the class was compiled. * the name of the source file from which the class was compiled.
* May be <tt>null</tt>. * May be <code>null</code>.
* @param debug * @param debug
* additional debug information to compute the correspondance * additional debug information to compute the correspondance
* between source and compiled elements of the class. May be * between source and compiled elements of the class. May be
* <tt>null</tt>. * <code>null</code>.
*/ */
public void visitSource(String source, String debug) { public void visitSource(String source, String debug) {
if (cv != null) { if (cv != null) {
@@ -166,7 +166,7 @@ public abstract class ClassVisitor {
* and {@code ACC_MANDATED}. * and {@code ACC_MANDATED}.
* @param version * @param version
* module version or null. * module version or null.
* @return a visitor to visit the module values, or <tt>null</tt> if * @return a visitor to visit the module values, or <code>null</code> if
* this visitor is not interested in visiting this module. * this visitor is not interested in visiting this module.
*/ */
public ModuleVisitor visitModule(String name, int access, String version) { public ModuleVisitor visitModule(String name, int access, String version) {
@@ -187,11 +187,11 @@ public abstract class ClassVisitor {
* internal name of the enclosing class of the class. * internal name of the enclosing class of the class.
* @param name * @param name
* the name of the method that contains the class, or * the name of the method that contains the class, or
* <tt>null</tt> if the class is not enclosed in a method of its * <code>null</code> if the class is not enclosed in a method of its
* enclosing class. * enclosing class.
* @param desc * @param desc
* the descriptor of the method that contains the class, or * the descriptor of the method that contains the class, or
* <tt>null</tt> if the class is not enclosed in a method of its * <code>null</code> if the class is not enclosed in a method of its
* enclosing class. * enclosing class.
*/ */
public void visitOuterClass(String owner, String name, String desc) { public void visitOuterClass(String owner, String name, String desc) {
@@ -206,8 +206,8 @@ public abstract class ClassVisitor {
* @param desc * @param desc
* the class descriptor of the annotation class. * the class descriptor of the annotation class.
* @param visible * @param visible
* <tt>true</tt> if the annotation is visible at runtime. * <code>true</code> if the annotation is visible at runtime.
* @return a visitor to visit the annotation values, or <tt>null</tt> if * @return a visitor to visit the annotation values, or <code>null</code> if
* this visitor is not interested in visiting this annotation. * this visitor is not interested in visiting this annotation.
*/ */
public AnnotationVisitor visitAnnotation(String desc, boolean visible) { public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
@@ -231,12 +231,12 @@ public abstract class ClassVisitor {
* @param typePath * @param typePath
* the path to the annotated type argument, wildcard bound, array * the path to the annotated type argument, wildcard bound, array
* element type, or static inner type within 'typeRef'. May be * element type, or static inner type within 'typeRef'. May be
* <tt>null</tt> if the annotation targets 'typeRef' as a whole. * <code>null</code> if the annotation targets 'typeRef' as a whole.
* @param desc * @param desc
* the class descriptor of the annotation class. * the class descriptor of the annotation class.
* @param visible * @param visible
* <tt>true</tt> if the annotation is visible at runtime. * <code>true</code> if the annotation is visible at runtime.
* @return a visitor to visit the annotation values, or <tt>null</tt> if * @return a visitor to visit the annotation values, or <code>null</code> if
* this visitor is not interested in visiting this annotation. * this visitor is not interested in visiting this annotation.
*/ */
public AnnotationVisitor visitTypeAnnotation(int typeRef, public AnnotationVisitor visitTypeAnnotation(int typeRef,
@@ -269,10 +269,10 @@ public abstract class ClassVisitor {
* @param outerName * @param outerName
* the internal name of the class to which the inner class * the internal name of the class to which the inner class
* belongs (see {@link Type#getInternalName() getInternalName}). * belongs (see {@link Type#getInternalName() getInternalName}).
* May be <tt>null</tt> for not member classes. * May be <code>null</code> for not member classes.
* @param innerName * @param innerName
* the (simple) name of the inner class inside its enclosing * the (simple) name of the inner class inside its enclosing
* class. May be <tt>null</tt> for anonymous inner classes. * class. May be <code>null</code> for anonymous inner classes.
* @param access * @param access
* the access flags of the inner class as originally declared in * the access flags of the inner class as originally declared in
* the enclosing class. * the enclosing class.
@@ -295,20 +295,20 @@ public abstract class ClassVisitor {
* @param desc * @param desc
* the field's descriptor (see {@link Type Type}). * the field's descriptor (see {@link Type Type}).
* @param signature * @param signature
* the field's signature. May be <tt>null</tt> if the field's * the field's signature. May be <code>null</code> if the field's
* type does not use generic types. * type does not use generic types.
* @param value * @param value
* the field's initial value. This parameter, which may be * the field's initial value. This parameter, which may be
* <tt>null</tt> if the field does not have an initial value, * <code>null</code> if the field does not have an initial value,
* must be an {@link Integer}, a {@link Float}, a {@link Long}, a * must be an {@link Integer}, a {@link Float}, a {@link Long}, a
* {@link Double} or a {@link String} (for <tt>int</tt>, * {@link Double} or a {@link String} (for <code>int</code>,
* <tt>float</tt>, <tt>long</tt> or <tt>String</tt> fields * <code>float</code>, <code>long</code> or <code>String</code> fields
* respectively). <i>This parameter is only used for static * respectively). <i>This parameter is only used for static
* fields</i>. Its value is ignored for non static fields, which * fields</i>. Its value is ignored for non static fields, which
* must be initialized through bytecode instructions in * must be initialized through bytecode instructions in
* constructors or methods. * constructors or methods.
* @return a visitor to visit field annotations and attributes, or * @return a visitor to visit field annotations and attributes, or
* <tt>null</tt> if this class visitor is not interested in visiting * <code>null</code> if this class visitor is not interested in visiting
* these annotations and attributes. * these annotations and attributes.
*/ */
public FieldVisitor visitField(int access, String name, String desc, public FieldVisitor visitField(int access, String name, String desc,
@@ -321,7 +321,7 @@ public abstract class ClassVisitor {
/** /**
* Visits a method of the class. This method <i>must</i> return a new * Visits a method of the class. This method <i>must</i> return a new
* {@link MethodVisitor} instance (or <tt>null</tt>) each time it is called, * {@link MethodVisitor} instance (or <code>null</code>) each time it is called,
* i.e., it should not return a previously returned visitor. * i.e., it should not return a previously returned visitor.
* *
* @param access * @param access
@@ -333,14 +333,14 @@ public abstract class ClassVisitor {
* @param desc * @param desc
* the method's descriptor (see {@link Type Type}). * the method's descriptor (see {@link Type Type}).
* @param signature * @param signature
* the method's signature. May be <tt>null</tt> if the method * the method's signature. May be <code>null</code> if the method
* parameters, return type and exceptions do not use generic * parameters, return type and exceptions do not use generic
* types. * types.
* @param exceptions * @param exceptions
* the internal names of the method's exception classes (see * the internal names of the method's exception classes (see
* {@link Type#getInternalName() getInternalName}). May be * {@link Type#getInternalName() getInternalName}). May be
* <tt>null</tt>. * <code>null</code>.
* @return an object to visit the byte code of the method, or <tt>null</tt> * @return an object to visit the byte code of the method, or <code>null</code>
* if this class visitor is not interested in visiting the code of * if this class visitor is not interested in visiting the code of
* this method. * this method.
*/ */

View File

@@ -391,8 +391,8 @@ public class ClassWriter extends ClassVisitor {
* A type table used to temporarily store internal names that will not * A type table used to temporarily store internal names that will not
* necessarily be stored in the constant pool. This type table is used by * necessarily be stored in the constant pool. This type table is used by
* the control flow and data flow analysis algorithm used to compute stack * the control flow and data flow analysis algorithm used to compute stack
* map frames from scratch. This array associates to each index <tt>i</tt> * map frames from scratch. This array associates to each index <code>i</code>
* the Item whose index is <tt>i</tt>. All Item objects stored in this array * the Item whose index is <code>i</code>. All Item objects stored in this array
* are also stored in the {@link #items} hash table. These two arrays allow * are also stored in the {@link #items} hash table. These two arrays allow
* to retrieve an Item from its index or, conversely, to get the index of an * to retrieve an Item from its index or, conversely, to get the index of an
* Item from its value. Each Item stores an internal name in its * Item from its value. Each Item stores an internal name in its
@@ -556,7 +556,7 @@ public class ClassWriter extends ClassVisitor {
private int compute; private int compute;
/** /**
* <tt>true</tt> if some methods have wide forward jumps using ASM pseudo * <code>true</code> if some methods have wide forward jumps using ASM pseudo
* instructions, which need to be expanded into sequences of standard * instructions, which need to be expanded into sequences of standard
* bytecode instructions. In this case the class is re-read and re-written * bytecode instructions. In this case the class is re-read and re-written
* with a ClassReader -> ClassWriter chain to perform this transformation. * with a ClassReader -> ClassWriter chain to perform this transformation.
@@ -1486,7 +1486,7 @@ public class ClassWriter extends ClassVisitor {
* @param desc * @param desc
* the method's descriptor. * the method's descriptor.
* @param itf * @param itf
* <tt>true</tt> if <tt>owner</tt> is an interface. * <code>true</code> if <code>owner</code> is an interface.
* @return a new or already existing method reference item. * @return a new or already existing method reference item.
*/ */
Item newMethodItem(final String owner, final String name, Item newMethodItem(final String owner, final String name,
@@ -1515,7 +1515,7 @@ public class ClassWriter extends ClassVisitor {
* @param desc * @param desc
* the method's descriptor. * the method's descriptor.
* @param itf * @param itf
* <tt>true</tt> if <tt>owner</tt> is an interface. * <code>true</code> if <code>owner</code> is an interface.
* @return the index of a new or already existing method reference item. * @return the index of a new or already existing method reference item.
*/ */
public int newMethod(final String owner, final String name, public int newMethod(final String owner, final String name,
@@ -1778,7 +1778,7 @@ public class ClassWriter extends ClassVisitor {
* @param key * @param key
* a constant pool item. * a constant pool item.
* @return the constant pool's hash table item which is equal to the given * @return the constant pool's hash table item which is equal to the given
* item, or <tt>null</tt> if there is no such item. * item, or <code>null</code> if there is no such item.
*/ */
private Item get(final Item key) { private Item get(final Item key) {
Item i = items[key.hashCode % items.length]; Item i = items[key.hashCode % items.length];

View File

@@ -60,8 +60,8 @@ package org.redkale.asm;
/** /**
* A visitor to visit a Java field. The methods of this class must be called in * A visitor to visit a Java field. The methods of this class must be called in
* the following order: ( <tt>visitAnnotation</tt> | * the following order: ( <code>visitAnnotation</code> |
* <tt>visitTypeAnnotation</tt> | <tt>visitAttribute</tt> )* <tt>visitEnd</tt>. * <code>visitTypeAnnotation</code> | <code>visitAttribute</code> )* <code>visitEnd</code>.
* *
* @author Eric Bruneton * @author Eric Bruneton
*/ */
@@ -111,8 +111,8 @@ public abstract class FieldVisitor {
* @param desc * @param desc
* the class descriptor of the annotation class. * the class descriptor of the annotation class.
* @param visible * @param visible
* <tt>true</tt> if the annotation is visible at runtime. * <code>true</code> if the annotation is visible at runtime.
* @return a visitor to visit the annotation values, or <tt>null</tt> if * @return a visitor to visit the annotation values, or <code>null</code> if
* this visitor is not interested in visiting this annotation. * this visitor is not interested in visiting this annotation.
*/ */
public AnnotationVisitor visitAnnotation(String desc, boolean visible) { public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
@@ -132,12 +132,12 @@ public abstract class FieldVisitor {
* @param typePath * @param typePath
* the path to the annotated type argument, wildcard bound, array * the path to the annotated type argument, wildcard bound, array
* element type, or static inner type within 'typeRef'. May be * element type, or static inner type within 'typeRef'. May be
* <tt>null</tt> if the annotation targets 'typeRef' as a whole. * <code>null</code> if the annotation targets 'typeRef' as a whole.
* @param desc * @param desc
* the class descriptor of the annotation class. * the class descriptor of the annotation class.
* @param visible * @param visible
* <tt>true</tt> if the annotation is visible at runtime. * <code>true</code> if the annotation is visible at runtime.
* @return a visitor to visit the annotation values, or <tt>null</tt> if * @return a visitor to visit the annotation values, or <code>null</code> if
* this visitor is not interested in visiting this annotation. * this visitor is not interested in visiting this annotation.
*/ */
public AnnotationVisitor visitTypeAnnotation(int typeRef, public AnnotationVisitor visitTypeAnnotation(int typeRef,

View File

@@ -100,28 +100,28 @@ final class FieldWriter extends FieldVisitor {
private int value; private int value;
/** /**
* The runtime visible annotations of this field. May be <tt>null</tt>. * The runtime visible annotations of this field. May be <code>null</code>.
*/ */
private AnnotationWriter anns; private AnnotationWriter anns;
/** /**
* The runtime invisible annotations of this field. May be <tt>null</tt>. * The runtime invisible annotations of this field. May be <code>null</code>.
*/ */
private AnnotationWriter ianns; private AnnotationWriter ianns;
/** /**
* The runtime visible type annotations of this field. May be <tt>null</tt>. * The runtime visible type annotations of this field. May be <code>null</code>.
*/ */
private AnnotationWriter tanns; private AnnotationWriter tanns;
/** /**
* The runtime invisible type annotations of this field. May be * The runtime invisible type annotations of this field. May be
* <tt>null</tt>. * <code>null</code>.
*/ */
private AnnotationWriter itanns; private AnnotationWriter itanns;
/** /**
* The non standard attributes of this field. May be <tt>null</tt>. * The non standard attributes of this field. May be <code>null</code>.
*/ */
private Attribute attrs; private Attribute attrs;
@@ -141,9 +141,9 @@ final class FieldWriter extends FieldVisitor {
* @param desc * @param desc
* the field's descriptor (see {@link Type}). * the field's descriptor (see {@link Type}).
* @param signature * @param signature
* the field's signature. May be <tt>null</tt>. * the field's signature. May be <code>null</code>.
* @param value * @param value
* the field's constant value. May be <tt>null</tt>. * the field's constant value. May be <code>null</code>.
*/ */
FieldWriter(final ClassWriter cw, final int access, final String name, FieldWriter(final ClassWriter cw, final int access, final String name,
final String desc, final String signature, final Object value) { final String desc, final String signature, final Object value) {

View File

@@ -1403,7 +1403,7 @@ class Frame {
/** /**
* Merges the input frame of the given basic block with the input and output * Merges the input frame of the given basic block with the input and output
* frames of this basic block. Returns <tt>true</tt> if the input frame of * frames of this basic block. Returns <code>true</code> if the input frame of
* the given label has been changed by this operation. * the given label has been changed by this operation.
* *
* @param cw * @param cw
@@ -1413,7 +1413,7 @@ class Frame {
* @param edge * @param edge
* the kind of the {@link Edge} between this label and 'label'. * the kind of the {@link Edge} between this label and 'label'.
* See {@link Edge#info}. * See {@link Edge#info}.
* @return <tt>true</tt> if the input frame of the given label has been * @return <code>true</code> if the input frame of the given label has been
* changed by this operation. * changed by this operation.
*/ */
final boolean merge(final ClassWriter cw, final Frame frame, final int edge) { final boolean merge(final ClassWriter cw, final Frame frame, final int edge) {
@@ -1511,7 +1511,7 @@ class Frame {
/** /**
* Merges the type at the given index in the given type array with the given * Merges the type at the given index in the given type array with the given
* type. Returns <tt>true</tt> if the type array has been modified by this * type. Returns <code>true</code> if the type array has been modified by this
* operation. * operation.
* *
* @param cw * @param cw
@@ -1522,7 +1522,7 @@ class Frame {
* an array of types. * an array of types.
* @param index * @param index
* the index of the type that must be merged in 'types'. * the index of the type that must be merged in 'types'.
* @return <tt>true</tt> if the type array has been modified by this * @return <code>true</code> if the type array has been modified by this
* operation. * operation.
*/ */
private static boolean merge(final ClassWriter cw, int t, private static boolean merge(final ClassWriter cw, int t,

View File

@@ -82,7 +82,7 @@ class Handler {
/** /**
* Internal name of the type of exceptions handled by this handler, or * Internal name of the type of exceptions handled by this handler, or
* <tt>null</tt> to catch any exceptions. * <code>null</code> to catch any exceptions.
*/ */
String desc; String desc;

View File

@@ -306,8 +306,8 @@ final class Item {
* @param i * @param i
* the item to be compared to this one. Both items must have the * the item to be compared to this one. Both items must have the
* same {@link #type}. * same {@link #type}.
* @return <tt>true</tt> if the given item if equal to this one, * @return <code>true</code> if the given item if equal to this one,
* <tt>false</tt> otherwise. * <code>false</code> otherwise.
*/ */
boolean isEqualTo(final Item i) { boolean isEqualTo(final Item i) {
switch (type) { switch (type) {

View File

@@ -325,8 +325,8 @@ public class Label {
* the position of first byte of the bytecode instruction that * the position of first byte of the bytecode instruction that
* contains this label. * contains this label.
* @param wideOffset * @param wideOffset
* <tt>true</tt> if the reference must be stored in 4 bytes, or * <code>true</code> if the reference must be stored in 4 bytes, or
* <tt>false</tt> if it must be stored with 2 bytes. * <code>false</code> if it must be stored with 2 bytes.
* @throws IllegalArgumentException * @throws IllegalArgumentException
* if this label has not been created by the given code writer. * if this label has not been created by the given code writer.
*/ */
@@ -389,7 +389,7 @@ public class Label {
* the position of this label in the bytecode. * the position of this label in the bytecode.
* @param data * @param data
* the bytecode of the method. * the bytecode of the method.
* @return <tt>true</tt> if a blank that was left for this label was too * @return <code>true</code> if a blank that was left for this label was too
* small to store the offset. In such a case the corresponding jump * small to store the offset. In such a case the corresponding jump
* instruction is replaced with a pseudo instruction (using unused * instruction is replaced with a pseudo instruction (using unused
* opcodes) using an unsigned two bytes offset. These pseudo * opcodes) using an unsigned two bytes offset. These pseudo

View File

@@ -60,24 +60,24 @@ package org.redkale.asm;
/** /**
* A visitor to visit a Java method. The methods of this class must be called in * A visitor to visit a Java method. The methods of this class must be called in
* the following order: ( <tt>visitParameter</tt> )* [ * the following order: ( <code>visitParameter</code> )* [
* <tt>visitAnnotationDefault</tt> ] ( <tt>visitAnnotation</tt> | * <code>visitAnnotationDefault</code> ] ( <code>visitAnnotation</code> |
* <tt>visitParameterAnnotation</tt> <tt>visitTypeAnnotation</tt> | * <code>visitParameterAnnotation</code> <code>visitTypeAnnotation</code> |
* <tt>visitAttribute</tt> )* [ <tt>visitCode</tt> ( <tt>visitFrame</tt> | * <code>visitAttribute</code> )* [ <code>visitCode</code> ( <code>visitFrame</code> |
* <tt>visit<i>X</i>Insn</tt> | <tt>visitLabel</tt> | * <code>visit<i>X</i>Insn</code> | <code>visitLabel</code> |
* <tt>visitInsnAnnotation</tt> | <tt>visitTryCatchBlock</tt> | * <code>visitInsnAnnotation</code> | <code>visitTryCatchBlock</code> |
* <tt>visitTryCatchAnnotation</tt> | <tt>visitLocalVariable</tt> | * <code>visitTryCatchAnnotation</code> | <code>visitLocalVariable</code> |
* <tt>visitLocalVariableAnnotation</tt> | <tt>visitLineNumber</tt> )* * <code>visitLocalVariableAnnotation</code> | <code>visitLineNumber</code> )*
* <tt>visitMaxs</tt> ] <tt>visitEnd</tt>. In addition, the * <code>visitMaxs</code> ] <code>visitEnd</code>. In addition, the
* <tt>visit<i>X</i>Insn</tt> and <tt>visitLabel</tt> methods must be called in * <code>visit<i>X</i>Insn</code> and <code>visitLabel</code> methods must be called in
* the sequential order of the bytecode instructions of the visited code, * the sequential order of the bytecode instructions of the visited code,
* <tt>visitInsnAnnotation</tt> must be called <i>after</i> the annotated * <code>visitInsnAnnotation</code> must be called <i>after</i> the annotated
* instruction, <tt>visitTryCatchBlock</tt> must be called <i>before</i> the * instruction, <code>visitTryCatchBlock</code> must be called <i>before</i> the
* labels passed as arguments have been visited, * labels passed as arguments have been visited,
* <tt>visitTryCatchBlockAnnotation</tt> must be called <i>after</i> the * <code>visitTryCatchBlockAnnotation</code> must be called <i>after</i> the
* corresponding try catch block has been visited, and the * corresponding try catch block has been visited, and the
* <tt>visitLocalVariable</tt>, <tt>visitLocalVariableAnnotation</tt> and * <code>visitLocalVariable</code>, <code>visitLocalVariableAnnotation</code> and
* <tt>visitLineNumber</tt> methods must be called <i>after</i> the labels * <code>visitLineNumber</code> methods must be called <i>after</i> the labels
* passed as arguments have been visited. * passed as arguments have been visited.
* *
* @author Eric Bruneton * @author Eric Bruneton
@@ -132,8 +132,8 @@ public abstract class MethodVisitor {
* @param name * @param name
* parameter name or null if none is provided. * parameter name or null if none is provided.
* @param access * @param access
* the parameter's access flags, only <tt>ACC_FINAL</tt>, * the parameter's access flags, only <code>ACC_FINAL</code>,
* <tt>ACC_SYNTHETIC</tt> or/and <tt>ACC_MANDATED</tt> are * <code>ACC_SYNTHETIC</code> or/and <code>ACC_MANDATED</code> are
* allowed (see {@link Opcodes}). * allowed (see {@link Opcodes}).
*/ */
public void visitParameter(String name, int access) { public void visitParameter(String name, int access) {
@@ -146,7 +146,7 @@ public abstract class MethodVisitor {
* Visits the default value of this annotation interface method. * Visits the default value of this annotation interface method.
* *
* @return a visitor to the visit the actual default value of this * @return a visitor to the visit the actual default value of this
* annotation interface method, or <tt>null</tt> if this visitor is * annotation interface method, or <code>null</code> if this visitor is
* not interested in visiting this default value. The 'name' * not interested in visiting this default value. The 'name'
* parameters passed to the methods of this annotation visitor are * parameters passed to the methods of this annotation visitor are
* ignored. Moreover, exacly one visit method must be called on this * ignored. Moreover, exacly one visit method must be called on this
@@ -165,8 +165,8 @@ public abstract class MethodVisitor {
* @param desc * @param desc
* the class descriptor of the annotation class. * the class descriptor of the annotation class.
* @param visible * @param visible
* <tt>true</tt> if the annotation is visible at runtime. * <code>true</code> if the annotation is visible at runtime.
* @return a visitor to visit the annotation values, or <tt>null</tt> if * @return a visitor to visit the annotation values, or <code>null</code> if
* this visitor is not interested in visiting this annotation. * this visitor is not interested in visiting this annotation.
*/ */
public AnnotationVisitor visitAnnotation(String desc, boolean visible) { public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
@@ -193,12 +193,12 @@ public abstract class MethodVisitor {
* @param typePath * @param typePath
* the path to the annotated type argument, wildcard bound, array * the path to the annotated type argument, wildcard bound, array
* element type, or static inner type within 'typeRef'. May be * element type, or static inner type within 'typeRef'. May be
* <tt>null</tt> if the annotation targets 'typeRef' as a whole. * <code>null</code> if the annotation targets 'typeRef' as a whole.
* @param desc * @param desc
* the class descriptor of the annotation class. * the class descriptor of the annotation class.
* @param visible * @param visible
* <tt>true</tt> if the annotation is visible at runtime. * <code>true</code> if the annotation is visible at runtime.
* @return a visitor to visit the annotation values, or <tt>null</tt> if * @return a visitor to visit the annotation values, or <code>null</code> if
* this visitor is not interested in visiting this annotation. * this visitor is not interested in visiting this annotation.
*/ */
public AnnotationVisitor visitTypeAnnotation(int typeRef, public AnnotationVisitor visitTypeAnnotation(int typeRef,
@@ -217,8 +217,8 @@ public abstract class MethodVisitor {
* @param desc * @param desc
* the class descriptor of the annotation class. * the class descriptor of the annotation class.
* @param visible * @param visible
* <tt>true</tt> if the annotation is visible at runtime. * <code>true</code> if the annotation is visible at runtime.
* @return a visitor to visit the annotation values, or <tt>null</tt> if * @return a visitor to visit the annotation values, or <code>null</code> if
* this visitor is not interested in visiting this annotation. * this visitor is not interested in visiting this annotation.
*/ */
public AnnotationVisitor visitParameterAnnotation(int parameter, public AnnotationVisitor visitParameterAnnotation(int parameter,
@@ -569,7 +569,7 @@ public abstract class MethodVisitor {
* the constant to be loaded on the stack. This parameter must be * the constant to be loaded on the stack. This parameter must be
* a non null {@link Integer}, a {@link Float}, a {@link Long}, a * a non null {@link Integer}, a {@link Float}, a {@link Long}, a
* {@link Double}, a {@link String}, a {@link Type} of OBJECT or * {@link Double}, a {@link String}, a {@link Type} of OBJECT or
* ARRAY sort for <tt>.class</tt> constants, for classes whose * ARRAY sort for <code>.class</code> constants, for classes whose
* version is 49.0, a {@link Type} of METHOD sort or a * version is 49.0, a {@link Type} of METHOD sort or a
* {@link Handle} for MethodType and MethodHandle constants, for * {@link Handle} for MethodType and MethodHandle constants, for
* classes whose version is 51.0. * classes whose version is 51.0.
@@ -604,8 +604,8 @@ public abstract class MethodVisitor {
* @param dflt * @param dflt
* beginning of the default handler block. * beginning of the default handler block.
* @param labels * @param labels
* beginnings of the handler blocks. <tt>labels[i]</tt> is the * beginnings of the handler blocks. <code>labels[i]</code> is the
* beginning of the handler block for the <tt>min + i</tt> key. * beginning of the handler block for the <code>min + i</code> key.
*/ */
public void visitTableSwitchInsn(int min, int max, Label dflt, public void visitTableSwitchInsn(int min, int max, Label dflt,
Label... labels) { Label... labels) {
@@ -622,8 +622,8 @@ public abstract class MethodVisitor {
* @param keys * @param keys
* the values of the keys. * the values of the keys.
* @param labels * @param labels
* beginnings of the handler blocks. <tt>labels[i]</tt> is the * beginnings of the handler blocks. <code>labels[i]</code> is the
* beginning of the handler block for the <tt>keys[i]</tt> key. * beginning of the handler block for the <code>keys[i]</code> key.
*/ */
public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) { public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) {
if (mv != null) { if (mv != null) {
@@ -668,12 +668,12 @@ public abstract class MethodVisitor {
* @param typePath * @param typePath
* the path to the annotated type argument, wildcard bound, array * the path to the annotated type argument, wildcard bound, array
* element type, or static inner type within 'typeRef'. May be * element type, or static inner type within 'typeRef'. May be
* <tt>null</tt> if the annotation targets 'typeRef' as a whole. * <code>null</code> if the annotation targets 'typeRef' as a whole.
* @param desc * @param desc
* the class descriptor of the annotation class. * the class descriptor of the annotation class.
* @param visible * @param visible
* <tt>true</tt> if the annotation is visible at runtime. * <code>true</code> if the annotation is visible at runtime.
* @return a visitor to visit the annotation values, or <tt>null</tt> if * @return a visitor to visit the annotation values, or <code>null</code> if
* this visitor is not interested in visiting this annotation. * this visitor is not interested in visiting this annotation.
*/ */
public AnnotationVisitor visitInsnAnnotation(int typeRef, public AnnotationVisitor visitInsnAnnotation(int typeRef,
@@ -699,7 +699,7 @@ public abstract class MethodVisitor {
* beginning of the exception handler's code. * beginning of the exception handler's code.
* @param type * @param type
* internal name of the type of exceptions handled by the * internal name of the type of exceptions handled by the
* handler, or <tt>null</tt> to catch any exceptions (for * handler, or <code>null</code> to catch any exceptions (for
* "finally" blocks). * "finally" blocks).
* @throws IllegalArgumentException * @throws IllegalArgumentException
* if one of the labels has already been visited by this visitor * if one of the labels has already been visited by this visitor
@@ -725,12 +725,12 @@ public abstract class MethodVisitor {
* @param typePath * @param typePath
* the path to the annotated type argument, wildcard bound, array * the path to the annotated type argument, wildcard bound, array
* element type, or static inner type within 'typeRef'. May be * element type, or static inner type within 'typeRef'. May be
* <tt>null</tt> if the annotation targets 'typeRef' as a whole. * <code>null</code> if the annotation targets 'typeRef' as a whole.
* @param desc * @param desc
* the class descriptor of the annotation class. * the class descriptor of the annotation class.
* @param visible * @param visible
* <tt>true</tt> if the annotation is visible at runtime. * <code>true</code> if the annotation is visible at runtime.
* @return a visitor to visit the annotation values, or <tt>null</tt> if * @return a visitor to visit the annotation values, or <code>null</code> if
* this visitor is not interested in visiting this annotation. * this visitor is not interested in visiting this annotation.
*/ */
public AnnotationVisitor visitTryCatchAnnotation(int typeRef, public AnnotationVisitor visitTryCatchAnnotation(int typeRef,
@@ -750,7 +750,7 @@ public abstract class MethodVisitor {
* the type descriptor of this local variable. * the type descriptor of this local variable.
* @param signature * @param signature
* the type signature of this local variable. May be * the type signature of this local variable. May be
* <tt>null</tt> if the local variable type does not use generic * <code>null</code> if the local variable type does not use generic
* types. * types.
* @param start * @param start
* the first instruction corresponding to the scope of this local * the first instruction corresponding to the scope of this local
@@ -782,7 +782,7 @@ public abstract class MethodVisitor {
* @param typePath * @param typePath
* the path to the annotated type argument, wildcard bound, array * the path to the annotated type argument, wildcard bound, array
* element type, or static inner type within 'typeRef'. May be * element type, or static inner type within 'typeRef'. May be
* <tt>null</tt> if the annotation targets 'typeRef' as a whole. * <code>null</code> if the annotation targets 'typeRef' as a whole.
* @param start * @param start
* the fist instructions corresponding to the continuous ranges * the fist instructions corresponding to the continuous ranges
* that make the scope of this local variable (inclusive). * that make the scope of this local variable (inclusive).
@@ -796,8 +796,8 @@ public abstract class MethodVisitor {
* @param desc * @param desc
* the class descriptor of the annotation class. * the class descriptor of the annotation class.
* @param visible * @param visible
* <tt>true</tt> if the annotation is visible at runtime. * <code>true</code> if the annotation is visible at runtime.
* @return a visitor to visit the annotation values, or <tt>null</tt> if * @return a visitor to visit the annotation values, or <code>null</code> if
* this visitor is not interested in visiting this annotation. * this visitor is not interested in visiting this annotation.
*/ */
public AnnotationVisitor visitLocalVariableAnnotation(int typeRef, public AnnotationVisitor visitLocalVariableAnnotation(int typeRef,
@@ -819,7 +819,7 @@ public abstract class MethodVisitor {
* @param start * @param start
* the first instruction corresponding to this line number. * the first instruction corresponding to this line number.
* @throws IllegalArgumentException * @throws IllegalArgumentException
* if <tt>start</tt> has not already been visited by this * if <code>start</code> has not already been visited by this
* visitor (by the {@link #visitLabel visitLabel} method). * visitor (by the {@link #visitLabel visitLabel} method).
*/ */
public void visitLineNumber(int line, Label start) { public void visitLineNumber(int line, Label start) {

View File

@@ -218,41 +218,41 @@ class MethodWriter extends MethodVisitor {
int[] exceptions; int[] exceptions;
/** /**
* The annotation default attribute of this method. May be <tt>null</tt>. * The annotation default attribute of this method. May be <code>null</code>.
*/ */
private ByteVector annd; private ByteVector annd;
/** /**
* The runtime visible annotations of this method. May be <tt>null</tt>. * The runtime visible annotations of this method. May be <code>null</code>.
*/ */
private AnnotationWriter anns; private AnnotationWriter anns;
/** /**
* The runtime invisible annotations of this method. May be <tt>null</tt>. * The runtime invisible annotations of this method. May be <code>null</code>.
*/ */
private AnnotationWriter ianns; private AnnotationWriter ianns;
/** /**
* The runtime visible type annotations of this method. May be <tt>null</tt> * The runtime visible type annotations of this method. May be <code>null</code>
* . * .
*/ */
private AnnotationWriter tanns; private AnnotationWriter tanns;
/** /**
* The runtime invisible type annotations of this method. May be * The runtime invisible type annotations of this method. May be
* <tt>null</tt>. * <code>null</code>.
*/ */
private AnnotationWriter itanns; private AnnotationWriter itanns;
/** /**
* The runtime visible parameter annotations of this method. May be * The runtime visible parameter annotations of this method. May be
* <tt>null</tt>. * <code>null</code>.
*/ */
private AnnotationWriter[] panns; private AnnotationWriter[] panns;
/** /**
* The runtime invisible parameter annotations of this method. May be * The runtime invisible parameter annotations of this method. May be
* <tt>null</tt>. * <code>null</code>.
*/ */
private AnnotationWriter[] ipanns; private AnnotationWriter[] ipanns;
@@ -381,12 +381,12 @@ class MethodWriter extends MethodVisitor {
private int lastCodeOffset; private int lastCodeOffset;
/** /**
* The runtime visible type annotations of the code. May be <tt>null</tt>. * The runtime visible type annotations of the code. May be <code>null</code>.
*/ */
private AnnotationWriter ctanns; private AnnotationWriter ctanns;
/** /**
* The runtime invisible type annotations of the code. May be <tt>null</tt>. * The runtime invisible type annotations of the code. May be <code>null</code>.
*/ */
private AnnotationWriter ictanns; private AnnotationWriter ictanns;
@@ -446,7 +446,7 @@ class MethodWriter extends MethodVisitor {
* is relative to the beginning of the current basic block, i.e., the true * is relative to the beginning of the current basic block, i.e., the true
* stack size after the last visited instruction is equal to the * stack size after the last visited instruction is equal to the
* {@link Label#inputStackTop beginStackSize} of the current basic block * {@link Label#inputStackTop beginStackSize} of the current basic block
* plus <tt>stackSize</tt>. * plus <code>stackSize</code>.
*/ */
private int stackSize; private int stackSize;
@@ -455,7 +455,7 @@ class MethodWriter extends MethodVisitor {
* This size is relative to the beginning of the current basic block, i.e., * This size is relative to the beginning of the current basic block, i.e.,
* the true maximum stack size after the last visited instruction is equal * the true maximum stack size after the last visited instruction is equal
* to the {@link Label#inputStackTop beginStackSize} of the current basic * to the {@link Label#inputStackTop beginStackSize} of the current basic
* block plus <tt>stackSize</tt>. * block plus <code>stackSize</code>.
*/ */
private int maxStackSize; private int maxStackSize;
@@ -475,10 +475,10 @@ class MethodWriter extends MethodVisitor {
* @param desc * @param desc
* the method's descriptor (see {@link Type}). * the method's descriptor (see {@link Type}).
* @param signature * @param signature
* the method's signature. May be <tt>null</tt>. * the method's signature. May be <code>null</code>.
* @param exceptions * @param exceptions
* the internal names of the method's exceptions. May be * the internal names of the method's exceptions. May be
* <tt>null</tt>. * <code>null</code>.
* @param compute * @param compute
* Indicates what must be automatically computed (see #compute). * Indicates what must be automatically computed (see #compute).
*/ */

View File

@@ -60,9 +60,9 @@ package org.redkale.asm;
/** /**
* A visitor to visit a Java module. The methods of this class must be called in * A visitor to visit a Java module. The methods of this class must be called in
* the following order: <tt>visitMainClass</tt> | ( <tt>visitPackage</tt> | * the following order: <code>visitMainClass</code> | ( <code>visitPackage</code> |
* <tt>visitRequire</tt> | <tt>visitExport</tt> | <tt>visitOpen</tt> | * <code>visitRequire</code> | <code>visitExport</code> | <code>visitOpen</code> |
* <tt>visitUse</tt> | <tt>visitProvide</tt> )* <tt>visitEnd</tt>. * <code>visitUse</code> | <code>visitProvide</code> )* <code>visitEnd</code>.
* *
* The methods {@link #visitRequire(String, int, String)}, {@link #visitExport(String, int, String...)}, * The methods {@link #visitRequire(String, int, String)}, {@link #visitExport(String, int, String...)},
* {@link #visitOpen(String, int, String...)} and {@link #visitPackage(String)} * {@link #visitOpen(String, int, String...)} and {@link #visitPackage(String)}
@@ -157,7 +157,7 @@ public abstract class ModuleVisitor {
* {@code ACC_MANDATED}. * {@code ACC_MANDATED}.
* @param modules the qualified names of the modules that can access to * @param modules the qualified names of the modules that can access to
* the public classes of the exported package or * the public classes of the exported package or
* <tt>null</tt>. * <code>null</code>.
*/ */
public void visitExport(String packaze, int access, String... modules) { public void visitExport(String packaze, int access, String... modules) {
if (mv != null) { if (mv != null) {
@@ -174,7 +174,7 @@ public abstract class ModuleVisitor {
* {@code ACC_MANDATED}. * {@code ACC_MANDATED}.
* @param modules the qualified names of the modules that can use deep * @param modules the qualified names of the modules that can use deep
* reflection to the classes of the open package or * reflection to the classes of the open package or
* <tt>null</tt>. * <code>null</code>.
*/ */
public void visitOpen(String packaze, int access, String... modules) { public void visitOpen(String packaze, int access, String... modules) {
if (mv != null) { if (mv != null) {

View File

@@ -71,47 +71,47 @@ import java.lang.reflect.Method;
public class Type { public class Type {
/** /**
* The sort of the <tt>void</tt> type. See {@link #getSort getSort}. * The sort of the <code>void</code> type. See {@link #getSort getSort}.
*/ */
public static final int VOID = 0; public static final int VOID = 0;
/** /**
* The sort of the <tt>boolean</tt> type. See {@link #getSort getSort}. * The sort of the <code>boolean</code> type. See {@link #getSort getSort}.
*/ */
public static final int BOOLEAN = 1; public static final int BOOLEAN = 1;
/** /**
* The sort of the <tt>char</tt> type. See {@link #getSort getSort}. * The sort of the <code>char</code> type. See {@link #getSort getSort}.
*/ */
public static final int CHAR = 2; public static final int CHAR = 2;
/** /**
* The sort of the <tt>byte</tt> type. See {@link #getSort getSort}. * The sort of the <code>byte</code> type. See {@link #getSort getSort}.
*/ */
public static final int BYTE = 3; public static final int BYTE = 3;
/** /**
* The sort of the <tt>short</tt> type. See {@link #getSort getSort}. * The sort of the <code>short</code> type. See {@link #getSort getSort}.
*/ */
public static final int SHORT = 4; public static final int SHORT = 4;
/** /**
* The sort of the <tt>int</tt> type. See {@link #getSort getSort}. * The sort of the <code>int</code> type. See {@link #getSort getSort}.
*/ */
public static final int INT = 5; public static final int INT = 5;
/** /**
* The sort of the <tt>float</tt> type. See {@link #getSort getSort}. * The sort of the <code>float</code> type. See {@link #getSort getSort}.
*/ */
public static final int FLOAT = 6; public static final int FLOAT = 6;
/** /**
* The sort of the <tt>long</tt> type. See {@link #getSort getSort}. * The sort of the <code>long</code> type. See {@link #getSort getSort}.
*/ */
public static final int LONG = 7; public static final int LONG = 7;
/** /**
* The sort of the <tt>double</tt> type. See {@link #getSort getSort}. * The sort of the <code>double</code> type. See {@link #getSort getSort}.
*/ */
public static final int DOUBLE = 8; public static final int DOUBLE = 8;
@@ -131,55 +131,55 @@ public class Type {
public static final int METHOD = 11; public static final int METHOD = 11;
/** /**
* The <tt>void</tt> type. * The <code>void</code> type.
*/ */
public static final Type VOID_TYPE = new Type(VOID, null, ('V' << 24) public static final Type VOID_TYPE = new Type(VOID, null, ('V' << 24)
| (5 << 16) | (0 << 8) | 0, 1); | (5 << 16) | (0 << 8) | 0, 1);
/** /**
* The <tt>boolean</tt> type. * The <code>boolean</code> type.
*/ */
public static final Type BOOLEAN_TYPE = new Type(BOOLEAN, null, ('Z' << 24) public static final Type BOOLEAN_TYPE = new Type(BOOLEAN, null, ('Z' << 24)
| (0 << 16) | (5 << 8) | 1, 1); | (0 << 16) | (5 << 8) | 1, 1);
/** /**
* The <tt>char</tt> type. * The <code>char</code> type.
*/ */
public static final Type CHAR_TYPE = new Type(CHAR, null, ('C' << 24) public static final Type CHAR_TYPE = new Type(CHAR, null, ('C' << 24)
| (0 << 16) | (6 << 8) | 1, 1); | (0 << 16) | (6 << 8) | 1, 1);
/** /**
* The <tt>byte</tt> type. * The <code>byte</code> type.
*/ */
public static final Type BYTE_TYPE = new Type(BYTE, null, ('B' << 24) public static final Type BYTE_TYPE = new Type(BYTE, null, ('B' << 24)
| (0 << 16) | (5 << 8) | 1, 1); | (0 << 16) | (5 << 8) | 1, 1);
/** /**
* The <tt>short</tt> type. * The <code>short</code> type.
*/ */
public static final Type SHORT_TYPE = new Type(SHORT, null, ('S' << 24) public static final Type SHORT_TYPE = new Type(SHORT, null, ('S' << 24)
| (0 << 16) | (7 << 8) | 1, 1); | (0 << 16) | (7 << 8) | 1, 1);
/** /**
* The <tt>int</tt> type. * The <code>int</code> type.
*/ */
public static final Type INT_TYPE = new Type(INT, null, ('I' << 24) public static final Type INT_TYPE = new Type(INT, null, ('I' << 24)
| (0 << 16) | (0 << 8) | 1, 1); | (0 << 16) | (0 << 8) | 1, 1);
/** /**
* The <tt>float</tt> type. * The <code>float</code> type.
*/ */
public static final Type FLOAT_TYPE = new Type(FLOAT, null, ('F' << 24) public static final Type FLOAT_TYPE = new Type(FLOAT, null, ('F' << 24)
| (2 << 16) | (2 << 8) | 1, 1); | (2 << 16) | (2 << 8) | 1, 1);
/** /**
* The <tt>long</tt> type. * The <code>long</code> type.
*/ */
public static final Type LONG_TYPE = new Type(LONG, null, ('J' << 24) public static final Type LONG_TYPE = new Type(LONG, null, ('J' << 24)
| (1 << 16) | (1 << 8) | 2, 1); | (1 << 16) | (1 << 8) | 2, 1);
/** /**
* The <tt>double</tt> type. * The <code>double</code> type.
*/ */
public static final Type DOUBLE_TYPE = new Type(DOUBLE, null, ('D' << 24) public static final Type DOUBLE_TYPE = new Type(DOUBLE, null, ('D' << 24)
| (3 << 16) | (3 << 8) | 2, 1); | (3 << 16) | (3 << 8) | 2, 1);
@@ -439,8 +439,8 @@ public class Type {
* @return the size of the arguments of the method (plus one for the * @return the size of the arguments of the method (plus one for the
* implicit this argument), argSize, and the size of its return * implicit this argument), argSize, and the size of its return
* value, retSize, packed into a single int i = * value, retSize, packed into a single int i =
* <tt>(argSize &lt;&lt; 2) | retSize</tt> (argSize is therefore equal to * <code>(argSize &lt;&lt; 2) | retSize</code> (argSize is therefore equal to
* <tt>i &gt;&gt; 2</tt>, and retSize to <tt>i &amp; 0x03</tt>). * <code>i &gt;&gt; 2</code>, and retSize to <code>i &amp; 0x03</code>).
*/ */
public static int getArgumentsAndReturnSizes(final String desc) { public static int getArgumentsAndReturnSizes(final String desc) {
int n = 1; int n = 1;
@@ -645,9 +645,9 @@ public class Type {
* @return the size of the arguments (plus one for the implicit this * @return the size of the arguments (plus one for the implicit this
* argument), argSize, and the size of the return value, retSize, * argument), argSize, and the size of the return value, retSize,
* packed into a single * packed into a single
* int i = <tt>(argSize &lt;&lt; 2) | retSize</tt> * int i = <code>(argSize &lt;&lt; 2) | retSize</code>
* (argSize is therefore equal to <tt>i &gt;&gt; 2</tt>, * (argSize is therefore equal to <code>i &gt;&gt; 2</code>,
* and retSize to <tt>i &amp; 0x03</tt>). * and retSize to <code>i &amp; 0x03</code>).
*/ */
public int getArgumentsAndReturnSizes() { public int getArgumentsAndReturnSizes() {
return getArgumentsAndReturnSizes(getDescriptor()); return getArgumentsAndReturnSizes(getDescriptor());
@@ -838,8 +838,8 @@ public class Type {
* Returns the size of values of this type. This method must not be used for * Returns the size of values of this type. This method must not be used for
* method types. * method types.
* *
* @return the size of values of this type, i.e., 2 for <tt>long</tt> and * @return the size of values of this type, i.e., 2 for <code>long</code> and
* <tt>double</tt>, 0 for <tt>void</tt> and 1 otherwise. * <code>double</code>, 0 for <code>void</code> and 1 otherwise.
*/ */
public int getSize() { public int getSize() {
// the size is in byte 0 of 'off' for primitive types (buf == null) // the size is in byte 0 of 'off' for primitive types (buf == null)
@@ -855,8 +855,8 @@ public class Type {
* ISTORE, IALOAD, IASTORE, IADD, ISUB, IMUL, IDIV, IREM, INEG, * ISTORE, IALOAD, IASTORE, IADD, ISUB, IMUL, IDIV, IREM, INEG,
* ISHL, ISHR, IUSHR, IAND, IOR, IXOR and IRETURN. * ISHL, ISHR, IUSHR, IAND, IOR, IXOR and IRETURN.
* @return an opcode that is similar to the given opcode, but adapted to * @return an opcode that is similar to the given opcode, but adapted to
* this Java type. For example, if this type is <tt>float</tt> and * this Java type. For example, if this type is <code>float</code> and
* <tt>opcode</tt> is IRETURN, this method returns FRETURN. * <code>opcode</code> is IRETURN, this method returns FRETURN.
*/ */
public int getOpcode(final int opcode) { public int getOpcode(final int opcode) {
if (opcode == Opcodes.IALOAD || opcode == Opcodes.IASTORE) { if (opcode == Opcodes.IALOAD || opcode == Opcodes.IASTORE) {
@@ -879,7 +879,7 @@ public class Type {
* *
* @param o * @param o
* the object to be compared to this type. * the object to be compared to this type.
* @return <tt>true</tt> if the given object is equal to this type. * @return <code>true</code> if the given object is equal to this type.
*/ */
@Override @Override
public boolean equals(final Object o) { public boolean equals(final Object o) {

View File

@@ -5,6 +5,7 @@
*/ */
package org.redkale.boot; package org.redkale.boot;
import org.redkale.cluster.ClusterAgent;
import org.redkale.util.RedkaleClassLoader; import org.redkale.util.RedkaleClassLoader;
import org.redkale.net.TransportGroupInfo; import org.redkale.net.TransportGroupInfo;
import java.io.*; import java.io.*;
@@ -23,7 +24,8 @@ import javax.xml.parsers.*;
import org.redkale.boot.ClassFilter.FilterEntry; import org.redkale.boot.ClassFilter.FilterEntry;
import org.redkale.convert.Convert; import org.redkale.convert.Convert;
import org.redkale.convert.bson.BsonFactory; import org.redkale.convert.bson.BsonFactory;
import org.redkale.convert.json.JsonFactory; import org.redkale.convert.json.*;
import org.redkale.mq.*;
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.*; import org.redkale.net.sncp.*;
@@ -56,6 +58,11 @@ public final class Application {
*/ */
public static final String RESNAME_APP_TIME = "APP_TIME"; public static final String RESNAME_APP_TIME = "APP_TIME";
/**
* 当前进程的名称, 类型String
*/
public static final String RESNAME_APP_NAME = "APP_NAME";
/** /**
* 当前进程的根目录, 类型String、File、Path、URI * 当前进程的根目录, 类型String、File、Path、URI
*/ */
@@ -72,12 +79,12 @@ public final class Application {
public static final String RESNAME_APP_GRES = "APP_GRES"; public static final String RESNAME_APP_GRES = "APP_GRES";
/** /**
* 当前进程节点的name 类型:String * 当前进程节点的nodeid 类型int
*/ */
public static final String RESNAME_APP_NODE = "APP_NODE"; public static final String RESNAME_APP_NODEID = "APP_NODEID";
/** /**
* 当前进程节点的IP地址 类型InetAddress、String * 当前进程节点的IP地址 类型:InetSocketAddress、InetAddress、String
*/ */
public static final String RESNAME_APP_ADDR = "APP_ADDR"; public static final String RESNAME_APP_ADDR = "APP_ADDR";
@@ -106,8 +113,14 @@ public final class Application {
*/ */
public static final String RESNAME_SERVER_RESFACTORY = Server.RESNAME_SERVER_RESFACTORY; public static final String RESNAME_SERVER_RESFACTORY = Server.RESNAME_SERVER_RESFACTORY;
//本进程节点ID
final int nodeid;
//本进程节点ID
final String name;
//本地IP地址 //本地IP地址
final InetAddress localAddress; final InetSocketAddress localAddress;
//CacheSource 资源 //CacheSource 资源
final List<CacheSource> cacheSources = new CopyOnWriteArrayList<>(); final List<CacheSource> cacheSources = new CopyOnWriteArrayList<>();
@@ -115,18 +128,29 @@ public final class Application {
//DataSource 资源 //DataSource 资源
final List<DataSource> dataSources = new CopyOnWriteArrayList<>(); final List<DataSource> dataSources = new CopyOnWriteArrayList<>();
//NodeServer 资源 //NodeServer 资源, 顺序必须是sncps, others, watchs
final List<NodeServer> servers = new CopyOnWriteArrayList<>(); final List<NodeServer> servers = new CopyOnWriteArrayList<>();
//SNCP传输端的TransportFactory, 注意: 只给SNCP使用 //SNCP传输端的TransportFactory, 注意: 只给SNCP使用
final TransportFactory sncpTransportFactory; final TransportFactory sncpTransportFactory;
//第三方服务发现管理接口
//@since 2.1.0
final ClusterAgent clusterAgent;
//MQ管理接口
//@since 2.1.0
final MessageAgent[] messageAgents;
//全局根ResourceFactory //全局根ResourceFactory
final ResourceFactory resourceFactory = ResourceFactory.root(); final ResourceFactory resourceFactory = ResourceFactory.root();
//服务配置项 //服务配置项
final AnyValue config; final AnyValue config;
//排除的jar路径
final String excludelibs;
//临时计数器 //临时计数器
CountDownLatch servicecdl; //会出现两次赋值 CountDownLatch servicecdl; //会出现两次赋值
@@ -192,24 +216,22 @@ public final class Application {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
String localaddr = config.getValue("address", "").trim(); String localaddr = config.getValue("address", "").trim();
this.localAddress = localaddr.isEmpty() ? Utility.localInetAddress() : new InetSocketAddress(localaddr, config.getIntValue("port")).getAddress(); InetAddress addr = localaddr.isEmpty() ? Utility.localInetAddress() : new InetSocketAddress(localaddr, config.getIntValue("port")).getAddress();
this.resourceFactory.register(RESNAME_APP_ADDR, this.localAddress.getHostAddress()); this.localAddress = new InetSocketAddress(addr, config.getIntValue("port"));
this.resourceFactory.register(RESNAME_APP_ADDR, InetAddress.class, this.localAddress); this.resourceFactory.register(RESNAME_APP_ADDR, addr.getHostAddress());
this.resourceFactory.register(RESNAME_APP_ADDR, InetAddress.class, addr);
this.resourceFactory.register(RESNAME_APP_ADDR, InetSocketAddress.class, this.localAddress);
{ {
String node = config.getValue("node", "").trim(); int nid = config.getIntValue("nodeid", 0);
if (node.isEmpty()) { this.nodeid = nid;
StringBuilder sb = new StringBuilder(); this.resourceFactory.register(RESNAME_APP_NODEID, nid);
byte[] bs = this.localAddress.getAddress(); System.setProperty(RESNAME_APP_NODEID, "" + nid);
int v1 = bs[bs.length - 2] & 0xff;
int v2 = bs[bs.length - 1] & 0xff;
if (v1 <= 0xf) sb.append('0');
sb.append(Integer.toHexString(v1));
if (v2 <= 0xf) sb.append('0');
sb.append(Integer.toHexString(v2));
node = sb.toString();
} }
this.resourceFactory.register(RESNAME_APP_NODE, node); {
System.setProperty(RESNAME_APP_NODE, node); this.name = checkName(config.getValue("name", ""));
this.resourceFactory.register(RESNAME_APP_NAME, name);
System.setProperty(RESNAME_APP_NAME, name);
} }
//以下是初始化日志配置 //以下是初始化日志配置
final URI logConfURI = "file".equals(confPath.getScheme()) ? new File(new File(confPath), "logging.properties").toURI() final URI logConfURI = "file".equals(confPath.getScheme()) ? new File(new File(confPath), "logging.properties").toURI()
@@ -271,6 +293,9 @@ public final class Application {
AsynchronousChannelGroup transportGroup = null; AsynchronousChannelGroup transportGroup = null;
final AnyValue resources = config.getAnyValue("resources"); final AnyValue resources = config.getAnyValue("resources");
TransportStrategy strategy = null; TransportStrategy strategy = null;
String excludelib0 = null;
ClusterAgent cluster = null;
MessageAgent[] mqs = null;
int bufferCapacity = 32 * 1024; int bufferCapacity = 32 * 1024;
int bufferPoolSize = Runtime.getRuntime().availableProcessors() * 8; int bufferPoolSize = Runtime.getRuntime().availableProcessors() * 8;
int readTimeoutSeconds = TransportFactory.DEFAULT_READTIMEOUTSECONDS; int readTimeoutSeconds = TransportFactory.DEFAULT_READTIMEOUTSECONDS;
@@ -278,6 +303,8 @@ public final class Application {
AtomicLong createBufferCounter = new AtomicLong(); AtomicLong createBufferCounter = new AtomicLong();
AtomicLong cycleBufferCounter = new AtomicLong(); AtomicLong cycleBufferCounter = new AtomicLong();
if (resources != null) { if (resources != null) {
AnyValue excludelibConf = resources.getAnyValue("excludelibs");
if (excludelibConf != null) excludelib0 = excludelibConf.getValue("value");
AnyValue transportConf = resources.getAnyValue("transport"); AnyValue transportConf = resources.getAnyValue("transport");
int groupsize = resources.getAnyValues("group").length; int groupsize = resources.getAnyValues("group").length;
if (groupsize > 0 && transportConf == null) transportConf = new DefaultAnyValue(); if (groupsize > 0 && transportConf == null) transportConf = new DefaultAnyValue();
@@ -314,6 +341,80 @@ public final class Application {
} }
logger.log(Level.INFO, Transport.class.getSimpleName() + " configure bufferCapacity = " + bufferCapacity / 1024 + "K; bufferPoolSize = " + bufferPoolSize + "; threads = " + threads + ";"); logger.log(Level.INFO, Transport.class.getSimpleName() + " configure bufferCapacity = " + bufferCapacity / 1024 + "K; bufferPoolSize = " + bufferPoolSize + "; threads = " + threads + ";");
} }
AnyValue clusterConf = resources.getAnyValue("cluster");
if (clusterConf != null) {
try {
String classval = clusterConf.getValue("value");
if (classval == null || classval.isEmpty()) {
Iterator<ClusterAgent> it = ServiceLoader.load(ClusterAgent.class, classLoader).iterator();
while (it.hasNext()) {
ClusterAgent agent = it.next();
if (agent.match(clusterConf)) {
cluster = agent;
cluster.setConfig(clusterConf);
break;
}
}
if (cluster == null) logger.log(Level.SEVERE, "load application cluster resource, but not found name='value' value error: " + clusterConf);
} else {
Class type = classLoader.loadClass(clusterConf.getValue("value"));
if (!ClusterAgent.class.isAssignableFrom(type)) {
logger.log(Level.SEVERE, "load application cluster resource, but not found " + ClusterAgent.class.getSimpleName() + " implements class error: " + clusterConf);
} else {
cluster = (ClusterAgent) type.getDeclaredConstructor().newInstance();
cluster.setConfig(clusterConf);
}
}
} catch (Exception e) {
logger.log(Level.SEVERE, "load application cluster resource error: " + clusterConf, e);
}
}
AnyValue[] mqConfs = resources.getAnyValues("mq");
if (mqConfs != null && mqConfs.length > 0) {
mqs = new MessageAgent[mqConfs.length];
Set<String> mqnames = new HashSet<>();
for (int i = 0; i < mqConfs.length; i++) {
AnyValue mqConf = mqConfs[0];
String mqname = mqConf.getValue("name", "");
if (mqnames.contains(mqname)) throw new RuntimeException("mq.name(" + mqname + ") is repeat");
mqnames.add(mqname);
String namex = mqConf.getValue("names");
if (namex != null && !namex.isEmpty()) {
for (String n : namex.split(";")) {
if (n.trim().isEmpty()) continue;
if (mqnames.contains(n.trim())) throw new RuntimeException("mq.name(" + n.trim() + ") is repeat");
mqnames.add(n.trim());
}
}
try {
String classval = mqConf.getValue("value");
if (classval == null || classval.isEmpty()) {
Iterator<MessageAgent> it = ServiceLoader.load(MessageAgent.class, classLoader).iterator();
while (it.hasNext()) {
MessageAgent messageAgent = it.next();
if (messageAgent.match(mqConf)) {
mqs[i] = messageAgent;
mqs[i].setConfig(mqConf);
break;
}
}
if (mqs[i] == null) logger.log(Level.SEVERE, "load application mq resource, but not found name='value' value error: " + mqConf);
} else {
Class type = classLoader.loadClass(classval);
if (!MessageAgent.class.isAssignableFrom(type)) {
logger.log(Level.SEVERE, "load application mq resource, but not found " + MessageAgent.class.getSimpleName() + " implements class error: " + mqConf);
} else {
mqs[i] = (MessageAgent) type.getDeclaredConstructor().newInstance();
mqs[i].setConfig(mqConf);
}
}
} catch (Exception e) {
logger.log(Level.SEVERE, "load application mq resource error: " + mqs[i], e);
}
}
}
} }
if (transportGroup == null) { if (transportGroup == null) {
final AtomicInteger counter = new AtomicInteger(); final AtomicInteger counter = new AtomicInteger();
@@ -338,15 +439,29 @@ public final class Application {
return true; return true;
}); });
} }
this.excludelibs = excludelib0;
this.sncpTransportFactory = TransportFactory.create(transportExec, transportPool, transportGroup, (SSLContext) null, readTimeoutSeconds, writeTimeoutSeconds, strategy); this.sncpTransportFactory = TransportFactory.create(transportExec, transportPool, transportGroup, (SSLContext) null, readTimeoutSeconds, writeTimeoutSeconds, strategy);
DefaultAnyValue tarnsportConf = DefaultAnyValue.create(TransportFactory.NAME_POOLMAXCONNS, System.getProperty("net.transport.poolmaxconns", "100")) DefaultAnyValue tarnsportConf = DefaultAnyValue.create(TransportFactory.NAME_POOLMAXCONNS, System.getProperty("net.transport.pool.maxconns", "100"))
.addValue(TransportFactory.NAME_PINGINTERVAL, System.getProperty("net.transport.pinginterval", "30")) .addValue(TransportFactory.NAME_PINGINTERVAL, System.getProperty("net.transport.ping.interval", "30"))
.addValue(TransportFactory.NAME_CHECKINTERVAL, System.getProperty("net.transport.checkinterval", "30")); .addValue(TransportFactory.NAME_CHECKINTERVAL, System.getProperty("net.transport.check.interval", "30"));
this.sncpTransportFactory.init(tarnsportConf, Sncp.PING_BUFFER, Sncp.PONG_BUFFER.remaining()); this.sncpTransportFactory.init(tarnsportConf, Sncp.PING_BUFFER, Sncp.PONG_BUFFER.remaining());
this.clusterAgent = cluster;
this.messageAgents = mqs;
Thread.currentThread().setContextClassLoader(this.classLoader); Thread.currentThread().setContextClassLoader(this.classLoader);
this.serverClassLoader = new RedkaleClassLoader(this.classLoader); this.serverClassLoader = new RedkaleClassLoader(this.classLoader);
} }
private String checkName(String name) { //不能含特殊字符
if (name.isEmpty()) return name;
if (name.charAt(0) >= '0' && name.charAt(0) <= '9') throw new RuntimeException("name only 0-9 a-z A-Z _ cannot begin 0-9");
for (char ch : name.toCharArray()) {
if (!((ch >= '0' && ch <= '9') || ch == '_' || (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z'))) { //不能含特殊字符
throw new RuntimeException("name only 0-9 a-z A-Z _ cannot begin 0-9");
}
}
return name;
}
public ResourceFactory getResourceFactory() { public ResourceFactory getResourceFactory() {
return resourceFactory; return resourceFactory;
} }
@@ -355,6 +470,22 @@ public final class Application {
return sncpTransportFactory; return sncpTransportFactory;
} }
public ClusterAgent getClusterAgent() {
return clusterAgent;
}
public MessageAgent getMessageAgent(String name) {
if (messageAgents == null) return null;
for (MessageAgent agent : messageAgents) {
if (agent.getName().equals(name)) return agent;
}
return null;
}
public MessageAgent[] getMessageAgents() {
return messageAgents;
}
public RedkaleClassLoader getClassLoader() { public RedkaleClassLoader getClassLoader() {
return classLoader; return classLoader;
} }
@@ -375,6 +506,14 @@ public final class Application {
return new ArrayList<>(cacheSources); return new ArrayList<>(cacheSources);
} }
public int getNodeid() {
return nodeid;
}
public String getName() {
return name;
}
public File getHome() { public File getHome() {
return home; return home;
} }
@@ -418,7 +557,7 @@ public final class Application {
pidstr = "APP_PID = " + pid + "\r\n"; pidstr = "APP_PID = " + pid + "\r\n";
} catch (Throwable t) { } catch (Throwable t) {
} }
logger.log(Level.INFO, pidstr + "APP_JAVA = " + System.getProperty("java.version") + "\r\n" + RESNAME_APP_ADDR + " = " + this.localAddress.getHostAddress() + "\r\n" + RESNAME_APP_HOME + " = " + homepath + "\r\n" + RESNAME_APP_CONF + " = " + confpath); logger.log(Level.INFO, pidstr + "APP_JAVA = " + System.getProperty("java.version") + "\r\n" + RESNAME_APP_NODEID + " = " + this.nodeid + "\r\n" + RESNAME_APP_ADDR + " = " + this.localAddress.getHostString() + ":" + this.localAddress.getPort() + "\r\n" + RESNAME_APP_HOME + " = " + homepath + "\r\n" + RESNAME_APP_CONF + " = " + confpath);
String lib = config.getValue("lib", "${APP_HOME}/libs/*").trim().replace("${APP_HOME}", homepath); String lib = config.getValue("lib", "${APP_HOME}/libs/*").trim().replace("${APP_HOME}", homepath);
lib = lib.isEmpty() ? confpath : (lib + ";" + confpath); lib = lib.isEmpty() ? confpath : (lib + ";" + confpath);
Server.loadLib(classLoader, logger, lib); Server.loadLib(classLoader, logger, lib);
@@ -534,6 +673,41 @@ public final class Application {
}, Application.class, ResourceFactory.class, TransportFactory.class, NodeSncpServer.class, NodeHttpServer.class, NodeWatchServer.class); }, Application.class, ResourceFactory.class, TransportFactory.class, NodeSncpServer.class, NodeHttpServer.class, NodeWatchServer.class);
//-------------------------------------------------------------------------- //--------------------------------------------------------------------------
if (this.clusterAgent != null) {
if (logger.isLoggable(Level.FINER)) logger.log(Level.FINER, "ClusterAgent initing");
long s = System.currentTimeMillis();
clusterAgent.setTransportFactory(this.sncpTransportFactory);
this.resourceFactory.inject(clusterAgent);
clusterAgent.init(clusterAgent.getConfig());
this.resourceFactory.register(ClusterAgent.class, clusterAgent);
logger.info("ClusterAgent init in " + (System.currentTimeMillis() - s) + " ms");
}
if (this.messageAgents != null) {
if (logger.isLoggable(Level.FINER)) logger.log(Level.FINER, "MessageAgent initing");
long s = System.currentTimeMillis();
for (MessageAgent agent : this.messageAgents) {
this.resourceFactory.inject(agent);
agent.init(agent.getConfig());
this.resourceFactory.register(agent.getName(), MessageAgent.class, agent);
this.resourceFactory.register(agent.getName(), HttpMessageClient.class, agent.getHttpMessageClient());
//this.resourceFactory.register(agent.getName(), SncpMessageClient.class, agent.getSncpMessageClient()); //不需要给开发者使用
}
logger.info("MessageAgent init in " + (System.currentTimeMillis() - s) + " ms");
}
//------------------------------------- 注册 DataSource --------------------------------------------------------
resourceFactory.register((ResourceFactory rf, final Object src, String resourceName, Field field, final Object attachment) -> {
try {
if (field.getAnnotation(Resource.class) == null) return;
if (clusterAgent == null) return;
HttpMessageClient messageClient = new HttpMessageClusterClient(clusterAgent);
field.set(src, messageClient);
rf.inject(messageClient, null); // 给其可能包含@Resource的字段赋值;
rf.register(resourceName, HttpMessageClient.class, messageClient);
} catch (Exception e) {
logger.log(Level.SEVERE, "[" + Thread.currentThread().getName() + "] DataSource inject error", e);
}
}, HttpMessageClient.class);
initResources(); initResources();
} }
@@ -548,7 +722,7 @@ public final class Application {
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"));
} }
TransportGroupInfo ginfo = new TransportGroupInfo(group, protocol, conf.getValue("subprotocol", ""), new LinkedHashSet<>()); TransportGroupInfo ginfo = new TransportGroupInfo(group, protocol, new LinkedHashSet<>());
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.putAddress(addr); ginfo.putAddress(addr);
@@ -602,7 +776,8 @@ public final class Application {
buffer.flip(); buffer.flip();
byte[] bytes = new byte[buffer.remaining()]; byte[] bytes = new byte[buffer.remaining()];
buffer.get(bytes); buffer.get(bytes);
if ("SHUTDOWN".equalsIgnoreCase(new String(bytes))) { final String cmd = new String(bytes);
if ("SHUTDOWN".equalsIgnoreCase(cmd)) {
try { try {
long s = System.currentTimeMillis(); long s = System.currentTimeMillis();
logger.info(application.getClass().getSimpleName() + " shutdowning"); logger.info(application.getClass().getSimpleName() + " shutdowning");
@@ -622,7 +797,7 @@ public final class Application {
buffer.flip(); buffer.flip();
channel.send(buffer, address); channel.send(buffer, address);
} }
} else if ("APIDOC".equalsIgnoreCase(new String(bytes))) { } else if ("APIDOC".equalsIgnoreCase(cmd)) {
try { try {
new ApiDocsService(application).run(); new ApiDocsService(application).run();
buffer.clear(); buffer.clear();
@@ -635,6 +810,16 @@ public final class Application {
buffer.flip(); buffer.flip();
channel.send(buffer, address); channel.send(buffer, address);
} }
} else {
long s = System.currentTimeMillis();
logger.info(application.getClass().getSimpleName() + " command " + cmd);
application.command(cmd);
buffer.clear();
buffer.put("COMMAND OK".getBytes());
buffer.flip();
channel.send(buffer, address);
long e = System.currentTimeMillis() - s;
logger.info(application.getClass().getSimpleName() + " command in " + e + " ms");
} }
} }
} catch (Exception e) { } catch (Exception e) {
@@ -645,10 +830,10 @@ public final class Application {
}.start(); }.start();
} }
private void sendCommand(String command) throws Exception { private static void sendCommand(Logger logger, int port, String command) throws Exception {
final DatagramChannel channel = DatagramChannel.open(); final DatagramChannel channel = DatagramChannel.open();
channel.configureBlocking(true); channel.configureBlocking(true);
channel.connect(new InetSocketAddress("127.0.0.1", config.getIntValue("port"))); channel.connect(new InetSocketAddress("127.0.0.1", port));
ByteBuffer buffer = ByteBuffer.allocate(128); ByteBuffer buffer = ByteBuffer.allocate(128);
buffer.put(command.getBytes()); buffer.put(command.getBytes());
buffer.flip(); buffer.flip();
@@ -661,7 +846,7 @@ public final class Application {
byte[] bytes = new byte[buffer.remaining()]; byte[] bytes = new byte[buffer.remaining()];
buffer.get(bytes); buffer.get(bytes);
channel.close(); channel.close();
logger.info("Send: " + command + ", Reply: " + new String(bytes)); if (logger != null) logger.info("Send: " + command + ", Reply: " + new String(bytes));
Thread.sleep(1000); Thread.sleep(1000);
} catch (Exception e) { } catch (Exception e) {
if (e instanceof PortUnreachableException) { if (e instanceof PortUnreachableException) {
@@ -670,7 +855,7 @@ public final class Application {
application.init(); application.init();
application.start(); application.start();
new ApiDocsService(application).run(); new ApiDocsService(application).run();
logger.info("APIDOC OK"); if (logger != null) logger.info("APIDOC OK");
return; return;
} }
} }
@@ -679,6 +864,9 @@ public final class Application {
} }
public void start() throws Exception { public void start() throws Exception {
if (!singletonrun && this.clusterAgent != null) {
this.clusterAgent.register(this);
}
final AnyValue[] entrys = config.getAnyValues("server"); final AnyValue[] entrys = config.getAnyValues("server");
CountDownLatch timecd = new CountDownLatch(entrys.length); CountDownLatch timecd = new CountDownLatch(entrys.length);
final List<AnyValue> sncps = new ArrayList<>(); final List<AnyValue> sncps = new ArrayList<>();
@@ -700,12 +888,40 @@ public final class Application {
runServers(timecd, others); runServers(timecd, others);
runServers(timecd, watchs); //必须在所有服务都启动后再启动WATCH服务 runServers(timecd, watchs); //必须在所有服务都启动后再启动WATCH服务
timecd.await(); timecd.await();
if (this.clusterAgent != null) this.clusterAgent.start();
if (this.messageAgents != null) {
if (logger.isLoggable(Level.FINER)) logger.log(Level.FINER, "MessageAgent starting");
long s = System.currentTimeMillis();
final StringBuffer sb = new StringBuffer();
Set<String> names = new HashSet<>();
for (MessageAgent agent : this.messageAgents) {
names.add(agent.getName());
Map<String, Long> map = agent.start().join();
AtomicInteger maxlen = new AtomicInteger();
map.keySet().forEach(str -> {
if (str.length() > maxlen.get()) maxlen.set(str.length());
});
new TreeMap<>(map).forEach((topic, ms) -> sb.append("MessageConsumer(topic=").append(alignString(topic, maxlen.get())).append(") init and start in ").append(ms).append(" ms\r\n")
);
}
if (sb.length() > 0) logger.info(sb.toString().trim());
logger.info("MessageAgent(names=" + JsonConvert.root().convertTo(names) + ") start in " + (System.currentTimeMillis() - s) + " ms");
}
//if (!singletonrun) signalHandle(); //if (!singletonrun) signalHandle();
//if (!singletonrun) clearPersistData(); //if (!singletonrun) clearPersistData();
logger.info(this.getClass().getSimpleName() + " started in " + (System.currentTimeMillis() - startTime) + " ms\r\n"); logger.info(this.getClass().getSimpleName() + " started in " + (System.currentTimeMillis() - startTime) + " ms\r\n");
if (!singletonrun) this.serversLatch.await(); if (!singletonrun) this.serversLatch.await();
} }
private static String alignString(String value, int maxlen) {
StringBuilder sb = new StringBuilder(maxlen);
sb.append(value);
for (int i = 0; i < maxlen - value.length(); i++) {
sb.append(' ');
}
return sb.toString();
}
// private void clearPersistData() { // private void clearPersistData() {
// File cachedir = new File(home, "cache"); // File cachedir = new File(home, "cache");
// if (!cachedir.isDirectory()) return; // if (!cachedir.isDirectory()) return;
@@ -784,13 +1000,12 @@ public final class Application {
synchronized (nodeClasses) { synchronized (nodeClasses) {
if (!inited.getAndSet(true)) { //加载自定义的协议SOCKS if (!inited.getAndSet(true)) { //加载自定义的协议SOCKS
ClassFilter profilter = new ClassFilter(classLoader, NodeProtocol.class, NodeServer.class, (Class[]) null); ClassFilter profilter = new ClassFilter(classLoader, NodeProtocol.class, NodeServer.class, (Class[]) null);
ClassFilter.Loader.load(home, serconf.getValue("excludelibs", "").split(";"), profilter); ClassFilter.Loader.load(home, ((excludelibs != null ? (excludelibs + ";") : "") + 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) {
final Class<? extends NodeServer> type = entry.getType(); final Class<? extends NodeServer> type = entry.getType();
NodeProtocol pros = type.getAnnotation(NodeProtocol.class); NodeProtocol pros = type.getAnnotation(NodeProtocol.class);
for (String p : pros.value()) { String p = pros.value().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) { if (old != null && old != type) {
@@ -801,7 +1016,6 @@ public final class Application {
} }
} }
} }
}
Class<? extends NodeServer> nodeClass = nodeClasses.get(protocol); Class<? extends NodeServer> nodeClass = nodeClasses.get(protocol);
if (nodeClass != null) server = NodeServer.create(nodeClass, Application.this, serconf); if (nodeClass != null) server = NodeServer.create(nodeClass, Application.this, serconf);
} }
@@ -832,14 +1046,14 @@ public final class Application {
public static <T extends Service> T singleton(String name, Class<T> serviceClass, Class<? extends Service>... extServiceClasses) throws Exception { public static <T extends Service> T singleton(String name, Class<T> serviceClass, Class<? extends Service>... extServiceClasses) throws Exception {
if (serviceClass == null) throw new IllegalArgumentException("serviceClass is null"); if (serviceClass == null) throw new IllegalArgumentException("serviceClass is null");
final Application application = Application.create(true); final Application application = Application.create(true);
System.setProperty("red" + "kale-singleton-serviceclass", serviceClass.getName()); System.setProperty("red" + "kale.singleton.serviceclass", serviceClass.getName());
if (extServiceClasses != null && extServiceClasses.length > 0) { if (extServiceClasses != null && extServiceClasses.length > 0) {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
for (Class clazz : extServiceClasses) { for (Class clazz : extServiceClasses) {
if (sb.length() > 0) sb.append(','); if (sb.length() > 0) sb.append(',');
sb.append(clazz.getName()); sb.append(clazz.getName());
} }
System.setProperty("red" + "kale-singleton-extserviceclasses", sb.toString()); System.setProperty("red" + "kale.singleton.extserviceclasses", sb.toString());
} }
application.init(); application.init();
application.start(); application.start();
@@ -853,6 +1067,10 @@ public final class Application {
} }
public static Application create(final boolean singleton) throws IOException { public static Application create(final boolean singleton) throws IOException {
return new Application(singleton, loadAppXml());
}
private static AnyValue loadAppXml() throws IOException {
final String home = new File(System.getProperty(RESNAME_APP_HOME, "")).getCanonicalPath().replace('\\', '/'); final String home = new File(System.getProperty(RESNAME_APP_HOME, "")).getCanonicalPath().replace('\\', '/');
System.setProperty(RESNAME_APP_HOME, home); System.setProperty(RESNAME_APP_HOME, home);
String confsubpath = System.getProperty(RESNAME_APP_CONF, "conf"); String confsubpath = System.getProperty(RESNAME_APP_CONF, "conf");
@@ -864,21 +1082,19 @@ public final class Application {
} else { } else {
appconf = new File(new File(home, confsubpath), "application.xml").toURI(); appconf = new File(new File(home, confsubpath), "application.xml").toURI();
} }
return new Application(singleton, load(appconf.toURL().openStream())); return load(appconf.toURL().openStream());
} }
public static void main(String[] args) throws Exception { public static void main(String[] args) throws Exception {
Utility.midnight(); //先初始化一下Utility Utility.midnight(); //先初始化一下Utility
Thread.currentThread().setName("Redkale-Application-Main-Thread"); Thread.currentThread().setName("Redkale-Application-Main-Thread");
//运行主程序 //运行主程序
final Application application = Application.create(false);
if (System.getProperty("CMD") != null) { if (System.getProperty("CMD") != null) {
application.sendCommand(System.getProperty("CMD")); AnyValue config = loadAppXml();
return; Application.sendCommand(null, config.getIntValue("port"), System.getProperty("CMD"));
} else if (System.getProperty("SHUTDOWN") != null) { //兼容旧接口
application.sendCommand("SHUTDOWN");
return; return;
} }
final Application application = Application.create(false);
application.init(); application.init();
application.startSelfServer(); application.startSelfServer();
try { try {
@@ -902,6 +1118,17 @@ public final class Application {
return null; return null;
} }
public void command(String cmd) {
List<NodeServer> localServers = new ArrayList<>(servers); //顺序sncps, others, watchs
localServers.stream().forEach((server) -> {
try {
server.command(cmd);
} catch (Exception t) {
logger.log(Level.WARNING, " command server(" + server.getSocketAddress() + ") error", t);
}
});
}
public void shutdown() throws Exception { public void shutdown() throws Exception {
for (ApplicationListener listener : this.listeners) { for (ApplicationListener listener : this.listeners) {
try { try {
@@ -910,8 +1137,26 @@ public final class Application {
logger.log(Level.WARNING, listener.getClass() + " preShutdown erroneous", e); logger.log(Level.WARNING, listener.getClass() + " preShutdown erroneous", e);
} }
} }
List<NodeServer> localServers = new ArrayList<>(servers); //顺序sncps, others, watchs
servers.stream().forEach((server) -> { Collections.reverse(localServers); //倒序, 必须让watchs先关闭watch包含服务发现和注销逻辑
if (this.messageAgents != null) {
Set<String> names = new HashSet<>();
if (logger.isLoggable(Level.FINER)) logger.log(Level.FINER, "MessageAgent stopping");
long s = System.currentTimeMillis();
for (MessageAgent agent : this.messageAgents) {
names.add(agent.getName());
agent.stop().join();
}
logger.info("MessageAgent(names=" + JsonConvert.root().convertTo(names) + ") stop in " + (System.currentTimeMillis() - s) + " ms");
}
if (clusterAgent != null) {
if (logger.isLoggable(Level.FINER)) logger.log(Level.FINER, "ClusterAgent destroying");
long s = System.currentTimeMillis();
clusterAgent.deregister(this);
clusterAgent.destroy(clusterAgent.getConfig());
logger.info("ClusterAgent destroy in " + (System.currentTimeMillis() - s) + " ms");
}
localServers.stream().forEach((server) -> {
try { try {
server.shutdown(); server.shutdown();
} catch (Exception t) { } catch (Exception t) {
@@ -920,7 +1165,16 @@ public final class Application {
serversLatch.countDown(); serversLatch.countDown();
} }
}); });
if (this.messageAgents != null) {
Set<String> names = new HashSet<>();
if (logger.isLoggable(Level.FINER)) logger.log(Level.FINER, "MessageAgent destroying");
long s = System.currentTimeMillis();
for (MessageAgent agent : this.messageAgents) {
names.add(agent.getName());
agent.destroy(agent.getConfig());
}
logger.info("MessageAgent(names=" + JsonConvert.root().convertTo(names) + ") destroy in " + (System.currentTimeMillis() - s) + " ms");
}
for (DataSource source : dataSources) { for (DataSource source : dataSources) {
if (source == null) continue; if (source == null) continue;
try { try {

View File

@@ -175,8 +175,9 @@ public final class ClassFilter<T> {
} }
if (!r && ors != null) { if (!r && ors != null) {
for (ClassFilter filter : ors) { for (ClassFilter filter : ors) {
if (filter.accept(property, clazzname)) { if (filter.accept(filter.conf, clazzname)) {
cf = filter; cf = filter;
property = cf.conf;
break; break;
} }
} }
@@ -189,11 +190,11 @@ public final class ClassFilter<T> {
if (property == null) { if (property == null) {
property = cf.conf; property = cf.conf;
} else if (property instanceof DefaultAnyValue) { } else if (property instanceof DefaultAnyValue) {
((DefaultAnyValue) property).addAll(cf.conf); ((DefaultAnyValue) property).addAllStringSet(cf.conf);
} else { } else {
DefaultAnyValue dav = new DefaultAnyValue(); DefaultAnyValue dav = new DefaultAnyValue();
dav.addAll(property); dav.addAllStringSet(property);
dav.addAll(cf.conf); dav.addAllStringSet(cf.conf);
property = dav; property = dav;
} }
} }
@@ -243,7 +244,7 @@ public final class ClassFilter<T> {
} }
if (!r && ors != null) { if (!r && ors != null) {
for (ClassFilter filter : ors) { for (ClassFilter filter : ors) {
if (filter.accept(property, classname)) return true; if (filter.accept(filter.conf, classname)) return true;
} }
} }
return r; return r;
@@ -407,7 +408,7 @@ public final class ClassFilter<T> {
str = str.trim(); str = str.trim();
if (str.endsWith(";")) str = str.substring(0, str.length() - 1); if (str.endsWith(";")) str = str.substring(0, str.length() - 1);
} }
if (str != null) groups.addAll(Arrays.asList(str.split(";"))); if (str != null) this.groups.addAll(Arrays.asList(str.split(";")));
this.property = property; this.property = property;
this.autoload = autoload; this.autoload = autoload;
this.expect = expect; this.expect = expect;
@@ -417,7 +418,7 @@ public final class ClassFilter<T> {
@Override @Override
public String toString() { public String toString() {
return this.getClass().getSimpleName() + "[thread=" + Thread.currentThread().getName() return this.getClass().getSimpleName() + "[thread=" + Thread.currentThread().getName()
+ ", type=" + this.type.getSimpleName() + ", name=" + name + ", groups=" + groups + "]"; + ", type=" + this.type.getSimpleName() + ", name=" + name + ", groups=" + this.groups + "]";
} }
@Override @Override
@@ -444,6 +445,10 @@ public final class ClassFilter<T> {
return property; return property;
} }
public boolean containsGroup(String group) {
return groups != null && groups.contains(group);
}
public boolean isEmptyGroups() { public boolean isEmptyGroups() {
return groups == null || groups.isEmpty(); return groups == null || groups.isEmpty();
} }
@@ -495,7 +500,7 @@ public final class ClassFilter<T> {
boolean skip = false; boolean skip = false;
for (Pattern p : excludePatterns) { for (Pattern p : excludePatterns) {
if (p.matcher(url.toString()).matches()) { if (p.matcher(url.toString()).matches()) {
skip = false; skip = true;
break; break;
} }
} }

View File

@@ -14,6 +14,8 @@ import java.util.logging.Level;
import javax.annotation.*; import javax.annotation.*;
import static org.redkale.boot.Application.RESNAME_SNCP_ADDR; import static org.redkale.boot.Application.RESNAME_SNCP_ADDR;
import org.redkale.boot.ClassFilter.FilterEntry; import org.redkale.boot.ClassFilter.FilterEntry;
import org.redkale.cluster.ClusterAgent;
import org.redkale.mq.MessageAgent;
import org.redkale.net.*; import org.redkale.net.*;
import org.redkale.net.http.*; import org.redkale.net.http.*;
import org.redkale.net.sncp.Sncp; import org.redkale.net.sncp.Sncp;
@@ -30,7 +32,7 @@ import org.redkale.watch.*;
* *
* @author zhangjx * @author zhangjx
*/ */
@NodeProtocol({"HTTP"}) @NodeProtocol("HTTP")
public class NodeHttpServer extends NodeServer { public class NodeHttpServer extends NodeServer {
protected final boolean rest; //是否加载REST服务 为true加载rest节点信息并将所有可REST化的Service生成RestServlet protected final boolean rest; //是否加载REST服务 为true加载rest节点信息并将所有可REST化的Service生成RestServlet
@@ -120,7 +122,15 @@ public class NodeHttpServer extends NodeServer {
resourceFactory.register(RESNAME_SNCP_ADDR, String.class, sncpResFactory.find(RESNAME_SNCP_ADDR, String.class)); resourceFactory.register(RESNAME_SNCP_ADDR, String.class, sncpResFactory.find(RESNAME_SNCP_ADDR, String.class));
} }
if (nodeService == null) { if (nodeService == null) {
nodeService = Sncp.createLocalService(serverClassLoader, resourceName, WebSocketNodeService.class, application.getResourceFactory(), application.getSncpTransportFactory(), (InetSocketAddress) null, (Set<String>) null, (AnyValue) null); MessageAgent messageAgent = null;
try {
Field c = src.getClass().getDeclaredField("messageAgent");
c.setAccessible(true);
messageAgent = (MessageAgent) c.get(src);
} catch (Exception ex) {
logger.log(Level.WARNING, "WebSocketServlet getMessageAgent error", ex);
}
nodeService = Sncp.createLocalService(serverClassLoader, resourceName, WebSocketNodeService.class, messageAgent, application.getResourceFactory(), application.getSncpTransportFactory(), (InetSocketAddress) null, (Set<String>) null, (AnyValue) null);
regFactory.register(resourceName, WebSocketNode.class, nodeService); regFactory.register(resourceName, WebSocketNode.class, nodeService);
} }
resourceFactory.inject(nodeService, self); resourceFactory.inject(nodeService, self);
@@ -136,7 +146,7 @@ public class NodeHttpServer extends NodeServer {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
protected void loadHttpFilter(final AnyValue filtersConf, final ClassFilter<? extends Filter> classFilter) 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 threadName = "[" + Thread.currentThread().getName() + "] "; final String localThreadName = "[" + Thread.currentThread().getName() + "] ";
List<FilterEntry<? extends Filter>> list = new ArrayList(classFilter.getFilterEntrys()); List<FilterEntry<? extends Filter>> list = new ArrayList(classFilter.getFilterEntrys());
for (FilterEntry<? extends Filter> en : list) { for (FilterEntry<? extends Filter> en : list) {
Class<HttpFilter> clazz = (Class<HttpFilter>) en.getType(); Class<HttpFilter> clazz = (Class<HttpFilter>) en.getType();
@@ -145,7 +155,7 @@ public class NodeHttpServer extends NodeServer {
resourceFactory.inject(filter, this); resourceFactory.inject(filter, this);
DefaultAnyValue filterConf = (DefaultAnyValue) en.getProperty(); DefaultAnyValue filterConf = (DefaultAnyValue) en.getProperty();
this.httpServer.addHttpFilter(filter, filterConf); this.httpServer.addHttpFilter(filter, filterConf);
if (sb != null) sb.append(threadName).append(" Load ").append(clazz.getName()).append(LINE_SEPARATOR); if (sb != null) sb.append(localThreadName).append(" Load ").append(clazz.getName()).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());
} }
@@ -158,7 +168,7 @@ public class NodeHttpServer extends NodeServer {
if (!prefix0.isEmpty() && prefix0.charAt(prefix0.length() - 1) == '/') prefix0 = prefix0.substring(0, prefix0.length() - 1); if (!prefix0.isEmpty() && prefix0.charAt(prefix0.length() - 1) == '/') prefix0 = prefix0.substring(0, prefix0.length() - 1);
if (!prefix0.isEmpty() && prefix0.charAt(0) != '/') prefix0 = '/' + prefix0; if (!prefix0.isEmpty() && prefix0.charAt(0) != '/') prefix0 = '/' + prefix0;
final String prefix = prefix0; final String prefix = prefix0;
final String threadName = "[" + Thread.currentThread().getName() + "] "; final String localThreadName = "[" + Thread.currentThread().getName() + "] ";
List<FilterEntry<? extends Servlet>> list = new ArrayList(servletFilter.getFilterEntrys()); 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());
@@ -176,7 +186,11 @@ public class NodeHttpServer extends NodeServer {
Class<HttpServlet> clazz = (Class<HttpServlet>) en.getType(); Class<HttpServlet> clazz = (Class<HttpServlet>) en.getType();
if (Modifier.isAbstract(clazz.getModifiers())) continue; if (Modifier.isAbstract(clazz.getModifiers())) continue;
WebServlet ws = clazz.getAnnotation(WebServlet.class); WebServlet ws = clazz.getAnnotation(WebServlet.class);
if (ws == null || ws.value().length == 0) continue; if (ws == null) continue;
if (ws.value().length == 0) {
logger.log(Level.INFO, "not found @WebServlet.value in " + clazz.getName());
continue;
}
final HttpServlet servlet = clazz.getDeclaredConstructor().newInstance(); final HttpServlet servlet = clazz.getDeclaredConstructor().newInstance();
resourceFactory.inject(servlet, this); resourceFactory.inject(servlet, this);
final String[] mappings = ws.value(); final String[] mappings = ws.value();
@@ -197,7 +211,7 @@ public class NodeHttpServer extends NodeServer {
if (as.getKey().length() > max) max = as.getKey().length(); if (as.getKey().length() > max) max = as.getKey().length();
} }
for (AbstractMap.SimpleEntry<String, String[]> as : ss) { for (AbstractMap.SimpleEntry<String, String[]> as : ss) {
sb.append(threadName).append(" Load ").append(as.getKey()); sb.append(localThreadName).append(" Load ").append(as.getKey());
for (int i = 0; i < max - as.getKey().length(); i++) { for (int i = 0; i < max - as.getKey().length(); i++) {
sb.append(' '); sb.append(' ');
} }
@@ -222,11 +236,18 @@ public class NodeHttpServer extends NodeServer {
String prefix0 = restConf.getValue("path", ""); 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(prefix0.length() - 1) == '/') prefix0 = prefix0.substring(0, prefix0.length() - 1);
if (!prefix0.isEmpty() && prefix0.charAt(0) != '/') prefix0 = '/' + prefix0; if (!prefix0.isEmpty() && prefix0.charAt(0) != '/') prefix0 = '/' + prefix0;
final String prefix = prefix0;
final String threadName = "[" + Thread.currentThread().getName() + "] "; final String localThreadName = "[" + 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<>();
String mqname = restConf.getValue("mq");
MessageAgent agent0 = null;
if (mqname != null) {
agent0 = application.getMessageAgent(mqname);
if (agent0 == null) throw new RuntimeException("not found " + MessageAgent.class.getSimpleName() + " config for (name=" + mqname + ")");
}
final MessageAgent messageAgent = agent0;
if (messageAgent != null) prefix0 = ""; //开启MQ时,prefix字段失效
final String prefix = prefix0;
final boolean autoload = restConf.getBoolValue("autoload", true); final boolean autoload = restConf.getBoolValue("autoload", true);
{ //加载RestService { //加载RestService
String userTypeStr = restConf.getValue("usertype"); String userTypeStr = restConf.getValue("usertype");
@@ -269,7 +290,9 @@ public class NodeHttpServer extends NodeServer {
WebServlet ws = servlet.getClass().getAnnotation(WebServlet.class); WebServlet ws = servlet.getClass().getAnnotation(WebServlet.class);
if (ws != null && !ws.repair()) prefix2 = ""; if (ws != null && !ws.repair()) prefix2 = "";
resourceFactory.inject(servlet, NodeHttpServer.this); resourceFactory.inject(servlet, NodeHttpServer.this);
//if (finest) logger.finest(threadName + " Create RestServlet(resource.name='" + name + "') = " + servlet); dynServletMap.put(service, servlet);
if (messageAgent != null) messageAgent.putService(this, service, servlet);
//if (finest) logger.finest(localThreadName + " Create RestServlet(resource.name='" + name + "') = " + servlet);
if (ss != null) { if (ss != null) {
String[] mappings = servlet.getClass().getAnnotation(WebServlet.class).value(); String[] mappings = servlet.getClass().getAnnotation(WebServlet.class).value();
for (int i = 0; i < mappings.length; i++) { for (int i = 0; i < mappings.length; i++) {
@@ -322,13 +345,13 @@ public class NodeHttpServer extends NodeServer {
return; return;
} }
restedObjects.add(stype); //避免重复创建Rest对象 restedObjects.add(stype); //避免重复创建Rest对象
HttpServlet servlet = httpServer.addRestWebSocketServlet(serverClassLoader, stype, prefix, en.getProperty()); WebSocketServlet servlet = httpServer.addRestWebSocketServlet(serverClassLoader, stype, messageAgent, prefix, en.getProperty());
if (servlet == null) return; //没有RestOnMessage方法的HttpServlet调用Rest.createRestWebSocketServlet就会返回null if (servlet == null) return; //没有RestOnMessage方法的HttpServlet调用Rest.createRestWebSocketServlet就会返回null
String prefix2 = prefix; String prefix2 = prefix;
WebServlet ws = servlet.getClass().getAnnotation(WebServlet.class); WebServlet ws = servlet.getClass().getAnnotation(WebServlet.class);
if (ws != null && !ws.repair()) prefix2 = ""; if (ws != null && !ws.repair()) prefix2 = "";
resourceFactory.inject(servlet, NodeHttpServer.this); resourceFactory.inject(servlet, NodeHttpServer.this);
if (finest) logger.finest(threadName + " " + stype.getName() + " create a RestWebSocketServlet"); if (finest) logger.finest(localThreadName + " " + stype.getName() + " create a RestWebSocketServlet");
if (ss != null) { if (ss != null) {
String[] mappings = servlet.getClass().getAnnotation(WebServlet.class).value(); String[] mappings = servlet.getClass().getAnnotation(WebServlet.class).value();
for (int i = 0; i < mappings.length; i++) { for (int i = 0; i < mappings.length; i++) {
@@ -338,6 +361,7 @@ public class NodeHttpServer extends NodeServer {
} }
} }
} }
if (messageAgent != null) this.messageAgents.put(messageAgent.getName(), messageAgent);
//输出信息 //输出信息
if (ss != null && !ss.isEmpty() && sb != null) { if (ss != null && !ss.isEmpty() && sb != null) {
ss.sort((AbstractMap.SimpleEntry<String, String[]> o1, AbstractMap.SimpleEntry<String, String[]> o2) -> o1.getKey().compareTo(o2.getKey())); ss.sort((AbstractMap.SimpleEntry<String, String[]> o1, AbstractMap.SimpleEntry<String, String[]> o2) -> o1.getKey().compareTo(o2.getKey()));
@@ -346,13 +370,30 @@ public class NodeHttpServer extends NodeServer {
if (as.getKey().length() > max) max = as.getKey().length(); if (as.getKey().length() > max) max = as.getKey().length();
} }
for (AbstractMap.SimpleEntry<String, String[]> as : ss) { for (AbstractMap.SimpleEntry<String, String[]> as : ss) {
sb.append(threadName).append(" Load ").append(as.getKey()); sb.append(localThreadName).append(" Load ").append(as.getKey());
for (int i = 0; i < max - as.getKey().length(); i++) { for (int i = 0; i < max - as.getKey().length(); i++) {
sb.append(' '); sb.append(' ');
} }
sb.append(" mapping to ").append(Arrays.toString(as.getValue())).append(LINE_SEPARATOR); sb.append(" mapping to ").append(Arrays.toString(as.getValue())).append(LINE_SEPARATOR);
} }
sb.append(threadName).append(" All HttpServlets load cost " + (System.currentTimeMillis() - starts) + " ms" + LINE_SEPARATOR); sb.append(localThreadName).append(" All HttpServlets load cost ").append(System.currentTimeMillis() - starts).append(" ms").append(LINE_SEPARATOR);
} }
} }
@Override //loadServlet执行之后调用
protected void postLoadServlets() {
final ClusterAgent cluster = application.clusterAgent;
if (cluster != null) {
NodeProtocol pros = getClass().getAnnotation(NodeProtocol.class);
String protocol = pros.value().toUpperCase();
if (!cluster.containsProtocol(protocol)) return;
if (!cluster.containsPort(server.getSocketAddress().getPort())) return;
cluster.register(this, protocol, dynServletMap.keySet(), new HashSet<>());
}
}
@Override
protected void afterClusterDeregisterOnPreDestroyServices(ClusterAgent cluster, String protocol) {
cluster.deregister(this, protocol, dynServletMap.keySet(), new HashSet<>());
}
} }

View File

@@ -20,5 +20,5 @@ import java.lang.annotation.*;
@Documented @Documented
public @interface NodeProtocol { public @interface NodeProtocol {
String[] value(); String value();
} }

View File

@@ -5,6 +5,8 @@
*/ */
package org.redkale.boot; package org.redkale.boot;
import org.redkale.cluster.ClusterAgent;
import org.redkale.mq.MessageAgent;
import org.redkale.util.RedkaleClassLoader; import org.redkale.util.RedkaleClassLoader;
import java.io.*; import java.io.*;
import java.lang.annotation.Annotation; import java.lang.annotation.Annotation;
@@ -27,7 +29,6 @@ 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.*; import org.redkale.util.*;
import org.redkale.util.AnyValue.DefaultAnyValue;
/** /**
* Server节点的初始化配置类 * Server节点的初始化配置类
@@ -67,11 +68,13 @@ public abstract class NodeServer {
private InetSocketAddress sncpAddress; private InetSocketAddress sncpAddress;
//加载Service时的处理函数 //加载Service时的处理函数
protected Consumer<Service> consumer; protected BiConsumer<MessageAgent, Service> consumer;
//server节点的配置 //server节点的配置
protected AnyValue serverConf; protected AnyValue serverConf;
protected final String threadName;
//加载server节点后的拦截器 //加载server节点后的拦截器
protected NodeInterceptor interceptor; protected NodeInterceptor interceptor;
@@ -84,11 +87,20 @@ public abstract class NodeServer {
//远程模式的Service对象集合 //远程模式的Service对象集合
protected final Set<Service> remoteServices = new LinkedHashSet<>(); protected final Set<Service> remoteServices = new LinkedHashSet<>();
protected final Map<Service, Servlet> dynServletMap = new LinkedHashMap<>();
//MessageAgent对象集合
protected final Map<String, MessageAgent> messageAgents = new HashMap<>();
//需要远程模式Service的MessageAgent对象集合
protected final Map<String, MessageAgent> sncpRemoteAgents = new HashMap<>();
private volatile int maxClassNameLength = 0; private volatile int maxClassNameLength = 0;
private volatile int maxNameLength = 0; private volatile int maxNameLength = 0;
public NodeServer(Application application, Server server) { public NodeServer(Application application, Server server) {
this.threadName = Thread.currentThread().getName();
this.application = application; this.application = application;
this.server = server; this.server = server;
this.resourceFactory = server.getResourceFactory(); this.resourceFactory = server.getResourceFactory();
@@ -111,7 +123,7 @@ public abstract class NodeServer {
this.serverConf = config == null ? AnyValue.create() : config; this.serverConf = config == null ? AnyValue.create() : config;
if (isSNCP()) { // SNCP协议 if (isSNCP()) { // SNCP协议
String host = this.serverConf.getValue("host", isWATCH() ? "127.0.0.1" : "0.0.0.0").replace("0.0.0.0", ""); String host = this.serverConf.getValue("host", isWATCH() ? "127.0.0.1" : "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.getAddress().getHostAddress() : host, this.serverConf.getIntValue("port"));
this.sncpGroup = application.sncpTransportFactory.findGroupName(this.sncpAddress); this.sncpGroup = application.sncpTransportFactory.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");
@@ -142,9 +154,9 @@ public abstract class NodeServer {
//必须要进行初始化, 构建Service时需要使用Context中的ExecutorService //必须要进行初始化, 构建Service时需要使用Context中的ExecutorService
server.init(this.serverConf); server.init(this.serverConf);
//init之后才有Executor //init之后才有Executor
resourceFactory.register(Server.RESNAME_SERVER_EXECUTOR, Executor.class, server.getExecutor()); resourceFactory.register(Server.RESNAME_SERVER_EXECUTOR, Executor.class, server.getWorkExecutor());
resourceFactory.register(Server.RESNAME_SERVER_EXECUTOR, ExecutorService.class, server.getExecutor()); resourceFactory.register(Server.RESNAME_SERVER_EXECUTOR, ExecutorService.class, server.getWorkExecutor());
resourceFactory.register(Server.RESNAME_SERVER_EXECUTOR, ThreadPoolExecutor.class, server.getExecutor()); resourceFactory.register(Server.RESNAME_SERVER_EXECUTOR, ThreadPoolExecutor.class, server.getWorkExecutor());
initResource(); //给 DataSource、CacheSource 注册依赖注入时的监听回调事件。 initResource(); //给 DataSource、CacheSource 注册依赖注入时的监听回调事件。
String interceptorClass = this.serverConf.getValue("interceptor", ""); String interceptorClass = this.serverConf.getValue("interceptor", "");
@@ -155,8 +167,8 @@ public abstract class NodeServer {
ClassFilter<Service> serviceFilter = createServiceClassFilter(); ClassFilter<Service> serviceFilter = createServiceClassFilter();
if (application.singletonrun) { //singleton模式下只加载指定的Service if (application.singletonrun) { //singleton模式下只加载指定的Service
final String ssc = System.getProperty("red" + "kale-singleton-serviceclass"); final String ssc = System.getProperty("red" + "kale.singleton.serviceclass");
final String extssc = System.getProperty("red" + "kale-singleton-extserviceclasses"); final String extssc = System.getProperty("red" + "kale.singleton.extserviceclasses");
if (ssc != null) { if (ssc != null) {
final List<String> sscList = new ArrayList<>(); final List<String> sscList = new ArrayList<>();
sscList.add(ssc); sscList.add(ssc);
@@ -172,13 +184,14 @@ public abstract class NodeServer {
ClassFilter<Servlet> servletFilter = createServletClassFilter(); ClassFilter<Servlet> servletFilter = createServletClassFilter();
ClassFilter otherFilter = createOtherClassFilter(); ClassFilter otherFilter = createOtherClassFilter();
long s = System.currentTimeMillis(); long s = System.currentTimeMillis();
ClassFilter.Loader.load(application.getHome(), serverConf.getValue("excludelibs", "").split(";"), serviceFilter, filterFilter, servletFilter, otherFilter); ClassFilter.Loader.load(application.getHome(), ((application.excludelibs != null ? (application.excludelibs + ";") : "") + serverConf.getValue("excludelibs", "")).split(";"), serviceFilter, filterFilter, servletFilter, otherFilter);
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, otherFilter); //必须在servlet之前 loadService(serviceFilter, otherFilter); //必须在servlet之前
if (!application.singletonrun) { //非singleton模式下才加载Filter、Servlet if (!application.singletonrun) { //非singleton模式下才加载Filter、Servlet
loadFilter(filterFilter, otherFilter); loadFilter(filterFilter, otherFilter);
loadServlet(servletFilter, otherFilter); loadServlet(servletFilter, otherFilter);
postLoadServlets();
} }
if (this.interceptor != null) this.resourceFactory.inject(this.interceptor); if (this.interceptor != null) this.resourceFactory.inject(this.interceptor);
} }
@@ -198,7 +211,20 @@ public abstract class NodeServer {
if (resources != null) { if (resources != null) {
for (AnyValue sourceConf : resources.getAnyValues("source")) { for (AnyValue sourceConf : resources.getAnyValues("source")) {
try { try {
Class type = serverClassLoader.loadClass(sourceConf.getValue("value")); String classval = sourceConf.getValue("value");
Class type = null;
if (classval == null || classval.isEmpty()) {
Iterator<CacheSource> it = ServiceLoader.load(CacheSource.class, serverClassLoader).iterator();
while (it.hasNext()) {
CacheSource s = it.next();
if (s.match(sourceConf)) {
type = s.getClass();
break;
}
}
} else {
type = serverClassLoader.loadClass(classval);
}
if (type == DataSource.class) { if (type == DataSource.class) {
type = DataMemorySource.class; type = DataMemorySource.class;
for (AnyValue itemConf : sourceConf.getAnyValues("property")) { for (AnyValue itemConf : sourceConf.getAnyValues("property")) {
@@ -271,9 +297,7 @@ public abstract class NodeServer {
SncpClient client = Sncp.getSncpClient(srcService); SncpClient client = Sncp.getSncpClient(srcService);
final InetSocketAddress sncpAddr = client == null ? null : client.getClientAddress(); final InetSocketAddress sncpAddr = client == null ? null : client.getClientAddress();
final Set<String> groups = new HashSet<>(); final Set<String> groups = new HashSet<>();
if (client != null && client.getSameGroup() != null) groups.add(client.getSameGroup()); source = (DataSource) Sncp.createLocalService(serverClassLoader, resourceName, sourceType, client == null ? null : client.getMessageAgent(), appResFactory, appSncpTranFactory, sncpAddr, groups, Sncp.getConf(srcService));
if (client != null && client.getDiffGroups() != null) groups.addAll(client.getDiffGroups());
source = (DataSource) Sncp.createLocalService(serverClassLoader, resourceName, sourceType, appResFactory, appSncpTranFactory, sncpAddr, groups, Sncp.getConf(srcService));
} }
} }
} }
@@ -284,20 +308,6 @@ public abstract class NodeServer {
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);
final InetSocketAddress sncpAddr = client == null ? null : client.getClientAddress();
if ((src instanceof DataSource) && sncpAddr != null && resourceFactory.find(resourceName, DataCacheListener.class) == null) { //只有DataSourceService 才能赋值 DataCacheListener
final NodeSncpServer sncpServer = application.findNodeSncpServer(sncpAddr);
final Set<String> groups = new HashSet<>();
if (client != null && client.getSameGroup() != null) groups.add(client.getSameGroup());
if (client != null && client.getDiffGroups() != null) groups.addAll(client.getDiffGroups());
Service cacheListenerService = Sncp.createLocalService(serverClassLoader, resourceName, DataCacheListenerService.class, appResFactory, appSncpTranFactory, sncpAddr, groups, Sncp.getConf((Service) src));
appResFactory.register(resourceName, DataCacheListener.class, cacheListenerService);
localServices.add(cacheListenerService);
sncpServer.consumerAccept(cacheListenerService);
rf.inject(cacheListenerService, self);
logger.info("[" + Thread.currentThread().getName() + "] Load Service " + cacheListenerService);
}
field.set(src, source); field.set(src, source);
rf.inject(source, self); // 给其可能包含@Resource的字段赋值; rf.inject(source, self); // 给其可能包含@Resource的字段赋值;
//NodeServer.this.watchFactory.inject(src); //NodeServer.this.watchFactory.inject(src);
@@ -318,20 +328,32 @@ public abstract class NodeServer {
final Service srcService = (Service) src; final Service srcService = (Service) src;
SncpClient client = Sncp.getSncpClient(srcService); SncpClient client = Sncp.getSncpClient(srcService);
final InetSocketAddress sncpAddr = client == null ? null : client.getClientAddress(); final InetSocketAddress sncpAddr = client == null ? null : client.getClientAddress();
final Set<String> groups = new HashSet<>();
if (client != null && client.getSameGroup() != null) groups.add(client.getSameGroup());
if (client != null && client.getDiffGroups() != null) groups.addAll(client.getDiffGroups());
SimpleEntry<Class, AnyValue> resEntry = cacheResource.get(resourceName); SimpleEntry<Class, AnyValue> resEntry = cacheResource.get(resourceName);
AnyValue sourceConf = resEntry == null ? null : resEntry.getValue(); AnyValue sourceConf = resEntry == null ? null : resEntry.getValue();
if (sourceConf == null) { if (sourceConf == null) {
SimpleEntry<Class, AnyValue> resEntry2 = dataResources.get(resourceName); SimpleEntry<Class, AnyValue> resEntry2 = dataResources.get(resourceName);
sourceConf = resEntry2 == null ? null : resEntry2.getValue(); sourceConf = resEntry2 == null ? null : resEntry2.getValue();
} }
final Class sourceType = sourceConf == null ? CacheMemorySource.class : serverClassLoader.loadClass(sourceConf.getValue("value")); Class sourceType0 = CacheMemorySource.class;
if (sourceConf != null) {
String classval = sourceConf.getValue("value");
if (classval == null || classval.isEmpty()) {
Iterator<CacheSource> it = ServiceLoader.load(CacheSource.class, serverClassLoader).iterator();
while (it.hasNext()) {
CacheSource s = it.next();
if (s.match(sourceConf)) {
sourceType0 = s.getClass();
break;
}
}
} else {
sourceType0 = serverClassLoader.loadClass(classval);
}
}
final Class sourceType = sourceType0;
Object source = null; Object source = null;
if (CacheSource.class.isAssignableFrom(sourceType)) { // CacheSource if (CacheSource.class.isAssignableFrom(sourceType)) { // CacheSource
source = (CacheSource) Sncp.createLocalService(serverClassLoader, resourceName, sourceType, appResFactory, appSncpTranFactory, sncpAddr, groups, Sncp.getConf(srcService)); source = Modifier.isFinal(sourceType.getModifiers()) ? sourceType.getConstructor().newInstance() : (CacheSource) Sncp.createLocalService(serverClassLoader, resourceName, sourceType, client == null ? null : client.getMessageAgent(), appResFactory, appSncpTranFactory, sncpAddr, null, Sncp.getConf(srcService));
Type genericType = field.getGenericType(); Type genericType = field.getGenericType();
ParameterizedType pt = (genericType instanceof ParameterizedType) ? (ParameterizedType) genericType : null; ParameterizedType pt = (genericType instanceof ParameterizedType) ? (ParameterizedType) genericType : null;
Type valType = pt == null ? null : pt.getActualTypeArguments()[0]; Type valType = pt == null ? null : pt.getActualTypeArguments()[0];
@@ -376,18 +398,21 @@ public abstract class NodeServer {
if (nodeService == null) { if (nodeService == null) {
final HashSet<String> groups = new HashSet<>(); final HashSet<String> groups = new HashSet<>();
if (groups.isEmpty() && isSNCP() && NodeServer.this.sncpGroup != null) groups.add(NodeServer.this.sncpGroup); if (groups.isEmpty() && isSNCP() && NodeServer.this.sncpGroup != null) groups.add(NodeServer.this.sncpGroup);
nodeService = Sncp.createLocalService(serverClassLoader, resourceName, WebSocketNodeService.class, application.getResourceFactory(), application.getSncpTransportFactory(), NodeServer.this.sncpAddress, groups, (AnyValue) null); nodeService = Sncp.createLocalService(serverClassLoader, resourceName, WebSocketNodeService.class, Sncp.getMessageAgent((Service) src), application.getResourceFactory(), application.getSncpTransportFactory(), NodeServer.this.sncpAddress, groups, (AnyValue) null);
(isSNCP() ? appResFactory : resourceFactory).register(resourceName, WebSocketNode.class, nodeService); (isSNCP() ? appResFactory : resourceFactory).register(resourceName, WebSocketNode.class, nodeService);
((WebSocketNodeService) nodeService).setName(resourceName);
} }
resourceFactory.inject(nodeService, self); resourceFactory.inject(nodeService, self);
MessageAgent messageAgent = Sncp.getMessageAgent((Service) src);
if (messageAgent != null && Sncp.getMessageAgent(nodeService) == null) Sncp.setMessageAgent(nodeService, messageAgent);
field.set(src, nodeService); field.set(src, nodeService);
if (Sncp.isRemote(nodeService)) { if (Sncp.isRemote(nodeService)) {
remoteServices.add(nodeService); remoteServices.add(nodeService);
} else { } else {
if (field != null) rf.inject(nodeService); //动态加载的Service也存在按需加载的注入资源 rf.inject(nodeService); //动态加载的Service也存在按需加载的注入资源
localServices.add(nodeService); localServices.add(nodeService);
interceptorServices.add(nodeService); interceptorServices.add(nodeService);
if (consumer != null) consumer.accept(nodeService); if (consumer != null) consumer.accept(null, nodeService);
} }
} catch (Exception e) { } catch (Exception e) {
logger.log(Level.SEVERE, "WebSocketNode inject error", e); logger.log(Level.SEVERE, "WebSocketNode inject error", e);
@@ -405,7 +430,7 @@ public abstract class NodeServer {
protected void loadService(ClassFilter<? extends Service> serviceFilter, ClassFilter otherFilter) throws Exception { protected void loadService(ClassFilter<? extends Service> serviceFilter, ClassFilter otherFilter) throws Exception {
if (serviceFilter == null) return; if (serviceFilter == null) return;
final long starts = System.currentTimeMillis(); final long starts = System.currentTimeMillis();
final String threadName = "[" + Thread.currentThread().getName() + "] "; final String localThreadName = "[" + Thread.currentThread().getName() + "] ";
final Set<FilterEntry<? extends Service>> entrys = (Set) 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(); final ResourceFactory appResourceFactory = application.getResourceFactory();
@@ -418,12 +443,11 @@ public abstract class NodeServer {
if (Modifier.isAbstract(serviceImplClass.getModifiers())) continue; //修饰abstract的类跳过 if (Modifier.isAbstract(serviceImplClass.getModifiers())) continue; //修饰abstract的类跳过
if (DataSource.class.isAssignableFrom(serviceImplClass)) continue; if (DataSource.class.isAssignableFrom(serviceImplClass)) continue;
if (CacheSource.class.isAssignableFrom(serviceImplClass)) continue; if (CacheSource.class.isAssignableFrom(serviceImplClass)) continue;
if (DataCacheListener.class.isAssignableFrom(serviceImplClass)) continue;
} }
if (entry.getName().contains("$")) throw new RuntimeException("<name> value cannot contains '$' in " + entry.getProperty()); if (entry.getName().contains("$")) throw new RuntimeException("<name> value cannot contains '$' in " + entry.getProperty());
Service oldother = resourceFactory.find(entry.getName(), serviceImplClass); Service oldother = resourceFactory.find(entry.getName(), serviceImplClass);
if (oldother != null) { //Server加载Service时需要判断是否已经加载过了。 if (oldother != null) { //Server加载Service时需要判断是否已经加载过了。
interceptorServices.add(oldother); if (!Sncp.isRemote(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属性。
@@ -437,16 +461,28 @@ public abstract class NodeServer {
final ResourceFactory.ResourceLoader resourceLoader = (ResourceFactory rf, final Object src, final String resourceName, Field field, final Object attachment) -> { final ResourceFactory.ResourceLoader resourceLoader = (ResourceFactory rf, final Object src, final String resourceName, Field field, final Object attachment) -> {
try { try {
if (SncpClient.parseMethod(serviceImplClass).isEmpty() && serviceImplClass.getAnnotation(Priority.class) == null) { //class没有可用的方法且没有标记启动优先级的 通常为BaseService if (SncpClient.parseMethod(serviceImplClass).isEmpty() && serviceImplClass.getAnnotation(Priority.class) == null) { //class没有可用的方法且没有标记启动优先级的 通常为BaseService
if (!serviceImplClass.getName().startsWith("org.redkale.")) logger.log(Level.FINE, serviceImplClass + " cannot load because not found less one public non-final method"); if (!serviceImplClass.getName().startsWith("org.redkale.") && !serviceImplClass.getSimpleName().contains("Base")) {
logger.log(Level.FINE, serviceImplClass + " cannot load because not found less one public non-final method");
}
return; return;
} }
MessageAgent agent = null;
if (entry.getProperty() != null && entry.getProperty().getValue("mq") != null) {
agent = application.getMessageAgent(entry.getProperty().getValue("mq"));
if (agent != null) messageAgents.put(agent.getName(), agent);
}
Service service; Service service;
boolean ws = src instanceof WebSocketServlet; final boolean ws = src instanceof WebSocketServlet;
if (ws || localed) { //本地模式 if (ws || localed) { //本地模式
service = Sncp.createLocalService(serverClassLoader, resourceName, serviceImplClass, appResourceFactory, appSncpTransFactory, NodeServer.this.sncpAddress, groups, entry.getProperty()); service = Sncp.createLocalService(serverClassLoader, resourceName, serviceImplClass, agent, appResourceFactory, appSncpTransFactory, NodeServer.this.sncpAddress, groups, entry.getProperty());
} else { } else {
service = Sncp.createRemoteService(serverClassLoader, resourceName, serviceImplClass, appSncpTransFactory, NodeServer.this.sncpAddress, groups, entry.getProperty()); service = Sncp.createRemoteService(serverClassLoader, resourceName, serviceImplClass, agent, appSncpTransFactory, NodeServer.this.sncpAddress, groups, entry.getProperty());
}
if (service instanceof WebSocketNodeService) {
((WebSocketNodeService) service).setName(resourceName);
if (agent != null) Sncp.setMessageAgent(service, agent);
} }
final Class restype = Sncp.getResourceType(service); final Class restype = Sncp.getResourceType(service);
if (rf.find(resourceName, restype) == null) { if (rf.find(resourceName, restype) == null) {
@@ -456,11 +492,12 @@ public abstract class NodeServer {
} }
if (Sncp.isRemote(service)) { if (Sncp.isRemote(service)) {
remoteServices.add(service); remoteServices.add(service);
if (agent != null) sncpRemoteAgents.put(agent.getName(), agent);
} else { } else {
if (field != null) rf.inject(service); //动态加载的Service也存在按需加载的注入资源 if (field != null) rf.inject(service); //动态加载的Service也存在按需加载的注入资源
localServices.add(service); localServices.add(service);
interceptorServices.add(service); interceptorServices.add(service);
if (consumer != null) consumer.accept(service); if (consumer != null) consumer.accept(agent, service);
} }
} catch (RuntimeException ex) { } catch (RuntimeException ex) {
throw ex; throw ex;
@@ -493,7 +530,13 @@ public abstract class NodeServer {
if (sb != null) { if (sb != null) {
remoteServices.forEach(y -> { remoteServices.forEach(y -> {
sb.append(threadName).append(Sncp.toSimpleString(y, maxNameLength, maxClassNameLength)).append(" load and inject").append(LINE_SEPARATOR); sb.append(localThreadName).append(Sncp.toSimpleString(y, maxNameLength, maxClassNameLength)).append(" load and inject").append(LINE_SEPARATOR);
});
}
if (isSNCP() && !sncpRemoteAgents.isEmpty()) {
sncpRemoteAgents.values().forEach(agent -> {
// agent.putSncpResp((NodeSncpServer) this);
// agent.startSncpRespConsumer();
}); });
} }
//----------------- init ----------------- //----------------- init -----------------
@@ -510,21 +553,25 @@ public abstract class NodeServer {
localServices.clear(); localServices.clear();
localServices.addAll(swlist); localServices.addAll(swlist);
//this.loadPersistData(); //this.loadPersistData();
long preinits = System.currentTimeMillis();
preInitServices(localServices, remoteServices);
long preinite = System.currentTimeMillis() - preinits;
final List<String> slist = sb == null ? null : new CopyOnWriteArrayList<>(); final List<String> slist = sb == null ? null : new CopyOnWriteArrayList<>();
localServices.stream().forEach(y -> { localServices.stream().forEach(y -> {
long s = System.currentTimeMillis(); long s = System.currentTimeMillis();
y.init(Sncp.getConf(y)); y.init(Sncp.getConf(y));
long e = System.currentTimeMillis() - s; long e = System.currentTimeMillis() - s;
String serstr = Sncp.toSimpleString(y, maxNameLength, maxClassNameLength); 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()); if (slist != null) slist.add(new StringBuilder().append(localThreadName).append(serstr).append(" load and init in ").append(e).append(" ms").append(LINE_SEPARATOR).toString());
}); });
if (slist != null && sb != null) { if (slist != null && sb != null) {
List<String> wlist = new ArrayList<>(slist); //直接使用CopyOnWriteArrayList偶尔会出现莫名的异常(CopyOnWriteArrayList源码1185行) List<String> wlist = new ArrayList<>(slist); //直接使用CopyOnWriteArrayList偶尔会出现莫名的异常(CopyOnWriteArrayList源码1185行)
for (String s : wlist) { for (String s : wlist) {
sb.append(s); sb.append(s);
} }
sb.append(threadName).append("All Services load cost " + (System.currentTimeMillis() - starts) + " ms" + LINE_SEPARATOR); sb.append(localThreadName).append("All Services load cost ").append(System.currentTimeMillis() - starts).append(" ms" + LINE_SEPARATOR);
} }
if (sb != null && preinite > 10) sb.append(localThreadName).append(ClusterAgent.class.getSimpleName()).append(" register ").append(preinite).append(" ms" + 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());
} }
@@ -533,6 +580,44 @@ public abstract class NodeServer {
maxClassNameLength = Math.max(maxClassNameLength, Sncp.getResourceType(y).getName().length() + 1); maxClassNameLength = Math.max(maxClassNameLength, Sncp.getResourceType(y).getName().length() + 1);
} }
//Service.init执行之前调用
protected void preInitServices(Set<Service> localServices, Set<Service> remoteServices) {
final ClusterAgent cluster = application.clusterAgent;
if (cluster == null) return;
NodeProtocol pros = getClass().getAnnotation(NodeProtocol.class);
String protocol = pros.value().toUpperCase();
if (!cluster.containsProtocol(protocol)) return;
if (!cluster.containsPort(server.getSocketAddress().getPort())) return;
cluster.register(this, protocol, localServices, remoteServices);
}
//loadServlet执行之后调用
protected void postLoadServlets() {
}
//Service.destroy执行之前调用
protected void preDestroyServices(Set<Service> localServices, Set<Service> remoteServices) {
if (application.clusterAgent != null) { //服务注销
final ClusterAgent cluster = application.clusterAgent;
NodeProtocol pros = getClass().getAnnotation(NodeProtocol.class);
String protocol = pros.value().toUpperCase();
if (cluster.containsProtocol(protocol) && cluster.containsPort(server.getSocketAddress().getPort())) {
cluster.deregister(this, protocol, localServices, remoteServices);
afterClusterDeregisterOnPreDestroyServices(cluster, protocol);
}
}
if (!this.messageAgents.isEmpty()) { //MQ
}
}
protected void afterClusterDeregisterOnPreDestroyServices(ClusterAgent cluster, String protocol) {
}
//Server.start执行之后调用
protected void postStartServer(Set<Service> localServices, Set<Service> remoteServices) {
}
protected abstract ClassFilter<Filter> createFilterClassFilter(); protected abstract ClassFilter<Filter> createFilterClassFilter();
protected abstract ClassFilter<Servlet> createServletClassFilter(); protected abstract ClassFilter<Servlet> createServletClassFilter();
@@ -563,22 +648,24 @@ public abstract class NodeServer {
} }
cf = null; cf = null;
for (AnyValue list : proplist) { for (AnyValue list : proplist) {
DefaultAnyValue prop = null; AnyValue.DefaultAnyValue prop = null;
String sc = list.getValue("groups"); String sc = list.getValue("groups");
String mq = list.getValue("mq");
if (sc != null) { if (sc != null) {
sc = sc.trim(); sc = sc.trim();
if (sc.endsWith(";")) sc = sc.substring(0, sc.length() - 1); if (sc.endsWith(";")) sc = sc.substring(0, sc.length() - 1);
} }
if (sc == null) sc = localGroup; if (sc == null) sc = localGroup;
if (sc != null) { if (sc != null || mq != null) {
prop = new AnyValue.DefaultAnyValue(); prop = new AnyValue.DefaultAnyValue();
prop.addValue("groups", sc); if (sc != null) prop.addValue("groups", sc);
if (mq != null) prop.addValue("mq", mq);
} }
ClassFilter filter = new ClassFilter(this.serverClassLoader, ref, inter, excludeSuperClasses, prop); ClassFilter filter = new ClassFilter(this.serverClassLoader, ref, inter, excludeSuperClasses, prop);
for (AnyValue av : list.getAnyValues(property)) { // <service>、<filter>、<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 AnyValue.DefaultAnyValue && items.length > 0) { //存在 <property>节点
DefaultAnyValue dav = DefaultAnyValue.create(); AnyValue.DefaultAnyValue dav = AnyValue.DefaultAnyValue.create();
final AnyValue.Entry<String>[] strings = av.getStringEntrys(); final AnyValue.Entry<String>[] strings = av.getStringEntrys();
if (strings != null) { //将<service>、<filter>、<servlet>节点的属性值传给dav if (strings != null) { //将<service>、<filter>、<servlet>节点的属性值传给dav
for (AnyValue.Entry<String> en : strings) { for (AnyValue.Entry<String> en : strings) {
@@ -591,7 +678,7 @@ public abstract class NodeServer {
if (!"property".equals(en.name)) dav.addValue(en.name, en.getValue()); if (!"property".equals(en.name)) dav.addValue(en.name, en.getValue());
} }
} }
DefaultAnyValue ps = DefaultAnyValue.create(); AnyValue.DefaultAnyValue ps = AnyValue.DefaultAnyValue.create();
for (AnyValue item : items) { for (AnyValue item : items) {
ps.addValue(item.getValue("name"), item.getValue("value")); ps.addValue(item.getValue("name"), item.getValue("value"));
} }
@@ -627,6 +714,10 @@ public abstract class NodeServer {
return false; return false;
} }
public Application getApplication() {
return application;
}
public ResourceFactory getResourceFactory() { public ResourceFactory getResourceFactory() {
return resourceFactory; return resourceFactory;
} }
@@ -660,12 +751,14 @@ public abstract class NodeServer {
public void start() throws IOException { public void start() throws IOException {
if (interceptor != null) interceptor.preStart(this); if (interceptor != null) interceptor.preStart(this);
server.start(); server.start();
postStartServer(localServices, remoteServices);
} }
public void shutdown() throws IOException { public void shutdown() throws IOException {
if (interceptor != null) interceptor.preShutdown(this); if (interceptor != null) interceptor.preShutdown(this);
final StringBuilder sb = logger.isLoggable(Level.INFO) ? new StringBuilder() : null; final StringBuilder sb = logger.isLoggable(Level.INFO) ? new StringBuilder() : null;
final boolean finest = logger.isLoggable(Level.FINEST); final boolean finest = logger.isLoggable(Level.FINEST);
preDestroyServices(localServices, remoteServices);
localServices.forEach(y -> { localServices.forEach(y -> {
long s = System.currentTimeMillis(); long s = System.currentTimeMillis();
if (finest) logger.finest(y + " is destroying"); if (finest) logger.finest(y + " is destroying");
@@ -680,6 +773,42 @@ public abstract class NodeServer {
server.shutdown(); server.shutdown();
} }
public void command(String cmd) throws IOException {
final StringBuilder sb = logger.isLoggable(Level.INFO) ? new StringBuilder() : null;
final boolean finest = logger.isLoggable(Level.FINEST);
localServices.forEach(y -> {
Set<Method> methods = new HashSet<>();
Class loop = y.getClass();
//do { public方法不用递归
for (Method m : loop.getMethods()) {
Command c = m.getAnnotation(Command.class);
if (c == null) continue;
if (Modifier.isStatic(m.getModifiers())) continue;
if (m.getReturnType() != void.class) continue;
if (m.getParameterCount() != 1) continue;
if (m.getParameterTypes()[0] != String.class) continue;
methods.add(m);
}
//} while ((loop = loop.getSuperclass()) != Object.class);
if (methods.isEmpty()) return;
long s = System.currentTimeMillis();
Method one = null;
try {
for (Method method : methods) {
one = method;
method.invoke(y, cmd);
}
} catch (Exception ex) {
logger.log(Level.SEVERE, one + " run error, cmd = " + cmd, ex);
}
long e = System.currentTimeMillis() - s;
if (e > 10 && sb != null) {
sb.append(Sncp.toSimpleString(y, maxNameLength, maxClassNameLength)).append(" command (").append(cmd).append(") ").append(e).append("ms").append(LINE_SEPARATOR);
}
});
if (sb != null && sb.length() > 0) logger.log(Level.INFO, sb.toString());
}
public <T extends Server> T getServer() { public <T extends Server> T getServer() {
return (T) server; return (T) server;
} }
@@ -696,4 +825,7 @@ public abstract class NodeServer {
return new LinkedHashSet<>(remoteServices); return new LinkedHashSet<>(remoteServices);
} }
public String getThreadName() {
return this.threadName;
}
} }

View File

@@ -10,9 +10,10 @@ import java.net.*;
import java.util.*; import java.util.*;
import java.util.logging.Level; import java.util.logging.Level;
import org.redkale.boot.ClassFilter.FilterEntry; import org.redkale.boot.ClassFilter.FilterEntry;
import org.redkale.mq.MessageAgent;
import org.redkale.net.*; import org.redkale.net.*;
import org.redkale.net.sncp.*; import org.redkale.net.sncp.*;
import org.redkale.service.Service; import org.redkale.service.*;
import org.redkale.util.*; import org.redkale.util.*;
import org.redkale.util.AnyValue.DefaultAnyValue; import org.redkale.util.AnyValue.DefaultAnyValue;
@@ -24,7 +25,7 @@ import org.redkale.util.AnyValue.DefaultAnyValue;
* *
* @author zhangjx * @author zhangjx
*/ */
@NodeProtocol({"SNCP"}) @NodeProtocol("SNCP")
public class NodeSncpServer extends NodeServer { public class NodeSncpServer extends NodeServer {
protected final SncpServer sncpServer; protected final SncpServer sncpServer;
@@ -32,14 +33,15 @@ public class NodeSncpServer extends NodeServer {
private NodeSncpServer(Application application, AnyValue serconf) { private NodeSncpServer(Application application, AnyValue serconf) {
super(application, createServer(application, serconf)); super(application, createServer(application, serconf));
this.sncpServer = (SncpServer) this.server; this.sncpServer = (SncpServer) this.server;
this.consumer = sncpServer == null || application.singletonrun ? null : x -> sncpServer.addSncpServlet(x); //singleton模式下不生成SncpServlet this.consumer = sncpServer == null || application.singletonrun ? null : (agent, x) -> {//singleton模式下不生成SncpServlet
if (x.getClass().getAnnotation(Local.class) != null) return;
SncpDynServlet servlet = sncpServer.addSncpServlet(x);
dynServletMap.put(x, servlet);
if (agent != null) agent.putService(this, x, servlet);
};
} }
public static NodeServer createNodeServer(Application application, AnyValue serconf) { public static NodeServer createNodeServer(Application application, AnyValue serconf) {
if (serconf != null && serconf.getAnyValue("rest") != null) {
((AnyValue.DefaultAnyValue) serconf).addValue("_$sncp", "true");
return new NodeHttpServer(application, serconf);
}
return new NodeSncpServer(application, serconf); return new NodeSncpServer(application, serconf);
} }
@@ -52,8 +54,8 @@ public class NodeSncpServer extends NodeServer {
return sncpServer == null ? null : sncpServer.getSocketAddress(); return sncpServer == null ? null : sncpServer.getSocketAddress();
} }
public void consumerAccept(Service service) { public void consumerAccept(MessageAgent messageAgent, Service service) {
if (this.consumer != null) this.consumer.accept(service); if (this.consumer != null) this.consumer.accept(messageAgent, service);
} }
@Override @Override
@@ -62,11 +64,11 @@ public class NodeSncpServer extends NodeServer {
//------------------------------------------------------------------- //-------------------------------------------------------------------
if (sncpServer == null) return; //调试时server才可能为null if (sncpServer == null) return; //调试时server才可能为null
final StringBuilder sb = logger.isLoggable(Level.FINE) ? new StringBuilder() : null; final StringBuilder sb = logger.isLoggable(Level.FINE) ? new StringBuilder() : null;
final String threadName = "[" + Thread.currentThread().getName() + "] "; final String localThreadName = "[" + Thread.currentThread().getName() + "] ";
List<SncpServlet> servlets = sncpServer.getSncpServlets(); List<SncpServlet> servlets = sncpServer.getSncpServlets();
Collections.sort(servlets); Collections.sort(servlets);
for (SncpServlet en : servlets) { for (SncpServlet en : servlets) {
if (sb != null) sb.append(threadName).append(" Load ").append(en).append(LINE_SEPARATOR); if (sb != null) sb.append(localThreadName).append(" Load ").append(en).append(LINE_SEPARATOR);
} }
if (sb != null && sb.length() > 0) logger.log(Level.FINE, sb.toString()); if (sb != null && sb.length() > 0) logger.log(Level.FINE, sb.toString());
} }
@@ -88,7 +90,7 @@ public class NodeSncpServer extends NodeServer {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
protected void loadSncpFilter(final AnyValue servletsConf, final ClassFilter<? extends Filter> classFilter) throws Exception { protected void loadSncpFilter(final AnyValue servletsConf, 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 threadName = "[" + Thread.currentThread().getName() + "] "; final String localThreadName = "[" + Thread.currentThread().getName() + "] ";
List<FilterEntry<? extends Filter>> list = new ArrayList(classFilter.getFilterEntrys()); List<FilterEntry<? extends Filter>> list = new ArrayList(classFilter.getFilterEntrys());
for (FilterEntry<? extends Filter> en : list) { for (FilterEntry<? extends Filter> en : list) {
Class<SncpFilter> clazz = (Class<SncpFilter>) en.getType(); Class<SncpFilter> clazz = (Class<SncpFilter>) en.getType();
@@ -97,7 +99,7 @@ public class NodeSncpServer extends NodeServer {
resourceFactory.inject(filter, this); resourceFactory.inject(filter, this);
DefaultAnyValue filterConf = (DefaultAnyValue) en.getProperty(); DefaultAnyValue filterConf = (DefaultAnyValue) en.getProperty();
this.sncpServer.addSncpFilter(filter, filterConf); this.sncpServer.addSncpFilter(filter, filterConf);
if (sb != null) sb.append(threadName).append(" Load ").append(clazz.getName()).append(LINE_SEPARATOR); if (sb != null) sb.append(localThreadName).append(" Load ").append(clazz.getName()).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());
} }

View File

@@ -16,7 +16,7 @@ import org.redkale.watch.*;
* *
* @author zhangjx * @author zhangjx
*/ */
@NodeProtocol({"WATCH"}) @NodeProtocol("WATCH")
public class NodeWatchServer extends NodeHttpServer { public class NodeWatchServer extends NodeHttpServer {
public NodeWatchServer(Application application, AnyValue serconf) { public NodeWatchServer(Application application, AnyValue serconf) {

View File

@@ -81,7 +81,7 @@ public class ServerWatchService extends AbstractWatchService {
protocol += "/HTTP"; protocol += "/HTTP";
} else { } else {
NodeProtocol np = node.getClass().getAnnotation(NodeProtocol.class); NodeProtocol np = node.getClass().getAnnotation(NodeProtocol.class);
if (np != null && np.value().length > 0) protocol += "/" + np.value()[0]; protocol += "/" + np.value();
} }
rs.put("name", server.getName()); rs.put("name", server.getName());
rs.put("protocol", protocol); rs.put("protocol", protocol);

View File

@@ -72,15 +72,6 @@ public class TransportWatchService extends AbstractWatchService {
if (client.getRemoteGroups() != null && client.getRemoteGroups().contains(group)) { if (client.getRemoteGroups() != null && client.getRemoteGroups().contains(group)) {
client.getRemoteGroupTransport().addRemoteAddresses(address); client.getRemoteGroupTransport().addRemoteAddresses(address);
} }
} else {
if (group.equals(client.getSameGroup())) {
client.getSameGroupTransport().addRemoteAddresses(address);
}
if (client.getDiffGroups() != null && client.getDiffGroups().contains(group)) {
for (Transport transport : client.getDiffGroupTransports()) {
transport.addRemoteAddresses(address);
}
}
} }
} }
DefaultAnyValue node = DefaultAnyValue.create("addr", addr).addValue("port", port); DefaultAnyValue node = DefaultAnyValue.create("addr", addr).addValue("port", port);
@@ -114,15 +105,6 @@ public class TransportWatchService extends AbstractWatchService {
if (client.getRemoteGroups() != null && client.getRemoteGroups().contains(group)) { if (client.getRemoteGroups() != null && client.getRemoteGroups().contains(group)) {
client.getRemoteGroupTransport().removeRemoteAddresses(address); client.getRemoteGroupTransport().removeRemoteAddresses(address);
} }
} else {
if (group.equals(client.getSameGroup())) {
client.getSameGroupTransport().removeRemoteAddresses(address);
}
if (client.getDiffGroups() != null && client.getDiffGroups().contains(group)) {
for (Transport transport : client.getDiffGroupTransports()) {
transport.removeRemoteAddresses(address);
}
}
} }
} }
for (AnyValue groupconf : application.getAppConfig().getAnyValue("resources").getAnyValues("group")) { for (AnyValue groupconf : application.getAppConfig().getAnyValue("resources").getAnyValues("group")) {

View File

@@ -0,0 +1,328 @@
/*
* 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.cluster;
import java.lang.ref.WeakReference;
import java.net.InetSocketAddress;
import java.util.*;
import java.util.concurrent.*;
import java.util.logging.Logger;
import javax.annotation.Resource;
import org.redkale.boot.*;
import static org.redkale.boot.Application.*;
import org.redkale.convert.json.JsonConvert;
import org.redkale.mq.MessageMultiConsumer;
import org.redkale.net.*;
import org.redkale.net.http.*;
import org.redkale.net.sncp.*;
import org.redkale.service.*;
import org.redkale.util.*;
/**
* 第三方服务发现管理接口cluster
*
*
* 详情见: https://redkale.org
*
* @author zhangjx
*
* @since 2.1.0
*/
public abstract class ClusterAgent {
protected final Logger logger = Logger.getLogger(this.getClass().getSimpleName());
@Resource(name = RESNAME_APP_NODEID)
protected int nodeid;
@Resource(name = RESNAME_APP_NAME)
protected String appName = "";
@Resource(name = RESNAME_APP_ADDR)
protected InetSocketAddress appAddress;
protected String name;
protected boolean waits;
protected String[] protocols; //必须全大写
protected int[] ports;
protected AnyValue config;
protected TransportFactory transportFactory;
protected final ConcurrentHashMap<String, ClusterEntry> localEntrys = new ConcurrentHashMap<>();
protected final ConcurrentHashMap<String, ClusterEntry> remoteEntrys = new ConcurrentHashMap<>();
public void init(AnyValue config) {
this.config = config;
this.name = config.getValue("name", "");
this.waits = config.getBoolValue("waits", false);
{
String ps = config.getValue("protocols", "").toUpperCase();
if (ps == null || ps.isEmpty()) ps = "SNCP;HTTP";
this.protocols = ps.split(";");
}
String ts = config.getValue("ports", "");
if (ts != null && !ts.isEmpty()) {
String[] its = ts.split(";");
List<Integer> list = new ArrayList<>();
for (String str : its) {
if (str.trim().isEmpty()) continue;
list.add(Integer.parseInt(str.trim()));
}
if (!list.isEmpty()) this.ports = list.stream().mapToInt(x -> x).toArray();
}
}
public void destroy(AnyValue config) {
}
//ServiceLoader时判断配置是否符合当前实现类
public abstract boolean match(AnyValue config);
public boolean containsProtocol(String protocol) {
if (protocol == null || protocol.isEmpty()) return false;
return protocols == null || Utility.contains(protocols, protocol.toUpperCase());
}
public boolean containsPort(int port) {
if (ports == null || ports.length == 0) return true;
return Utility.contains(ports, port);
}
public abstract void register(Application application);
public abstract void deregister(Application application);
//注册服务, 在NodeService调用Service.init方法之前调用
public void register(NodeServer ns, String protocol, Set<Service> localServices, Set<Service> remoteServices) {
if (localServices.isEmpty()) return;
//注册本地模式
for (Service service : localServices) {
if (!canRegister(protocol, service)) continue;
register(ns, protocol, service);
ClusterEntry htentry = new ClusterEntry(ns, protocol, service);
localEntrys.put(htentry.serviceid, htentry);
if (protocol.toLowerCase().startsWith("http")) {
MessageMultiConsumer mmc = service.getClass().getAnnotation(MessageMultiConsumer.class);
if (mmc != null) {
register(ns, "mqtp", service);
ClusterEntry mqentry = new ClusterEntry(ns, "mqtp", service);
localEntrys.put(mqentry.serviceid, mqentry);
}
}
}
//远程模式加载IP列表, 只支持SNCP协议
if (ns.isSNCP()) {
for (Service service : remoteServices) {
ClusterEntry entry = new ClusterEntry(ns, protocol, service);
updateSncpTransport(entry);
remoteEntrys.put(entry.serviceid, entry);
}
}
}
//注销服务, 在NodeService调用Service.destroy 方法之前调用
public void deregister(NodeServer ns, String protocol, Set<Service> localServices, Set<Service> remoteServices) {
//注销本地模式 远程模式不注册
for (Service service : localServices) {
if (!canRegister(protocol, service)) continue;
deregister(ns, protocol, service);
}
afterDeregister(ns, protocol);
}
protected boolean canRegister(String protocol, Service service) {
if ("SNCP".equalsIgnoreCase(protocol) && service.getClass().getAnnotation(Local.class) != null) return false;
if (service instanceof WebSocketNode) {
if (((WebSocketNode) service).getLocalWebSocketEngine() == null) return false;
}
return true;
}
public void start() {
}
protected void afterDeregister(NodeServer ns, String protocol) {
if (!this.waits) return;
int s = intervalCheckSeconds();
if (s > 0) { //暂停,弥补其他依赖本进程服务的周期偏差
try {
Thread.sleep(s * 1000);
} catch (InterruptedException ex) {
}
logger.info(this.getClass().getSimpleName() + " wait for " + s * 1000 + "ms after deregister");
}
}
public int intervalCheckSeconds() {
return 10;
}
//获取MQTP的HTTP远程服务的可用ip列表, key = servicename的后半段
public abstract CompletableFuture<Map<String, Collection<InetSocketAddress>>> queryMqtpAddress(String protocol, String module, String resname);
//获取HTTP远程服务的可用ip列表
public abstract CompletableFuture<Collection<InetSocketAddress>> queryHttpAddress(String protocol, String module, String resname);
//获取远程服务的可用ip列表
protected abstract CompletableFuture<Collection<InetSocketAddress>> queryAddress(ClusterEntry entry);
//注册服务
protected abstract void register(NodeServer ns, String protocol, Service service);
//注销服务
protected abstract void deregister(NodeServer ns, String protocol, Service service);
//格式: protocol:classtype-resourcename
protected void updateSncpTransport(ClusterEntry entry) {
Service service = entry.serviceref.get();
if (service == null) return;
Collection<InetSocketAddress> addrs = ClusterAgent.this.queryAddress(entry).join();
Sncp.updateTransport(service, transportFactory, Sncp.getResourceType(service).getName() + "-" + Sncp.getResourceName(service), entry.netprotocol, entry.address, null, addrs);
}
protected String generateApplicationServiceName() {
return "application" + (appName == null || appName.isEmpty() ? "" : ("." + appName)) + ".node" + this.nodeid;
}
protected String generateApplicationServiceId() { //与servicename相同
return generateApplicationServiceName();
}
protected String generateApplicationCheckName() {
return "check-" + generateApplicationServiceName();
}
protected String generateApplicationCheckId() {
return "check-" + generateApplicationServiceId();
}
//也会提供给HttpMessageClusterAgent适用
public String generateHttpServiceName(String protocol, String module, String resname) {
return protocol.toLowerCase() + ":" + module + (resname == null || resname.isEmpty() ? "" : ("-" + resname));
}
//格式: protocol:classtype-resourcename
protected String generateServiceName(NodeServer ns, String protocol, Service service) {
if (protocol.toLowerCase().startsWith("http")) { //HTTP使用RestService.name方式是为了与MessageClient中的module保持一致, 因为HTTP依靠的url中的module无法知道Service类名
String resname = Sncp.getResourceName(service);
String module = Rest.getRestModule(service).toLowerCase();
return protocol.toLowerCase() + ":" + module + (resname.isEmpty() ? "" : ("-" + resname));
}
if ("mqtp".equalsIgnoreCase(protocol)) {
MessageMultiConsumer mmc = service.getClass().getAnnotation(MessageMultiConsumer.class);
String selfmodule = Rest.getRestModule(service).toLowerCase();
return protocol.toLowerCase() + ":" + mmc.module() + ":" + selfmodule;
}
if (!Sncp.isSncpDyn(service)) return protocol.toLowerCase() + ":" + service.getClass().getName();
String resname = Sncp.getResourceName(service);
return protocol.toLowerCase() + ":" + Sncp.getResourceType(service).getName() + (resname.isEmpty() ? "" : ("-" + resname));
}
//格式: protocol:classtype-resourcename:nodeid
protected String generateServiceId(NodeServer ns, String protocol, Service service) {
return generateServiceName(ns, protocol, service) + ":" + this.nodeid;
}
protected String generateCheckName(NodeServer ns, String protocol, Service service) {
return "check-" + generateServiceName(ns, protocol, service);
}
protected String generateCheckId(NodeServer ns, String protocol, Service service) {
return "check-" + generateServiceId(ns, protocol, service);
}
protected ConcurrentHashMap<String, ClusterEntry> getLocalEntrys() {
return localEntrys;
}
protected ConcurrentHashMap<String, ClusterEntry> getRemoteEntrys() {
return remoteEntrys;
}
@Override
public String toString() {
return JsonConvert.root().convertTo(this);
}
public TransportFactory getTransportFactory() {
return transportFactory;
}
public void setTransportFactory(TransportFactory transportFactory) {
this.transportFactory = transportFactory;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String[] getProtocols() {
return protocols;
}
public void setProtocols(String[] protocols) {
this.protocols = protocols;
}
public int[] getPorts() {
return ports;
}
public void setPorts(int[] ports) {
this.ports = ports;
}
public AnyValue getConfig() {
return config;
}
public void setConfig(AnyValue config) {
this.config = config;
}
public class ClusterEntry {
public String serviceid;
public String servicename;
public String checkid;
public String checkname;
public String protocol;
public String netprotocol;
public WeakReference<Service> serviceref;
public InetSocketAddress address;
public ScheduledFuture checkScheduledFuture;
public ClusterEntry(NodeServer ns, String protocol, Service service) {
this.serviceid = generateServiceId(ns, protocol, service);
this.servicename = generateServiceName(ns, protocol, service);
this.checkid = generateCheckId(ns, protocol, service);
this.checkname = generateCheckName(ns, protocol, service);
this.protocol = protocol;
this.address = ns.getSocketAddress();
this.serviceref = new WeakReference(service);
Server server = ns.getServer();
this.netprotocol = server instanceof SncpServer ? ((SncpServer) server).getNetprotocol() : Transport.DEFAULT_PROTOCOL;
}
}
}

View File

@@ -54,6 +54,10 @@ public abstract class Convert<R extends Reader, W extends Writer> {
public abstract <T> T convertFrom(final Type type, final ConvertMask mask, final ByteBuffer... buffers); public abstract <T> T convertFrom(final Type type, final ConvertMask mask, final ByteBuffer... buffers);
public abstract byte[] convertToBytes(final Object value);
public abstract byte[] convertToBytes(final Type type, final Object value);
public abstract ByteBuffer[] convertTo(final Supplier<ByteBuffer> supplier, final Object value); public abstract ByteBuffer[] convertTo(final Supplier<ByteBuffer> supplier, final Object value);
public abstract ByteBuffer[] convertTo(final Supplier<ByteBuffer> supplier, final Type type, final Object value); public abstract ByteBuffer[] convertTo(final Supplier<ByteBuffer> supplier, final Type type, final Object value);

View File

@@ -17,8 +17,10 @@ public enum ConvertType {
JSON(1), JSON(1),
BSON(2), BSON(2),
DIY(64), PROTOBUF(64),
ALL(127); PROTOBUF_JSON(64 + 1),
DIY(256),
ALL(1023);
private final int value; private final int value;
@@ -26,8 +28,19 @@ public enum ConvertType {
this.value = v; this.value = v;
} }
public int getValue() {
return value;
}
public boolean contains(ConvertType type) { public boolean contains(ConvertType type) {
if (type == null) return false; if (type == null) return false;
return this.value >= type.value && (this.value & type.value) > 0; return this.value >= type.value && (this.value & type.value) > 0;
} }
public static ConvertType find(int value) {
for (ConvertType t : ConvertType.values()) {
if (value == t.value) return t;
}
return null;
}
} }

View File

@@ -101,7 +101,7 @@ public class ObjectDecoder<R extends Reader, T> implements Decodeable<R, T> {
Type t = TypeToken.createClassType(TypeToken.getGenericType(field.getGenericType(), this.type), this.type); Type t = TypeToken.createClassType(TypeToken.getGenericType(field.getGenericType(), this.type), this.type);
fieldCoder = factory.loadDecoder(t); fieldCoder = factory.loadDecoder(t);
} }
DeMember member = new DeMember(ObjectEncoder.createAttribute(factory, clazz, field, null, null), fieldCoder); DeMember member = new DeMember(ObjectEncoder.createAttribute(factory, type, clazz, field, null, null), fieldCoder);
if (ref != null) member.index = ref.getIndex(); if (ref != null) member.index = ref.getIndex();
list.add(member); list.add(member);
} }
@@ -131,7 +131,7 @@ public class ObjectDecoder<R extends Reader, T> implements Decodeable<R, T> {
Type t = TypeToken.createClassType(TypeToken.getGenericType(method.getGenericParameterTypes()[0], this.type), this.type); Type t = TypeToken.createClassType(TypeToken.getGenericType(method.getGenericParameterTypes()[0], this.type), this.type);
fieldCoder = factory.loadDecoder(t); fieldCoder = factory.loadDecoder(t);
} }
DeMember member = new DeMember(ObjectEncoder.createAttribute(factory, clazz, null, null, method), fieldCoder); DeMember member = new DeMember(ObjectEncoder.createAttribute(factory, type, clazz, null, null, method), fieldCoder);
if (ref != null) member.index = ref.getIndex(); if (ref != null) member.index = ref.getIndex();
list.add(member); list.add(member);
} }
@@ -149,7 +149,7 @@ public class ObjectDecoder<R extends Reader, T> implements Decodeable<R, T> {
try { try {
Field f = clazz.getDeclaredField(constructorField); Field f = clazz.getDeclaredField(constructorField);
Type t = TypeToken.createClassType(f.getGenericType(), this.type); Type t = TypeToken.createClassType(f.getGenericType(), this.type);
list.add(new DeMember(ObjectEncoder.createAttribute(factory, clazz, f, null, null), factory.loadDecoder(t))); list.add(new DeMember(ObjectEncoder.createAttribute(factory, type, clazz, f, null, null), factory.loadDecoder(t)));
} catch (NoSuchFieldException nsfe) { //不存在field 可能存在getter方法 } catch (NoSuchFieldException nsfe) { //不存在field 可能存在getter方法
char[] fs = constructorField.toCharArray(); char[] fs = constructorField.toCharArray();
fs[0] = Character.toUpperCase(fs[0]); fs[0] = Character.toUpperCase(fs[0]);
@@ -161,7 +161,7 @@ public class ObjectDecoder<R extends Reader, T> implements Decodeable<R, T> {
getter = clazz.getMethod("is" + mn); getter = clazz.getMethod("is" + mn);
} }
Type t = TypeToken.createClassType(TypeToken.getGenericType(getter.getGenericParameterTypes()[0], this.type), this.type); Type t = TypeToken.createClassType(TypeToken.getGenericType(getter.getGenericParameterTypes()[0], this.type), this.type);
list.add(new DeMember(ObjectEncoder.createAttribute(factory, clazz, null, getter, null), factory.loadDecoder(t))); list.add(new DeMember(ObjectEncoder.createAttribute(factory, type, clazz, null, getter, null), factory.loadDecoder(t)));
} }
} }
} }

View File

@@ -82,7 +82,7 @@ public class ObjectEncoder<W extends Writer, T> implements Encodeable<W, T> {
Type t = TypeToken.createClassType(TypeToken.getGenericType(field.getGenericType(), this.type), this.type); Type t = TypeToken.createClassType(TypeToken.getGenericType(field.getGenericType(), this.type), this.type);
fieldCoder = factory.loadEncoder(t); fieldCoder = factory.loadEncoder(t);
} }
EnMember member = new EnMember(createAttribute(factory, clazz, field, null, null), fieldCoder); EnMember member = new EnMember(createAttribute(factory, type, clazz, field, null, null), fieldCoder);
if (ref != null) member.index = ref.getIndex(); if (ref != null) member.index = ref.getIndex();
list.add(member); list.add(member);
} }
@@ -111,7 +111,7 @@ public class ObjectEncoder<W extends Writer, T> implements Encodeable<W, T> {
Type t = TypeToken.createClassType(TypeToken.getGenericType(method.getGenericReturnType(), this.type), this.type); Type t = TypeToken.createClassType(TypeToken.getGenericType(method.getGenericReturnType(), this.type), this.type);
fieldCoder = factory.loadEncoder(t); fieldCoder = factory.loadEncoder(t);
} }
EnMember member = new EnMember(createAttribute(factory, clazz, null, method, null), fieldCoder); EnMember member = new EnMember(createAttribute(factory, type, clazz, null, method, null), fieldCoder);
if (ref != null) member.index = ref.getIndex(); if (ref != null) member.index = ref.getIndex();
list.add(member); list.add(member);
} }
@@ -263,7 +263,7 @@ public class ObjectEncoder<W extends Writer, T> implements Encodeable<W, T> {
} }
} }
static Attribute createAttribute(final ConvertFactory factory, Class clazz, final Field field, final Method getter, final Method setter) { static Attribute createAttribute(final ConvertFactory factory, Type realType, Class clazz, final Field field, final Method getter, final Method setter) {
String fieldalias; String fieldalias;
if (field != null) { // public field if (field != null) { // public field
ConvertColumnEntry ref = factory.findRef(clazz, field); ConvertColumnEntry ref = factory.findRef(clazz, field);
@@ -289,7 +289,7 @@ public class ObjectEncoder<W extends Writer, T> implements Encodeable<W, T> {
} }
fieldalias = ref == null || ref.name().isEmpty() ? mfieldname : ref.name(); fieldalias = ref == null || ref.name().isEmpty() ? mfieldname : ref.name();
} }
return Attribute.create(clazz, fieldalias, field, getter, setter); return Attribute.create(realType, clazz, fieldalias, null, field, getter, setter, null);
} }
} }

View File

@@ -20,9 +20,7 @@ import java.util.*;
*/ */
public class OptionalCoder<R extends Reader, W extends Writer, T> extends SimpledCoder<R, W, Optional<T>> { public class OptionalCoder<R extends Reader, W extends Writer, T> extends SimpledCoder<R, W, Optional<T>> {
private final Type type; protected final Type componentType;
private final Type componentType;
protected final Class componentClass; protected final Class componentClass;

View File

@@ -177,6 +177,16 @@ public class BsonConvert extends BinaryConvert<BsonReader, BsonWriter> {
return result; return result;
} }
@Override
public byte[] convertToBytes(final Object value) {
return convertTo(value);
}
@Override
public byte[] convertToBytes(final Type type, final Object value) {
return convertTo(type, value);
}
@Override @Override
public byte[] convertMapTo(final Object... values) { public byte[] convertMapTo(final Object... values) {
if (values == null) return null; if (values == null) return null;

View File

@@ -69,6 +69,10 @@ public class JsonByteBufferReader extends JsonReader {
*/ */
@Override @Override
protected final char nextChar() { protected final char nextChar() {
return nextChar(null);
}
protected final char nextChar(StringBuilder sb) {
if (currentChar != 0) { if (currentChar != 0) {
char ch = currentChar; char ch = currentChar;
this.currentChar = 0; this.currentChar = 0;
@@ -78,14 +82,18 @@ public class JsonByteBufferReader extends JsonReader {
int remain = this.currentBuffer.remaining(); int remain = this.currentBuffer.remaining();
if (remain == 0 && this.currentIndex + 1 >= this.buffers.length) return 0; if (remain == 0 && this.currentIndex + 1 >= this.buffers.length) return 0;
} }
byte b1 = nextByte(); byte b = nextByte();
if (b1 >= 0) {// 1 byte, 7 bits: 0xxxxxxx if (b >= 0) {// 1 byte, 7 bits: 0xxxxxxx
return (char) b1; return (char) b;
} else if ((b1 >> 5) == -2 && (b1 & 0x1e) != 0) { // 2 bytes, 11 bits: 110xxxxx 10xxxxxx } else if ((b >> 5) == -2 && (b & 0x1e) != 0) { // 2 bytes, 11 bits: 110xxxxx 10xxxxxx
return (char) (((b1 << 6) ^ nextByte()) ^ (((byte) 0xC0 << 6) ^ ((byte) 0x80))); return (char) (((b << 6) ^ nextByte()) ^ (((byte) 0xC0 << 6) ^ ((byte) 0x80)));
} else if ((b1 >> 4) == -2) { // 3 bytes, 16 bits: 1110xxxx 10xxxxxx 10xxxxxx } else if ((b >> 4) == -2) { // 3 bytes, 16 bits: 1110xxxx 10xxxxxx 10xxxxxx
return (char) ((b1 << 12) ^ (nextByte() << 6) ^ (nextByte() ^ (((byte) 0xE0 << 12) ^ ((byte) 0x80 << 6) ^ ((byte) 0x80)))); return (char) ((b << 12) ^ (nextByte() << 6) ^ (nextByte() ^ (((byte) 0xE0 << 12) ^ ((byte) 0x80 << 6) ^ ((byte) 0x80))));
} else { // 4 bytes, 21 bits: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx } else if ((b >> 3) == -2) {// 4 bytes, 21 bits: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
int uc = ((b << 18) ^ (nextByte() << 12) ^ (nextByte() << 6) ^ (nextByte() ^ (((byte) 0xF0 << 18) ^ ((byte) 0x80 << 12) ^ ((byte) 0x80 << 6) ^ ((byte) 0x80))));
if (sb != null) sb.append(Character.highSurrogate(uc));
return Character.lowSurrogate(uc);
} else {
throw new RuntimeException(new UnmappableCharacterException(4)); throw new RuntimeException(new UnmappableCharacterException(4));
} }
} }
@@ -208,9 +216,9 @@ public class JsonByteBufferReader extends JsonReader {
if (ch == '"' || ch == '\'') { if (ch == '"' || ch == '\'') {
final char quote = ch; final char quote = ch;
for (;;) { for (;;) {
ch = nextChar(); ch = nextChar(sb);
if (ch == '\\') { if (ch == '\\') {
char c = nextChar(); char c = nextChar(sb);
switch (c) { switch (c) {
case '"': case '"':
case '\'': case '\'':
@@ -249,9 +257,9 @@ public class JsonByteBufferReader extends JsonReader {
} else { } else {
sb.append(ch); sb.append(ch);
for (;;) { for (;;) {
ch = nextChar(); ch = nextChar(sb);
if (ch == '\\') { if (ch == '\\') {
char c = nextChar(); char c = nextChar(sb);
switch (c) { switch (c) {
case '"': case '"':
case '\'': case '\'':

View File

@@ -11,6 +11,7 @@ import java.nio.*;
import java.nio.charset.*; import java.nio.charset.*;
import java.util.function.*; import java.util.function.*;
import org.redkale.convert.*; import org.redkale.convert.*;
import org.redkale.service.RetResult;
import org.redkale.util.*; import org.redkale.util.*;
/** /**
@@ -26,6 +27,9 @@ public class JsonConvert extends TextConvert<JsonReader, JsonWriter> {
public static final Type TYPE_MAP_STRING_STRING = new TypeToken<java.util.HashMap<String, String>>() { public static final Type TYPE_MAP_STRING_STRING = new TypeToken<java.util.HashMap<String, String>>() {
}.getType(); }.getType();
public static final Type TYPE_RETRESULT_STRING = new TypeToken<RetResult<String>>() {
}.getType();
private static final ObjectPool<JsonReader> readerPool = JsonReader.createPool(Integer.getInteger("convert.json.pool.size", 16)); private static final ObjectPool<JsonReader> readerPool = JsonReader.createPool(Integer.getInteger("convert.json.pool.size", 16));
private static final ObjectPool<JsonWriter> writerPool = JsonWriter.createPool(Integer.getInteger("convert.json.pool.size", 16)); private static final ObjectPool<JsonWriter> writerPool = JsonWriter.createPool(Integer.getInteger("convert.json.pool.size", 16));
@@ -214,6 +218,25 @@ public class JsonConvert extends TextConvert<JsonReader, JsonWriter> {
return result; return result;
} }
@Override
public byte[] convertToBytes(final Object value) {
if (value == null) return null;
String result = convertTo(value.getClass(), value);
return result == null ? null : result.getBytes(StandardCharsets.UTF_8);
}
@Override
public byte[] convertToBytes(final Type type, final Object value) {
if (type == null) return null;
if (value == null) return null;
final JsonWriter writer = pollJsonWriter();
writer.specify(type);
factory.loadEncoder(type).convertTo(writer, value);
String result = writer.toString();
writerPool.accept(writer);
return result == null ? null : result.getBytes(StandardCharsets.UTF_8);
}
@Override @Override
public String convertMapTo(final Object... values) { public String convertMapTo(final Object... values) {
if (values == null) return "null"; if (values == null) return "null";

View File

@@ -0,0 +1,238 @@
/*
* 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.mq;
import java.lang.reflect.Type;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicLong;
import java.util.logging.Logger;
import org.redkale.convert.ConvertType;
import org.redkale.convert.json.JsonConvert;
import org.redkale.net.http.*;
/**
* 不依赖MessageRecord则可兼容RPC方式
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*
* @since 2.1.0
*/
public class HttpMessageClient extends MessageClient {
protected final Logger logger = Logger.getLogger(this.getClass().getSimpleName());
protected HttpMessageClient(MessageAgent messageAgent) {
super(messageAgent);
if (messageAgent != null) { // //RPC方式下无messageAgent
this.respTopic = messageAgent.generateHttpRespTopic();
}
}
//格式: http.req.user
public String generateHttpReqTopic(String module) {
return MessageAgent.generateHttpReqTopic(module);
}
//格式: http.req.user-n10
public String generateHttpReqTopic(String module, String resname) {
return MessageAgent.generateHttpReqTopic(module, resname);
}
public String generateHttpReqTopic(HttpSimpleRequest request, String path) {
String module = request.getRequestURI();
if (path != null && !path.isEmpty() && module.startsWith(path)) module = module.substring(path.length());
module = module.substring(1); //去掉/
module = module.substring(0, module.indexOf('/'));
Map<String, String> headers = request.getHeaders();
String resname = headers == null ? "" : headers.getOrDefault(Rest.REST_HEADER_RESOURCE_NAME, "");
return MessageAgent.generateHttpReqTopic(module, resname);
}
public final void produceMessage(HttpSimpleRequest request) {
produceMessage(generateHttpReqTopic(request, null), ConvertType.JSON, 0, null, request, null);
}
public final void produceMessage(HttpSimpleRequest request, AtomicLong counter) {
produceMessage(generateHttpReqTopic(request, null), ConvertType.JSON, 0, null, request, counter);
}
public final void produceMessage(int userid, HttpSimpleRequest request) {
produceMessage(generateHttpReqTopic(request, null), ConvertType.JSON, userid, null, request, null);
}
public final void produceMessage(int userid, String groupid, HttpSimpleRequest request) {
produceMessage(generateHttpReqTopic(request, null), ConvertType.JSON, userid, groupid, request, null);
}
public final void produceMessage(int userid, String groupid, HttpSimpleRequest request, AtomicLong counter) {
produceMessage(generateHttpReqTopic(request, null), ConvertType.JSON, userid, groupid, request, counter);
}
public final void produceMessage(String topic, HttpSimpleRequest request) {
produceMessage(topic, ConvertType.JSON, 0, null, request, null);
}
public final void produceMessage(String topic, HttpSimpleRequest request, AtomicLong counter) {
produceMessage(topic, ConvertType.JSON, 0, null, request, counter);
}
public final void produceMessage(String topic, ConvertType convertType, HttpSimpleRequest request) {
produceMessage(topic, convertType, 0, null, request, null);
}
public final void produceMessage(String topic, ConvertType convertType, HttpSimpleRequest request, AtomicLong counter) {
produceMessage(topic, convertType, 0, null, request, counter);
}
public final void produceMessage(String topic, int userid, String groupid, HttpSimpleRequest request) {
produceMessage(topic, ConvertType.JSON, userid, groupid, request, null);
}
public final void produceMessage(String topic, int userid, String groupid, HttpSimpleRequest request, AtomicLong counter) {
produceMessage(topic, ConvertType.JSON, userid, groupid, request, counter);
}
public final void produceMessage(String topic, ConvertType convertType, int userid, String groupid, HttpSimpleRequest request) {
produceMessage(topic, convertType, userid, groupid, request, null);
}
public final void broadcastMessage(HttpSimpleRequest request) {
broadcastMessage(generateHttpReqTopic(request, null), ConvertType.JSON, 0, null, request, null);
}
public final void broadcastMessage(HttpSimpleRequest request, AtomicLong counter) {
broadcastMessage(generateHttpReqTopic(request, null), ConvertType.JSON, 0, null, request, counter);
}
public final void broadcastMessage(int userid, HttpSimpleRequest request) {
broadcastMessage(generateHttpReqTopic(request, null), ConvertType.JSON, userid, null, request, null);
}
public final void broadcastMessage(int userid, String groupid, HttpSimpleRequest request) {
broadcastMessage(generateHttpReqTopic(request, null), ConvertType.JSON, userid, groupid, request, null);
}
public final void broadcastMessage(int userid, String groupid, HttpSimpleRequest request, AtomicLong counter) {
broadcastMessage(generateHttpReqTopic(request, null), ConvertType.JSON, userid, groupid, request, counter);
}
public final void broadcastMessage(String topic, HttpSimpleRequest request) {
broadcastMessage(topic, ConvertType.JSON, 0, null, request, null);
}
public final void broadcastMessage(String topic, HttpSimpleRequest request, AtomicLong counter) {
broadcastMessage(topic, ConvertType.JSON, 0, null, request, counter);
}
public final void broadcastMessage(String topic, ConvertType convertType, HttpSimpleRequest request) {
broadcastMessage(topic, convertType, 0, null, request, null);
}
public final void broadcastMessage(String topic, ConvertType convertType, HttpSimpleRequest request, AtomicLong counter) {
broadcastMessage(topic, convertType, 0, null, request, counter);
}
public final void broadcastMessage(String topic, int userid, String groupid, HttpSimpleRequest request) {
broadcastMessage(topic, ConvertType.JSON, userid, groupid, request, null);
}
public final void broadcastMessage(String topic, int userid, String groupid, HttpSimpleRequest request, AtomicLong counter) {
broadcastMessage(topic, ConvertType.JSON, userid, groupid, request, counter);
}
public final void broadcastMessage(String topic, ConvertType convertType, int userid, String groupid, HttpSimpleRequest request) {
broadcastMessage(topic, convertType, userid, groupid, request, null);
}
public final <T> CompletableFuture<T> sendMessage(HttpSimpleRequest request, Type type) {
return sendMessage(generateHttpReqTopic(request, null), ConvertType.JSON, 0, null, request, null).thenApply((HttpResult<byte[]> httbs) -> {
if (httbs == null || httbs.getResult() == null) return null;
return JsonConvert.root().convertFrom(type, httbs.getResult());
});
}
public final <T> CompletableFuture<T> sendMessage(int userid, HttpSimpleRequest request, Type type) {
return sendMessage(generateHttpReqTopic(request, null), ConvertType.JSON, userid, null, request, null).thenApply((HttpResult<byte[]> httbs) -> {
if (httbs == null || httbs.getResult() == null) return null;
return JsonConvert.root().convertFrom(type, httbs.getResult());
});
}
public final CompletableFuture<HttpResult<byte[]>> sendMessage(HttpSimpleRequest request) {
return sendMessage(generateHttpReqTopic(request, null), ConvertType.JSON, 0, null, request, null);
}
public final CompletableFuture<HttpResult<byte[]>> sendMessage(HttpSimpleRequest request, AtomicLong counter) {
return sendMessage(generateHttpReqTopic(request, null), ConvertType.JSON, 0, null, request, counter);
}
public final CompletableFuture<HttpResult<byte[]>> sendMessage(int userid, HttpSimpleRequest request) {
return sendMessage(generateHttpReqTopic(request, null), ConvertType.JSON, userid, null, request, null);
}
public final CompletableFuture<HttpResult<byte[]>> sendMessage(int userid, String groupid, HttpSimpleRequest request) {
return sendMessage(generateHttpReqTopic(request, null), ConvertType.JSON, userid, groupid, request, null);
}
public final CompletableFuture<HttpResult<byte[]>> sendMessage(int userid, String groupid, HttpSimpleRequest request, AtomicLong counter) {
return sendMessage(generateHttpReqTopic(request, null), ConvertType.JSON, userid, groupid, request, counter);
}
public final CompletableFuture<HttpResult<byte[]>> sendMessage(String topic, HttpSimpleRequest request) {
return sendMessage(topic, ConvertType.JSON, 0, null, request, null);
}
public final CompletableFuture<HttpResult<byte[]>> sendMessage(String topic, HttpSimpleRequest request, AtomicLong counter) {
return sendMessage(topic, ConvertType.JSON, 0, null, request, counter);
}
public final CompletableFuture<HttpResult<byte[]>> sendMessage(String topic, ConvertType convertType, HttpSimpleRequest request) {
return sendMessage(topic, convertType, 0, null, request, null);
}
public final CompletableFuture<HttpResult<byte[]>> sendMessage(String topic, ConvertType convertType, HttpSimpleRequest request, AtomicLong counter) {
return sendMessage(topic, convertType, 0, null, request, counter);
}
public final CompletableFuture<HttpResult<byte[]>> sendMessage(String topic, int userid, String groupid, HttpSimpleRequest request) {
return sendMessage(topic, ConvertType.JSON, userid, groupid, request, null);
}
public final CompletableFuture<HttpResult<byte[]>> sendMessage(String topic, int userid, String groupid, HttpSimpleRequest request, AtomicLong counter) {
return sendMessage(topic, ConvertType.JSON, userid, groupid, request, counter);
}
public final CompletableFuture<HttpResult<byte[]>> sendMessage(String topic, ConvertType convertType, int userid, String groupid, HttpSimpleRequest request) {
return sendMessage(topic, convertType, userid, groupid, request, null);
}
public CompletableFuture<HttpResult<byte[]>> sendMessage(String topic, ConvertType convertType, int userid, String groupid, HttpSimpleRequest request, AtomicLong counter) {
MessageRecord message = new MessageRecord(convertType, topic, null, HttpSimpleRequestCoder.getInstance().encode(request));
message.userid(userid).groupid(groupid);
return sendMessage(message, true, counter).thenApply(r -> r.decodeContent(HttpResultCoder.getInstance()));
}
public void broadcastMessage(String topic, ConvertType convertType, int userid, String groupid, HttpSimpleRequest request, AtomicLong counter) {
MessageRecord message = new MessageRecord(convertType, topic, null, HttpSimpleRequestCoder.getInstance().encode(request));
message.userid(userid).groupid(groupid);
sendMessage(message, false, counter);
}
public void produceMessage(String topic, ConvertType convertType, int userid, String groupid, HttpSimpleRequest request, AtomicLong counter) {
MessageRecord message = new MessageRecord(convertType, topic, null, HttpSimpleRequestCoder.getInstance().encode(request));
message.userid(userid).groupid(groupid);
sendMessage(message, false, counter);
}
@Override
protected MessageProducers getProducer() {
return messageAgent.getHttpProducer();
}
}

View File

@@ -0,0 +1,150 @@
/*
* 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.mq;
import java.net.*;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.*;
import java.util.logging.Level;
import org.redkale.cluster.ClusterAgent;
import org.redkale.convert.ConvertType;
import org.redkale.net.http.*;
/**
* 没有配置MQ的情况下依赖ClusterAgent实现的默认HttpMessageClient实例
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*
* @since 2.1.0
*/
public class HttpMessageClusterClient extends HttpMessageClient {
//jdk.internal.net.http.common.Utils.DISALLOWED_HEADERS_SET
private static final Set<String> DISALLOWED_HEADERS_SET = Set.of("connection", "content-length",
"date", "expect", "from", "host", "origin",
"referer", "upgrade", "via", "warning");
protected ClusterAgent clusterAgent;
protected java.net.http.HttpClient httpClient;
public HttpMessageClusterClient(ClusterAgent clusterAgent) {
super(null);
Objects.requireNonNull(clusterAgent);
this.clusterAgent = clusterAgent;
this.httpClient = java.net.http.HttpClient.newHttpClient();
}
@Override
public CompletableFuture<HttpResult<byte[]>> sendMessage(String topic, ConvertType convertType, int userid, String groupid, HttpSimpleRequest request, AtomicLong counter) {
return httpAsync(userid, request);
}
@Override
public void produceMessage(String topic, ConvertType convertType, int userid, String groupid, HttpSimpleRequest request, AtomicLong counter) {
httpAsync(userid, request);
}
@Override
public void broadcastMessage(String topic, ConvertType convertType, int userid, String groupid, HttpSimpleRequest request, AtomicLong counter) {
mqtpAsync(userid, request);
}
private CompletableFuture<HttpResult<byte[]>> mqtpAsync(int userid, HttpSimpleRequest req) {
final boolean finest = logger.isLoggable(Level.FINEST);
String module = req.getRequestURI();
module = module.substring(1); //去掉/
module = module.substring(0, module.indexOf('/'));
Map<String, String> headers = req.getHeaders();
String resname = headers == null ? "" : headers.getOrDefault(Rest.REST_HEADER_RESOURCE_NAME, "");
return clusterAgent.queryMqtpAddress("mqtp", module, resname).thenCompose(addrmap -> {
if (addrmap == null || addrmap.isEmpty()) return new HttpResult().status(404).toAnyFuture();
java.net.http.HttpRequest.Builder builder = java.net.http.HttpRequest.newBuilder().timeout(Duration.ofMillis(30000));
if (req.isRpc()) builder.header(Rest.REST_HEADER_RPC_NAME, "true");
if (userid != 0) builder.header(Rest.REST_HEADER_CURRUSERID_NAME, "" + userid);
if (headers != null) headers.forEach((n, v) -> {
if (!DISALLOWED_HEADERS_SET.contains(n.toLowerCase())) builder.header(n, v);
});
builder.header("Content-Type", "x-www-form-urlencoded");
String paramstr = req.getParametersToString();
if (paramstr != null) builder.POST(java.net.http.HttpRequest.BodyPublishers.ofString(paramstr));
List<CompletableFuture> futures = new ArrayList<>();
for (Map.Entry<String, Collection<InetSocketAddress>> en : addrmap.entrySet()) {
String realmodule = en.getKey();
Collection<InetSocketAddress> addrs = en.getValue();
if (addrs == null || addrs.isEmpty()) continue;
String suburi = req.getRequestURI();
suburi = suburi.substring(1); //跳过 /
suburi = "/" + realmodule + suburi.substring(suburi.indexOf('/'));
futures.add(forEachCollectionFuture(finest, userid, req, (req.getPath() != null && !req.getPath().isEmpty() ? req.getPath() : "") + suburi, builder, addrs.iterator()));
}
if (futures.isEmpty()) return CompletableFuture.completedFuture(null);
return CompletableFuture.allOf(futures.toArray(new CompletableFuture[futures.size()])).thenApply(v -> null);
});
}
private CompletableFuture<HttpResult<byte[]>> httpAsync(int userid, HttpSimpleRequest req) {
final boolean finest = logger.isLoggable(Level.FINEST);
String module = req.getRequestURI();
module = module.substring(1); //去掉/
module = module.substring(0, module.indexOf('/'));
Map<String, String> headers = req.getHeaders();
String resname = headers == null ? "" : headers.getOrDefault(Rest.REST_HEADER_RESOURCE_NAME, "");
return clusterAgent.queryHttpAddress("http", module, resname).thenCompose(addrs -> {
if (addrs == null || addrs.isEmpty()) return new HttpResult().status(404).toAnyFuture();
java.net.http.HttpRequest.Builder builder = java.net.http.HttpRequest.newBuilder().timeout(Duration.ofMillis(30000));
if (req.isRpc()) builder.header(Rest.REST_HEADER_RPC_NAME, "true");
if (userid != 0) builder.header(Rest.REST_HEADER_CURRUSERID_NAME, "" + userid);
if (headers != null) headers.forEach((n, v) -> {
if (!DISALLOWED_HEADERS_SET.contains(n.toLowerCase())) builder.header(n, v);
});
builder.header("Content-Type", "x-www-form-urlencoded");
String paramstr = req.getParametersToString();
if (paramstr != null) builder.POST(java.net.http.HttpRequest.BodyPublishers.ofString(paramstr));
return forEachCollectionFuture(finest, userid, req, (req.getPath() != null && !req.getPath().isEmpty() ? req.getPath() : "") + req.getRequestURI(), builder, addrs.iterator());
});
}
private CompletableFuture<HttpResult<byte[]>> forEachCollectionFuture(boolean finest, int userid, HttpSimpleRequest req, String requesturi, java.net.http.HttpRequest.Builder builder, Iterator<InetSocketAddress> it) {
if (!it.hasNext()) return CompletableFuture.completedFuture(null);
InetSocketAddress addr = it.next();
String url = "http://" + addr.getHostString() + ":" + addr.getPort() + requesturi;
return httpClient.sendAsync(builder.copy().uri(URI.create(url)).build(), java.net.http.HttpResponse.BodyHandlers.ofByteArray()).thenCompose(resp -> {
if (resp.statusCode() != 200) return forEachCollectionFuture(finest, userid, req, requesturi, builder, it);
HttpResult rs = new HttpResult();
java.net.http.HttpHeaders hs = resp.headers();
if (hs != null) {
Map<String, List<String>> hm = hs.map();
if (hm != null) {
for (Map.Entry<String, List<String>> en : hm.entrySet()) {
if ("date".equals(en.getKey()) || "content-type".equals(en.getKey())
|| "server".equals(en.getKey()) || "connection".equals(en.getKey())) continue;
List<String> val = en.getValue();
if (val != null && val.size() == 1) {
rs.header(en.getKey(), val.get(0));
}
}
}
}
rs.setResult(resp.body());
if (finest) {
StringBuilder sb = new StringBuilder();
Map<String, String> params = req.getParams();
if (params != null && !params.isEmpty()) {
params.forEach((n, v) -> sb.append('&').append(n).append('=').append(v));
}
logger.log(Level.FINEST, url + "?userid=" + userid + sb + ", result = " + new String(resp.body(), StandardCharsets.UTF_8));
}
return CompletableFuture.completedFuture(rs);
});
}
}

View File

@@ -0,0 +1,126 @@
/*
* 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.mq;
import java.util.concurrent.*;
import java.util.logging.*;
import org.redkale.boot.NodeHttpServer;
import org.redkale.net.http.*;
import org.redkale.service.Service;
import org.redkale.util.ThreadHashExecutor;
/**
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*
* @since 2.1.0
*/
public class HttpMessageProcessor implements MessageProcessor {
protected final boolean finest;
protected final Logger logger;
protected final MessageProducers producer;
protected final NodeHttpServer server;
protected final ThreadHashExecutor workExecutor;
protected final Service service;
protected final HttpServlet servlet;
protected final boolean multiconsumer;
protected final String restmodule; // 前后有/, 例如: /user/
protected final String multimodule; // 前后有/, 例如: /userstat/
protected CountDownLatch cdl;
protected final Runnable innerCallback = () -> {
if (cdl != null) cdl.countDown();
};
public HttpMessageProcessor(Logger logger, ThreadHashExecutor workExecutor, MessageProducers producer, NodeHttpServer server, Service service, HttpServlet servlet) {
this.logger = logger;
this.finest = logger.isLoggable(Level.FINEST);
this.producer = producer;
this.server = server;
this.service = service;
this.servlet = servlet;
MessageMultiConsumer mmc = service.getClass().getAnnotation(MessageMultiConsumer.class);
this.multiconsumer = mmc != null;
this.restmodule = "/" + Rest.getRestModule(service) + "/";
this.multimodule = mmc != null ? ("/" + mmc.module() + "/") : null;
this.workExecutor = workExecutor;
}
@Override
public void begin(final int size) {
if (this.workExecutor != null) this.cdl = new CountDownLatch(size);
}
@Override
public void process(final MessageRecord message, final Runnable callback) {
if (this.workExecutor == null) {
execute(message, innerCallback);
} else {
this.workExecutor.execute(message.hash(), () -> execute(message, innerCallback));
}
}
private void execute(final MessageRecord message, final Runnable callback) {
try {
if (finest) logger.log(Level.FINEST, "HttpMessageProcessor.process message: " + message);
if (multiconsumer) message.setResptopic(null); //不容许有响应
HttpContext context = server.getHttpServer().getContext();
HttpMessageRequest request = new HttpMessageRequest(context, message);
if (multiconsumer) {
request.setRequestURI(request.getRequestURI().replaceFirst(this.multimodule, this.restmodule));
}
HttpMessageResponse response = new HttpMessageResponse(context, request, callback, null, null, producer.getProducer(message));
servlet.execute(request, response);
} catch (Throwable ex) {
if (message.getResptopic() != null && !message.getResptopic().isEmpty()) {
HttpMessageResponse.finishHttpResult(finest, message, callback, producer.getProducer(message), message.getResptopic(), new HttpResult().status(500));
}
logger.log(Level.SEVERE, HttpMessageProcessor.class.getSimpleName() + " process error, message=" + message, ex);
}
}
@Override
public void commit() {
if (this.cdl != null) {
try {
this.cdl.await(30, TimeUnit.SECONDS);
} catch (Exception ex) {
}
this.cdl = null;
}
}
public MessageProducers getProducer() {
return producer;
}
public NodeHttpServer getServer() {
return server;
}
public Service getService() {
return service;
}
public HttpServlet getServlet() {
return servlet;
}
}

View File

@@ -0,0 +1,32 @@
/*
* 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.mq;
import org.redkale.net.http.*;
/**
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*
* @since 2.1.0
*/
public class HttpMessageRequest extends HttpRequest {
protected MessageRecord message;
public HttpMessageRequest(HttpContext context, MessageRecord message) {
super(context, message.decodeContent(HttpSimpleRequestCoder.getInstance()));
this.message = message;
this.currentUserid = message.getUserid();
}
public void setRequestURI(String uri) {
this.requestURI = uri;
}
}

View File

@@ -0,0 +1,167 @@
/*
* 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.mq;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.logging.Level;
import org.redkale.convert.*;
import org.redkale.net.Response;
import org.redkale.net.http.*;
import org.redkale.service.RetResult;
import org.redkale.util.ObjectPool;
/**
*
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*
* @since 2.1.0
*/
public class HttpMessageResponse extends HttpResponse {
protected MessageRecord message;
protected MessageProducer producer;
protected boolean finest;
protected Runnable callback;
public HttpMessageResponse(HttpContext context, HttpMessageRequest request, Runnable callback,
ObjectPool<Response> responsePool, HttpResponseConfig config, MessageProducer producer) {
super(context, request, responsePool, config);
this.message = request.message;
this.callback = callback;
this.producer = producer;
this.finest = producer.logger.isLoggable(Level.FINEST);
}
public HttpMessageResponse(HttpContext context, MessageRecord message, Runnable callback, HttpResponseConfig config, MessageProducer producer) {
super(context, new HttpMessageRequest(context, message), null, config);
this.message = message;
this.callback = callback;
this.producer = producer;
}
public void finishHttpResult(HttpResult result) {
finishHttpResult(this.finest, this.message, this.callback, this.producer, message.getResptopic(), result);
}
public static void finishHttpResult(boolean finest, MessageRecord msg, Runnable callback, MessageProducer producer, String resptopic, HttpResult result) {
if (callback != null) callback.run();
if (resptopic == null || resptopic.isEmpty()) return;
if (result.getResult() instanceof RetResult) {
RetResult ret = (RetResult) result.getResult();
//必须要塞入retcode 开发者可以无需反序列化ret便可确定操作是否返回成功
if (!ret.isSuccess()) result.header("retcode", String.valueOf(ret.getRetcode()));
}
ConvertType format = result.convert() == null ? null : result.convert().getFactory().getConvertType();
if (finest) {
Object innerrs = result.getResult();
if (innerrs instanceof byte[]) innerrs = new String((byte[]) innerrs, StandardCharsets.UTF_8);
producer.logger.log(Level.FINEST, "HttpMessageProcessor.process seqid=" + msg.getSeqid() + ", content: " + innerrs + ", status: " + result.getStatus() + ", headers: " + result.getHeaders());
}
byte[] content = HttpResultCoder.getInstance().encode(result);
producer.apply(new MessageRecord(msg.getSeqid(), format, resptopic, null, content));
}
@Override
public void finishJson(org.redkale.service.RetResult ret) {
if (message.isEmptyResptopic()) {
if (callback != null) callback.run();
return;
}
finishHttpResult(new HttpResult(ret.clearConvert(), ret));
}
@Override
public void finish(String obj) {
if (message.isEmptyResptopic()) {
if (callback != null) callback.run();
return;
}
finishHttpResult(new HttpResult(obj == null ? "" : obj));
}
@Override
public void finish404() {
finish(404, null);
}
@Override
public void finish(int status, String message) {
if (finest) producer.logger.log(Level.FINEST, "HttpMessageResponse.finish status: " + status);
if (this.message.isEmptyResptopic()) {
if (callback != null) callback.run();
return;
}
finishHttpResult(new HttpResult(message == null ? "" : message).status(status));
}
@Override
public void finish(final Convert convert, HttpResult result) {
if (message.isEmptyResptopic()) {
if (callback != null) callback.run();
return;
}
if (convert != null) result.convert(convert);
finishHttpResult(result);
}
@Override
public void finish(final byte[] bs) {
if (message.isEmptyResptopic()) {
if (callback != null) callback.run();
return;
}
finishHttpResult(new HttpResult(bs));
}
@Override
public void finish(final String contentType, final byte[] bs) {
if (message.isEmptyResptopic()) {
if (callback != null) callback.run();
return;
}
finishHttpResult(new HttpResult(bs).contentType(contentType));
}
@Override
public void finish(boolean kill, ByteBuffer buffer) {
if (message.isEmptyResptopic()) {
if (callback != null) callback.run();
return;
}
byte[] bs = new byte[buffer.remaining()];
buffer.get(bs);
finishHttpResult(new HttpResult(bs));
}
@Override
public void finish(boolean kill, ByteBuffer... buffers) {
if (message.isEmptyResptopic()) {
if (callback != null) callback.run();
return;
}
int size = 0;
for (ByteBuffer buf : buffers) {
size += buf.remaining();
}
byte[] bs = new byte[size];
int index = 0;
for (ByteBuffer buf : buffers) {
int r = buf.remaining();
buf.get(bs, index, r);
index += r;
}
finishHttpResult(new HttpResult(bs));
}
}

View File

@@ -0,0 +1,132 @@
/*
* 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.mq;
import java.net.HttpCookie;
import java.nio.ByteBuffer;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import org.redkale.convert.*;
import org.redkale.convert.json.JsonConvert;
import static org.redkale.mq.MessageCoder.*;
import org.redkale.net.http.HttpResult;
import org.redkale.util.Utility;
/**
* HttpResult的MessageCoder实现
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*
* @since 2.1.0
*/
public class HttpResultCoder implements MessageCoder<HttpResult> {
private static final HttpResultCoder instance = new HttpResultCoder();
public static HttpResultCoder getInstance() {
return instance;
}
@Override
public byte[] encode(HttpResult data) {
if (data == null) return null;
byte[] contentType = MessageCoder.getBytes(data.getContentType());
byte[] headers = MessageCoder.getBytes(data.getHeaders());
byte[] cookies = getBytes(data.getCookies());
byte[] content;
if (data.getResult() == null) {
content = new byte[0]; //""
} else if (data.getResult() instanceof byte[]) {
content = (byte[]) data.getResult();
} else if (data.getResult() instanceof CharSequence) {
content = MessageCoder.getBytes(data.getResult().toString());
} else {
Convert cc = data.convert();
if (cc == null || !(cc instanceof TextConvert)) cc = JsonConvert.root();
content = cc.convertToBytes(data.getResult());
}
int count = 4 + 2 + contentType.length + headers.length + cookies.length + 4 + (content == null ? 0 : content.length);
final byte[] bs = new byte[count];
ByteBuffer buffer = ByteBuffer.wrap(bs);
buffer.putInt(data.getStatus());
buffer.putChar((char) contentType.length);
if (contentType.length > 0) buffer.put(contentType);
buffer.put(headers);
buffer.put(cookies);
if (content == null || content.length == 0) {
buffer.putInt(0);
} else {
buffer.putInt(content.length);
buffer.put(content);
}
return bs;
}
@Override
public HttpResult<byte[]> decode(byte[] data) {
if (data == null) return null;
ByteBuffer buffer = ByteBuffer.wrap(data);
HttpResult result = new HttpResult();
result.setStatus(buffer.getInt());
result.setContentType(MessageCoder.getShortString(buffer));
result.setHeaders(MessageCoder.getMap(buffer));
result.setCookies(getCookieList(buffer));
int len = buffer.getInt();
if (len > 0) {
byte[] bs = new byte[len];
buffer.get(bs);
result.setResult(bs);
}
return result;
}
public static byte[] getBytes(final List<HttpCookie> list) {
if (list == null || list.isEmpty()) return new byte[2];
final AtomicInteger len = new AtomicInteger(2);
list.forEach(cookie -> {
len.addAndGet(2 + (cookie.getName() == null ? 0 : Utility.encodeUTF8Length(cookie.getName())));
len.addAndGet(2 + (cookie.getValue() == null ? 0 : Utility.encodeUTF8Length(cookie.getValue())));
len.addAndGet(2 + (cookie.getDomain() == null ? 0 : Utility.encodeUTF8Length(cookie.getDomain())));
len.addAndGet(2 + (cookie.getPath() == null ? 0 : Utility.encodeUTF8Length(cookie.getPath())));
len.addAndGet(2 + (cookie.getPortlist() == null ? 0 : Utility.encodeUTF8Length(cookie.getPortlist())));
len.addAndGet(8 + 1 + 1); //maxage Secure HttpOnly
});
final byte[] bs = new byte[len.get()];
final ByteBuffer buffer = ByteBuffer.wrap(bs);
buffer.putChar((char) list.size());
list.forEach(cookie -> {
putShortString(buffer, cookie.getName());
putShortString(buffer, cookie.getValue());
putShortString(buffer, cookie.getDomain());
putShortString(buffer, cookie.getPath());
putShortString(buffer, cookie.getPortlist());
buffer.putLong(cookie.getMaxAge());
buffer.put(cookie.getSecure() ? (byte) 1 : (byte) 0);
buffer.put(cookie.isHttpOnly() ? (byte) 1 : (byte) 0);
});
return bs;
}
public static List<HttpCookie> getCookieList(ByteBuffer buffer) {
int len = buffer.getChar();
if (len == 0) return null;
final List<HttpCookie> list = new ArrayList<>(len);
for (int i = 0; i < len; i++) {
HttpCookie cookie = new HttpCookie(getShortString(buffer), getShortString(buffer));
cookie.setDomain(getShortString(buffer));
cookie.setPath(getShortString(buffer));
cookie.setPortlist(getShortString(buffer));
cookie.setMaxAge(buffer.getLong());
cookie.setSecure(buffer.get() == 1);
cookie.setHttpOnly(buffer.get() == 1);
list.add(cookie);
}
return list;
}
}

View File

@@ -0,0 +1,93 @@
/*
* 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.mq;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import org.redkale.net.http.HttpSimpleRequest;
/**
* HttpSimpleRequest的MessageCoder实现
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*
* @since 2.1.0
*/
public class HttpSimpleRequestCoder implements MessageCoder<HttpSimpleRequest> {
private static final HttpSimpleRequestCoder instance = new HttpSimpleRequestCoder();
public static HttpSimpleRequestCoder getInstance() {
return instance;
}
@Override
public byte[] encode(HttpSimpleRequest data) {
byte[] requestURI = MessageCoder.getBytes(data.getRequestURI()); //long-string
byte[] path = MessageCoder.getBytes(data.getRequestURI()); //short-string
byte[] remoteAddr = MessageCoder.getBytes(data.getRemoteAddr());//short-string
byte[] sessionid = MessageCoder.getBytes(data.getSessionid());//short-string
byte[] contentType = MessageCoder.getBytes(data.getContentType());//short-string
byte[] headers = MessageCoder.getBytes(data.getHeaders());
byte[] params = MessageCoder.getBytes(data.getParams());
byte[] body = MessageCoder.getBytes(data.getBody());
int count = 1 + 4 + requestURI.length + 2 + path.length + 2 + remoteAddr.length + 2 + sessionid.length
+ 2 + contentType.length + 4 + headers.length + params.length + 4 + body.length;
byte[] bs = new byte[count];
ByteBuffer buffer = ByteBuffer.wrap(bs);
buffer.put((byte) (data.isRpc() ? 'T' : 'F'));
buffer.putInt(requestURI.length);
if (requestURI.length > 0) buffer.put(requestURI);
buffer.putChar((char) path.length);
if (path.length > 0) buffer.put(path);
buffer.putChar((char) remoteAddr.length);
if (remoteAddr.length > 0) buffer.put(remoteAddr);
buffer.putChar((char) sessionid.length);
if (sessionid.length > 0) buffer.put(sessionid);
buffer.putChar((char) contentType.length);
if (contentType.length > 0) buffer.put(contentType);
buffer.putInt(data.getCurrentUserid());
buffer.put(headers);
buffer.put(params);
buffer.putInt(body.length);
if (body.length > 0) buffer.put(body);
return bs;
}
@Override
public HttpSimpleRequest decode(byte[] data) {
if (data == null) return null;
ByteBuffer buffer = ByteBuffer.wrap(data);
HttpSimpleRequest req = new HttpSimpleRequest();
req.setRpc(buffer.get() == 'T');
req.setRequestURI(MessageCoder.getLongString(buffer));
req.setPath(MessageCoder.getShortString(buffer));
req.setRemoteAddr(MessageCoder.getShortString(buffer));
req.setSessionid(MessageCoder.getShortString(buffer));
req.setContentType(MessageCoder.getShortString(buffer));
req.setCurrentUserid(buffer.getInt());
req.setHeaders(MessageCoder.getMap(buffer));
req.setParams(MessageCoder.getMap(buffer));
int len = buffer.getInt();
if (len > 0) {
byte[] bs = new byte[len];
buffer.get(bs);
req.setBody(bs);
}
return req;
}
protected static String getString(ByteBuffer buffer) {
int len = buffer.getInt();
if (len == 0) return null;
byte[] bs = new byte[len];
buffer.get(bs);
return new String(bs, StandardCharsets.UTF_8);
}
}

View File

@@ -0,0 +1,308 @@
/*
* 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.mq;
import java.util.*;
import java.util.concurrent.*;
import java.util.logging.Logger;
import java.util.stream.Collectors;
import javax.annotation.Resource;
import org.redkale.boot.*;
import static org.redkale.boot.Application.RESNAME_APP_NODEID;
import org.redkale.net.Servlet;
import org.redkale.net.http.*;
import org.redkale.net.sncp.*;
import org.redkale.service.Service;
import org.redkale.util.*;
/**
* MQ管理器
*
*
* 详情见: https://redkale.org
*
* @author zhangjx
*
* @since 2.1.0
*/
public abstract class MessageAgent {
protected final Logger logger = Logger.getLogger(this.getClass().getSimpleName());
@Resource(name = RESNAME_APP_NODEID)
protected int nodeid;
protected String name;
protected AnyValue config;
protected MessageProducers httpProducer;
protected MessageProducers sncpProducer;
protected final Object httpProducerLock = new Object();
protected final Object sncpProducerLock = new Object();
protected HttpMessageClient httpMessageClient;
protected SncpMessageClient sncpMessageClient;
protected ScheduledThreadPoolExecutor timeoutExecutor;
protected ThreadHashExecutor workExecutor;
//本地Service消息接收处理器 key:consumer
protected HashMap<String, MessageConsumerNode> messageNodes = new LinkedHashMap<>();
public void init(AnyValue config) {
this.name = checkName(config.getValue("name", ""));
this.httpMessageClient = new HttpMessageClient(this);
this.sncpMessageClient = new SncpMessageClient(this);
this.workExecutor = new ThreadHashExecutor(Math.max(4, config.getIntValue("threads", Runtime.getRuntime().availableProcessors())));
// application (it doesn't execute completion handlers).
this.timeoutExecutor = (ScheduledThreadPoolExecutor) Executors.newScheduledThreadPool(1, (Runnable r) -> {
Thread t = new Thread(r);
t.setName("MessageAgent-Timeout-Thread");
t.setDaemon(true);
return t;
});
this.timeoutExecutor.setRemoveOnCancelPolicy(true);
}
public CompletableFuture<Map<String, Long>> start() {
final LinkedHashMap<String, Long> map = new LinkedHashMap<>();
final List<CompletableFuture> futures = new ArrayList<>();
this.messageNodes.values().forEach(node -> {
long s = System.currentTimeMillis();
futures.add(node.consumer.startup().whenComplete((r, t) -> map.put(node.consumer.consumerid, System.currentTimeMillis() - s)));
});
return CompletableFuture.allOf(futures.toArray(new CompletableFuture[futures.size()])).thenApply(r -> map);
}
//Application.shutdown 在执行server.shutdown之前执行
public CompletableFuture<Void> stop() {
List<CompletableFuture> futures = new ArrayList<>();
this.messageNodes.values().forEach(node -> {
futures.add(node.consumer.shutdown());
});
return CompletableFuture.allOf(futures.toArray(new CompletableFuture[futures.size()]));
}
//Application.shutdown 在所有server.shutdown执行后执行
public void destroy(AnyValue config) {
this.httpMessageClient.close().join();
this.sncpMessageClient.close().join();
this.workExecutor.shutdown();
if (this.timeoutExecutor != null) this.timeoutExecutor.shutdown();
if (this.sncpProducer != null) this.sncpProducer.shutdown().join();
if (this.httpProducer != null) this.httpProducer.shutdown().join();
}
protected List<MessageConsumer> getAllMessageConsumer() {
List<MessageConsumer> consumers = new ArrayList<>();
MessageConsumer one = this.httpMessageClient == null ? null : this.httpMessageClient.consumer;
if (one != null) consumers.add(one);
one = this.sncpMessageClient == null ? null : this.sncpMessageClient.consumer;
if (one != null) consumers.add(one);
consumers.addAll(messageNodes.values().stream().map(mcn -> mcn.consumer).collect(Collectors.toList()));
return consumers;
}
protected List<MessageProducer> getAllMessageProducer() {
List<MessageProducer> producers = new ArrayList<>();
if (this.httpProducer != null) producers.addAll(List.of(this.httpProducer.producers));
if (this.sncpProducer != null) producers.addAll(List.of(this.sncpProducer.producers));
MessageProducers one = this.httpMessageClient == null ? null : this.httpMessageClient.getProducer();
if (one != null) producers.addAll(List.of(one.producers));
one = this.sncpMessageClient == null ? null : this.sncpMessageClient.getProducer();
if (one != null) producers.addAll(List.of(one.producers));
return producers;
}
public Logger getLogger() {
return logger;
}
public String getName() {
return name;
}
public AnyValue getConfig() {
return config;
}
public void setConfig(AnyValue config) {
this.config = config;
}
public HttpMessageClient getHttpMessageClient() {
return httpMessageClient;
}
public SncpMessageClient getSncpMessageClient() {
return sncpMessageClient;
}
protected String checkName(String name) { //不能含特殊字符
if (name.isEmpty()) return name;
if (name.charAt(0) >= '0' && name.charAt(0) <= '9') throw new RuntimeException("name only 0-9 a-z A-Z _ cannot begin 0-9");
for (char ch : name.toCharArray()) {
if (!((ch >= '0' && ch <= '9') || ch == '_' || (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z'))) { //不能含特殊字符
throw new RuntimeException("name only 0-9 a-z A-Z _ cannot begin 0-9");
}
}
return name;
}
//获取指定topic的生产处理器
public MessageProducers getSncpProducer() {
if (this.sncpProducer == null) {
synchronized (sncpProducerLock) {
if (this.sncpProducer == null) {
MessageProducer[] producers = new MessageProducer[Runtime.getRuntime().availableProcessors()];
for (int i = 0; i < producers.length; i++) {
MessageProducer producer = createProducer("SncpProducer");
producer.startup().join();
producers[i] = producer;
}
this.sncpProducer = new MessageProducers(producers);
}
}
}
return this.sncpProducer;
}
public MessageProducers getHttpProducer() {
if (this.httpProducer == null) {
synchronized (httpProducerLock) {
if (this.httpProducer == null) {
MessageProducer[] producers = new MessageProducer[Runtime.getRuntime().availableProcessors()];
for (int i = 0; i < producers.length; i++) {
MessageProducer producer = createProducer("HttpProducer");
producer.startup().join();
producers[i] = producer;
}
this.httpProducer = new MessageProducers(producers);
}
}
}
return this.httpProducer;
}
//创建指定topic的生产处理器
protected abstract MessageProducer createProducer(String name);
//创建topic如果已存在则跳过
public abstract boolean createTopic(String... topics);
//删除topic如果不存在则跳过
public abstract boolean deleteTopic(String... topics);
//查询所有topic
public abstract List<String> queryTopic();
//ServiceLoader时判断配置是否符合当前实现类
public abstract boolean match(AnyValue config);
//创建指定topic的消费处理器
public abstract MessageConsumer createConsumer(String[] topics, String group, MessageProcessor processor);
public final synchronized void putService(NodeHttpServer ns, Service service, HttpServlet servlet) {
String[] topics = generateHttpReqTopics(service);
String consumerid = generateHttpConsumerid(topics, service);
if (messageNodes.containsKey(consumerid)) throw new RuntimeException("consumerid(" + consumerid + ") is repeat");
HttpMessageProcessor processor = new HttpMessageProcessor(this.logger, this.workExecutor, getHttpProducer(), ns, service, servlet);
this.messageNodes.put(consumerid, new MessageConsumerNode(ns, service, servlet, processor, createConsumer(topics, consumerid, processor)));
}
public final synchronized void putService(NodeSncpServer ns, Service service, SncpServlet servlet) {
String topic = generateSncpReqTopic(service);
String consumerid = generateSncpConsumerid(topic, service);
if (messageNodes.containsKey(consumerid)) throw new RuntimeException("consumerid(" + consumerid + ") is repeat");
SncpMessageProcessor processor = new SncpMessageProcessor(this.logger, this.workExecutor, getSncpProducer(), ns, service, servlet);
this.messageNodes.put(consumerid, new MessageConsumerNode(ns, service, servlet, processor, createConsumer(new String[]{topic}, consumerid, processor)));
}
//格式: sncp.req.user
public final String generateSncpReqTopic(Service service) {
if (service instanceof WebSocketNode) {
String resname = Sncp.getResourceName(service);
return "sncp.req.ws" + (resname.isEmpty() ? "" : ("-" + resname)) + ".node" + nodeid;
}
String resname = Sncp.getResourceName(service);
return "sncp.req." + Sncp.getResourceType(service).getSimpleName().replaceAll("Service.*$", "").toLowerCase() + (resname.isEmpty() ? "" : ("-" + resname));
}
//格式: consumer-sncp.req.user 不提供外部使用
protected final String generateSncpConsumerid(String topic, Service service) {
return "consumer-" + topic;
}
//格式: http.req.user
public static String generateHttpReqTopic(String module) {
return "http.req." + module.toLowerCase();
}
//格式: http.req.user
public static String generateHttpReqTopic(String module, String resname) {
return "http.req." + module.toLowerCase() + (resname == null || resname.isEmpty() ? "" : ("-" + resname));
}
//格式: sncp.resp.node10
protected String generateSncpRespTopic() {
return "sncp.resp.node" + nodeid;
}
//格式: http.resp.node10
protected String generateHttpRespTopic() {
return "http.resp.node" + nodeid;
}
//格式: http.req.user
protected String[] generateHttpReqTopics(Service service) {
String resname = Sncp.getResourceName(service);
String module = Rest.getRestModule(service).toLowerCase();
MessageMultiConsumer mmc = service.getClass().getAnnotation(MessageMultiConsumer.class);
if (mmc != null) return new String[]{generateHttpReqTopic(mmc.module()) + (resname.isEmpty() ? "" : ("-" + resname))};
return new String[]{"http.req." + module + (resname.isEmpty() ? "" : ("-" + resname))};
}
//格式: consumer-http.req.user
protected String generateHttpConsumerid(String[] topics, Service service) {
String resname = Sncp.getResourceName(service);
String key = Rest.getRestModule(service).toLowerCase();
return "consumer-http.req." + key + (resname.isEmpty() ? "" : ("-" + resname));
}
//格式: xxxx.resp.node10
protected String generateRespTopic(String protocol) {
return protocol + ".resp.node" + nodeid;
}
protected static class MessageConsumerNode {
public final NodeServer server;
public final Service service;
public final Servlet servlet;
public final MessageProcessor processor;
public final MessageConsumer consumer;
public MessageConsumerNode(NodeServer server, Service service, Servlet servlet, MessageProcessor processor, MessageConsumer consumer) {
this.server = server;
this.service = service;
this.servlet = servlet;
this.processor = processor;
this.consumer = consumer;
}
}
}

View File

@@ -0,0 +1,89 @@
/*
* 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.mq;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicLong;
import java.util.logging.Level;
import org.redkale.convert.ConvertType;
/**
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*
* @since 2.1.0
*/
public abstract class MessageClient {
protected final ConcurrentHashMap<Long, MessageRespFutureNode> respNodes = new ConcurrentHashMap<>();
protected final MessageAgent messageAgent;
protected MessageConsumer consumer;
protected String respTopic;
protected String respConsumerid;
protected ConvertType convertType;
protected MessageClient(MessageAgent messageAgent) {
this.messageAgent = messageAgent;
}
protected CompletableFuture<Void> close() {
if (this.consumer == null) return CompletableFuture.completedFuture(null);
return this.consumer.shutdown();
}
protected CompletableFuture<MessageRecord> sendMessage(MessageRecord message, boolean needresp, AtomicLong counter) {
CompletableFuture<MessageRecord> future = new CompletableFuture<>();
try {
if (this.consumer == null) {
synchronized (this) {
if (this.respConsumerid == null) this.respConsumerid = "consumer-" + this.respTopic;
if (this.consumer == null) {
MessageProcessor processor = (msg, callback) -> {
MessageRespFutureNode node = respNodes.remove(msg.getSeqid());
if (node == null) {
messageAgent.logger.log(Level.WARNING, MessageClient.this.getClass().getSimpleName() + " process " + msg + " error not found msgnode");
return;
}
if (node.getCounter() != null) node.getCounter().decrementAndGet();
node.future.complete(msg);
};
MessageConsumer one = messageAgent.createConsumer(new String[]{respTopic}, respConsumerid, processor);
one.startup().join();
this.consumer = one;
}
}
}
if (convertType != null) message.setFormat(convertType);
if (needresp && (message.getResptopic() == null || message.getResptopic().isEmpty())) {
message.setResptopic(respTopic);
}
if (counter != null) counter.incrementAndGet();
getProducer().apply(message);
if (needresp) {
MessageRespFutureNode node = new MessageRespFutureNode(message, respNodes, counter, future);
respNodes.put(message.getSeqid(), node);
ScheduledThreadPoolExecutor executor = messageAgent.timeoutExecutor;
if (executor != null) executor.schedule(node, 30, TimeUnit.SECONDS);
} else {
future.complete(null);
}
} catch (Exception ex) {
future.completeExceptionally(ex);
} finally {
return future;
}
}
protected abstract MessageProducers getProducer();
}

View File

@@ -0,0 +1,107 @@
/*
* 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.mq;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import org.redkale.util.Utility;
/**
* 将MessageRecord.content内容加解密
*
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*
* @since 2.1.0
*
* @param <T> 泛型
*/
public interface MessageCoder<T> {
//编码
public byte[] encode(T data);
//解码
public T decode(byte[] data);
public static byte[] getBytes(byte[] value) {
if (value == null) return MessageRecord.EMPTY_BYTES;
return value;
}
public static byte[] getBytes(String value) {
if (value == null || value.isEmpty()) return MessageRecord.EMPTY_BYTES;
return value.getBytes(StandardCharsets.UTF_8);
}
public static byte[] getBytes(final Map<String, String> map) {
if (map == null || map.isEmpty()) return new byte[2];
final AtomicInteger len = new AtomicInteger(2);
map.forEach((key, value) -> {
len.addAndGet(2 + (key == null ? 0 : Utility.encodeUTF8Length(key)));
len.addAndGet(4 + (value == null ? 0 : Utility.encodeUTF8Length(value)));
});
final byte[] bs = new byte[len.get()];
final ByteBuffer buffer = ByteBuffer.wrap(bs);
buffer.putChar((char) map.size());
map.forEach((key, value) -> {
putShortString(buffer, key);
putLongString(buffer, value);
});
return bs;
}
public static void putLongString(ByteBuffer buffer, String value) {
if (value == null || value.isEmpty()) {
buffer.putInt(0);
} else {
byte[] bs = value.getBytes(StandardCharsets.UTF_8);
buffer.putInt(bs.length);
buffer.put(bs);
}
}
public static String getLongString(ByteBuffer buffer) {
int len = buffer.getInt();
if (len == 0) return null;
byte[] bs = new byte[len];
buffer.get(bs);
return new String(bs, StandardCharsets.UTF_8);
}
public static void putShortString(ByteBuffer buffer, String value) {
if (value == null || value.isEmpty()) {
buffer.putChar((char) 0);
} else {
byte[] bs = value.getBytes(StandardCharsets.UTF_8);
buffer.putChar((char) bs.length);
buffer.put(bs);
}
}
public static String getShortString(ByteBuffer buffer) {
int len = buffer.getChar();
if (len == 0) return null;
byte[] bs = new byte[len];
buffer.get(bs);
return new String(bs, StandardCharsets.UTF_8);
}
public static Map<String, String> getMap(ByteBuffer buffer) {
int len = buffer.getChar();
if (len == 0) return null;
Map<String, String> map = new HashMap<>(len);
for (int i = 0; i < len; i++) {
map.put(getShortString(buffer), getLongString(buffer));
}
return map;
}
}

View File

@@ -0,0 +1,63 @@
/*
* 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.mq;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.logging.Logger;
/**
*
* <p>
* 详情见: https://redkale.org
*
*
* @author zhangjx
*
* @since 2.1.0
*/
public abstract class MessageConsumer {
protected final String[] topics;
protected final String consumerid;
protected MessageAgent messageAgent;
protected final MessageProcessor processor;
protected final Logger logger;
protected volatile boolean closed;
protected MessageConsumer(MessageAgent messageAgent, String[] topics, final String consumerid, MessageProcessor processor) {
Objects.requireNonNull(messageAgent);
Objects.requireNonNull(topics);
Objects.requireNonNull(consumerid);
Objects.requireNonNull(processor);
this.messageAgent = messageAgent;
this.logger = messageAgent.logger;
this.topics = topics;
this.consumerid = consumerid;
this.processor = processor;
}
public MessageProcessor getProcessor() {
return processor;
}
public String[] getTopics() {
return topics;
}
public abstract CompletableFuture<Void> startup();
public boolean isClosed() {
return closed;
}
public abstract CompletableFuture<Void> shutdown();
}

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.mq;
import java.lang.annotation.*;
import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
/**
* 多消费组,需要同 &#64;RestService 一起使用
* <p>
* 通常一个topic只会被一个RestService消费 当一个topic需要被其他RestService消费时就需要使用&#64;MessageMultiConsumer
*
* <blockquote><pre>
* &#64;RestService(name = "user", comment = "用户服务")
* public class UserService implements Service{
*
* &#64;RestMapping(comment = "用户登录")
* public RetResult login(LoginBean bean){
* //do something
* }
* }
* </pre></blockquote>
*
* 需求:统计用户登录次数, 可以创建一个MessageMultiConsumer 的 RestService
* <blockquote><pre>
* <b>&#64;MessageMultiConsumer(module = "user") </b>
* &#64;RestService(name = "loginstat", comment = "用户统计服务")
* public class LoginStatService implements Service{
*
* private AtomicLong counter = new AtomicLong();
*
* &#64;RestMapping(name = "login", comment = "用户登录统计")
* public void stat(LoginBean bean){ //参数必须和UserService.login方法一致
* counter.incrementAndGet();
* }
* }
* </pre></blockquote>
*
* <p>
* 注: 标记 &#64;MessageMultiConsumer 的Service的&#64;RestMapping方法都只能是void返回类型
*
* <p>
* 详情见: https://redkale.org
*
*
* @author zhangjx
*
* @since 2.1.0
*/
@Inherited
@Documented
@Target({TYPE})
@Retention(RUNTIME)
public @interface MessageMultiConsumer {
String module();
}

View File

@@ -0,0 +1,26 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.mq;
/**
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*
* @since 2.1.0
*/
public interface MessageProcessor {
default void begin(int size) {
}
public void process(MessageRecord message, Runnable callback);
default void commit() {
}
}

View File

@@ -0,0 +1,42 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.mq;
import java.util.concurrent.CompletableFuture;
import java.util.logging.Logger;
/**
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*
* @since 2.1.0
*/
public abstract class MessageProducer {
protected final Logger logger;
protected final String name;
protected volatile boolean closed;
protected MessageProducer(String name, Logger logger) {
this.name = name;
this.logger = logger;
}
public abstract CompletableFuture<Void> apply(MessageRecord message);
public abstract CompletableFuture<Void> startup();
public boolean isClosed() {
return closed;
}
public abstract CompletableFuture<Void> shutdown();
}

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.mq;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicInteger;
/**
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*
* @since 2.1.0
*/
public class MessageProducers {
protected final MessageProducer[] producers;
protected final AtomicInteger index = new AtomicInteger();
public MessageProducers(MessageProducer[] producers) {
this.producers = producers;
}
public MessageProducer getProducer(MessageRecord message) {
int hash = index.incrementAndGet();
if (index.get() > 1000 * producers.length) {
synchronized (index) {
if (index.get() > 1000 * producers.length) {
index.addAndGet(-1000 * producers.length);
}
}
}
return producers[hash % producers.length];
}
public CompletableFuture<Void> apply(MessageRecord message) {
return getProducer(message).apply(message);
}
public CompletableFuture<Void> startup() {
CompletableFuture[] futures = new CompletableFuture[producers.length];
for (int i = 0; i < producers.length; i++) {
futures[i] = producers[i].startup();
}
return CompletableFuture.allOf(futures);
}
public CompletableFuture<Void> shutdown() {
CompletableFuture[] futures = new CompletableFuture[producers.length];
for (int i = 0; i < producers.length; i++) {
futures[i] = producers[i].shutdown();
}
return CompletableFuture.allOf(futures);
}
}

View File

@@ -0,0 +1,320 @@
/*
* 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.mq;
import java.io.Serializable;
import java.nio.charset.StandardCharsets;
import org.redkale.convert.*;
import org.redkale.convert.json.JsonConvert;
import org.redkale.util.Comment;
/**
* 存在MQ里面的数据结构<p>
* groupid + userid 来确定partition 优先使用 groupid
*
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*
* @since 2.1.0
*/
public class MessageRecord implements Serializable {
static final byte[] EMPTY_BYTES = new byte[0];
@ConvertColumn(index = 1)
@Comment("消息序列号")
protected long seqid;
@ConvertColumn(index = 2)
@Comment("版本")
protected int version;
@ConvertColumn(index = 3)
@Comment("内容的格式, 只能是JSON、BSON、PROTOBUF、DIY和null, 普通文本也归于JSON")
protected ConvertType format;
@ConvertColumn(index = 4)
@Comment("标记位, 自定义时使用")
protected int flag;
@ConvertColumn(index = 5)
@Comment("创建时间")
protected long createtime;
@ConvertColumn(index = 6)
@Comment("用户ID无用户信息视为0")
protected int userid;
@ConvertColumn(index = 7)
@Comment("组ID")
protected String groupid;
@ConvertColumn(index = 8)
@Comment("当前topic")
protected String topic;
@ConvertColumn(index = 9)
@Comment("目标topic, 为空表示无目标topic")
protected String resptopic;
@ConvertColumn(index = 10)
@Comment("消息内容")
protected byte[] content;
public MessageRecord() {
}
public MessageRecord(String resptopic, String content) {
this(System.nanoTime(), content == null ? null : ConvertType.JSON, 0, 0, null, null, resptopic, content == null ? null : content.getBytes(StandardCharsets.UTF_8));
}
public MessageRecord(String topic, String resptopic, String content) {
this(System.nanoTime(), content == null ? null : ConvertType.JSON, 0, 0, null, topic, resptopic, content == null ? null : content.getBytes(StandardCharsets.UTF_8));
}
public MessageRecord(int userid, String topic, String resptopic, String content) {
this(System.nanoTime(), content == null ? null : ConvertType.JSON, 0, userid, null, topic, resptopic, content == null ? null : content.getBytes(StandardCharsets.UTF_8));
}
public MessageRecord(ConvertType format, String topic, String resptopic, byte[] content) {
this(System.nanoTime(), format, 0, 0, null, topic, resptopic, content);
}
public MessageRecord(long seqid, ConvertType format, String topic, String resptopic, byte[] content) {
this(seqid, format, 0, null, topic, resptopic, content);
}
public MessageRecord(long seqid, ConvertType format, int userid, String groupid, String topic, String resptopic, byte[] content) {
this(seqid, format, 0, userid, groupid, topic, resptopic, content);
}
public MessageRecord(String topic, String resptopic, Convert convert, Object bean) {
this(0, null, topic, resptopic, convert, bean);
}
public MessageRecord(int userid, String topic, String resptopic, Convert convert, Object bean) {
this(userid, null, topic, resptopic, convert, bean);
}
public MessageRecord(int userid, String groupid, String topic, String resptopic, Convert convert, Object bean) {
this(0, userid, groupid, topic, resptopic, convert, bean);
}
public MessageRecord(int flag, int userid, String groupid, String topic, String resptopic, Convert convert, Object bean) {
this(System.nanoTime(), convert.getFactory().getConvertType(), flag, userid, groupid, topic, resptopic, convert.convertToBytes(bean));
}
public MessageRecord(long seqid, ConvertType format, int flag, int userid, String groupid, String topic, String resptopic, byte[] content) {
this(seqid, 1, format, flag, System.currentTimeMillis(), userid, groupid, topic, resptopic, content);
}
public MessageRecord(long seqid, int version, ConvertType format, int flag, long createtime, int userid, String groupid, String topic, String resptopic, byte[] content) {
this.seqid = seqid;
this.version = version;
this.format = format;
this.flag = flag;
this.createtime = createtime;
this.userid = userid;
this.groupid = groupid;
this.topic = topic;
this.resptopic = resptopic;
this.content = content;
}
public String contentString() {
return content == null ? null : new String(content, StandardCharsets.UTF_8);
}
@ConvertDisabled
public boolean isEmptyTopic() {
return this.topic == null || this.topic.isEmpty();
}
@ConvertDisabled
public boolean isEmptyResptopic() {
return this.resptopic == null || this.resptopic.isEmpty();
}
public <T> T convertFromContent(Convert convert, java.lang.reflect.Type type) {
if (this.content == null || this.content.length == 0) return null;
return (T) convert.convertFrom(type, this.content);
}
public <T> T decodeContent(MessageCoder<T> coder) {
if (this.content == null || this.content.length == 0) return null;
return (T) coder.decode(this.content);
}
public <T> MessageRecord encodeContent(MessageCoder<T> coder, T data) {
this.content = coder.encode(data);
return this;
}
public int hash() {
if (groupid != null && !groupid.isEmpty()) {
return groupid.hashCode();
} else if (userid > 0) {
return userid;
} else {
return 0;
}
}
public MessageRecord version(int version) {
this.version = version;
return this;
}
public MessageRecord format(ConvertType format) {
this.format = format;
return this;
}
public MessageRecord flag(int flag) {
this.flag = flag;
return this;
}
public MessageRecord createtime(long createtime) {
this.createtime = createtime;
return this;
}
public MessageRecord userid(int userid) {
this.userid = userid;
return this;
}
public MessageRecord groupid(String groupid) {
this.groupid = groupid;
return this;
}
public MessageRecord topic(String topic) {
this.topic = topic;
return this;
}
public MessageRecord resptopic(String resptopic) {
this.resptopic = resptopic;
return this;
}
public MessageRecord content(byte[] content) {
this.content = content;
return this;
}
public MessageRecord contentString(String content) {
this.content = content == null ? null : content.getBytes(StandardCharsets.UTF_8);
return this;
}
public long getSeqid() {
return seqid;
}
public void setSeqid(long seqid) {
this.seqid = seqid;
}
public int getVersion() {
return version;
}
public void setVersion(int version) {
this.version = version;
}
public ConvertType getFormat() {
return format;
}
public void setFormat(ConvertType format) {
this.format = format;
}
public int getFlag() {
return flag;
}
public void setFlag(int flag) {
this.flag = flag;
}
public long getCreatetime() {
return createtime;
}
public void setCreatetime(long createtime) {
this.createtime = createtime;
}
public int getUserid() {
return userid;
}
public void setUserid(int userid) {
this.userid = userid;
}
public String getGroupid() {
return groupid;
}
public void setGroupid(String groupid) {
this.groupid = groupid;
}
public String getTopic() {
return topic;
}
public void setTopic(String topic) {
this.topic = topic;
}
public String getResptopic() {
return resptopic;
}
public void setResptopic(String resptopic) {
this.resptopic = resptopic;
}
public byte[] getContent() {
return content;
}
public void setContent(byte[] content) {
this.content = content;
}
@Override
public String toString() {
//return JsonConvert.root().convertTo(this);
StringBuilder sb = new StringBuilder(128);
sb.append("{\"seqid\":").append(this.seqid);
sb.append(",\"version\":").append(this.version);
if (this.format != null) sb.append(",\"format\":\"").append(this.format).append("\"");
if (this.flag != 0) sb.append(",\"flag\":").append(this.flag);
if (this.createtime != 0) sb.append(",\"createtime\":").append(this.createtime);
if (this.userid != 0) sb.append(",\"userid\":").append(this.userid);
if (this.groupid != null) sb.append(",\"groupid\":\"").append(this.groupid).append("\"");
if (this.topic != null) sb.append(",\"topic\":\"").append(this.topic).append("\"");
if (this.resptopic != null) sb.append(",\"resptopic\":\"").append(this.resptopic).append("\"");
if (this.content != null) sb.append(",\"content\":").append(this.format == ConvertType.JSON ? ("\"" + new String(this.content, StandardCharsets.UTF_8) + "\"") : JsonConvert.root().convertTo(this.content));
sb.append("}");
return sb.toString();
}
// public static void main(String[] args) throws Throwable {
// System.out.println(new MessageRecord(333, ConvertType.JSON, 2, 3, null, "tt", null, "xxx".getBytes()));
// }
}

View File

@@ -0,0 +1,84 @@
/*
* 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.mq;
import java.nio.ByteBuffer;
import org.redkale.convert.ConvertType;
/**
* MessageRecord的MessageCoder实现
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*
* @since 2.1.0
*/
public class MessageRecordCoder implements MessageCoder<MessageRecord> {
private static final MessageRecordCoder instance = new MessageRecordCoder();
public static MessageRecordCoder getInstance() {
return instance;
}
@Override
public byte[] encode(MessageRecord data) {
if (data == null) return null;
byte[] stopics = MessageCoder.getBytes(data.getTopic());
byte[] dtopics = MessageCoder.getBytes(data.getResptopic());
byte[] groupid = MessageCoder.getBytes(data.getGroupid());
int count = 8 + 4 + 4 + 4 + 8 + 4 + 2 + stopics.length + 2 + dtopics.length + 2 + groupid.length + 4 + (data.getContent() == null ? 0 : data.getContent().length);
final byte[] bs = new byte[count];
ByteBuffer buffer = ByteBuffer.wrap(bs);
buffer.putLong(data.getSeqid());
buffer.putInt(data.getVersion());
buffer.putInt(data.getFormat() == null ? 0 : data.getFormat().getValue());
buffer.putInt(data.getFlag());
buffer.putLong(data.getCreatetime());
buffer.putInt(data.getUserid());
buffer.putChar((char) groupid.length);
if (groupid.length > 0) buffer.put(groupid);
buffer.putChar((char) stopics.length);
if (stopics.length > 0) buffer.put(stopics);
buffer.putChar((char) dtopics.length);
if (dtopics.length > 0) buffer.put(dtopics);
if (data.getContent() == null) {
buffer.putInt(0);
} else {
buffer.putInt(data.getContent().length);
buffer.put(data.getContent());
}
return bs;
}
@Override
public MessageRecord decode(byte[] data) {
if (data == null) return null;
ByteBuffer buffer = ByteBuffer.wrap(data);
long seqid = buffer.getLong();
int version = buffer.getInt();
ConvertType format = ConvertType.find(buffer.getInt());
int flag = buffer.getInt();
long createtime = buffer.getLong();
int userid = buffer.getInt();
String groupid = MessageCoder.getShortString(buffer);
String topic = MessageCoder.getShortString(buffer);
String resptopic = MessageCoder.getShortString(buffer);
byte[] content = null;
int contentlen = buffer.getInt();
if (contentlen > 0) {
content = new byte[contentlen];
buffer.get(content);
}
return new MessageRecord(seqid, version, format, flag,
createtime, userid, groupid, topic, resptopic, content);
}
}

View File

@@ -0,0 +1,62 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.mq;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicLong;
/**
* MQ管理器
*
*
* 详情见: https://redkale.org
*
* @author zhangjx
*
* @since 2.1.0
*/
public class MessageRespFutureNode implements Runnable {
protected final long seqid;
protected final long createtime;
protected final AtomicLong counter;
protected final CompletableFuture<MessageRecord> future;
protected final ConcurrentHashMap<Long, MessageRespFutureNode> respNodes;
public MessageRespFutureNode(MessageRecord message, ConcurrentHashMap<Long, MessageRespFutureNode> respNodes, AtomicLong counter, CompletableFuture<MessageRecord> future) {
this.seqid = message.getSeqid();
this.respNodes = respNodes;
this.counter = counter;
this.future = future;
this.createtime = System.currentTimeMillis();
}
@Override //超时后被timeoutExecutor调用
public void run() { //timeout
respNodes.remove(this.seqid);
future.completeExceptionally(new TimeoutException());
}
public long getSeqid() {
return seqid;
}
public long getCreatetime() {
return createtime;
}
public AtomicLong getCounter() {
return counter;
}
public CompletableFuture<MessageRecord> getFuture() {
return future;
}
}

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.mq;
/**
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*
* @since 2.1.0
*/
public interface MessageResponse {
public void finish(MessageRecord message);
}

View File

@@ -0,0 +1,58 @@
/*
* 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.mq;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicLong;
import org.redkale.convert.ConvertType;
/**
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*
* @since 2.1.0
*/
public class SncpMessageClient extends MessageClient {
protected SncpMessageClient(MessageAgent messageAgent) {
super(messageAgent);
this.respTopic = messageAgent.generateSncpRespTopic();
this.convertType = ConvertType.BSON;
}
@Override
protected MessageProducers getProducer() {
return messageAgent.getSncpProducer();
}
public String getRespTopic() {
return this.respTopic;
}
//只发送消息,不需要响应
public final void produceMessage(MessageRecord message) {
produceMessage(message, null);
}
//只发送消息,不需要响应
public final void produceMessage(MessageRecord message, AtomicLong counter) {
sendMessage(message, false, counter);
}
//发送消息,需要响应
public final CompletableFuture<MessageRecord> sendMessage(MessageRecord message) {
return sendMessage(message, null);
}
//发送消息,需要响应
public final CompletableFuture<MessageRecord> sendMessage(MessageRecord message, AtomicLong counter) {
return sendMessage(message, true, counter);
}
}

View File

@@ -0,0 +1,107 @@
/*
* 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.mq;
import java.util.concurrent.*;
import java.util.logging.*;
import org.redkale.boot.NodeSncpServer;
import org.redkale.net.sncp.*;
import org.redkale.service.Service;
import org.redkale.util.ThreadHashExecutor;
/**
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*
* @since 2.1.0
*/
public class SncpMessageProcessor implements MessageProcessor {
protected final Logger logger;
protected final MessageProducers producer;
protected final NodeSncpServer server;
protected final ThreadHashExecutor workExecutor;
protected final Service service;
protected final SncpServlet servlet;
protected CountDownLatch cdl;
protected final Runnable innerCallback = () -> {
if (cdl != null) cdl.countDown();
};
public SncpMessageProcessor(Logger logger, ThreadHashExecutor workExecutor, MessageProducers producer, NodeSncpServer server, Service service, SncpServlet servlet) {
this.logger = logger;
this.producer = producer;
this.server = server;
this.service = service;
this.servlet = servlet;
this.workExecutor = workExecutor;
}
@Override
public void begin(final int size) {
if (this.workExecutor != null) this.cdl = new CountDownLatch(size);
}
@Override
public void process(final MessageRecord message, final Runnable callback) {
if (this.workExecutor == null) {
execute(message, innerCallback);
} else {
this.workExecutor.execute(message.hash(), () -> execute(message, innerCallback));
}
}
private void execute(final MessageRecord message, final Runnable callback) {
SncpMessageResponse response = null;
try {
SncpContext context = server.getSncpServer().getContext();
SncpMessageRequest request = new SncpMessageRequest(context, message);
response = new SncpMessageResponse(context, request, callback, null, producer.getProducer(message));
servlet.execute(request, response);
} catch (Throwable ex) {
if (response != null) response.finish(SncpResponse.RETCODE_ILLSERVICEID, null);
logger.log(Level.SEVERE, SncpMessageProcessor.class.getSimpleName() + " process error, message=" + message, ex);
}
}
@Override
public void commit() {
if (this.cdl != null) {
try {
this.cdl.await(30, TimeUnit.SECONDS);
} catch (Exception ex) {
}
this.cdl = null;
}
}
public MessageProducers getProducer() {
return producer;
}
public NodeSncpServer getServer() {
return server;
}
public Service getService() {
return service;
}
public SncpServlet getServlet() {
return servlet;
}
}

View File

@@ -0,0 +1,31 @@
/*
* 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.mq;
import java.nio.ByteBuffer;
import org.redkale.net.sncp.*;
/**
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*
* @since 2.1.0
*/
public class SncpMessageRequest extends SncpRequest {
protected MessageRecord message;
@SuppressWarnings("OverridableMethodCallInConstructor")
public SncpMessageRequest(SncpContext context, MessageRecord message) {
super(context, null);
this.message = message;
readHeader(ByteBuffer.wrap(message.getContent()));
}
}

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.mq;
import java.nio.ByteBuffer;
import org.redkale.convert.ConvertType;
import org.redkale.convert.bson.BsonWriter;
import org.redkale.net.Response;
import org.redkale.net.sncp.*;
import static org.redkale.net.sncp.SncpRequest.HEADER_SIZE;
import org.redkale.util.ObjectPool;
/**
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*
* @since 2.1.0
*/
public class SncpMessageResponse extends SncpResponse {
protected MessageRecord message;
protected MessageProducer producer;
protected Runnable callback;
public SncpMessageResponse(SncpContext context, SncpMessageRequest request, Runnable callback, ObjectPool<Response> responsePool, MessageProducer producer) {
super(context, request, responsePool);
this.message = request.message;
this.callback = callback;
this.producer = producer;
}
public SncpMessageResponse(SncpContext context, MessageRecord message, Runnable callback, ObjectPool<Response> responsePool, MessageProducer producer) {
super(context, new SncpMessageRequest(context, message), responsePool);
this.message = message;
this.callback = callback;
this.producer = producer;
}
@Override
public void finish(final int retcode, final BsonWriter out) {
if (callback != null) callback.run();
if (out == null) {
final byte[] result = new byte[SncpRequest.HEADER_SIZE];
fillHeader(ByteBuffer.wrap(result), 0, retcode);
producer.apply(new MessageRecord(message.getSeqid(), ConvertType.BSON, message.getResptopic(), null, (byte[]) null));
return;
}
final int respBodyLength = out.count(); //body总长度
final byte[] result = out.toArray();
fillHeader(ByteBuffer.wrap(result), respBodyLength - HEADER_SIZE, retcode);
producer.apply(new MessageRecord(message.getSeqid(), ConvertType.BSON, message.getResptopic(), null, result));
}
}

View File

@@ -5,7 +5,7 @@
*/ */
package org.redkale.net; package org.redkale.net;
import java.io.IOException; import java.io.*;
import java.net.*; import java.net.*;
import java.nio.*; import java.nio.*;
import java.nio.channels.*; import java.nio.channels.*;
@@ -23,45 +23,49 @@ import org.redkale.util.*;
* *
* @author zhangjx * @author zhangjx
*/ */
public abstract class AsyncConnection implements ReadableByteChannel, WritableByteChannel, AutoCloseable { public abstract class AsyncConnection implements AutoCloseable {
protected SSLContext sslContext; private SSLContext sslContext;
protected Map<String, Object> attributes; //用于存储绑定在Connection上的对象集合 private Map<String, Object> attributes; //用于存储绑定在Connection上的对象集合
protected Object subobject; //用于存储绑定在Connection上的对象 同attributes 只绑定单个对象时尽量使用subobject而非attributes private Object subobject; //用于存储绑定在Connection上的对象 同attributes 只绑定单个对象时尽量使用subobject而非attributes
protected volatile long readtime; protected volatile long readtime;
protected volatile long writetime; protected volatile long writetime;
protected final Supplier<ByteBuffer> bufferSupplier; private final Supplier<ByteBuffer> bufferSupplier;
protected final Consumer<ByteBuffer> bufferConsumer; private final Consumer<ByteBuffer> bufferConsumer;
protected ByteBuffer readBuffer; private ByteBuffer readBuffer;
//在线数 //在线数
protected AtomicLong livingCounter; private AtomicLong livingCounter;
//关闭数 //关闭数
protected AtomicLong closedCounter; private AtomicLong closedCounter;
protected Consumer<AsyncConnection> beforeCloseListener; private Consumer<AsyncConnection> beforeCloseListener;
//关联的事件数, 小于1表示没有事件 //关联的事件数, 小于1表示没有事件
protected final AtomicInteger eventing = new AtomicInteger(); private final AtomicInteger eventing = new AtomicInteger();
protected AsyncConnection(ObjectPool<ByteBuffer> bufferPool, SSLContext sslContext) { protected AsyncConnection(ObjectPool<ByteBuffer> bufferPool, SSLContext sslContext,
this(bufferPool, bufferPool, sslContext); final AtomicLong livingCounter, final AtomicLong closedCounter) {
this(bufferPool, bufferPool, sslContext, livingCounter, closedCounter);
} }
protected AsyncConnection(Supplier<ByteBuffer> bufferSupplier, Consumer<ByteBuffer> bufferConsumer, SSLContext sslContext) { protected AsyncConnection(Supplier<ByteBuffer> bufferSupplier, Consumer<ByteBuffer> bufferConsumer, SSLContext sslContext,
final AtomicLong livingCounter, final AtomicLong closedCounter) {
Objects.requireNonNull(bufferSupplier); Objects.requireNonNull(bufferSupplier);
Objects.requireNonNull(bufferConsumer); Objects.requireNonNull(bufferConsumer);
this.bufferSupplier = bufferSupplier; this.bufferSupplier = bufferSupplier;
this.bufferConsumer = bufferConsumer; this.bufferConsumer = bufferConsumer;
this.sslContext = sslContext; this.sslContext = sslContext;
this.livingCounter = livingCounter;
this.closedCounter = closedCounter;
} }
public Supplier<ByteBuffer> getBufferSupplier() { public Supplier<ByteBuffer> getBufferSupplier() {
@@ -88,7 +92,6 @@ public abstract class AsyncConnection implements ReadableByteChannel, WritableBy
return eventing.decrementAndGet(); return eventing.decrementAndGet();
} }
@Override
public abstract boolean isOpen(); public abstract boolean isOpen();
public abstract boolean isTCP(); public abstract boolean isTCP();
@@ -113,13 +116,11 @@ public abstract class AsyncConnection implements ReadableByteChannel, WritableBy
public abstract void setWriteTimeoutSeconds(int writeTimeoutSeconds); public abstract void setWriteTimeoutSeconds(int writeTimeoutSeconds);
@Override public abstract ReadableByteChannel readableByteChannel();
public abstract int read(ByteBuffer dst) throws IOException;
public abstract void read(CompletionHandler<Integer, ByteBuffer> handler); public abstract void read(CompletionHandler<Integer, ByteBuffer> handler);
@Override public abstract WritableByteChannel writableByteChannel();
public abstract int write(ByteBuffer src) throws IOException;
public abstract <A> void write(ByteBuffer src, A attachment, CompletionHandler<Integer, ? super A> handler); public abstract <A> void write(ByteBuffer src, A attachment, CompletionHandler<Integer, ? super A> handler);
@@ -140,40 +141,23 @@ public abstract class AsyncConnection implements ReadableByteChannel, WritableBy
this.readBuffer = null; this.readBuffer = null;
return rs; return rs;
} }
// Thread thread = Thread.currentThread();
// if (thread instanceof IOThread) {
// return ((IOThread) thread).getBufferPool().get();
// }
return bufferSupplier.get(); return bufferSupplier.get();
} }
public void offerBuffer(ByteBuffer buffer) { public void offerBuffer(ByteBuffer buffer) {
if (buffer == null) return; if (buffer == null) return;
// Thread thread = Thread.currentThread();
// if (thread instanceof IOThread) {
// ((IOThread) thread).getBufferPool().accept((ByteBuffer) buffer);
// return;
// }
bufferConsumer.accept(buffer); bufferConsumer.accept(buffer);
} }
public void offerBuffer(ByteBuffer... buffers) { public void offerBuffer(ByteBuffer... buffers) {
if (buffers == null) return; if (buffers == null) return;
Consumer<ByteBuffer> consumer = this.bufferConsumer; Consumer<ByteBuffer> consumer = this.bufferConsumer;
// Thread thread = Thread.currentThread();
// if (thread instanceof IOThread) {
// consumer = ((IOThread) thread).getBufferPool();
// }
for (ByteBuffer buffer : buffers) { for (ByteBuffer buffer : buffers) {
consumer.accept(buffer); consumer.accept(buffer);
} }
} }
public ByteBuffer pollWriteBuffer() { public ByteBuffer pollWriteBuffer() {
// Thread thread = Thread.currentThread();
// if (thread instanceof IOThread) {
// return ((IOThread) thread).getBufferPool().get();
// }
return bufferSupplier.get(); return bufferSupplier.get();
} }

View File

@@ -1,61 +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;
import java.nio.ByteBuffer;
import java.util.concurrent.ExecutorService;
import org.redkale.util.*;
/**
* 协议处理的IO线程类
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*/
public class IOThread extends Thread {
protected Thread localThread;
protected final ExecutorService executor;
protected ObjectPool<ByteBuffer> bufferPool;
public IOThread(ExecutorService executor, ObjectPool<ByteBuffer> bufferPool, Runnable runner) {
super(runner);
this.executor = executor;
this.bufferPool = bufferPool;
this.setDaemon(true);
}
public void runAsync(Runnable runner) {
executor.execute(runner);
}
public ExecutorService getExecutor() {
return executor;
}
public ObjectPool<ByteBuffer> getBufferPool() {
return bufferPool;
}
@Override
public void run() {
this.localThread = Thread.currentThread();
super.run();
}
public boolean inSameThread() {
return this.localThread == Thread.currentThread();
}
public boolean inSameThread(Thread thread) {
return this.localThread == thread;
}
}

View File

@@ -73,7 +73,7 @@ public abstract class ProtocolServer {
} else if ("aio".equalsIgnoreCase(netimpl)) { } else if ("aio".equalsIgnoreCase(netimpl)) {
return new TcpAioProtocolServer(context); return new TcpAioProtocolServer(context);
} else if ("nio".equalsIgnoreCase(netimpl)) { } else if ("nio".equalsIgnoreCase(netimpl)) {
return null;// return new TcpNioProtocolServer(context); return new TcpNioProtocolServer(context);
} }
} else if ("UDP".equalsIgnoreCase(protocol)) { } else if ("UDP".equalsIgnoreCase(protocol)) {
if (netimpl == null || netimpl.isEmpty()) { if (netimpl == null || netimpl.isEmpty()) {

View File

@@ -27,7 +27,7 @@ public abstract class Response<C extends Context, R extends Request<C>> {
protected final C context; protected final C context;
protected final ObjectPool<Response> responsePool; protected final ObjectPool<Response> responsePool; //虚拟构建的Response可能不存在responsePool
protected final R request; protected final R request;
@@ -223,7 +223,7 @@ public abstract class Response<C extends Context, R extends Request<C>> {
public void finish(final byte[] bs) { public void finish(final byte[] bs) {
if (!this.inited) return; //避免重复关闭 if (!this.inited) return; //避免重复关闭
if (this.context.bufferCapacity == bs.length) { if (this.context.bufferCapacity == bs.length) {
ByteBuffer buffer = channel.bufferSupplier.get(); ByteBuffer buffer = channel.getBufferSupplier().get();
buffer.put(bs); buffer.put(bs);
buffer.flip(); buffer.flip();
this.finish(buffer); this.finish(buffer);

View File

@@ -86,7 +86,7 @@ public abstract class Server<K extends Serializable, C extends Context, R extend
protected int threads; protected int threads;
//线程池 //线程池
protected ThreadPoolExecutor executor; protected ThreadPoolExecutor workExecutor;
//ByteBuffer池大小 //ByteBuffer池大小
protected int bufferPoolSize; protected int bufferPoolSize;
@@ -151,8 +151,8 @@ public abstract class Server<K extends Serializable, C extends Context, R extend
final AtomicInteger counter = new AtomicInteger(); final AtomicInteger counter = new AtomicInteger();
final Format f = createFormat(); final Format f = createFormat();
final String n = name; final String n = name;
this.executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(threads, (Runnable r) -> { this.workExecutor = (ThreadPoolExecutor) Executors.newFixedThreadPool(threads, (Runnable r) -> {
Thread t = new WorkThread(executor, r); Thread t = new WorkThread(workExecutor, r);
t.setName("Redkale-" + n + "-ServletThread-" + f.format(counter.incrementAndGet())); t.setName("Redkale-" + n + "-ServletThread-" + f.format(counter.incrementAndGet()));
return t; return t;
}); });
@@ -187,8 +187,8 @@ public abstract class Server<K extends Serializable, C extends Context, R extend
return resourceFactory; return resourceFactory;
} }
public ThreadPoolExecutor getExecutor() { public ThreadPoolExecutor getWorkExecutor() {
return executor; return workExecutor;
} }
public InetSocketAddress getSocketAddress() { public InetSocketAddress getSocketAddress() {
@@ -285,7 +285,7 @@ public abstract class Server<K extends Serializable, C extends Context, R extend
final String threadName = "[" + Thread.currentThread().getName() + "] "; final String threadName = "[" + Thread.currentThread().getName() + "] ";
postStart(); postStart();
logger.info(threadName + this.getClass().getSimpleName() + ("TCP".equalsIgnoreCase(protocol) ? "" : ("." + protocol)) + " listen: " + address logger.info(threadName + this.getClass().getSimpleName() + ("TCP".equalsIgnoreCase(protocol) ? "" : ("." + protocol)) + " listen: " + address
+ ", threads: " + threads + ", maxbody: " + formatLenth(context.maxbody) + ", bufferCapacity: " + formatLenth(bufferCapacity) + ", bufferPoolSize: " + bufferPoolSize + ", responsePoolSize: " + responsePoolSize + ", cpu: " + Runtime.getRuntime().availableProcessors() + ", threads: " + threads + ", maxbody: " + formatLenth(context.maxbody) + ", bufferCapacity: " + formatLenth(bufferCapacity) + ", bufferPoolSize: " + bufferPoolSize + ", responsePoolSize: " + responsePoolSize
+ ", started in " + (System.currentTimeMillis() - context.getServerStartTime()) + " ms"); + ", started in " + (System.currentTimeMillis() - context.getServerStartTime()) + " ms");
} }

View File

@@ -5,7 +5,7 @@
*/ */
package org.redkale.net; package org.redkale.net;
import java.io.IOException; import java.io.*;
import java.net.*; import java.net.*;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.nio.channels.*; import java.nio.channels.*;
@@ -22,10 +22,9 @@ import javax.net.ssl.SSLContext;
* *
* @author zhangjx * @author zhangjx
*/ */
public class TcpAioAsyncConnection extends AsyncConnection { class TcpAioAsyncConnection extends AsyncConnection {
//private final Semaphore semaphore = new Semaphore(1); //private final Semaphore semaphore = new Semaphore(1);
private int readTimeoutSeconds; private int readTimeoutSeconds;
private int writeTimeoutSeconds; private int writeTimeoutSeconds;
@@ -40,7 +39,7 @@ public class TcpAioAsyncConnection extends AsyncConnection {
final AsynchronousSocketChannel ch, final SSLContext sslContext, final SocketAddress addr0, final AsynchronousSocketChannel ch, final SSLContext sslContext, final SocketAddress addr0,
final int readTimeoutSeconds, final int writeTimeoutSeconds, final int readTimeoutSeconds, final int writeTimeoutSeconds,
final AtomicLong livingCounter, final AtomicLong closedCounter) { final AtomicLong livingCounter, final AtomicLong closedCounter) {
super(bufferSupplier, bufferConsumer, sslContext); super(bufferSupplier, bufferConsumer, sslContext, livingCounter, closedCounter);
this.channel = ch; this.channel = ch;
this.readTimeoutSeconds = readTimeoutSeconds; this.readTimeoutSeconds = readTimeoutSeconds;
this.writeTimeoutSeconds = writeTimeoutSeconds; this.writeTimeoutSeconds = writeTimeoutSeconds;
@@ -53,8 +52,6 @@ public class TcpAioAsyncConnection extends AsyncConnection {
} }
} }
this.remoteAddress = addr; this.remoteAddress = addr;
this.livingCounter = livingCounter;
this.closedCounter = closedCounter;
} }
@Override @Override
@@ -132,7 +129,6 @@ public class TcpAioAsyncConnection extends AsyncConnection {
// semaphore.release(); // semaphore.release();
// } // }
// } // }
@Override @Override
public <A> void write(ByteBuffer src, A attachment, CompletionHandler<Integer, ? super A> handler) { public <A> void write(ByteBuffer src, A attachment, CompletionHandler<Integer, ? super A> handler) {
write(true, src, attachment, handler); write(true, src, attachment, handler);
@@ -232,23 +228,57 @@ public class TcpAioAsyncConnection extends AsyncConnection {
} }
@Override @Override
public final int read(ByteBuffer dst) throws IOException { public final ReadableByteChannel readableByteChannel() {
return new ReadableByteChannel() {
@Override
public int read(ByteBuffer dst) throws IOException {
try { try {
return channel.read(dst).get(); return channel.read(dst).get(readTimeoutSeconds > 0 ? readTimeoutSeconds : 6, TimeUnit.SECONDS);
} catch (InterruptedException | ExecutionException e) { } catch (Exception e) {
throw new IOException(e); throw new IOException(e);
} }
} }
@Override @Override
public final int write(ByteBuffer src) throws IOException { @SuppressWarnings("InfiniteRecursion")
public boolean isOpen() {
return isOpen();
}
@Override
@SuppressWarnings("InfiniteRecursion")
public void close() throws IOException {
close();
}
};
}
@Override
public final WritableByteChannel writableByteChannel() {
return new WritableByteChannel() {
@Override
public int write(ByteBuffer src) throws IOException {
try { try {
return channel.write(src).get(); return channel.write(src).get(readTimeoutSeconds > 0 ? readTimeoutSeconds : 6, TimeUnit.SECONDS);
} catch (InterruptedException | ExecutionException e) { } catch (Exception e) {
throw new IOException(e); throw new IOException(e);
} }
} }
@Override
@SuppressWarnings("InfiniteRecursion")
public boolean isOpen() {
return isOpen();
}
@Override
@SuppressWarnings("InfiniteRecursion")
public void close() throws IOException {
close();
}
};
}
@Override @Override
public final void close() throws IOException { public final void close() throws IOException {
super.close(); super.close();

View File

@@ -0,0 +1,545 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.net;
import java.io.IOException;
import java.net.*;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.*;
import javax.net.ssl.SSLContext;
import org.redkale.net.AsyncConnection;
import org.redkale.net.nio.NioCompletionHandler;
import org.redkale.net.nio.NioThread;
import org.redkale.net.nio.NioThreadGroup;
import org.redkale.util.ObjectPool;
/**
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*
* @since 2.1.0
*/
public class TcpNioAsyncConnection extends AsyncConnection {
private int readTimeoutSeconds;
private int writeTimeoutSeconds;
private final SocketAddress remoteAddress;
final SocketChannel channel;
final NioThread ioThread;
final NioThreadGroup ioGroup;
final ExecutorService workExecutor;
//连
private Object connectAttachment;
private CompletionHandler<Void, Object> connectCompletionHandler;
private boolean connectPending;
private SelectionKey connectKey;
//读操作
private ByteBuffer readByteBuffer;
private CompletionHandler<Integer, ByteBuffer> readCompletionHandler;
private boolean readPending;
private SelectionKey readKey;
//写操作, 二选一要么writeByteBuffer有值要么writeByteBuffers、writeOffset、writeLength有值
private ByteBuffer writeByteBuffer;
private ByteBuffer[] writeByteBuffers;
private int writeOffset;
private int writeLength;
private Object writeAttachment;
private CompletionHandler<Integer, Object> writeCompletionHandler;
private boolean writePending;
private SelectionKey writeKey;
public TcpNioAsyncConnection(NioThreadGroup ioGroup, NioThread ioThread, ExecutorService workExecutor,
ObjectPool<ByteBuffer> bufferPool, SocketChannel ch,
SSLContext sslContext, final SocketAddress addr0, AtomicLong livingCounter, AtomicLong closedCounter) {
super(bufferPool, sslContext, livingCounter, closedCounter);
this.ioGroup = ioGroup;
this.ioThread = ioThread;
this.workExecutor = workExecutor;
this.channel = ch;
SocketAddress addr = addr0;
if (addr == null) {
try {
addr = ch.getRemoteAddress();
} catch (Exception e) {
//do nothing
}
}
this.remoteAddress = addr;
}
public TcpNioAsyncConnection(NioThreadGroup ioGroup, NioThread ioThread, ExecutorService workExecutor,
Supplier<ByteBuffer> bufferSupplier, Consumer<ByteBuffer> bufferConsumer,
SocketChannel ch, SSLContext sslContext, final SocketAddress addr0,
AtomicLong livingCounter, AtomicLong closedCounter) {
super(bufferSupplier, bufferConsumer, sslContext, livingCounter, closedCounter);
this.ioGroup = ioGroup;
this.ioThread = ioThread;
this.workExecutor = workExecutor;
this.channel = ch;
SocketAddress addr = addr0;
if (addr == null) {
try {
addr = ch.getRemoteAddress();
} catch (Exception e) {
//do nothing
}
}
this.remoteAddress = addr;
}
@Override
public boolean isOpen() {
return this.channel.isOpen();
}
@Override
public boolean isTCP() {
return true;
}
@Override
public boolean shutdownInput() {
try {
this.channel.shutdownInput();
return true;
} catch (IOException e) {
return false;
}
}
@Override
public boolean shutdownOutput() {
try {
this.channel.shutdownOutput();
return true;
} catch (IOException e) {
return false;
}
}
@Override
public <T> boolean setOption(SocketOption<T> name, T value) {
try {
this.channel.setOption(name, value);
return true;
} catch (IOException e) {
return false;
}
}
@Override
public Set<SocketOption<?>> supportedOptions() {
return this.channel.supportedOptions();
}
@Override
public SocketAddress getRemoteAddress() {
return remoteAddress;
}
@Override
public SocketAddress getLocalAddress() {
try {
return channel.getLocalAddress();
} catch (IOException e) {
return null;
}
}
@Override
public void setReadTimeoutSeconds(int readTimeoutSeconds) {
this.readTimeoutSeconds = readTimeoutSeconds;
}
@Override
public void setWriteTimeoutSeconds(int writeTimeoutSeconds) {
this.writeTimeoutSeconds = writeTimeoutSeconds;
}
@Override
public int getReadTimeoutSeconds() {
return this.readTimeoutSeconds;
}
@Override
public int getWriteTimeoutSeconds() {
return this.writeTimeoutSeconds;
}
@Override
public ReadableByteChannel readableByteChannel() {
return this.channel;
}
@Override
public void read(CompletionHandler<Integer, ByteBuffer> handler) {
Objects.requireNonNull(handler);
if (!this.channel.isConnected()) {
if (this.workExecutor == null) {
handler.failed(new NotYetConnectedException(), pollReadBuffer());
} else {
this.workExecutor.execute(() -> handler.failed(new NotYetConnectedException(), pollReadBuffer()));
}
return;
}
if (this.readPending) {
if (this.workExecutor == null) {
handler.failed(new ReadPendingException(), pollReadBuffer());
} else {
this.workExecutor.execute(() -> handler.failed(new ReadPendingException(), pollReadBuffer()));
}
return;
}
this.readPending = true;
if (this.readTimeoutSeconds > 0) {
NioCompletionHandler newhandler = new NioCompletionHandler(handler, this.readByteBuffer);
this.readCompletionHandler = newhandler;
newhandler.timeoutFuture = ioGroup.scheduleTimeout(newhandler, this.readTimeoutSeconds, TimeUnit.SECONDS);
} else {
this.readCompletionHandler = handler;
}
doRead();
}
@Override
public WritableByteChannel writableByteChannel() {
return this.channel;
}
@Override
public <A> void write(ByteBuffer src, A attachment, CompletionHandler<Integer, ? super A> handler) {
Objects.requireNonNull(src);
Objects.requireNonNull(handler);
if (!this.channel.isConnected()) {
if (this.workExecutor == null) {
handler.failed(new NotYetConnectedException(), attachment);
} else {
this.workExecutor.execute(() -> handler.failed(new NotYetConnectedException(), attachment));
}
return;
}
if (this.writePending) {
if (this.workExecutor == null) {
handler.failed(new WritePendingException(), attachment);
} else {
this.workExecutor.execute(() -> handler.failed(new WritePendingException(), attachment));
}
return;
}
this.writePending = true;
this.writeByteBuffer = src;
this.writeAttachment = attachment;
if (this.writeTimeoutSeconds > 0) {
NioCompletionHandler newhandler = new NioCompletionHandler(handler, attachment);
this.writeCompletionHandler = newhandler;
newhandler.timeoutFuture = ioGroup.scheduleTimeout(newhandler, this.writeTimeoutSeconds, TimeUnit.SECONDS);
} else {
this.writeCompletionHandler = (CompletionHandler) handler;
}
doWrite();
}
@Override
public <A> void write(ByteBuffer[] srcs, int offset, int length, A attachment, CompletionHandler<Integer, ? super A> handler) {
Objects.requireNonNull(srcs);
Objects.requireNonNull(handler);
if (!this.channel.isConnected()) {
if (this.workExecutor == null) {
handler.failed(new NotYetConnectedException(), attachment);
} else {
this.workExecutor.execute(() -> handler.failed(new NotYetConnectedException(), attachment));
}
return;
}
if (this.writePending) {
if (this.workExecutor == null) {
handler.failed(new WritePendingException(), attachment);
} else {
this.workExecutor.execute(() -> handler.failed(new WritePendingException(), attachment));
}
return;
}
this.writePending = true;
this.writeByteBuffers = srcs;
this.writeOffset = offset;
this.writeLength = length;
this.writeAttachment = attachment;
if (this.writeTimeoutSeconds > 0) {
NioCompletionHandler newhandler = new NioCompletionHandler(handler, attachment);
this.writeCompletionHandler = newhandler;
newhandler.timeoutFuture = ioGroup.scheduleTimeout(newhandler, this.writeTimeoutSeconds, TimeUnit.SECONDS);
} else {
this.writeCompletionHandler = (CompletionHandler) handler;
}
doWrite();
}
public void doConnect() {
try {
boolean connected = channel.isConnectionPending();
if (connected || channel.connect(remoteAddress)) {
connected = channel.finishConnect();
}
if (connected) {
CompletionHandler handler = this.connectCompletionHandler;
Object attach = this.connectAttachment;
clearConnect();
if (handler != null) {
if (this.workExecutor == null) {
handler.completed(null, attach);
} else {
this.workExecutor.execute(() -> handler.completed(null, attach));
}
}
} else if (connectKey == null) {
ioThread.register(selector -> {
try {
connectKey = channel.register(selector, SelectionKey.OP_CONNECT);
connectKey.attach(this);
} catch (ClosedChannelException e) {
CompletionHandler handler = this.connectCompletionHandler;
Object attach = this.connectAttachment;
clearConnect();
if (handler != null) {
if (this.workExecutor == null) {
handler.failed(e, attach);
} else {
this.workExecutor.execute(() -> handler.failed(e, attach));
}
}
}
});
} else {
CompletionHandler handler = this.connectCompletionHandler;
Object attach = this.connectAttachment;
clearConnect();
if (handler != null) {
IOException e = new IOException();
if (this.workExecutor == null) {
handler.failed(e, attach);
} else {
this.workExecutor.execute(() -> handler.failed(e, attach));
}
}
}
} catch (IOException e) {
CompletionHandler handler = this.connectCompletionHandler;
Object attach = this.connectAttachment;
clearConnect();
if (handler != null) {
if (this.workExecutor == null) {
handler.failed(e, attach);
} else {
this.workExecutor.execute(() -> handler.failed(e, attach));
}
}
}
}
private void clearConnect() {
this.connectCompletionHandler = null;
this.connectAttachment = null;
this.connectPending = false;//必须放最后
}
public void doRead() {
try {
final boolean invokeDirect = this.ioThread.inSameThread();
int totalCount = 0;
boolean hasRemain = true;
if (invokeDirect && this.readByteBuffer == null) {
this.readByteBuffer = pollReadBuffer();
if (this.readTimeoutSeconds > 0) {
((NioCompletionHandler) this.readCompletionHandler).setAttachment(this.readByteBuffer);
}
}
while (invokeDirect && hasRemain) {
int readCount = this.channel.read(readByteBuffer);
hasRemain = readByteBuffer.hasRemaining();
if (readCount <= 0) {
if (totalCount == 0) totalCount = readCount;
break;
}
totalCount += readCount;
}
if (totalCount != 0 || !hasRemain) {
if (readKey != null) readKey.interestOps(readKey.interestOps() & ~SelectionKey.OP_READ);
CompletionHandler<Integer, ByteBuffer> handler = this.readCompletionHandler;
ByteBuffer attach = this.readByteBuffer;
clearRead();
if (handler != null) {
if (this.workExecutor == null) {
handler.completed(totalCount, attach);
} else {
final int totalCount0 = totalCount;
this.workExecutor.execute(() -> handler.completed(totalCount0, attach));
}
}
} else if (readKey == null) {
ioThread.register(selector -> {
try {
readKey = channel.register(selector, SelectionKey.OP_READ);
readKey.attach(this);
} catch (ClosedChannelException e) {
CompletionHandler<Integer, ByteBuffer> handler = this.readCompletionHandler;
ByteBuffer attach = this.readByteBuffer;
clearRead();
if (handler != null) {
if (this.workExecutor == null) {
handler.failed(e, attach);
} else {
this.workExecutor.execute(() -> handler.failed(e, attach));
}
}
}
});
} else {
ioGroup.interestOpsOr(ioThread, readKey, SelectionKey.OP_READ);
}
} catch (Exception e) {
CompletionHandler<Integer, ByteBuffer> handler = this.readCompletionHandler;
ByteBuffer attach = this.readByteBuffer;
clearRead();
if (handler != null) {
if (this.workExecutor == null) {
handler.failed(e, attach);
} else {
this.workExecutor.execute(() -> handler.failed(e, attach));
}
}
}
}
private void clearRead() {
this.readCompletionHandler = null;
this.readByteBuffer = null;
this.readPending = false; //必须放最后
}
public void doWrite() {
try {
final boolean invokeDirect = this.ioThread.inSameThread();
int totalCount = 0;
boolean hasRemain = true;
while (invokeDirect && hasRemain) {
int writeCount;
if (writeByteBuffer != null) {
writeCount = channel.write(writeByteBuffer);
hasRemain = writeByteBuffer.hasRemaining();
} else {
writeCount = (int) channel.write(writeByteBuffers, writeOffset, writeLength);
boolean remain = false;
for (int i = writeByteBuffers.length - 1; i >= writeOffset; i--) {
if (writeByteBuffers[i].hasRemaining()) {
remain = true;
break;
}
}
hasRemain = remain;
}
if (writeCount <= 0) {
if (totalCount == 0) totalCount = writeCount;
break;
}
totalCount += writeCount;
}
if (totalCount > 0 || !hasRemain) {
if (writeKey != null) writeKey.interestOps(writeKey.interestOps() & ~SelectionKey.OP_WRITE);
CompletionHandler<Integer, Object> handler = this.writeCompletionHandler;
Object attach = this.writeAttachment;
clearWrite();
if (handler != null) {
if (this.workExecutor == null) {
handler.completed(totalCount, attach);
} else {
final int totalCount0 = totalCount;
this.workExecutor.execute(() -> handler.completed(totalCount0, attach));
}
}
} else if (writeKey == null) {
ioThread.register(selector -> {
try {
writeKey = channel.register(selector, SelectionKey.OP_WRITE);
writeKey.attach(this);
} catch (ClosedChannelException e) {
CompletionHandler<Integer, Object> handler = this.writeCompletionHandler;
Object attach = this.writeAttachment;
clearWrite();
if (handler != null) {
if (this.workExecutor == null) {
handler.failed(e, attach);
} else {
this.workExecutor.execute(() -> handler.failed(e, attach));
}
}
}
});
} else {
ioGroup.interestOpsOr(ioThread, writeKey, SelectionKey.OP_WRITE);
}
} catch (IOException e) {
CompletionHandler<Integer, Object> handler = this.writeCompletionHandler;
Object attach = this.writeAttachment;
clearWrite();
if (handler != null) {
if (this.workExecutor == null) {
handler.failed(e, attach);
} else {
this.workExecutor.execute(() -> handler.failed(e, attach));
}
}
}
}
private void clearWrite() {
this.writeCompletionHandler = null;
this.writeAttachment = null;
this.writeByteBuffer = null;
this.writeByteBuffers = null;
this.writeOffset = 0;
this.writeLength = 0;
this.writePending = false; //必须放最后
}
@Override
public final void close() throws IOException {
super.close();
if (this.connectKey != null) this.connectKey.cancel();
if (this.readKey != null) this.readKey.cancel();
if (this.writeKey != null) this.writeKey.cancel();
channel.close();
}
}

View File

@@ -0,0 +1,146 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.net;
import java.io.IOException;
import java.net.*;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.*;
import java.util.concurrent.atomic.AtomicLong;
import org.redkale.net.*;
import org.redkale.net.nio.*;
import org.redkale.util.*;
/**
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*
* @since 2.1.0
*/
public class TcpNioProtocolServer extends ProtocolServer {
private ObjectPool<ByteBuffer> bufferPool;
private ObjectPool<Response> responsePool;
private ServerSocketChannel serverChannel;
private Selector selector;
private NioThreadGroup ioGroup;
private Thread acceptThread;
private boolean closed;
public TcpNioProtocolServer(Context context) {
super(context);
}
@Override
public void open(AnyValue config) throws IOException {
this.serverChannel = ServerSocketChannel.open();
this.serverChannel.configureBlocking(false);
this.selector = Selector.open();
final Set<SocketOption<?>> options = this.serverChannel.supportedOptions();
if (options.contains(StandardSocketOptions.TCP_NODELAY)) {
this.serverChannel.setOption(StandardSocketOptions.TCP_NODELAY, true);
}
if (options.contains(StandardSocketOptions.SO_KEEPALIVE)) {
this.serverChannel.setOption(StandardSocketOptions.SO_KEEPALIVE, true);
}
if (options.contains(StandardSocketOptions.SO_REUSEADDR)) {
this.serverChannel.setOption(StandardSocketOptions.SO_REUSEADDR, true);
}
if (options.contains(StandardSocketOptions.SO_RCVBUF)) {
this.serverChannel.setOption(StandardSocketOptions.SO_RCVBUF, 16 * 1024);
}
if (options.contains(StandardSocketOptions.SO_SNDBUF)) {
this.serverChannel.setOption(StandardSocketOptions.SO_SNDBUF, 16 * 1024);
}
}
@Override
public void bind(SocketAddress local, int backlog) throws IOException {
this.serverChannel.bind(local, backlog);
}
@Override
public <T> Set<SocketOption<?>> supportedOptions() {
return this.serverChannel.supportedOptions();
}
@Override
public <T> void setOption(SocketOption<T> name, T value) throws IOException {
this.serverChannel.setOption(name, value);
}
@Override
public void accept(Server server) throws IOException {
this.serverChannel.register(this.selector, SelectionKey.OP_ACCEPT);
AtomicLong createBufferCounter = new AtomicLong();
AtomicLong cycleBufferCounter = new AtomicLong();
this.bufferPool = server.createBufferPool(createBufferCounter, cycleBufferCounter, server.bufferPoolSize);
AtomicLong createResponseCounter = new AtomicLong();
AtomicLong cycleResponseCounter = new AtomicLong();
this.responsePool = server.createResponsePool(createResponseCounter, cycleResponseCounter, server.responsePoolSize);
this.responsePool.setCreator(server.createResponseCreator(bufferPool, responsePool));
this.ioGroup = new NioThreadGroup(Runtime.getRuntime().availableProcessors(), context.executor, bufferPool);
this.ioGroup.start();
this.acceptThread = new Thread() {
@Override
public void run() {
while (!closed) {
try {
int count = selector.select();
if (count == 0) continue;
Set<SelectionKey> keys = selector.selectedKeys();
Iterator<SelectionKey> it = keys.iterator();
while (it.hasNext()) {
SelectionKey key = it.next();
it.remove();
if (key.isAcceptable()) accept(key);
}
} catch (Throwable t) {
t.printStackTrace();
}
}
}
};
this.acceptThread.start();
}
private void accept(SelectionKey key) throws IOException {
SocketChannel channel = this.serverChannel.accept();
channel.configureBlocking(false);
channel.setOption(StandardSocketOptions.TCP_NODELAY, true);
channel.setOption(StandardSocketOptions.SO_KEEPALIVE, true);
channel.setOption(StandardSocketOptions.SO_REUSEADDR, true);
channel.setOption(StandardSocketOptions.SO_RCVBUF, 16 * 1024);
channel.setOption(StandardSocketOptions.SO_SNDBUF, 16 * 1024);
NioThread ioThread = ioGroup.nextThread();
AsyncConnection conn = new TcpNioAsyncConnection(ioGroup, ioThread, context.executor, bufferPool, channel, context.getSSLContext(), null, livingCounter, closedCounter);
new PrepareRunner(context, responsePool, conn, null, null).run();
}
@Override
public void close() throws IOException {
if (this.closed) return;
this.closed = true;
this.selector.wakeup();
this.ioGroup.close();
this.serverChannel.close();
this.selector.close();
}
}

View File

@@ -38,8 +38,6 @@ public final class Transport {
protected final String name; //即<group>的name属性 protected final String name; //即<group>的name属性
protected final String subprotocol; //即<group>的subprotocol属性
protected final boolean tcp; protected final boolean tcp;
protected final String protocol; protected final String protocol;
@@ -58,18 +56,19 @@ public final class Transport {
//负载均衡策略 //负载均衡策略
protected final TransportStrategy strategy; protected final TransportStrategy strategy;
protected Transport(String name, String subprotocol, TransportFactory factory, final ObjectPool<ByteBuffer> transportBufferPool, //连接上限, 为null表示无限制
protected Semaphore semaphore;
protected Transport(String name, TransportFactory factory, final ObjectPool<ByteBuffer> transportBufferPool,
final AsynchronousChannelGroup transportChannelGroup, final SSLContext sslContext, final InetSocketAddress clientAddress, final AsynchronousChannelGroup transportChannelGroup, final SSLContext sslContext, final InetSocketAddress clientAddress,
final Collection<InetSocketAddress> addresses, final TransportStrategy strategy) { final Collection<InetSocketAddress> addresses, final TransportStrategy strategy) {
this(name, DEFAULT_PROTOCOL, subprotocol, factory, transportBufferPool, transportChannelGroup, sslContext, clientAddress, addresses, strategy); this(name, DEFAULT_PROTOCOL, factory, transportBufferPool, transportChannelGroup, sslContext, clientAddress, addresses, strategy);
} }
protected Transport(String name, String protocol, String subprotocol, protected Transport(String name, String protocol, final TransportFactory factory, final ObjectPool<ByteBuffer> transportBufferPool,
final TransportFactory factory, final ObjectPool<ByteBuffer> transportBufferPool,
final AsynchronousChannelGroup transportChannelGroup, final SSLContext sslContext, final InetSocketAddress clientAddress, final AsynchronousChannelGroup transportChannelGroup, final SSLContext sslContext, final InetSocketAddress clientAddress,
final Collection<InetSocketAddress> addresses, final TransportStrategy strategy) { final Collection<InetSocketAddress> addresses, final TransportStrategy strategy) {
this.name = name; this.name = name;
this.subprotocol = subprotocol == null ? "" : subprotocol.trim();
this.protocol = protocol; this.protocol = protocol;
this.factory = factory; this.factory = factory;
factory.transportReferences.add(new WeakReference<>(this)); factory.transportReferences.add(new WeakReference<>(this));
@@ -82,9 +81,28 @@ public final class Transport {
updateRemoteAddresses(addresses); updateRemoteAddresses(addresses);
} }
public Semaphore getSemaphore() {
return semaphore;
}
public void setSemaphore(Semaphore semaphore) {
this.semaphore = semaphore;
}
public final InetSocketAddress[] updateRemoteAddresses(final Collection<InetSocketAddress> addresses) { public final InetSocketAddress[] updateRemoteAddresses(final Collection<InetSocketAddress> addresses) {
final TransportNode[] oldNodes = this.transportNodes; final TransportNode[] oldNodes = this.transportNodes;
synchronized (this) { synchronized (this) {
boolean same = false;
if (this.transportNodes != null && addresses != null && this.transportNodes.length == addresses.size()) {
same = true;
for (TransportNode node : this.transportNodes) {
if (!addresses.contains(node.getAddress())) {
same = false;
break;
}
}
}
if (!same) {
List<TransportNode> list = new ArrayList<>(); List<TransportNode> list = new ArrayList<>();
if (addresses != null) { if (addresses != null) {
for (InetSocketAddress addr : addresses) { for (InetSocketAddress addr : addresses) {
@@ -103,6 +121,7 @@ public final class Transport {
} }
this.transportNodes = list.toArray(new TransportNode[list.size()]); this.transportNodes = list.toArray(new TransportNode[list.size()]);
} }
}
InetSocketAddress[] rs = new InetSocketAddress[oldNodes.length]; InetSocketAddress[] rs = new InetSocketAddress[oldNodes.length];
for (int i = 0; i < rs.length; i++) { for (int i = 0; i < rs.length; i++) {
rs[i] = oldNodes[i].getAddress(); rs[i] = oldNodes[i].getAddress();
@@ -138,10 +157,6 @@ public final class Transport {
return name; return name;
} }
public String getSubprotocol() {
return subprotocol;
}
public void close() { public void close() {
TransportNode[] nodes = this.transportNodes; TransportNode[] nodes = this.transportNodes;
if (nodes == null) return; if (nodes == null) return;
@@ -198,10 +213,52 @@ public final class Transport {
return group; return group;
} }
public String getProtocol() {
return protocol;
}
public boolean isTCP() { public boolean isTCP() {
return tcp; return tcp;
} }
protected CompletableFuture<AsyncConnection> pollAsync(TransportNode node, SocketAddress addr, Supplier<CompletableFuture<AsyncConnection>> func, final int count) {
if (count >= 5) {
CompletableFuture<AsyncConnection> future = new CompletableFuture<>();
future.completeExceptionally(new RuntimeException("create AsyncConnection error"));
return future;
}
final BlockingQueue<AsyncConnection> queue = node.conns;
if (!queue.isEmpty()) {
AsyncConnection conn;
while ((conn = queue.poll()) != null) {
if (conn.isOpen()) {
return CompletableFuture.completedFuture(conn);
} else {
conn.dispose();
}
}
}
if (semaphore != null && !semaphore.tryAcquire()) {
return CompletableFuture.supplyAsync(() -> {
try {
return queue.poll(1, TimeUnit.SECONDS);
} catch (Exception t) {
return null;
}
}, factory.executor).thenCompose((conn2) -> {
if (conn2 != null && conn2.isOpen()) {
return CompletableFuture.completedFuture(conn2);
}
return pollAsync(node, addr, func, count + 1);
});
}
return func.get().thenApply(conn -> {
if (conn != null && semaphore != null) conn.beforeCloseListener((c) -> semaphore.release());
return conn;
});
}
public CompletableFuture<AsyncConnection> pollConnection(SocketAddress addr0) { public CompletableFuture<AsyncConnection> pollConnection(SocketAddress addr0) {
if (this.strategy != null) return strategy.pollConnection(addr0, this); if (this.strategy != null) return strategy.pollConnection(addr0, this);
final TransportNode[] nodes = this.transportNodes; final TransportNode[] nodes = this.transportNodes;
@@ -222,18 +279,7 @@ public final class Transport {
if (node == null) { if (node == null) {
return AsyncConnection.createTCP(bufferPool, group, sslContext, addr, factory.readTimeoutSeconds, factory.writeTimeoutSeconds); return AsyncConnection.createTCP(bufferPool, group, sslContext, addr, factory.readTimeoutSeconds, factory.writeTimeoutSeconds);
} }
final BlockingQueue<AsyncConnection> queue = node.conns; return pollAsync(node, addr, () -> AsyncConnection.createTCP(bufferPool, group, sslContext, addr, factory.readTimeoutSeconds, factory.writeTimeoutSeconds), 1);
if (!queue.isEmpty()) {
AsyncConnection conn;
while ((conn = queue.poll()) != null) {
if (conn.isOpen()) {
return CompletableFuture.completedFuture(conn);
} else {
conn.dispose();
}
}
}
return AsyncConnection.createTCP(bufferPool, group, sslContext, addr, factory.readTimeoutSeconds, factory.writeTimeoutSeconds);
} }
//---------------------随机取地址------------------------ //---------------------随机取地址------------------------
@@ -257,11 +303,18 @@ public final class Transport {
} }
} }
} }
return pollAsync(one, one.getAddress(), () -> {
CompletableFuture future = new CompletableFuture(); CompletableFuture future = new CompletableFuture();
final AsynchronousSocketChannel channel = AsynchronousSocketChannel.open(group); AsynchronousSocketChannel channel0 = null;
channel.setOption(StandardSocketOptions.TCP_NODELAY, true); try {
channel.setOption(StandardSocketOptions.SO_KEEPALIVE, true); channel0 = AsynchronousSocketChannel.open(group);
channel.setOption(StandardSocketOptions.SO_REUSEADDR, true); channel0.setOption(StandardSocketOptions.TCP_NODELAY, true);
channel0.setOption(StandardSocketOptions.SO_KEEPALIVE, true);
channel0.setOption(StandardSocketOptions.SO_REUSEADDR, true);
} catch (Exception ex) {
ex.printStackTrace();
}
final AsynchronousSocketChannel channel = channel0;
channel.connect(one.address, one, new CompletionHandler<Void, TransportNode>() { channel.connect(one.address, one, new CompletionHandler<Void, TransportNode>() {
@Override @Override
public void completed(Void result, TransportNode attachment) { public void completed(Void result, TransportNode attachment) {
@@ -296,6 +349,7 @@ public final class Transport {
} }
}); });
return future; return future;
}, 1);
} }
return pollConnection0(nodes, null, now); return pollConnection0(nodes, null, now);
} catch (Exception ex) { } catch (Exception ex) {

View File

@@ -21,8 +21,8 @@ import org.redkale.service.Service;
import org.redkale.util.*; import org.redkale.util.*;
/** /**
* System.getProperty("net.transport.pinginterval", "30") 心跳周期默认30秒 * System.getProperty("net.transport.ping.interval", "30") 心跳周期默认30秒
* System.getProperty("net.transport.checkinterval", "30") 检查不可用地址周期默认30秒 * System.getProperty("net.transport.check.interval", "30") 检查不可用地址周期默认30秒
* *
* <p> * <p>
* 详情见: https://redkale.org * 详情见: https://redkale.org
@@ -65,10 +65,10 @@ public class TransportFactory {
protected final List<WeakReference<Transport>> transportReferences = new CopyOnWriteArrayList<>(); protected final List<WeakReference<Transport>> transportReferences = new CopyOnWriteArrayList<>();
//连接池大小 //连接池大小
protected int poolmaxconns = Integer.getInteger("net.transport.poolmaxconns", Math.max(100, Runtime.getRuntime().availableProcessors() * 16)); //最少是wsthreads的两倍 protected int poolmaxconns = Integer.getInteger("net.transport.pool.maxconns", Math.max(100, Runtime.getRuntime().availableProcessors() * 16)); //最少是wsthreads的两倍
//检查不可用地址周期, 单位:秒 //检查不可用地址周期, 单位:秒
protected int checkinterval = Integer.getInteger("net.transport.checkinterval", 30); protected int checkinterval = Integer.getInteger("net.transport.check.interval", 30);
//心跳周期, 单位:秒 //心跳周期, 单位:秒
protected int pinginterval; protected int pinginterval;
@@ -203,16 +203,11 @@ public class TransportFactory {
} }
public Transport createTransportTCP(String name, final InetSocketAddress clientAddress, final Collection<InetSocketAddress> addresses) { public Transport createTransportTCP(String name, final InetSocketAddress clientAddress, final Collection<InetSocketAddress> addresses) {
return new Transport(name, "TCP", "", this, this.bufferPool, this.channelGroup, this.sslContext, clientAddress, addresses, strategy); return new Transport(name, "TCP", this, this.bufferPool, this.channelGroup, this.sslContext, clientAddress, addresses, strategy);
} }
public Transport createTransport(String name, String protocol, final InetSocketAddress clientAddress, final Collection<InetSocketAddress> addresses) { public Transport createTransport(String name, String protocol, final InetSocketAddress clientAddress, final Collection<InetSocketAddress> addresses) {
return new Transport(name, protocol, "", this, this.bufferPool, this.channelGroup, this.sslContext, clientAddress, addresses, strategy); return new Transport(name, protocol, this, this.bufferPool, this.channelGroup, this.sslContext, clientAddress, addresses, strategy);
}
public Transport createTransport(String name, String protocol, String subprotocol,
final InetSocketAddress clientAddress, final Collection<InetSocketAddress> addresses) {
return new Transport(name, protocol, subprotocol, this, this.bufferPool, this.channelGroup, this.sslContext, clientAddress, addresses, strategy);
} }
public String findGroupName(InetSocketAddress addr) { public String findGroupName(InetSocketAddress addr) {
@@ -251,7 +246,6 @@ public class TransportFactory {
if (!checkName(info.name)) throw new RuntimeException("Transport.group.name only 0-9 a-z A-Z _ cannot begin 0-9"); 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); 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.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) { for (InetSocketAddress addr : info.addresses) {
if (!groupAddrs.getOrDefault(addr, info.name).equals(info.name)) throw new RuntimeException(addr + " repeat but different group.name"); if (!groupAddrs.getOrDefault(addr, info.name).equals(info.name)) throw new RuntimeException(addr + " repeat but different group.name");
} }
@@ -266,23 +260,7 @@ public class TransportFactory {
return true; return true;
} }
public Transport loadSameGroupTransport(InetSocketAddress sncpAddress) { public Transport loadTransport(InetSocketAddress sncpAddress, final Set<String> groups) {
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; if (groups == null) return null;
Set<InetSocketAddress> addresses = new HashSet<>(); Set<InetSocketAddress> addresses = new HashSet<>();
TransportGroupInfo info = null; TransportGroupInfo info = null;
@@ -293,14 +271,7 @@ public class TransportFactory {
} }
if (info == null) info = new TransportGroupInfo("TCP"); if (info == null) info = new TransportGroupInfo("TCP");
if (sncpAddress != null) addresses.remove(sncpAddress); if (sncpAddress != null) addresses.remove(sncpAddress);
return new Transport(groups.stream().sorted().collect(Collectors.joining(";")), info.protocol, info.subprotocol, this, this.bufferPool, this.channelGroup, this.sslContext, sncpAddress, addresses, this.strategy); return new Transport(groups.stream().sorted().collect(Collectors.joining(";")), info.protocol, this, this.bufferPool, this.channelGroup, this.sslContext, sncpAddress, addresses, this.strategy);
}
private Transport loadTransport(final String groupName, InetSocketAddress sncpAddress) {
if (groupName == null) return null;
TransportGroupInfo info = groupInfos.get(groupName);
if (info == null) return null;
return new Transport(groupName, info.protocol, info.subprotocol, this, this.bufferPool, this.channelGroup, this.sslContext, sncpAddress, info.addresses, this.strategy);
} }
public ExecutorService getExecutor() { public ExecutorService getExecutor() {

View File

@@ -24,30 +24,27 @@ public class TransportGroupInfo {
protected String protocol; //协议 取值范围: TCP、UDP protected String protocol; //协议 取值范围: TCP、UDP
protected String subprotocol; //子协议,预留使用
protected Set<InetSocketAddress> addresses; //地址列表, 对应 resources-&#62;group-&#62;node节点信息 protected Set<InetSocketAddress> addresses; //地址列表, 对应 resources-&#62;group-&#62;node节点信息
public TransportGroupInfo() { public TransportGroupInfo() {
} }
public TransportGroupInfo(String name, InetSocketAddress... addrs) { public TransportGroupInfo(String name, InetSocketAddress... addrs) {
this(name, "TCP", "", Utility.ofSet(addrs)); this(name, "TCP", Utility.ofSet(addrs));
} }
public TransportGroupInfo(String name, Set<InetSocketAddress> addrs) { public TransportGroupInfo(String name, Set<InetSocketAddress> addrs) {
this(name, "TCP", "", addrs); this(name, "TCP", addrs);
} }
public TransportGroupInfo(String name, String protocol, String subprotocol, InetSocketAddress... addrs) { public TransportGroupInfo(String name, String protocol, InetSocketAddress... addrs) {
this(name, protocol, subprotocol, Utility.ofSet(addrs)); this(name, protocol, Utility.ofSet(addrs));
} }
public TransportGroupInfo(String name, String protocol, String subprotocol, Set<InetSocketAddress> addrs) { public TransportGroupInfo(String name, String protocol, Set<InetSocketAddress> addrs) {
Objects.requireNonNull(name, "Transport.group.name can not null"); Objects.requireNonNull(name, "Transport.group.name can not null");
this.name = name; this.name = name;
this.protocol = protocol == null ? "TCP" : protocol; this.protocol = protocol == null ? "TCP" : protocol;
this.subprotocol = subprotocol == null ? "" : subprotocol;
this.addresses = addrs; this.addresses = addrs;
} }
@@ -68,15 +65,6 @@ public class TransportGroupInfo {
this.protocol = protocol == null ? "TCP" : 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() { public Set<InetSocketAddress> getAddresses() {
return addresses; return addresses;
} }

View File

@@ -5,7 +5,7 @@
*/ */
package org.redkale.net; package org.redkale.net;
import java.io.IOException; import java.io.*;
import java.net.*; import java.net.*;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.nio.channels.*; import java.nio.channels.*;
@@ -21,7 +21,7 @@ import javax.net.ssl.SSLContext;
* *
* @author zhangjx * @author zhangjx
*/ */
public class UdpBioAsyncConnection extends AsyncConnection { class UdpBioAsyncConnection extends AsyncConnection {
private int readTimeoutSeconds; private int readTimeoutSeconds;
@@ -37,7 +37,7 @@ public class UdpBioAsyncConnection extends AsyncConnection {
final DatagramChannel ch, final SSLContext sslContext, SocketAddress addr0, final boolean client0, final DatagramChannel ch, final SSLContext sslContext, SocketAddress addr0, final boolean client0,
final int readTimeoutSeconds0, final int writeTimeoutSeconds0, final int readTimeoutSeconds0, final int writeTimeoutSeconds0,
final AtomicLong livingCounter, final AtomicLong closedCounter) { final AtomicLong livingCounter, final AtomicLong closedCounter) {
super(bufferSupplier, bufferConsumer, sslContext); super(bufferSupplier, bufferConsumer, sslContext, livingCounter, closedCounter);
this.channel = ch; this.channel = ch;
this.client = client0; this.client = client0;
this.readTimeoutSeconds = readTimeoutSeconds0; this.readTimeoutSeconds = readTimeoutSeconds0;
@@ -51,8 +51,6 @@ public class UdpBioAsyncConnection extends AsyncConnection {
} }
} }
this.remoteAddress = addr; this.remoteAddress = addr;
this.livingCounter = livingCounter;
this.closedCounter = closedCounter;
} }
@Override @Override
@@ -142,10 +140,13 @@ public class UdpBioAsyncConnection extends AsyncConnection {
} }
@Override @Override
public int read(ByteBuffer dst) throws IOException { public final ReadableByteChannel readableByteChannel() {
int rs = channel.read(dst); return this.channel;
this.readtime = System.currentTimeMillis(); }
return rs;
@Override
public final WritableByteChannel writableByteChannel() {
return this.channel;
} }
@Override @Override
@@ -159,13 +160,6 @@ public class UdpBioAsyncConnection extends AsyncConnection {
} }
} }
@Override
public int write(ByteBuffer src) throws IOException {
int rs = channel.send(src, remoteAddress);
this.writetime = System.currentTimeMillis();
return rs;
}
@Override @Override
public final void close() throws IOException { public final void close() throws IOException {
super.close(); super.close();

View File

@@ -30,6 +30,11 @@ public @interface HttpMapping {
*/ */
int actionid() default 0; int actionid() default 0;
/**
* 请求地址
*
* @return String
*/
String url(); String url();
/** /**
@@ -41,6 +46,13 @@ public @interface HttpMapping {
*/ */
int cacheseconds() default 0; int cacheseconds() default 0;
/**
* 是否只接受RPC请求 默认为false
*
* @return 默认false
*/
boolean rpconly() default false;
/** /**
* 是否鉴权,默认需要鉴权 <br> * 是否鉴权,默认需要鉴权 <br>
* *

View File

@@ -1,5 +1,5 @@
/* /*
* To change this license header, choose License Headers in Project Properties. * To change this license headers, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates * To change this template file, choose Tools | Templates
* and open the template in the editor. * and open the template in the editor.
*/ */
@@ -14,9 +14,9 @@ import java.nio.channels.Channels;
import java.nio.charset.*; import java.nio.charset.*;
import java.util.*; import java.util.*;
import java.util.logging.Level; import java.util.logging.Level;
import org.redkale.convert.ConvertDisabled;
import org.redkale.convert.json.JsonConvert; import org.redkale.convert.json.JsonConvert;
import org.redkale.net.*; import org.redkale.net.*;
import org.redkale.util.AnyValue.DefaultAnyValue;
import org.redkale.util.*; import org.redkale.util.*;
/** /**
@@ -34,39 +34,41 @@ import org.redkale.util.*;
*/ */
public class HttpRequest extends Request<HttpContext> { public class HttpRequest extends Request<HttpContext> {
protected static final Serializable CURRUSERID_NIL = new Serializable() {
};
public static final String SESSIONID_NAME = "JSESSIONID"; public static final String SESSIONID_NAME = "JSESSIONID";
@Comment("Method GET/POST/...") protected boolean rpc;
private String method;
private String protocol; @Comment("Method GET/POST/...")
protected String method;
protected String protocol;
protected String requestURI; protected String requestURI;
private byte[] queryBytes; protected byte[] queryBytes;
private long contentLength = -1; protected long contentLength = -1;
private String contentType; protected String contentType;
private String host; protected String host;
private String connection; protected String connection;
@Comment("原始的cookie字符串解析后值赋给HttpCookie[] cookies") @Comment("原始的cookie字符串解析后值赋给HttpCookie[] cookies")
protected String cookie; protected String cookie;
private HttpCookie[] cookies; protected HttpCookie[] cookies;
protected String newsessionid; protected String newsessionid;
protected final DefaultAnyValue header = new DefaultAnyValue(); //protected final DefaultAnyValue headers = new DefaultAnyValue();
protected final Map<String, String> headers = new HashMap<>();
protected final DefaultAnyValue params = new DefaultAnyValue(); protected final Map<String, String> params = new HashMap<>();
private final ByteArray array = new ByteArray();
private boolean bodyparsed = false;
protected boolean boundary = false; protected boolean boundary = false;
@@ -76,8 +78,17 @@ public class HttpRequest extends Request<HttpContext> {
protected Annotation[] annotations; protected Annotation[] annotations;
// @since 2.1.0
protected Serializable currentUserid = CURRUSERID_NIL;
protected Object currentUser; protected Object currentUser;
protected String remoteAddr;
private final ByteArray array = new ByteArray();
private boolean bodyparsed = false;
private final String remoteAddrHeader; private final String remoteAddrHeader;
Object attachment; //仅供HttpServlet传递Entry使用 Object attachment; //仅供HttpServlet传递Entry使用
@@ -87,6 +98,52 @@ public class HttpRequest extends Request<HttpContext> {
this.remoteAddrHeader = context.remoteAddrHeader; this.remoteAddrHeader = context.remoteAddrHeader;
} }
public HttpRequest(HttpContext context, HttpSimpleRequest req) {
super(context, null);
this.remoteAddrHeader = null;
if (req != null) {
this.rpc = req.rpc;
if (req.getBody() != null) this.array.write(req.getBody());
if (req.getHeaders() != null) this.headers.putAll(req.getHeaders());
if (req.getParams() != null) this.params.putAll(req.getParams());
if (req.getCurrentUserid() != 0) this.currentUserid = req.getCurrentUserid();
this.contentType = req.getContentType();
this.remoteAddr = req.getRemoteAddr();
this.requestURI = req.getRequestURI();
this.method = "POST";
if (req.getSessionid() != null && !req.getSessionid().isEmpty()) {
this.cookies = new HttpCookie[]{new HttpCookie(SESSIONID_NAME, req.getSessionid())};
}
}
}
public HttpSimpleRequest createSimpleRequest(String prefix) {
HttpSimpleRequest req = new HttpSimpleRequest();
req.setBody(array.size() == 0 ? null : array.getBytes());
if (!headers.isEmpty()) {
if (headers.containsKey(Rest.REST_HEADER_RPC_NAME)
|| headers.containsKey(Rest.REST_HEADER_CURRUSERID_NAME)) { //外部request不能包含RPC的header信息
req.setHeaders(new HashMap<>(headers));
req.removeHeader(Rest.REST_HEADER_RPC_NAME);
req.removeHeader(Rest.REST_HEADER_CURRUSERID_NAME);
} else {
req.setHeaders(headers);
}
}
req.setParams(params.isEmpty() ? null : params);
req.setRemoteAddr(getRemoteAddr());
req.setContentType(getContentType());
req.setPath(prefix);
String uri = this.requestURI;
if (prefix != null && !prefix.isEmpty() && uri.startsWith(prefix)) {
uri = uri.substring(prefix.length());
}
req.setRequestURI(uri);
req.setSessionid(getSessionid(false));
req.setRpc(this.rpc);
return req;
}
protected boolean isWebSocket() { protected boolean isWebSocket() {
return connection != null && connection.contains("Upgrade") && "GET".equalsIgnoreCase(method) && "websocket".equalsIgnoreCase(getHeader("Upgrade")); return connection != null && connection.contains("Upgrade") && "GET".equalsIgnoreCase(method) && "websocket".equalsIgnoreCase(getHeader("Upgrade"));
} }
@@ -176,10 +233,18 @@ public class HttpRequest extends Request<HttpContext> {
} }
break; break;
case "user-agent": case "user-agent":
header.addValue("User-Agent", value); headers.put("User-Agent", value);
break;
case Rest.REST_HEADER_RPC_NAME:
this.rpc = "true".equalsIgnoreCase(value);
headers.put(name, value);
break;
case Rest.REST_HEADER_CURRUSERID_NAME:
this.currentUserid = value;
headers.put(name, value);
break; break;
default: default:
header.addValue(name, value); headers.put(name, value);
} }
} }
if (this.contentType != null && this.contentType.contains("boundary=")) this.boundary = true; if (this.contentType != null && this.contentType.contains("boundary=")) this.boundary = true;
@@ -214,10 +279,10 @@ public class HttpRequest extends Request<HttpContext> {
private void parseBody() { private void parseBody() {
if (this.boundary || bodyparsed) return; if (this.boundary || bodyparsed) return;
bodyparsed = true;
if (this.contentType != null && this.contentType.toLowerCase().contains("x-www-form-urlencoded")) { if (this.contentType != null && this.contentType.toLowerCase().contains("x-www-form-urlencoded")) {
addParameter(array, 0, array.size()); addParameter(array, 0, array.size());
} }
bodyparsed = true;
} }
private void addParameter(final ByteArray array, final int offset, final int len) { private void addParameter(final ByteArray array, final int offset, final int len) {
@@ -234,7 +299,7 @@ public class HttpRequest extends Request<HttpContext> {
if (name.charAt(0) == '<') return; //内容可能是xml格式; 如: <?xml version="1.0" if (name.charAt(0) == '<') return; //内容可能是xml格式; 如: <?xml version="1.0"
++keypos; ++keypos;
String value = array.toDecodeString(keypos, (valpos < 0) ? (limit - keypos) : (valpos - keypos), charset); String value = array.toDecodeString(keypos, (valpos < 0) ? (limit - keypos) : (valpos - keypos), charset);
this.params.addValue(name, value); this.params.put(name, value);
if (valpos >= 0) { if (valpos >= 0) {
addParameter(array, valpos + 1, limit - valpos - 1); addParameter(array, valpos + 1, limit - valpos - 1);
} }
@@ -273,6 +338,53 @@ public class HttpRequest extends Request<HttpContext> {
} }
/** /**
* 设置当前用户ID, 通常在HttpServlet.preExecute方法里设置currentUserid <br>
* 数据类型只能是int、long、String、JavaBean
*
* @param <T> 泛型
* @param userid 用户ID
*
* @return HttpRequest
*
* @since 2.1.0
*/
public <T extends Serializable> HttpRequest setCurrentUserid(T userid) {
this.currentUserid = userid;
return this;
}
/**
* 获取当前用户ID<br>
*
* @param <T> 数据类型只能是int、long、String、JavaBean
* @param type 类型
*
* @return 用户ID
*
* @since 2.1.0
*/
@SuppressWarnings("unchecked")
public <T extends Serializable> T currentUserid(Class<T> type) {
if (currentUserid == CURRUSERID_NIL || currentUserid == null) {
if (type == int.class) return (T) (Integer) (int) 0;
if (type == long.class) return (T) (Long) (long) 0;
return null;
}
if (type == int.class) {
if (this.currentUserid instanceof Number) return (T) (Integer) ((Number) this.currentUserid).intValue();
return (T) (Integer) Integer.parseInt(this.currentUserid.toString());
}
if (type == long.class) {
if (this.currentUserid instanceof Number) return (T) (Long) ((Number) this.currentUserid).longValue();
return (T) (Long) Long.parseLong(this.currentUserid.toString());
}
if (type == String.class) return (T) this.currentUserid.toString();
if (this.currentUserid instanceof CharSequence) return JsonConvert.root().convertFrom(type, this.currentUserid.toString());
return (T) this.currentUserid;
}
/**
* 建议使用 setCurrentUserid, 通过userid从Service或缓存中获取用户信息<br>
* 设置当前用户信息, 通常在HttpServlet.preExecute方法里设置currentUser <br> * 设置当前用户信息, 通常在HttpServlet.preExecute方法里设置currentUser <br>
* 数据类型由&#64;HttpUserType指定 * 数据类型由&#64;HttpUserType指定
* *
@@ -281,12 +393,14 @@ public class HttpRequest extends Request<HttpContext> {
* *
* @return HttpRequest * @return HttpRequest
*/ */
@Deprecated
public <T> HttpRequest setCurrentUser(T user) { public <T> HttpRequest setCurrentUser(T user) {
this.currentUser = user; this.currentUser = user;
return this; return this;
} }
/** /**
* 建议使用 currentUserid, 通过userid从Service或缓存中获取用户信息<br>
* 获取当前用户信息<br> * 获取当前用户信息<br>
* 数据类型由&#64;HttpUserType指定 * 数据类型由&#64;HttpUserType指定
* *
@@ -294,6 +408,7 @@ public class HttpRequest extends Request<HttpContext> {
* *
* @return 用户信息 * @return 用户信息
*/ */
@Deprecated
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public <T> T currentUser() { public <T> T currentUser() {
return (T) this.currentUser; return (T) this.currentUser;
@@ -304,6 +419,7 @@ public class HttpRequest extends Request<HttpContext> {
* *
* @return 模块ID * @return 模块ID
*/ */
@ConvertDisabled
public int getModuleid() { public int getModuleid() {
return this.moduleid; return this.moduleid;
} }
@@ -313,6 +429,7 @@ public class HttpRequest extends Request<HttpContext> {
* *
* @return 模块ID * @return 模块ID
*/ */
@ConvertDisabled
public int getActionid() { public int getActionid() {
return this.actionid; return this.actionid;
} }
@@ -322,6 +439,7 @@ public class HttpRequest extends Request<HttpContext> {
* *
* @return Annotation[] * @return Annotation[]
*/ */
@ConvertDisabled
public Annotation[] getAnnotations() { public Annotation[] getAnnotations() {
if (this.annotations == null) return new Annotation[0]; if (this.annotations == null) return new Annotation[0];
Annotation[] newanns = new Annotation[this.annotations.length]; Annotation[] newanns = new Annotation[this.annotations.length];
@@ -371,6 +489,7 @@ public class HttpRequest extends Request<HttpContext> {
* *
* @return 地址 * @return 地址
*/ */
@ConvertDisabled
public SocketAddress getRemoteAddress() { public SocketAddress getRemoteAddress() {
return this.channel == null || !this.channel.isOpen() ? null : this.channel.getRemoteAddress(); return this.channel == null || !this.channel.isOpen() ? null : this.channel.getRemoteAddress();
} }
@@ -382,14 +501,22 @@ public class HttpRequest extends Request<HttpContext> {
* @return 地址 * @return 地址
*/ */
public String getRemoteAddr() { public String getRemoteAddr() {
if (this.remoteAddr != null) return this.remoteAddr;
if (remoteAddrHeader != null) { if (remoteAddrHeader != null) {
String val = getHeader(remoteAddrHeader); String val = getHeader(remoteAddrHeader);
if (val != null) return val; if (val != null) {
this.remoteAddr = val;
return val;
}
} }
SocketAddress addr = getRemoteAddress(); SocketAddress addr = getRemoteAddress();
if (addr == null) return ""; if (addr == null) return "";
if (addr instanceof InetSocketAddress) return ((InetSocketAddress) addr).getAddress().getHostAddress(); if (addr instanceof InetSocketAddress) {
return String.valueOf(addr); this.remoteAddr = ((InetSocketAddress) addr).getAddress().getHostAddress();
return this.remoteAddr;
}
this.remoteAddr = String.valueOf(addr);
return this.remoteAddr;
} }
/** /**
@@ -408,6 +535,7 @@ public class HttpRequest extends Request<HttpContext> {
* *
* @return 内容 * @return 内容
*/ */
@ConvertDisabled
public String getBodyUTF8() { public String getBodyUTF8() {
return array.toString(StandardCharsets.UTF_8); return array.toString(StandardCharsets.UTF_8);
} }
@@ -447,7 +575,7 @@ public class HttpRequest extends Request<HttpContext> {
* @return 内容 * @return 内容
*/ */
public byte[] getBody() { public byte[] getBody() {
return array.getBytes(); return array.size() == 0 ? null : array.getBytes();
} }
/** /**
@@ -455,6 +583,7 @@ public class HttpRequest extends Request<HttpContext> {
* *
* @return body对象 * @return body对象
*/ */
@ConvertDisabled
protected ByteArray getDirectBody() { protected ByteArray getDirectBody() {
return array; return array;
} }
@@ -466,7 +595,20 @@ public class HttpRequest extends Request<HttpContext> {
+ ", \r\n remoteAddr: " + this.getRemoteAddr() + ", \r\n cookies: " + this.cookie + ", \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: " + toMapString(this.params, 4) + ", \r\n header: " + toMapString(this.headers, 4) + "\r\n}"; //this.headers.toString(4)
}
private static CharSequence toMapString(Map<String, String> map, int indent) {
char[] chars = new char[indent];
Arrays.fill(chars, ' ');
final String space = new String(chars);
StringBuilder sb = new StringBuilder();
sb.append("{\r\n");
for (Map.Entry en : map.entrySet()) {
sb.append(space).append(" '").append(en.getKey()).append("': '").append(en.getValue()).append("',\r\n");
}
sb.append(space).append('}');
return sb;
} }
/** /**
@@ -474,9 +616,10 @@ public class HttpRequest extends Request<HttpContext> {
* *
* @return 文件上传对象 * @return 文件上传对象
*/ */
@ConvertDisabled
public final MultiContext getMultiContext() { public final MultiContext getMultiContext() {
return new MultiContext(context.getCharset(), this.getContentType(), this.params, return new MultiContext(context.getCharset(), this.getContentType(), this.params,
new BufferedInputStream(Channels.newInputStream(this.channel), Math.max(array.size(), 8192)) { new BufferedInputStream(Channels.newInputStream(this.channel.readableByteChannel()), Math.max(array.size(), 8192)) {
{ {
array.copyTo(this.buf); array.copyTo(this.buf);
this.count = array.size(); this.count = array.size();
@@ -491,6 +634,7 @@ public class HttpRequest extends Request<HttpContext> {
* *
* @throws IOException IO异常 * @throws IOException IO异常
*/ */
@ConvertDisabled
public final Iterable<MultiPart> multiParts() throws IOException { public final Iterable<MultiPart> multiParts() throws IOException {
return getMultiContext().parts(); return getMultiContext().parts();
} }
@@ -513,11 +657,13 @@ public class HttpRequest extends Request<HttpContext> {
this.moduleid = 0; this.moduleid = 0;
this.actionid = 0; this.actionid = 0;
this.annotations = null; this.annotations = null;
this.currentUserid = CURRUSERID_NIL;
this.currentUser = null; this.currentUser = null;
this.remoteAddr = null;
this.attachment = null; this.attachment = null;
this.header.clear(); this.headers.clear();
this.params.clear(); this.params.clear();
this.array.clear(); this.array.clear();
super.recycle(); super.recycle();
@@ -530,6 +676,7 @@ public class HttpRequest extends Request<HttpContext> {
* *
* @return sessionid * @return sessionid
*/ */
@ConvertDisabled
public String getSessionid(boolean create) { public String getSessionid(boolean create) {
String sessionid = getCookie(SESSIONID_NAME, null); String sessionid = getCookie(SESSIONID_NAME, null);
if (create && (sessionid == null || sessionid.isEmpty())) { if (create && (sessionid == null || sessionid.isEmpty())) {
@@ -575,7 +722,7 @@ public class HttpRequest extends Request<HttpContext> {
*/ */
public HttpCookie[] getCookies() { public HttpCookie[] getCookies() {
if (this.cookies == null) this.cookies = parseCookies(this.cookie); if (this.cookies == null) this.cookies = parseCookies(this.cookie);
return this.cookies; return this.cookies.length == 0 ? null : this.cookies;
} }
/** /**
@@ -598,7 +745,9 @@ public class HttpRequest extends Request<HttpContext> {
* @return cookie值 * @return cookie值
*/ */
public String getCookie(String name, String dfvalue) { public String getCookie(String name, String dfvalue) {
for (HttpCookie c : getCookies()) { HttpCookie[] cs = getCookies();
if (cs == null) return dfvalue;
for (HttpCookie c : cs) {
if (name.equals(c.getName())) return c.getValue(); if (name.equals(c.getName())) return c.getValue();
} }
return dfvalue; return dfvalue;
@@ -697,6 +846,7 @@ public class HttpRequest extends Request<HttpContext> {
* *
* @return String * @return String
*/ */
@ConvertDisabled
public String getRequstURILastPath() { public String getRequstURILastPath() {
if (requestURI == null) return ""; if (requestURI == null) return "";
return requestURI.substring(requestURI.lastIndexOf('/') + 1); return requestURI.substring(requestURI.lastIndexOf('/') + 1);
@@ -1046,8 +1196,8 @@ public class HttpRequest extends Request<HttpContext> {
* *
* @return AnyValue * @return AnyValue
*/ */
public AnyValue getHeaders() { public Map<String, String> getHeaders() {
return header; return headers;
} }
/** /**
@@ -1057,10 +1207,11 @@ public class HttpRequest extends Request<HttpContext> {
* *
* @return Map * @return Map
*/ */
@ConvertDisabled
public Map<String, String> getHeadersToMap(Map<String, String> map) { public Map<String, String> getHeadersToMap(Map<String, String> map) {
if (map == null) map = new LinkedHashMap<>(); if (map == null) map = new LinkedHashMap<>();
final Map<String, String> map0 = map; final Map<String, String> map0 = map;
header.forEach((k, v) -> map0.put(k, v)); headers.forEach((k, v) -> map0.put(k, v));
return map0; return map0;
} }
@@ -1069,8 +1220,10 @@ public class HttpRequest extends Request<HttpContext> {
* *
* @return header名数组 * @return header名数组
*/ */
@ConvertDisabled
public String[] getHeaderNames() { public String[] getHeaderNames() {
return header.getNames(); Set<String> names = headers.keySet();
return names.toArray(new String[names.size()]);
} }
/** /**
@@ -1081,7 +1234,7 @@ public class HttpRequest extends Request<HttpContext> {
* @return header值 * @return header值
*/ */
public String getHeader(String name) { public String getHeader(String name) {
return header.getValue(name); return headers.get(name);
} }
/** /**
@@ -1093,7 +1246,7 @@ public class HttpRequest extends Request<HttpContext> {
* @return header值 * @return header值
*/ */
public String getHeader(String name, String defaultValue) { public String getHeader(String name, String defaultValue) {
return header.getValue(name, defaultValue); return headers.getOrDefault(name, defaultValue);
} }
/** /**
@@ -1134,7 +1287,9 @@ public class HttpRequest extends Request<HttpContext> {
* @return header值 * @return header值
*/ */
public boolean getBooleanHeader(String name, boolean defaultValue) { public boolean getBooleanHeader(String name, boolean defaultValue) {
return header.getBoolValue(name, defaultValue); //return headers.getBoolValue(name, defaultValue);
String value = headers.get(name);
return value == null || value.length() == 0 ? defaultValue : Boolean.parseBoolean(value);
} }
/** /**
@@ -1146,7 +1301,14 @@ public class HttpRequest extends Request<HttpContext> {
* @return header值 * @return header值
*/ */
public short getShortHeader(String name, short defaultValue) { public short getShortHeader(String name, short defaultValue) {
return header.getShortValue(name, defaultValue); //return headers.getShortValue(name, defaultValue);
String value = headers.get(name);
if (value == null || value.length() == 0) return defaultValue;
try {
return Short.decode(value);
} catch (NumberFormatException e) {
return defaultValue;
}
} }
/** /**
@@ -1159,7 +1321,14 @@ public class HttpRequest extends Request<HttpContext> {
* @return header值 * @return header值
*/ */
public short getShortHeader(int radix, String name, short defaultValue) { public short getShortHeader(int radix, String name, short defaultValue) {
return header.getShortValue(name, defaultValue); //return headers.getShortValue(name, defaultValue);
String value = headers.get(name);
if (value == null || value.length() == 0) return defaultValue;
try {
return (radix == 10 ? Short.decode(value) : Short.parseShort(value, radix));
} catch (NumberFormatException e) {
return defaultValue;
}
} }
/** /**
@@ -1171,7 +1340,14 @@ public class HttpRequest extends Request<HttpContext> {
* @return header值 * @return header值
*/ */
public short getShortHeader(String name, int defaultValue) { public short getShortHeader(String name, int defaultValue) {
return header.getShortValue(name, (short) defaultValue); //return headers.getShortValue(name, (short) defaultValue);
String value = headers.get(name);
if (value == null || value.length() == 0) return (short) defaultValue;
try {
return Short.decode(value);
} catch (NumberFormatException e) {
return (short) defaultValue;
}
} }
/** /**
@@ -1184,7 +1360,14 @@ public class HttpRequest extends Request<HttpContext> {
* @return header值 * @return header值
*/ */
public short getShortHeader(int radix, String name, int defaultValue) { public short getShortHeader(int radix, String name, int defaultValue) {
return header.getShortValue(radix, name, (short) defaultValue); //return headers.getShortValue(radix, name, (short) defaultValue);
String value = headers.get(name);
if (value == null || value.length() == 0) return (short) defaultValue;
try {
return (radix == 10 ? Short.decode(value) : Short.parseShort(value, radix));
} catch (NumberFormatException e) {
return (short) defaultValue;
}
} }
/** /**
@@ -1196,7 +1379,14 @@ public class HttpRequest extends Request<HttpContext> {
* @return header值 * @return header值
*/ */
public int getIntHeader(String name, int defaultValue) { public int getIntHeader(String name, int defaultValue) {
return header.getIntValue(name, defaultValue); //return headers.getIntValue(name, defaultValue);
String value = headers.get(name);
if (value == null || value.length() == 0) return defaultValue;
try {
return Integer.parseInt(value);
} catch (NumberFormatException e) {
return defaultValue;
}
} }
/** /**
@@ -1209,7 +1399,14 @@ public class HttpRequest extends Request<HttpContext> {
* @return header值 * @return header值
*/ */
public int getIntHeader(int radix, String name, int defaultValue) { public int getIntHeader(int radix, String name, int defaultValue) {
return header.getIntValue(radix, name, defaultValue); //return headers.getIntValue(radix, name, defaultValue);
String value = headers.get(name);
if (value == null || value.length() == 0) return defaultValue;
try {
return (radix == 10 ? Integer.decode(value) : Integer.parseInt(value, radix));
} catch (NumberFormatException e) {
return defaultValue;
}
} }
/** /**
@@ -1221,7 +1418,14 @@ public class HttpRequest extends Request<HttpContext> {
* @return header值 * @return header值
*/ */
public long getLongHeader(String name, long defaultValue) { public long getLongHeader(String name, long defaultValue) {
return header.getLongValue(name, defaultValue); //return headers.getLongValue(name, defaultValue);
String value = headers.get(name);
if (value == null || value.length() == 0) return defaultValue;
try {
return Long.decode(value);
} catch (NumberFormatException e) {
return defaultValue;
}
} }
/** /**
@@ -1234,7 +1438,14 @@ public class HttpRequest extends Request<HttpContext> {
* @return header值 * @return header值
*/ */
public long getLongHeader(int radix, String name, long defaultValue) { public long getLongHeader(int radix, String name, long defaultValue) {
return header.getLongValue(radix, name, defaultValue); //return headers.getLongValue(radix, name, defaultValue);
String value = headers.get(name);
if (value == null || value.length() == 0) return defaultValue;
try {
return (radix == 10 ? Long.decode(value) : Long.parseLong(value, radix));
} catch (NumberFormatException e) {
return defaultValue;
}
} }
/** /**
@@ -1246,7 +1457,14 @@ public class HttpRequest extends Request<HttpContext> {
* @return header值 * @return header值
*/ */
public float getFloatHeader(String name, float defaultValue) { public float getFloatHeader(String name, float defaultValue) {
return header.getFloatValue(name, defaultValue); //return headers.getFloatValue(name, defaultValue);
String value = headers.get(name);
if (value == null || value.length() == 0) return defaultValue;
try {
return Float.parseFloat(value);
} catch (NumberFormatException e) {
return defaultValue;
}
} }
/** /**
@@ -1258,7 +1476,14 @@ public class HttpRequest extends Request<HttpContext> {
* @return header值 * @return header值
*/ */
public double getDoubleHeader(String name, double defaultValue) { public double getDoubleHeader(String name, double defaultValue) {
return header.getDoubleValue(name, defaultValue); //return headers.getDoubleValue(name, defaultValue);
String value = headers.get(name);
if (value == null || value.length() == 0) return defaultValue;
try {
return Double.parseDouble(value);
} catch (NumberFormatException e) {
return defaultValue;
}
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
@@ -1267,7 +1492,7 @@ public class HttpRequest extends Request<HttpContext> {
* *
* @return AnyValue * @return AnyValue
*/ */
public AnyValue getParameters() { public Map<String, String> getParameters() {
parseBody(); parseBody();
return params; return params;
} }
@@ -1279,6 +1504,7 @@ public class HttpRequest extends Request<HttpContext> {
* *
* @return Map * @return Map
*/ */
@ConvertDisabled
public Map<String, String> getParametersToMap(Map<String, String> map) { public Map<String, String> getParametersToMap(Map<String, String> map) {
if (map == null) map = new LinkedHashMap<>(); if (map == null) map = new LinkedHashMap<>();
final Map<String, String> map0 = map; final Map<String, String> map0 = map;
@@ -1293,6 +1519,7 @@ public class HttpRequest extends Request<HttpContext> {
* *
* @return String * @return String
*/ */
@ConvertDisabled
public String getParametersToString() { public String getParametersToString() {
return getParametersToString(null); return getParametersToString(null);
} }
@@ -1318,9 +1545,11 @@ public class HttpRequest extends Request<HttpContext> {
* *
* @return 参数名数组 * @return 参数名数组
*/ */
@ConvertDisabled
public String[] getParameterNames() { public String[] getParameterNames() {
parseBody(); parseBody();
return params.getNames(); Set<String> names = params.keySet();
return names.toArray(new String[names.size()]);
} }
/** /**
@@ -1332,7 +1561,7 @@ public class HttpRequest extends Request<HttpContext> {
*/ */
public String getParameter(String name) { public String getParameter(String name) {
parseBody(); parseBody();
return params.getValue(name); return params.get(name);
} }
/** /**
@@ -1345,7 +1574,7 @@ public class HttpRequest extends Request<HttpContext> {
*/ */
public String getParameter(String name, String defaultValue) { public String getParameter(String name, String defaultValue) {
parseBody(); parseBody();
return params.getValue(name, defaultValue); return params.getOrDefault(name, defaultValue);
} }
/** /**
@@ -1387,7 +1616,8 @@ public class HttpRequest extends Request<HttpContext> {
*/ */
public boolean getBooleanParameter(String name, boolean defaultValue) { public boolean getBooleanParameter(String name, boolean defaultValue) {
parseBody(); parseBody();
return params.getBoolValue(name, defaultValue); String value = params.get(name);
return value == null || value.length() == 0 ? defaultValue : Boolean.parseBoolean(value);
} }
/** /**
@@ -1400,7 +1630,13 @@ public class HttpRequest extends Request<HttpContext> {
*/ */
public short getShortParameter(String name, short defaultValue) { public short getShortParameter(String name, short defaultValue) {
parseBody(); parseBody();
return params.getShortValue(name, defaultValue); String value = params.get(name);
if (value == null || value.length() == 0) return defaultValue;
try {
return Short.decode(value);
} catch (NumberFormatException e) {
return defaultValue;
}
} }
/** /**
@@ -1414,7 +1650,13 @@ public class HttpRequest extends Request<HttpContext> {
*/ */
public short getShortParameter(int radix, String name, short defaultValue) { public short getShortParameter(int radix, String name, short defaultValue) {
parseBody(); parseBody();
return params.getShortValue(radix, name, defaultValue); String value = params.get(name);
if (value == null || value.length() == 0) return defaultValue;
try {
return (radix == 10 ? Short.decode(value) : Short.parseShort(value, radix));
} catch (NumberFormatException e) {
return defaultValue;
}
} }
/** /**
@@ -1427,7 +1669,13 @@ public class HttpRequest extends Request<HttpContext> {
*/ */
public short getShortParameter(String name, int defaultValue) { public short getShortParameter(String name, int defaultValue) {
parseBody(); parseBody();
return params.getShortValue(name, (short) defaultValue); String value = params.get(name);
if (value == null || value.length() == 0) return (short) defaultValue;
try {
return Short.decode(value);
} catch (NumberFormatException e) {
return (short) defaultValue;
}
} }
/** /**
@@ -1440,7 +1688,13 @@ public class HttpRequest extends Request<HttpContext> {
*/ */
public int getIntParameter(String name, int defaultValue) { public int getIntParameter(String name, int defaultValue) {
parseBody(); parseBody();
return params.getIntValue(name, defaultValue); String value = params.get(name);
if (value == null || value.length() == 0) return defaultValue;
try {
return Integer.decode(value);
} catch (NumberFormatException e) {
return defaultValue;
}
} }
/** /**
@@ -1454,7 +1708,13 @@ public class HttpRequest extends Request<HttpContext> {
*/ */
public int getIntParameter(int radix, String name, int defaultValue) { public int getIntParameter(int radix, String name, int defaultValue) {
parseBody(); parseBody();
return params.getIntValue(radix, name, defaultValue); String value = params.get(name);
if (value == null || value.length() == 0) return defaultValue;
try {
return (radix == 10 ? Integer.decode(value) : Integer.parseInt(value, radix));
} catch (NumberFormatException e) {
return defaultValue;
}
} }
/** /**
@@ -1467,7 +1727,13 @@ public class HttpRequest extends Request<HttpContext> {
*/ */
public long getLongParameter(String name, long defaultValue) { public long getLongParameter(String name, long defaultValue) {
parseBody(); parseBody();
return params.getLongValue(name, defaultValue); String value = params.get(name);
if (value == null || value.length() == 0) return defaultValue;
try {
return Long.decode(value);
} catch (NumberFormatException e) {
return defaultValue;
}
} }
/** /**
@@ -1481,7 +1747,13 @@ public class HttpRequest extends Request<HttpContext> {
*/ */
public long getLongParameter(int radix, String name, long defaultValue) { public long getLongParameter(int radix, String name, long defaultValue) {
parseBody(); parseBody();
return params.getLongValue(radix, name, defaultValue); String value = params.get(name);
if (value == null || value.length() == 0) return defaultValue;
try {
return (radix == 10 ? Long.decode(value) : Long.parseLong(value, radix));
} catch (NumberFormatException e) {
return defaultValue;
}
} }
/** /**
@@ -1494,7 +1766,13 @@ public class HttpRequest extends Request<HttpContext> {
*/ */
public float getFloatParameter(String name, float defaultValue) { public float getFloatParameter(String name, float defaultValue) {
parseBody(); parseBody();
return params.getFloatValue(name, defaultValue); String value = params.get(name);
if (value == null || value.length() == 0) return defaultValue;
try {
return Float.parseFloat(value);
} catch (NumberFormatException e) {
return defaultValue;
}
} }
/** /**
@@ -1507,7 +1785,13 @@ public class HttpRequest extends Request<HttpContext> {
*/ */
public double getDoubleParameter(String name, double defaultValue) { public double getDoubleParameter(String name, double defaultValue) {
parseBody(); parseBody();
return params.getDoubleValue(name, defaultValue); String value = params.get(name);
if (value == null || value.length() == 0) return defaultValue;
try {
return Double.parseDouble(value);
} catch (NumberFormatException e) {
return defaultValue;
}
} }
/** /**

View File

@@ -160,16 +160,16 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
public HttpResponse(HttpContext context, HttpRequest request, ObjectPool<Response> responsePool, HttpResponseConfig config) { public HttpResponse(HttpContext context, HttpRequest request, ObjectPool<Response> responsePool, HttpResponseConfig config) {
super(context, request, responsePool); super(context, request, responsePool);
this.plainContentType = config.plainContentType == null || config.plainContentType.isEmpty() ? "text/plain; charset=utf-8" : config.plainContentType; this.plainContentType = config == null || config.plainContentType == null || config.plainContentType.isEmpty() ? "text/plain; charset=utf-8" : config.plainContentType;
this.jsonContentType = config.jsonContentType == null || config.jsonContentType.isEmpty() ? "application/json; charset=utf-8" : config.jsonContentType; this.jsonContentType = config == null || config.jsonContentType == null || config.jsonContentType.isEmpty() ? "application/json; charset=utf-8" : config.jsonContentType;
this.plainContentTypeBytes = ("Content-Type: " + this.plainContentType + "\r\n").getBytes(); this.plainContentTypeBytes = ("Content-Type: " + this.plainContentType + "\r\n").getBytes();
this.jsonContentTypeBytes = ("Content-Type: " + this.jsonContentType + "\r\n").getBytes(); this.jsonContentTypeBytes = ("Content-Type: " + this.jsonContentType + "\r\n").getBytes();
this.defaultAddHeaders = config.defaultAddHeaders; this.defaultAddHeaders = config == null ? null : config.defaultAddHeaders;
this.defaultSetHeaders = config.defaultSetHeaders; this.defaultSetHeaders = config == null ? null : config.defaultSetHeaders;
this.defaultCookie = config.defaultCookie; this.defaultCookie = config == null ? null : config.defaultCookie;
this.autoOptions = config.autoOptions; this.autoOptions = config == null ? false : config.autoOptions;
this.dateSupplier = config.dateSupplier; this.dateSupplier = config == null ? null : config.dateSupplier;
this.renders = config.renders; this.renders = config == null ? null : config.renders;
this.hasRender = renders != null && !renders.isEmpty(); this.hasRender = renders != null && !renders.isEmpty();
this.onlyoneHttpRender = renders != null && renders.size() == 1 ? renders.get(0) : null; this.onlyoneHttpRender = renders != null && renders.size() == 1 ? renders.get(0) : null;
this.contentType = this.plainContentType; this.contentType = this.plainContentType;
@@ -430,6 +430,26 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
finish(convert.convertTo(getBodyBufferSupplier(), ret)); finish(convert.convertTo(getBodyBufferSupplier(), ret));
} }
/**
* 将HttpResult对象输出
*
* @param convert 指定的Convert
* @param result HttpResult输出对象
*/
public void finish(final Convert convert, HttpResult result) {
if (result.getContentType() != null) setContentType(result.getContentType());
addHeader(result.getHeaders()).addCookie(result.getCookies()).setStatus(result.getStatus() < 1 ? 200 : result.getStatus());
if (result.getResult() == null) {
finish("");
} else if (result.getResult() instanceof CharSequence) {
finish(result.getResult().toString());
} else {
Convert cc = result.convert();
if (cc == null || !(cc instanceof TextConvert)) cc = convert;
finish(cc, result.getResult());
}
}
/** /**
* 将CompletableFuture的结果对象以JSON格式输出 * 将CompletableFuture的结果对象以JSON格式输出
* *
@@ -521,18 +541,7 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
} else if (obj instanceof org.redkale.service.RetResult) { } else if (obj instanceof org.redkale.service.RetResult) {
finishJson((org.redkale.service.RetResult) obj); finishJson((org.redkale.service.RetResult) obj);
} else if (obj instanceof HttpResult) { } else if (obj instanceof HttpResult) {
HttpResult result = (HttpResult) obj; finish(convert, (HttpResult) obj);
if (result.getContentType() != null) setContentType(result.getContentType());
addHeader(result.getHeaders()).addCookie(result.getCookies()).setStatus(result.getStatus() < 1 ? 200 : result.getStatus());
if (result.getResult() == null) {
finish("");
} else if (result.getResult() instanceof CharSequence) {
finish(result.getResult().toString());
} else {
Convert cc = result.convert();
if (cc == null || !(cc instanceof TextConvert)) cc = convert;
finish(cc, result.getResult());
}
} else { } else {
if (hasRender) { if (hasRender) {
if (onlyoneHttpRender != null) { if (onlyoneHttpRender != null) {
@@ -566,6 +575,10 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
this.header.addValue("retcode", String.valueOf(ret.getRetcode())).addValue("retinfo", ret.getRetinfo()); this.header.addValue("retcode", String.valueOf(ret.getRetcode())).addValue("retinfo", ret.getRetinfo());
} }
} }
if (this.channel == null) { //虚拟的HttpResponse
finish(type == null ? convert.convertToBytes(obj) : convert.convertToBytes(type, obj));
return;
}
ByteBuffer[] buffers = type == null ? convert.convertTo(getBodyBufferSupplier(), obj) ByteBuffer[] buffers = type == null ? convert.convertTo(getBodyBufferSupplier(), obj)
: convert.convertTo(getBodyBufferSupplier(), type, obj); : convert.convertTo(getBodyBufferSupplier(), type, obj);
finish(buffers); finish(buffers);
@@ -1013,7 +1026,7 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
if (defaultCookie.getDomain() != null && cookie.getDomain() == null) cookie.setDomain(defaultCookie.getDomain()); if (defaultCookie.getDomain() != null && cookie.getDomain() == null) cookie.setDomain(defaultCookie.getDomain());
if (defaultCookie.getPath() != null && cookie.getPath() == null) cookie.setPath(defaultCookie.getPath()); if (defaultCookie.getPath() != null && cookie.getPath() == null) cookie.setPath(defaultCookie.getPath());
} }
buffer.put(("Set-Cookie: " + genString(cookie) + "\r\n").getBytes()); buffer.put(("Set-Cookie: " + cookieString(cookie) + "\r\n").getBytes());
} }
} }
buffer.put(LINE); buffer.put(LINE);
@@ -1022,7 +1035,7 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
return buffer; return buffer;
} }
private CharSequence genString(HttpCookie cookie) { private CharSequence cookieString(HttpCookie cookie) {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
sb.append(cookie.getName()).append("=").append(cookie.getValue()).append("; Version=1"); sb.append(cookie.getName()).append("=").append(cookie.getValue()).append("; Version=1");
if (cookie.getDomain() != null) sb.append("; Domain=").append(cookie.getDomain()); if (cookie.getDomain() != null) sb.append("; Domain=").append(cookie.getDomain());

View File

@@ -7,7 +7,9 @@ package org.redkale.net.http;
import java.io.Serializable; import java.io.Serializable;
import java.net.HttpCookie; import java.net.HttpCookie;
import java.nio.charset.StandardCharsets;
import java.util.*; import java.util.*;
import java.util.concurrent.CompletableFuture;
import org.redkale.convert.*; import org.redkale.convert.*;
import org.redkale.convert.json.JsonConvert; import org.redkale.convert.json.JsonConvert;
@@ -23,17 +25,20 @@ public class HttpResult<T> {
public static final String SESSIONID_COOKIENAME = HttpRequest.SESSIONID_NAME; public static final String SESSIONID_COOKIENAME = HttpRequest.SESSIONID_NAME;
protected Map<String, String> headers; @ConvertColumn(index = 1)
protected List<HttpCookie> cookies;
protected String contentType;
protected T result;
protected int status = 0; //不设置则为 200 protected int status = 0; //不设置则为 200
protected String message; @ConvertColumn(index = 2)
protected String contentType;
@ConvertColumn(index = 3)
protected Map<String, String> headers;
@ConvertColumn(index = 4)
protected List<HttpCookie> cookies;
@ConvertColumn(index = 5)
protected T result;
protected Convert convert; protected Convert convert;
@@ -85,17 +90,29 @@ public class HttpResult<T> {
return this; return this;
} }
public HttpResult<T> message(String message) {
this.message = message;
return this;
}
public Convert convert() { public Convert convert() {
return convert; return convert;
} }
public void convert(Convert convert) { public HttpResult<T> convert(Convert convert) {
this.convert = convert; this.convert = convert;
return this;
}
public String getHeader(String name) {
return headers == null ? null : headers.get(name);
}
public String getHeader(String name, String dfvalue) {
return headers == null ? null : headers.getOrDefault(name, dfvalue);
}
public CompletableFuture<HttpResult<T>> toFuture() {
return CompletableFuture.completedFuture(this);
}
public CompletableFuture toAnyFuture() {
return CompletableFuture.completedFuture(this);
} }
public Map<String, String> getHeaders() { public Map<String, String> getHeaders() {
@@ -138,16 +155,17 @@ public class HttpResult<T> {
this.status = status; this.status = status;
} }
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
@Override @Override
public String toString() { public String toString() {
if (this.result instanceof byte[]) {
HttpResult tmp = new HttpResult();
tmp.contentType = this.contentType;
tmp.cookies = this.cookies;
tmp.headers = this.headers;
tmp.status = this.status;
tmp.result = new String((byte[]) this.result, StandardCharsets.UTF_8);
return JsonConvert.root().convertTo(tmp);
}
return JsonConvert.root().convertTo(this); return JsonConvert.root().convertTo(this);
} }
} }

View File

@@ -16,6 +16,7 @@ import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Supplier; import java.util.function.Supplier;
import java.util.logging.Level; import java.util.logging.Level;
import org.redkale.mq.MessageAgent;
import org.redkale.net.*; import org.redkale.net.*;
import org.redkale.net.http.HttpContext.HttpContextConfig; import org.redkale.net.http.HttpContext.HttpContextConfig;
import org.redkale.net.http.HttpResponse.HttpResponseConfig; import org.redkale.net.http.HttpResponse.HttpResponseConfig;
@@ -215,13 +216,14 @@ public class HttpServer extends Server<String, HttpContext, HttpRequest, HttpRes
* @param <T> HttpServlet * @param <T> HttpServlet
* @param classLoader ClassLoader * @param classLoader ClassLoader
* @param webSocketType WebSocket的类型 * @param webSocketType WebSocket的类型
* @param messageAgent MessageAgent
* @param prefix url前缀 * @param prefix url前缀
* @param conf 配置信息 * @param conf 配置信息
* *
* @return RestServlet * @return RestServlet
*/ */
public <S extends WebSocket, T extends HttpServlet> T addRestWebSocketServlet(final ClassLoader classLoader, final Class<S> webSocketType, final String prefix, final AnyValue conf) { public <S extends WebSocket, T extends WebSocketServlet> T addRestWebSocketServlet(final ClassLoader classLoader, final Class<S> webSocketType, MessageAgent messageAgent, final String prefix, final AnyValue conf) {
T servlet = Rest.createRestWebSocketServlet(classLoader, webSocketType); T servlet = Rest.createRestWebSocketServlet(classLoader, webSocketType, messageAgent);
if (servlet != null) this.prepare.addServlet(servlet, prefix, conf); if (servlet != null) this.prepare.addServlet(servlet, prefix, conf);
return servlet; return servlet;
} }
@@ -434,7 +436,7 @@ public class HttpServer extends Server<String, HttpContext, HttpRequest, HttpRes
final HttpContextConfig contextConfig = new HttpContextConfig(); final HttpContextConfig contextConfig = new HttpContextConfig();
contextConfig.serverStartTime = this.serverStartTime; contextConfig.serverStartTime = this.serverStartTime;
contextConfig.logger = this.logger; contextConfig.logger = this.logger;
contextConfig.executor = this.executor; contextConfig.executor = this.workExecutor;
contextConfig.sslContext = this.sslContext; contextConfig.sslContext = this.sslContext;
contextConfig.bufferCapacity = this.bufferCapacity; contextConfig.bufferCapacity = this.bufferCapacity;
contextConfig.maxconns = this.maxconns; contextConfig.maxconns = this.maxconns;

View File

@@ -44,9 +44,13 @@ public class HttpServlet extends Servlet<HttpContext, HttpRequest, HttpResponse>
@Override @Override
public void execute(HttpRequest request, HttpResponse response) throws IOException { public void execute(HttpRequest request, HttpResponse response) throws IOException {
InnerActionEntry entry = (InnerActionEntry) request.attachment; InnerActionEntry entry = (InnerActionEntry) request.attachment;
if (entry.rpconly && !request.rpc) {
response.finish(503, null);
return;
}
if (entry.cacheseconds > 0) {//有缓存设置 if (entry.cacheseconds > 0) {//有缓存设置
CacheEntry ce = entry.cache.get(request.getRequestURI()); CacheEntry ce = entry.cache.get(request.getRequestURI());
if (ce != null && ce.time + entry.cacheseconds > System.currentTimeMillis()) { //缓存有效 if (ce != null && ce.time + entry.cacheseconds * 1000 > System.currentTimeMillis()) { //缓存有效
response.setStatus(ce.status); response.setStatus(ce.status);
response.setContentType(ce.contentType); response.setContentType(ce.contentType);
response.finish(ce.getBuffers()); response.finish(ce.getBuffers());
@@ -82,7 +86,8 @@ public class HttpServlet extends Servlet<HttpContext, HttpRequest, HttpResponse>
return; return;
} }
} }
throw new IOException(this.getClass().getName() + " not found method for URI(" + request.getRequestURI() + ")"); response.finish404();
//throw new IOException(this.getClass().getName() + " not found method for URI(" + request.getRequestURI() + ")");
} }
}; };
@@ -215,18 +220,19 @@ public class HttpServlet extends Servlet<HttpContext, HttpRequest, HttpResponse>
protected static final class InnerActionEntry { protected static final class InnerActionEntry {
InnerActionEntry(int moduleid, int actionid, String name, String[] methods, Method method, HttpServlet servlet) { InnerActionEntry(int moduleid, int actionid, String name, String[] methods, Method method, HttpServlet servlet) {
this(moduleid, actionid, name, methods, method, auth(method), cacheseconds(method), servlet); this(moduleid, actionid, name, methods, method, rpconly(method), auth(method), cacheseconds(method), servlet);
this.annotations = annotations(method); this.annotations = annotations(method);
} }
//供Rest类使用参数不能随便更改 //供Rest类使用参数不能随便更改
public InnerActionEntry(int moduleid, int actionid, String name, String[] methods, Method method, boolean auth, int cacheseconds, HttpServlet servlet) { public InnerActionEntry(int moduleid, int actionid, String name, String[] methods, Method method, boolean rpconly, boolean auth, int cacheseconds, HttpServlet servlet) {
this.moduleid = moduleid; this.moduleid = moduleid;
this.actionid = actionid; this.actionid = actionid;
this.name = name; this.name = name;
this.methods = methods; this.methods = methods;
this.method = method; //rest构建会为null this.method = method; //rest构建会为null
this.servlet = servlet; this.servlet = servlet;
this.rpconly = rpconly;
this.auth = auth; this.auth = auth;
this.cacheseconds = cacheseconds; this.cacheseconds = cacheseconds;
this.cache = cacheseconds > 0 ? new ConcurrentHashMap<>() : null; this.cache = cacheseconds > 0 ? new ConcurrentHashMap<>() : null;
@@ -244,6 +250,11 @@ public class HttpServlet extends Servlet<HttpContext, HttpRequest, HttpResponse>
return mapping == null || mapping.auth(); return mapping == null || mapping.auth();
} }
protected static boolean rpconly(Method method) {
HttpMapping mapping = method.getAnnotation(HttpMapping.class);
return mapping == null || mapping.rpconly();
}
protected static int cacheseconds(Method method) { protected static int cacheseconds(Method method) {
HttpMapping mapping = method.getAnnotation(HttpMapping.class); HttpMapping mapping = method.getAnnotation(HttpMapping.class);
return mapping == null ? 0 : mapping.cacheseconds(); return mapping == null ? 0 : mapping.cacheseconds();
@@ -272,6 +283,8 @@ public class HttpServlet extends Servlet<HttpContext, HttpRequest, HttpResponse>
final int cacheseconds; final int cacheseconds;
final boolean rpconly;
final boolean auth; final boolean auth;
final int moduleid; final int moduleid;

View File

@@ -0,0 +1,317 @@
/*
* 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.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.concurrent.atomic.AtomicBoolean;
import org.redkale.convert.*;
import org.redkale.convert.json.JsonConvert;
import org.redkale.util.*;
/**
* HttpRequest的缩减版, 只提供部分字段
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*
* @since 2.1.0
*/
public class HttpSimpleRequest implements java.io.Serializable {
@ConvertColumn(index = 1)
@Comment("是否RPC请求, 该类通常是为RPC创建的故默认是true")
protected boolean rpc = true;
@ConvertColumn(index = 2)
@Comment("请求的URI")
protected String requestURI;
@ConvertColumn(index = 3)
@Comment("请求的前缀")
protected String path;
@ConvertColumn(index = 4)
@Comment("客户端IP")
protected String remoteAddr;
@ConvertColumn(index = 5)
@Comment("会话ID")
protected String sessionid;
@ConvertColumn(index = 6)
@Comment("Content-Type")
protected String contentType;
@ConvertColumn(index = 7)
protected int currentUserid;
@ConvertColumn(index = 8)
@Comment("http header信息")
protected Map<String, String> headers;
@ConvertColumn(index = 9)
@Comment("参数信息")
protected Map<String, String> params;
@ConvertColumn(index = 10)
@Comment("http body信息")
protected byte[] body; //对应HttpRequest.array
public static HttpSimpleRequest create(String requestURI) {
return new HttpSimpleRequest().requestURI(requestURI);
}
public static HttpSimpleRequest create(String requestURI, Object... params) {
HttpSimpleRequest req = new HttpSimpleRequest().requestURI(requestURI);
int len = params.length / 2;
for (int i = 0; i < len; i++) {
req.param(params[i * 2].toString(), params[i * 2 + 1]);
}
return req;
}
@ConvertDisabled
public String getParametersToString() {
if (this.params == null || this.params.isEmpty()) return null;
final StringBuilder sb = new StringBuilder();
AtomicBoolean no2 = new AtomicBoolean(false);
this.params.forEach((n, v) -> {
if (no2.get()) sb.append('&');
sb.append(n).append('=').append(URLEncoder.encode(v, StandardCharsets.UTF_8));
no2.set(true);
});
return sb.toString();
}
public HttpSimpleRequest rpc(boolean rpc) {
this.rpc = rpc;
return this;
}
public HttpSimpleRequest requestURI(String requestURI) {
this.requestURI = requestURI;
return this;
}
public HttpSimpleRequest path(String path) {
this.path = path;
return this;
}
public HttpSimpleRequest remoteAddr(String remoteAddr) {
this.remoteAddr = remoteAddr;
return this;
}
public HttpSimpleRequest sessionid(String sessionid) {
this.sessionid = sessionid;
return this;
}
public HttpSimpleRequest contentType(String contentType) {
this.contentType = contentType;
return this;
}
public HttpSimpleRequest currentUserid(int userid) {
this.currentUserid = userid;
return this;
}
public HttpSimpleRequest removeHeader(String name) {
if (this.headers != null) this.headers.remove(name);
return this;
}
public HttpSimpleRequest removeParam(String name) {
if (this.params != null) this.params.remove(name);
return this;
}
public HttpSimpleRequest headers(Map<String, String> headers) {
this.headers = headers;
return this;
}
public HttpSimpleRequest params(Map<String, String> params) {
this.params = params;
return this;
}
public HttpSimpleRequest header(String key, String value) {
if (this.headers == null) this.headers = new HashMap<>();
this.headers.put(key, value);
return this;
}
public HttpSimpleRequest header(String key, JsonConvert convert, Object value) {
if (value == null) return this;
if (this.headers == null) this.headers = new HashMap<>();
if (convert == null) convert = JsonConvert.root();
this.headers.put(key, convert.convertTo(value));
return this;
}
public HttpSimpleRequest header(String key, Object value) {
if (value == null) return this;
if (this.headers == null) this.headers = new HashMap<>();
this.headers.put(key, JsonConvert.root().convertTo(value));
return this;
}
public HttpSimpleRequest header(String key, int value) {
if (this.headers == null) this.headers = new HashMap<>();
this.headers.put(key, String.valueOf(value));
return this;
}
public HttpSimpleRequest header(String key, long value) {
if (this.headers == null) this.headers = new HashMap<>();
this.headers.put(key, String.valueOf(value));
return this;
}
public HttpSimpleRequest param(String key, String value) {
if (this.params == null) this.params = new HashMap<>();
this.params.put(key, value);
return this;
}
public HttpSimpleRequest param(String key, JsonConvert convert, Object value) {
if (value == null) return this;
if (this.params == null) this.params = new HashMap<>();
if (convert == null) convert = JsonConvert.root();
this.params.put(key, convert.convertTo(value));
return this;
}
public HttpSimpleRequest param(String key, Object value) {
if (value == null) return this;
if (this.params == null) this.params = new HashMap<>();
this.params.put(key, value instanceof CharSequence ? value.toString() : JsonConvert.root().convertTo(value));
return this;
}
public HttpSimpleRequest body(byte[] body) {
this.body = body;
return this;
}
public HttpSimpleRequest clearParams() {
this.params = null;
return this;
}
public HttpSimpleRequest clearHeaders() {
this.headers = null;
return this;
}
public HttpSimpleRequest clearRemoteAddr() {
this.remoteAddr = null;
return this;
}
public HttpSimpleRequest clearSessionid() {
this.sessionid = null;
return this;
}
public HttpSimpleRequest clearContentType() {
this.contentType = null;
return this;
}
public boolean isRpc() {
return rpc;
}
public void setRpc(boolean rpc) {
this.rpc = rpc;
}
public String getRequestURI() {
return requestURI;
}
public void setRequestURI(String requestURI) {
this.requestURI = requestURI;
}
public String getPath() {
return path;
}
public void setPath(String path) {
this.path = path;
}
public String getSessionid() {
return sessionid;
}
public void setSessionid(String sessionid) {
this.sessionid = sessionid;
}
public String getRemoteAddr() {
return remoteAddr;
}
public void setRemoteAddr(String remoteAddr) {
this.remoteAddr = remoteAddr;
}
public int getCurrentUserid() {
return currentUserid;
}
public void setCurrentUserid(int currentUserid) {
this.currentUserid = currentUserid;
}
public String getContentType() {
return contentType;
}
public void setContentType(String contentType) {
this.contentType = contentType;
}
public Map<String, String> getHeaders() {
return headers;
}
public void setHeaders(Map<String, String> headers) {
this.headers = headers;
}
public Map<String, String> getParams() {
return params;
}
public void setParams(Map<String, String> params) {
this.params = params;
}
public byte[] getBody() {
return body;
}
public void setBody(byte[] body) {
this.body = body;
}
@Override
public String toString() {
return JsonConvert.root().convertTo(this);
}
}

View File

@@ -12,7 +12,6 @@ import java.util.*;
import java.util.concurrent.atomic.*; import java.util.concurrent.atomic.*;
import java.util.logging.*; import java.util.logging.*;
import java.util.regex.*; import java.util.regex.*;
import org.redkale.util.AnyValue.DefaultAnyValue;
/** /**
* HTTP的文件上传请求的上下文对象 * HTTP的文件上传请求的上下文对象
@@ -38,7 +37,7 @@ public final class MultiContext {
private final ByteArray buf = new ByteArray(64); private final ByteArray buf = new ByteArray(64);
private final DefaultAnyValue parameters; private final Map<String, String> parameters;
private final Pattern fielnamePattern; private final Pattern fielnamePattern;
@@ -55,7 +54,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 Map<String, String> params, final InputStream in, String fielnameRegex) {
this.charset = charsetName == null ? StandardCharsets.UTF_8 : charsetName; this.charset = charsetName == null ? StandardCharsets.UTF_8 : charsetName;
this.contentType = contentType == null ? "" : contentType.trim(); this.contentType = contentType == null ? "" : contentType.trim();
this.parameters = params; this.parameters = params;
@@ -205,7 +204,7 @@ public final class MultiContext {
final byte[] boundarray = ("\n" + boundarystr).getBytes(); final byte[] boundarray = ("\n" + boundarystr).getBytes();
final byte[] buffer = new byte[boundarray.length]; final byte[] buffer = new byte[boundarray.length];
final InputStream input = this.in; final InputStream input = this.in;
final DefaultAnyValue params = this.parameters; final Map<String, String> params = this.parameters;
final AtomicBoolean finaled = new AtomicBoolean(false); final AtomicBoolean finaled = new AtomicBoolean(false);
return () -> new Iterator<MultiPart>() { return () -> new Iterator<MultiPart>() {
@@ -305,7 +304,7 @@ public final class MultiContext {
return true; return true;
} else { //不是文件 } else { //不是文件
readLine(); //读掉空白 readLine(); //读掉空白
params.addValue(parseValue(disposition, "name"), readLine()); params.put(parseValue(disposition, "name"), readLine());
this.boundaryline = null; this.boundaryline = null;
this.lastentry = null; this.lastentry = null;
return this.hasNext(); return this.hasNext();

View File

@@ -22,7 +22,9 @@ import static org.redkale.asm.Opcodes.*;
import org.redkale.asm.Type; import org.redkale.asm.Type;
import org.redkale.convert.*; import org.redkale.convert.*;
import org.redkale.convert.json.*; import org.redkale.convert.json.*;
import org.redkale.mq.*;
import org.redkale.net.Cryptor; import org.redkale.net.Cryptor;
import org.redkale.net.sncp.Sncp;
import org.redkale.service.*; import org.redkale.service.*;
import org.redkale.util.*; import org.redkale.util.*;
import org.redkale.source.Flipper; import org.redkale.source.Flipper;
@@ -38,6 +40,10 @@ public final class Rest {
public static final String REST_HEADER_RESOURCE_NAME = "rest-resource-name"; public static final String REST_HEADER_RESOURCE_NAME = "rest-resource-name";
public static final String REST_HEADER_RPC_NAME = "rest-rpc-name";
public static final String REST_HEADER_CURRUSERID_NAME = "rest-curruserid-name";
static final String REST_SERVICE_FIELD_NAME = "_redkale_service"; static final String REST_SERVICE_FIELD_NAME = "_redkale_service";
static final String REST_TOSTRINGOBJ_FIELD_NAME = "_redkale_tostringsupplier"; static final String REST_TOSTRINGOBJ_FIELD_NAME = "_redkale_tostringsupplier";
@@ -48,6 +54,9 @@ public final class Rest {
private static final String REST_PARAMTYPES_FIELD_NAME = "_redkale_paramtypes"; //存在泛型的参数数组 Type[][] 第1维度是方法的下标 第二维度是参数的下标 private static final String REST_PARAMTYPES_FIELD_NAME = "_redkale_paramtypes"; //存在泛型的参数数组 Type[][] 第1维度是方法的下标 第二维度是参数的下标
private static final java.lang.reflect.Type TYPE_MAP_STRING_STRING = new TypeToken<Map<String, String>>() {
}.getType();
private static final Set<String> EXCLUDERMETHODS = new HashSet<>(); private static final Set<String> EXCLUDERMETHODS = new HashSet<>();
static { static {
@@ -205,7 +214,19 @@ public final class Rest {
} }
} }
public static <T extends HttpServlet> T createRestWebSocketServlet(final ClassLoader classLoader, final Class<? extends WebSocket> webSocketType) { public static String getRestModule(Service service) {
final RestService controller = service.getClass().getAnnotation(RestService.class);
if (controller != null && !controller.name().isEmpty()) return controller.name();
final Class serviceType = Sncp.getServiceType(service);
return serviceType.getSimpleName().replaceAll("Service.*$", "").toLowerCase();
}
//仅供Rest动态构建里 currentUserid() 使用
public static <T> T orElse(T t, T defValue) {
return t == null ? defValue : t;
}
public static <T extends WebSocketServlet> T createRestWebSocketServlet(final ClassLoader classLoader, final Class<? extends WebSocket> webSocketType, MessageAgent messageAgent) {
if (webSocketType == null) throw new RuntimeException("Rest WebSocket Class is null on createRestWebSocketServlet"); if (webSocketType == null) throw new RuntimeException("Rest WebSocket Class is null on createRestWebSocketServlet");
if (Modifier.isAbstract(webSocketType.getModifiers())) throw new RuntimeException("Rest WebSocket Class(" + webSocketType + ") cannot abstract on createRestWebSocketServlet"); if (Modifier.isAbstract(webSocketType.getModifiers())) throw new RuntimeException("Rest WebSocket Class(" + webSocketType + ") cannot abstract on createRestWebSocketServlet");
if (Modifier.isFinal(webSocketType.getModifiers())) throw new RuntimeException("Rest WebSocket Class(" + webSocketType + ") cannot final on createRestWebSocketServlet"); if (Modifier.isFinal(webSocketType.getModifiers())) throw new RuntimeException("Rest WebSocket Class(" + webSocketType + ") cannot final on createRestWebSocketServlet");
@@ -713,6 +734,7 @@ public final class Rest {
cryptorField.setAccessible(true); cryptorField.setAccessible(true);
cryptorField.set(servlet, cryptor); cryptorField.set(servlet, cryptor);
} }
if (messageAgent != null) ((WebSocketServlet) servlet).messageAgent = messageAgent;
return servlet; return servlet;
} catch (Exception e) { } catch (Exception e) {
throw new RuntimeException(e); throw new RuntimeException(e);
@@ -726,6 +748,7 @@ public final class Rest {
if (!java.lang.reflect.Modifier.isPublic(mod)) throw new RuntimeException(baseServletType + " is not Public Class on createRestServlet"); if (!java.lang.reflect.Modifier.isPublic(mod)) throw new RuntimeException(baseServletType + " is not Public Class on createRestServlet");
if (java.lang.reflect.Modifier.isAbstract(mod)) throw new RuntimeException(baseServletType + " cannot a abstract Class on createRestServlet"); if (java.lang.reflect.Modifier.isAbstract(mod)) throw new RuntimeException(baseServletType + " cannot a abstract Class on createRestServlet");
final String restInternalName = Type.getInternalName(Rest.class);
final String serviceDesc = Type.getDescriptor(serviceType); final String serviceDesc = Type.getDescriptor(serviceType);
final String webServletDesc = Type.getDescriptor(WebServlet.class); final String webServletDesc = Type.getDescriptor(WebServlet.class);
final String resDesc = Type.getDescriptor(Resource.class); final String resDesc = Type.getDescriptor(Resource.class);
@@ -762,6 +785,7 @@ public final class Rest {
final String supDynName = baseServletType.getName().replace('.', '/'); final String supDynName = baseServletType.getName().replace('.', '/');
final RestService controller = serviceType.getAnnotation(RestService.class); final RestService controller = serviceType.getAnnotation(RestService.class);
if (controller != null && controller.ignore()) throw new RuntimeException(serviceType + " is ignore Rest Service Class"); //标记为ignore=true不创建Servlet if (controller != null && controller.ignore()) throw new RuntimeException(serviceType + " is ignore Rest Service Class"); //标记为ignore=true不创建Servlet
final boolean serrpconly = controller != null && controller.rpconly();
ClassLoader loader = classLoader == null ? Thread.currentThread().getContextClassLoader() : classLoader; ClassLoader loader = classLoader == null ? Thread.currentThread().getContextClassLoader() : classLoader;
String stname = serviceType.getSimpleName(); String stname = serviceType.getSimpleName();
if (stname.startsWith("Service")) { //类似ServiceWatchService这样的类保留第一个Service字样 if (stname.startsWith("Service")) { //类似ServiceWatchService这样的类保留第一个Service字样
@@ -800,13 +824,19 @@ public final class Rest {
//获取所有可以转换成HttpMapping的方法 //获取所有可以转换成HttpMapping的方法
int methodidex = 0; int methodidex = 0;
final List<java.lang.reflect.Type[]> paramtypes = new ArrayList<>(); final List<java.lang.reflect.Type[]> paramtypes = new ArrayList<>();
final MessageMultiConsumer mmc = serviceType.getAnnotation(MessageMultiConsumer.class);
if (mmc != null && (mmc.module() == null || mmc.module().isEmpty())) {
throw new RuntimeException("@" + MessageMultiConsumer.class.getSimpleName() + ".module can not empty in " + serviceType.getName());
}
for (final Method method : serviceType.getMethods()) { for (final Method method : serviceType.getMethods()) {
if (Modifier.isStatic(method.getModifiers())) continue; if (Modifier.isStatic(method.getModifiers())) continue;
if (method.isSynthetic()) continue; if (method.isSynthetic()) continue;
if (EXCLUDERMETHODS.contains(method.getName())) continue; if (EXCLUDERMETHODS.contains(method.getName())) continue;
if (method.getParameterCount() == 1 && method.getParameterTypes()[0] == AnyValue.class) {
if ("init".equals(method.getName())) continue; if ("init".equals(method.getName())) continue;
if ("stop".equals(method.getName())) continue;
if ("destroy".equals(method.getName())) continue; if ("destroy".equals(method.getName())) continue;
if ("version".equals(method.getName())) continue; }
if (controller == null) continue; if (controller == null) continue;
RestMapping[] mappings = method.getAnnotationsByType(RestMapping.class); RestMapping[] mappings = method.getAnnotationsByType(RestMapping.class);
@@ -828,14 +858,17 @@ public final class Rest {
} }
} }
} }
if (mmc != null && method.getReturnType() != void.class) {
throw new RuntimeException("@" + RestMapping.class.getSimpleName() + " only for method(" + method + ") with return void by @" + MessageMultiConsumer.class.getSimpleName() + " Service");
}
paramtypes.add(TypeToken.getGenericType(method.getGenericParameterTypes(), serviceType)); paramtypes.add(TypeToken.getGenericType(method.getGenericParameterTypes(), serviceType));
if (mappings.length == 0) { //没有Mapping设置一个默认值 if (mappings.length == 0) { //没有Mapping设置一个默认值
MappingEntry entry = new MappingEntry(methodidex, null, bigmodulename, method); MappingEntry entry = new MappingEntry(serrpconly, methodidex, null, bigmodulename, method);
if (entrys.contains(entry)) throw new RuntimeException(serviceType.getName() + " on " + method.getName() + " 's mapping(" + entry.name + ") is repeat"); if (entrys.contains(entry)) throw new RuntimeException(serviceType.getName() + " on " + method.getName() + " 's mapping(" + entry.name + ") is repeat");
entrys.add(entry); entrys.add(entry);
} else { } else {
for (RestMapping mapping : mappings) { for (RestMapping mapping : mappings) {
MappingEntry entry = new MappingEntry(methodidex, mapping, defmodulename, method); MappingEntry entry = new MappingEntry(serrpconly, methodidex, mapping, defmodulename, method);
if (entrys.contains(entry)) throw new RuntimeException(serviceType.getName() + " on " + method.getName() + " 's mapping(" + entry.name + ") is repeat"); if (entrys.contains(entry)) throw new RuntimeException(serviceType.getName() + " on " + method.getName() + " 's mapping(" + entry.name + ") is repeat");
entrys.add(entry); entrys.add(entry);
} }
@@ -1067,6 +1100,31 @@ public final class Rest {
comment = annuri.comment(); comment = annuri.comment();
} }
RestUserid userid = param.getAnnotation(RestUserid.class);
if (userid != null) {
if (annhead != null) throw new RuntimeException("@RestUserid and @RestHeader cannot on the same Parameter in " + method);
if (anncookie != null) throw new RuntimeException("@RestUserid and @RestCookie cannot on the same Parameter in " + method);
if (annsid != null) throw new RuntimeException("@RestUserid and @RestSessionid cannot on the same Parameter in " + method);
if (annaddr != null) throw new RuntimeException("@RestUserid and @RestAddress cannot on the same Parameter in " + method);
if (annbody != null) throw new RuntimeException("@RestUserid and @RestBody cannot on the same Parameter in " + method);
if (annfile != null) throw new RuntimeException("@RestUserid and @RestUploadFile cannot on the same Parameter in " + method);
if (!ptype.isPrimitive() && !java.io.Serializable.class.isAssignableFrom(ptype)) throw new RuntimeException("@RestUserid must on java.io.Serializable Parameter in " + method);
comment = "";
}
RestHeaders annheaders = param.getAnnotation(RestHeaders.class);
if (annheaders != null) {
if (annhead != null) throw new RuntimeException("@RestHeaders and @RestHeader cannot on the same Parameter in " + method);
if (anncookie != null) throw new RuntimeException("@RestHeaders and @RestCookie cannot on the same Parameter in " + method);
if (annsid != null) throw new RuntimeException("@RestHeaders and @RestSessionid cannot on the same Parameter in " + method);
if (annaddr != null) throw new RuntimeException("@RestHeaders and @RestAddress cannot on the same Parameter in " + method);
if (annbody != null) throw new RuntimeException("@RestHeaders and @RestBody cannot on the same Parameter in " + method);
if (annfile != null) throw new RuntimeException("@RestHeaders and @RestUploadFile cannot on the same Parameter in " + method);
if (userid != null) throw new RuntimeException("@RestHeaders and @RestUserid cannot on the same Parameter in " + method);
if (!TYPE_MAP_STRING_STRING.equals(param.getParameterizedType())) throw new RuntimeException("@RestHeaders must on Map<String, String> Parameter in " + method);
comment = "";
}
RestParam annpara = param.getAnnotation(RestParam.class); RestParam annpara = param.getAnnotation(RestParam.class);
if (annpara != null) radix = annpara.radix(); if (annpara != null) radix = annpara.radix();
if (annpara != null) comment = annpara.comment(); if (annpara != null) comment = annpara.comment();
@@ -1100,7 +1158,8 @@ public final class Rest {
} }
} while ((loop = loop.getSuperclass()) != Object.class); } while ((loop = loop.getSuperclass()) != Object.class);
} }
paramlist.add(new Object[]{param, n, ptype, radix, comment, required, annpara, annsid, annaddr, annhead, anncookie, annbody, annfile, annuri, param.getParameterizedType()}); java.lang.reflect.Type paramtype = TypeToken.getGenericType( param.getParameterizedType(),serviceType);
paramlist.add(new Object[]{param, n, ptype, radix, comment, required, annpara, annsid, annaddr, annhead, anncookie, annbody, annfile, annuri, userid, annheaders, paramtype});
} }
Map<String, Object> mappingMap = new LinkedHashMap<>(); Map<String, Object> mappingMap = new LinkedHashMap<>();
@@ -1116,6 +1175,7 @@ public final class Rest {
av0 = mv.visitAnnotation(mappingDesc, true); av0 = mv.visitAnnotation(mappingDesc, true);
String url = (catalog.isEmpty() ? "/" : ("/" + catalog + "/")) + (defmodulename.isEmpty() ? "" : (defmodulename + "/")) + entry.name + (reqpath ? "/" : ""); String url = (catalog.isEmpty() ? "/" : ("/" + catalog + "/")) + (defmodulename.isEmpty() ? "" : (defmodulename + "/")) + entry.name + (reqpath ? "/" : "");
av0.visit("url", url); av0.visit("url", url);
av0.visit("rpconly", entry.rpconly);
av0.visit("auth", entry.auth); av0.visit("auth", entry.auth);
av0.visit("cacheseconds", entry.cacheseconds); av0.visit("cacheseconds", entry.cacheseconds);
av0.visit("actionid", entry.actionid); av0.visit("actionid", entry.actionid);
@@ -1132,6 +1192,7 @@ public final class Rest {
av0.visitEnd(); av0.visitEnd();
mappingMap.put("url", url); mappingMap.put("url", url);
mappingMap.put("rpconly", entry.rpconly);
mappingMap.put("auth", entry.auth); mappingMap.put("auth", entry.auth);
mappingMap.put("cacheseconds", entry.cacheseconds); mappingMap.put("cacheseconds", entry.cacheseconds);
mappingMap.put("actionid", entry.actionid); mappingMap.put("actionid", entry.actionid);
@@ -1218,7 +1279,10 @@ public final class Rest {
RestBody annbody = (RestBody) ps[11]; RestBody annbody = (RestBody) ps[11];
RestUploadFile annfile = (RestUploadFile) ps[12]; RestUploadFile annfile = (RestUploadFile) ps[12];
RestURI annuri = (RestURI) ps[13]; RestURI annuri = (RestURI) ps[13];
java.lang.reflect.Type pgentype = (java.lang.reflect.Type) ps[14]; RestUserid userid = (RestUserid) ps[14];
RestHeaders annheaders = (RestHeaders) ps[15];
java.lang.reflect.Type pgentype = (java.lang.reflect.Type) ps[16];
final boolean ishead = annhead != null; //是否取getHeader 而不是 getParameter final boolean ishead = annhead != null; //是否取getHeader 而不是 getParameter
final boolean iscookie = anncookie != null; //是否取getCookie final boolean iscookie = anncookie != null; //是否取getCookie
@@ -1252,6 +1316,11 @@ public final class Rest {
mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getRemoteAddr", "()Ljava/lang/String;", false); mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getRemoteAddr", "()Ljava/lang/String;", false);
mv.visitVarInsn(ASTORE, maxLocals); mv.visitVarInsn(ASTORE, maxLocals);
varInsns.add(new int[]{ALOAD, maxLocals}); varInsns.add(new int[]{ALOAD, maxLocals});
} else if (annheaders != null) { //HttpRequest.getHeaders
mv.visitVarInsn(ALOAD, 1);
mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getHeaders", "()Ljava/util/Map;", false);
mv.visitVarInsn(ASTORE, maxLocals);
varInsns.add(new int[]{ALOAD, maxLocals});
} else if (annbody != null) { //HttpRequest.getBodyUTF8 / HttpRequest.getBody } else if (annbody != null) { //HttpRequest.getBodyUTF8 / HttpRequest.getBody
if (ptype == String.class) { if (ptype == String.class) {
mv.visitVarInsn(ALOAD, 1); mv.visitVarInsn(ALOAD, 1);
@@ -1283,6 +1352,42 @@ public final class Rest {
mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getRequestURI", "()Ljava/lang/String;", false); mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getRequestURI", "()Ljava/lang/String;", false);
mv.visitVarInsn(ASTORE, maxLocals); mv.visitVarInsn(ASTORE, maxLocals);
varInsns.add(new int[]{ALOAD, maxLocals}); varInsns.add(new int[]{ALOAD, maxLocals});
} else if (userid != null) { //HttpRequest.currentUserid
mv.visitVarInsn(ALOAD, 1);
if (ptype == int.class) {
mv.visitFieldInsn(GETSTATIC, "java/lang/Integer", "TYPE", "Ljava/lang/Class;");
} else if (ptype == long.class) {
mv.visitFieldInsn(GETSTATIC, "java/lang/Long", "TYPE", "Ljava/lang/Class;");
} else {
mv.visitLdcInsn(Type.getType(Type.getInternalName(ptype)));
}
mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "currentUserid", "(Ljava/lang/Class;)Ljava/io/Serializable;", false);
if (ptype == int.class) {
mv.visitTypeInsn(CHECKCAST, "java/lang/Integer");
mv.visitInsn(ICONST_0);
mv.visitMethodInsn(INVOKESTATIC, "java/lang/Integer", "valueOf", "(I)Ljava/lang/Integer;", false);
mv.visitMethodInsn(INVOKESTATIC, restInternalName, "orElse", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;", false);
mv.visitTypeInsn(CHECKCAST, "java/lang/Integer");
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Integer", "intValue", "()I", false);
mv.visitVarInsn(ISTORE, maxLocals);
varInsns.add(new int[]{ILOAD, maxLocals});
} else if (ptype == long.class) {
mv.visitTypeInsn(CHECKCAST, "java/lang/Long");
mv.visitInsn(LCONST_0);
mv.visitMethodInsn(INVOKESTATIC, "java/lang/Long", "valueOf", "(J)Ljava/lang/Long;", false);
mv.visitMethodInsn(INVOKESTATIC, restInternalName, "orElse", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;", false);
mv.visitTypeInsn(CHECKCAST, "java/lang/Long");
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Long", "longValue", "()J", false);
mv.visitVarInsn(LSTORE, maxLocals);
varInsns.add(new int[]{LLOAD, maxLocals});
maxLocals++;
} else {
mv.visitTypeInsn(CHECKCAST, Type.getInternalName(ptype));
mv.visitVarInsn(ASTORE, maxLocals);
varInsns.add(new int[]{ALOAD, maxLocals});
}
} else if ("#".equals(pname)) { //从request.getRequstURI 中取参数 } else if ("#".equals(pname)) { //从request.getRequstURI 中取参数
if (ptype == boolean.class) { if (ptype == boolean.class) {
mv.visitVarInsn(ALOAD, 1); mv.visitVarInsn(ALOAD, 1);
@@ -1848,8 +1953,8 @@ public final class Rest {
// HashMap<String, InnerActionEntry> _createRestInnerActionEntry() { // HashMap<String, InnerActionEntry> _createRestInnerActionEntry() {
// HashMap<String, InnerActionEntry> map = new HashMap<>(); // HashMap<String, InnerActionEntry> map = new HashMap<>();
// map.put("asyncfind3", new InnerActionEntry(100000,200000,"asyncfind3", new String[]{},null,false,0, new _Dync_asyncfind3_HttpServlet())); // map.put("asyncfind3", new InnerActionEntry(100000,200000,"asyncfind3", new String[]{},null,false,false,0, new _Dync_asyncfind3_HttpServlet()));
// map.put("asyncfind2", new InnerActionEntry(1,2,"asyncfind2", new String[]{"GET", "POST"},null,true,0, new _Dync_asyncfind2_HttpServlet())); // map.put("asyncfind2", new InnerActionEntry(1,2,"asyncfind2", new String[]{"GET", "POST"},null,false,true,0, new _Dync_asyncfind2_HttpServlet()));
// return map; // return map;
// } // }
Map<String, Method> mappingurlToMethod = new HashMap<>(); Map<String, Method> mappingurlToMethod = new HashMap<>();
@@ -1879,13 +1984,14 @@ public final class Rest {
mv.visitInsn(AASTORE); mv.visitInsn(AASTORE);
} }
mv.visitInsn(ACONST_NULL); //method mv.visitInsn(ACONST_NULL); //method
mv.visitInsn(entry.rpconly ? ICONST_1 : ICONST_0); //rpconly
mv.visitInsn(entry.auth ? ICONST_1 : ICONST_0); //auth mv.visitInsn(entry.auth ? ICONST_1 : ICONST_0); //auth
pushInt(mv, entry.cacheseconds); //cacheseconds pushInt(mv, entry.cacheseconds); //cacheseconds
mv.visitTypeInsn(NEW, newDynName + "$" + entry.newActionClassName); mv.visitTypeInsn(NEW, newDynName + "$" + entry.newActionClassName);
mv.visitInsn(DUP); mv.visitInsn(DUP);
mv.visitVarInsn(ALOAD, 0); mv.visitVarInsn(ALOAD, 0);
mv.visitMethodInsn(INVOKESPECIAL, newDynName + "$" + entry.newActionClassName, "<init>", "(L" + newDynName + ";)V", false); mv.visitMethodInsn(INVOKESPECIAL, newDynName + "$" + entry.newActionClassName, "<init>", "(L" + newDynName + ";)V", false);
mv.visitMethodInsn(INVOKESPECIAL, innerEntryName, "<init>", "(IILjava/lang/String;[Ljava/lang/String;Ljava/lang/reflect/Method;ZILorg/redkale/net/http/HttpServlet;)V", false); mv.visitMethodInsn(INVOKESPECIAL, innerEntryName, "<init>", "(IILjava/lang/String;[Ljava/lang/String;Ljava/lang/reflect/Method;ZZILorg/redkale/net/http/HttpServlet;)V", false);
mv.visitMethodInsn(INVOKEVIRTUAL, "java/util/HashMap", "put", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;", false); mv.visitMethodInsn(INVOKEVIRTUAL, "java/util/HashMap", "put", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;", false);
mv.visitInsn(POP); mv.visitInsn(POP);
} }
@@ -2044,7 +2150,7 @@ public final class Rest {
} }
} }
public MappingEntry(int methodidx, RestMapping mapping, final String defmodulename, Method method) { public MappingEntry(final boolean serrpconly, int methodidx, RestMapping mapping, final String defmodulename, Method method) {
if (mapping == null) mapping = DEFAULT__MAPPING; if (mapping == null) mapping = DEFAULT__MAPPING;
this.methodidx = methodidx; this.methodidx = methodidx;
this.ignore = mapping.ignore(); this.ignore = mapping.ignore();
@@ -2058,6 +2164,7 @@ public final class Rest {
this.mappingMethod = method; this.mappingMethod = method;
this.methods = mapping.methods(); this.methods = mapping.methods();
this.auth = mapping.auth(); this.auth = mapping.auth();
this.rpconly = serrpconly || mapping.rpconly();
this.actionid = mapping.actionid(); this.actionid = mapping.actionid();
this.cacheseconds = mapping.cacheseconds(); this.cacheseconds = mapping.cacheseconds();
this.comment = mapping.comment(); this.comment = mapping.comment();
@@ -2091,6 +2198,8 @@ public final class Rest {
public final String[] methods; public final String[] methods;
public final boolean rpconly;
public final boolean auth; public final boolean auth;
public final int actionid; public final int actionid;

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.net.http;
import java.lang.annotation.*;
import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
/**
* 只能注解于RestService类的方法的参数或参数内的Map&#60;String, String&#62;字段
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*
* @since 2.1.0
*/
@Inherited
@Documented
@Target({PARAMETER, FIELD})
@Retention(RUNTIME)
public @interface RestHeaders {
}

View File

@@ -46,6 +46,13 @@ public @interface RestMapping {
*/ */
String comment() default ""; String comment() default "";
/**
* 是否只接收RPC请求, 对应&#64;HttpMapping.rpconly
*
* @return boolean
*/
boolean rpconly() default false;
/** /**
* 是否鉴权,默认需要鉴权, 对应&#64;HttpMapping.auth * 是否鉴权,默认需要鉴权, 对应&#64;HttpMapping.auth
* *

View File

@@ -43,6 +43,13 @@ public @interface RestService {
*/ */
int moduleid() default 0; int moduleid() default 0;
/**
* 是否只接受RPC请求 默认为false, 为true则覆盖所有&#64;RestMapping的方法的rpconly值都转为true
*
* @return 默认false
*/
boolean rpconly() default false;
/** /**
* 没有标记&#64;RestMapping的方法是否转换 默认为false * 没有标记&#64;RestMapping的方法是否转换 默认为false
* *

View File

@@ -0,0 +1,29 @@
/*
* 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类的方法的参数或参数内的Serializable字段
* <p>
* 用于获取HTTP请求端的用户ID HttpRequest.currentUserid
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*
* @since 2.1.0
*/
@Inherited
@Documented
@Target({PARAMETER, FIELD})
@Retention(RUNTIME)
public @interface RestUserid {
}

View File

@@ -14,6 +14,7 @@ import java.util.concurrent.*;
import java.util.function.*; import java.util.function.*;
import java.util.logging.*; import java.util.logging.*;
import java.util.stream.Stream; import java.util.stream.Stream;
import java.util.zip.*;
import org.redkale.convert.Convert; import org.redkale.convert.Convert;
import org.redkale.net.AsyncConnection; import org.redkale.net.AsyncConnection;
import org.redkale.util.Comment; import org.redkale.util.Comment;
@@ -103,7 +104,11 @@ public abstract class WebSocket<G extends Serializable, T> {
java.lang.reflect.Type _messageTextType; //不可能为空 java.lang.reflect.Type _messageTextType; //不可能为空
private long createtime = System.currentTimeMillis(); Deflater deflater; //压缩
Inflater inflater; //解压
long createtime = System.currentTimeMillis();
private long pingtime; private long pingtime;
@@ -517,7 +522,7 @@ public abstract class WebSocket<G extends Serializable, T> {
* *
* @return 地址列表 * @return 地址列表
*/ */
public CompletableFuture<Collection<InetSocketAddress>> getRpcNodeAddresses(final Serializable userid) { public CompletableFuture<Collection<WebSocketAddress>> getRpcNodeAddresses(final Serializable userid) {
if (_engine.node == null) return CompletableFuture.completedFuture(null); if (_engine.node == null) return CompletableFuture.completedFuture(null);
return _engine.node.getRpcNodeAddresses(userid); return _engine.node.getRpcNodeAddresses(userid);
} }
@@ -531,7 +536,7 @@ public abstract class WebSocket<G extends Serializable, T> {
* *
* @return 地址集合 * @return 地址集合
*/ */
public CompletableFuture<Map<InetSocketAddress, List<String>>> getRpcNodeWebSocketAddresses(final Serializable userid) { public CompletableFuture<Map<WebSocketAddress, List<String>>> getRpcNodeWebSocketAddresses(final Serializable userid) {
if (_engine.node == null) return CompletableFuture.completedFuture(null); if (_engine.node == null) return CompletableFuture.completedFuture(null);
return _engine.node.getRpcNodeWebSocketAddresses(userid); return _engine.node.getRpcNodeWebSocketAddresses(userid);
} }
@@ -837,7 +842,7 @@ public abstract class WebSocket<G extends Serializable, T> {
} }
/** /**
* 当Single模式下用户重复登时回调函数,默认处理方式: 关闭旧连接 * 当Single模式下用户重复登时回调函数,默认处理方式: 关闭旧连接
* *
* @return Future 可以为null, 为null或者Future值为false表示关闭新连接 Future值为true表示关闭旧连接 * @return Future 可以为null, 为null或者Future值为false表示关闭新连接 Future值为true表示关闭旧连接
*/ */
@@ -894,6 +899,8 @@ public abstract class WebSocket<G extends Serializable, T> {
* 显式地关闭WebSocket * 显式地关闭WebSocket
*/ */
public final void close() { public final void close() {
if (this.deflater != null) this.deflater.end();
if (this.inflater != null) this.inflater.end();
if (this._runner != null) { if (this._runner != null) {
CompletableFuture<Void> future = this._runner.closeRunner(CLOSECODE_SERVERCLOSE, "user close"); CompletableFuture<Void> future = this._runner.closeRunner(CLOSECODE_SERVERCLOSE, "user close");
if (future != null) future.join(); if (future != null) future.join();

View File

@@ -35,6 +35,14 @@ public class WebSocketAction implements Serializable {
this.attach = attach; this.attach = attach;
} }
public String findAttach(String name) {
return attach == null ? null : attach.get(name);
}
public String findAttach(String name, String defvalue) {
return attach == null ? defvalue : attach.getOrDefault(name, defvalue);
}
public String getAction() { public String getAction() {
return action; return action;
} }

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.io.Serializable;
import java.net.InetSocketAddress;
import java.util.Objects;
import org.redkale.convert.json.JsonConvert;
/**
* 存放用户WS连接的SNCP地址和MQ topic 当消息使用MQ代理时topic才会有值
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*
* @since 2.1.0
*/
public class WebSocketAddress implements Serializable {
protected InetSocketAddress addr;
protected String topic;
public WebSocketAddress() {
}
public WebSocketAddress(String topic, InetSocketAddress addr) {
this.topic = topic;
this.addr = addr;
}
@Override
public int hashCode() {
int hash = 7;
hash = 37 * hash + Objects.hashCode(this.addr);
hash = 37 * hash + Objects.hashCode(this.topic);
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 WebSocketAddress other = (WebSocketAddress) obj;
return Objects.equals(this.topic, other.topic) && Objects.equals(this.addr, other.addr);
}
public String getTopic() {
return topic;
}
public void setTopic(String topic) {
this.topic = topic;
}
public InetSocketAddress getAddr() {
return addr;
}
public void setAddr(InetSocketAddress addr) {
this.addr = addr;
}
@Override
public String toString() {
return JsonConvert.root().convertTo(this);
}
}

View File

@@ -152,6 +152,7 @@ public class WebSocketEngine {
@Comment("从WebSocketEngine删除指定WebSocket") @Comment("从WebSocketEngine删除指定WebSocket")
CompletableFuture<Void> removeLocalThenClose(WebSocket socket) { CompletableFuture<Void> removeLocalThenClose(WebSocket socket) {
Serializable userid = socket._userid; Serializable userid = socket._userid;
if (userid == null) return null; //尚未登录成功
if (single) { if (single) {
currconns.decrementAndGet(); currconns.decrementAndGet();
websockets.remove(userid); websockets.remove(userid);
@@ -244,7 +245,7 @@ public class WebSocketEngine {
if (bufferSupplier == null) { if (bufferSupplier == null) {
bufferSupplier = websocket.getBufferSupplier(); bufferSupplier = websocket.getBufferSupplier();
bufferConsumer = websocket.getBufferConsumer(); bufferConsumer = websocket.getBufferConsumer();
packet.setSendBuffers(packet.encode(bufferSupplier, bufferConsumer, cryptor)); packet.encodePacket(bufferSupplier, bufferConsumer, cryptor);
} }
future = future == null ? websocket.sendPacket(packet) : future.thenCombine(websocket.sendPacket(packet), (a, b) -> a | (Integer) b); future = future == null ? websocket.sendPacket(packet) : future.thenCombine(websocket.sendPacket(packet), (a, b) -> a | (Integer) b);
} }
@@ -255,7 +256,7 @@ public class WebSocketEngine {
if (bufferSupplier == null) { if (bufferSupplier == null) {
bufferSupplier = websocket.getBufferSupplier(); bufferSupplier = websocket.getBufferSupplier();
bufferConsumer = websocket.getBufferConsumer(); bufferConsumer = websocket.getBufferConsumer();
packet.setSendBuffers(packet.encode(bufferSupplier, bufferConsumer, cryptor)); packet.encodePacket(bufferSupplier, bufferConsumer, cryptor);
} }
future = future == null ? websocket.sendPacket(packet) : future.thenCombine(websocket.sendPacket(packet), (a, b) -> a | (Integer) b); future = future == null ? websocket.sendPacket(packet) : future.thenCombine(websocket.sendPacket(packet), (a, b) -> a | (Integer) b);
} }
@@ -312,7 +313,7 @@ public class WebSocketEngine {
final WebSocketPacket packet = (message instanceof WebSocketPacket) ? (WebSocketPacket) message final WebSocketPacket packet = (message instanceof WebSocketPacket) ? (WebSocketPacket) message
: ((message == null || message instanceof CharSequence || message instanceof byte[]) : ((message == null || message instanceof CharSequence || message instanceof byte[])
? new WebSocketPacket((Serializable) message, last) : new WebSocketPacket(this.sendConvert, false, message, last)); ? new WebSocketPacket((Serializable) message, last) : new WebSocketPacket(this.sendConvert, false, message, last));
//packet.setSendBuffers(packet.encode(context.getBufferSupplier(), context.getBufferConsumer(), cryptor)); //packet.encode(context.getBufferSupplier(), context.getBufferConsumer(), cryptor);
CompletableFuture<Integer> future = null; CompletableFuture<Integer> future = null;
if (single) { if (single) {
for (Serializable userid : userids) { for (Serializable userid : userids) {
@@ -321,7 +322,7 @@ public class WebSocketEngine {
if (bufferSupplier == null) { if (bufferSupplier == null) {
bufferSupplier = websocket.getBufferSupplier(); bufferSupplier = websocket.getBufferSupplier();
bufferConsumer = websocket.getBufferConsumer(); bufferConsumer = websocket.getBufferConsumer();
packet.setSendBuffers(packet.encode(bufferSupplier, bufferConsumer, cryptor)); packet.encodePacket(bufferSupplier, bufferConsumer, cryptor);
} }
future = future == null ? websocket.sendPacket(packet) : future.thenCombine(websocket.sendPacket(packet), (a, b) -> a | (Integer) b); future = future == null ? websocket.sendPacket(packet) : future.thenCombine(websocket.sendPacket(packet), (a, b) -> a | (Integer) b);
} }
@@ -333,7 +334,7 @@ public class WebSocketEngine {
if (bufferSupplier == null) { if (bufferSupplier == null) {
bufferSupplier = websocket.getBufferSupplier(); bufferSupplier = websocket.getBufferSupplier();
bufferConsumer = websocket.getBufferConsumer(); bufferConsumer = websocket.getBufferConsumer();
packet.setSendBuffers(packet.encode(bufferSupplier, bufferConsumer, cryptor)); packet.encodePacket(bufferSupplier, bufferConsumer, cryptor);
} }
future = future == null ? websocket.sendPacket(packet) : future.thenCombine(websocket.sendPacket(packet), (a, b) -> a | (Integer) b); future = future == null ? websocket.sendPacket(packet) : future.thenCombine(websocket.sendPacket(packet), (a, b) -> a | (Integer) b);
} }

View File

@@ -16,6 +16,7 @@ import javax.annotation.*;
import org.redkale.boot.*; import org.redkale.boot.*;
import org.redkale.convert.*; import org.redkale.convert.*;
import org.redkale.convert.json.JsonConvert; import org.redkale.convert.json.JsonConvert;
import org.redkale.mq.MessageAgent;
import org.redkale.service.*; import org.redkale.service.*;
import org.redkale.source.*; import org.redkale.source.*;
import org.redkale.util.*; import org.redkale.util.*;
@@ -33,7 +34,7 @@ public abstract class WebSocketNode {
public static final String SOURCE_SNCP_USERID_PREFIX = "sncpws_uid:"; public static final String SOURCE_SNCP_USERID_PREFIX = "sncpws_uid:";
@Comment("存储当前SNCP节点列表的key") @Comment("存储当前SNCP节点列表的key")
public static final String SOURCE_SNCP_ADDRS_KEY = "sncpws_addrs"; public static final String SOURCE_SNCP_NODES_KEY = "sncpws_nodes";
protected final Logger logger = Logger.getLogger(this.getClass().getSimpleName()); protected final Logger logger = Logger.getLogger(this.getClass().getSimpleName());
@@ -41,6 +42,10 @@ public abstract class WebSocketNode {
@Resource(name = Application.RESNAME_SNCP_ADDR) @Resource(name = Application.RESNAME_SNCP_ADDR)
protected InetSocketAddress localSncpAddress; //为SncpServer的服务address protected InetSocketAddress localSncpAddress; //为SncpServer的服务address
protected WebSocketAddress wsaddress;
protected String name;
//如果不是分布式(没有SNCP) 值为null //如果不是分布式(没有SNCP) 值为null
@RpcRemote @RpcRemote
protected WebSocketNode remoteNode; protected WebSocketNode remoteNode;
@@ -48,15 +53,17 @@ public abstract class WebSocketNode {
@Resource(name = "$_sendconvert") @Resource(name = "$_sendconvert")
protected Convert sendConvert; protected Convert sendConvert;
//存放所有用户分布在节点上的队列信息,Set<InetSocketAddress> 为 sncpnode 的集合, key: groupid //存放所有用户分布在节点上的队列信息,Set<WebSocketAddress> 为 sncpnode 的集合, key: groupid
//集合包含 localSncpAddress //集合包含 localSncpAddress
//如果不是分布式(没有SNCP)sncpNodeAddresses 将不会被用到 //如果不是分布式(没有SNCP)source 将不会被用到
@Resource(name = "$") @Resource(name = "$")
protected CacheSource<InetSocketAddress> sncpNodeAddresses; protected CacheSource source;
//当前节点的本地WebSocketEngine //当前节点的本地WebSocketEngine
protected WebSocketEngine localEngine; protected WebSocketEngine localEngine;
protected MessageAgent messageAgent;
protected Semaphore semaphore; protected Semaphore semaphore;
private int tryAcquireSeconds = 12; private int tryAcquireSeconds = 12;
@@ -64,14 +71,18 @@ public abstract class WebSocketNode {
public void init(AnyValue conf) { public void init(AnyValue conf) {
this.tryAcquireSeconds = Integer.getInteger("WebSocketNode.tryAcquireSeconds", 12); this.tryAcquireSeconds = Integer.getInteger("WebSocketNode.tryAcquireSeconds", 12);
if (sncpNodeAddresses != null && "memory".equals(sncpNodeAddresses.getType())) { if (source != null && "memory".equals(source.getType())) {
sncpNodeAddresses.initValueType(InetSocketAddress.class); source.initValueType(WebSocketAddress.class);
} }
if (localEngine != null) { if (localEngine != null) {
int wsthreads = localEngine.wsthreads; int wsthreads = localEngine.wsthreads;
if (wsthreads == 0) wsthreads = Runtime.getRuntime().availableProcessors() * 8; if (wsthreads == 0) wsthreads = Runtime.getRuntime().availableProcessors() * 8;
if (wsthreads > 0) this.semaphore = new Semaphore(wsthreads); if (wsthreads > 0) this.semaphore = new Semaphore(wsthreads);
} }
String mqtopic = this.messageAgent == null ? null : this.messageAgent.generateSncpReqTopic((Service)this);
if (mqtopic != null || this.localSncpAddress != null) {
this.wsaddress = new WebSocketAddress(mqtopic, localSncpAddress);
}
} }
public void destroy(AnyValue conf) { public void destroy(AnyValue conf) {
@@ -82,50 +93,59 @@ public abstract class WebSocketNode {
return semaphore; return semaphore;
} }
@Local
public final MessageAgent getMessageAgent() {
return messageAgent;
}
@Local @Local
protected void postDestroy(AnyValue conf) { protected void postDestroy(AnyValue conf) {
if (this.localEngine == null) return; if (this.localEngine == null) return;
//关掉所有本地本地WebSocket //关掉所有本地本地WebSocket
this.localEngine.getLocalWebSockets().forEach(g -> g.close()); this.localEngine.getLocalWebSockets().forEach(g -> g.close());
if (sncpNodeAddresses != null && localSncpAddress != null) { if (source != null && wsaddress != null) {
sncpNodeAddresses.removeSetItem(SOURCE_SNCP_ADDRS_KEY, InetSocketAddress.class, localSncpAddress); source.removeSetItem(SOURCE_SNCP_NODES_KEY, WebSocketAddress.class, this.wsaddress);
} }
} }
protected abstract CompletableFuture<List<String>> getWebSocketAddresses(@RpcTargetAddress InetSocketAddress targetAddress, Serializable userid); protected abstract CompletableFuture<List<String>> getWebSocketAddresses(@RpcTargetTopic String topic, @RpcTargetAddress InetSocketAddress targetAddress, Serializable userid);
protected abstract CompletableFuture<Integer> sendMessage(@RpcTargetAddress InetSocketAddress targetAddress, Object message, boolean last, Serializable... userids); protected abstract CompletableFuture<Integer> sendMessage(@RpcTargetTopic String topic, @RpcTargetAddress InetSocketAddress targetAddress, Object message, boolean last, Serializable... userids);
protected abstract CompletableFuture<Integer> broadcastMessage(@RpcTargetAddress InetSocketAddress targetAddress, WebSocketRange wsrange, Object message, boolean last); protected abstract CompletableFuture<Integer> broadcastMessage(@RpcTargetTopic String topic, @RpcTargetAddress InetSocketAddress targetAddress, WebSocketRange wsrange, Object message, boolean last);
protected abstract CompletableFuture<Integer> sendAction(@RpcTargetAddress InetSocketAddress targetAddress, WebSocketAction action, Serializable... userids); protected abstract CompletableFuture<Integer> sendAction(@RpcTargetTopic String topic, @RpcTargetAddress InetSocketAddress targetAddress, WebSocketAction action, Serializable... userids);
protected abstract CompletableFuture<Integer> broadcastAction(@RpcTargetAddress InetSocketAddress targetAddress, WebSocketAction action); protected abstract CompletableFuture<Integer> broadcastAction(@RpcTargetTopic String topic, @RpcTargetAddress InetSocketAddress targetAddress, WebSocketAction action);
protected abstract CompletableFuture<Void> connect(Serializable userid, InetSocketAddress sncpAddr); protected abstract CompletableFuture<Void> connect(Serializable userid, WebSocketAddress wsaddr);
protected abstract CompletableFuture<Void> disconnect(Serializable userid, InetSocketAddress sncpAddr); protected abstract CompletableFuture<Void> disconnect(Serializable userid, WebSocketAddress wsaddr);
protected abstract CompletableFuture<Void> changeUserid(Serializable fromuserid, Serializable touserid, InetSocketAddress sncpAddr); protected abstract CompletableFuture<Void> changeUserid(Serializable fromuserid, Serializable touserid, WebSocketAddress wsaddr);
protected abstract CompletableFuture<Boolean> existsWebSocket(Serializable userid, @RpcTargetAddress InetSocketAddress targetAddress); protected abstract CompletableFuture<Boolean> existsWebSocket(Serializable userid, @RpcTargetTopic String topic, @RpcTargetAddress InetSocketAddress targetAddress);
protected abstract CompletableFuture<Integer> forceCloseWebSocket(Serializable userid, @RpcTargetAddress InetSocketAddress targetAddress); protected abstract CompletableFuture<Integer> forceCloseWebSocket(Serializable userid, @RpcTargetTopic String topic, @RpcTargetAddress InetSocketAddress targetAddress);
//-------------------------------------------------------------------------------- //--------------------------------------------------------------------------------
final CompletableFuture<Void> connect(final Serializable userid) { final CompletableFuture<Void> connect(final Serializable userid) {
if (logger.isLoggable(Level.FINEST)) logger.finest(localSncpAddress + " receive websocket connect event (" + userid + " on " + (this.localEngine == null ? null : this.localEngine.getEngineid()) + ")."); if (logger.isLoggable(Level.FINEST)) logger.finest(this.wsaddress + " receive websocket connect event (" + userid + " on " + (this.localEngine == null ? null : this.localEngine.getEngineid()) + ").");
return connect(userid, localSncpAddress); return connect(userid, this.wsaddress);
} }
final CompletableFuture<Void> disconnect(final Serializable userid) { final CompletableFuture<Void> disconnect(final Serializable userid) {
if (logger.isLoggable(Level.FINEST)) logger.finest(localSncpAddress + " receive websocket disconnect event (" + userid + " on " + (this.localEngine == null ? null : this.localEngine.getEngineid()) + ")."); if (logger.isLoggable(Level.FINEST)) logger.finest(this.wsaddress + " receive websocket disconnect event (" + userid + " on " + (this.localEngine == null ? null : this.localEngine.getEngineid()) + ").");
return disconnect(userid, localSncpAddress); return disconnect(userid, this.wsaddress);
} }
final CompletableFuture<Void> changeUserid(Serializable olduserid, final Serializable newuserid) { final CompletableFuture<Void> changeUserid(Serializable olduserid, final Serializable newuserid) {
if (logger.isLoggable(Level.FINEST)) logger.finest(localSncpAddress + " receive websocket changeUserid event (from " + olduserid + " to " + newuserid + " on " + (this.localEngine == null ? null : this.localEngine.getEngineid()) + ")."); if (logger.isLoggable(Level.FINEST)) logger.finest(this.wsaddress + " receive websocket changeUserid event (from " + olduserid + " to " + newuserid + " on " + (this.localEngine == null ? null : this.localEngine.getEngineid()) + ").");
return changeUserid(olduserid, newuserid, localSncpAddress); return changeUserid(olduserid, newuserid, this.wsaddress);
}
public final String getName() {
return name;
} }
//-------------------------------------------------------------------------------- //--------------------------------------------------------------------------------
@@ -133,15 +153,16 @@ public abstract class WebSocketNode {
* 获取目标地址 <br> * 获取目标地址 <br>
* 该方法仅供内部调用 * 该方法仅供内部调用
* *
* @param topic RpcTargetTopic
* @param targetAddress InetSocketAddress * @param targetAddress InetSocketAddress
* @param userid Serializable * @param userid Serializable
* *
* @return 客户端地址列表 * @return 客户端地址列表
*/ */
protected CompletableFuture<List<String>> remoteWebSocketAddresses(@RpcTargetAddress InetSocketAddress targetAddress, Serializable userid) { protected CompletableFuture<List<String>> remoteWebSocketAddresses(@RpcTargetTopic String topic, @RpcTargetAddress InetSocketAddress targetAddress, Serializable userid) {
if (remoteNode == null) return CompletableFuture.completedFuture(null); if (remoteNode == null) return CompletableFuture.completedFuture(null);
try { try {
return remoteNode.getWebSocketAddresses(targetAddress, userid); return remoteNode.getWebSocketAddresses(topic, 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 CompletableFuture.completedFuture(null); return CompletableFuture.completedFuture(null);
@@ -150,21 +171,21 @@ public abstract class WebSocketNode {
/** /**
* 获取用户在线的SNCP节点地址列表不是分布式则返回元素数量为1且元素值为null的列表<br> * 获取用户在线的SNCP节点地址列表不是分布式则返回元素数量为1且元素值为null的列表<br>
* InetSocketAddress 为 SNCP节点地址 * WebSocketAddress 为 SNCP节点地址
* *
* @param userid Serializable * @param userid Serializable
* *
* @return 地址列表 * @return 地址列表
*/ */
public CompletableFuture<Collection<InetSocketAddress>> getRpcNodeAddresses(final Serializable userid) { public CompletableFuture<Collection<WebSocketAddress>> getRpcNodeAddresses(final Serializable userid) {
if (this.sncpNodeAddresses != null) { if (this.source != null) {
tryAcquireSemaphore(); tryAcquireSemaphore();
CompletableFuture<Collection<InetSocketAddress>> result = this.sncpNodeAddresses.getCollectionAsync(SOURCE_SNCP_USERID_PREFIX + userid, InetSocketAddress.class); CompletableFuture<Collection<WebSocketAddress>> result = this.source.getCollectionAsync(SOURCE_SNCP_USERID_PREFIX + userid, WebSocketAddress.class);
if (semaphore != null) result.whenComplete((r, e) -> releaseSemaphore()); if (semaphore != null) result.whenComplete((r, e) -> releaseSemaphore());
return result; return result;
} }
List<InetSocketAddress> rs = new ArrayList<>(); List<WebSocketAddress> rs = new ArrayList<>();
rs.add(this.localSncpAddress); rs.add(this.wsaddress);
return CompletableFuture.completedFuture(rs); return CompletableFuture.completedFuture(rs);
} }
@@ -177,14 +198,14 @@ public abstract class WebSocketNode {
* *
* @return 地址集合 * @return 地址集合
*/ */
public CompletableFuture<Map<InetSocketAddress, List<String>>> getRpcNodeWebSocketAddresses(final Serializable userid) { public CompletableFuture<Map<WebSocketAddress, List<String>>> getRpcNodeWebSocketAddresses(final Serializable userid) {
CompletableFuture<Collection<InetSocketAddress>> sncpFuture = getRpcNodeAddresses(userid); CompletableFuture<Collection<WebSocketAddress>> sncpFuture = getRpcNodeAddresses(userid);
return sncpFuture.thenCompose((Collection<InetSocketAddress> addrs) -> { return sncpFuture.thenCompose((Collection<WebSocketAddress> addrs) -> {
if (logger.isLoggable(Level.FINEST)) logger.finest("websocket found userid:" + userid + " on " + addrs); if (logger.isLoggable(Level.FINEST)) logger.finest("websocket found userid:" + userid + " on " + addrs);
if (addrs == null || addrs.isEmpty()) return CompletableFuture.completedFuture(new HashMap<>()); if (addrs == null || addrs.isEmpty()) return CompletableFuture.completedFuture(new HashMap<>());
CompletableFuture<Map<InetSocketAddress, List<String>>> future = null; CompletableFuture<Map<WebSocketAddress, List<String>>> future = null;
for (final InetSocketAddress nodeAddress : addrs) { for (final WebSocketAddress nodeAddress : addrs) {
CompletableFuture<Map<InetSocketAddress, List<String>>> mapFuture = getWebSocketAddresses(nodeAddress, userid) CompletableFuture<Map<WebSocketAddress, List<String>>> mapFuture = getWebSocketAddresses(nodeAddress.getTopic(), nodeAddress.getAddr(), userid)
.thenCompose((List<String> list) -> CompletableFuture.completedFuture(Utility.ofMap(nodeAddress, list))); .thenCompose((List<String> list) -> CompletableFuture.completedFuture(Utility.ofMap(nodeAddress, list)));
future = future == null ? mapFuture : future.thenCombine(mapFuture, (a, b) -> Utility.merge(a, b)); future = future == null ? mapFuture : future.thenCombine(mapFuture, (a, b) -> Utility.merge(a, b));
} }
@@ -199,11 +220,12 @@ public abstract class WebSocketNode {
* @return boolean * @return boolean
*/ */
public CompletableFuture<Integer> getUserSize() { public CompletableFuture<Integer> getUserSize() {
if (this.localEngine != null && this.sncpNodeAddresses == null) { if (this.localEngine != null && this.source == null) {
return CompletableFuture.completedFuture(this.localEngine.getLocalUserSize()); return CompletableFuture.completedFuture(this.localEngine.getLocalUserSize());
} }
tryAcquireSemaphore(); tryAcquireSemaphore();
CompletableFuture<Integer> rs = this.sncpNodeAddresses.queryKeysStartsWithAsync(SOURCE_SNCP_USERID_PREFIX).thenApply(v -> v.size()); CompletableFuture<List<String>> listFuture = this.source.queryKeysStartsWithAsync(SOURCE_SNCP_USERID_PREFIX);
CompletableFuture<Integer> rs = listFuture.thenApply(v -> v.size());
if (semaphore != null) rs.whenComplete((r, e) -> releaseSemaphore()); if (semaphore != null) rs.whenComplete((r, e) -> releaseSemaphore());
return rs; return rs;
} }
@@ -215,11 +237,12 @@ public abstract class WebSocketNode {
* @return boolean * @return boolean
*/ */
public CompletableFuture<Set<String>> getUserSet() { public CompletableFuture<Set<String>> getUserSet() {
if (this.localEngine != null && this.sncpNodeAddresses == null) { if (this.localEngine != null && this.source == null) {
return CompletableFuture.completedFuture(new LinkedHashSet<>(this.localEngine.getLocalUserSet().stream().map(x -> String.valueOf(x)).collect(Collectors.toList()))); return CompletableFuture.completedFuture(new LinkedHashSet<>(this.localEngine.getLocalUserSet().stream().map(x -> String.valueOf(x)).collect(Collectors.toList())));
} }
tryAcquireSemaphore(); tryAcquireSemaphore();
CompletableFuture<Set<String>> rs = this.sncpNodeAddresses.queryKeysStartsWithAsync(SOURCE_SNCP_USERID_PREFIX).thenApply(v -> new LinkedHashSet<>(v.stream().map(x -> x.substring(SOURCE_SNCP_USERID_PREFIX.length())).collect(Collectors.toList()))); CompletableFuture<List<String>> listFuture = this.source.queryKeysStartsWithAsync(SOURCE_SNCP_USERID_PREFIX);
CompletableFuture<Set<String>> rs = listFuture.thenApply(v -> new LinkedHashSet<>(v.stream().map(x -> x.substring(SOURCE_SNCP_USERID_PREFIX.length())).collect(Collectors.toList())));
if (semaphore != null) rs.whenComplete((r, e) -> releaseSemaphore()); if (semaphore != null) rs.whenComplete((r, e) -> releaseSemaphore());
return rs; return rs;
} }
@@ -236,23 +259,23 @@ public abstract class WebSocketNode {
if (userid instanceof WebSocketUserAddress) return existsWebSocket((WebSocketUserAddress) userid); if (userid instanceof WebSocketUserAddress) return existsWebSocket((WebSocketUserAddress) userid);
CompletableFuture<Boolean> localFuture = null; CompletableFuture<Boolean> localFuture = null;
if (this.localEngine != null) localFuture = CompletableFuture.completedFuture(localEngine.existsLocalWebSocket(userid)); if (this.localEngine != null) localFuture = CompletableFuture.completedFuture(localEngine.existsLocalWebSocket(userid));
if (this.sncpNodeAddresses == null || this.remoteNode == null) { if (this.source == null || this.remoteNode == null) {
if (logger.isLoggable(Level.FINEST)) logger.finest("websocket remote node is null"); if (logger.isLoggable(Level.FINEST)) logger.finest("websocket remote node is null");
//没有CacheSource就不会有分布式节点 //没有CacheSource就不会有分布式节点
return localFuture; return localFuture == null ? CompletableFuture.completedFuture(false) : localFuture;
} }
//远程节点关闭 //远程节点关闭
tryAcquireSemaphore(); tryAcquireSemaphore();
CompletableFuture<Collection<InetSocketAddress>> addrsFuture = sncpNodeAddresses.getCollectionAsync(SOURCE_SNCP_USERID_PREFIX + userid, InetSocketAddress.class); CompletableFuture<Collection<WebSocketAddress>> addrsFuture = source.getCollectionAsync(SOURCE_SNCP_USERID_PREFIX + userid, WebSocketAddress.class);
if (semaphore != null) addrsFuture.whenComplete((r, e) -> releaseSemaphore()); if (semaphore != null) addrsFuture.whenComplete((r, e) -> releaseSemaphore());
CompletableFuture<Boolean> remoteFuture = addrsFuture.thenCompose((Collection<InetSocketAddress> addrs) -> { CompletableFuture<Boolean> remoteFuture = addrsFuture.thenCompose((Collection<WebSocketAddress> addrs) -> {
//if (logger.isLoggable(Level.FINEST)) logger.finest("websocket found userid:" + userid + " on " + addrs); //if (logger.isLoggable(Level.FINEST)) logger.finest("websocket found userid:" + userid + " on " + addrs);
if (addrs == null || addrs.isEmpty()) return CompletableFuture.completedFuture(false); if (addrs == null || addrs.isEmpty()) return CompletableFuture.completedFuture(false);
CompletableFuture<Boolean> future = null; CompletableFuture<Boolean> future = null;
for (InetSocketAddress addr : addrs) { for (WebSocketAddress addr : addrs) {
if (addr == null || addr.equals(localSncpAddress)) continue; if (addr == null || addr.equals(wsaddress)) continue;
future = future == null ? remoteNode.existsWebSocket(userid, addr) future = future == null ? remoteNode.existsWebSocket(userid, addr.getTopic(), addr.getAddr())
: future.thenCombine(remoteNode.existsWebSocket(userid, addr), (a, b) -> a | b); : future.thenCombine(remoteNode.existsWebSocket(userid, addr.getTopic(), addr.getAddr()), (a, b) -> a | b);
} }
return future == null ? CompletableFuture.completedFuture(false) : future; return future == null ? CompletableFuture.completedFuture(false) : future;
}); });
@@ -268,25 +291,24 @@ public abstract class WebSocketNode {
*/ */
@Local @Local
public CompletableFuture<Boolean> existsWebSocket(final WebSocketUserAddress userAddress) { public CompletableFuture<Boolean> existsWebSocket(final WebSocketUserAddress userAddress) {
CompletableFuture<Boolean> localFuture = null; if (this.localEngine != null && localEngine.existsLocalWebSocket(userAddress.userid())) return CompletableFuture.completedFuture(true);
if (this.localEngine != null) localFuture = CompletableFuture.completedFuture(localEngine.existsLocalWebSocket(userAddress.userid())); if (this.source == null || this.remoteNode == null) {
if (this.sncpNodeAddresses == null || this.remoteNode == null) {
if (logger.isLoggable(Level.FINEST)) logger.finest("websocket remote node is null"); if (logger.isLoggable(Level.FINEST)) logger.finest("websocket remote node is null");
//没有CacheSource就不会有分布式节点 //没有CacheSource就不会有分布式节点
return localFuture; return CompletableFuture.completedFuture(false);
} }
Collection<InetSocketAddress> addrs = userAddress.sncpAddresses(); Collection<WebSocketAddress> addrs = userAddress.addresses();
if (addrs != null) addrs = new ArrayList<>(addrs); //不能修改参数内部值 if (addrs != null) addrs = new ArrayList<>(addrs); //不能修改参数内部值
if (userAddress.sncpAddress() != null) { if (userAddress.address() != null) {
if (addrs == null) addrs = new ArrayList<>(); if (addrs == null) addrs = new ArrayList<>();
addrs.add(userAddress.sncpAddress()); addrs.add(userAddress.address());
} }
if (addrs == null || addrs.isEmpty()) return CompletableFuture.completedFuture(false); if (addrs == null || addrs.isEmpty()) return CompletableFuture.completedFuture(false);
CompletableFuture<Boolean> future = null; CompletableFuture<Boolean> future = null;
for (InetSocketAddress addr : addrs) { for (WebSocketAddress addr : addrs) {
if (addr == null || addr.equals(localSncpAddress)) continue; if (addr == null || addr.equals(wsaddress)) continue;
future = future == null ? remoteNode.existsWebSocket(userAddress.userid(), addr) future = future == null ? remoteNode.existsWebSocket(userAddress.userid(), addr.getTopic(), addr.getAddr())
: future.thenCombine(remoteNode.existsWebSocket(userAddress.userid(), addr), (a, b) -> a | b); : future.thenCombine(remoteNode.existsWebSocket(userAddress.userid(), addr.getTopic(), addr.getAddr()), (a, b) -> a | b);
} }
return future == null ? CompletableFuture.completedFuture(false) : future; return future == null ? CompletableFuture.completedFuture(false) : future;
} }
@@ -318,35 +340,35 @@ public abstract class WebSocketNode {
private CompletableFuture<Integer> forceCloseWebSocket(final Serializable userid, final WebSocketUserAddress userAddress) { private CompletableFuture<Integer> forceCloseWebSocket(final Serializable userid, final WebSocketUserAddress userAddress) {
CompletableFuture<Integer> localFuture = null; CompletableFuture<Integer> localFuture = null;
if (this.localEngine != null) localFuture = CompletableFuture.completedFuture(localEngine.forceCloseLocalWebSocket(userAddress == null ? userid : userAddress.userid())); if (this.localEngine != null) localFuture = CompletableFuture.completedFuture(localEngine.forceCloseLocalWebSocket(userAddress == null ? userid : userAddress.userid()));
if (this.sncpNodeAddresses == null || this.remoteNode == null) { if (this.source == null || this.remoteNode == null) {
if (logger.isLoggable(Level.FINEST)) logger.finest("websocket remote node is null"); if (logger.isLoggable(Level.FINEST)) logger.finest("websocket remote node is null");
//没有CacheSource就不会有分布式节点 //没有CacheSource就不会有分布式节点
return localFuture; return localFuture == null ? CompletableFuture.completedFuture(0) : localFuture;
} }
//远程节点关闭 //远程节点关闭
CompletableFuture<Collection<InetSocketAddress>> addrsFuture; CompletableFuture<Collection<WebSocketAddress>> addrsFuture;
if (userAddress == null) { if (userAddress == null) {
tryAcquireSemaphore(); tryAcquireSemaphore();
addrsFuture = sncpNodeAddresses.getCollectionAsync(SOURCE_SNCP_USERID_PREFIX + userid, InetSocketAddress.class); addrsFuture = source.getCollectionAsync(SOURCE_SNCP_USERID_PREFIX + userid, WebSocketAddress.class);
if (semaphore != null) addrsFuture.whenComplete((r, e) -> releaseSemaphore()); if (semaphore != null) addrsFuture.whenComplete((r, e) -> releaseSemaphore());
} else { } else {
Collection<InetSocketAddress> addrs = userAddress.sncpAddresses(); Collection<WebSocketAddress> addrs = userAddress.addresses();
if (addrs != null) addrs = new ArrayList<>(addrs); //不能修改参数内部值 if (addrs != null) addrs = new ArrayList<>(addrs); //不能修改参数内部值
if (userAddress.sncpAddress() != null) { if (userAddress.address() != null) {
if (addrs == null) addrs = new ArrayList<>(); if (addrs == null) addrs = new ArrayList<>();
addrs.add(userAddress.sncpAddress()); addrs.add(userAddress.address());
} }
if (addrs == null || addrs.isEmpty()) return CompletableFuture.completedFuture(0); if (addrs == null || addrs.isEmpty()) return CompletableFuture.completedFuture(0);
addrsFuture = CompletableFuture.completedFuture(addrs); addrsFuture = CompletableFuture.completedFuture(addrs);
} }
CompletableFuture<Integer> remoteFuture = addrsFuture.thenCompose((Collection<InetSocketAddress> addrs) -> { CompletableFuture<Integer> remoteFuture = addrsFuture.thenCompose((Collection<WebSocketAddress> addrs) -> {
if (logger.isLoggable(Level.FINEST)) logger.finest("websocket found userid:" + userid + " on " + addrs); if (logger.isLoggable(Level.FINEST)) logger.finest("websocket found userid:" + userid + " on " + addrs);
if (addrs == null || addrs.isEmpty()) return CompletableFuture.completedFuture(0); if (addrs == null || addrs.isEmpty()) return CompletableFuture.completedFuture(0);
CompletableFuture<Integer> future = null; CompletableFuture<Integer> future = null;
for (InetSocketAddress addr : addrs) { for (WebSocketAddress addr : addrs) {
if (addr == null || addr.equals(localSncpAddress)) continue; if (addr == null || addr.equals(wsaddress)) continue;
future = future == null ? remoteNode.forceCloseWebSocket(userid, addr) future = future == null ? remoteNode.forceCloseWebSocket(userid, addr.getTopic(), addr.getAddr())
: future.thenCombine(remoteNode.forceCloseWebSocket(userid, addr), (a, b) -> a + b); : future.thenCombine(remoteNode.forceCloseWebSocket(userid, addr.getTopic(), addr.getAddr()), (a, b) -> a + b);
} }
return future == null ? CompletableFuture.completedFuture(0) : future; return future == null ? CompletableFuture.completedFuture(0) : future;
}); });
@@ -497,7 +519,7 @@ public abstract class WebSocketNode {
} }
if (message0 instanceof CompletableFuture) return ((CompletableFuture) message0).thenApply(msg -> sendMessage(convert, msg, last, userids)); if (message0 instanceof CompletableFuture) return ((CompletableFuture) message0).thenApply(msg -> sendMessage(convert, msg, last, userids));
final Object message = (convert == null || message0 instanceof WebSocketPacket) ? message0 : ((convert instanceof TextConvert) ? new WebSocketPacket(((TextConvert) convert).convertTo(message0), last) : new WebSocketPacket(((BinaryConvert) convert).convertTo(message0), last)); final Object message = (convert == null || message0 instanceof WebSocketPacket) ? message0 : ((convert instanceof TextConvert) ? new WebSocketPacket(((TextConvert) convert).convertTo(message0), last) : new WebSocketPacket(((BinaryConvert) convert).convertTo(message0), last));
if (this.localEngine != null && this.sncpNodeAddresses == null) { //本地模式且没有分布式 if (this.localEngine != null && this.source == null) { //本地模式且没有分布式
return this.localEngine.sendLocalMessage(message, last, userids); return this.localEngine.sendLocalMessage(message, last, userids);
} }
final Object remoteMessage = formatRemoteMessage(message); final Object remoteMessage = formatRemoteMessage(message);
@@ -512,16 +534,16 @@ public abstract class WebSocketNode {
keyuser.put(keys[i], userids[i]); keyuser.put(keys[i], userids[i]);
} }
tryAcquireSemaphore(); tryAcquireSemaphore();
CompletableFuture<Map<String, Collection<InetSocketAddress>>> addrsFuture = sncpNodeAddresses.getCollectionMapAsync(true, InetSocketAddress.class, keys); CompletableFuture<Map<String, Collection<WebSocketAddress>>> addrsFuture = source.getCollectionMapAsync(true, WebSocketAddress.class, keys);
if (semaphore != null) addrsFuture.whenComplete((r, e) -> releaseSemaphore()); if (semaphore != null) addrsFuture.whenComplete((r, e) -> releaseSemaphore());
rsfuture = addrsFuture.thenCompose((Map<String, Collection<InetSocketAddress>> addrs) -> { rsfuture = addrsFuture.thenCompose((Map<String, Collection<WebSocketAddress>> addrs) -> {
if (addrs == null || addrs.isEmpty()) { if (addrs == null || addrs.isEmpty()) {
if (logger.isLoggable(Level.FINER)) logger.finer("websocket not found userids:" + JsonConvert.root().convertTo(userids) + " on any node "); if (logger.isLoggable(Level.FINER)) logger.finer("websocket not found userids:" + JsonConvert.root().convertTo(userids) + " on any node ");
return CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY); return CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY);
} }
Map<InetSocketAddress, List<Serializable>> addrUsers = new HashMap<>(); Map<WebSocketAddress, List<Serializable>> addrUsers = new HashMap<>();
addrs.forEach((key, as) -> { addrs.forEach((key, as) -> {
for (InetSocketAddress a : as) { for (WebSocketAddress a : as) {
addrUsers.computeIfAbsent(a, k -> new ArrayList<>()).add(keyuser.get(key)); addrUsers.computeIfAbsent(a, k -> new ArrayList<>()).add(keyuser.get(key));
} }
}); });
@@ -529,7 +551,7 @@ public abstract class WebSocketNode {
logger.finest("websocket(localaddr=" + localSncpAddress + ", userids=" + JsonConvert.root().convertTo(userids) + ") found message-addr-userids: " + addrUsers); logger.finest("websocket(localaddr=" + localSncpAddress + ", userids=" + JsonConvert.root().convertTo(userids) + ") found message-addr-userids: " + addrUsers);
} }
CompletableFuture<Integer> future = null; CompletableFuture<Integer> future = null;
for (Map.Entry<InetSocketAddress, List<Serializable>> en : addrUsers.entrySet()) { for (Map.Entry<WebSocketAddress, List<Serializable>> en : addrUsers.entrySet()) {
Serializable[] oneaddrUserids = en.getValue().toArray(new Serializable[en.getValue().size()]); Serializable[] oneaddrUserids = en.getValue().toArray(new Serializable[en.getValue().size()]);
future = future == null ? sendOneAddrMessage(en.getKey(), remoteMessage, last, oneaddrUserids) future = future == null ? sendOneAddrMessage(en.getKey(), remoteMessage, last, oneaddrUserids)
: future.thenCombine(sendOneAddrMessage(en.getKey(), remoteMessage, last, oneaddrUserids), (a, b) -> a | b); : future.thenCombine(sendOneAddrMessage(en.getKey(), remoteMessage, last, oneaddrUserids), (a, b) -> a | b);
@@ -556,17 +578,17 @@ public abstract class WebSocketNode {
if (useraddrs == null || useraddrs.length < 1) return CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY); if (useraddrs == null || useraddrs.length < 1) return CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY);
if (message0 instanceof CompletableFuture) return ((CompletableFuture) message0).thenApply(msg -> sendMessage(convert, msg, last, useraddrs)); if (message0 instanceof CompletableFuture) return ((CompletableFuture) message0).thenApply(msg -> sendMessage(convert, msg, last, useraddrs));
final Object message = (convert == null || message0 instanceof WebSocketPacket) ? message0 : ((convert instanceof TextConvert) ? new WebSocketPacket(((TextConvert) convert).convertTo(message0), last) : new WebSocketPacket(((BinaryConvert) convert).convertTo(message0), last)); final Object message = (convert == null || message0 instanceof WebSocketPacket) ? message0 : ((convert instanceof TextConvert) ? new WebSocketPacket(((TextConvert) convert).convertTo(message0), last) : new WebSocketPacket(((BinaryConvert) convert).convertTo(message0), last));
if (this.localEngine != null && this.sncpNodeAddresses == null) { //本地模式且没有分布式 if (this.localEngine != null && this.source == null) { //本地模式且没有分布式
return this.localEngine.sendLocalMessage(message, last, userAddressToUserids(useraddrs)); return this.localEngine.sendLocalMessage(message, last, userAddressToUserids(useraddrs));
} }
final Object remoteMessage = formatRemoteMessage(message); final Object remoteMessage = formatRemoteMessage(message);
final Map<InetSocketAddress, List<Serializable>> addrUsers = userAddressToAddrMap(useraddrs); final Map<WebSocketAddress, List<Serializable>> addrUsers = userAddressToAddrMap(useraddrs);
if (logger.isLoggable(Level.FINEST)) { if (logger.isLoggable(Level.FINEST)) {
logger.finest("websocket(localaddr=" + localSncpAddress + ", useraddrs=" + JsonConvert.root().convertTo(useraddrs) + ") found message-addr-userids: " + addrUsers); logger.finest("websocket(localaddr=" + localSncpAddress + ", useraddrs=" + JsonConvert.root().convertTo(useraddrs) + ") found message-addr-userids: " + addrUsers);
} }
CompletableFuture<Integer> future = null; CompletableFuture<Integer> future = null;
for (Map.Entry<InetSocketAddress, List<Serializable>> en : addrUsers.entrySet()) { for (Map.Entry<WebSocketAddress, List<Serializable>> en : addrUsers.entrySet()) {
Serializable[] oneaddrUserids = en.getValue().toArray(new Serializable[en.getValue().size()]); Serializable[] oneaddrUserids = en.getValue().toArray(new Serializable[en.getValue().size()]);
future = future == null ? sendOneAddrMessage(en.getKey(), remoteMessage, last, oneaddrUserids) future = future == null ? sendOneAddrMessage(en.getKey(), remoteMessage, last, oneaddrUserids)
: future.thenCombine(sendOneAddrMessage(en.getKey(), remoteMessage, last, oneaddrUserids), (a, b) -> a | b); : future.thenCombine(sendOneAddrMessage(en.getKey(), remoteMessage, last, oneaddrUserids), (a, b) -> a | b);
@@ -582,7 +604,7 @@ public abstract class WebSocketNode {
} }
CompletableFuture<Integer> localFuture = null; CompletableFuture<Integer> localFuture = null;
if (this.localEngine != null) localFuture = localEngine.sendLocalMessage(message, last, userid); if (this.localEngine != null) localFuture = localEngine.sendLocalMessage(message, last, userid);
if (this.sncpNodeAddresses == null || this.remoteNode == null) { if (this.source == null || this.remoteNode == null) {
if (logger.isLoggable(Level.FINEST)) logger.finest("websocket remote node is null"); if (logger.isLoggable(Level.FINEST)) logger.finest("websocket remote node is null");
//没有CacheSource就不会有分布式节点 //没有CacheSource就不会有分布式节点
return localFuture == null ? CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY) : localFuture; return localFuture == null ? CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY) : localFuture;
@@ -590,40 +612,40 @@ public abstract class WebSocketNode {
//远程节点发送消息 //远程节点发送消息
final Object remoteMessage = formatRemoteMessage(message); final Object remoteMessage = formatRemoteMessage(message);
tryAcquireSemaphore(); tryAcquireSemaphore();
CompletableFuture<Collection<InetSocketAddress>> addrsFuture = sncpNodeAddresses.getCollectionAsync(SOURCE_SNCP_USERID_PREFIX + userid, InetSocketAddress.class); CompletableFuture<Collection<WebSocketAddress>> addrsFuture = source.getCollectionAsync(SOURCE_SNCP_USERID_PREFIX + userid, WebSocketAddress.class);
if (semaphore != null) addrsFuture.whenComplete((r, e) -> releaseSemaphore()); if (semaphore != null) addrsFuture.whenComplete((r, e) -> releaseSemaphore());
CompletableFuture<Integer> remoteFuture = addrsFuture.thenCompose((Collection<InetSocketAddress> addrs) -> { CompletableFuture<Integer> remoteFuture = addrsFuture.thenCompose((Collection<WebSocketAddress> addrs) -> {
if (addrs == null || addrs.isEmpty()) { if (addrs == null || addrs.isEmpty()) {
if (logger.isLoggable(Level.FINER)) logger.finer("websocket not found userid:" + userid + " on any node "); if (logger.isLoggable(Level.FINER)) logger.finer("websocket not found userid:" + userid + " on any node ");
return CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY); return CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY);
} }
if (logger.isLoggable(Level.FINEST)) logger.finest("websocket(localaddr=" + localSncpAddress + ") found userid:" + userid + " on " + addrs); if (logger.isLoggable(Level.FINEST)) logger.finest("websocket(localaddr=" + wsaddress + ") found userid:" + userid + " on " + addrs);
CompletableFuture<Integer> future = null; CompletableFuture<Integer> future = null;
for (InetSocketAddress addr : addrs) { for (WebSocketAddress addr : addrs) {
if (addr == null || addr.equals(localSncpAddress)) continue; if (addr == null || addr.equals(wsaddress)) continue;
future = future == null ? remoteNode.sendMessage(addr, remoteMessage, last, userid) future = future == null ? remoteNode.sendMessage(addr.getTopic(), addr.getAddr(), remoteMessage, last, userid)
: future.thenCombine(remoteNode.sendMessage(addr, remoteMessage, last, userid), (a, b) -> a | b); : future.thenCombine(remoteNode.sendMessage(addr.getTopic(), addr.getAddr(), remoteMessage, last, userid), (a, b) -> a | b);
} }
return future == null ? CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY) : future; return future == null ? CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY) : future;
}); });
return localFuture == null ? remoteFuture : localFuture.thenCombine(remoteFuture, (a, b) -> a | b); return localFuture == null ? remoteFuture : localFuture.thenCombine(remoteFuture, (a, b) -> a | b);
} }
protected CompletableFuture<Integer> sendOneAddrMessage(final InetSocketAddress sncpAddr, final Object message, final boolean last, final Serializable... userids) { protected CompletableFuture<Integer> sendOneAddrMessage(final WebSocketAddress addr, final Object message, final boolean last, final Serializable... userids) {
if (message instanceof CompletableFuture) return ((CompletableFuture) message).thenApply(msg -> sendOneAddrMessage(sncpAddr, msg, last, userids)); if (message instanceof CompletableFuture) return ((CompletableFuture) message).thenApply(msg -> sendOneAddrMessage(addr, msg, last, userids));
if (logger.isLoggable(Level.FINEST) && this.localEngine == null) { //只打印远程模式的 if (logger.isLoggable(Level.FINEST) && this.localEngine == null) { //只打印远程模式的
logger.finest("websocket want send message {userids:" + JsonConvert.root().convertTo(userids) + ", sncpaddr:" + sncpAddr + ", content:" + (message instanceof WebSocketPacket ? ((WebSocketPacket) message).toSimpleString() : (message instanceof CharSequence ? message : JsonConvert.root().convertTo(message))) + "} from locale node to " + ((this.localEngine != null) ? "locale" : "remote") + " engine"); logger.finest("websocket want send message {userids:" + JsonConvert.root().convertTo(userids) + ", sncpaddr:" + addr + ", content:" + (message instanceof WebSocketPacket ? ((WebSocketPacket) message).toSimpleString() : (message instanceof CharSequence ? message : JsonConvert.root().convertTo(message))) + "} from locale node to " + ((this.localEngine != null) ? "locale" : "remote") + " engine");
} }
if (Objects.equals(sncpAddr, this.localSncpAddress)) { if (Objects.equals(addr, this.wsaddress)) {
return this.localEngine == null ? CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY) : localEngine.sendLocalMessage(message, last, userids); return this.localEngine == null ? CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY) : localEngine.sendLocalMessage(message, last, userids);
} }
if (this.sncpNodeAddresses == null || this.remoteNode == null) { if (this.source == null || this.remoteNode == null) {
if (logger.isLoggable(Level.FINEST)) logger.finest("websocket remote node is null"); if (logger.isLoggable(Level.FINEST)) logger.finest("websocket remote node is null");
//没有CacheSource就不会有分布式节点 //没有CacheSource就不会有分布式节点
return CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY); return CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY);
} }
final Object remoteMessage = formatRemoteMessage(message); final Object remoteMessage = formatRemoteMessage(message);
return remoteNode.sendMessage(sncpAddr, remoteMessage, last, userids); return remoteNode.sendMessage(addr.getTopic(), addr.getAddr(), remoteMessage, last, userids);
} }
protected Serializable[] userAddressToUserids(WebSocketUserAddress... useraddrs) { protected Serializable[] userAddressToUserids(WebSocketUserAddress... useraddrs) {
@@ -635,14 +657,14 @@ public abstract class WebSocketNode {
return set.toArray(new Serializable[set.size()]); return set.toArray(new Serializable[set.size()]);
} }
protected Map<InetSocketAddress, List<Serializable>> userAddressToAddrMap(WebSocketUserAddress... useraddrs) { protected Map<WebSocketAddress, List<Serializable>> userAddressToAddrMap(WebSocketUserAddress... useraddrs) {
final Map<InetSocketAddress, List<Serializable>> addrUsers = new HashMap<>(); final Map<WebSocketAddress, List<Serializable>> addrUsers = new HashMap<>();
for (WebSocketUserAddress userAddress : useraddrs) { for (WebSocketUserAddress userAddress : useraddrs) {
if (userAddress.sncpAddress() != null) { if (userAddress.address() != null) {
addrUsers.computeIfAbsent(userAddress.sncpAddress(), k -> new ArrayList<>()).add(userAddress.userid()); addrUsers.computeIfAbsent(userAddress.address(), k -> new ArrayList<>()).add(userAddress.userid());
} }
if (userAddress.sncpAddresses() != null) { if (userAddress.addresses() != null) {
for (InetSocketAddress addr : userAddress.sncpAddresses()) { for (WebSocketAddress addr : userAddress.addresses()) {
if (addr != null) { if (addr != null) {
addrUsers.computeIfAbsent(addr, k -> new ArrayList<>()).add(userAddress.userid()); addrUsers.computeIfAbsent(addr, k -> new ArrayList<>()).add(userAddress.userid());
} }
@@ -759,22 +781,22 @@ public abstract class WebSocketNode {
public CompletableFuture<Integer> broadcastMessage(final WebSocketRange wsrange, final Convert convert, final Object message0, final boolean last) { public CompletableFuture<Integer> broadcastMessage(final WebSocketRange wsrange, final Convert convert, final Object message0, final boolean last) {
if (message0 instanceof CompletableFuture) return ((CompletableFuture) message0).thenApply(msg -> broadcastMessage(wsrange, convert, msg, last)); if (message0 instanceof CompletableFuture) return ((CompletableFuture) message0).thenApply(msg -> broadcastMessage(wsrange, convert, msg, last));
final Object message = (convert == null || message0 instanceof WebSocketPacket) ? message0 : ((convert instanceof TextConvert) ? new WebSocketPacket(((TextConvert) convert).convertTo(message0), last) : new WebSocketPacket(((BinaryConvert) convert).convertTo(message0), last)); final Object message = (convert == null || message0 instanceof WebSocketPacket) ? message0 : ((convert instanceof TextConvert) ? new WebSocketPacket(((TextConvert) convert).convertTo(message0), last) : new WebSocketPacket(((BinaryConvert) convert).convertTo(message0), last));
if (this.localEngine != null && this.sncpNodeAddresses == null) { //本地模式且没有分布式 if (this.localEngine != null && this.source == null) { //本地模式且没有分布式
return this.localEngine.broadcastLocalMessage(wsrange, message, last); return this.localEngine.broadcastLocalMessage(wsrange, message, last);
} }
final Object remoteMessage = formatRemoteMessage(message); final Object remoteMessage = formatRemoteMessage(message);
CompletableFuture<Integer> localFuture = this.localEngine == null ? null : this.localEngine.broadcastLocalMessage(wsrange, message, last); CompletableFuture<Integer> localFuture = this.localEngine == null ? null : this.localEngine.broadcastLocalMessage(wsrange, message, last);
tryAcquireSemaphore(); tryAcquireSemaphore();
CompletableFuture<Collection<InetSocketAddress>> addrsFuture = sncpNodeAddresses.getCollectionAsync(SOURCE_SNCP_ADDRS_KEY, InetSocketAddress.class); CompletableFuture<Collection<WebSocketAddress>> addrsFuture = source.getCollectionAsync(SOURCE_SNCP_NODES_KEY, WebSocketAddress.class);
if (semaphore != null) addrsFuture.whenComplete((r, e) -> releaseSemaphore()); if (semaphore != null) addrsFuture.whenComplete((r, e) -> releaseSemaphore());
CompletableFuture<Integer> remoteFuture = addrsFuture.thenCompose((Collection<InetSocketAddress> addrs) -> { CompletableFuture<Integer> remoteFuture = addrsFuture.thenCompose((Collection<WebSocketAddress> addrs) -> {
if (logger.isLoggable(Level.FINEST)) logger.finest("websocket broadcast message (" + remoteMessage + ") on " + addrs); if (logger.isLoggable(Level.FINEST)) logger.finest("websocket broadcast message (" + remoteMessage + ") on " + addrs);
if (addrs == null || addrs.isEmpty()) return CompletableFuture.completedFuture(0); if (addrs == null || addrs.isEmpty()) return CompletableFuture.completedFuture(0);
CompletableFuture<Integer> future = null; CompletableFuture<Integer> future = null;
for (InetSocketAddress addr : addrs) { for (WebSocketAddress addr : addrs) {
if (addr == null || addr.equals(localSncpAddress)) continue; if (addr == null || addr.equals(wsaddress)) continue;
future = future == null ? remoteNode.broadcastMessage(addr, wsrange, remoteMessage, last) future = future == null ? remoteNode.broadcastMessage(addr.getTopic(), addr.getAddr(), wsrange, remoteMessage, last)
: future.thenCombine(remoteNode.broadcastMessage(addr, wsrange, remoteMessage, last), (a, b) -> a | b); : future.thenCombine(remoteNode.broadcastMessage(addr.getTopic(), addr.getAddr(), wsrange, remoteMessage, last), (a, b) -> a | b);
} }
return future == null ? CompletableFuture.completedFuture(0) : future; return future == null ? CompletableFuture.completedFuture(0) : future;
}); });
@@ -790,21 +812,21 @@ public abstract class WebSocketNode {
*/ */
@Local @Local
public CompletableFuture<Integer> broadcastAction(final WebSocketAction action) { public CompletableFuture<Integer> broadcastAction(final WebSocketAction action) {
if (this.localEngine != null && this.sncpNodeAddresses == null) { //本地模式且没有分布式 if (this.localEngine != null && this.source == null) { //本地模式且没有分布式
return this.localEngine.broadcastLocalAction(action); return this.localEngine.broadcastLocalAction(action);
} }
CompletableFuture<Integer> localFuture = this.localEngine == null ? null : this.localEngine.broadcastLocalAction(action); CompletableFuture<Integer> localFuture = this.localEngine == null ? null : this.localEngine.broadcastLocalAction(action);
tryAcquireSemaphore(); tryAcquireSemaphore();
CompletableFuture<Collection<InetSocketAddress>> addrsFuture = sncpNodeAddresses.getCollectionAsync(SOURCE_SNCP_ADDRS_KEY, InetSocketAddress.class); CompletableFuture<Collection<WebSocketAddress>> addrsFuture = source.getCollectionAsync(SOURCE_SNCP_NODES_KEY, WebSocketAddress.class);
if (semaphore != null) addrsFuture.whenComplete((r, e) -> releaseSemaphore()); if (semaphore != null) addrsFuture.whenComplete((r, e) -> releaseSemaphore());
CompletableFuture<Integer> remoteFuture = addrsFuture.thenCompose((Collection<InetSocketAddress> addrs) -> { CompletableFuture<Integer> remoteFuture = addrsFuture.thenCompose((Collection<WebSocketAddress> addrs) -> {
if (logger.isLoggable(Level.FINEST)) logger.finest("websocket broadcast action (" + action + ") on " + addrs); if (logger.isLoggable(Level.FINEST)) logger.finest("websocket broadcast action (" + action + ") on " + addrs);
if (addrs == null || addrs.isEmpty()) return CompletableFuture.completedFuture(0); if (addrs == null || addrs.isEmpty()) return CompletableFuture.completedFuture(0);
CompletableFuture<Integer> future = null; CompletableFuture<Integer> future = null;
for (InetSocketAddress addr : addrs) { for (WebSocketAddress addr : addrs) {
if (addr == null || addr.equals(localSncpAddress)) continue; if (addr == null || addr.equals(wsaddress)) continue;
future = future == null ? remoteNode.broadcastAction(addr, action) future = future == null ? remoteNode.broadcastAction(addr.getTopic(), addr.getAddr(), action)
: future.thenCombine(remoteNode.broadcastAction(addr, action), (a, b) -> a | b); : future.thenCombine(remoteNode.broadcastAction(addr.getTopic(), addr.getAddr(), action), (a, b) -> a | b);
} }
return future == null ? CompletableFuture.completedFuture(0) : future; return future == null ? CompletableFuture.completedFuture(0) : future;
}); });
@@ -830,7 +852,7 @@ public abstract class WebSocketNode {
} }
return sendAction(action, useraddrs); return sendAction(action, useraddrs);
} }
if (this.localEngine != null && this.sncpNodeAddresses == null) { //本地模式且没有分布式 if (this.localEngine != null && this.source == null) { //本地模式且没有分布式
return this.localEngine.sendLocalAction(action, userids); return this.localEngine.sendLocalAction(action, userids);
} }
CompletableFuture<Integer> rsfuture; CompletableFuture<Integer> rsfuture;
@@ -844,16 +866,16 @@ public abstract class WebSocketNode {
keyuser.put(keys[i], userids[i]); keyuser.put(keys[i], userids[i]);
} }
tryAcquireSemaphore(); tryAcquireSemaphore();
CompletableFuture<Map<String, Collection<InetSocketAddress>>> addrsFuture = sncpNodeAddresses.getCollectionMapAsync(true, InetSocketAddress.class, keys); CompletableFuture<Map<String, Collection<WebSocketAddress>>> addrsFuture = source.getCollectionMapAsync(true, WebSocketAddress.class, keys);
if (semaphore != null) addrsFuture.whenComplete((r, e) -> releaseSemaphore()); if (semaphore != null) addrsFuture.whenComplete((r, e) -> releaseSemaphore());
rsfuture = addrsFuture.thenCompose((Map<String, Collection<InetSocketAddress>> addrs) -> { rsfuture = addrsFuture.thenCompose((Map<String, Collection<WebSocketAddress>> addrs) -> {
if (addrs == null || addrs.isEmpty()) { if (addrs == null || addrs.isEmpty()) {
if (logger.isLoggable(Level.FINER)) logger.finer("websocket not found userids:" + JsonConvert.root().convertTo(userids) + " on any node "); if (logger.isLoggable(Level.FINER)) logger.finer("websocket not found userids:" + JsonConvert.root().convertTo(userids) + " on any node ");
return CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY); return CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY);
} }
Map<InetSocketAddress, List<Serializable>> addrUsers = new HashMap<>(); Map<WebSocketAddress, List<Serializable>> addrUsers = new HashMap<>();
addrs.forEach((key, as) -> { addrs.forEach((key, as) -> {
for (InetSocketAddress a : as) { for (WebSocketAddress a : as) {
addrUsers.computeIfAbsent(a, k -> new ArrayList<>()).add(keyuser.get(key)); addrUsers.computeIfAbsent(a, k -> new ArrayList<>()).add(keyuser.get(key));
} }
}); });
@@ -861,7 +883,7 @@ public abstract class WebSocketNode {
logger.finest("websocket(localaddr=" + localSncpAddress + ", userids=" + JsonConvert.root().convertTo(userids) + ") found action-userid-addrs: " + addrUsers); logger.finest("websocket(localaddr=" + localSncpAddress + ", userids=" + JsonConvert.root().convertTo(userids) + ") found action-userid-addrs: " + addrUsers);
} }
CompletableFuture<Integer> future = null; CompletableFuture<Integer> future = null;
for (Map.Entry<InetSocketAddress, List<Serializable>> en : addrUsers.entrySet()) { for (Map.Entry<WebSocketAddress, List<Serializable>> en : addrUsers.entrySet()) {
Serializable[] oneaddrUserids = en.getValue().toArray(new Serializable[en.getValue().size()]); Serializable[] oneaddrUserids = en.getValue().toArray(new Serializable[en.getValue().size()]);
future = future == null ? sendOneAddrAction(en.getKey(), action, oneaddrUserids) future = future == null ? sendOneAddrAction(en.getKey(), action, oneaddrUserids)
: future.thenCombine(sendOneAddrAction(en.getKey(), action, oneaddrUserids), (a, b) -> a | b); : future.thenCombine(sendOneAddrAction(en.getKey(), action, oneaddrUserids), (a, b) -> a | b);
@@ -884,16 +906,16 @@ public abstract class WebSocketNode {
@Local @Local
public CompletableFuture<Integer> sendAction(final WebSocketAction action, final WebSocketUserAddress... useraddrs) { public CompletableFuture<Integer> sendAction(final WebSocketAction action, final WebSocketUserAddress... useraddrs) {
if (useraddrs == null || useraddrs.length < 1) return CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY); if (useraddrs == null || useraddrs.length < 1) return CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY);
if (this.localEngine != null && this.sncpNodeAddresses == null) { //本地模式且没有分布式 if (this.localEngine != null && this.source == null) { //本地模式且没有分布式
return this.localEngine.sendLocalAction(action, userAddressToUserids(useraddrs)); return this.localEngine.sendLocalAction(action, userAddressToUserids(useraddrs));
} }
final Map<InetSocketAddress, List<Serializable>> addrUsers = userAddressToAddrMap(useraddrs); final Map<WebSocketAddress, List<Serializable>> addrUsers = userAddressToAddrMap(useraddrs);
if (logger.isLoggable(Level.FINEST)) { if (logger.isLoggable(Level.FINEST)) {
logger.finest("websocket(localaddr=" + localSncpAddress + ", useraddrs=" + JsonConvert.root().convertTo(useraddrs) + ") found action-userid-addrs: " + addrUsers); logger.finest("websocket(localaddr=" + localSncpAddress + ", useraddrs=" + JsonConvert.root().convertTo(useraddrs) + ") found action-userid-addrs: " + addrUsers);
} }
CompletableFuture<Integer> future = null; CompletableFuture<Integer> future = null;
for (Map.Entry<InetSocketAddress, List<Serializable>> en : addrUsers.entrySet()) { for (Map.Entry<WebSocketAddress, List<Serializable>> en : addrUsers.entrySet()) {
Serializable[] oneaddrUserids = en.getValue().toArray(new Serializable[en.getValue().size()]); Serializable[] oneaddrUserids = en.getValue().toArray(new Serializable[en.getValue().size()]);
future = future == null ? sendOneAddrAction(en.getKey(), action, oneaddrUserids) future = future == null ? sendOneAddrAction(en.getKey(), action, oneaddrUserids)
: future.thenCombine(sendOneAddrAction(en.getKey(), action, oneaddrUserids), (a, b) -> a | b); : future.thenCombine(sendOneAddrAction(en.getKey(), action, oneaddrUserids), (a, b) -> a | b);
@@ -907,45 +929,45 @@ public abstract class WebSocketNode {
} }
CompletableFuture<Integer> localFuture = null; CompletableFuture<Integer> localFuture = null;
if (this.localEngine != null) localFuture = localEngine.sendLocalAction(action, userid); if (this.localEngine != null) localFuture = localEngine.sendLocalAction(action, userid);
if (this.sncpNodeAddresses == null || this.remoteNode == null) { if (this.source == null || this.remoteNode == null) {
if (logger.isLoggable(Level.FINEST)) logger.finest("websocket remote node is null"); if (logger.isLoggable(Level.FINEST)) logger.finest("websocket remote node is null");
//没有CacheSource就不会有分布式节点 //没有CacheSource就不会有分布式节点
return localFuture == null ? CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY) : localFuture; return localFuture == null ? CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY) : localFuture;
} }
//远程节点发送操作 //远程节点发送操作
tryAcquireSemaphore(); tryAcquireSemaphore();
CompletableFuture<Collection<InetSocketAddress>> addrsFuture = sncpNodeAddresses.getCollectionAsync(SOURCE_SNCP_USERID_PREFIX + userid, InetSocketAddress.class); CompletableFuture<Collection<WebSocketAddress>> addrsFuture = source.getCollectionAsync(SOURCE_SNCP_USERID_PREFIX + userid, WebSocketAddress.class);
if (semaphore != null) addrsFuture.whenComplete((r, e) -> releaseSemaphore()); if (semaphore != null) addrsFuture.whenComplete((r, e) -> releaseSemaphore());
CompletableFuture<Integer> remoteFuture = addrsFuture.thenCompose((Collection<InetSocketAddress> addrs) -> { CompletableFuture<Integer> remoteFuture = addrsFuture.thenCompose((Collection<WebSocketAddress> addrs) -> {
if (addrs == null || addrs.isEmpty()) { if (addrs == null || addrs.isEmpty()) {
if (logger.isLoggable(Level.FINER)) logger.finer("websocket not found userid:" + userid + " on any node "); if (logger.isLoggable(Level.FINER)) logger.finer("websocket not found userid:" + userid + " on any node ");
return CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY); return CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY);
} }
if (logger.isLoggable(Level.FINEST)) logger.finest("websocket(localaddr=" + localSncpAddress + ") found userid:" + userid + " on " + addrs); if (logger.isLoggable(Level.FINEST)) logger.finest("websocket(localaddr=" + localSncpAddress + ") found userid:" + userid + " on " + addrs);
CompletableFuture<Integer> future = null; CompletableFuture<Integer> future = null;
for (InetSocketAddress addr : addrs) { for (WebSocketAddress addr : addrs) {
if (addr == null || addr.equals(localSncpAddress)) continue; if (addr == null || addr.equals(wsaddress)) continue;
future = future == null ? remoteNode.sendAction(addr, action, userid) future = future == null ? remoteNode.sendAction(addr.getTopic(), addr.getAddr(), action, userid)
: future.thenCombine(remoteNode.sendAction(addr, action, userid), (a, b) -> a | b); : future.thenCombine(remoteNode.sendAction(addr.getTopic(), addr.getAddr(), action, userid), (a, b) -> a | b);
} }
return future == null ? CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY) : future; return future == null ? CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY) : future;
}); });
return localFuture == null ? remoteFuture : localFuture.thenCombine(remoteFuture, (a, b) -> a | b); return localFuture == null ? remoteFuture : localFuture.thenCombine(remoteFuture, (a, b) -> a | b);
} }
protected CompletableFuture<Integer> sendOneAddrAction(final InetSocketAddress sncpAddr, final WebSocketAction action, final Serializable... userids) { protected CompletableFuture<Integer> sendOneAddrAction(final WebSocketAddress addr, final WebSocketAction action, final Serializable... userids) {
if (logger.isLoggable(Level.FINEST) && this.localEngine == null) { //只打印远程模式的 if (logger.isLoggable(Level.FINEST) && this.localEngine == null) { //只打印远程模式的
logger.finest("websocket want send action {userids:" + JsonConvert.root().convertTo(userids) + ", sncpaddr:" + sncpAddr + ", action:" + action + " from locale node to " + ((this.localEngine != null) ? "locale" : "remote") + " engine"); logger.finest("websocket want send action {userids:" + JsonConvert.root().convertTo(userids) + ", sncpaddr:" + addr + ", action:" + action + " from locale node to " + ((this.localEngine != null) ? "locale" : "remote") + " engine");
} }
if (Objects.equals(sncpAddr, this.localSncpAddress)) { if (Objects.equals(addr, this.wsaddress)) {
return this.localEngine == null ? CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY) : localEngine.sendLocalAction(action, userids); return this.localEngine == null ? CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY) : localEngine.sendLocalAction(action, userids);
} }
if (this.sncpNodeAddresses == null || this.remoteNode == null) { if (this.source == null || this.remoteNode == null) {
if (logger.isLoggable(Level.FINEST)) logger.finest("websocket remote node is null"); if (logger.isLoggable(Level.FINEST)) logger.finest("websocket remote node is null");
//没有CacheSource就不会有分布式节点 //没有CacheSource就不会有分布式节点
return CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY); return CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY);
} }
return remoteNode.sendAction(sncpAddr, action, userids); return remoteNode.sendAction(addr.getTopic(), addr.getAddr(), action, userids);
} }
protected Object formatRemoteMessage(Object message) { protected Object formatRemoteMessage(Object message) {

View File

@@ -9,9 +9,10 @@ import org.redkale.util.Utility;
import java.io.*; import java.io.*;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.AbstractMap; import java.util.*;
import java.util.function.*; import java.util.function.*;
import java.util.logging.*; import java.util.logging.*;
import java.util.zip.*;
import org.redkale.convert.*; import org.redkale.convert.*;
import org.redkale.net.Cryptor; import org.redkale.net.Cryptor;
import org.redkale.util.*; import org.redkale.util.*;
@@ -29,6 +30,8 @@ public final class WebSocketPacket {
static final WebSocketPacket NONE = new WebSocketPacket(); static final WebSocketPacket NONE = new WebSocketPacket();
private static final byte[] EOM_BYTES = new byte[]{0, 0, -1, -1};
public static final WebSocketPacket DEFAULT_PING_PACKET = new WebSocketPacket(FrameType.PING, new byte[0]); public static final WebSocketPacket DEFAULT_PING_PACKET = new WebSocketPacket(FrameType.PING, new byte[0]);
public static enum MessageType { public static enum MessageType {
@@ -82,6 +85,8 @@ public final class WebSocketPacket {
//---------------接收------------------------ //---------------接收------------------------
MessageType receiveType; MessageType receiveType;
boolean receiveCompress;
int receiveCount; int receiveCount;
int receiveLength; int receiveLength;
@@ -148,16 +153,6 @@ public final class WebSocketPacket {
if (mapconvable && !(json instanceof Object[])) throw new IllegalArgumentException(); if (mapconvable && !(json instanceof Object[])) throw new IllegalArgumentException();
} }
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[] duplicateSendBuffers() {
ByteBuffer[] rs = new ByteBuffer[this.sendBuffers.length]; ByteBuffer[] rs = new ByteBuffer[this.sendBuffers.length];
for (int i = 0; i < this.sendBuffers.length; i++) { for (int i = 0; i < this.sendBuffers.length; i++) {
@@ -223,7 +218,7 @@ public final class WebSocketPacket {
* *
* @return ByteBuffer[] * @return ByteBuffer[]
*/ */
ByteBuffer[] encode(final Supplier<ByteBuffer> supplier, final Consumer<ByteBuffer> consumer, final Cryptor cryptor) { void encodePacket(final Supplier<ByteBuffer> supplier, final Consumer<ByteBuffer> consumer, final Cryptor cryptor) {
final byte opcode = (byte) (this.type.getValue() | 0x80); final byte opcode = (byte) (this.type.getValue() | 0x80);
if (this.sendConvert != null) { if (this.sendConvert != null) {
Supplier<ByteBuffer> newsupplier = new Supplier<ByteBuffer>() { Supplier<ByteBuffer> newsupplier = new Supplier<ByteBuffer>() {
@@ -263,7 +258,8 @@ public final class WebSocketPacket {
firstbuf.put(1, (byte) 0x7F); //127 firstbuf.put(1, (byte) 0x7F); //127
firstbuf.putInt(2, contentLength); firstbuf.putInt(2, contentLength);
} }
return buffers; this.sendBuffers = buffers;
return;
} }
ByteBuffer buffer = supplier.get(); //确保ByteBuffer的capacity不能小于128 ByteBuffer buffer = supplier.get(); //确保ByteBuffer的capacity不能小于128
@@ -294,7 +290,8 @@ public final class WebSocketPacket {
buffer.put((byte) len); buffer.put((byte) len);
buffer.put(content); buffer.put(content);
buffer.flip(); buffer.flip();
return new ByteBuffer[]{buffer}; this.sendBuffers = new ByteBuffer[]{buffer};
return;
} }
if (len <= 0xFFFF) { // 65535 if (len <= 0xFFFF) { // 65535
buffer.put(opcode); buffer.put(opcode);
@@ -310,7 +307,8 @@ public final class WebSocketPacket {
if (pend <= 0) { if (pend <= 0) {
buffer.put(content); buffer.put(content);
buffer.flip(); buffer.flip();
return new ByteBuffer[]{buffer}; this.sendBuffers = new ByteBuffer[]{buffer};
return;
} }
buffer.put(content, 0, buffer.remaining()); buffer.put(content, 0, buffer.remaining());
buffer.flip(); buffer.flip();
@@ -325,7 +323,7 @@ public final class WebSocketPacket {
start += capacity; start += capacity;
pend -= capacity; pend -= capacity;
} }
return buffers; this.sendBuffers = buffers;
} }
// public static void main(String[] args) throws Throwable { // public static void main(String[] args) throws Throwable {
@@ -391,10 +389,10 @@ public final class WebSocketPacket {
* *
* @return 返回NONE表示Buffer内容不够 返回this表示解析完成或部分解析完成返回null表示解析异常 * @return 返回NONE表示Buffer内容不够 返回this表示解析完成或部分解析完成返回null表示解析异常
*/ */
WebSocketPacket decode(final Logger logger, final WebSocketRunner runner, final WebSocket webSocket, final int wsmaxbody, WebSocketPacket decodePacket(final Logger logger, final WebSocketRunner runner, final WebSocket webSocket, final int wsmaxbody,
final AbstractMap.SimpleEntry<String, byte[]> halfBytes, final ByteBuffer buffer) { final AbstractMap.SimpleEntry<String, byte[]> halfBytes, final ByteBuffer buffer) {
//开始 //开始
final boolean debug = false; //调试开关 final boolean debug = true; //调试开关
if (debug) logger.log(Level.FINEST, "read websocket message's length = " + buffer.remaining()); if (debug) logger.log(Level.FINEST, "read websocket message's length = " + buffer.remaining());
if (!buffer.hasRemaining()) return NONE; if (!buffer.hasRemaining()) return NONE;
if (buffer.remaining() < 2) { if (buffer.remaining() < 2) {
@@ -404,20 +402,9 @@ public final class WebSocketPacket {
return NONE; return NONE;
} }
final byte opcode = buffer.get(); //第一个字节 final byte opcode = buffer.get(); //第一个字节
this.last = (opcode & 0b1000_0000) != 0; this.last = (opcode & 0B1000_0000) != 0;
this.type = FrameType.valueOf(opcode & 0xF); this.type = FrameType.valueOf(opcode & 0B0000_1111);
if (type == FrameType.CLOSE) {
if (debug) logger.log(Level.FINEST, " receive close command from websocket client");
}
if (type == null) {
logger.log(Level.SEVERE, " receive unknown frametype(opcode=" + (opcode & 0xF) + ") from websocket client");
}
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 表示一个后续帧 //0x00 表示一个后续帧
//0x01 表示一个文本帧 //0x01 表示一个文本帧
//0x02 表示一个二进制帧 //0x02 表示一个二进制帧
@@ -426,12 +413,26 @@ public final class WebSocketPacket {
//0x9 表示一个ping //0x9 表示一个ping
//0xA 表示一个pong //0xA 表示一个pong
//0x0B-0F 为以后的控制帧保留 //0x0B-0F 为以后的控制帧保留
final boolean control = (opcode & 0b0000_1000) != 0; //是否控制帧 final boolean control = (opcode & 0B0000_1000) != 0; //是否控制帧
this.receiveCompress = !control && webSocket.inflater != null && (opcode & 0B0100_0000) != 0; //rsv1 为 1
if (type == FrameType.CLOSE) {
if (debug) logger.log(Level.FINEST, " receive close command from websocket client");
}
if (type == null) {
logger.log(Level.SEVERE, " receive unknown frametype(opcode=" + (opcode & 0B0000_1111) + ") from websocket client");
}
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
}
final byte crcode = buffer.get(); //第二个字节 final byte crcode = buffer.get(); //第二个字节
byte lengthCode = crcode; byte lengthCode = crcode;
final boolean masked = (lengthCode & 0x80) == 0x80; final boolean masked = (lengthCode & 0x80) == 0x80;
if (masked) lengthCode ^= 0x80; //mask if (masked) lengthCode ^= 0x80; //mask
if (debug) logger.log(Level.FINEST, " receive type=" + type + ", control=" + control + ", opcode=" + opcode + ", masked=" + masked + ", remaining=" + buffer.remaining());
//判断Buffer剩余内容够不够基本信息的创建 //判断Buffer剩余内容够不够基本信息的创建
int minBufferLength = ((lengthCode <= 0x7D) ? 0 : (lengthCode == 0x7E ? 2 : 4)) + (masked ? 4 : 0); int minBufferLength = ((lengthCode <= 0x7D) ? 0 : (lengthCode == 0x7E ? 2 : 4)) + (masked ? 4 : 0);
@@ -441,6 +442,7 @@ public final class WebSocketPacket {
bs[1] = crcode; bs[1] = crcode;
buffer.get(bs, 2, buffer.remaining()); buffer.get(bs, 2, buffer.remaining());
halfBytes.setValue(bs); halfBytes.setValue(bs);
if (debug) logger.log(Level.FINEST, " receive not enough bufferlength =" + bs.length);
return NONE; return NONE;
} }
@@ -506,14 +508,18 @@ public final class WebSocketPacket {
if (selfType == FrameType.TEXT) { if (selfType == FrameType.TEXT) {
Convert textConvert = webSocket.getTextConvert(); Convert textConvert = webSocket.getTextConvert();
if (textConvert == null || (!runner.mergemsg && (series || !this.last))) { if (textConvert == null || (!runner.mergemsg && (series || !this.last))) {
this.receiveMessage = new String(this.getReceiveBytes(buffers), StandardCharsets.UTF_8); this.receiveMessage = new String(this.getReceiveBytes(webSocket, selfType, buffers), StandardCharsets.UTF_8);
this.receiveType = MessageType.STRING; this.receiveType = MessageType.STRING;
} else { } else {
if (this.last || !runner.mergemsg) { if (this.last || !runner.mergemsg) {
if (runner.currSeriesMergeMessage == null) { if (runner.currSeriesMergeMessage == null) {
this.receiveMessage = textConvert.convertFrom(webSocket._messageTextType, this.receiveMasker, buffers); if (this.receiveCompress) {
this.receiveMessage = textConvert.convertFrom(webSocket._messageTextType, this.getReceiveBytes(webSocket, selfType, buffers));
} else { } else {
runner.currSeriesMergeMessage.write(this.getReceiveBytes(buffers)); this.receiveMessage = textConvert.convertFrom(webSocket._messageTextType, this.receiveMasker, buffers);
}
} else {
runner.currSeriesMergeMessage.write(this.getReceiveBytes(webSocket, selfType, buffers));
try { try {
this.receiveMessage = textConvert.convertFrom(webSocket._messageTextType, runner.currSeriesMergeMessage.getBytes()); this.receiveMessage = textConvert.convertFrom(webSocket._messageTextType, runner.currSeriesMergeMessage.getBytes());
} finally { } finally {
@@ -522,7 +528,7 @@ public final class WebSocketPacket {
} }
} else { } else {
if (runner.currSeriesMergeMessage == null) runner.currSeriesMergeMessage = new ByteArray(); if (runner.currSeriesMergeMessage == null) runner.currSeriesMergeMessage = new ByteArray();
runner.currSeriesMergeMessage.write(this.getReceiveBytes(buffers)); runner.currSeriesMergeMessage.write(this.getReceiveBytes(webSocket, selfType, buffers));
this.receiveMessage = MESSAGE_NIL; this.receiveMessage = MESSAGE_NIL;
} }
this.receiveCount = this.receiveLength; this.receiveCount = this.receiveLength;
@@ -531,14 +537,18 @@ public final class WebSocketPacket {
} else if (selfType == FrameType.BINARY) { } else if (selfType == FrameType.BINARY) {
Convert binaryConvert = webSocket.getBinaryConvert(); Convert binaryConvert = webSocket.getBinaryConvert();
if (binaryConvert == null || (!runner.mergemsg && (series || !this.last))) { if (binaryConvert == null || (!runner.mergemsg && (series || !this.last))) {
this.receiveMessage = this.getReceiveBytes(buffers); this.receiveMessage = this.getReceiveBytes(webSocket, selfType, buffers);
this.receiveType = MessageType.BYTES; this.receiveType = MessageType.BYTES;
} else { } else {
if (this.last || !runner.mergemsg) { if (this.last || !runner.mergemsg) {
if (runner.currSeriesMergeMessage == null) { if (runner.currSeriesMergeMessage == null) {
this.receiveMessage = binaryConvert.convertFrom(webSocket._messageTextType, this.receiveMasker, buffers); if (this.receiveCompress) {
this.receiveMessage = binaryConvert.convertFrom(webSocket._messageTextType, this.getReceiveBytes(webSocket, selfType, buffers));
} else { } else {
runner.currSeriesMergeMessage.write(this.getReceiveBytes(buffers)); this.receiveMessage = binaryConvert.convertFrom(webSocket._messageTextType, this.receiveMasker, buffers);
}
} else {
runner.currSeriesMergeMessage.write(this.getReceiveBytes(webSocket, selfType, buffers));
try { try {
this.receiveMessage = binaryConvert.convertFrom(webSocket._messageTextType, runner.currSeriesMergeMessage.getBytes()); this.receiveMessage = binaryConvert.convertFrom(webSocket._messageTextType, runner.currSeriesMergeMessage.getBytes());
} finally { } finally {
@@ -547,20 +557,20 @@ public final class WebSocketPacket {
} }
} else { } else {
if (runner.currSeriesMergeMessage == null) runner.currSeriesMergeMessage = new ByteArray(); if (runner.currSeriesMergeMessage == null) runner.currSeriesMergeMessage = new ByteArray();
runner.currSeriesMergeMessage.write(this.getReceiveBytes(buffers)); runner.currSeriesMergeMessage.write(this.getReceiveBytes(webSocket, selfType, buffers));
this.receiveMessage = MESSAGE_NIL; this.receiveMessage = MESSAGE_NIL;
} }
this.receiveCount = this.receiveLength; this.receiveCount = this.receiveLength;
this.receiveType = MessageType.OBJECT; this.receiveType = MessageType.OBJECT;
} }
} else if (selfType == FrameType.PING) { } else if (selfType == FrameType.PING) {
this.receiveMessage = this.getReceiveBytes(buffers); this.receiveMessage = this.getReceiveBytes(webSocket, selfType, buffers);
this.receiveType = MessageType.BYTES; this.receiveType = MessageType.BYTES;
} else if (selfType == FrameType.PONG) { } else if (selfType == FrameType.PONG) {
this.receiveMessage = this.getReceiveBytes(buffers); this.receiveMessage = this.getReceiveBytes(webSocket, selfType, buffers);
this.receiveType = MessageType.BYTES; this.receiveType = MessageType.BYTES;
} else if (selfType == FrameType.CLOSE) { } else if (selfType == FrameType.CLOSE) {
this.receiveMessage = this.getReceiveBytes(buffers); this.receiveMessage = this.getReceiveBytes(webSocket, selfType, buffers);
this.receiveType = MessageType.BYTES; this.receiveType = MessageType.BYTES;
} }
} }
@@ -569,7 +579,7 @@ public final class WebSocketPacket {
return this.receiveLength <= this.receiveCount; return this.receiveLength <= this.receiveCount;
} }
byte[] getReceiveBytes(ByteBuffer... buffers) { byte[] getReceiveBytes(WebSocket webSocket, FrameType frameType, ByteBuffer... buffers) {
final int length = this.receiveLength; final int length = this.receiveLength;
if (length == 0) return new byte[0]; if (length == 0) return new byte[0];
byte[] bs = new byte[length]; byte[] bs = new byte[length];
@@ -587,6 +597,24 @@ public final class WebSocketPacket {
bs[i] = mask.unmask(bs[i]); bs[i] = mask.unmask(bs[i]);
} }
} }
if (bs.length > 6 && this.receiveCompress && (frameType == FrameType.BINARY || frameType == FrameType.TEXT || frameType == FrameType.SERIES)) {
Inflater inflater = webSocket.inflater;
inflater.reset();
ByteArrayOutputStream baos = new ByteArrayOutputStream(bs.length);
inflater.setInput(Utility.append(bs, EOM_BYTES));
byte[] buff = new byte[1024];
try {
while (!inflater.finished()) {
int count = inflater.inflate(buff);
if (count == 0) break;
baos.write(buff, 0, count);
}
return baos.toByteArray();
} catch (Exception ex) {
ex.printStackTrace();
return bs;
}
}
return bs; return bs;
} }

View File

@@ -108,7 +108,7 @@ class WebSocketRunner implements Runnable {
if (onePacket != null) packets.add(onePacket); if (onePacket != null) packets.add(onePacket);
try { try {
while (true) { while (true) {
WebSocketPacket packet = new WebSocketPacket().decode(context.getLogger(), self, webSocket, wsmaxbody, halfBytes, readBuffer); WebSocketPacket packet = new WebSocketPacket().decodePacket(context.getLogger(), self, webSocket, wsmaxbody, halfBytes, readBuffer);
if (packet == WebSocketPacket.NONE) break; //解析完毕但是buffer有多余字节 if (packet == WebSocketPacket.NONE) break; //解析完毕但是buffer有多余字节
if (packet != null && !packet.isReceiveFinished()) { if (packet != null && !packet.isReceiveFinished()) {
unfinishPacket = packet; unfinishPacket = packet;
@@ -230,7 +230,8 @@ class WebSocketRunner implements Runnable {
//System.out.println("推送消息"); //System.out.println("推送消息");
final CompletableFuture<Integer> futureResult = new CompletableFuture<>(); final CompletableFuture<Integer> futureResult = new CompletableFuture<>();
try { try {
ByteBuffer[] buffers = packet.sendBuffers != null ? packet.duplicateSendBuffers() : packet.encode(webSocket._channel.getBufferSupplier(), webSocket._channel.getBufferConsumer(), webSocket._engine.cryptor); if (packet.sendBuffers == null) packet.encodePacket(webSocket._channel.getBufferSupplier(), webSocket._channel.getBufferConsumer(), webSocket._engine.cryptor);
ByteBuffer[] buffers = packet.duplicateSendBuffers();
//if (debug) context.getLogger().log(Level.FINEST, "wsrunner.sending websocket message: " + packet); //if (debug) context.getLogger().log(Level.FINEST, "wsrunner.sending websocket message: " + packet);
CompletionHandler<Integer, ByteBuffer[]> handler = new CompletionHandler<Integer, ByteBuffer[]>() { CompletionHandler<Integer, ByteBuffer[]> handler = new CompletionHandler<Integer, ByteBuffer[]>() {

View File

@@ -15,8 +15,10 @@ import java.util.*;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import java.util.function.*; import java.util.function.*;
import java.util.logging.*; import java.util.logging.*;
import java.util.zip.*;
import javax.annotation.*; import javax.annotation.*;
import org.redkale.convert.Convert; import org.redkale.convert.Convert;
import org.redkale.mq.MessageAgent;
import org.redkale.net.Cryptor; import org.redkale.net.Cryptor;
import org.redkale.service.*; import org.redkale.service.*;
import org.redkale.util.*; import org.redkale.util.*;
@@ -97,6 +99,10 @@ public abstract class WebSocketServlet extends HttpServlet implements Resourcabl
//同RestWebSocket.cryptor, 变量名不可改, 被Rest.createRestWebSocketServlet用到 //同RestWebSocket.cryptor, 变量名不可改, 被Rest.createRestWebSocketServlet用到
protected Cryptor cryptor; protected Cryptor cryptor;
protected boolean permessageDeflate = false;
protected MessageAgent messageAgent;
@Resource(name = "jsonconvert") @Resource(name = "jsonconvert")
protected Convert jsonConvert; protected Convert jsonConvert;
@@ -146,6 +152,7 @@ public abstract class WebSocketServlet extends HttpServlet implements Resourcabl
if (logger.isLoggable(Level.WARNING)) logger.warning("Not found WebSocketNode, create a default value for " + getClass().getName()); if (logger.isLoggable(Level.WARNING)) logger.warning("Not found WebSocketNode, create a default value for " + getClass().getName());
} }
if (this.node.sendConvert == null) this.node.sendConvert = this.sendConvert; if (this.node.sendConvert == null) this.node.sendConvert = this.sendConvert;
if (this.messageAgent != null) this.node.messageAgent = this.messageAgent;
{ {
AnyValue props = conf; AnyValue props = conf;
if (conf != null && conf.getAnyValue("properties") != null) props = conf.getAnyValue("properties"); if (conf != null && conf.getAnyValue("properties") != null) props = conf.getAnyValue("properties");
@@ -210,6 +217,10 @@ public abstract class WebSocketServlet extends HttpServlet implements Resourcabl
webSocket._remoteAddress = request.getRemoteAddress(); webSocket._remoteAddress = request.getRemoteAddress();
webSocket._remoteAddr = request.getRemoteAddr(); webSocket._remoteAddr = request.getRemoteAddr();
webSocket._sncpAddress = this.node.localSncpAddress; webSocket._sncpAddress = this.node.localSncpAddress;
if (this.permessageDeflate && request.getHeader("Sec-WebSocket-Extensions", "").contains("permessage-deflate")) {
webSocket.deflater = new Deflater(Deflater.DEFAULT_COMPRESSION, true);
webSocket.inflater = new Inflater(true);
}
initRestWebSocket(webSocket); initRestWebSocket(webSocket);
CompletableFuture<String> sessionFuture = webSocket.onOpen(request); CompletableFuture<String> sessionFuture = webSocket.onOpen(request);
if (sessionFuture == null) { if (sessionFuture == null) {
@@ -233,6 +244,8 @@ public abstract class WebSocketServlet extends HttpServlet implements Resourcabl
response.setHeader("Connection", "Upgrade"); response.setHeader("Connection", "Upgrade");
response.addHeader("Upgrade", "websocket"); response.addHeader("Upgrade", "websocket");
response.addHeader("Sec-WebSocket-Accept", Base64.getEncoder().encodeToString(bytes)); response.addHeader("Sec-WebSocket-Accept", Base64.getEncoder().encodeToString(bytes));
if (webSocket.deflater != null) response.addHeader("Sec-WebSocket-Extensions", "permessage-deflate");
response.sendBody((ByteBuffer) null, null, new CompletionHandler<Integer, Void>() { response.sendBody((ByteBuffer) null, null, new CompletionHandler<Integer, Void>() {
WebSocketRunner temprunner = null; WebSocketRunner temprunner = null;

View File

@@ -20,44 +20,57 @@ public interface WebSocketUserAddress extends Serializable {
Serializable userid(); Serializable userid();
InetSocketAddress sncpAddress(); WebSocketAddress address();
Collection<InetSocketAddress> sncpAddresses(); Collection<WebSocketAddress> addresses();
public static WebSocketUserAddress create(WebSocketUserAddress userAddress) { public static WebSocketUserAddress create(WebSocketUserAddress userAddress) {
return new SimpleWebSocketUserAddress(userAddress); return new SimpleWebSocketUserAddress(userAddress);
} }
public static WebSocketUserAddress create(Serializable userid, InetSocketAddress sncpAddress) { public static WebSocketUserAddress createTopic(Serializable userid, String mqtopic, InetSocketAddress sncpAddress) {
return new SimpleWebSocketUserAddress(userid, sncpAddress, null); return new SimpleWebSocketUserAddress(userid, mqtopic, sncpAddress);
} }
public static WebSocketUserAddress create(Serializable userid, Collection<InetSocketAddress> sncpAddresses) { public static WebSocketUserAddress create(Serializable userid, WebSocketAddress address) {
return new SimpleWebSocketUserAddress(userid, null, sncpAddresses); return new SimpleWebSocketUserAddress(userid, address);
}
public static WebSocketUserAddress create(Serializable userid, Collection<WebSocketAddress> addresses) {
return new SimpleWebSocketUserAddress(userid, addresses);
} }
public static class SimpleWebSocketUserAddress implements WebSocketUserAddress { public static class SimpleWebSocketUserAddress implements WebSocketUserAddress {
private Serializable userid; private Serializable userid;
private InetSocketAddress sncpAddress; private WebSocketAddress address;
private Collection<InetSocketAddress> sncpAddresses; private Collection<WebSocketAddress> addresses;
public SimpleWebSocketUserAddress() { public SimpleWebSocketUserAddress() {
} }
public SimpleWebSocketUserAddress(Serializable userid, InetSocketAddress sncpAddress, Collection<InetSocketAddress> sncpAddresses) { public SimpleWebSocketUserAddress(Serializable userid, String mqtopic, InetSocketAddress sncpAddress) {
this.userid = userid; this.userid = userid;
this.sncpAddress = sncpAddress; this.address = new WebSocketAddress(mqtopic, sncpAddress);
this.sncpAddresses = sncpAddresses; }
public SimpleWebSocketUserAddress(Serializable userid, WebSocketAddress address) {
this.userid = userid;
this.address = address;
}
public SimpleWebSocketUserAddress(Serializable userid, Collection<WebSocketAddress> addresses) {
this.userid = userid;
this.addresses = addresses;
} }
public SimpleWebSocketUserAddress(WebSocketUserAddress userAddress) { public SimpleWebSocketUserAddress(WebSocketUserAddress userAddress) {
if (userAddress == null) return; if (userAddress == null) return;
this.userid = userAddress.userid(); this.userid = userAddress.userid();
this.sncpAddress = userAddress.sncpAddress(); this.address = userAddress.address();
this.sncpAddresses = userAddress.sncpAddresses(); this.addresses = userAddress.addresses();
} }
@Override @Override
@@ -66,13 +79,13 @@ public interface WebSocketUserAddress extends Serializable {
} }
@Override @Override
public InetSocketAddress sncpAddress() { public WebSocketAddress address() {
return sncpAddress; return address;
} }
@Override @Override
public Collection<InetSocketAddress> sncpAddresses() { public Collection<WebSocketAddress> addresses() {
return sncpAddresses; return addresses;
} }
public Serializable getUserid() { public Serializable getUserid() {
@@ -83,20 +96,20 @@ public interface WebSocketUserAddress extends Serializable {
this.userid = userid; this.userid = userid;
} }
public InetSocketAddress getSncpAddress() { public WebSocketAddress getAddress() {
return sncpAddress; return address;
} }
public void setSncpAddress(InetSocketAddress sncpAddress) { public void setAddress(WebSocketAddress address) {
this.sncpAddress = sncpAddress; this.address = address;
} }
public Collection<InetSocketAddress> getSncpAddresses() { public Collection<WebSocketAddress> getAddresses() {
return sncpAddresses; return addresses;
} }
public void setSncpAddresses(Collection<InetSocketAddress> sncpAddresses) { public void setAddresses(Collection<WebSocketAddress> addresses) {
this.sncpAddresses = sncpAddresses; this.addresses = addresses;
} }
@Override @Override

View File

@@ -0,0 +1,62 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.net.nio;
import java.nio.channels.CompletionHandler;
import java.util.concurrent.*;
/**
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*
* @since 2.1.0
*/
public class NioCompletionHandler<A> implements CompletionHandler<Integer, A>, Runnable {
private final CompletionHandler<Integer, A> handler;
private A attachment;
public ScheduledFuture timeoutFuture;
public NioCompletionHandler(CompletionHandler<Integer, A> handler, A attachment) {
this.handler = handler;
this.attachment = attachment;
}
public void setAttachment(A attachment) {
this.attachment = attachment;
}
@Override
public void completed(Integer result, A attach) {
ScheduledFuture future = this.timeoutFuture;
if (future != null) {
this.timeoutFuture = null;
future.cancel(true);
}
handler.completed(result, attachment);
}
@Override
public void failed(Throwable exc, A attach) {
ScheduledFuture future = this.timeoutFuture;
if (future != null) {
this.timeoutFuture = null;
future.cancel(true);
}
handler.failed(exc, attachment);
}
@Override
public void run() {
handler.failed(new TimeoutException(), attachment);
}
}

View File

@@ -0,0 +1,97 @@
/*
* 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.nio;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.*;
import java.util.concurrent.*;
import java.util.function.Consumer;
import org.redkale.net.TcpNioAsyncConnection;
import org.redkale.util.*;
/**
* 协议处理的IO线程类
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*
* @since 2.1.0
*/
public class NioThread extends Thread {
final Selector selector;
private final ExecutorService executor;
private final ObjectPool<ByteBuffer> bufferPool;
private final ConcurrentLinkedQueue<Consumer<Selector>> registers = new ConcurrentLinkedQueue<>();
private Thread localThread;
private boolean closed;
public NioThread(Selector selector, ExecutorService executor, ObjectPool<ByteBuffer> bufferPool) {
super();
this.selector = selector;
this.executor = executor;
this.bufferPool = bufferPool;
this.setDaemon(true);
}
public void register(Consumer<Selector> consumer) {
registers.offer(consumer);
selector.wakeup();
}
@Override
public void run() {
this.localThread = Thread.currentThread();
while (!this.closed) {
try {
Consumer<Selector> register;
while ((register = registers.poll()) != null) {
register.accept(selector);
}
int count = selector.select();
if (count == 0) continue;
Set<SelectionKey> keys = selector.selectedKeys();
Iterator<SelectionKey> it = keys.iterator();
while (it.hasNext()) {
SelectionKey key = it.next();
it.remove();
TcpNioAsyncConnection conn = (TcpNioAsyncConnection) key.attachment();
if (key.isWritable()) {
//key.interestOps(key.interestOps() & ~SelectionKey.OP_WRITE);
conn.doWrite();
} else if (key.isReadable()) {
conn.doRead();
} else if (key.isConnectable()) {
conn.doConnect();
}
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
public boolean inSameThread() {
return this.localThread == Thread.currentThread();
}
public boolean inSameThread(Thread thread) {
return this.localThread == thread;
}
public void close() {
this.closed = true;
this.interrupt();
}
}

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