366 Commits
1.7.0 ... 1.8.5

Author SHA1 Message Date
Redkale
500545fc93 DataSource.queryMap重载了FilterBean、FilterNode参数等系列方法 2017-10-30 09:49:34 +08:00
Redkale
78f1bd90f4 2017-10-27 16:42:01 +08:00
Redkale
f5a04319d5 2017-10-26 20:04:32 +08:00
Redkale
76ce6787d1 2017-10-26 14:48:15 +08:00
Redkale
bd53d4a8ab 2017-10-26 14:17:24 +08:00
Redkale
53b11116b9 DataSource增加queryMap系列方法 2017-10-26 13:20:05 +08:00
Redkale
734b1bbbb4 2017-10-26 10:23:43 +08:00
Redkale
f014bffb6c Redkale 1.8.5 开始 2017-10-25 09:51:08 +08:00
Redkale
f633e72c5f 2017-10-25 09:26:57 +08:00
Redkale
b8c85284ab 2017-10-25 09:21:48 +08:00
Redkale
a3af6749f6 暂时屏蔽Persist功能 2017-10-25 09:21:00 +08:00
Redkale
9128dffe35 新增Persist注解, 用于Service成员变量数据的临时缓存 2017-10-24 17:30:51 +08:00
Redkale
9273f2917e 2017-10-24 15:23:43 +08:00
Redkale
4d9d09af8c 2017-10-24 10:28:21 +08:00
Redkale
33beb60efe 2017-10-23 11:38:11 +08:00
Redkale
129bcf2f78 2017-10-22 16:01:20 +08:00
Redkale
f6c7dde28f Server增加setThreads方法,可以动态修改线程池的大小 2017-10-22 15:39:00 +08:00
Redkale
8fcd33b511 Copy一份javax.annotation.Resource源码到Redkale中,因JDK9中java.base模块中不包含javax.annotation.Resource 2017-10-22 13:01:03 +08:00
Redkale
66ec26e0ce 2017-10-22 11:14:49 +08:00
Redkale
969f7ada82 2017-10-22 11:04:00 +08:00
Redkale
7e37889372 增加Priority功能, 可以对Service、Filter、HttpServlet的加载顺序进行优先级设置,同时删掉Filter的getIndex方法 2017-10-22 11:00:09 +08:00
Redkale
c819d4d45b WATCH协议服务器默认host改为127.0.0.1 2017-10-22 10:13:24 +08:00
Redkale
6dc59b7abc 2017-10-21 11:07:16 +08:00
Redkale
60b24fa1ae 2017-10-20 10:58:31 +08:00
Redkale
b784993110 WebSocket的sendMessage系列方法增加Stream userids参数 2017-10-20 10:45:56 +08:00
Redkale
cc150a2cc6 @RestUploadFile标记的File对象可以获取到用户上传的文件名 2017-10-20 09:41:34 +08:00
Redkale
f6b407aa44 WebSocket识别Single模式,Single模式下重复登陆时默认会关闭之前的WebSocket连接 2017-10-18 10:47:43 +08:00
Redkale
a69d813bf5 WebSocket增加forceCloseWebSocket系列方法 2017-10-18 10:05:10 +08:00
Redkale
d1eff6144d convertMapTo方法识别CompletableFuture 2017-10-18 09:16:55 +08:00
Redkale
99589387d8 RetResult增加Map<String, String> attach成员变量 2017-10-17 09:42:32 +08:00
Redkale
e0041235fe WebSocket增加sendMap方法 2017-10-16 11:19:58 +08:00
Redkale
84b4eee7b5 重载WebSocket的sendMessage和broadcastMessage系列方法,增加Convert参数 2017-10-16 10:48:14 +08:00
Redkale
96c0a9bfe4 2017-10-15 22:22:31 +08:00
Redkale
89ad976744 增加BigDecimal的序列化和反序列化 2017-10-15 22:01:16 +08:00
Redkale
0eedc2c180 Redkale 1.8.4 开始 2017-10-15 21:59:59 +08:00
Redkale
8b3658143a 修复HttpResponse.finishFile传输偶尔异常的BUG 2017-09-25 11:40:49 +08:00
Redkale
6d69ff546b 2017-09-13 11:10:24 +08:00
Redkale
9555e3c9b9 2017-09-13 10:43:34 +08:00
Redkale
744634dbdd ResourceFactory 增加 public <A> List<A> query(final BiPredicate<String, A> predicate) 方法 2017-09-12 19:34:50 +08:00
Redkale
de5ee844c4 Convert 增加 convertMapTo 系列方法, HttpResponse增加 finishMapJson 系列方法 2017-09-09 22:36:35 +08:00
Redkale
ae73cee357 2017-09-09 13:24:19 +08:00
Redkale
d1cf9be8d7 AnyEncoder增加convertMapTo方法 2017-09-09 13:23:35 +08:00
Redkale
43ae77ab33 2017-09-01 11:13:22 +08:00
Redkale
182a75cfad 修复创建RestServlet时方法内含try/catch导致获取方法参数名错位的BUG 2017-09-01 10:51:41 +08:00
Redkale
222dc0edce 2017-08-28 21:54:48 +08:00
Redkale
c7a81513fe Redkale 1.8.3 开始 2017-08-28 19:20:51 +08:00
Redkale
4931c66868 2017-08-28 18:11:55 +08:00
Redkale
fcff1c3a4b 修复ByteBuffer的缓存区域会重复的BUG 2017-08-28 17:50:09 +08:00
Redkale
2005bf7e3b 2017-08-27 17:18:13 +08:00
Redkale
cb07a38f04 2017-08-27 14:37:06 +08:00
Redkale
6085cd5eef 2017-08-25 12:54:23 +08:00
Redkale
086275c135 2017-08-22 15:55:25 +08:00
Redkale
a449a96ef9 2017-08-22 15:10:17 +08:00
Redkale
bc3209a09c 2017-08-21 14:40:36 +08:00
Redkale
63d1ef985d 2017-08-18 14:48:21 +08:00
Redkale
24505564c8 2017-08-14 16:22:46 +08:00
Redkale
93a7bd63cf WebSocket 增加 onOccurException 方法 2017-08-12 07:56:42 +08:00
Redkale
0b9b5baa49 HTTP服务增加OPTIONS配置项功能 2017-08-10 16:19:58 +08:00
Redkale
5c4100e762 2017-08-10 15:05:46 +08:00
Redkale
eb861014c4 2017-08-10 10:24:37 +08:00
Redkale
e62f7ea63d 2017-08-06 12:25:56 +08:00
Redkale
d6df2055b2 2017-08-03 14:30:03 +08:00
Redkale
570aac947a 2017-08-03 12:16:14 +08:00
Redkale
1fdc33b565 2017-08-03 10:21:16 +08:00
Redkale
af8d0e978e 2017-07-27 16:52:00 +08:00
Redkale
c58022a81e Update README.md 2017-07-25 11:32:57 +08:00
Redkale
f4cf828993 Update README.md 2017-07-25 11:31:23 +08:00
Redkale
c4dc0de5fe Redkale 1.8.2 开始 2017-07-25 11:24:04 +08:00
Redkale
44507a97a6 兼容Oracle的复制表结构 2017-07-25 11:05:49 +08:00
Redkale
f4a7f1cff6 修复负载均衡无法正确切换节点的BUG 2017-07-24 18:29:40 +08:00
Redkale
a5fcb45a88 2017-07-24 17:34:07 +08:00
Redkale
bc8b68526d 2017-07-24 17:32:45 +08:00
Redkale
180f201dc0 修复多个<services>节点且其下指定service时无法获取所有service实例的BUG 2017-07-24 17:20:16 +08:00
Redkale
9ab315a405 2017-07-24 16:10:13 +08:00
Redkale
27b4742b6d 增加 TransportStrategy 功能 2017-07-21 16:07:13 +08:00
Redkale
702220d18e 2017-07-19 10:51:17 +08:00
Redkale
414489da8e 2017-07-15 22:24:10 +08:00
Redkale
77057df25d 2017-07-11 11:59:12 +08:00
Redkale
2f98cd1ab5 2017-07-11 11:29:57 +08:00
Redkale
8809fe8ec9 2017-07-11 10:09:49 +08:00
Redkale
f9702a9517 WebSocket时request.keepAlive设置为false 2017-07-11 10:07:42 +08:00
Redkale
29e46b9b68 暂时屏蔽304 2017-07-11 09:47:40 +08:00
Redkale
f838e35413 返回304的响应中增加Content-Length值 2017-07-11 09:30:27 +08:00
Redkale
f3bb77c49b 增加获取最后一次ping的时间点 2017-07-10 22:46:16 +08:00
Redkale
12fa033e15 增加判断用户是否在线和获取在线用户总数接口 2017-07-05 15:57:22 +08:00
Redkale
f4abfafea2 2017-07-05 15:42:54 +08:00
Redkale
0918af71d2 CacheSource增加getKeySize方法 2017-07-05 15:18:16 +08:00
Redkale
275befa330 private方法改成protected,方便重载 2017-07-05 10:28:55 +08:00
Redkale
ab4cd8bcb6 2017-07-02 17:30:08 +08:00
Redkale
36c109b32f 2017-07-02 17:29:22 +08:00
Redkale
73a915665d 2017-07-02 17:00:59 +08:00
Redkale
bd6d71c94a 修复@RestWebSocket对应的WebSocketServlet.resourceName没有重载的BUG 2017-07-01 16:31:00 +08:00
Redkale
842e93507c WebSocketEngine.broadcastMessage 增加 Predicate<WebSocket> 参数 2017-07-01 15:46:51 +08:00
Redkale
76df1108d7 WebSocketEngine.broadcastMessage 增加 Predicate<WebSocket> 参数 2017-07-01 15:38:47 +08:00
Redkale
941d09cde2 2017-07-01 15:29:26 +08:00
Redkale
9dd3e1da07 允许本地模式的Service通过@Resource可以获取Application、ResourceFactory等资源 2017-06-30 13:35:44 +08:00
Redkale
2bf73245ec Redkale 1.8.1 开始 2017-06-26 09:40:30 +08:00
Redkale
b0ab792f72 2017-06-24 22:45:19 +08:00
Redkale
0df9a940c5 2017-06-24 16:24:13 +08:00
Redkale
fc35fc5abc 2017-06-24 15:26:52 +08:00
Redkale
eaa0a99933 2017-06-24 12:33:48 +08:00
Redkale
83bdb97842 2017-06-24 12:31:20 +08:00
Redkale
a71a4d0fed 2017-06-24 10:47:41 +08:00
Redkale
835435c220 2017-06-24 10:38:41 +08:00
Redkale
c524ba1797 2017-06-24 10:32:00 +08:00
Redkale
f254b48693 2017-06-24 10:09:26 +08:00
Redkale
922697eb4d 2017-06-22 14:37:42 +08:00
Redkale
450e3e3ea2 2017-06-22 09:42:24 +08:00
Redkale
b31f75f4f6 2017-06-22 00:54:51 +08:00
Redkale
1d1f18b046 2017-06-21 23:05:40 +08:00
Redkale
73f942746b 2017-06-21 17:35:18 +08:00
Redkale
b6cefe8c2d 2017-06-21 17:34:08 +08:00
Redkale
ebc0e4eb41 2017-06-21 17:32:22 +08:00
Redkale
179a7b22ea 2017-06-21 16:27:18 +08:00
Redkale
0b87d9a261 2017-06-21 16:10:15 +08:00
Redkale
685a686ead 2017-06-20 21:58:20 +08:00
Redkale
568e1cf62d 2017-06-20 21:57:29 +08:00
Redkale
1d121bd2ab 2017-06-20 13:56:45 +08:00
Redkale
2ca4bdaaec 2017-06-19 13:46:32 +08:00
Redkale
878fda30f6 2017-06-19 13:45:16 +08:00
Redkale
abb611382c 2017-06-19 13:41:43 +08:00
Redkale
b53510a26f 2017-06-19 10:07:59 +08:00
Redkale
2aee84d477 2017-06-19 09:53:29 +08:00
Redkale
0ba2e25f2e 2017-06-19 08:20:52 +08:00
Redkale
98e8a7eb05 2017-06-18 22:31:13 +08:00
Redkale
62139efca9 2017-06-18 21:58:28 +08:00
Redkale
2b62cbe455 2017-06-18 21:51:20 +08:00
Redkale
e44602fe3b 2017-06-15 23:27:56 +08:00
Redkale
276cb4da92 2017-06-15 08:08:38 +08:00
Redkale
7081f94afc 2017-06-12 14:43:32 +08:00
Redkale
2d6cefeb43 2017-06-07 09:38:57 +08:00
Redkale
ea6c703ac6 2017-06-07 07:02:16 +08:00
Redkale
d1f14962fd 2017-06-07 06:59:51 +08:00
Redkale
79ca63bf81 2017-06-06 09:44:51 +08:00
Redkale
6421bc2851 2017-06-06 09:43:31 +08:00
Redkale
43f9f50f4c 2017-06-06 09:35:41 +08:00
Redkale
5f140a8ce9 2017-06-06 09:31:27 +08:00
Redkale
c0f8cdf902 2017-06-06 09:30:46 +08:00
Redkale
8d66b1b4a7 2017-06-06 08:35:57 +08:00
Redkale
8c7ee4136c 2017-06-06 08:20:47 +08:00
Redkale
d9498c9a6c 2017-06-05 16:09:06 +08:00
Redkale
08060a8c86 2017-06-05 09:18:05 +08:00
Redkale
2bdf0e4a50 2017-06-05 08:55:37 +08:00
Redkale
eb184df100 2017-06-04 11:30:07 +08:00
Redkale
cf545a731c 2017-06-04 08:03:03 +08:00
Redkale
95e18dfd48 2017-06-04 08:00:13 +08:00
Redkale
023a9abdef 2017-06-03 22:32:47 +08:00
Redkale
7f3776c224 2017-06-03 22:12:29 +08:00
Redkale
7b15ba33e0 2017-06-03 22:05:53 +08:00
Redkale
a6c105d63d 2017-06-03 18:41:08 +08:00
Redkale
10e22b0873 2017-06-03 16:50:40 +08:00
Redkale
53a35e6397 2017-06-03 10:20:36 +08:00
Redkale
b5a3c39f4f 2017-06-03 09:51:53 +08:00
Redkale
99381d4842 2017-06-02 15:24:53 +08:00
Redkale
5f2c2a9f2c 2017-06-02 14:00:14 +08:00
Redkale
dc3f318949 2017-06-02 09:46:58 +08:00
Redkale
cfae61faea 2017-06-01 20:06:46 +08:00
Redkale
dd7626b1a3 2017-06-01 15:21:28 +08:00
Redkale
2171aa1232 2017-06-01 13:23:35 +08:00
Redkale
801ad489d2 2017-06-01 11:38:49 +08:00
Redkale
c88d0b402d 2017-05-31 21:19:51 +08:00
Redkale
542bb4353b 2017-05-31 14:07:21 +08:00
Redkale
fe1e0a845a 2017-05-31 13:41:10 +08:00
Redkale
f320f4c550 2017-05-31 12:17:10 +08:00
Redkale
0ec2bf211a 2017-05-31 10:51:02 +08:00
Redkale
b3c54e4db5 2017-05-31 10:50:10 +08:00
Redkale
52838f04a9 2017-05-31 08:31:40 +08:00
Redkale
fa5bd95a2b 2017-05-30 17:12:51 +08:00
Redkale
b2e73d378c 2017-05-30 16:42:10 +08:00
Redkale
6895b31ad0 2017-05-30 16:11:29 +08:00
Redkale
56637ff7ef 2017-05-30 16:04:25 +08:00
Redkale
40b46f9c7f 2017-05-30 14:57:58 +08:00
Redkale
0aed26652d 2017-05-30 11:12:42 +08:00
Redkale
14238a0203 2017-05-30 07:22:45 +08:00
Redkale
369a70e857 2017-05-29 21:20:30 +08:00
Redkale
6ede9b0f31 2017-05-29 21:19:52 +08:00
Redkale
6c039dc8f4 2017-05-29 21:01:56 +08:00
Redkale
1e69a6755f 2017-05-29 19:02:43 +08:00
Redkale
f6c617574c 2017-05-29 17:22:12 +08:00
Redkale
2291beb5e7 2017-05-29 16:35:11 +08:00
Redkale
d8e091f888 2017-05-29 15:13:46 +08:00
Redkale
6a42ae7570 2017-05-29 15:05:39 +08:00
Redkale
756e4634d9 2017-05-29 15:03:27 +08:00
Redkale
06773ccdc0 2017-05-29 14:03:11 +08:00
Redkale
0da74be2fa 2017-05-29 09:17:54 +08:00
Redkale
1d1b732a74 2017-05-28 15:04:10 +08:00
Redkale
ab90c80785 2017-05-28 13:30:50 +08:00
Redkale
6cd232efd2 2017-05-28 10:19:56 +08:00
Redkale
96e4b8834d 2017-05-27 21:56:34 +08:00
Redkale
7ef5ddfd46 2017-05-27 21:41:24 +08:00
Redkale
73a973e0ed 2017-05-27 21:05:17 +08:00
Redkale
e0411a94f6 2017-05-27 20:44:47 +08:00
Redkale
054e853074 2017-05-27 19:34:35 +08:00
Redkale
88672b5522 2017-05-27 18:20:49 +08:00
Redkale
0a60b81a98 2017-05-27 18:08:14 +08:00
Redkale
37f8208b1b 2017-05-27 17:54:55 +08:00
Redkale
a8b9cc9753 2017-05-27 15:31:35 +08:00
Redkale
a99c7d3454 2017-05-27 15:07:24 +08:00
Redkale
b88987dd98 2017-05-27 14:59:52 +08:00
Redkale
54f4f8e35d 2017-05-27 13:18:32 +08:00
Redkale
97670261e6 2017-05-27 12:43:03 +08:00
Redkale
82a2a513f5 2017-05-27 12:31:58 +08:00
Redkale
174a8a2a0c 2017-05-27 11:54:28 +08:00
Redkale
92b3d0bbd4 2017-05-27 10:25:53 +08:00
Redkale
24c90b015a 2017-05-26 14:19:34 +08:00
Redkale
2742e935cb 2017-05-26 13:57:50 +08:00
Redkale
b7770c89b8 2017-05-26 13:52:36 +08:00
Redkale
779a9cca4d 2017-05-26 13:26:39 +08:00
Redkale
493c27beb5 2017-05-26 13:25:57 +08:00
Redkale
c68d988d51 2017-05-26 11:15:04 +08:00
Redkale
ceeb924d4d 2017-05-26 10:18:55 +08:00
Redkale
4d0a16d35d 2017-05-26 07:39:41 +08:00
Redkale
1e60adf5bb 2017-05-26 00:40:51 +08:00
Redkale
59b08684a8 2017-05-25 23:51:27 +08:00
Redkale
c326b7ed05 2017-05-25 23:06:27 +08:00
Redkale
d9bf5c8412 2017-05-25 17:54:14 +08:00
Redkale
39d4e6405f 2017-05-25 17:43:22 +08:00
Redkale
03bcea30df 2017-05-25 17:18:20 +08:00
Redkale
27b67cde0e 2017-05-25 16:50:46 +08:00
Redkale
31fca5630b 2017-05-25 16:49:01 +08:00
Redkale
3504d735c1 2017-05-25 16:01:11 +08:00
Redkale
74f554fe33 2017-05-25 15:57:18 +08:00
Redkale
b02d4e6731 2017-05-25 15:06:15 +08:00
Redkale
23fd72b116 2017-05-24 23:05:14 +08:00
Redkale
4cd1b10c35 2017-05-24 22:54:11 +08:00
Redkale
71c0763304 2017-05-24 17:57:23 +08:00
Redkale
0ea4ddb5eb 2017-05-24 17:50:49 +08:00
Redkale
9bc266ca61 2017-05-24 12:36:38 +08:00
Redkale
b202de4916 2017-05-24 10:53:51 +08:00
Redkale
7d26198e88 2017-05-24 09:29:14 +08:00
Redkale
d98e249fd7 2017-05-24 09:21:30 +08:00
Redkale
7a8b6cac9d 2017-05-23 23:23:08 +08:00
Redkale
822ac078b5 2017-05-23 23:19:16 +08:00
Redkale
5e3718af19 2017-05-23 23:16:37 +08:00
Redkale
713fdefb56 2017-05-23 23:14:30 +08:00
Redkale
804e0e27e7 2017-05-23 22:14:03 +08:00
Redkale
2653808f8f 2017-05-23 22:12:55 +08:00
Redkale
ba12df2cba 2017-05-23 20:12:32 +08:00
Redkale
d2bfa9ab56 2017-05-23 20:03:09 +08:00
Redkale
0fb108bd9b 2017-05-23 19:59:14 +08:00
Redkale
145527db38 2017-05-23 19:45:52 +08:00
Redkale
d7c50532cf 2017-05-23 18:59:56 +08:00
Redkale
6562ac0a2b 2017-05-23 14:25:24 +08:00
Redkale
6f4c9dca48 2017-05-23 13:15:13 +08:00
Redkale
f18ef4f94d 2017-05-23 12:19:42 +08:00
Redkale
4786e17243 2017-05-23 11:23:58 +08:00
Redkale
96beb9cef4 2017-05-23 11:04:54 +08:00
Redkale
a59bf92ee7 2017-05-23 09:32:52 +08:00
Redkale
cdc3dbf9ea 2017-05-22 22:21:17 +08:00
Redkale
a957a18e32 2017-05-22 22:18:03 +08:00
Redkale
95c53f99e0 2017-05-22 20:20:29 +08:00
Redkale
03ac849451 2017-05-22 20:01:52 +08:00
Redkale
d9946ceb64 2017-05-22 19:32:26 +08:00
Redkale
fda9c30dc4 2017-05-22 19:22:24 +08:00
Redkale
205162ce38 2017-05-22 17:13:16 +08:00
Redkale
f5379df63b 2017-05-22 16:47:53 +08:00
Redkale
859e56af4d 2017-05-22 16:46:59 +08:00
Redkale
a29cc94f32 2017-05-22 16:00:25 +08:00
Redkale
9e6840f5cb 2017-05-22 15:27:53 +08:00
Redkale
52d559ea4a 2017-05-22 14:41:03 +08:00
Redkale
95a2b752af 2017-05-22 13:19:12 +08:00
Redkale
33da94960c 2017-05-22 13:05:50 +08:00
Redkale
dee2002cf3 2017-05-22 12:43:09 +08:00
Redkale
d9a318bba8 2017-05-22 09:32:14 +08:00
Redkale
05925b4f78 2017-05-21 21:21:56 +08:00
Redkale
45fe7cb3e9 2017-05-21 21:00:21 +08:00
Redkale
58271c803b 2017-05-21 20:56:57 +08:00
Redkale
88942c61b5 2017-05-21 19:31:54 +08:00
Redkale
88c4824c4f 2017-05-21 19:29:56 +08:00
Redkale
62b0be802e 2017-05-21 19:26:27 +08:00
Redkale
cdec316312 2017-05-21 18:33:39 +08:00
Redkale
8dcb999444 2017-05-21 14:18:47 +08:00
Redkale
ee460b4196 2017-05-21 14:08:55 +08:00
Redkale
d049b3f9ea 2017-05-21 13:47:31 +08:00
Redkale
c244c4edab 2017-05-21 13:19:32 +08:00
Redkale
7c533ce8d3 2017-05-21 12:52:01 +08:00
Redkale
f68686114d 2017-05-21 12:29:58 +08:00
Redkale
0859dee201 2017-05-20 13:43:02 +08:00
Redkale
46ccd83acc 2017-05-20 13:40:58 +08:00
Redkale
d5e44787c0 2017-05-20 13:15:23 +08:00
Redkale
4029b09d81 2017-05-20 13:14:00 +08:00
Redkale
e939241a8c 临时 2017-05-20 13:01:43 +08:00
Redkale
cee2c47d9a 2017-05-19 13:22:51 +08:00
Redkale
ccceaa2607 2017-05-19 13:21:09 +08:00
Redkale
358a50ecc7 2017-05-19 12:53:28 +08:00
Redkale
6aabae849d 2017-05-19 12:34:00 +08:00
Redkale
60cbd9b37d 2017-05-19 11:43:46 +08:00
Redkale
df2ee8273d 2017-05-19 11:39:10 +08:00
Redkale
8bbee0aff8 2017-05-19 11:26:21 +08:00
Redkale
01ea7f07f5 2017-05-18 16:52:44 +08:00
Redkale
ba928b389b 2017-05-18 14:22:44 +08:00
Redkale
39ba0f86f6 2017-05-17 19:10:51 +08:00
Redkale
4d523d1ca4 2017-05-17 18:35:37 +08:00
Redkale
2f60fe795c 2017-05-17 18:24:04 +08:00
Redkale
c3560b0ef0 2017-05-17 18:11:44 +08:00
Redkale
804b4dc07d 2017-05-17 18:00:15 +08:00
Redkale
bb87774ba9 临时修改 2017-05-17 02:32:36 +08:00
Redkale
f76ab977d1 2017-05-17 02:17:03 +08:00
Redkale
8003252cf3 2017-05-16 21:11:18 +08:00
Redkale
95c0d0f5cf 2017-05-16 21:09:28 +08:00
Redkale
3dcc6ea28f 2017-05-16 20:14:35 +08:00
Redkale
85c708b075 2017-05-16 20:11:45 +08:00
Redkale
278c51e26b 2017-05-16 17:13:51 +08:00
Redkale
7c0e60d191 2017-05-14 20:41:29 +08:00
Redkale
d1d10f90b9 2017-05-14 20:04:40 +08:00
Redkale
3b601979f4 2017-05-14 19:42:08 +08:00
Redkale
69cc09e76d 2017-05-14 16:05:41 +08:00
Redkale
ef28e32e04 2017-05-14 15:52:15 +08:00
Redkale
9954eaf469 2017-05-14 13:29:45 +08:00
Redkale
5ce5f53ed8 2017-05-14 13:27:07 +08:00
Redkale
8e36a7b450 2017-05-14 13:07:02 +08:00
Redkale
f15754386b 2017-05-14 12:27:27 +08:00
Redkale
6cc90ac7fe 2017-05-14 11:49:53 +08:00
Redkale
3c7f10d657 2017-05-14 11:49:31 +08:00
Redkale
21c84865b9 2017-05-14 11:47:00 +08:00
Redkale
853e823a8d 2017-05-14 11:34:15 +08:00
Redkale
6d74355fc0 2017-05-13 22:37:54 +08:00
Redkale
987914e748 2017-05-13 22:35:16 +08:00
Redkale
6e66ee0c99 2017-05-13 21:15:53 +08:00
Redkale
4f7145319f 2017-05-13 18:54:39 +08:00
Redkale
ab2656cde6 2017-05-13 18:47:03 +08:00
Redkale
27468d9f0c 2017-05-13 18:44:32 +08:00
Redkale
bb8462af2a 2017-05-13 18:17:36 +08:00
Redkale
e1df150a37 2017-05-13 16:49:21 +08:00
Redkale
bee4f31323 2017-05-13 16:40:26 +08:00
Redkale
df3a1ef84d 2017-05-13 15:39:58 +08:00
Redkale
242b13fff0 2017-05-13 15:27:34 +08:00
Redkale
c3cc9de5b5 2017-05-13 14:33:44 +08:00
Redkale
72e16b88f7 2017-05-13 14:19:23 +08:00
Redkale
f1f0227dca 2017-05-13 14:18:46 +08:00
Redkale
63fe26f9cb 2017-05-13 14:16:51 +08:00
Redkale
02fcb7b089 2017-05-13 13:56:02 +08:00
Redkale
9db9db4f5e 2017-05-13 11:23:14 +08:00
Redkale
e5ce250304 2017-05-13 11:20:18 +08:00
Redkale
a5756c0b4d 2017-05-13 10:50:52 +08:00
Redkale
0be3d8a4fa 2017-05-13 09:38:28 +08:00
Redkale
017ab1ae84 2017-05-12 22:02:19 +08:00
Redkale
c622e2437d 2017-05-12 21:46:10 +08:00
Redkale
3a1f20d438 2017-05-12 21:38:30 +08:00
Redkale
2a9b64e53e 2017-05-12 21:34:46 +08:00
Redkale
225745a282 2017-05-12 21:30:18 +08:00
Redkale
83680c46e8 2017-05-12 19:56:09 +08:00
Redkale
7484b80fb2 2017-05-12 17:57:17 +08:00
Redkale
77674ac8d2 2017-05-12 17:51:27 +08:00
Redkale
564067602f 2017-05-12 14:58:52 +08:00
Redkale
26778c58c9 2017-05-12 14:21:47 +08:00
Redkale
77cd24fa42 2017-05-12 14:19:06 +08:00
Redkale
4feea0e784 重构HttpServlet 2017-05-12 13:42:10 +08:00
Redkale
681faa415f 2017-05-12 08:09:05 +08:00
Redkale
3b54484832 改造HttpBaseServlet 的preExecute 和 authenticate 方法 2017-05-11 14:56:48 +08:00
Redkale
fa2513d934 2017-05-11 13:58:33 +08:00
Redkale
5e2be5e926 2017-05-11 13:56:36 +08:00
Redkale
4c071b0a1d 2017-05-11 13:49:52 +08:00
Redkale
9e7949d9eb 2017-05-11 13:35:09 +08:00
Redkale
29843a9812 2017-05-11 13:34:33 +08:00
Redkale
48a08f27a7 2017-05-11 13:07:37 +08:00
Redkale
4f1a0849ec 2017-05-11 12:55:40 +08:00
Redkale
6676c3fd37 REST 增加 @RestBody 特性, 获取请求内容, 参数可以是String 或 byte[] 2017-05-10 23:35:47 +08:00
Redkale
6daa45ff05 2017-05-10 17:40:56 +08:00
Redkale
28f95a89df 2017-05-03 13:41:08 +08:00
Redkale
44d11dae34 2017-05-02 08:20:54 +08:00
Redkale
a1b39ba99b 2017-05-02 08:19:32 +08:00
Redkale
abcfb8f10c 2017-05-02 08:12:19 +08:00
Redkale
33f49c7632 2017-05-02 08:10:51 +08:00
Redkale
d800a33ded 2017-05-02 08:09:01 +08:00
Redkale
f587e13bdc 2017-05-02 08:08:25 +08:00
175 changed files with 10095 additions and 6411 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

1
libs/readme.txt Normal file
View File

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

View File

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

View File

@@ -15,9 +15,9 @@ com.sun.level = INFO
java.util.logging.FileHandler.limit = 10485760 java.util.logging.FileHandler.limit = 10485760
java.util.logging.FileHandler.count = 100 java.util.logging.FileHandler.count = 100
java.util.logging.FileHandler.encoding = UTF-8 java.util.logging.FileHandler.encoding = UTF-8
java.util.logging.FileHandler.pattern = ${APP_HOME}/logs-%m/log-%u.log java.util.logging.FileHandler.pattern = ${APP_HOME}/logs-%m/log-%d.log
#java.util.logging.FileHandler.unusual \u5c5e\u6027\u8868\u793a\u5c06 WARNING\u3001SEVERE \u7ea7\u522b\u7684\u65e5\u5fd7\u590d\u5236\u5199\u5165\u5355\u72ec\u7684\u6587\u4ef6\u4e2d #java.util.logging.FileHandler.unusual \u5c5e\u6027\u8868\u793a\u5c06 WARNING\u3001SEVERE \u7ea7\u522b\u7684\u65e5\u5fd7\u590d\u5236\u5199\u5165\u5355\u72ec\u7684\u6587\u4ef6\u4e2d
#java.util.logging.FileHandler.unusual = ${APP_HOME}/logs-%m/log-error-%u.log java.util.logging.FileHandler.unusual = ${APP_HOME}/logs-%m/log-warnerr-%d.log
java.util.logging.FileHandler.append = true java.util.logging.FileHandler.append = true
#java.util.logging.ConsoleHandler.level = FINE #java.util.logging.ConsoleHandler.level = FINE

View File

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

View File

@@ -0,0 +1,33 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package javax.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 值越大,优先级越高
*
* @since Common Annotations 1.2
*/
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Priority {
int value();
}

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 javax.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @since Common Annotations 1.0
*/
@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Resource {
public enum AuthenticationType {
CONTAINER,
APPLICATION
}
public String name() default "";
public Class<?> type() default Object.class;
public AuthenticationType authenticationType() default AuthenticationType.CONTAINER;
public boolean shareable() default true;
public String description() default "";
public String mappedName() default "";
public String lookup() default "";
}

View File

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

View File

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

View File

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

View File

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

View File

@@ -22,6 +22,7 @@ import java.util.logging.Formatter;
* *
* @author zhangjx * @author zhangjx
*/ */
@SuppressWarnings("unchecked")
public class LogFileHandler extends Handler { public class LogFileHandler extends Handler {
/** /**

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -6,6 +6,7 @@
package org.redkale.convert; package org.redkale.convert;
import java.lang.reflect.Type; import java.lang.reflect.Type;
import java.util.concurrent.CompletableFuture;
/** /**
* 对不明类型的对象进行序列化; BSON序列化时将对象的类名写入WriterJSON则不写入。 * 对不明类型的对象进行序列化; BSON序列化时将对象的类名写入WriterJSON则不写入。
@@ -35,6 +36,28 @@ public final class AnyEncoder<T> implements Encodeable<Writer, T> {
} }
} }
@SuppressWarnings("unchecked")
public void convertMapTo(final Writer out, final Object... values) {
if (values == null) {
out.writeNull();
} else {
int count = values.length - values.length % 2;
out.writeMapB(count / 2);
for (int i = 0; i < count; i += 2) {
if (i > 0) out.writeArrayMark();
this.convertTo(out, (T) values[i]);
out.writeMapMark();
Object val = values[i + 1];
if (val instanceof CompletableFuture) {
this.convertTo(out, (T) ((CompletableFuture) val).join());
} else {
this.convertTo(out, (T) val);
}
}
out.writeMapE();
}
}
@Override @Override
public Type getType() { public Type getType() {
return Object.class; return Object.class;

View File

@@ -0,0 +1,36 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.convert;
import java.lang.reflect.Type;
/**
* 二进制序列化/反序列化操作类
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
* @param <R> Reader输入的子类
* @param <W> Writer输出的子类
*/
public abstract class BinaryConvert<R extends Reader, W extends Writer> extends Convert<R, W> {
protected BinaryConvert(ConvertFactory<R, W> factory) {
super(factory);
}
@Override
public final boolean isBinary() {
return true;
}
public abstract byte[] convertTo(final Object value);
public abstract byte[] convertTo(final Type type, final Object value);
public abstract byte[] convertMapTo(final Object... values);
}

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,25 +0,0 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.convert;
import java.lang.annotation.*;
import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.*;
/**
* ConvertColumn 的多用类
*
* <p> 详情见: https://redkale.org
* @author zhangjx
*/
@Inherited
@Documented
@Target({METHOD, FIELD})
@Retention(RUNTIME)
public @interface ConvertColumns {
ConvertColumn[] value();
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,36 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.convert;
import java.lang.reflect.Type;
/**
* 文本序列化/反序列化操作类
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
* @param <R> Reader输入的子类
* @param <W> Writer输出的子类
*/
public abstract class TextConvert<R extends Reader, W extends Writer> extends Convert<R, W> {
protected TextConvert(ConvertFactory<R, W> factory) {
super(factory);
}
@Override
public final boolean isBinary() {
return false;
}
public abstract String convertTo(final Object value);
public abstract String convertTo(final Type type, final Object value);
public abstract String convertMapTo(final Object... values);
}

View File

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

View File

@@ -37,7 +37,7 @@ import org.redkale.util.*;
* *
* @author zhangjx * @author zhangjx
*/ */
public final class BsonConvert extends Convert<BsonReader, BsonWriter> { public final class BsonConvert extends BinaryConvert<BsonReader, BsonWriter> {
private static final ObjectPool<BsonReader> readerPool = BsonReader.createPool(Integer.getInteger("convert.bson.pool.size", 16)); private static final ObjectPool<BsonReader> readerPool = BsonReader.createPool(Integer.getInteger("convert.bson.pool.size", 16));
@@ -61,7 +61,7 @@ public final class BsonConvert extends Convert<BsonReader, BsonWriter> {
//------------------------------ reader ----------------------------------------------------------- //------------------------------ reader -----------------------------------------------------------
public BsonReader pollBsonReader(final ByteBuffer... buffers) { public BsonReader pollBsonReader(final ByteBuffer... buffers) {
return new BsonByteBufferReader(buffers); return new BsonByteBufferReader((ConvertMask) null, buffers);
} }
public BsonReader pollBsonReader(final InputStream in) { public BsonReader pollBsonReader(final InputStream in) {
@@ -99,6 +99,7 @@ public final class BsonConvert extends Convert<BsonReader, BsonWriter> {
return convertFrom(type, bytes, 0, bytes.length); return convertFrom(type, bytes, 0, bytes.length);
} }
@SuppressWarnings("unchecked")
public <T> T convertFrom(final Type type, final byte[] bytes, final int start, final int len) { public <T> T convertFrom(final Type type, final byte[] bytes, final int start, final int len) {
if (type == null) return null; if (type == null) return null;
final BsonReader in = readerPool.get(); final BsonReader in = readerPool.get();
@@ -109,16 +110,27 @@ public final class BsonConvert extends Convert<BsonReader, BsonWriter> {
return rs; return rs;
} }
@SuppressWarnings("unchecked")
public <T> T convertFrom(final Type type, final InputStream in) { public <T> T convertFrom(final Type type, final InputStream in) {
if (type == null || in == null) return null; if (type == null || in == null) return null;
return (T) factory.loadDecoder(type).convertFrom(new BsonStreamReader(in)); return (T) factory.loadDecoder(type).convertFrom(new BsonStreamReader(in));
} }
@Override
@SuppressWarnings("unchecked")
public <T> T convertFrom(final Type type, final ByteBuffer... buffers) { public <T> T convertFrom(final Type type, final ByteBuffer... buffers) {
if (type == null || buffers.length < 1) return null; if (type == null || buffers.length < 1) return null;
return (T) factory.loadDecoder(type).convertFrom(new BsonByteBufferReader(buffers)); return (T) factory.loadDecoder(type).convertFrom(new BsonByteBufferReader((ConvertMask) null, buffers));
} }
@Override
@SuppressWarnings("unchecked")
public <T> T convertFrom(final Type type, final ConvertMask mask, final ByteBuffer... buffers) {
if (type == null || buffers.length < 1) return null;
return (T) factory.loadDecoder(type).convertFrom(new BsonByteBufferReader(mask, buffers));
}
@SuppressWarnings("unchecked")
public <T> T convertFrom(final Type type, final BsonReader reader) { public <T> T convertFrom(final Type type, final BsonReader reader) {
if (type == null) return null; if (type == null) return null;
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@@ -127,6 +139,7 @@ public final class BsonConvert extends Convert<BsonReader, BsonWriter> {
} }
//------------------------------ convertTo ----------------------------------------------------------- //------------------------------ convertTo -----------------------------------------------------------
@Override
public byte[] convertTo(final Object value) { public byte[] convertTo(final Object value) {
if (value == null) { if (value == null) {
final BsonWriter out = writerPool.get().tiny(tiny); final BsonWriter out = writerPool.get().tiny(tiny);
@@ -138,6 +151,7 @@ public final class BsonConvert extends Convert<BsonReader, BsonWriter> {
return convertTo(value.getClass(), value); return convertTo(value.getClass(), value);
} }
@Override
public byte[] convertTo(final Type type, final Object value) { public byte[] convertTo(final Type type, final Object value) {
if (type == null) return null; if (type == null) return null;
final BsonWriter out = writerPool.get().tiny(tiny); final BsonWriter out = writerPool.get().tiny(tiny);
@@ -147,6 +161,16 @@ public final class BsonConvert extends Convert<BsonReader, BsonWriter> {
return result; return result;
} }
@Override
public byte[] convertMapTo(final Object... values) {
if (values == null) return null;
final BsonWriter out = writerPool.get().tiny(tiny);
((AnyEncoder) factory.getAnyEncoder()).convertMapTo(out, values);
byte[] result = out.toArray();
writerPool.offer(out);
return result;
}
public void convertTo(final OutputStream out, final Object value) { public void convertTo(final OutputStream out, final Object value) {
if (value == null) { if (value == null) {
new BsonStreamWriter(tiny, out).writeNull(); new BsonStreamWriter(tiny, out).writeNull();
@@ -164,6 +188,27 @@ public final class BsonConvert extends Convert<BsonReader, BsonWriter> {
} }
} }
public void convertMapTo(final OutputStream out, final Object... values) {
if (values == null) {
new BsonStreamWriter(tiny, out).writeNull();
} else {
((AnyEncoder) factory.getAnyEncoder()).convertMapTo(new BsonStreamWriter(tiny, out), values);
}
}
@Override
public ByteBuffer[] convertTo(final Supplier<ByteBuffer> supplier, final Object value) {
if (supplier == null) return null;
BsonByteBufferWriter out = new BsonByteBufferWriter(tiny, supplier);
if (value == null) {
out.writeNull();
} else {
factory.loadEncoder(value.getClass()).convertTo(out, value);
}
return out.toBuffers();
}
@Override
public ByteBuffer[] convertTo(final Supplier<ByteBuffer> supplier, final Type type, final Object value) { public ByteBuffer[] convertTo(final Supplier<ByteBuffer> supplier, final Type type, final Object value) {
if (supplier == null || type == null) return null; if (supplier == null || type == null) return null;
BsonByteBufferWriter out = new BsonByteBufferWriter(tiny, supplier); BsonByteBufferWriter out = new BsonByteBufferWriter(tiny, supplier);
@@ -175,13 +220,14 @@ public final class BsonConvert extends Convert<BsonReader, BsonWriter> {
return out.toBuffers(); return out.toBuffers();
} }
public ByteBuffer[] convertTo(final Supplier<ByteBuffer> supplier, final Object value) { @Override
public ByteBuffer[] convertMapTo(final Supplier<ByteBuffer> supplier, final Object... values) {
if (supplier == null) return null; if (supplier == null) return null;
BsonByteBufferWriter out = new BsonByteBufferWriter(tiny, supplier); BsonByteBufferWriter out = new BsonByteBufferWriter(tiny, supplier);
if (value == null) { if (values == null) {
out.writeNull(); out.writeNull();
} else { } else {
factory.loadEncoder(value.getClass()).convertTo(out, value); ((AnyEncoder) factory.getAnyEncoder()).convertMapTo(out, values);
} }
return out.toBuffers(); return out.toBuffers();
} }
@@ -199,6 +245,14 @@ public final class BsonConvert extends Convert<BsonReader, BsonWriter> {
factory.loadEncoder(type).convertTo(writer, value); factory.loadEncoder(type).convertTo(writer, value);
} }
public void convertMapTo(final BsonWriter writer, final Object... values) {
if (values == null) {
writer.writeNull();
} else {
((AnyEncoder) factory.getAnyEncoder()).convertMapTo(writer, values);
}
}
public BsonWriter convertToWriter(final Object value) { public BsonWriter convertToWriter(final Object value) {
if (value == null) return null; if (value == null) return null;
return convertToWriter(value.getClass(), value); return convertToWriter(value.getClass(), value);
@@ -211,4 +265,9 @@ public final class BsonConvert extends Convert<BsonReader, BsonWriter> {
return out; return out;
} }
public BsonWriter convertMapToWriter(final Object... values) {
final BsonWriter out = writerPool.get().tiny(tiny);
((AnyEncoder) factory.getAnyEncoder()).convertMapTo(out, values);
return out;
}
} }

View File

@@ -87,6 +87,7 @@ public class BsonReader extends Reader {
* 跳过属性的值 * 跳过属性的值
*/ */
@Override @Override
@SuppressWarnings("unchecked")
public final void skipValue() { public final void skipValue() {
if (typeval == 0) return; if (typeval == 0) return;
final byte val = this.typeval; final byte val = this.typeval;

View File

@@ -9,7 +9,7 @@ import java.io.*;
import org.redkale.convert.*; import org.redkale.convert.*;
/** /**
* *
* 详情见: https://redkale.org * 详情见: https://redkale.org
* *
* @author zhangjx * @author zhangjx
@@ -21,6 +21,7 @@ class BsonStreamReader extends BsonByteBufferReader {
private byte currByte; private byte currByte;
protected BsonStreamReader(InputStream in) { protected BsonStreamReader(InputStream in) {
super((ConvertMask) null);
this.in = in; this.in = in;
} }

View File

@@ -0,0 +1,44 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.convert.ext;
import org.redkale.convert.SimpledCoder;
import org.redkale.convert.Writer;
import org.redkale.convert.Reader;
import java.math.BigDecimal;
import org.redkale.util.Utility;
/**
* BigDecimal 的SimpledCoder实现
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
* @param <R> Reader输入的子类型
* @param <W> Writer输出的子类型
*/
public final class BigDecimalSimpledCoder<R extends Reader, W extends Writer> extends SimpledCoder<R, W, BigDecimal> {
public static final BigDecimalSimpledCoder instance = new BigDecimalSimpledCoder();
@Override
public void convertTo(W out, BigDecimal value) {
if (value == null) {
out.writeNull();
return;
}
out.writeSmallString(value.toString());
}
@Override
public BigDecimal convertFrom(R in) {
String value = in.readSmallString();
if (value == null) return null;
return new BigDecimal(Utility.charArray(value));
}
}

View File

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

View File

@@ -32,7 +32,7 @@ public final class DateSimpledCoder<R extends Reader, W extends Writer> extends
@Override @Override
public Date convertFrom(R in) { public Date convertFrom(R in) {
long t = in.readLong(); long t = in.readLong();
return t == 0 ? null : new Date(); return t == 0 ? null : new Date(t);
} }
} }

View File

@@ -0,0 +1,41 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.convert.ext;
import java.io.File;
import org.redkale.convert.*;
/**
* 文件 的SimpledCoder实现
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
* @param <R> Reader输入的子类型
* @param <W> Writer输出的子类型
*/
public class FileSimpledCoder<R extends Reader, W extends Writer> extends SimpledCoder<R, W, File> {
public static final PatternSimpledCoder instance = new PatternSimpledCoder();
@Override
public void convertTo(W out, File value) {
if (value == null) {
out.writeNull();
} else {
out.writeString(value.getPath());
}
}
@Override
public File convertFrom(R in) {
String value = in.readString();
if (value == null) return null;
return new File(value);
}
}

View File

@@ -20,6 +20,7 @@ import java.net.*;
* @param <R> Reader输入的子类型 * @param <R> Reader输入的子类型
* @param <W> Writer输出的子类型 * @param <W> Writer输出的子类型
*/ */
@SuppressWarnings("unchecked")
public final class InetAddressSimpledCoder<R extends Reader, W extends Writer> extends SimpledCoder<R, W, InetAddress> { public final class InetAddressSimpledCoder<R extends Reader, W extends Writer> extends SimpledCoder<R, W, InetAddress> {
public static final InetAddressSimpledCoder instance = new InetAddressSimpledCoder(); public static final InetAddressSimpledCoder instance = new InetAddressSimpledCoder();
@@ -50,6 +51,7 @@ public final class InetAddressSimpledCoder<R extends Reader, W extends Writer> e
* @param <R> Reader输入的子类型 * @param <R> Reader输入的子类型
* @param <W> Writer输出的子类型 * @param <W> Writer输出的子类型
*/ */
@SuppressWarnings("unchecked")
public final static class InetSocketAddressSimpledCoder<R extends Reader, W extends Writer> extends SimpledCoder<R, W, InetSocketAddress> { public final static class InetSocketAddressSimpledCoder<R extends Reader, W extends Writer> extends SimpledCoder<R, W, InetSocketAddress> {
public static final InetSocketAddressSimpledCoder instance = new InetSocketAddressSimpledCoder(); public static final InetSocketAddressSimpledCoder instance = new InetSocketAddressSimpledCoder();

View File

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

View File

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

View File

@@ -21,7 +21,7 @@ import org.redkale.util.*;
* @author zhangjx * @author zhangjx
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public final class JsonConvert extends Convert<JsonReader, JsonWriter> { public final class JsonConvert extends TextConvert<JsonReader, JsonWriter> {
public static final Type TYPE_MAP_STRING_STRING = new TypeToken<java.util.LinkedHashMap<String, String>>() { public static final Type TYPE_MAP_STRING_STRING = new TypeToken<java.util.LinkedHashMap<String, String>>() {
}.getType(); }.getType();
@@ -48,7 +48,7 @@ public final class JsonConvert extends Convert<JsonReader, JsonWriter> {
//------------------------------ reader ----------------------------------------------------------- //------------------------------ reader -----------------------------------------------------------
public JsonReader pollJsonReader(final ByteBuffer... buffers) { public JsonReader pollJsonReader(final ByteBuffer... buffers) {
return new JsonByteBufferReader(buffers); return new JsonByteBufferReader((ConvertMask) null, buffers);
} }
public JsonReader pollJsonReader(final InputStream in) { public JsonReader pollJsonReader(final InputStream in) {
@@ -109,9 +109,16 @@ public final class JsonConvert extends Convert<JsonReader, JsonWriter> {
return (T) factory.loadDecoder(type).convertFrom(new JsonStreamReader(in)); return (T) factory.loadDecoder(type).convertFrom(new JsonStreamReader(in));
} }
@Override
public <T> T convertFrom(final Type type, final ByteBuffer... buffers) { public <T> T convertFrom(final Type type, final ByteBuffer... buffers) {
if (type == null || buffers == null || buffers.length == 0) return null; if (type == null || buffers == null || buffers.length == 0) return null;
return (T) factory.loadDecoder(type).convertFrom(new JsonByteBufferReader(buffers)); return (T) factory.loadDecoder(type).convertFrom(new JsonByteBufferReader((ConvertMask) null, buffers));
}
@Override
public <T> T convertFrom(final Type type, final ConvertMask mask, final ByteBuffer... buffers) {
if (type == null || buffers == null || buffers.length == 0) return null;
return (T) factory.loadDecoder(type).convertFrom(new JsonByteBufferReader(mask, buffers));
} }
public <T> T convertFrom(final Type type, final JsonReader reader) { public <T> T convertFrom(final Type type, final JsonReader reader) {
@@ -122,11 +129,13 @@ public final class JsonConvert extends Convert<JsonReader, JsonWriter> {
} }
//------------------------------ convertTo ----------------------------------------------------------- //------------------------------ convertTo -----------------------------------------------------------
@Override
public String convertTo(final Object value) { public String convertTo(final Object value) {
if (value == null) return "null"; if (value == null) return "null";
return convertTo(value.getClass(), value); return convertTo(value.getClass(), value);
} }
@Override
public String convertTo(final Type type, final Object value) { public String convertTo(final Type type, final Object value) {
if (type == null) return null; if (type == null) return null;
if (value == null) return "null"; if (value == null) return "null";
@@ -137,6 +146,16 @@ public final class JsonConvert extends Convert<JsonReader, JsonWriter> {
return result; return result;
} }
@Override
public String convertMapTo(final Object... values) {
if (values == null) return "null";
final JsonWriter out = writerPool.get().tiny(tiny);
((AnyEncoder) factory.getAnyEncoder()).convertMapTo(out, values);
String result = out.toString();
writerPool.offer(out);
return result;
}
public void convertTo(final OutputStream out, final Object value) { public void convertTo(final OutputStream out, final Object value) {
if (value == null) { if (value == null) {
new JsonStreamWriter(tiny, out).writeNull(); new JsonStreamWriter(tiny, out).writeNull();
@@ -154,6 +173,15 @@ public final class JsonConvert extends Convert<JsonReader, JsonWriter> {
} }
} }
public void convertMapTo(final OutputStream out, final Object... values) {
if (values == null) {
new JsonStreamWriter(tiny, out).writeNull();
} else {
((AnyEncoder) factory.getAnyEncoder()).convertMapTo(new JsonStreamWriter(tiny, out), values);
}
}
@Override
public ByteBuffer[] convertTo(final Supplier<ByteBuffer> supplier, final Object value) { public ByteBuffer[] convertTo(final Supplier<ByteBuffer> supplier, final Object value) {
if (supplier == null) return null; if (supplier == null) return null;
JsonByteBufferWriter out = new JsonByteBufferWriter(tiny, null, supplier); JsonByteBufferWriter out = new JsonByteBufferWriter(tiny, null, supplier);
@@ -165,6 +193,7 @@ public final class JsonConvert extends Convert<JsonReader, JsonWriter> {
return out.toBuffers(); return out.toBuffers();
} }
@Override
public ByteBuffer[] convertTo(final Supplier<ByteBuffer> supplier, final Type type, final Object value) { public ByteBuffer[] convertTo(final Supplier<ByteBuffer> supplier, final Type type, final Object value) {
if (supplier == null || type == null) return null; if (supplier == null || type == null) return null;
JsonByteBufferWriter out = new JsonByteBufferWriter(tiny, null, supplier); JsonByteBufferWriter out = new JsonByteBufferWriter(tiny, null, supplier);
@@ -176,6 +205,18 @@ public final class JsonConvert extends Convert<JsonReader, JsonWriter> {
return out.toBuffers(); return out.toBuffers();
} }
@Override
public ByteBuffer[] convertMapTo(final Supplier<ByteBuffer> supplier, final Object... values) {
if (supplier == null) return null;
JsonByteBufferWriter out = new JsonByteBufferWriter(tiny, null, supplier);
if (values == null) {
out.writeNull();
} else {
((AnyEncoder) factory.getAnyEncoder()).convertMapTo(out, values);
}
return out.toBuffers();
}
public void convertTo(final JsonWriter writer, final Object value) { public void convertTo(final JsonWriter writer, final Object value) {
if (value == null) { if (value == null) {
writer.writeNull(); writer.writeNull();
@@ -193,6 +234,14 @@ public final class JsonConvert extends Convert<JsonReader, JsonWriter> {
} }
} }
public void convertMapTo(final JsonWriter writer, final Object... values) {
if (values == null) {
writer.writeNull();
} else {
((AnyEncoder) factory.getAnyEncoder()).convertMapTo(writer, values);
}
}
public JsonWriter convertToWriter(final Object value) { public JsonWriter convertToWriter(final Object value) {
if (value == null) return null; if (value == null) return null;
return convertToWriter(value.getClass(), value); return convertToWriter(value.getClass(), value);
@@ -204,4 +253,10 @@ public final class JsonConvert extends Convert<JsonReader, JsonWriter> {
factory.loadEncoder(type).convertTo(out, value); factory.loadEncoder(type).convertTo(out, value);
return out; return out;
} }
public JsonWriter convertMapToWriter(final Object... values) {
final JsonWriter out = writerPool.get().tiny(tiny);
((AnyEncoder) factory.getAnyEncoder()).convertMapTo(out, values);
return out;
}
} }

View File

@@ -20,6 +20,7 @@ import org.redkale.util.*;
* *
* @author zhangjx * @author zhangjx
*/ */
@SuppressWarnings("unchecked")
public final class JsonFactory extends ConvertFactory<JsonReader, JsonWriter> { public final class JsonFactory extends ConvertFactory<JsonReader, JsonWriter> {
private static final JsonFactory instance = new JsonFactory(null, Boolean.getBoolean("convert.json.tiny")); private static final JsonFactory instance = new JsonFactory(null, Boolean.getBoolean("convert.json.tiny"));

View File

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

View File

@@ -11,7 +11,7 @@ import org.redkale.convert.*;
/** /**
* *
* 详情见: https://redkale.org * 详情见: https://redkale.org
* *
* @author zhangjx * @author zhangjx
*/ */
class JsonStreamReader extends JsonByteBufferReader { class JsonStreamReader extends JsonByteBufferReader {
@@ -19,6 +19,7 @@ class JsonStreamReader extends JsonByteBufferReader {
private InputStream in; private InputStream in;
protected JsonStreamReader(InputStream in) { protected JsonStreamReader(InputStream in) {
super((ConvertMask) null);
this.in = in; this.in = in;
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -51,6 +51,7 @@ public abstract class Request<C extends Context> {
* 返回值Integer.MIN_VALUE: 帧数据; -1数据不合法 0解析完毕 &gt;0: 需再读取的字节数。 * 返回值Integer.MIN_VALUE: 帧数据; -1数据不合法 0解析完毕 &gt;0: 需再读取的字节数。
* *
* @param buffer ByteBuffer对象 * @param buffer ByteBuffer对象
*
* @return 缺少的字节数 * @return 缺少的字节数
*/ */
protected abstract int readHeader(ByteBuffer buffer); protected abstract int readHeader(ByteBuffer buffer);
@@ -59,6 +60,7 @@ public abstract class Request<C extends Context> {
* 读取buffer并返回读取的有效数据长度 * 读取buffer并返回读取的有效数据长度
* *
* @param buffer ByteBuffer对象 * @param buffer ByteBuffer对象
*
* @return 有效数据长度 * @return 有效数据长度
*/ */
protected abstract int readBody(ByteBuffer buffer); protected abstract int readBody(ByteBuffer buffer);
@@ -82,8 +84,9 @@ public abstract class Request<C extends Context> {
return (T) properties.get(name); return (T) properties.get(name);
} }
@SuppressWarnings("unchecked")
protected <T> T removeProperty(String name) { protected <T> T removeProperty(String name) {
return (T)properties.remove(name); return (T) properties.remove(name);
} }
protected Map<String, Object> getProperties() { protected Map<String, Object> getProperties() {
@@ -100,8 +103,9 @@ public abstract class Request<C extends Context> {
return (T) attributes.get(name); return (T) attributes.get(name);
} }
@SuppressWarnings("unchecked")
public <T> T removeAttribute(String name) { public <T> T removeAttribute(String name) {
return (T)attributes.remove(name); return (T) attributes.remove(name);
} }
public Map<String, Object> getAttributes() { public Map<String, Object> getAttributes() {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,21 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.net;
import java.net.SocketAddress;
/**
* 远程请求的负载均衡策略
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*/
public interface TransportStrategy {
public AsyncConnection pollConnection(SocketAddress addr, Transport transport);
}

View File

@@ -1,29 +0,0 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.net.http;
import java.io.IOException;
/**
* 默认Servlet, 没有配置RestHttpServlet实现类则使用该默认类
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*/
public class DefaultRestServlet extends RestHttpServlet<Object> {
@Override
protected Object currentUser(HttpRequest req) throws IOException {
return new Object();
}
@Override
public void authenticate(int moduleid, int actionid, HttpRequest request, HttpResponse response, HttpServlet next) throws IOException {
next.execute(request, response);
}
}

View File

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

View File

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

View File

@@ -0,0 +1,20 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.net.http;
import org.redkale.net.Filter;
/**
* HTTP 过滤器 <br>
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*/
public abstract class HttpFilter extends Filter<HttpContext, HttpRequest, HttpResponse> {
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

@@ -0,0 +1,43 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.net.http;
import java.lang.annotation.*;
import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
/**
* 只能依附在Service实现类的public方法上, 当方法的返回值以JSON输出时对指定类型的转换设定。 <br>
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*/
@Inherited
@Documented
@Target({METHOD})
@Retention(RUNTIME)
@Repeatable(RestConvert.RestConverts.class)
public @interface RestConvert {
boolean tiny() default true;
Class type();
String[] ignoreColumns() default {};
String[] convertColumns() default {};
@Inherited
@Documented
@Target({METHOD})
@Retention(RUNTIME)
@interface RestConverts {
RestConvert[] value();
}
}

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,26 +0,0 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.net.http;
import java.lang.annotation.*;
import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.*;
/**
* RestMapping 的多用类
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*/
@Inherited
@Documented
@Target({METHOD})
@Retention(RUNTIME)
public @interface RestMappings {
RestMapping[] value();
}

View File

@@ -0,0 +1,39 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.net.http;
import java.lang.annotation.*;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
/**
* 标记在RestWebSocket的接收消息方法上
*
* <br><p>
* 详情见: https://redkale.org
*
* @author zhangjx
*/
@Inherited
@Documented
@Target({METHOD})
@Retention(RUNTIME)
public @interface RestOnMessage {
/**
* 请求的方法名, 不能含特殊字符,不能以数字开头(能作为变量名)
*
* @return String
*/
String name();
/**
* 备注描述
*
* @return String
*/
String comment() default "";
}

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