228 Commits
1.8.0 ... 1.8.8

Author SHA1 Message Date
Redkale
921aedaf9d 增加Optional的序列化和反序列化 2017-12-25 10:12:01 +08:00
Redkale
0f545923b2 2017-12-23 10:39:46 +08:00
Redkale
5b32a91874 2017-12-23 10:14:27 +08:00
Redkale
25b2528416 2017-12-23 10:11:38 +08:00
Redkale
d29c95c38f 2017-12-22 10:48:31 +08:00
Redkale
a661ab2ff5 Utility增加max、min方法 2017-12-21 19:14:01 +08:00
Redkale
ac08ebee75 Utility增加joining方法 2017-12-21 17:39:45 +08:00
Redkale
8e1a287ed1 2017-12-21 17:33:18 +08:00
Redkale
9b656b3970 2017-12-21 16:50:21 +08:00
Redkale
a1c0bbf413 2017-12-21 16:24:08 +08:00
Redkale
2ad8c5d425 2017-12-21 16:19:41 +08:00
Redkale
75aaa980cf 2017-12-21 16:09:17 +08:00
Redkale
7c55326d23 2017-12-20 18:49:47 +08:00
Redkale
aa3ade5912 Convert增加AtomicIntegerSimpledCoder 2017-12-20 18:45:14 +08:00
Redkale
c692deebe9 优化MapEncoder 2017-12-20 14:20:02 +08:00
Redkale
9ff161c97d 增加WebSocket在onOpen和createUserid方法里返回无效用户信息前也可以发送信息 2017-12-20 08:54:54 +08:00
Redkale
5b1f820621 2017-12-19 20:07:57 +08:00
Redkale
30b2cffcb8 2017-12-19 16:40:58 +08:00
Redkale
709439bfca 2017-12-18 11:48:36 +08:00
Redkale
34adb238f7 ObjectPool实现Consumer接口,且将offer设为过期,建议使用accept方法 2017-12-18 10:00:18 +08:00
Redkale
76c54f8d54 修复部分带有@RestConvert方法生成RestServlet时出现的BUG 2017-12-16 16:32:21 +08:00
Redkale
6196c05f12 2017-12-16 10:42:50 +08:00
Redkale
37df0af56c Redkale 1.8.8 开始 2017-12-16 09:40:30 +08:00
Redkale
8a5e1252ab 2017-12-15 09:06:26 +08:00
Redkale
ae5430af42 2017-12-14 19:25:10 +08:00
Redkale
13cf188e25 WebSocketEngine增加forEachLocalWebSocket方法 2017-12-14 19:10:49 +08:00
Redkale
41d3dea1ac 2017-12-14 18:43:23 +08:00
Redkale
be816088f5 增加对IntStream、LongStream、DoubleStream的序列化支持 2017-12-14 18:37:54 +08:00
Redkale
5bef900b76 2017-12-14 16:01:13 +08:00
Redkale
52f61b0f96 2017-12-14 15:56:31 +08:00
Redkale
b5a4646e3b 2017-12-14 15:50:48 +08:00
Redkale
d055d5c824 2017-12-14 15:09:20 +08:00
Redkale
f14ef05c88 HttpResponse增加finish(Object)系列方法 2017-12-14 15:02:16 +08:00
Redkale
426506324f 2017-12-14 10:58:27 +08:00
Redkale
7a5e58a112 删掉AsyncHandler, 采用CompletionHandler代替 2017-12-14 10:42:24 +08:00
Redkale
2e0c58cbea WebSocket增加changeUserid功能 2017-12-12 09:33:56 +08:00
Redkale
9ded3fbb9a Update README.md 2017-12-11 18:51:48 +08:00
Redkale
acbc1032e6 Update README.md 2017-12-11 18:50:59 +08:00
Redkale
d8186b00ba 启动打印java.version信息 2017-12-11 11:39:57 +08:00
Redkale
767adcbefe 2017-12-08 19:44:39 +08:00
Redkale
67df072275 HttpRequest增加getParametersToString方法 2017-12-08 19:38:54 +08:00
Redkale
dab70af4d4 2017-12-08 14:27:22 +08:00
Redkale
b42826692d 2017-12-08 11:50:12 +08:00
Redkale
e2ab4b20c9 HttpResult支持byte[]、ByteBuffer和ByteBuffer[] 2017-12-08 11:36:24 +08:00
Redkale
511ee8a6df EntityInfo增加getQueryColumns方法 2017-12-06 18:58:30 +08:00
Redkale
463269a796 增加ApplicationListener功能 2017-12-06 14:33:39 +08:00
Redkale
2d4b865432 RestWebSocket增加anyuser功能 2017-12-06 10:20:13 +08:00
Redkale
1da73429f7 对SNCP的Address进行IPv4判断 2017-12-04 09:48:52 +08:00
Redkale
e97e6b8262 2017-11-29 14:06:01 +08:00
Redkale
e6bc34d6f8 2017-11-29 14:03:53 +08:00
Redkale
f1c4ac9e67 2017-11-29 14:01:57 +08:00
Redkale
de6c6076e4 【新增功能】DataSource可以通过application.xml配置 2017-11-29 13:56:26 +08:00
Redkale
a36e3d3819 2017-11-29 12:05:18 +08:00
Redkale
043b847f05 DataSource增加getType()方法 2017-11-29 12:01:42 +08:00
Redkale
65bc8192f0 DataJdbcSource增加preConstruct方法,方便重载 2017-11-29 11:28:10 +08:00
Redkale
71e0b60200 增加manifest的MimeType 2017-11-28 20:03:29 +08:00
Redkale
99dc7ac189 2017-11-25 16:48:59 +08:00
Redkale
8605c44f14 Redkale 1.8.7 开始 2017-11-25 14:40:39 +08:00
Redkale
c47e301263 2017-11-24 19:51:46 +08:00
Redkale
6c11ef70d4 2017-11-23 08:56:40 +08:00
Redkale
dddb2397c4 2017-11-22 21:07:29 +08:00
Redkale
f66803b9fd 2017-11-22 21:06:48 +08:00
Redkale
0801ce8cad 2017-11-22 20:08:23 +08:00
Redkale
5a690912c1 2017-11-22 16:07:39 +08:00
Redkale
a3dddd20c7 修复FilterBean对List<>不支持的BUG 2017-11-17 15:56:18 +08:00
Redkale
7200a74b18 2017-11-17 15:44:47 +08:00
Redkale
3847cf47e4 2017-11-17 14:19:22 +08:00
Redkale
0b5a79c1d8 2017-11-17 10:38:43 +08:00
Redkale
685667fd69 修改WebSocketPacket解析协议方式,并修复多报并发请求导致后面的消息丢失的BUG 2017-11-16 17:11:11 +08:00
Redkale
72e81bd034 2017-11-16 17:09:48 +08:00
Redkale
39eadcd08b 2017-11-16 09:24:14 +08:00
Redkale
a46f79a448 2017-11-15 21:23:21 +08:00
Redkale
5ddca03fb6 2017-11-15 21:07:54 +08:00
Redkale
0bd5787992 2017-11-15 21:06:17 +08:00
Redkale
bf88c4da06 2017-11-15 19:51:49 +08:00
Redkale
5e72e782cc 2017-11-15 17:14:18 +08:00
Redkale
3c0609fa0e 修改DataJdbcSource的directQuery实现 2017-11-15 17:12:49 +08:00
Redkale
945f9f9ef5 2017-11-15 11:00:08 +08:00
Redkale
7c5ac7970e 2017-11-15 08:49:17 +08:00
Redkale
68f0c1de29 2017-11-15 08:46:12 +08:00
Redkale
337a2a3038 CacheSource增加getString/getLong一系列方法 2017-11-14 20:35:31 +08:00
Redkale
2a5e0e82be WebSocketNode的CacheSource资源名改成与WebSocketNode同名 2017-11-14 11:42:39 +08:00
Redkale
e0b2120ee5 2017-11-14 10:54:56 +08:00
Redkale
ac5662114a 2017-11-14 10:44:13 +08:00
Redkale
f2963e01e0 Transport的pollConnection方法改成异步模式 2017-11-13 21:27:28 +08:00
Redkale
fc54fc3f24 2017-11-13 21:09:24 +08:00
Redkale
41990e7e6a AsyncConnection提供createTCP方法。异步构建AsyncConnection 2017-11-13 19:43:01 +08:00
Redkale
01e8649e2a Context提供createAsynchronousChannelGroup方法 2017-11-13 19:15:45 +08:00
Redkale
71ead72dec 修复WebSocketNode在远程模式下使用SNCP序列化导致目标服务器没有对应的JavaBean类而无法解析的BUG 2017-11-13 17:22:01 +08:00
Redkale
da600ecf20 修复WebSocketNode在半远程模式下SNCP出异常的BUG 2017-11-13 16:22:48 +08:00
Redkale
d46807a585 2017-11-13 14:54:23 +08:00
Redkale
571d13075b 2017-11-13 14:43:45 +08:00
Redkale
3ad8eeaae6 2017-11-13 14:25:30 +08:00
Redkale
e540128154 修复WebSocketNode无法获取SNCP_ADDR的BUG 2017-11-13 13:19:43 +08:00
Redkale
c6d83440bb WebSocketServlet的maxconns改成wsmaxconns 2017-11-13 10:16:50 +08:00
Redkale
97f43a4d8d 2017-11-13 09:39:51 +08:00
Redkale
6fddd8b53b 修复将null开头的字符串(例如nullaxx)都视为null的BUG 2017-11-11 23:57:45 +08:00
Redkale
98a6c3ef79 AnyValue增加forEach方法 2017-11-11 13:39:08 +08:00
Redkale
6e70f2043e 增加TCP级别上的最大连接数限制 2017-11-11 10:37:25 +08:00
Redkale
123b94398a 修复Json中不带引号的n开头且长度大于3的字符串转成String时不能正常解析的BUG 2017-11-10 19:56:21 +08:00
Redkale
40c19c1521 CacheSource增加incr或decr方法 2017-11-10 19:33:10 +08:00
Redkale
7089fae390 2017-11-10 19:09:28 +08:00
Redkale
936fe8d1ab SNCP连接增加心跳功能 2017-11-10 18:04:43 +08:00
Redkale
7780e0090c 修复iconst问题 2017-11-10 17:48:33 +08:00
Redkale
1a7dc97335 修改logger的finest方式 2017-11-10 11:31:41 +08:00
Redkale
9af8c863b1 修复ASM的BIPUSH问题,RestWebSocket增加maxconns文件配置功能 2017-11-10 11:01:58 +08:00
Redkale
2c13224fcc CacheSource增加getType方法 2017-11-09 15:53:36 +08:00
Redkale
5e13575e84 2017-11-09 10:21:16 +08:00
Redkale
eabdc13c53 2017-11-09 10:17:31 +08:00
Redkale
7261bcfadb CacheSource改善 2017-11-08 16:20:04 +08:00
Redkale
0569e87d4e 2017-11-08 15:02:56 +08:00
Redkale
5c339f6f66 2017-11-08 14:46:28 +08:00
Redkale
44eada2697 2017-11-08 11:14:39 +08:00
Redkale
3e0b9daeaf CacheSource中的key固定使用String类型 2017-11-08 11:06:55 +08:00
Redkale
3061422d83 2017-11-08 09:40:55 +08:00
Redkale
4280464f85 2017-11-08 09:39:38 +08:00
Redkale
2b1d09b027 WebSocket增加最大连接数设置功能 2017-11-07 17:39:30 +08:00
Redkale
4f3c2e071a RestConvert 增加 skipIgnore 功能 2017-11-04 10:47:40 +08:00
Redkale
d2aacf7b8c @Resource支持含{system.property.xxx}模式 2017-11-03 16:40:13 +08:00
Redkale
fbb1fb5a5f 2017-11-03 11:58:42 +08:00
Redkale
c9187a78bc 2017-11-03 11:19:42 +08:00
Redkale
d984ab2a8f 2017-11-03 11:03:01 +08:00
Redkale
ad2a3f0d54 改造Transport的构造函数,便于TransportFactory统一管理 2017-11-02 19:04:55 +08:00
Redkale
e559379294 Resource依赖注入支持system.property.开头的系统变量 2017-11-02 17:42:16 +08:00
Redkale
e125aa9885 2017-11-02 15:34:52 +08:00
Redkale
8026ebb215 2017-11-02 11:00:16 +08:00
Redkale
45b04da483 RestWebSocket的resourceName增加可配功能,通过System.getProperty可以配置 2017-11-02 10:09:29 +08:00
Redkale
0da5867b70 2017-10-31 14:29:22 +08:00
Redkale
b1f51b1c30 WebSocket补上识别Close事件,之前漏掉了 2017-10-31 11:28:53 +08:00
Redkale
054253fb90 AsyncConnection增加获取最后一次进行读写IO操作时间的方法 2017-10-30 19:19:31 +08:00
Redkale
141041dbba Redkale 1.8.6 开始 2017-10-30 17:01:10 +08:00
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
121 changed files with 5438 additions and 1347 deletions

View File

@@ -22,3 +22,8 @@
&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/articles.html' target='_blank'>https://redkale.org/articles.html</a></h5>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<h5>欢迎加入Redkale QQ群: 527523235</h5>
&nbsp;

View File

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

View File

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

View File

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

View File

@@ -10,6 +10,16 @@
<server protocol="HTTP" host="0.0.0.0" port="6060" root="root">
<request>
<remoteaddr value="request.headers.X-RemoteAddress"/>
</request>
<response>
<defcookie domain="" path=""/>
<addheader name="Access-Control-Allow-Origin" value="request.headers.Origin" />
<setheader name="Access-Control-Allow-Credentials" value="true"/>
</response>
<services autoload="true"/>
<filters autoload="true"/>

View File

@@ -18,7 +18,7 @@ java.util.logging.FileHandler.limit = 10485760
java.util.logging.FileHandler.count = 10000
java.util.logging.FileHandler.encoding = UTF-8
java.util.logging.FileHandler.pattern = ${APP_HOME}/logs-%m/log-%d.log
java.util.logging.FileHandler.unusual = ${APP_HOME}/logs-%m/log-warnerr-%u.log
java.util.logging.FileHandler.unusual = ${APP_HOME}/logs-%m/log-warnerr-%d.log
java.util.logging.FileHandler.append = true
java.util.logging.ConsoleHandler.level = FINER

View File

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

View File

@@ -37,6 +37,7 @@
threads 线程总数, 默认: <group>节点数*CPU核数*8
bufferCapacity: ByteBuffer的初始化大小 默认: 8K;
bufferPoolSize ByteBuffer池的大小默认: <group>节点数*CPU核数*8
strategy: 远程请求的负载均衡策略, 必须是org.redkale.net.TransportStrategy的实现类
-->
<transport bufferCapacity="8K" bufferPoolSize="32" threads="32"/>
@@ -70,7 +71,13 @@
<source name="redis" value="org.redkalex.cache.RedisCacheSource" xxx="16">
<node addr="127.0.0.1" port="7070"/>
</source>
<!--
Application启动的监听事件,可配置多个节点
value: 类名必须是ApplicationListener的子类
-->
<listener value="org.redkalex.xxx.XXXApplicationListener"/>
<!--
【节点全局唯一】
全局的参数配置, 可以通过@Resource(name="property.xxxxxx") 进行注入<property>的信息, 被注解的字段类型只能是String、primitive class
@@ -106,6 +113,7 @@
charset: 文本编码, 默认: UTF-8
backlog: 默认10K
threads 线程总数, 默认: CPU核数*16
maxconns最大连接数, 小于1表示无限制 默认: 0
maxbody: request.body最大值 默认: 64K
bufferCapacity: ByteBuffer的初始化大小 默认: 8K; 如果是HTTP协议则默认: 16K + 16B (兼容HTTP 2.0、WebSocket)
bufferPoolSize ByteBuffer池的大小默认: CPU核数*512
@@ -210,11 +218,14 @@
如果addheader、setheader 的value值以request.parameters.开头则表示从request.parameters中获取对应的parameter值
如果addheader、setheader 的value值以request.headers.开头则表示从request.headers中获取对应的header值
例如下面例子是在Response输出header时添加两个header一个addHeader 一个setHeader
options 节点: 设置了该节点却auto=true当request的method=OPTIONS自动设置addheader、setheader并返回200状态码
-->
<response>
<defcookie domain="" path=""/>
<defcookie domain="" path=""/>
<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>
<!--

View File

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

View File

@@ -3,13 +3,15 @@
<persistence>
<!-- 系统基本库 -->
<persistence-unit name="demouser">
<!-- 为NONE表示不启动缓存@Cacheable 失效; 非NONE值(通常用ALL)表示开启缓存。 -->
<shared-cache-mode>NONE</shared-cache-mode>
<properties>
<!--
DataSource的实现类没有设置默认为org.redkale.source.DataJdbcSource的实现使用常规基于JDBC的数据库驱动一般无需设置
-->
<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"/>
<!--
@@ -42,7 +44,6 @@
</persistence-unit>
<!-- IM消息库 -->
<persistence-unit name="demoim">
<shared-cache-mode>NONE</shared-cache-mode>
<properties>
<!-- 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"/>

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 "";
}

29
src/module-info.java Normal file
View File

@@ -0,0 +1,29 @@
/**
* <p>
* see: https://redkale.org
*
* @author zhangjx
*
module org.redkale {
requires java.se;
requires jdk.unsupported;
exports javax.annotation;
exports javax.persistence;
exports org.redkale.boot;
exports org.redkale.boot.watch;
exports org.redkale.convert;
exports org.redkale.convert.bson;
exports org.redkale.convert.ext;
exports org.redkale.convert.json;
exports org.redkale.net;
exports org.redkale.net.http;
exports org.redkale.net.sncp;
exports org.redkale.service;
exports org.redkale.source;
exports org.redkale.util;
exports org.redkale.watch;
}
*/

View File

@@ -0,0 +1,26 @@
need copy classes:
AnnotationVisitor.java
AnnotationWriter.java
Attribute.java
ByteVector.java
ClassReader.java
ClassVisitor.java
ClassWriter.java
Context.java
CurrentFrame.java
Edge.java
FieldVisitor.java
FieldWriter.java
Frame.java
Handle.java
Handler.java
Item.java
Label.java
MethodVisitor.java
MethodWriter.java
ModuleVisitor.java
ModuleWriter.java
Opcodes.java
Type.java
TypePath.java

View File

@@ -104,8 +104,8 @@ public final class Application {
//NodeServer 资源
final List<NodeServer> servers = new CopyOnWriteArrayList<>();
//传输端的TransportFactory
final TransportFactory transportFactory;
//SNCP传输端的TransportFactory, 注意: 只给SNCP使用
final TransportFactory sncpTransportFactory;
//全局根ResourceFactory
final ResourceFactory resourceFactory = ResourceFactory.root();
@@ -131,6 +131,9 @@ public final class Application {
//日志
private final Logger logger;
//监听事件
private final List<ApplicationListener> listeners = new CopyOnWriteArrayList<>();
//服务启动时间
private final long startTime = System.currentTimeMillis();
@@ -232,31 +235,40 @@ public final class Application {
}
this.logger = Logger.getLogger(this.getClass().getSimpleName());
this.serversLatch = new CountDownLatch(config.getAnyValues("server").length + 1);
logger.log(Level.INFO, "------------------------------- Redkale " + Redkale.getDotedVersion() + " -------------------------------");
this.classLoader = new RedkaleClassLoader(Thread.currentThread().getContextClassLoader());
logger.log(Level.INFO, "------------------------- Redkale " + Redkale.getDotedVersion() + " -------------------------");
//------------------配置 <transport> 节点 ------------------
ObjectPool<ByteBuffer> transportPool = null;
ExecutorService transportExec = null;
AsynchronousChannelGroup transportGroup = null;
final AnyValue resources = config.getAnyValue("resources");
TransportStrategy strategy = null;
int bufferCapacity = 8 * 1024;
int bufferPoolSize = Runtime.getRuntime().availableProcessors() * 16;
AtomicLong createBufferCounter = new AtomicLong();
AtomicLong cycleBufferCounter = new AtomicLong();
if (resources != null) {
AnyValue transportConf = resources.getAnyValue("transport");
int groupsize = resources.getAnyValues("group").length;
if (groupsize > 0 && transportConf == null) transportConf = new DefaultAnyValue();
if (transportConf != null) {
//--------------transportBufferPool-----------
AtomicLong createBufferCounter = new AtomicLong();
AtomicLong cycleBufferCounter = new AtomicLong();
final int bufferCapacity = transportConf.getIntValue("bufferCapacity", 8 * 1024);
final int bufferPoolSize = transportConf.getIntValue("bufferPoolSize", groupsize * Runtime.getRuntime().availableProcessors() * 8);
final int threads = transportConf.getIntValue("threads", groupsize * Runtime.getRuntime().availableProcessors() * 8);
bufferCapacity = Math.max(parseLenth(transportConf.getValue("bufferCapacity"), bufferCapacity), 4 * 1024);
bufferPoolSize = parseLenth(transportConf.getValue("bufferPoolSize"), groupsize * Runtime.getRuntime().availableProcessors() * 8);
final int threads = parseLenth(transportConf.getValue("threads"), groupsize * Runtime.getRuntime().availableProcessors() * 8);
final int capacity = bufferCapacity;
transportPool = new ObjectPool<>(createBufferCounter, cycleBufferCounter, bufferPoolSize,
(Object... params) -> ByteBuffer.allocateDirect(bufferCapacity), null, (e) -> {
if (e == null || e.isReadOnly() || e.capacity() != bufferCapacity) return false;
(Object... params) -> ByteBuffer.allocateDirect(capacity), null, (e) -> {
if (e == null || e.isReadOnly() || e.capacity() != capacity) return false;
e.clear();
return true;
});
//-----------transportChannelGroup--------------
try {
final String strategyClass = transportConf.getValue("strategy");
if (strategyClass != null && !strategyClass.isEmpty()) {
strategy = (TransportStrategy) classLoader.loadClass(strategyClass).newInstance();
}
final AtomicInteger counter = new AtomicInteger();
transportExec = Executors.newFixedThreadPool(threads, (Runnable r) -> {
Thread t = new Thread(r);
@@ -271,8 +283,32 @@ public final class Application {
logger.log(Level.INFO, Transport.class.getSimpleName() + " configure bufferCapacity = " + bufferCapacity + "; bufferPoolSize = " + bufferPoolSize + "; threads = " + threads + ";");
}
}
this.transportFactory = new TransportFactory(transportExec, transportPool, transportGroup);
this.classLoader = new RedkaleClassLoader(Thread.currentThread().getContextClassLoader());
if (transportGroup == null) {
final AtomicInteger counter = new AtomicInteger();
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);
}
}
if (transportPool == null) {
final int capacity = bufferCapacity;
transportPool = new ObjectPool<>(createBufferCounter, cycleBufferCounter, bufferPoolSize,
(Object... params) -> ByteBuffer.allocateDirect(capacity), null, (e) -> {
if (e == null || e.isReadOnly() || e.capacity() != capacity) return false;
e.clear();
return true;
});
}
this.sncpTransportFactory = TransportFactory.create(transportExec, transportPool, transportGroup, strategy);
DefaultAnyValue tarnsportConf = DefaultAnyValue.create(TransportFactory.NAME_PINGINTERVAL, System.getProperty("net.transport.pinginterval", "30"));
this.sncpTransportFactory.init(tarnsportConf, Sncp.PING_BUFFER, Sncp.PONG_BUFFER.remaining());
Thread.currentThread().setContextClassLoader(this.classLoader);
this.serverClassLoader = new RedkaleClassLoader(this.classLoader);
}
@@ -281,8 +317,8 @@ public final class Application {
return resourceFactory;
}
public TransportFactory getTransportFactory() {
return transportFactory;
public TransportFactory getSncpTransportFactory() {
return sncpTransportFactory;
}
public RedkaleClassLoader getClassLoader() {
@@ -321,8 +357,8 @@ public final class Application {
File persist = new File(this.home, "conf/persistence.xml");
final String homepath = this.home.getCanonicalPath();
if (persist.isFile()) System.setProperty(DataSources.DATASOURCE_CONFPATH, persist.getCanonicalPath());
logger.log(Level.INFO, RESNAME_APP_HOME + "= " + homepath + "\r\n" + RESNAME_APP_ADDR + "= " + this.localAddress.getHostAddress());
String lib = config.getValue("lib", "").trim().replace("${APP_HOME}", homepath);
logger.log(Level.INFO, "APP_JAVA = " + System.getProperty("java.version") + "\r\n" + RESNAME_APP_ADDR + " = " + this.localAddress.getHostAddress() + "\r\n" + RESNAME_APP_HOME + " = " + homepath);
String lib = config.getValue("lib", "${APP_HOME}/libs/*").trim().replace("${APP_HOME}", homepath);
lib = lib.isEmpty() ? (homepath + "/conf") : (lib + ";" + homepath + "/conf");
Server.loadLib(classLoader, logger, lib);
@@ -379,14 +415,14 @@ public final class Application {
try {
Resource res = field.getAnnotation(Resource.class);
if (res == null) return;
if (!(src instanceof WatchService) || Sncp.isRemote((Service) src)) 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);
field.set(src, application.sncpTransportFactory);
} else if (type == NodeSncpServer.class) {
NodeServer server = null;
for (NodeServer ns : application.getNodeServers()) {
@@ -431,7 +467,7 @@ public final class Application {
return false;
}
}, Application.class, TransportFactory.class, NodeSncpServer.class, NodeHttpServer.class, NodeWatchServer.class);
}, Application.class, ResourceFactory.class, TransportFactory.class, NodeSncpServer.class, NodeHttpServer.class, NodeWatchServer.class);
//--------------------------------------------------------------------------
initResources();
}
@@ -452,7 +488,16 @@ public final class Application {
final InetSocketAddress addr = new InetSocketAddress(node.getValue("addr"), node.getIntValue("port"));
ginfo.putAddress(addr);
}
transportFactory.addGroupInfo(ginfo);
sncpTransportFactory.addGroupInfo(ginfo);
}
for (AnyValue conf : resources.getAnyValues("listener")) {
final String listenClass = conf.getValue("value", "");
if (listenClass.isEmpty()) continue;
Class clazz = Class.forName(listenClass);
if (!ApplicationListener.class.isAssignableFrom(clazz)) continue;
ApplicationListener listener = (ApplicationListener) clazz.newInstance();
listener.init(config);
this.listeners.add(listener);
}
}
//------------------------------------------------------------------------
@@ -588,10 +633,49 @@ public final class Application {
runServers(timecd, others);
runServers(timecd, watchs); //必须在所有服务都启动后再启动WATCH服务
timecd.await();
//if (!singletonrun) signalHandle();
if (!singletonrun) clearPersistData();
logger.info(this.getClass().getSimpleName() + " started in " + (System.currentTimeMillis() - startTime) + " ms\r\n");
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")
private void runServers(CountDownLatch timecd, final List<AnyValue> serconfs) throws Exception {
this.servicecdl = new CountDownLatch(serconfs.size());
@@ -711,6 +795,9 @@ public final class Application {
application.init();
application.startSelfServer();
try {
for (ApplicationListener listener : application.listeners) {
listener.preStart(application);
}
application.start();
} catch (Exception e) {
application.logger.log(Level.SEVERE, "Application start error", e);
@@ -729,6 +816,14 @@ public final class Application {
}
private void shutdown() throws Exception {
for (ApplicationListener listener : this.listeners) {
try {
listener.preShutdown(this);
} catch (Exception e) {
logger.log(Level.WARNING, listener.getClass() + " preShutdown erroneous", e);
}
}
servers.stream().forEach((server) -> {
try {
server.shutdown();
@@ -755,7 +850,16 @@ public final class Application {
logger.log(Level.FINER, source.getClass() + " close CacheSource erroneous", e);
}
}
this.transportFactory.shutdownNow();
this.sncpTransportFactory.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) {

View File

@@ -0,0 +1,45 @@
/*
* 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 org.redkale.util.AnyValue;
/**
* Application启动和关闭时的监听事件 <br>
* 只能通过application.xml配置
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*/
public interface ApplicationListener {
/**
* 初始化方法
*
* @param config 配置参数
*/
default void init(AnyValue config) {
}
/**
* Application 在运行start前调用
*
* @param application Application
*/
default void preStart(Application application) {
}
/**
* Application 在运行shutdown前调用
*
* @param application Application
*/
default void preShutdown(Application application) {
}
}

View File

@@ -99,7 +99,11 @@ public final class ClassFilter<T> {
* @return Set&lt;FilterEntry&lt;T&gt;&gt;
*/
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;
}
/**
@@ -108,7 +112,11 @@ public final class ClassFilter<T> {
* @return Set&lt;FilterEntry&lt;T&gt;&gt;
*/
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;
}
/**
@@ -118,8 +126,8 @@ public final class ClassFilter<T> {
*/
public final Set<FilterEntry<T>> getAllFilterEntrys() {
HashSet<FilterEntry<T>> rs = new HashSet<>();
rs.addAll(entrys);
rs.addAll(expectEntrys);
rs.addAll(getFilterEntrys());
rs.addAll(getFilterExpectEntrys());
return rs;
}
@@ -183,7 +191,7 @@ public final class ClassFilter<T> {
} catch (Throwable cfe) {
if (finer && !clazzname.startsWith("sun.") && !clazzname.startsWith("javax.")
&& !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);
}
}
}

View File

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

View File

@@ -7,10 +7,11 @@ package org.redkale.boot;
import java.lang.annotation.Annotation;
import java.lang.reflect.*;
import java.net.InetSocketAddress;
import java.net.*;
import java.util.*;
import java.util.logging.Level;
import javax.annotation.Resource;
import javax.annotation.*;
import static org.redkale.boot.Application.RESNAME_SNCP_ADDR;
import org.redkale.boot.ClassFilter.FilterEntry;
import org.redkale.net.*;
import org.redkale.net.http.*;
@@ -108,8 +109,13 @@ public class NodeHttpServer extends NodeServer {
if (loader != null) loader.load(sncpResFactory, src, resourceName, field, attachment);
synchronized (regFactory) {
Service nodeService = (Service) rf.find(resourceName, WebSocketNode.class);
if (sncpResFactory != null && resourceFactory.find(RESNAME_SNCP_ADDR, String.class) == null) {
resourceFactory.register(RESNAME_SNCP_ADDR, InetSocketAddress.class, sncpResFactory.find(RESNAME_SNCP_ADDR, InetSocketAddress.class));
resourceFactory.register(RESNAME_SNCP_ADDR, SocketAddress.class, sncpResFactory.find(RESNAME_SNCP_ADDR, SocketAddress.class));
resourceFactory.register(RESNAME_SNCP_ADDR, String.class, sncpResFactory.find(RESNAME_SNCP_ADDR, String.class));
}
if (nodeService == null) {
nodeService = Sncp.createLocalService(serverClassLoader, resourceName, WebSocketNodeService.class, application.getResourceFactory(), application.getTransportFactory(), (InetSocketAddress) null, (Set<String>) null, (AnyValue) null);
nodeService = Sncp.createLocalService(serverClassLoader, resourceName, WebSocketNodeService.class, application.getResourceFactory(), application.getSncpTransportFactory(), (InetSocketAddress) null, (Set<String>) null, (AnyValue) null);
regFactory.register(resourceName, WebSocketNode.class, nodeService);
}
resourceFactory.inject(nodeService, self);
@@ -122,6 +128,7 @@ public class NodeHttpServer extends NodeServer {
}, WebSocketNode.class);
}
@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 String threadName = "[" + Thread.currentThread().getName() + "] ";
@@ -138,6 +145,7 @@ public class NodeHttpServer extends NodeServer {
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;
@@ -150,7 +158,12 @@ public class NodeHttpServer extends NodeServer {
list.sort((FilterEntry<? extends Servlet> o1, FilterEntry<? extends Servlet> o2) -> { //必须保证WebSocketServlet优先加载 因为要确保其他的HttpServlet可以注入本地模式的WebSocketNode
boolean ws1 = WebSocketServlet.class.isAssignableFrom(o1.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;
});
final List<AbstractMap.SimpleEntry<String, String[]>> ss = sb == null ? null : new ArrayList<>();
@@ -195,6 +208,7 @@ public class NodeHttpServer extends NodeServer {
if (sb != null && sb.length() > 0) logger.log(Level.INFO, sb.toString().trim());
}
@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 (restConf == null) return; //不存在REST服务

View File

@@ -15,10 +15,11 @@ import java.util.*;
import java.util.concurrent.*;
import java.util.function.*;
import java.util.logging.*;
import javax.annotation.Resource;
import javax.annotation.*;
import javax.persistence.Transient;
import static org.redkale.boot.Application.*;
import org.redkale.boot.ClassFilter.FilterEntry;
import org.redkale.convert.bson.*;
import org.redkale.net.Filter;
import org.redkale.net.*;
import org.redkale.net.http.WebSocketServlet;
@@ -56,7 +57,7 @@ public abstract class NodeServer {
//ClassLoader
protected RedkaleClassLoader serverClassLoader;
protected final Thread serverThread;
//当前Server的SNCP协议的组
@@ -108,9 +109,9 @@ public abstract class NodeServer {
public void init(AnyValue config) throws Exception {
this.serverConf = config == null ? AnyValue.create() : config;
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.sncpGroup = application.transportFactory.findGroupName(this.sncpAddress);
this.sncpGroup = application.sncpTransportFactory.findGroupName(this.sncpAddress);
//单向SNCP服务不需要对等group
//if (this.sncpGroup == null) throw new RuntimeException("Server (" + String.valueOf(config).replaceAll("\\s+", " ") + ") not found <group> info");
}
@@ -134,7 +135,7 @@ public abstract class NodeServer {
resourceFactory.register(Server.RESNAME_SERVER_ROOT, Path.class, myroot.toPath());
//加入指定的classpath
Server.loadLib(serverClassLoader, logger, this.serverConf.getValue("lib", "${APP_HOME}/libs/*").replace("${APP_HOME}", application.getHome().getPath().replace('\\', '/')));
Server.loadLib(serverClassLoader, logger, this.serverConf.getValue("lib", "").replace("${APP_HOME}", application.getHome().getPath().replace('\\', '/')));
this.serverThread.setContextClassLoader(this.serverClassLoader);
}
//必须要进行初始化, 构建Service时需要使用Context中的ExecutorService
@@ -170,10 +171,10 @@ public abstract class NodeServer {
final NodeServer self = this;
//---------------------------------------------------------------------------------------------
final ResourceFactory appResFactory = application.getResourceFactory();
final TransportFactory appTranFactory = application.getTransportFactory();
final TransportFactory appSncpTranFactory = application.getSncpTransportFactory();
final AnyValue resources = application.config.getAnyValue("resources");
final Map<String, AnyValue> cacheResource = new HashMap<>();
//final Map<String, AnyValue> dataResources = new HashMap<>();
final Map<String, AnyValue> dataResources = new HashMap<>();
if (resources != null) {
for (AnyValue sourceConf : resources.getAnyValues("source")) {
try {
@@ -183,9 +184,7 @@ public abstract class NodeServer {
} else if (CacheSource.class.isAssignableFrom(type)) {
cacheResource.put(sourceConf.getValue("name", ""), sourceConf);
} else if (DataSource.class.isAssignableFrom(type)) {
//dataResources.put(sourceConf.getValue("name", ""), sourceConf);
//暂时不支持DataSource通过<resources>设置
logger.log(Level.SEVERE, "load application source resource, but not CacheSource error: " + sourceConf);
dataResources.put(sourceConf.getValue("name", ""), sourceConf);
} else {
logger.log(Level.SEVERE, "load application source resource, but not CacheSource error: " + sourceConf);
}
@@ -222,7 +221,25 @@ public abstract class NodeServer {
try {
if (field.getAnnotation(Resource.class) == null) return;
if ((src instanceof Service) && Sncp.isRemote((Service) src)) return; //远程模式不得注入 DataSource
DataSource source = DataSources.createDataSource(resourceName);
AnyValue sourceConf = dataResources.get(resourceName);
DataSource source = null;
boolean needinit = true;
if (sourceConf != null) {
final Class sourceType = serverClassLoader.loadClass(sourceConf.getValue("value"));
if (DataSource.class.isAssignableFrom(sourceType)) { // DataSource
final Service srcService = (Service) src;
SncpClient client = Sncp.getSncpClient(srcService);
final InetSocketAddress sncpAddr = client == null ? null : client.getClientAddress();
final Set<String> groups = new HashSet<>();
if (client != null && client.getSameGroup() != null) groups.add(client.getSameGroup());
if (client != null && client.getDiffGroups() != null) groups.addAll(client.getDiffGroups());
source = (DataSource) Sncp.createLocalService(serverClassLoader, resourceName, sourceType, appResFactory, appSncpTranFactory, sncpAddr, groups, Sncp.getConf(srcService));
}
}
if (source == null) {
source = DataSources.createDataSource(resourceName); //从persistence.xml配置中创建
needinit = false;
}
application.dataSources.add(source);
appResFactory.register(resourceName, DataSource.class, source);
@@ -233,7 +250,7 @@ public abstract class NodeServer {
final Set<String> groups = new HashSet<>();
if (client != null && client.getSameGroup() != null) groups.add(client.getSameGroup());
if (client != null && client.getDiffGroups() != null) groups.addAll(client.getDiffGroups());
Service cacheListenerService = Sncp.createLocalService(serverClassLoader, resourceName, DataCacheListenerService.class, appResFactory, appTranFactory, sncpAddr, groups, Sncp.getConf((Service) src));
Service cacheListenerService = Sncp.createLocalService(serverClassLoader, resourceName, DataCacheListenerService.class, appResFactory, appSncpTranFactory, sncpAddr, groups, Sncp.getConf((Service) src));
appResFactory.register(resourceName, DataCacheListener.class, cacheListenerService);
localServices.add(cacheListenerService);
sncpServer.consumerAccept(cacheListenerService);
@@ -243,7 +260,7 @@ public abstract class NodeServer {
field.set(src, source);
rf.inject(source, self); // 给其可能包含@Resource的字段赋值;
//NodeServer.this.watchFactory.inject(src);
if (source instanceof Service) ((Service) source).init(null);
if (source instanceof Service && needinit) ((Service) source).init(sourceConf);
} catch (Exception e) {
logger.log(Level.SEVERE, "DataSource inject error", e);
}
@@ -258,23 +275,28 @@ public abstract class NodeServer {
final Service srcService = (Service) src;
SncpClient client = Sncp.getSncpClient(srcService);
final InetSocketAddress sncpAddr = client == null ? null : client.getClientAddress();
final AnyValue sourceConf = cacheResource.get(resourceName);
final Class sourceType = sourceConf == null ? CacheMemorySource.class : serverClassLoader.loadClass(sourceConf.getValue("type"));
final Set<String> groups = new HashSet<>();
if (client != null && client.getSameGroup() != null) groups.add(client.getSameGroup());
if (client != null && client.getDiffGroups() != null) groups.addAll(client.getDiffGroups());
final 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之后
AnyValue sourceConf = cacheResource.get(resourceName);
if (sourceConf == null) sourceConf = dataResources.get(resourceName);
final Class sourceType = sourceConf == null ? CacheMemorySource.class : serverClassLoader.loadClass(sourceConf.getValue("value"));
Object source = null;
if (CacheSource.class.isAssignableFrom(sourceType)) { // CacheSource
source = (CacheSource) Sncp.createLocalService(serverClassLoader, resourceName, sourceType, appResFactory, appSncpTranFactory, sncpAddr, groups, Sncp.getConf(srcService));
Type genericType = field.getGenericType();
ParameterizedType pt = (genericType instanceof ParameterizedType) ? (ParameterizedType) genericType : null;
Type valType = pt == null ? null : pt.getActualTypeArguments()[0];
if (CacheSource.class.isAssignableFrom(sourceType)) {
CacheSource cacheSource = (CacheSource) source;
cacheSource.initValueType(valType instanceof Class ? (Class) valType : Object.class);
cacheSource.initTransient(field.getAnnotation(Transient.class) != null); //必须在initValueType之后
}
application.cacheSources.add((CacheSource) source);
appResFactory.register(resourceName, genericType, source);
appResFactory.register(resourceName, CacheSource.class, source);
}
application.cacheSources.add(source);
appResFactory.register(resourceName, genericType, source);
appResFactory.register(resourceName, CacheSource.class, source);
field.set(src, source);
rf.inject(source, self); //
if (source instanceof Service) ((Service) source).init(sourceConf);
@@ -303,7 +325,7 @@ public abstract class NodeServer {
final Set<FilterEntry<? extends Service>> entrys = (Set) serviceFilter.getAllFilterEntrys();
ResourceFactory regFactory = isSNCP() ? application.getResourceFactory() : resourceFactory;
final ResourceFactory appResourceFactory = application.getResourceFactory();
final TransportFactory appTransportFactory = application.getTransportFactory();
final TransportFactory appSncpTransFactory = application.getSncpTransportFactory();
for (FilterEntry<? extends Service> entry : entrys) { //service实现类
final Class<? extends Service> serviceImplClass = entry.getType();
if (Modifier.isFinal(serviceImplClass.getModifiers())) continue; //修饰final的类跳过
@@ -334,11 +356,11 @@ public abstract class NodeServer {
Service service;
boolean ws = src instanceof WebSocketServlet;
if (ws || localed) { //本地模式
service = Sncp.createLocalService(serverClassLoader, resourceName, serviceImplClass, appResourceFactory, appTransportFactory, NodeServer.this.sncpAddress, groups, entry.getProperty());
service = Sncp.createLocalService(serverClassLoader, resourceName, serviceImplClass, appResourceFactory, appSncpTransFactory, NodeServer.this.sncpAddress, groups, entry.getProperty());
} else {
service = Sncp.createRemoteService(serverClassLoader, resourceName, serviceImplClass, appTransportFactory, NodeServer.this.sncpAddress, groups, entry.getProperty());
service = Sncp.createRemoteService(serverClassLoader, resourceName, serviceImplClass, appSncpTransFactory, 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 Class restype = Sncp.getResourceType(service);
if (rf.find(resourceName, restype) == null) {
@@ -391,15 +413,20 @@ public abstract class NodeServer {
//----------------- init -----------------
List<Service> swlist = new ArrayList<>(localServices);
Collections.sort(swlist, (o1, o2) -> {
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));
return rs;
});
localServices.clear();
localServices.addAll(swlist);
//this.loadPersistData();
final List<String> slist = sb == null ? null : new CopyOnWriteArrayList<>();
CountDownLatch clds = new CountDownLatch(localServices.size());
localServices.parallelStream().forEach(y -> {
localServices.stream().forEach(y -> {
try {
long s = System.currentTimeMillis();
y.init(Sncp.getConf(y));
@@ -413,7 +440,6 @@ public abstract class NodeServer {
clds.await();
if (slist != null && sb != null) {
List<String> wlist = new ArrayList<>(slist); //直接使用CopyOnWriteArrayList偶尔会出现莫名的异常(CopyOnWriteArrayList源码1185行)
Collections.sort(wlist);
for (String s : wlist) {
sb.append(s);
}
@@ -426,6 +452,125 @@ public abstract class NodeServer {
maxClassNameLength = Math.max(maxClassNameLength, Sncp.getResourceType(y).getName().length() + 1);
}
//尚未完整实现, 先屏蔽, 单个Service在多个Server中存在的情况下进行缓存的方案还未考虑清楚
@SuppressWarnings("unchecked")
private void loadPersistData() throws Exception {
File home = application.getHome();
if (home == null || !home.isDirectory()) return;
File cachedir = new File(home, "cache");
if (!cachedir.isDirectory()) return;
int port = this.server.getSocketAddress().getPort();
final String prefix = "persist-" + port + "-";
final BsonConvert convert = BsonFactory.create().skipAllIgnore(true).getConvert();
synchronized (this.application) {
for (final File file : cachedir.listFiles((dir, name) -> name.startsWith(prefix))) {
if (!file.getName().endsWith(".bat")) continue;
String classAndResname = file.getName().substring(prefix.length(), file.getName().length() - 4); //去掉尾部的.bat
int pos = classAndResname.indexOf('-');
String servtype = pos > 0 ? classAndResname.substring(0, pos) : classAndResname;
String resname = pos > 0 ? classAndResname.substring(pos + 1) : "";
FileInputStream in = new FileInputStream(file);
ByteArrayOutputStream out = new ByteArrayOutputStream();
int b;
while ((b = in.read()) != '\n') out.write(b);
final String[] fieldNames = out.toString().split(",");
int timeout = (int) ((System.currentTimeMillis() - file.lastModified()) / 1000);
for (final Service service : this.localServices) {
if (!servtype.equals(Sncp.getResourceType(service).getName())) continue;
if (!resname.equals(Sncp.getResourceName(service))) continue;
for (final String fieldName : fieldNames) {
Field field = null;
Class clzz = service.getClass();
do {
try {
field = clzz.getDeclaredField(fieldName);
break;
} catch (Exception e) {
}
} while ((clzz = clzz.getSuperclass()) != Object.class);
field.setAccessible(true);
Object val = convert.convertFrom(field.getGenericType(), in);
Persist persist = field.getAnnotation(Persist.class);
if (persist.timeout() == 0 || persist.timeout() >= timeout) {
if (Modifier.isFinal(field.getModifiers())) {
if (Map.class.isAssignableFrom(field.getType())) {
((Map) field.get(service)).putAll((Map) val);
} else if (Collection.class.isAssignableFrom(field.getType())) {
((Collection) field.get(service)).addAll((Collection) val);
}
} else {
field.set(service, val);
}
}
if (in.read() != '\n') logger.log(Level.SEVERE, servtype + "'s [" + resname + "] load value error");
}
}
in.close();
}
}
}
//尚未完整实现, 先屏蔽
@SuppressWarnings("unchecked")
private void savePersistData() throws IOException {
File home = application.getHome();
if (home == null || !home.isDirectory()) return;
File cachedir = new File(home, "cache");
int port = this.server.getSocketAddress().getPort();
final String prefix = "persist-" + port + "-";
final BsonConvert convert = BsonFactory.create().skipAllIgnore(true).getConvert();
for (final Service service : this.localServices) {
Class clzz = service.getClass();
final Set<String> fieldNameSet = new HashSet<>();
final List<Field> fields = new ArrayList<>();
final StringBuilder sb = new StringBuilder();
do {
for (Field field : clzz.getDeclaredFields()) {
if (field.getAnnotation(Persist.class) == null) continue;
if (fieldNameSet.contains(field.getName())) continue;
if (Modifier.isStatic(field.getModifiers())) throw new RuntimeException(field + " cannot static on @" + Persist.class.getName() + " in " + clzz.getName());
if (Modifier.isFinal(field.getModifiers()) && !Map.class.isAssignableFrom(field.getType()) && !Collection.class.isAssignableFrom(field.getType())) {
throw new RuntimeException(field + " cannot final on @" + Persist.class.getName() + " in " + clzz.getName());
}
fieldNameSet.add(field.getName());
field.setAccessible(true);
try {
if (field.get(service) == null) continue;
} catch (Exception e) {
logger.log(Level.SEVERE, field + " get value error", e);
continue;
}
fields.add(field);
if (sb.length() > 0) sb.append(',');
sb.append(field.getName());
}
} while ((clzz = clzz.getSuperclass()) != Object.class);
if (fields.isEmpty()) continue; //没有数据需要缓存
// synchronized (this.application.localServices) {
// if (this.application.localServices.contains(service)) continue;
// this.application.localServices.add(service);
// }
if (!cachedir.isDirectory()) cachedir.mkdirs();
String resname = Sncp.getResourceName(service);
FileOutputStream out = new FileOutputStream(new File(cachedir, prefix + Sncp.getResourceType(service).getName() + (resname.isEmpty() ? "" : ("-" + resname)) + ".bat"));
out.write(sb.toString().getBytes());
out.write('\n');
for (Field field : fields) {
Object val = null;
try {
val = field.get(service);
} catch (Exception e) {
logger.log(Level.SEVERE, field + " save value error", e);
}
convert.convertTo(out, field.getGenericType(), val);
out.write('\n');
}
out.close();
}
}
protected abstract ClassFilter<Filter> createFilterClassFilter();
protected abstract ClassFilter<Servlet> createServletClassFilter();
@@ -435,7 +580,7 @@ public abstract class NodeServer {
}
protected ClassFilter<Service> createServiceClassFilter() {
return createClassFilter(this.sncpGroup, null, Service.class, (!isSNCP() || application.watching) ? null : new Class[]{org.redkale.watch.WatchService.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,
@@ -531,7 +676,7 @@ public abstract class NodeServer {
public void setServerClassLoader(RedkaleClassLoader serverClassLoader) {
Objects.requireNonNull(this.serverClassLoader);
this.serverClassLoader = serverClassLoader;
this.serverThread.setContextClassLoader(serverClassLoader);
this.serverThread.setContextClassLoader(serverClassLoader);
}
public InetSocketAddress getSncpAddress() {

View File

@@ -85,6 +85,7 @@ public class NodeSncpServer extends NodeServer {
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() + "] ";
@@ -106,8 +107,9 @@ public class NodeSncpServer extends NodeServer {
}
@Override
@SuppressWarnings("unchecked")
protected ClassFilter<Filter> createFilterClassFilter() {
return createClassFilter(null, null, SncpFilter.class, null, null, "filters", "filter");
return createClassFilter(null, null, SncpFilter.class, new Class[]{org.redkale.watch.WatchFilter.class}, null, "filters", "filter");
}
@Override

View File

@@ -6,6 +6,7 @@
package org.redkale.convert;
import java.lang.reflect.Type;
import java.util.concurrent.CompletableFuture;
/**
* 对不明类型的对象进行序列化; 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
public Type getType() {
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

@@ -50,7 +50,7 @@ public final class CollectionDecoder<T> implements Decodeable<Reader, Collection
factory.register(type, this);
this.decoder = factory.loadDecoder(this.componentType);
} else {
throw new ConvertException("collectiondecoder not support the type (" + type + ")");
throw new ConvertException("CollectionDecoder not support the type (" + type + ")");
}
} finally {
inited = true;

View File

@@ -40,4 +40,7 @@ public abstract class Convert<R extends Reader, W extends Writer> {
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

@@ -7,15 +7,15 @@ package org.redkale.convert;
import java.io.File;
import java.lang.reflect.*;
import java.math.BigInteger;
import java.math.*;
import java.net.*;
import java.nio.ByteBuffer;
import java.nio.channels.CompletionHandler;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.*;
import java.util.regex.Pattern;
import java.util.stream.Stream;
import org.redkale.convert.ext.InetAddressSimpledCoder.InetSocketAddressSimpledCoder;
import java.util.stream.*;
import org.redkale.convert.ext.*;
import org.redkale.util.*;
@@ -88,15 +88,17 @@ public abstract class ConvertFactory<R extends Reader, W extends Writer> {
this.register(String.class, StringSimpledCoder.instance);
this.register(CharSequence.class, CharSequenceSimpledCoder.instance);
this.register(java.util.Date.class, DateSimpledCoder.instance);
this.register(AtomicInteger.class, AtomicIntegerSimpledCoder.instance);
this.register(AtomicLong.class, AtomicLongSimpledCoder.instance);
this.register(BigInteger.class, BigIntegerSimpledCoder.instance);
this.register(BigDecimal.class, BigDecimalSimpledCoder.instance);
this.register(InetAddress.class, InetAddressSimpledCoder.instance);
this.register(DLong.class, DLongSimpledCoder.instance);
this.register(Class.class, TypeSimpledCoder.instance);
this.register(InetSocketAddress.class, InetSocketAddressSimpledCoder.instance);
this.register(InetSocketAddress.class, InetAddressSimpledCoder.InetSocketAddressSimpledCoder.instance);
this.register(Pattern.class, PatternSimpledCoder.instance);
this.register(File.class, FileSimpledCoder.instance);
this.register(CompletionHandler.class, CompletionHandlerSimpledCoder.instance);
this.register(AsyncHandler.class, AsyncHandlerSimpledCoder.instance);
this.register(URL.class, URLSimpledCoder.instance);
this.register(URI.class, URISimpledCoder.instance);
//---------------------------------------------------------
@@ -106,9 +108,12 @@ public abstract class ConvertFactory<R extends Reader, W extends Writer> {
this.register(short[].class, ShortArraySimpledCoder.instance);
this.register(char[].class, CharArraySimpledCoder.instance);
this.register(int[].class, IntArraySimpledCoder.instance);
this.register(IntStream.class, IntArraySimpledCoder.IntStreamSimpledCoder.instance);
this.register(long[].class, LongArraySimpledCoder.instance);
this.register(LongStream.class, LongArraySimpledCoder.LongStreamSimpledCoder.instance);
this.register(float[].class, FloatArraySimpledCoder.instance);
this.register(double[].class, DoubleArraySimpledCoder.instance);
this.register(DoubleStream.class, DoubleArraySimpledCoder.DoubleStreamSimpledCoder.instance);
this.register(String[].class, StringArraySimpledCoder.instance);
//---------------------------------------------------------
this.register(AnyValue.class, Creator.create(AnyValue.DefaultAnyValue.class));
@@ -372,12 +377,17 @@ public abstract class ConvertFactory<R extends Reader, W extends Writer> {
encoders.put(clazz, coder);
}
public final <E> void register(final Type clazz, final Decodeable<R, E> decoder, final Encodeable<W, E> encoder) {
decoders.put(clazz, decoder);
encoders.put(clazz, encoder);
}
public final <E> void register(final Type clazz, final Decodeable<R, E> decoder) {
decoders.put(clazz, decoder);
}
public final <E> void register(final Type clazz, final Encodeable<W, E> printer) {
encoders.put(clazz, printer);
public final <E> void register(final Type clazz, final Encodeable<W, E> encoder) {
encoders.put(clazz, encoder);
}
public final <E> Decodeable<R, E> findDecoder(final Type type) {
@@ -458,6 +468,8 @@ public abstract class ConvertFactory<R extends Reader, W extends Writer> {
decoder = new StreamDecoder(this, type);
} else if (Map.class.isAssignableFrom(clazz)) {
decoder = new MapDecoder(this, type);
} else if (Optional.class == clazz) {
decoder = new OptionalCoder(this, type);
} else if (clazz == Object.class) {
od = new ObjectDecoder(type);
decoder = od;
@@ -543,6 +555,8 @@ public abstract class ConvertFactory<R extends Reader, W extends Writer> {
encoder = new StreamEncoder(this, type);
} else if (Map.class.isAssignableFrom(clazz)) {
encoder = new MapEncoder(this, type);
} else if (Optional.class == clazz) {
encoder = new OptionalCoder(this, type);
} else if (clazz == Object.class) {
return (Encodeable<W, E>) this.anyEncoder;
} else if (!clazz.getName().startsWith("java.") || java.net.HttpCookie.class == clazz) {

View File

@@ -50,6 +50,12 @@ public final class MapDecoder<K, V> implements Decodeable<Reader, Map<K, V>> {
factory.register(type, this);
this.keyDecoder = factory.loadDecoder(this.keyType);
this.valueDecoder = factory.loadDecoder(this.valueType);
} else if (factory.isReversible()) {
this.keyType = Object.class;
this.valueType = Object.class;
this.creator = factory.loadCreator((Class) type);
this.keyDecoder = factory.loadDecoder(this.keyType);
this.valueDecoder = factory.loadDecoder(this.valueType);
} else {
throw new ConvertException("mapdecoder not support the type (" + type + ")");
}

View File

@@ -0,0 +1,107 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.convert;
import java.lang.reflect.*;
import java.util.*;
/**
* Optional 的SimpledCoder实现
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
* @param <R> Reader输入的子类型
* @param <W> Writer输出的子类型
*/
public class OptionalCoder<R extends Reader, W extends Writer, T> extends SimpledCoder<R, W, Optional<T>> {
private final Type type;
private final Type componentType;
protected final Class componentClass;
protected final Decodeable<Reader, T> decoder;
protected final Encodeable<Writer, T> encoder;
private boolean inited = false;
private final Object lock = new Object();
public OptionalCoder(final ConvertFactory factory, final Type type) {
this.type = type;
try {
if (type instanceof ParameterizedType) {
final ParameterizedType pt = (ParameterizedType) type;
this.componentType = pt.getActualTypeArguments()[0];
factory.register(type, this);
this.decoder = factory.loadDecoder(this.componentType);
if (this.componentType instanceof TypeVariable) {
this.encoder = factory.getAnyEncoder();
this.componentClass = Object.class;
} else {
if (componentType instanceof ParameterizedType) {
final ParameterizedType pt2 = (ParameterizedType) componentType;
this.componentClass = (Class) pt2.getRawType();
} else {
this.componentClass = (Class) componentType;
}
this.encoder = factory.loadEncoder(this.componentType);
}
} else {
this.componentType = Object.class;
this.componentClass = Object.class;
this.decoder = factory.loadDecoder(this.componentType);
this.encoder = factory.getAnyEncoder();
}
} finally {
inited = true;
synchronized (lock) {
lock.notifyAll();
}
}
}
@Override
public void convertTo(W out, Optional<T> value) {
if (value == null || !value.isPresent()) {
out.writeObjectNull(null);
return;
}
if (this.encoder == null) {
if (!this.inited) {
synchronized (lock) {
try {
lock.wait();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
this.encoder.convertTo(out, value.get());
}
@Override
public Optional<T> convertFrom(R in) {
if (this.decoder == null) {
if (!this.inited) {
synchronized (lock) {
try {
lock.wait();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
return Optional.ofNullable(this.decoder.convertFrom(in));
}
}

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

@@ -37,7 +37,7 @@ import org.redkale.util.*;
*
* @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));
@@ -59,11 +59,6 @@ public final class BsonConvert extends Convert<BsonReader, BsonWriter> {
return BsonFactory.root().getConvert();
}
@Override
public boolean isBinary() {
return true;
}
//------------------------------ reader -----------------------------------------------------------
public BsonReader pollBsonReader(final ByteBuffer... buffers) {
return new BsonByteBufferReader((ConvertMask) null, buffers);
@@ -78,7 +73,7 @@ public final class BsonConvert extends Convert<BsonReader, BsonWriter> {
}
public void offerBsonReader(final BsonReader in) {
if (in != null) readerPool.offer(in);
if (in != null) readerPool.accept(in);
}
//------------------------------ writer -----------------------------------------------------------
@@ -95,7 +90,7 @@ public final class BsonConvert extends Convert<BsonReader, BsonWriter> {
}
public void offerBsonWriter(final BsonWriter out) {
if (out != null) writerPool.offer(out);
if (out != null) writerPool.accept(out);
}
//------------------------------ convertFrom -----------------------------------------------------------
@@ -104,33 +99,38 @@ public final class BsonConvert extends Convert<BsonReader, BsonWriter> {
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) {
if (type == null) return null;
final BsonReader in = readerPool.get();
in.setBytes(bytes, start, len);
@SuppressWarnings("unchecked")
T rs = (T) factory.loadDecoder(type).convertFrom(in);
readerPool.offer(in);
readerPool.accept(in);
return rs;
}
@SuppressWarnings("unchecked")
public <T> T convertFrom(final Type type, final InputStream in) {
if (type == null || in == null) return null;
return (T) factory.loadDecoder(type).convertFrom(new BsonStreamReader(in));
}
@Override
@SuppressWarnings("unchecked")
public <T> T convertFrom(final Type type, final ByteBuffer... buffers) {
if (type == null || buffers.length < 1) return null;
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) {
if (type == null) return null;
@SuppressWarnings("unchecked")
@@ -139,23 +139,35 @@ public final class BsonConvert extends Convert<BsonReader, BsonWriter> {
}
//------------------------------ convertTo -----------------------------------------------------------
@Override
public byte[] convertTo(final Object value) {
if (value == null) {
final BsonWriter out = writerPool.get().tiny(tiny);
out.writeNull();
byte[] result = out.toArray();
writerPool.offer(out);
writerPool.accept(out);
return result;
}
return convertTo(value.getClass(), value);
}
@Override
public byte[] convertTo(final Type type, final Object value) {
if (type == null) return null;
final BsonWriter out = writerPool.get().tiny(tiny);
factory.loadEncoder(type).convertTo(out, value);
byte[] result = out.toArray();
writerPool.offer(out);
writerPool.accept(out);
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.accept(out);
return result;
}
@@ -176,6 +188,14 @@ 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;
@@ -200,6 +220,18 @@ public final class BsonConvert extends Convert<BsonReader, BsonWriter> {
return out.toBuffers();
}
@Override
public ByteBuffer[] convertMapTo(final Supplier<ByteBuffer> supplier, final Object... values) {
if (supplier == null) return null;
BsonByteBufferWriter out = new BsonByteBufferWriter(tiny, supplier);
if (values == null) {
out.writeNull();
} else {
((AnyEncoder) factory.getAnyEncoder()).convertMapTo(out, values);
}
return out.toBuffers();
}
public void convertTo(final BsonWriter writer, final Object value) {
if (value == null) {
writer.writeNull();
@@ -213,6 +245,14 @@ public final class BsonConvert extends Convert<BsonReader, BsonWriter> {
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) {
if (value == null) return null;
return convertToWriter(value.getClass(), value);
@@ -225,4 +265,9 @@ public final class BsonConvert extends Convert<BsonReader, BsonWriter> {
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
@SuppressWarnings("unchecked")
public final void skipValue() {
if (typeval == 0) return;
final byte val = this.typeval;

View File

@@ -1,36 +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.ext;
import org.redkale.convert.*;
import org.redkale.util.AsyncHandler;
/**
* AsyncHandlerSimpledCoder 的SimpledCoder实现, 只输出null
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
* @param <R> Reader输入的子类型
* @param <W> Writer输出的子类型
*/
public final class AsyncHandlerSimpledCoder<R extends Reader, W extends Writer> extends SimpledCoder<R, W, AsyncHandler> {
public static final AsyncHandlerSimpledCoder instance = new AsyncHandlerSimpledCoder();
@Override
public void convertTo(W out, AsyncHandler value) {
out.writeObjectNull(AsyncHandler.class);
}
@Override
public AsyncHandler convertFrom(R in) {
in.readObjectB(AsyncHandler.class);
return null;
}
}

View File

@@ -0,0 +1,35 @@
/*
* 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.util.concurrent.atomic.AtomicInteger;
import org.redkale.convert.*;
/**
* AtomicInteger 的SimpledCoder实现
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
* @param <R> Reader输入的子类型
* @param <W> Writer输出的子类型
*/
public class AtomicIntegerSimpledCoder<R extends Reader, W extends Writer> extends SimpledCoder<R, W, AtomicInteger> {
public static final AtomicIntegerSimpledCoder instance = new AtomicIntegerSimpledCoder();
@Override
public void convertTo(W out, AtomicInteger value) {
out.writeInt(value == null ? 0 : value.get());
}
@Override
public AtomicInteger convertFrom(R in) {
return new AtomicInteger(in.readInt());
}
}

View File

@@ -0,0 +1,35 @@
/*
* 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.util.concurrent.atomic.AtomicLong;
import org.redkale.convert.*;
/**
* AtomicLong 的SimpledCoder实现
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
* @param <R> Reader输入的子类型
* @param <W> Writer输出的子类型
*/
public final class AtomicLongSimpledCoder<R extends Reader, W extends Writer> extends SimpledCoder<R, W, AtomicLong> {
public static final AtomicLongSimpledCoder instance = new AtomicLongSimpledCoder();
@Override
public void convertTo(W out, AtomicLong value) {
out.writeLong(value == null ? 0 : value.get());
}
@Override
public AtomicLong convertFrom(R in) {
return new AtomicLong(in.readLong());
}
}

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

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

View File

@@ -5,6 +5,7 @@
*/
package org.redkale.convert.ext;
import java.util.stream.DoubleStream;
import org.redkale.convert.Reader;
import org.redkale.convert.SimpledCoder;
import org.redkale.convert.Writer;
@@ -12,7 +13,9 @@ import org.redkale.convert.Writer;
/**
* double[] 的SimpledCoder实现
*
* <p> 详情见: https://redkale.org
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
* @param <R> Reader输入的子类型
* @param <W> Writer输出的子类型
@@ -66,4 +69,24 @@ public final class DoubleArraySimpledCoder<R extends Reader, W extends Writer> e
}
}
public final static class DoubleStreamSimpledCoder<R extends Reader, W extends Writer> extends SimpledCoder<R, W, DoubleStream> {
public static final DoubleStreamSimpledCoder instance = new DoubleStreamSimpledCoder();
@Override
public void convertTo(W out, DoubleStream values) {
if (values == null) {
out.writeNull();
return;
}
DoubleArraySimpledCoder.instance.convertTo(out, values.toArray());
}
@Override
public DoubleStream convertFrom(R in) {
double[] value = DoubleArraySimpledCoder.instance.convertFrom(in);
return value == null ? null : DoubleStream.of(value);
}
}
}

View File

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

View File

@@ -5,6 +5,7 @@
*/
package org.redkale.convert.ext;
import java.util.stream.IntStream;
import org.redkale.convert.Reader;
import org.redkale.convert.SimpledCoder;
import org.redkale.convert.Writer;
@@ -12,7 +13,9 @@ import org.redkale.convert.Writer;
/**
* int[] 的SimpledCoder实现
*
* <p> 详情见: https://redkale.org
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
* @param <R> Reader输入的子类型
* @param <W> Writer输出的子类型
@@ -66,4 +69,24 @@ public final class IntArraySimpledCoder<R extends Reader, W extends Writer> exte
}
}
public final static class IntStreamSimpledCoder<R extends Reader, W extends Writer> extends SimpledCoder<R, W, IntStream> {
public static final IntStreamSimpledCoder instance = new IntStreamSimpledCoder();
@Override
public void convertTo(W out, IntStream values) {
if (values == null) {
out.writeNull();
return;
}
IntArraySimpledCoder.instance.convertTo(out, values.toArray());
}
@Override
public IntStream convertFrom(R in) {
int[] value = IntArraySimpledCoder.instance.convertFrom(in);
return value == null ? null : IntStream.of(value);
}
}
}

View File

@@ -5,6 +5,7 @@
*/
package org.redkale.convert.ext;
import java.util.stream.LongStream;
import org.redkale.convert.Reader;
import org.redkale.convert.SimpledCoder;
import org.redkale.convert.Writer;
@@ -68,4 +69,24 @@ public final class LongArraySimpledCoder<R extends Reader, W extends Writer> ext
}
}
public final static class LongStreamSimpledCoder<R extends Reader, W extends Writer> extends SimpledCoder<R, W, LongStream> {
public static final LongStreamSimpledCoder instance = new LongStreamSimpledCoder();
@Override
public void convertTo(W out, LongStream values) {
if (values == null) {
out.writeNull();
return;
}
LongArraySimpledCoder.instance.convertTo(out, values.toArray());
}
@Override
public LongStream convertFrom(R in) {
long[] value = LongArraySimpledCoder.instance.convertFrom(in);
return value == null ? null : LongStream.of(value);
}
}
}

View File

@@ -21,9 +21,9 @@ import org.redkale.util.*;
* @author zhangjx
*/
@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.HashMap<String, String>>() {
}.getType();
private static final ObjectPool<JsonReader> readerPool = JsonReader.createPool(Integer.getInteger("convert.json.pool.size", 16));
@@ -46,11 +46,6 @@ public final class JsonConvert extends Convert<JsonReader, JsonWriter> {
return JsonFactory.root().getConvert();
}
@Override
public boolean isBinary() {
return false;
}
//------------------------------ reader -----------------------------------------------------------
public JsonReader pollJsonReader(final ByteBuffer... buffers) {
return new JsonByteBufferReader((ConvertMask) null, buffers);
@@ -65,7 +60,7 @@ public final class JsonConvert extends Convert<JsonReader, JsonWriter> {
}
public void offerJsonReader(final JsonReader in) {
if (in != null) readerPool.offer(in);
if (in != null) readerPool.accept(in);
}
//------------------------------ writer -----------------------------------------------------------
@@ -86,7 +81,7 @@ public final class JsonConvert extends Convert<JsonReader, JsonWriter> {
}
public void offerJsonWriter(final JsonWriter out) {
if (out != null) writerPool.offer(out);
if (out != null) writerPool.accept(out);
}
//------------------------------ convertFrom -----------------------------------------------------------
@@ -105,7 +100,7 @@ public final class JsonConvert extends Convert<JsonReader, JsonWriter> {
final JsonReader in = readerPool.get();
in.setText(text, start, len);
T rs = (T) factory.loadDecoder(type).convertFrom(in);
readerPool.offer(in);
readerPool.accept(in);
return rs;
}
@@ -134,18 +129,30 @@ public final class JsonConvert extends Convert<JsonReader, JsonWriter> {
}
//------------------------------ convertTo -----------------------------------------------------------
@Override
public String convertTo(final Object value) {
if (value == null) return "null";
return convertTo(value.getClass(), value);
}
@Override
public String convertTo(final Type type, final Object value) {
if (type == null) return null;
if (value == null) return "null";
final JsonWriter out = writerPool.get().tiny(tiny);
factory.loadEncoder(type).convertTo(out, value);
String result = out.toString();
writerPool.offer(out);
writerPool.accept(out);
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.accept(out);
return result;
}
@@ -153,7 +160,7 @@ public final class JsonConvert extends Convert<JsonReader, JsonWriter> {
if (value == null) {
new JsonStreamWriter(tiny, out).writeNull();
} else {
factory.loadEncoder(value.getClass()).convertTo(new JsonStreamWriter(tiny, out), value);
convertTo(out, value.getClass(), value);
}
}
@@ -162,7 +169,31 @@ public final class JsonConvert extends Convert<JsonReader, JsonWriter> {
if (value == null) {
new JsonStreamWriter(tiny, out).writeNull();
} else {
factory.loadEncoder(type).convertTo(new JsonStreamWriter(tiny, out), value);
final JsonWriter writer = writerPool.get().tiny(tiny);
factory.loadEncoder(type).convertTo(writer, value);
byte[] bs = writer.toBytes();
writerPool.accept(writer);
try {
out.write(bs);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
public void convertMapTo(final OutputStream out, final Object... values) {
if (values == null) {
new JsonStreamWriter(tiny, out).writeNull();
} else {
final JsonWriter writer = writerPool.get().tiny(tiny);
((AnyEncoder) factory.getAnyEncoder()).convertMapTo(writer, values);
byte[] bs = writer.toBytes();
writerPool.accept(writer);
try {
out.write(bs);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
@@ -190,6 +221,18 @@ public final class JsonConvert extends Convert<JsonReader, JsonWriter> {
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) {
if (value == null) {
writer.writeNull();
@@ -207,6 +250,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) {
if (value == null) return null;
return convertToWriter(value.getClass(), value);
@@ -218,4 +269,10 @@ public final class JsonConvert extends Convert<JsonReader, JsonWriter> {
factory.loadEncoder(type).convertTo(out, value);
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
*/
@SuppressWarnings("unchecked")
public final class JsonFactory extends ConvertFactory<JsonReader, JsonWriter> {
private static final JsonFactory instance = new JsonFactory(null, Boolean.getBoolean("convert.json.tiny"));

View File

@@ -162,6 +162,7 @@ public class JsonReader extends Reader {
* 判断下一个非空白字符是否为{
*
* @param clazz 类名
*
* @return 返回 null 表示对象为null 返回空字符串表示当前class与返回的class一致返回非空字符串表示class是当前class的子类。
*/
@Override
@@ -404,7 +405,7 @@ public class JsonReader extends Reader {
@Override
public final DeMember readFieldName(final DeMember[] members) {
final String exceptedfield = this.readSmallString();
if(exceptedfield == null) return null;
if (exceptedfield == null) return null;
final int len = members.length;
if (this.fieldIndex >= len) this.fieldIndex = 0;
for (int k = this.fieldIndex; k < len; k++) {
@@ -475,12 +476,22 @@ public class JsonReader extends Reader {
}
}
if (expected != '"' && expected != '\'') {
if (expected == 'n' && text0.length > currpos + 3) {
if (expected == 'n' && text0.length > currpos + 3 && (text0[1 + currpos] == 'u' && text0[2 + currpos] == 'l' && text0[3 + currpos] == 'l')) {
if (text0[++currpos] == 'u' && text0[++currpos] == 'l' && text0[++currpos] == 'l') {
this.position = currpos;
if (text0.length > currpos + 4) {
char ch = text0[currpos + 1];
if (ch == ',' || ch <= ' ' || ch == '}' || ch == ']' || ch == ':') return null;
final int start = currpos - 3;
for (;;) {
if (currpos >= text0.length) break;
ch = text0[currpos];
if (ch == ',' || ch <= ' ' || ch == '}' || ch == ']' || ch == ':') break;
currpos++;
}
if (currpos == start) throw new ConvertException("expected a string after a key but '" + text0[position] + "' (position = " + position + ") in (" + new String(this.text) + ")");
this.position = currpos - 1;
return new String(text0, start, currpos - start);
} else {
return null;
}
@@ -488,6 +499,7 @@ public class JsonReader extends Reader {
} else {
final int start = currpos;
for (;;) {
if (currpos >= text0.length) break;
char ch = text0[currpos];
if (ch == ',' || ch <= ' ' || ch == '}' || ch == ']' || ch == ':') break;
currpos++;

View File

@@ -59,6 +59,7 @@ public class JsonWriter extends Writer {
* 返回指定至少指定长度的缓冲区
*
* @param len
*
* @return
*/
private char[] expand(int len) {
@@ -108,6 +109,10 @@ public class JsonWriter extends Writer {
return new ByteBuffer[]{ByteBuffer.wrap(Utility.encodeUTF8(content, 0, count))};
}
public byte[] toBytes() {
return Utility.encodeUTF8(content, 0, count);
}
public int count() {
return this.count;
}

View File

@@ -26,11 +26,23 @@ public abstract class AsyncConnection implements AsynchronousByteChannel, AutoCl
protected Object subobject; //用于存储绑定在Connection上的对象 同attributes 只绑定单个对象时尽量使用subobject而非attributes
protected volatile long readtime;
protected volatile long writetime;
//关闭数
AtomicLong closedCounter = new AtomicLong();
AtomicLong closedCounter;
//在线数
AtomicLong livingCounter = new AtomicLong();
AtomicLong livingCounter;
public final long getLastReadTime() {
return readtime;
}
public final long getLastWriteTime() {
return writetime;
}
public abstract boolean isTCP();
@@ -110,40 +122,58 @@ public abstract class AsyncConnection implements AsynchronousByteChannel, AutoCl
}
//------------------------------------------------------------------------------------------------------------------------------
public static AsyncConnection create(final String protocol, final AsynchronousChannelGroup group, final SocketAddress address) throws IOException {
return create(protocol, group, address, 0, 0);
/**
* 创建TCP协议客户端连接
*
* @param address 连接点子
* @param group 连接AsynchronousChannelGroup
* @param readTimeoutSecond 读取超时秒数
* @param writeTimeoutSecond 写入超时秒数
*
* @return 连接CompletableFuture
*/
public static CompletableFuture<AsyncConnection> createTCP(final AsynchronousChannelGroup group, final SocketAddress address,
final int readTimeoutSecond, final int writeTimeoutSecond) {
return createTCP(group, address, false, readTimeoutSecond, writeTimeoutSecond);
}
/**
* 创建客户端连接
* 创建TCP协议客户端连接
*
* @param protocol 连接类型 只能是TCP或UDP
* @param address 连接点子
* @param group 连接AsynchronousChannelGroup
* @param readTimeoutSecond0 读取超时秒数
* @param writeTimeoutSecond0 写入超时秒数
* @param address 连接点子
* @param group 连接AsynchronousChannelGroup
* @param noDelay TcpNoDelay
* @param readTimeoutSecond 读取超时秒数
* @param writeTimeoutSecond 写入超时秒数
*
* @return 连接
* @throws java.io.IOException 异常
* @return 连接CompletableFuture
*/
public static AsyncConnection create(final String protocol, final AsynchronousChannelGroup group, final SocketAddress address,
final int readTimeoutSecond0, final int writeTimeoutSecond0) throws IOException {
if ("TCP".equalsIgnoreCase(protocol)) {
AsynchronousSocketChannel channel = AsynchronousSocketChannel.open(group);
try {
channel.connect(address).get(3, TimeUnit.SECONDS);
} catch (Exception e) {
throw new IOException("AsyncConnection connect " + address, e);
}
return create(channel, address, readTimeoutSecond0, writeTimeoutSecond0);
} else if ("UDP".equalsIgnoreCase(protocol)) {
DatagramChannel channel = DatagramChannel.open();
channel.configureBlocking(true);
channel.connect(address);
return create(channel, address, true, readTimeoutSecond0, writeTimeoutSecond0);
} else {
throw new RuntimeException("AsyncConnection not support protocol " + protocol);
public static CompletableFuture<AsyncConnection> createTCP(final AsynchronousChannelGroup group, final SocketAddress address,
final boolean noDelay, final int readTimeoutSecond, final int writeTimeoutSecond) {
final CompletableFuture future = new CompletableFuture();
try {
final AsynchronousSocketChannel channel = AsynchronousSocketChannel.open(group);
channel.connect(address, null, new CompletionHandler<Void, Void>() {
@Override
public void completed(Void result, Void attachment) {
if (noDelay) {
try {
channel.setOption(StandardSocketOptions.TCP_NODELAY, true);
} catch (IOException e) {
}
}
future.complete(create(channel, address, readTimeoutSecond, writeTimeoutSecond));
}
@Override
public void failed(Throwable exc, Void attachment) {
future.completeExceptionally(exc);
}
});
} catch (IOException e) {
future.completeExceptionally(e);
}
return future;
}
private static class BIOUDPAsyncConnection extends AsyncConnection {
@@ -209,6 +239,7 @@ public abstract class AsyncConnection implements AsynchronousByteChannel, AutoCl
rs += channel.send(srcs[i], remoteAddress);
if (i != offset) Thread.sleep(10);
}
this.writetime = System.currentTimeMillis();
if (handler != null) handler.completed(rs, attachment);
} catch (Exception e) {
if (handler != null) handler.failed(e, attachment);
@@ -219,6 +250,7 @@ public abstract class AsyncConnection implements AsynchronousByteChannel, AutoCl
public <A> void read(ByteBuffer dst, A attachment, CompletionHandler<Integer, ? super A> handler) {
try {
int rs = channel.read(dst);
this.readtime = System.currentTimeMillis();
if (handler != null) handler.completed(rs, attachment);
} catch (IOException e) {
if (handler != null) handler.failed(e, attachment);
@@ -229,6 +261,7 @@ public abstract class AsyncConnection implements AsynchronousByteChannel, AutoCl
public Future<Integer> read(ByteBuffer dst) {
try {
int rs = channel.read(dst);
this.readtime = System.currentTimeMillis();
return CompletableFuture.completedFuture(rs);
} catch (IOException e) {
throw new RuntimeException(e);
@@ -239,6 +272,7 @@ public abstract class AsyncConnection implements AsynchronousByteChannel, AutoCl
public <A> void write(ByteBuffer src, A attachment, CompletionHandler<Integer, ? super A> handler) {
try {
int rs = channel.send(src, remoteAddress);
this.writetime = System.currentTimeMillis();
if (handler != null) handler.completed(rs, attachment);
} catch (IOException e) {
if (handler != null) handler.failed(e, attachment);
@@ -249,6 +283,7 @@ public abstract class AsyncConnection implements AsynchronousByteChannel, AutoCl
public Future<Integer> write(ByteBuffer src) {
try {
int rs = channel.send(src, remoteAddress);
this.writetime = System.currentTimeMillis();
return CompletableFuture.completedFuture(rs);
} catch (IOException e) {
throw new RuntimeException(e);
@@ -359,6 +394,7 @@ public abstract class AsyncConnection implements AsynchronousByteChannel, AutoCl
for (int i = offset; i < offset + length; i++) {
rs += writeChannel.write(srcs[i]);
}
this.writetime = System.currentTimeMillis();
if (handler != null) handler.completed(rs, attachment);
} catch (IOException e) {
if (handler != null) handler.failed(e, attachment);
@@ -369,6 +405,7 @@ public abstract class AsyncConnection implements AsynchronousByteChannel, AutoCl
public <A> void read(ByteBuffer dst, A attachment, CompletionHandler<Integer, ? super A> handler) {
try {
int rs = readChannel.read(dst);
this.readtime = System.currentTimeMillis();
if (handler != null) handler.completed(rs, attachment);
} catch (IOException e) {
if (handler != null) handler.failed(e, attachment);
@@ -379,6 +416,7 @@ public abstract class AsyncConnection implements AsynchronousByteChannel, AutoCl
public Future<Integer> read(ByteBuffer dst) {
try {
int rs = readChannel.read(dst);
this.readtime = System.currentTimeMillis();
return CompletableFuture.completedFuture(rs);
} catch (IOException e) {
throw new RuntimeException(e);
@@ -389,6 +427,7 @@ public abstract class AsyncConnection implements AsynchronousByteChannel, AutoCl
public <A> void write(ByteBuffer src, A attachment, CompletionHandler<Integer, ? super A> handler) {
try {
int rs = writeChannel.write(src);
this.writetime = System.currentTimeMillis();
if (handler != null) handler.completed(rs, attachment);
} catch (IOException e) {
if (handler != null) handler.failed(e, attachment);
@@ -399,6 +438,7 @@ public abstract class AsyncConnection implements AsynchronousByteChannel, AutoCl
public Future<Integer> write(ByteBuffer src) {
try {
int rs = writeChannel.write(src);
this.writetime = System.currentTimeMillis();
return CompletableFuture.completedFuture(rs);
} catch (IOException e) {
throw new RuntimeException(e);
@@ -459,6 +499,7 @@ public abstract class AsyncConnection implements AsynchronousByteChannel, AutoCl
@Override
public <A> void read(ByteBuffer dst, A attachment, CompletionHandler<Integer, ? super A> handler) {
this.readtime = System.currentTimeMillis();
if (readTimeoutSecond > 0) {
channel.read(dst, readTimeoutSecond, TimeUnit.SECONDS, attachment, handler);
} else {
@@ -468,6 +509,7 @@ public abstract class AsyncConnection implements AsynchronousByteChannel, AutoCl
@Override
public <A> void write(ByteBuffer src, A attachment, CompletionHandler<Integer, ? super A> handler) {
this.writetime = System.currentTimeMillis();
if (writeTimeoutSecond > 0) {
channel.write(src, writeTimeoutSecond, TimeUnit.SECONDS, attachment, handler);
} else {
@@ -477,6 +519,7 @@ public abstract class AsyncConnection implements AsynchronousByteChannel, AutoCl
@Override
public <A> void write(ByteBuffer[] srcs, int offset, int length, A attachment, final CompletionHandler<Integer, ? super A> handler) {
this.writetime = System.currentTimeMillis();
channel.write(srcs, offset, length, writeTimeoutSecond > 0 ? writeTimeoutSecond : 60, TimeUnit.SECONDS,
attachment, new CompletionHandler<Long, A>() {
@@ -559,8 +602,8 @@ public abstract class AsyncConnection implements AsynchronousByteChannel, AutoCl
return create(ch, null, 0, 0);
}
public static AsyncConnection create(final AsynchronousSocketChannel ch, final SocketAddress addr0, final int readTimeoutSecond0, final int writeTimeoutSecond0) {
return new AIOTCPAsyncConnection(ch, addr0, readTimeoutSecond0, writeTimeoutSecond0);
public static AsyncConnection create(final AsynchronousSocketChannel ch, final SocketAddress addr0, final int readTimeoutSecond, final int writeTimeoutSecond) {
return new AIOTCPAsyncConnection(ch, addr0, readTimeoutSecond, writeTimeoutSecond);
}
}

View File

@@ -5,8 +5,10 @@
*/
package org.redkale.net;
import java.io.IOException;
import java.net.*;
import java.nio.*;
import java.nio.channels.AsynchronousChannelGroup;
import java.nio.charset.*;
import java.util.concurrent.*;
import java.util.function.*;
@@ -31,7 +33,7 @@ public class Context {
protected final long serverStartTime;
//Server的线程池
protected final ExecutorService executor;
protected final ThreadPoolExecutor executor;
//ByteBuffer的容量默认8K
protected final int bufferCapacity;
@@ -69,7 +71,7 @@ public class Context {
//JSON操作工厂
protected final JsonFactory jsonFactory;
public Context(long serverStartTime, Logger logger, ExecutorService executor, int bufferCapacity, ObjectPool<ByteBuffer> bufferPool, ObjectPool<Response> responsePool,
public Context(long serverStartTime, Logger logger, ThreadPoolExecutor executor, int bufferCapacity, ObjectPool<ByteBuffer> bufferPool, ObjectPool<Response> responsePool,
final int maxbody, Charset charset, InetSocketAddress address, final PrepareServlet prepare, final int readTimeoutSecond, final int writeTimeoutSecond) {
this.serverStartTime = serverStartTime;
this.logger = logger;
@@ -107,6 +109,10 @@ public class Context {
return executor.submit(r);
}
public AsynchronousChannelGroup createAsynchronousChannelGroup() throws IOException {
return AsynchronousChannelGroup.withThreadPool(executor);
}
public void runAsync(Runnable r) {
executor.execute(r);
}
@@ -124,13 +130,13 @@ public class Context {
}
public void offerBuffer(ByteBuffer buffer) {
bufferPool.offer(buffer);
bufferPool.accept(buffer);
}
public void offerBuffer(ByteBuffer... buffers) {
if (buffers == null) return;
for (ByteBuffer buffer : buffers) {
bufferPool.offer(buffer);
bufferPool.accept(buffer);
}
}

View File

@@ -6,6 +6,7 @@
package org.redkale.net;
import java.io.IOException;
import javax.annotation.Priority;
import org.redkale.util.*;
/**
@@ -33,18 +34,11 @@ public abstract class Filter<C extends Context, R extends Request<C>, P extends
public void destroy(C context, AnyValue config) {
}
/**
* 值越小越靠前执行
*
* @return int
*/
public int getIndex() {
return 0;
}
@Override
public final int compareTo(Object o) {
public int compareTo(Object o) {
if (!(o instanceof Filter)) return 1;
return this.getIndex() - ((Filter) o).getIndex();
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

@@ -118,6 +118,7 @@ public abstract class PrepareServlet<K extends Serializable, C extends Context,
}
@Override
@SuppressWarnings("unchecked")
public void init(C context, AnyValue config) {
synchronized (filters) {
if (!filters.isEmpty()) {
@@ -136,6 +137,7 @@ public abstract class PrepareServlet<K extends Serializable, C extends Context,
}
@Override
@SuppressWarnings("unchecked")
public void destroy(C context, AnyValue config) {
synchronized (filters) {
if (!filters.isEmpty()) {
@@ -146,6 +148,7 @@ public abstract class PrepareServlet<K extends Serializable, C extends Context,
}
}
@SuppressWarnings("unchecked")
public void addFilter(Filter<C, R, P> filter, AnyValue conf) {
filter._conf = conf;
synchronized (filters) {
@@ -176,6 +179,7 @@ public abstract class PrepareServlet<K extends Serializable, C extends Context,
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) {
@@ -198,10 +202,12 @@ public abstract class PrepareServlet<K extends Serializable, C extends Context,
}
}
@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 final void prepare(final ByteBuffer buffer, final R request, final P response) throws IOException {

View File

@@ -32,6 +32,9 @@ public abstract class ProtocolServer {
//在线数
protected final AtomicLong livingCounter = new AtomicLong();
//最大连接数小于1表示无限制
protected int maxconns;
public abstract void open() throws IOException;
public abstract void bind(SocketAddress local, int backlog) throws IOException;
@@ -42,6 +45,10 @@ public abstract class ProtocolServer {
public abstract void accept();
public void setMaxconns(int maxconns) {
this.maxconns = maxconns;
}
public abstract void close() throws IOException;
public abstract AsynchronousChannelGroup getChannelGroup();
@@ -198,6 +205,13 @@ public abstract class ProtocolServer {
@Override
public void completed(final AsynchronousSocketChannel channel, Void attachment) {
serchannel.accept(null, this);
if (maxconns > 0 && livingCounter.get() >= maxconns) {
try {
channel.close();
} catch (Exception e) {
}
return;
}
createCounter.incrementAndGet();
livingCounter.incrementAndGet();
AsyncConnection conn = AsyncConnection.create(channel, null, context.readTimeoutSecond, context.writeTimeoutSecond);

View File

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

View File

@@ -9,6 +9,7 @@ import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.CompletionHandler;
import java.util.function.BiConsumer;
import java.util.logging.Level;
/**
* 协议响应对象
@@ -113,8 +114,7 @@ public abstract class Response<C extends Context, R extends Request<C>> {
try {
recycleListener.accept(request, this);
} catch (Exception e) {
System.err.println(request);
e.printStackTrace();
context.logger.log(Level.WARNING, "Response.recycleListener error, request = " + request, e);
}
recycleListener = null;
}
@@ -195,7 +195,7 @@ public abstract class Response<C extends Context, R extends Request<C>> {
if (!this.inited) return; //避免重复关闭
//System.println("耗时: " + (System.currentTimeMillis() - request.createtime));
if (kill) refuseAlive();
this.context.responsePool.offer(this);
this.context.responsePool.accept(this);
}
public void finish(final byte[] bs) {

View File

@@ -71,7 +71,7 @@ public abstract class Server<K extends Serializable, C extends Context, R extend
protected int threads;
//线程池
protected ExecutorService executor;
protected ThreadPoolExecutor executor;
//ByteBuffer池大小
protected int bufferPoolSize;
@@ -88,6 +88,9 @@ public abstract class Server<K extends Serializable, C extends Context, R extend
//IO写入 的超时秒数小于1视为不设置
protected int writeTimeoutSecond;
//最大连接数
protected int maxconns;
protected Server(long serverStartTime, String protocol, PrepareServlet<K, C, R, P, S> servlet) {
this.serverStartTime = serverStartTime;
this.protocol = protocol;
@@ -99,11 +102,12 @@ public abstract class Server<K extends Serializable, C extends Context, R extend
this.config = config;
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.backlog = config.getIntValue("backlog", 8 * 1024);
this.maxconns = config.getIntValue("maxconns", 0);
this.readTimeoutSecond = config.getIntValue("readTimeoutSecond", 0);
this.writeTimeoutSecond = config.getIntValue("writeTimeoutSecond", 0);
this.maxbody = config.getIntValue("maxbody", 64 * 1024);
int bufCapacity = config.getIntValue("bufferCapacity", 8 * 1024);
this.backlog = parseLenth(config.getValue("backlog"), 8 * 1024);
this.maxbody = parseLenth(config.getValue("maxbody"), 64 * 1024);
int bufCapacity = parseLenth(config.getValue("bufferCapacity"), 8 * 1024);
this.bufferCapacity = bufCapacity < 256 ? 256 : bufCapacity;
this.threads = config.getIntValue("threads", Runtime.getRuntime().availableProcessors() * 16);
this.bufferPoolSize = config.getIntValue("bufferPoolSize", Runtime.getRuntime().availableProcessors() * 512);
@@ -113,13 +117,31 @@ public abstract class Server<K extends Serializable, C extends Context, R extend
final AtomicInteger counter = new AtomicInteger();
final Format f = createFormat();
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);
t.setName(n + "-ServletThread-" + f.format(counter.incrementAndGet()));
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 {
this.prepare.destroy(context, config);
}
@@ -148,6 +170,13 @@ public abstract class Server<K extends Serializable, C extends Context, R extend
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")
public void addServlet(S servlet, final Object attachment, AnyValue conf, K... mappings) {
this.prepare.addServlet(servlet, attachment, conf, mappings);
@@ -162,7 +191,8 @@ public abstract class Server<K extends Serializable, C extends Context, R extend
this.serverChannel.setOption(StandardSocketOptions.TCP_NODELAY, true);
}
serverChannel.bind(address, backlog);
serverChannel.accept();
serverChannel.setMaxconns(this.maxconns);
serverChannel.accept();
final String threadName = "[" + Thread.currentThread().getName() + "] ";
logger.info(threadName + this.getClass().getSimpleName() + ("TCP".equalsIgnoreCase(protocol) ? "" : ("." + protocol)) + " listen: " + address
+ ", threads: " + threads + ", bufferCapacity: " + bufferCapacity + ", bufferPoolSize: " + bufferPoolSize + ", responsePoolSize: " + responsePoolSize

View File

@@ -5,12 +5,16 @@
*/
package org.redkale.net;
import java.lang.ref.WeakReference;
import java.net.*;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.*;
import java.util.concurrent.*;
import java.util.function.Supplier;
import java.util.logging.Level;
import org.redkale.convert.*;
import org.redkale.convert.json.JsonConvert;
import org.redkale.util.*;
/**
@@ -40,6 +44,8 @@ public final class Transport {
supportTcpNoDelay = tcpNoDelay;
}
protected final TransportFactory factory;
protected final String name; //即<group>的name属性
protected final String subprotocol; //即<group>的subprotocol属性
@@ -52,52 +58,66 @@ public final class Transport {
protected final InetSocketAddress clientAddress;
protected InetSocketAddress[] remoteAddres = new InetSocketAddress[0];
protected TransportAddress[] transportAddres = new TransportAddress[0];
protected final ObjectPool<ByteBuffer> bufferPool;
//负载均衡策略
protected final TransportStrategy strategy;
protected final ConcurrentHashMap<SocketAddress, BlockingQueue<AsyncConnection>> connPool = new ConcurrentHashMap<>();
public Transport(String name, String subprotocol, final ObjectPool<ByteBuffer> transportBufferPool,
final AsynchronousChannelGroup transportChannelGroup, final InetSocketAddress clientAddress, final Collection<InetSocketAddress> addresses) {
this(name, DEFAULT_PROTOCOL, subprotocol, transportBufferPool, transportChannelGroup, clientAddress, addresses);
protected Transport(String name, String subprotocol, TransportFactory factory, final ObjectPool<ByteBuffer> transportBufferPool,
final AsynchronousChannelGroup transportChannelGroup, final InetSocketAddress clientAddress,
final Collection<InetSocketAddress> addresses, final TransportStrategy strategy) {
this(name, DEFAULT_PROTOCOL, subprotocol, factory, transportBufferPool, transportChannelGroup, clientAddress, addresses, strategy);
}
public Transport(String name, String protocol, String subprotocol, final ObjectPool<ByteBuffer> transportBufferPool,
final AsynchronousChannelGroup transportChannelGroup, final InetSocketAddress clientAddress, final Collection<InetSocketAddress> addresses) {
protected Transport(String name, String protocol, String subprotocol,
final TransportFactory factory, final ObjectPool<ByteBuffer> transportBufferPool,
final AsynchronousChannelGroup transportChannelGroup, final InetSocketAddress clientAddress,
final Collection<InetSocketAddress> addresses, final TransportStrategy strategy) {
this.name = name;
this.subprotocol = subprotocol == null ? "" : subprotocol.trim();
this.protocol = protocol;
this.factory = factory;
factory.transportReferences.add(new WeakReference<>(this));
this.tcp = "TCP".equalsIgnoreCase(protocol);
this.group = transportChannelGroup;
this.bufferPool = transportBufferPool;
this.clientAddress = clientAddress;
this.strategy = strategy;
updateRemoteAddresses(addresses);
}
public final InetSocketAddress[] updateRemoteAddresses(final Collection<InetSocketAddress> addresses) {
InetSocketAddress[] oldAddresses = this.remoteAddres;
List<InetSocketAddress> list = new ArrayList<>();
TransportAddress[] oldAddresses = this.transportAddres;
List<TransportAddress> list = new ArrayList<>();
if (addresses != null) {
for (InetSocketAddress addr : addresses) {
if (clientAddress != null && clientAddress.equals(addr)) continue;
list.add(addr);
list.add(new TransportAddress(addr));
}
}
this.remoteAddres = list.toArray(new InetSocketAddress[list.size()]);
return oldAddresses;
this.transportAddres = list.toArray(new TransportAddress[list.size()]);
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.remoteAddres == null) {
this.remoteAddres = new InetSocketAddress[]{addr};
if (this.transportAddres == null) {
this.transportAddres = new TransportAddress[]{new TransportAddress(addr)};
} else {
for (InetSocketAddress i : this.remoteAddres) {
if (addr.equals(i)) return false;
for (TransportAddress i : this.transportAddres) {
if (addr.equals(i.address)) return false;
}
this.remoteAddres = Utility.append(remoteAddres, addr);
this.transportAddres = Utility.append(transportAddres, new TransportAddress(addr));
}
return true;
}
@@ -105,9 +125,9 @@ public final class Transport {
public final boolean removeRemoteAddresses(InetSocketAddress addr) {
if (addr == null) return false;
if (this.remoteAddres == null) return false;
if (this.transportAddres == null) return false;
synchronized (this) {
this.remoteAddres = Utility.remove(remoteAddres, addr);
this.transportAddres = Utility.remove(transportAddres, new TransportAddress(addr));
}
return true;
}
@@ -128,13 +148,25 @@ public final class Transport {
return clientAddress;
}
public TransportAddress[] getTransportAddresses() {
return transportAddres;
}
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
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() {
@@ -146,7 +178,7 @@ public final class Transport {
}
public void offerBuffer(ByteBuffer buffer) {
bufferPool.offer(buffer);
bufferPool.accept(buffer);
}
public void offerBuffer(ByteBuffer... buffers) {
@@ -157,51 +189,71 @@ public final class Transport {
return tcp;
}
public AsyncConnection pollConnection(SocketAddress addr) {
if (addr == null && remoteAddres.length == 1) addr = remoteAddres[0];
public CompletableFuture<AsyncConnection> pollConnection(SocketAddress addr) {
if (this.strategy != null) return strategy.pollConnection(addr, this);
if (addr == null && this.transportAddres.length == 1) addr = this.transportAddres[0].address;
final boolean rand = addr == null;
if (rand && 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 {
if (tcp) {
AsynchronousSocketChannel channel = null;
if (rand) { //取地址
for (int i = 0; i < remoteAddres.length; i++) {
addr = remoteAddres[i];
BlockingQueue<AsyncConnection> queue = connPool.get(addr);
if (queue != null && !queue.isEmpty()) {
TransportAddress transportAddr;
boolean tryed = false;
for (int i = 0; i < transportAddres.length; i++) {
transportAddr = transportAddres[i];
addr = transportAddr.address;
if (!transportAddr.enable) continue;
final BlockingQueue<AsyncConnection> queue = transportAddr.conns;
if (!queue.isEmpty()) {
AsyncConnection conn;
while ((conn = queue.poll()) != null) {
if (conn.isOpen()) return conn;
if (conn.isOpen()) return CompletableFuture.completedFuture(conn);
}
}
tryed = true;
if (channel == null) {
channel = AsynchronousSocketChannel.open(group);
if (supportTcpNoDelay) channel.setOption(StandardSocketOptions.TCP_NODELAY, true);
}
try {
channel.connect(addr).get(2, TimeUnit.SECONDS);
transportAddr.enable = true;
break;
} catch (Exception iex) {
iex.printStackTrace();
if (i == remoteAddres.length - 1) channel = null;
transportAddr.enable = false;
channel = null;
}
}
if (channel == null && !tryed) {
for (int i = 0; i < transportAddres.length; i++) {
transportAddr = transportAddres[i];
addr = transportAddr.address;
if (channel == null) {
channel = AsynchronousSocketChannel.open(group);
if (supportTcpNoDelay) channel.setOption(StandardSocketOptions.TCP_NODELAY, true);
}
try {
channel.connect(addr).get(2, TimeUnit.SECONDS);
transportAddr.enable = true;
break;
} catch (Exception iex) {
transportAddr.enable = false;
channel = null;
}
}
}
} else {
channel = AsynchronousSocketChannel.open(group);
if (supportTcpNoDelay) channel.setOption(StandardSocketOptions.TCP_NODELAY, true);
channel.connect(addr).get(2, TimeUnit.SECONDS);
return AsyncConnection.createTCP(group, addr, supportTcpNoDelay, 6, 6);
}
if (channel == null) return null;
return AsyncConnection.create(channel, addr, 3000, 3000);
if (channel == null) return CompletableFuture.completedFuture(null);
return CompletableFuture.completedFuture(AsyncConnection.create(channel, addr, 6, 6));
} else { // UDP
if (rand) addr = remoteAddres[0];
if (rand) addr = this.transportAddres[0].address;
DatagramChannel channel = DatagramChannel.open();
channel.configureBlocking(true);
channel.connect(addr);
return AsyncConnection.create(channel, addr, true, 3000, 3000);
// AsyncDatagramChannel channel = AsyncDatagramChannel.open(group);
// channel.connect(addr);
// return AsyncConnection.create(channel, addr, true, 3000, 3000);
return CompletableFuture.completedFuture(AsyncConnection.create(channel, addr, true, 6, 6));
}
} catch (Exception ex) {
throw new RuntimeException("transport address = " + addr, ex);
@@ -224,36 +276,91 @@ public final class Transport {
}
public <A> void async(SocketAddress addr, final ByteBuffer buffer, A att, final CompletionHandler<Integer, A> handler) {
final AsyncConnection conn = pollConnection(addr);
conn.write(buffer, buffer, new CompletionHandler<Integer, ByteBuffer>() {
@Override
public void completed(Integer result, ByteBuffer attachment) {
buffer.clear();
conn.read(buffer, buffer, new CompletionHandler<Integer, ByteBuffer>() {
@Override
public void completed(Integer result, ByteBuffer attachment) {
if (handler != null) handler.completed(result, att);
offerBuffer(buffer);
offerConnection(false, conn);
}
@Override
public void failed(Throwable exc, ByteBuffer attachment) {
offerBuffer(buffer);
offerConnection(true, conn);
}
});
pollConnection(addr).whenComplete((conn, ex) -> {
if (ex != null) {
factory.getLogger().log(Level.WARNING, Transport.class.getSimpleName() + " async error", ex);
return;
}
conn.write(buffer, buffer, new CompletionHandler<Integer, ByteBuffer>() {
@Override
public void failed(Throwable exc, ByteBuffer attachment) {
offerBuffer(buffer);
offerConnection(true, conn);
}
@Override
public void completed(Integer result, ByteBuffer attachment) {
buffer.clear();
conn.read(buffer, buffer, new CompletionHandler<Integer, ByteBuffer>() {
@Override
public void completed(Integer result, ByteBuffer attachment) {
if (handler != null) handler.completed(result, att);
offerBuffer(buffer);
offerConnection(false, conn);
}
@Override
public void failed(Throwable exc, ByteBuffer attachment) {
offerBuffer(buffer);
offerConnection(true, conn);
}
});
}
@Override
public void failed(Throwable exc, ByteBuffer attachment) {
offerBuffer(buffer);
offerConnection(true, conn);
}
});
});
}
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

@@ -5,18 +5,23 @@
*/
package org.redkale.net;
import java.io.IOException;
import java.lang.ref.WeakReference;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousChannelGroup;
import java.nio.channels.*;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.*;
import java.util.function.Supplier;
import java.util.logging.*;
import java.util.stream.Collectors;
import org.redkale.service.Service;
import org.redkale.util.ObjectPool;
import org.redkale.util.*;
/**
* System.getProperty("net.transport.pinginterval", "30") 心跳周期默认30秒
*
* <p>
* 详情见: https://redkale.org
*
@@ -24,6 +29,8 @@ import org.redkale.util.ObjectPool;
*/
public class TransportFactory {
public static final String NAME_PINGINTERVAL = "pinginterval";
protected static final Logger logger = Logger.getLogger(TransportFactory.class.getSimpleName());
//传输端的线程池
@@ -43,10 +50,102 @@ public class TransportFactory {
protected final List<WeakReference<Service>> services = new CopyOnWriteArrayList<>();
public TransportFactory(ExecutorService executor, ObjectPool<ByteBuffer> bufferPool, AsynchronousChannelGroup channelGroup) {
protected final List<WeakReference<Transport>> transportReferences = new CopyOnWriteArrayList<>();
//心跳周期, 单位:秒
protected int pinginterval;
//ping的定时器
private ScheduledThreadPoolExecutor pingScheduler;
//ping的内容
private ByteBuffer pingBuffer;
//pong的数据长度, 小于0表示不进行判断
protected int pongLength;
//负载均衡策略
protected final TransportStrategy strategy;
protected TransportFactory(ExecutorService executor, ObjectPool<ByteBuffer> bufferPool, AsynchronousChannelGroup channelGroup,
final TransportStrategy strategy) {
this.executor = executor;
this.bufferPool = bufferPool;
this.channelGroup = channelGroup;
this.strategy = strategy;
}
protected TransportFactory(ExecutorService executor, ObjectPool<ByteBuffer> bufferPool, AsynchronousChannelGroup channelGroup) {
this(executor, bufferPool, channelGroup, null);
}
public void init(AnyValue conf, ByteBuffer pingBuffer, int pongLength) {
if (conf != null) {
this.pinginterval = conf.getIntValue(NAME_PINGINTERVAL, 0);
}
if (this.pinginterval > 0) {
if (this.pingScheduler == null && pingBuffer != null) {
this.pingBuffer = pingBuffer.asReadOnlyBuffer();
this.pongLength = pongLength;
this.pingScheduler = new ScheduledThreadPoolExecutor(1, (Runnable r) -> {
final Thread t = new Thread(r, this.getClass().getSimpleName() + "-TransportFactoryPingTask-Thread");
t.setDaemon(true);
return t;
});
pingScheduler.scheduleAtFixedRate(() -> {
pings();
}, pinginterval, pinginterval, TimeUnit.SECONDS);
}
}
}
public static TransportFactory create(int threads) {
return create(threads, threads * 2, 8 * 1024);
}
public static TransportFactory create(int threads, int bufferPoolSize, int bufferCapacity) {
final ObjectPool<ByteBuffer> transportPool = new ObjectPool<>(new AtomicLong(), new AtomicLong(), bufferPoolSize,
(Object... params) -> ByteBuffer.allocateDirect(bufferCapacity), null, (e) -> {
if (e == null || e.isReadOnly() || e.capacity() != bufferCapacity) return false;
e.clear();
return true;
});
final AtomicInteger counter = new AtomicInteger();
ExecutorService transportExec = Executors.newFixedThreadPool(threads, (Runnable r) -> {
Thread t = new Thread(r);
t.setDaemon(true);
t.setName("Transport-Thread-" + counter.incrementAndGet());
return t;
});
AsynchronousChannelGroup transportGroup = null;
try {
transportGroup = AsynchronousChannelGroup.withCachedThreadPool(transportExec, 1);
} catch (IOException e) {
throw new RuntimeException(e);
}
return create(transportExec, transportPool, transportGroup);
}
public static TransportFactory create(ExecutorService executor, ObjectPool<ByteBuffer> bufferPool, AsynchronousChannelGroup channelGroup) {
return new TransportFactory(executor, bufferPool, channelGroup, null);
}
public static TransportFactory create(ExecutorService executor, ObjectPool<ByteBuffer> bufferPool, AsynchronousChannelGroup channelGroup,
final TransportStrategy strategy) {
return new TransportFactory(executor, bufferPool, channelGroup, strategy);
}
public Transport createTransportTCP(String name, final InetSocketAddress clientAddress, final Collection<InetSocketAddress> addresses) {
return new Transport(name, "TCP", "", this, this.bufferPool, this.channelGroup, clientAddress, addresses, strategy);
}
public Transport createTransport(String name, String protocol, final InetSocketAddress clientAddress, final Collection<InetSocketAddress> addresses) {
return new Transport(name, protocol, "", this, this.bufferPool, this.channelGroup, clientAddress, addresses, strategy);
}
public Transport createTransport(String name, String protocol, String subprotocol,
final InetSocketAddress clientAddress, final Collection<InetSocketAddress> addresses) {
return new Transport(name, protocol, subprotocol, this, this.bufferPool, this.channelGroup, clientAddress, addresses, strategy);
}
public String findGroupName(InetSocketAddress addr) {
@@ -125,26 +224,34 @@ public class TransportFactory {
if (info == null) continue;
addresses.addAll(info.addresses);
}
if (info == null) return null;
if (info == null) info = new TransportGroupInfo("TCP");
if (sncpAddress != null) addresses.remove(sncpAddress);
return new Transport(groups.stream().sorted().collect(Collectors.joining(";")), info.protocol, info.subprotocol, this.bufferPool, this.channelGroup, sncpAddress, addresses);
return new Transport(groups.stream().sorted().collect(Collectors.joining(";")), info.protocol, info.subprotocol, this, 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);
return new Transport(groupName, info.protocol, info.subprotocol, this, this.bufferPool, this.channelGroup, sncpAddress, info.addresses, this.strategy);
}
public ExecutorService getExecutor() {
return executor;
}
public Supplier<ByteBuffer> getBufferSupplier() {
return bufferPool;
}
public List<TransportGroupInfo> getGroupInfos() {
return new ArrayList<>(this.groupInfos.values());
}
public Logger getLogger() {
return logger;
}
public void addSncpService(Service service) {
if (service == null) return;
services.add(new WeakReference<>(service));
@@ -160,6 +267,7 @@ public class TransportFactory {
}
public void shutdownNow() {
if (this.pingScheduler != null) this.pingScheduler.shutdownNow();
try {
this.channelGroup.shutdownNow();
} catch (Exception e) {
@@ -167,6 +275,73 @@ public class TransportFactory {
}
}
private void pings() {
long timex = System.currentTimeMillis() - (this.pinginterval < 15 ? this.pinginterval : (this.pinginterval - 3)) * 1000;
List<WeakReference> nulllist = new ArrayList<>();
for (WeakReference<Transport> ref : transportReferences) {
Transport transport = ref.get();
if (transport == null) {
nulllist.add(ref);
continue;
}
List<BlockingQueue<AsyncConnection>> list = new ArrayList<>(transport.getAsyncConnectionPool().values());
for (final BlockingQueue<AsyncConnection> queue : list) {
AsyncConnection conn;
while ((conn = queue.poll()) != null) {
if (conn.getLastWriteTime() > timex && false) { //最近几秒内已经进行过IO操作
queue.offer(conn);
} else { //超过一定时间的连接需要进行ping处理
ByteBuffer sendBuffer = pingBuffer.duplicate();
final AsyncConnection localconn = conn;
final BlockingQueue<AsyncConnection> localqueue = queue;
localconn.write(sendBuffer, sendBuffer, new CompletionHandler<Integer, ByteBuffer>() {
@Override
public void completed(Integer result, ByteBuffer buffer) {
if (buffer.hasRemaining()) {
localconn.write(buffer, buffer, this);
return;
}
ByteBuffer pongBuffer = bufferPool.get();
localconn.read(pongBuffer, pongBuffer, new CompletionHandler<Integer, ByteBuffer>() {
int counter = 0;
@Override
public void completed(Integer result, ByteBuffer attachment) {
if (counter > 3) {
bufferPool.accept(attachment);
localconn.dispose();
return;
}
if (pongLength > 0 && attachment.position() < pongLength) {
counter++;
localconn.read(pongBuffer, pongBuffer, this);
return;
}
bufferPool.accept(attachment);
localqueue.offer(localconn);
}
@Override
public void failed(Throwable exc, ByteBuffer attachment) {
localconn.dispose();
}
});
}
@Override
public void failed(Throwable exc, ByteBuffer buffer) {
localconn.dispose();
}
});
}
}
}
}
for (WeakReference ref : nulllist) {
transportReferences.remove(ref);
}
}
private static boolean checkName(String name) { //不能含特殊字符
if (name.isEmpty()) return false;
if (name.charAt(0) >= '0' && name.charAt(0) <= '9') return false;

View File

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

View File

@@ -7,6 +7,7 @@ package org.redkale.net.http;
import java.net.*;
import java.nio.*;
import java.nio.channels.CompletionHandler;
import java.nio.charset.*;
import java.security.*;
import java.util.concurrent.*;
@@ -30,7 +31,7 @@ public class HttpContext extends Context {
protected final ConcurrentHashMap<Class, Creator> asyncHandlerCreators = new ConcurrentHashMap<>();
public HttpContext(long serverStartTime, Logger logger, ExecutorService executor, int bufferCapacity, ObjectPool<ByteBuffer> bufferPool,
public HttpContext(long serverStartTime, Logger logger, ThreadPoolExecutor executor, int bufferCapacity, ObjectPool<ByteBuffer> bufferPool,
ObjectPool<Response> responsePool, int maxbody, Charset charset, InetSocketAddress address, PrepareServlet prepare,
int readTimeoutSecond, int writeTimeoutSecond) {
super(serverStartTime, logger, executor, bufferCapacity, bufferPool, responsePool, maxbody, charset,
@@ -53,7 +54,8 @@ public class HttpContext extends Context {
return responsePool;
}
protected <H extends AsyncHandler> Creator<H> loadAsyncHandlerCreator(Class<H> handlerClass) {
@SuppressWarnings("unchecked")
protected <H extends CompletionHandler> Creator<H> loadAsyncHandlerCreator(Class<H> handlerClass) {
Creator<H> creator = asyncHandlerCreators.get(handlerClass);
if (creator == null) {
creator = createAsyncHandlerCreator(handlerClass);
@@ -62,14 +64,15 @@ public class HttpContext extends Context {
return creator;
}
private <H extends AsyncHandler> Creator<H> createAsyncHandlerCreator(Class<H> handlerClass) {
@SuppressWarnings("unchecked")
private <H extends CompletionHandler> 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);
final String handlerName = CompletionHandler.class.getName().replace('.', '/');
final String handlerDesc = Type.getDescriptor(CompletionHandler.class);
final String newDynName = handlerClass.getName().replace('.', '/') + "_DyncAsyncHandler_" + (System.currentTimeMillis() % 10000);
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
FieldVisitor fv;
@@ -155,7 +158,7 @@ public class HttpContext extends Context {
}
cw.visitEnd();
byte[] bytes = cw.toByteArray();
Class<AsyncHandler> newHandlerClazz = (Class<AsyncHandler>) new ClassLoader(handlerClass.getClassLoader()) {
Class<CompletionHandler> newHandlerClazz = (Class<CompletionHandler>) new ClassLoader(handlerClass.getClassLoader()) {
public final Class<?> loadClass(String name, byte[] b) {
return defineClass(name, b, 0, b.length);
}

View File

@@ -235,6 +235,10 @@ public class HttpPrepareServlet extends PrepareServlet<String, HttpContext, Http
try {
final String uri = request.getRequestURI();
HttpServlet servlet;
if (response.isAutoOptions() && "OPTIONS".equals(request.getMethod())) {
response.finish(200, null);
return;
}
if (request.isWebSocket()) {
servlet = wsmappings.get(uri);
if (servlet == null && this.regWsArray != null) {

View File

@@ -10,6 +10,7 @@ import java.net.*;
import java.nio.ByteBuffer;
import java.nio.channels.Channels;
import java.nio.charset.Charset;
import java.util.*;
import org.redkale.convert.json.JsonConvert;
import org.redkale.net.*;
import org.redkale.util.*;
@@ -32,7 +33,7 @@ public class HttpRequest extends Request<HttpContext> {
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;
@@ -121,7 +122,6 @@ public class HttpRequest extends Request<HttpContext> {
} else {
this.requestURI = array.toDecodeString(index, offset - index, charset).trim();
}
if (this.requestURI.contains("../")) return -1;
index = ++offset;
this.protocol = array.toString(index, array.size() - index, charset).trim();
while (readLine(buffer, array)) {
@@ -519,8 +519,8 @@ public class HttpRequest extends Request<HttpContext> {
* @return cookie值
*/
public String getCookie(String name, String dfvalue) {
for (HttpCookie cookie : getCookies()) {
if (name.equals(cookie.getName())) return cookie.getValue();
for (HttpCookie c : getCookies()) {
if (name.equals(c.getName())) return c.getValue();
}
return dfvalue;
}
@@ -889,6 +889,29 @@ public class HttpRequest extends Request<HttpContext> {
}
//------------------------------------------------------------------------------
/**
* 获取请求Header总对象
*
* @return AnyValue
*/
public AnyValue getHeaders() {
return header;
}
/**
* 将请求Header转换成Map
*
* @param map Map
*
* @return Map
*/
public Map<String, String> getHeadersToMap(Map<String, String> map) {
if (map == null) map = new LinkedHashMap<>();
final Map<String, String> map0 = map;
header.forEach((k, v) -> map0.put(k, v));
return map0;
}
/**
* 获取所有的header名
*
@@ -1087,6 +1110,62 @@ public class HttpRequest extends Request<HttpContext> {
}
//------------------------------------------------------------------------------
/**
* 获取请求参数总对象
*
* @return AnyValue
*/
public AnyValue getParameters() {
parseBody();
return params;
}
/**
* 将请求参数转换成Map
*
* @param map Map
*
* @return Map
*/
public Map<String, String> getParametersToMap(Map<String, String> map) {
if (map == null) map = new LinkedHashMap<>();
final Map<String, String> map0 = map;
getParameters().forEach((k, v) -> map0.put(k, v));
return map0;
}
/**
* 将请求参数转换成String, 字符串格式为: bean1={}&amp;id=13&amp;name=xxx <br>
* 不会返回null没有参数返回空字符串
*
*
* @return String
*/
public String getParametersToString() {
return getParametersToString(null);
}
/**
* 将请求参数转换成String, 字符串格式为: bean1={}&amp;id=13&amp;name=xxx <br>
* 不会返回null没有参数返回空字符串
*
* @param prefix 拼接前缀, 如果无参数,返回的字符串不会含有拼接前缀
*
* @return String
*/
public String getParametersToString(String prefix) {
final StringBuilder sb = new StringBuilder();
getParameters().forEach((k, v) -> {
if (sb.length() > 0) sb.append('&');
try {
sb.append(k).append('=').append(URLEncoder.encode(v, "UTF-8"));
} catch (IOException ex) {
throw new RuntimeException(ex);
}
});
return (sb.length() > 0 && prefix != null) ? (prefix + sb) : sb.toString();
}
/**
* 获取所有参数名
*

View File

@@ -83,8 +83,6 @@ public class HttpResourceServlet extends HttpServlet {
}
}
protected final boolean finest = logger.isLoggable(Level.FINEST);
protected final LongAdder cachedLength = new LongAdder();
//缓存总大小, 默认0
@@ -192,6 +190,11 @@ public class HttpResourceServlet extends HttpServlet {
@Override
public void execute(HttpRequest request, HttpResponse response) throws IOException {
String uri = request.getRequestURI();
if (uri.contains("../")) {
if (logger.isLoggable(Level.FINEST)) logger.log(Level.FINEST, "Not found resource (404) be " + uri + ", request = " + request);
response.finish404();
return;
}
if (locationRewrites != null) {
for (SimpleEntry<Pattern, String> entry : locationRewrites) {
Matcher matcher = entry.getKey().matcher(uri);
@@ -215,7 +218,7 @@ public class HttpResourceServlet extends HttpServlet {
entry = files.computeIfAbsent(uri, x -> createFileEntry(x));
}
if (entry == null) {
if (finest) logger.log(Level.FINEST, "Not found resource (404), request = " + request);
if (logger.isLoggable(Level.FINER)) logger.log(Level.FINER, "Not found resource (404), request = " + request);
response.finish404();
} else {
//file = null 表示资源内容在内存而不是在File中

View File

@@ -16,6 +16,7 @@ import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicLong;
import java.util.logging.Level;
import org.redkale.convert.*;
import org.redkale.convert.json.JsonConvert;
import org.redkale.net.*;
import org.redkale.util.AnyValue.DefaultAnyValue;
@@ -42,13 +43,13 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
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();
protected static final byte[] LINE = new byte[]{'\r', '\n'};
protected static final byte[] serverNameBytes = ("Server: redkale/" + Redkale.getDotedVersion() + "\r\n").getBytes();
protected static final byte[] serverNameBytes = ("Server: " + System.getProperty("http.response.header.server", "redkale" + "/" + Redkale.getDotedVersion()) + "\r\n").getBytes();
private static final Set<OpenOption> options = new HashSet<>();
@@ -125,17 +126,21 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
private final String[][] defaultSetHeaders;
private final boolean autoOptions;
private final HttpCookie defcookie;
public static ObjectPool<Response> createPool(AtomicLong creatCounter, AtomicLong cycleCounter, int max, Creator<Response> creator) {
return new ObjectPool<>(creatCounter, cycleCounter, max, creator, (x) -> ((HttpResponse) x).prepare(), (x) -> ((HttpResponse) x).recycle());
}
public HttpResponse(HttpContext context, HttpRequest request, String[][] defaultAddHeaders, String[][] defaultSetHeaders, HttpCookie defcookie) {
public HttpResponse(HttpContext context, HttpRequest request, String[][] defaultAddHeaders, String[][] defaultSetHeaders,
HttpCookie defcookie, boolean autoOptions) {
super(context, request);
this.defaultAddHeaders = defaultAddHeaders;
this.defaultSetHeaders = defaultSetHeaders;
this.defcookie = defcookie;
this.autoOptions = autoOptions;
}
@Override
@@ -143,8 +148,13 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
return super.removeChannel();
}
protected AsyncConnection getChannel() {
return channel;
}
@Override
protected boolean recycle() {
boolean rs = super.recycle();
this.status = 200;
this.contentLength = -1;
this.contentType = null;
@@ -152,7 +162,7 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
this.headsended = false;
this.header.clear();
this.bufferHandler = null;
return super.recycle();
return rs;
}
@Override
@@ -181,10 +191,15 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
}
@Override
@SuppressWarnings("unchecked")
protected void thenEvent(Servlet servlet) {
this.servlet = servlet;
}
protected boolean isAutoOptions() {
return this.autoOptions;
}
/**
* 增加Cookie值
*
@@ -210,37 +225,32 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
}
/**
* 创建AsyncHandler实例
* 创建CompletionHandler实例
*
* @return AsyncHandler
* @return CompletionHandler
*/
public AsyncHandler createAsyncHandler() {
return AsyncHandler.create((v, a) -> {
if (v instanceof org.redkale.service.RetResult) {
finishJson((org.redkale.service.RetResult) v);
} else if (v instanceof CharSequence) {
finish(String.valueOf(v));
} else {
finishJson(v);
}
public CompletionHandler createAsyncHandler() {
return Utility.createAsyncHandler((v, a) -> {
finish(v);
}, (t, a) -> {
request.getContext().getLogger().log(Level.WARNING, "Servlet occur, forece to close channel. request = " + request, t);
request.getContext().getLogger().log(Level.WARNING, "Servlet occur, forece to close channel. request = " + request + ", result is CompletionHandler", (Throwable) t);
finish(500, null);
});
}
/**
* 创建AsyncHandler子类的实例 <br>
* 创建CompletionHandler子类的实例 <br>
*
* 传入的AsyncHandler子类必须是public且保证其子类可被继承且completed、failed可被重载且包含空参数的构造函数。
* 传入的CompletionHandler子类必须是public且保证其子类可被继承且completed、failed可被重载且包含空参数的构造函数。
*
* @param <H> 泛型
* @param handlerClass AsyncHandler子类
* @param handlerClass CompletionHandler子类
*
* @return AsyncHandler AsyncHandler
* @return CompletionHandler
*/
public <H extends AsyncHandler> H createAsyncHandler(Class<H> handlerClass) {
if (handlerClass == null || handlerClass == AsyncHandler.class) return (H) createAsyncHandler();
@SuppressWarnings("unchecked")
public <H extends CompletionHandler> H createAsyncHandler(Class<H> handlerClass) {
if (handlerClass == null || handlerClass == CompletionHandler.class) return (H) createAsyncHandler();
return context.loadAsyncHandlerCreator(handlerClass).create(createAsyncHandler());
}
@@ -255,6 +265,18 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
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格式输出
*
@@ -267,6 +289,19 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
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格式输出
*
@@ -340,7 +375,7 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
* @param future 输出对象的句柄
*/
public void finishJson(final CompletableFuture future) {
finishJson(request.getJsonConvert(), future);
finish(request.getJsonConvert(), (Type) null, future);
}
/**
@@ -349,21 +384,9 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
* @param convert 指定的JsonConvert
* @param future 输出对象的句柄
*/
@SuppressWarnings("unchecked")
public void finishJson(final JsonConvert convert, final CompletableFuture future) {
future.whenComplete((v, e) -> {
if (e != null) {
context.getLogger().log(Level.WARNING, "Servlet occur, forece to close channel. request = " + request, e);
finish(500, null);
return;
}
if (v instanceof CharSequence) {
finish(v.toString());
} else if (v instanceof org.redkale.service.RetResult) {
finishJson(convert, (org.redkale.service.RetResult) v);
} else {
finishJson(convert, v);
}
});
finish(convert, (Type) null, future);
}
/**
@@ -373,60 +396,88 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
* @param type 指定的类型
* @param future 输出对象的句柄
*/
@SuppressWarnings("unchecked")
public void finishJson(final JsonConvert convert, final Type type, final CompletableFuture future) {
future.whenComplete((v, e) -> {
if (e != null) {
context.getLogger().log(Level.WARNING, "Servlet occur, forece to close channel. request = " + request, e);
finish(500, null);
return;
}
if (v instanceof CharSequence) {
finish(v.toString());
} else if (v instanceof HttpResult) {
finishJson(convert, (HttpResult) v);
} else if (v instanceof org.redkale.service.RetResult) {
finishJson(convert, (org.redkale.service.RetResult) v);
} else {
finishJson(convert, type, v);
}
});
finish(convert, type, future);
}
/**
* 将HttpResult的结果对象以JSON格式输出
* 将结果对象输出
*
* @param result HttpResult对象
* @param obj 输出对象
*/
public void finishJson(final HttpResult result) {
finishJson(request.getJsonConvert(), result);
@SuppressWarnings("unchecked")
public void finish(final Object obj) {
finish(request.getJsonConvert(), (Type) null, obj);
}
/**
* 将HttpResult的结果对象以JSON格式输出
* 将结果对象输出
*
* @param convert 指定的JsonConvert
* @param result HttpResult对象
* @param convert 指定的Convert
* @param obj 输出对象
*/
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) {
@SuppressWarnings("unchecked")
public void finish(final Convert convert, final Object obj) {
finish(convert, (Type) null, obj);
}
/**
* 将结果对象输出
*
* @param convert 指定的Convert
* @param type 指定的类型
* @param obj 输出对象
*/
@SuppressWarnings("unchecked")
public void finish(final Convert convert, final Type type, final Object obj) {
if (obj == null) {
finish("null");
} else if (obj instanceof CompletableFuture) {
((CompletableFuture) obj).whenComplete((v, e) -> {
if (e != null) {
context.getLogger().log(Level.WARNING, "Servlet occur, forece to close channel. request = " + request + ", result is CompletableFuture", (Throwable) e);
finish(500, null);
return;
}
finish(convert, type, v);
});
} else if (obj instanceof CharSequence) {
finish((String) obj.toString());
} else if (obj instanceof byte[]) {
finish((byte[]) obj);
} else if (obj instanceof ByteBuffer) {
finish((ByteBuffer) obj);
} else if (obj instanceof ByteBuffer[]) {
finish((ByteBuffer[]) obj);
} else if (obj instanceof File) {
try {
finish((File) result.getResult());
finish((File) obj);
} catch (IOException e) {
getContext().getLogger().log(Level.WARNING, "HttpServlet finishJson HttpResult File occur, forece to close channel. request = " + getRequest(), e);
getContext().getLogger().log(Level.WARNING, "HttpServlet finish 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 if (obj instanceof HttpResult) {
HttpResult result = (HttpResult) obj;
if (result.getContentType() != null) setContentType(result.getContentType());
addHeader(result.getHeaders()).addCookie(result.getCookies()).setStatus(result.getStatus() < 1 ? 200 : result.getStatus());
if (result.getResult() == null) {
finish("");
} else {
finish(convert, result.getResult());
}
} else {
finishJson(result.getResult());
if (convert instanceof TextConvert) this.contentType = "text/plain; charset=utf-8";
if (this.recycleListener != null) this.output = obj;
if (obj instanceof org.redkale.service.RetResult) {
org.redkale.service.RetResult ret = (org.redkale.service.RetResult) obj;
if (!ret.isSuccess()) {
this.header.addValue("retcode", String.valueOf(ret.getRetcode())).addValue("retinfo", ret.getRetinfo());
}
}
ByteBuffer[] buffers = type == null ? convert.convertTo(context.getBufferSupplier(), obj)
: convert.convertTo(context.getBufferSupplier(), type, obj);
finish(buffers);
}
}
@@ -439,6 +490,7 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
if (isClosed()) return;
if (this.recycleListener != null) this.output = obj;
if (obj == null || obj.isEmpty()) {
this.contentLength = 0;
final ByteBuffer headbuf = createHeader();
headbuf.flip();
super.finish(headbuf);
@@ -602,7 +654,7 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
* @param attachment 异步回调参数
* @param handler 异步回调函数
*/
public <A> void sendBody(ByteBuffer buffer, A attachment, AsyncHandler<Integer, A> handler) {
public <A> void sendBody(ByteBuffer buffer, A attachment, CompletionHandler<Integer, A> handler) {
if (!this.headsended) {
if (this.contentLength < 0) this.contentLength = buffer == null ? 0 : buffer.remaining();
ByteBuffer headbuf = createHeader();
@@ -625,7 +677,7 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
* @param attachment 异步回调参数
* @param handler 异步回调函数
*/
public <A> void sendBody(ByteBuffer[] buffers, A attachment, AsyncHandler<Integer, A> handler) {
public <A> void sendBody(ByteBuffer[] buffers, A attachment, CompletionHandler<Integer, A> handler) {
if (!this.headsended) {
if (this.contentLength < 0) {
int len = 0;
@@ -704,8 +756,8 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
final String match = request.getHeader("If-None-Match");
final String etag = (file == null ? 0L : file.lastModified()) + "-" + length;
if (match != null && etag.equals(match)) {
finish304();
return;
//finish304();
//return;
}
this.contentLength = length;
if (filename != null && !filename.isEmpty() && file != null) {
@@ -733,18 +785,20 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
ByteBuffer hbuffer = createHeader();
hbuffer.flip();
if (fileBody == null) {
if (this.recycleListener != null) this.output = file;
finishFile(hbuffer, file, start, len);
} else {
if (start >= 0) {
fileBody.position((int) start);
if (len > 0) fileBody.limit((int) (fileBody.position() + len));
}
if (this.recycleListener != null) this.output = fileBody;
super.finish(hbuffer, fileBody);
}
}
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() {
@@ -754,7 +808,7 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
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());
}
if (!this.request.isKeepAlive()) {
@@ -974,7 +1028,9 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
this.bufferHandler = bufferHandler;
}
protected final class TransferFileHandler implements AsyncHandler<Integer, ByteBuffer> {
protected final class TransferFileHandler implements CompletionHandler<Integer, ByteBuffer> {
private final File file;
private final AsynchronousFileChannel filechannel;
@@ -982,48 +1038,68 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
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) {
this.filechannel = channel;
this.max = -1;
public TransferFileHandler(File file) throws IOException {
this.file = file;
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) {
this.filechannel = channel;
this.position = offset <= 0 ? 0 : offset;
this.max = len;
public TransferFileHandler(File file, long offset, long len) throws IOException {
this.file = file;
this.filechannel = AsynchronousFileChannel.open(file.toPath(), options, ((HttpContext) context).getExecutor());
this.readpos = offset <= 0 ? 0 : offset;
this.max = len <= 0 ? file.length() : len;
}
@Override
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);
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) {
read = false;
if (next) {
position += result;
} else {
next = true;
}
attachment.clear();
filechannel.read(attachment, position, attachment, this);
filechannel.read(attachment, readpos, attachment, this);
} else {
read = true;
if (max > 0) {
count += result;
if (count > max) {
attachment.limit((int) (attachment.position() + max - count));
}
if (count > max) {
attachment.limit((int) (attachment.position() + max - count));
}
attachment.flip();
channel.write(attachment, attachment, this);
if (attachment.hasRemaining()) {
channel.write(attachment, attachment, this);
} else {
failed(null, attachment);
}
}
}

View File

@@ -10,6 +10,7 @@ import java.net.HttpCookie;
import java.nio.ByteBuffer;
import java.util.*;
import java.util.concurrent.atomic.AtomicLong;
import java.util.logging.Level;
import org.redkale.net.*;
import org.redkale.net.sncp.Sncp;
import org.redkale.service.Service;
@@ -216,6 +217,7 @@ public class HttpServer extends Server<String, HttpContext, HttpRequest, HttpRes
*
* @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);
@@ -231,8 +233,7 @@ public class HttpServer extends Server<String, HttpContext, HttpRequest, HttpRes
break;
}
} catch (NoSuchFieldException | SecurityException e) {
System.err.println("serviceType = " + serviceType + ", servletClass = " + item.getClass());
e.printStackTrace();
logger.log(Level.SEVERE, "serviceType = " + serviceType + ", servletClass = " + item.getClass(), e);
}
}
final boolean first = servlet == null;
@@ -280,6 +281,8 @@ public class HttpServer extends Server<String, HttpContext, HttpRequest, HttpRes
});
final List<String[]> defaultAddHeaders = new ArrayList<>();
final List<String[]> defaultSetHeaders = new ArrayList<>();
boolean autoOptions = false;
HttpCookie defaultCookie = null;
String remoteAddrHeader = null;
if (config != null) {
@@ -342,10 +345,15 @@ public class HttpServer extends Server<String, HttpContext, HttpRequest, HttpRes
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[][] setHeaders = defaultSetHeaders.isEmpty() ? null : defaultSetHeaders.toArray(new String[defaultSetHeaders.size()][]);
final boolean options = autoOptions;
final HttpCookie defCookie = defaultCookie;
final String addrHeader = remoteAddrHeader;
AtomicLong createResponseCounter = new AtomicLong();
@@ -353,7 +361,7 @@ public class HttpServer extends Server<String, HttpContext, HttpRequest, HttpRes
ObjectPool<Response> responsePool = HttpResponse.createPool(createResponseCounter, cycleResponseCounter, this.responsePoolSize, null);
HttpContext httpcontext = new HttpContext(this.serverStartTime, this.logger, executor, rcapacity, bufferPool, responsePool,
this.maxbody, this.charset, this.address, this.prepare, this.readTimeoutSecond, this.writeTimeoutSecond);
responsePool.setCreator((Object... params) -> new HttpResponse(httpcontext, new HttpRequest(httpcontext, addrHeader), addHeaders, setHeaders, defCookie));
responsePool.setCreator((Object... params) -> new HttpResponse(httpcontext, new HttpRequest(httpcontext, addrHeader), addHeaders, setHeaders, defCookie, options));
return httpcontext;
}

View File

@@ -81,6 +81,7 @@ public class HttpServlet extends Servlet<HttpContext, HttpRequest, HttpResponse>
}
};
@SuppressWarnings("unchecked")
void preInit(HttpContext context, AnyValue config) {
String path = _prefix == null ? "" : _prefix;
WebServlet ws = this.getClass().getAnnotation(WebServlet.class);

View File

@@ -76,6 +76,7 @@ public class MimeType {
contentTypes.put("m3u", "audio/x-mpegurl");
contentTypes.put("mac", "image/x-macpaint");
contentTypes.put("man", "application/x-troff-man");
contentTypes.put("manifest", "text/cache-manifest");
contentTypes.put("mathml", "application/mathml+xml");
contentTypes.put("me", "application/x-troff-me");
contentTypes.put("mid", "audio/x-midi");

View File

@@ -133,17 +133,13 @@ public final class MultiContext {
has = true;
if (filenameReg != null && !filenameReg.isEmpty() && !part.getFilename().matches(filenameReg)) continue;
if (contentTypeReg != null && !contentTypeReg.isEmpty() && !part.getContentType().matches(contentTypeReg)) continue;
String name = part.getFilename();
int pos = name.lastIndexOf('.');
if (pos > 0) {
int pos2 = name.lastIndexOf('.', pos - 1);
if (pos2 >= 0) pos = pos2;
}
File file = new File(home, "tmp/redkale_" + System.nanoTime() + (pos > 0 ? name.substring(pos) : name));
file.getParentFile().mkdirs();
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;
}
@@ -169,17 +165,13 @@ public final class MultiContext {
for (MultiPart part : parts()) {
if (filenameReg != null && !filenameReg.isEmpty() && !part.getFilename().matches(filenameReg)) continue;
if (contentTypeReg != null && !contentTypeReg.isEmpty() && !part.getContentType().matches(contentTypeReg)) continue;
String name = part.getFilename();
int pos = name.lastIndexOf('.');
if (pos > 0) {
int pos2 = name.lastIndexOf('.', pos - 1);
if (pos2 >= 0) pos = pos2;
}
File file = new File(home, "tmp/redkale_" + System.nanoTime() + (pos > 0 ? name.substring(pos) : name));
file.getParentFile().mkdirs();
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<>();

View File

@@ -10,6 +10,7 @@ import java.lang.annotation.*;
import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.reflect.*;
import java.nio.channels.CompletionHandler;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import javax.annotation.Resource;
@@ -17,6 +18,7 @@ import jdk.internal.org.objectweb.asm.*;
import static jdk.internal.org.objectweb.asm.ClassWriter.COMPUTE_FRAMES;
import static jdk.internal.org.objectweb.asm.Opcodes.*;
import jdk.internal.org.objectweb.asm.Type;
import org.redkale.convert.Convert;
import org.redkale.convert.json.*;
import org.redkale.service.*;
import org.redkale.util.*;
@@ -29,6 +31,7 @@ import org.redkale.source.Flipper;
*
* @author zhangjx
*/
@SuppressWarnings("unchecked")
public final class Rest {
public static final String REST_HEADER_RESOURCE_NAME = "rest-resource-name";
@@ -92,7 +95,16 @@ public final class Rest {
return new MethodVisitor(Opcodes.ASM5) {
@Override
public void visitLocalVariable(String name, String description, String signature, Label start, Label end, int index) {
if (index > 0) fieldnames.add(name);
if (index < 1) return;
int size = fieldnames.size();
//index并不会按顺序执行的
if (index > size) {
for (int i = size; i < index; i++) {
fieldnames.add(" ");
}
fieldnames.set(index - 1, name);
}
fieldnames.set(index - 1, name);
}
};
}
@@ -117,21 +129,34 @@ public final class Rest {
List<Class> types = new ArrayList<>();
for (RestConvert rc : converts) {
if (types.contains(rc.type())) throw new RuntimeException("@RestConvert type(" + rc.type() + ") repeat");
childFactory.register(rc.type(), false, rc.convertColumns());
childFactory.register(rc.type(), true, rc.ignoreColumns());
childFactory.reloadCoder(rc.type());
if (rc.skipIgnore()) {
childFactory.registerSkipIgnore(rc.type());
childFactory.reloadCoder(rc.type());
} else {
childFactory.register(rc.type(), false, rc.convertColumns());
childFactory.register(rc.type(), true, rc.ignoreColumns());
childFactory.reloadCoder(rc.type());
}
types.add(rc.type());
childFactory.tiny(rc.tiny());
}
return childFactory.getConvert();
}
static String getWebModuleName(Class<? extends Service> serviceType) {
static String getWebModuleNameLowerCase(Class<? extends Service> serviceType) {
final RestService controller = serviceType.getAnnotation(RestService.class);
if (controller == null) return serviceType.getSimpleName().replaceAll("Service.*$", "").toLowerCase();
if (controller.ignore()) return null;
return (!controller.name().isEmpty()) ? controller.name() : serviceType.getSimpleName().replaceAll("Service.*$", "").toLowerCase();
}
static String getWebModuleName(Class<? extends Service> serviceType) {
final RestService controller = serviceType.getAnnotation(RestService.class);
if (controller == null) return serviceType.getSimpleName().replaceAll("Service.*$", "");
if (controller.ignore()) return null;
return (!controller.name().isEmpty()) ? controller.name() : serviceType.getSimpleName().replaceAll("Service.*$", "");
}
static boolean isRestDyn(HttpServlet servlet) {
return servlet.getClass().getAnnotation(RestDyn.class) != null;
}
@@ -172,22 +197,25 @@ public final class Rest {
}
}
if (!valid) throw new RuntimeException("Rest WebSocket Class(" + webSocketType + ") must have public or protected Constructor on createRestWebSocketServlet");
final String rwsname = ResourceFactory.formatResourceName(rws.name());
if (!checkName(rws.catalog())) throw new RuntimeException(webSocketType.getName() + " have illeal " + RestWebSocket.class.getSimpleName() + ".catalog, only 0-9 a-z A-Z _ cannot begin 0-9");
if (!checkName(rws.name())) throw new RuntimeException(webSocketType.getName() + " have illeal " + RestWebSocket.class.getSimpleName() + ".name, only 0-9 a-z A-Z _ cannot begin 0-9");
if (!checkName(rwsname)) throw new RuntimeException(webSocketType.getName() + " have illeal " + RestWebSocket.class.getSimpleName() + ".name, only 0-9 a-z A-Z _ cannot begin 0-9");
//----------------------------------------------------------------------------------------
final Set<Field> resourcesFieldSet = new LinkedHashSet<>();
final ClassLoader loader = classLoader == null ? Thread.currentThread().getContextClassLoader() : classLoader;
final Set<String> resourcesFieldNameSet = new HashSet<>();
Class clzz = webSocketType;
do {
for (Field field : webSocketType.getDeclaredFields()) {
for (Field field : clzz.getDeclaredFields()) {
if (field.getAnnotation(Resource.class) == null) continue;
if (Modifier.isStatic(webSocketType.getModifiers())) throw new RuntimeException(field + " cannot static on createRestWebSocketServlet");
if (Modifier.isFinal(webSocketType.getModifiers())) throw new RuntimeException(field + " cannot final on createRestWebSocketServlet");
if (!Modifier.isPublic(webSocketType.getModifiers()) && !Modifier.isProtected(webSocketType.getModifiers())) {
if (resourcesFieldNameSet.contains(field.getName())) continue;
if (Modifier.isStatic(field.getModifiers())) throw new RuntimeException(field + " cannot static on createRestWebSocketServlet");
if (Modifier.isFinal(field.getModifiers())) throw new RuntimeException(field + " cannot final on createRestWebSocketServlet");
if (!Modifier.isPublic(field.getModifiers()) && !Modifier.isProtected(field.getModifiers())) {
throw new RuntimeException(field + " must be public or protected on createRestWebSocketServlet");
}
resourcesFieldNameSet.add(field.getName());
resourcesFieldSet.add(field);
}
} while ((clzz = clzz.getSuperclass()) != Object.class);
@@ -246,7 +274,7 @@ public final class Rest {
av0.visitEnd();
}
{ //注入 @WebServlet 注解
String urlpath = (rws.catalog().isEmpty() ? "/" : ("/" + rws.catalog() + "/")) + rws.name();
String urlpath = (rws.catalog().isEmpty() ? "/" : ("/" + rws.catalog() + "/")) + rwsname;
av0 = cw.visitAnnotation(webServletDesc, true);
{
AnnotationVisitor av1 = av0.visitArray("value");
@@ -285,25 +313,36 @@ public final class Rest {
fv.visitEnd();
}
}
{ //构造函数
{ //_DynWebSocketServlet构造函数
mv = new AsmMethodVisitor(cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null));
mv.visitVarInsn(ALOAD, 0);
mv.visitMethodInsn(INVOKESPECIAL, supDynName, "<init>", "()V", false);
mv.visitVarInsn(ALOAD, 0);
mv.visitLdcInsn(Type.getObjectType(newDynName + "$" + newDynWebSokcetSimpleName + "Message"));
mv.visitFieldInsn(PUTFIELD, newDynName, "messageTextType", "Ljava/lang/reflect/Type;");
mv.visitVarInsn(ALOAD, 0);
if (rws.liveinterval() < 6) {
mv.visitInsn(ICONST_0 + rws.liveinterval());
} else {
mv.visitIntInsn(BIPUSH, rws.liveinterval());
}
pushInt(mv, rws.liveinterval());
mv.visitFieldInsn(PUTFIELD, newDynName, "liveinterval", "I");
mv.visitVarInsn(ALOAD, 0);
pushInt(mv, rws.wsmaxconns());
mv.visitFieldInsn(PUTFIELD, newDynName, "wsmaxconns", "I");
mv.visitVarInsn(ALOAD, 0);
pushInt(mv, rws.wsmaxbody());
mv.visitFieldInsn(PUTFIELD, newDynName, "wsmaxbody", "I");
mv.visitVarInsn(ALOAD, 0);
mv.visitInsn(rws.single() ? ICONST_1 : ICONST_0);
mv.visitFieldInsn(PUTFIELD, newDynName, "single", "Z");
mv.visitVarInsn(ALOAD, 0);
mv.visitInsn(rws.anyuser() ? ICONST_1 : ICONST_0);
mv.visitFieldInsn(PUTFIELD, newDynName, "anyuser", "Z");
mv.visitInsn(RETURN);
mv.visitMaxs(2, 1);
mv.visitMaxs(3, 1);
mv.visitEnd();
}
{ //createWebSocket 方法
@@ -328,6 +367,13 @@ public final class Rest {
mv.visitMaxs(2, 1);
mv.visitEnd();
}
{ //resourceName
mv = new AsmMethodVisitor(cw.visitMethod(ACC_PUBLIC, "resourceName", "()Ljava/lang/String;", null, null));
mv.visitLdcInsn(rwsname);
mv.visitInsn(ARETURN);
mv.visitMaxs(1, 1);
mv.visitEnd();
}
RestClassLoader newLoader = new RestClassLoader(loader);
@@ -541,7 +587,9 @@ public final class Rest {
final String webServletDesc = Type.getDescriptor(WebServlet.class);
final String reqDesc = Type.getDescriptor(HttpRequest.class);
final String respDesc = Type.getDescriptor(HttpResponse.class);
final String convertDesc = Type.getDescriptor(JsonConvert.class);
final String convertDesc = Type.getDescriptor(Convert.class);
final String typeDesc = Type.getDescriptor(java.lang.reflect.Type.class);
final String jsonConvertDesc = Type.getDescriptor(JsonConvert.class);
final String retDesc = Type.getDescriptor(RetResult.class);
final String futureDesc = Type.getDescriptor(CompletableFuture.class);
final String flipperDesc = Type.getDescriptor(Flipper.class);
@@ -568,7 +616,8 @@ public final class Rest {
String newDynName = serviceTypeInternalName.substring(0, serviceTypeInternalName.lastIndexOf('/') + 1) + "_Dyn" + serviceType.getSimpleName().replaceAll("Service.*$", "") + "RestServlet";
//------------------------------------------------------------------------------
final String defmodulename = getWebModuleName(serviceType);
final String defmodulename = getWebModuleNameLowerCase(serviceType);
final String bigmodulename = getWebModuleName(serviceType);
final String catalog = controller == null ? "" : controller.catalog();
if (!checkName(catalog)) throw new RuntimeException(serviceType.getName() + " have illeal " + RestService.class.getSimpleName() + ".catalog, only 0-9 a-z A-Z _ cannot begin 0-9");
if (!checkName(defmodulename)) throw new RuntimeException(serviceType.getName() + " have illeal " + RestService.class.getSimpleName() + ".value, only 0-9 a-z A-Z _ cannot begin 0-9");
@@ -671,7 +720,7 @@ public final class Rest {
if (ignore) continue;
paramtypes.add(method.getGenericParameterTypes());
if (mappings.length == 0) { //没有Mapping设置一个默认值
MappingEntry entry = new MappingEntry(methodidex, null, defmodulename, method);
MappingEntry entry = new MappingEntry(methodidex, null, bigmodulename, method);
if (entrys.contains(entry)) throw new RuntimeException(serviceType.getName() + " on " + method.getName() + " 's mapping(" + entry.name + ") is repeat");
entrys.add(entry);
} else {
@@ -684,7 +733,6 @@ public final class Rest {
methodidex++;
}
if (entrys.isEmpty()) return null; //没有可HttpMapping的方法
//将每个Service可转换的方法生成HttpServlet对应的HttpMapping方法
final Map<String, List<String>> asmParamMap = MethodParamClassVisitor.getMethodParamNames(serviceType);
final Map<String, java.lang.reflect.Type> bodyTypes = new HashMap<>();
@@ -831,7 +879,7 @@ public final class Rest {
if (ptype.isPrimitive() || ptype == String.class) n = "#";
}
if (annhead == null && anncookie == null && annsid == null && annaddr == null && annbody == null && annfile == null
&& !ptype.isPrimitive() && ptype != String.class && ptype != Flipper.class && !AsyncHandler.class.isAssignableFrom(ptype)
&& !ptype.isPrimitive() && ptype != String.class && ptype != Flipper.class && !CompletionHandler.class.isAssignableFrom(ptype)
&& !ptype.getName().startsWith("java") && n.charAt(0) != '#' && !"&".equals(n)) { //判断Json对象是否包含@RestUploadFile
Class loop = ptype;
do {
@@ -861,7 +909,7 @@ public final class Rest {
}
}
av0 = mv.visitAnnotation(mappingDesc, true);
String url = (catalog.isEmpty() ? "/" : ("/" + catalog + "/")) + defmodulename.toLowerCase() + "/" + entry.name + (reqpath ? "/" : "");
String url = (catalog.isEmpty() ? "/" : ("/" + catalog + "/")) + defmodulename + "/" + entry.name + (reqpath ? "/" : "");
av0.visit("url", url);
av0.visit("auth", entry.auth);
av0.visit("cacheseconds", entry.cacheseconds);
@@ -968,17 +1016,17 @@ public final class Rest {
paramMap.put("name", pname);
paramMap.put("type", ptype.getName());
if (AsyncHandler.class.isAssignableFrom(ptype)) { //HttpResponse.createAsyncHandler() or HttpResponse.createAsyncHandler(Class)
if (ptype == AsyncHandler.class) {
if (CompletionHandler.class.isAssignableFrom(ptype)) { //HttpResponse.createAsyncHandler() or HttpResponse.createAsyncHandler(Class)
if (ptype == CompletionHandler.class) {
mv.visitVarInsn(ALOAD, 2);
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "createAsyncHandler", "()Lorg/redkale/util/AsyncHandler;", false);
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "createAsyncHandler", "()Ljava/nio/channels/CompletionHandler;", false);
mv.visitVarInsn(ASTORE, maxLocals);
varInsns.add(new int[]{ALOAD, maxLocals});
} else {
mv.visitVarInsn(ALOAD, 3);
mv.visitVarInsn(ALOAD, 2);
mv.visitLdcInsn(Type.getType(Type.getDescriptor(ptype)));
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "createAsyncHandler", "(Ljava/lang/Class;)Lorg/redkale/util/AsyncHandler;", false);
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "createAsyncHandler", "(Ljava/lang/Class;)Ljava/nio/channels/CompletionHandler;", false);
mv.visitTypeInsn(CHECKCAST, ptype.getName().replace('.', '/'));
mv.visitVarInsn(ASTORE, maxLocals);
varInsns.add(new int[]{ALOAD, maxLocals});
@@ -1262,11 +1310,7 @@ public final class Rest {
} else {
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, newDynName, REST_PARAMTYPES_FIELD_NAME, "[[Ljava/lang/reflect/Type;");
if (entry.methodidx <= 5) { //方法下标
mv.visitInsn(ICONST_0 + entry.methodidx);
} else {
mv.visitIntInsn(BIPUSH, entry.methodidx);
}
pushInt(mv, entry.methodidx);//方法下标
mv.visitInsn(AALOAD);
int paramidx = 0;
for (int i = 0; i < params.length; i++) {
@@ -1275,11 +1319,7 @@ public final class Rest {
break;
}
}
if (paramidx <= 5) { //参数下标
mv.visitInsn(ICONST_0 + paramidx);
} else {
mv.visitIntInsn(BIPUSH, paramidx);
}
pushInt(mv, paramidx); //参数下标
mv.visitInsn(AALOAD);
}
mv.visitLdcInsn(pname);
@@ -1507,34 +1547,6 @@ public final class Rest {
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finish", "(Ljava/io/File;)V", false);
mv.visitInsn(RETURN);
maxLocals++;
} else if (RetResult.class.isAssignableFrom(returnType)) {
mv.visitVarInsn(ASTORE, maxLocals);
mv.visitVarInsn(ALOAD, 2); //response
if (rcs != null && rcs.length > 0) {
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, newDynName, REST_JSONCONVERT_FIELD_PREFIX + restConverts.size(), convertDesc);
mv.visitVarInsn(ALOAD, maxLocals);
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finishJson", "(" + convertDesc + retDesc + ")V", false);
} else {
mv.visitVarInsn(ALOAD, maxLocals);
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finishJson", "(" + retDesc + ")V", false);
}
mv.visitInsn(RETURN);
maxLocals++;
} else if (HttpResult.class.isAssignableFrom(returnType)) {
mv.visitVarInsn(ASTORE, maxLocals);
mv.visitVarInsn(ALOAD, 2); //response
if (rcs != null && rcs.length > 0) {
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, newDynName, REST_JSONCONVERT_FIELD_PREFIX + restConverts.size(), convertDesc);
mv.visitVarInsn(ALOAD, maxLocals);
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finishJson", "(" + convertDesc + httprsDesc + ")V", false);
} else {
mv.visitVarInsn(ALOAD, maxLocals);
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finishJson", "(" + httprsDesc + ")V", false);
}
mv.visitInsn(RETURN);
maxLocals++;
} else if (Number.class.isAssignableFrom(returnType) || CharSequence.class.isAssignableFrom(returnType)) { //returnType == String.class 必须放在前面
mv.visitVarInsn(ASTORE, maxLocals);
mv.visitVarInsn(ALOAD, 2); //response
@@ -1543,31 +1555,17 @@ public final class Rest {
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finish", "(Ljava/lang/String;)V", false);
mv.visitInsn(RETURN);
maxLocals++;
} else if (CompletableFuture.class.isAssignableFrom(returnType)) {
mv.visitVarInsn(ASTORE, maxLocals);
mv.visitVarInsn(ALOAD, 2);//response
if (rcs != null && rcs.length > 0) {
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, newDynName, REST_JSONCONVERT_FIELD_PREFIX + restConverts.size(), convertDesc);
mv.visitVarInsn(ALOAD, maxLocals);
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finishJson", "(" + convertDesc + futureDesc + ")V", false);
} else {
mv.visitVarInsn(ALOAD, maxLocals);
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finishJson", "(" + futureDesc + ")V", false);
}
mv.visitInsn(RETURN);
maxLocals++;
} else {
mv.visitVarInsn(ASTORE, maxLocals);
mv.visitVarInsn(ALOAD, 2); //response
if (rcs != null && rcs.length > 0) {
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, newDynName, REST_JSONCONVERT_FIELD_PREFIX + restConverts.size(), convertDesc);
mv.visitFieldInsn(GETFIELD, newDynName, REST_JSONCONVERT_FIELD_PREFIX + restConverts.size(), jsonConvertDesc);
mv.visitVarInsn(ALOAD, maxLocals);
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finishJson", "(" + convertDesc + "Ljava/lang/Object;)V", false);
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finish", "(" + convertDesc + "Ljava/lang/Object;)V", false);
} else {
mv.visitVarInsn(ALOAD, maxLocals);
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finishJson", "(Ljava/lang/Object;)V", false);
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finish", "(Ljava/lang/Object;)V", false);
}
mv.visitInsn(RETURN);
maxLocals++;
@@ -1588,7 +1586,7 @@ public final class Rest {
}
for (int i = 1; i <= restConverts.size(); i++) {
fv = cw.visitField(ACC_PRIVATE, REST_JSONCONVERT_FIELD_PREFIX + i, convertDesc, null, null);
fv = cw.visitField(ACC_PRIVATE, REST_JSONCONVERT_FIELD_PREFIX + i, jsonConvertDesc, null, null);
fv.visitEnd();
}
@@ -1644,6 +1642,30 @@ public final class Rest {
return true;
}
private static void pushInt(AsmMethodVisitor mv, int num) {
if (num < 6) {
mv.visitInsn(ICONST_0 + num);
} else if (num <= Byte.MAX_VALUE) {
mv.visitIntInsn(BIPUSH, num);
} else if (num <= Short.MAX_VALUE) {
mv.visitIntInsn(SIPUSH, num);
} else {
mv.visitLdcInsn(num);
}
}
private static void pushInt(MethodVisitor mv, int num) {
if (num < 6) {
mv.visitInsn(ICONST_0 + num);
} else if (num <= Byte.MAX_VALUE) {
mv.visitIntInsn(BIPUSH, num);
} else if (num <= Short.MAX_VALUE) {
mv.visitIntInsn(SIPUSH, num);
} else {
mv.visitLdcInsn(num);
}
}
private static class RestClassLoader extends ClassLoader {
public RestClassLoader(ClassLoader parent) {

View File

@@ -24,6 +24,10 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME;
@Repeatable(RestConvert.RestConverts.class)
public @interface RestConvert {
boolean tiny() default true;
boolean skipIgnore() default false;
Class type();
String[] ignoreColumns() default {};

View File

@@ -12,6 +12,7 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME;
/**
* 只能依附在WebSocket类上name默认为Service的类名小写并去掉Service字样及后面的字符串 (如HelloWebSocket/HelloWebSocketImpl的默认路径为 hello)。 <br>
* <b>注意: </b> 被标记&#64;RestWebSocket的WebSocket不能被修饰为abstract或final且其内部标记为&#64;Resource的字段只能是protected或public且必须要有一个protected或public的空参数构造函数。 <br>
* name值支持含{system.property.xxx}模式
* <p>
* 详情见: https://redkale.org
*
@@ -51,6 +52,13 @@ public @interface RestWebSocket {
*/
boolean single() default true;
/**
* WebSocket.createUserid返回的值是否不能表示户登录态
*
* @return 默认false
*/
boolean anyuser() default false;
/**
* WebScoket服务器给客户端进行ping操作的间隔时间, 单位: 秒, 默认值15秒
*
@@ -58,6 +66,20 @@ public @interface RestWebSocket {
*/
int liveinterval() default WebSocketServlet.DEFAILT_LIVEINTERVAL;
/**
* 最大连接数, 小于1表示无限制
*
* @return 最大连接数
*/
int wsmaxconns() default 0;
/**
* 最大消息体长度, 小于1表示无限制
*
* @return 最大消息体长度
*/
int wsmaxbody() default 16 * 1024;
/**
* 是否屏蔽该类的转换
*

View File

@@ -12,6 +12,7 @@ import java.nio.ByteBuffer;
import java.util.*;
import java.util.concurrent.*;
import java.util.function.Supplier;
import java.util.logging.*;
import java.util.stream.Stream;
import org.redkale.convert.Convert;
import org.redkale.util.Comment;
@@ -36,6 +37,9 @@ import org.redkale.util.Comment;
*/
public abstract class WebSocket<G extends Serializable, T> {
@Comment("强制关闭结果码")
public static final int CLOSECODE_FORCED = 1;
@Comment("消息不合法")
public static final int RETCODE_SEND_ILLPACKET = 1 << 1; //2
@@ -60,6 +64,9 @@ public abstract class WebSocket<G extends Serializable, T> {
@Comment("WebSocket已离线")
public static final int RETCODE_WSOFFLINE = 1 << 8; //256
@Comment("WebSocket将延迟发送")
public static final int RETCODE_DEAYSEND = 1 << 9; //512
WebSocketRunner _runner; //不可能为空
WebSocketEngine _engine; //不可能为空
@@ -82,18 +89,24 @@ public abstract class WebSocket<G extends Serializable, T> {
private long createtime = System.currentTimeMillis();
private long pingtime;
private Map<String, Object> attributes = new HashMap<>(); //非线程安全
List<WebSocketPacket> delayPackets;
protected WebSocket() {
}
//----------------------------------------------------------------
public final CompletableFuture<Integer> sendPing() {
this.pingtime = System.currentTimeMillis();
//if (_engine.finest) _engine.logger.finest(this + " on "+_engine.getEngineid()+" ping...");
return sendPacket(WebSocketPacket.DEFAULT_PING_PACKET);
}
public final CompletableFuture<Integer> sendPing(byte[] data) {
this.pingtime = System.currentTimeMillis();
return sendPacket(new WebSocketPacket(FrameType.PING, data));
}
@@ -113,7 +126,18 @@ public abstract class WebSocket<G extends Serializable, T> {
* @return 0表示成功 非0表示错误码
*/
public final CompletableFuture<Integer> send(Object message) {
return send(message, true);
return send(false, message, true);
}
/**
* 给自身发送消息, 消息类型是key-value键值对
*
* @param messages key-value键值对
*
* @return 0表示成功 非0表示错误码
*/
public final CompletableFuture<Integer> sendMap(Object... messages) {
return send(true, messages, true);
}
/**
@@ -125,6 +149,31 @@ public abstract class WebSocket<G extends Serializable, T> {
* @return 0表示成功 非0表示错误码
*/
public final CompletableFuture<Integer> send(Object message, boolean last) {
return send(false, message, last);
}
/**
* 给自身发送消息, 消息类型是key-value键值对
*
* @param last 是否最后一条
* @param messages key-value键值对
*
* @return 0表示成功 非0表示错误码
*/
public final CompletableFuture<Integer> sendMap(boolean last, Object... messages) {
return send(true, messages, last);
}
/**
* 给自身发送消息, 消息类型是Object[]
*
* @param mapconvable 是否convertMapTo
* @param message 不可为空, 只能是String或byte[]或可JavaBean对象或Object[]
* @param last 是否最后一条
*
* @return 0表示成功 非0表示错误码
*/
private CompletableFuture<Integer> send(boolean mapconvable, Object message, boolean last) {
if (message instanceof CompletableFuture) {
return ((CompletableFuture) message).thenCompose((json) -> {
if (json == null || json instanceof CharSequence || json instanceof byte[]) {
@@ -132,7 +181,7 @@ public abstract class WebSocket<G extends Serializable, T> {
} else if (message instanceof WebSocketPacket) {
return sendPacket((WebSocketPacket) message);
} else {
return sendPacket(new WebSocketPacket(getSendConvert(), json, last));
return sendPacket(new WebSocketPacket(getSendConvert(), mapconvable, json, last));
}
});
}
@@ -141,7 +190,7 @@ public abstract class WebSocket<G extends Serializable, T> {
} else if (message instanceof WebSocketPacket) {
return sendPacket((WebSocketPacket) message);
} else {
return sendPacket(new WebSocketPacket(getSendConvert(), message, last));
return sendPacket(new WebSocketPacket(getSendConvert(), mapconvable, message, last));
}
}
@@ -168,9 +217,9 @@ public abstract class WebSocket<G extends Serializable, T> {
*/
public final CompletableFuture<Integer> send(Convert convert, Object message, boolean last) {
if (message instanceof CompletableFuture) {
return ((CompletableFuture) message).thenCompose((json) -> sendPacket(new WebSocketPacket(convert == null ? getSendConvert() : convert, json, last)));
return ((CompletableFuture) message).thenCompose((json) -> sendPacket(new WebSocketPacket(convert == null ? getSendConvert() : convert, false, json, last)));
}
return sendPacket(new WebSocketPacket(convert == null ? getSendConvert() : convert, message, last));
return sendPacket(new WebSocketPacket(convert == null ? getSendConvert() : convert, false, message, last));
}
/**
@@ -181,12 +230,31 @@ public abstract class WebSocket<G extends Serializable, T> {
* @return 0表示成功 非0表示错误码
*/
CompletableFuture<Integer> sendPacket(WebSocketPacket packet) {
if (this._runner == null) {
if (delayPackets == null) delayPackets = new ArrayList<>();
delayPackets.add(packet);
return CompletableFuture.completedFuture(RETCODE_DEAYSEND);
}
CompletableFuture<Integer> rs = this._runner.sendMessage(packet);
if (_engine.finest) _engine.logger.finest("userid:" + getUserid() + " send websocket message(" + packet + ")" + " on " + this);
if (_engine.logger.isLoggable(Level.FINEST) && packet != WebSocketPacket.DEFAULT_PING_PACKET) {
_engine.logger.finest("userid:" + getUserid() + " send websocket message(" + packet + ")" + " on " + this);
}
return rs == null ? CompletableFuture.completedFuture(RETCODE_WSOCKET_CLOSED) : rs;
}
//----------------------------------------------------------------
/**
* 给指定userid的WebSocket节点发送 二进制消息/文本消息/JavaBean对象消息
*
* @param message 不可为空
* @param userids Stream
*
* @return 为0表示成功 其他值表示异常
*/
public final CompletableFuture<Integer> sendMessage(Object message, Stream<G> userids) {
return sendMessage(message, true, userids);
}
/**
* 给指定userid的WebSocket节点发送 二进制消息/文本消息/JavaBean对象消息
*
@@ -199,6 +267,45 @@ public abstract class WebSocket<G extends Serializable, T> {
return sendMessage(message, true, userids);
}
/**
* 给指定userid的WebSocket节点发送 二进制消息/文本消息/JavaBean对象消息
*
* @param convert Convert
* @param message 不可为空
* @param userids Stream
*
* @return 为0表示成功 其他值表示异常
*/
public final CompletableFuture<Integer> sendMessage(final Convert convert, Object message, Stream<G> userids) {
return sendMessage(convert, message, true, userids);
}
/**
* 给指定userid的WebSocket节点发送 二进制消息/文本消息/JavaBean对象消息
*
* @param convert Convert
* @param message 不可为空
* @param userids Serializable[]
*
* @return 为0表示成功 其他值表示异常
*/
public final CompletableFuture<Integer> sendMessage(final Convert convert, Object message, G... userids) {
return sendMessage(convert, message, true, userids);
}
/**
* 给指定userid的WebSocket节点发送 二进制消息/文本消息/JavaBean对象消息
*
* @param message 不可为空
* @param last 是否最后一条
* @param userids Serializable[]
*
* @return 为0表示成功 其他值表示异常
*/
public final CompletableFuture<Integer> sendMessage(Object message, boolean last, Stream<G> userids) {
return sendMessage((Convert) null, message, last, userids);
}
/**
* 给指定userid的WebSocket节点发送 二进制消息/文本消息/JavaBean对象消息
*
@@ -209,12 +316,45 @@ public abstract class WebSocket<G extends Serializable, T> {
* @return 为0表示成功 其他值表示异常
*/
public final CompletableFuture<Integer> sendMessage(Object message, boolean last, G... userids) {
return sendMessage((Convert) null, message, last, userids);
}
/**
* 给指定userid的WebSocket节点发送 二进制消息/文本消息/JavaBean对象消息
*
* @param convert Convert
* @param message 不可为空
* @param last 是否最后一条
* @param userids Stream
*
* @return 为0表示成功 其他值表示异常
*/
public final CompletableFuture<Integer> sendMessage(final Convert convert, Object message, boolean last, final Stream<G> userids) {
Object[] array = userids.toArray();
Serializable[] ss = new Serializable[array.length];
for (int i = 0; i < array.length; i++) {
ss[i] = (Serializable) array[i];
}
return sendMessage(convert, message, last, ss);
}
/**
* 给指定userid的WebSocket节点发送 二进制消息/文本消息/JavaBean对象消息
*
* @param convert Convert
* @param message 不可为空
* @param last 是否最后一条
* @param userids Serializable[]
*
* @return 为0表示成功 其他值表示异常
*/
public final CompletableFuture<Integer> sendMessage(final Convert convert, Object message, boolean last, Serializable... userids) {
if (_engine.node == null) return CompletableFuture.completedFuture(RETCODE_NODESERVICE_NULL);
if (message instanceof CompletableFuture) {
return ((CompletableFuture) message).thenCompose((json) -> _engine.node.sendMessage(json, last, userids));
return ((CompletableFuture) message).thenCompose((json) -> _engine.node.sendMessage(convert, json, last, userids));
}
CompletableFuture<Integer> rs = _engine.node.sendMessage(message, last, userids);
if (_engine.finest) _engine.logger.finest("userids:" + Arrays.toString(userids) + " send websocket message(" + message + ")");
CompletableFuture<Integer> rs = _engine.node.sendMessage(convert, message, last, userids);
if (_engine.logger.isLoggable(Level.FINEST)) _engine.logger.finest("userids:" + Arrays.toString(userids) + " send websocket message(" + message + ")");
return rs;
}
@@ -226,7 +366,19 @@ public abstract class WebSocket<G extends Serializable, T> {
* @return 为0表示成功 其他值表示部分发送异常
*/
public final CompletableFuture<Integer> broadcastMessage(final Object message) {
return broadcastMessage(message, true);
return broadcastMessage((Convert) null, message, true);
}
/**
* 广播消息, 给所有人发消息
*
* @param convert Convert
* @param message 消息内容
*
* @return 为0表示成功 其他值表示部分发送异常
*/
public final CompletableFuture<Integer> broadcastMessage(final Convert convert, final Object message) {
return broadcastMessage(convert, message, true);
}
/**
@@ -238,12 +390,25 @@ public abstract class WebSocket<G extends Serializable, T> {
* @return 为0表示成功 其他值表示部分发送异常
*/
public final CompletableFuture<Integer> broadcastMessage(final Object message, final boolean last) {
return broadcastMessage((Convert) null, message, last);
}
/**
* 广播消息, 给所有人发消息
*
* @param convert Convert
* @param message 消息内容
* @param last 是否最后一条
*
* @return 为0表示成功 其他值表示部分发送异常
*/
public final CompletableFuture<Integer> broadcastMessage(final Convert convert, final Object message, final boolean last) {
if (_engine.node == null) return CompletableFuture.completedFuture(RETCODE_NODESERVICE_NULL);
if (message instanceof CompletableFuture) {
return ((CompletableFuture) message).thenCompose((json) -> _engine.node.broadcastMessage(json, last));
return ((CompletableFuture) message).thenCompose((json) -> _engine.node.broadcastMessage(convert, json, last));
}
CompletableFuture<Integer> rs = _engine.node.broadcastMessage(message, last);
if (_engine.finest) _engine.logger.finest("broadcast send websocket message(" + message + ")");
CompletableFuture<Integer> rs = _engine.node.broadcastMessage(convert, message, last);
if (_engine.logger.isLoggable(Level.FINEST)) _engine.logger.finest("broadcast send websocket message(" + message + ")");
return rs;
}
@@ -274,6 +439,30 @@ public abstract class WebSocket<G extends Serializable, T> {
return _engine.node.getRpcNodeWebSocketAddresses(userid);
}
/**
* 更改本WebSocket的userid
*
* @param newuserid 新用户ID不能为null
*
* @return CompletableFuture
*/
public CompletableFuture<Void> changeUserid(final G newuserid) {
if (newuserid == null) throw new NullPointerException("newuserid is null");
return _engine.changeUserid(this, newuserid);
}
/**
* 强制关闭用户的所有WebSocket
*
* @param userid Serializable
*
* @return int
*/
@Comment("强制关闭用户的所有WebSocket")
public CompletableFuture<Integer> forceCloseWebSocket(Serializable userid) {
return _engine.node.forceCloseWebSocket(userid);
}
/**
* 获取当前WebSocket下的属性非线程安全
*
@@ -478,6 +667,33 @@ public abstract class WebSocket<G extends Serializable, T> {
public void onClose(int code, String reason) {
}
/**
* 发生异常时调用
*
* @param t 异常
* @param buffers ByteBuffer[]
*/
public void onOccurException(Throwable t, ByteBuffer[] buffers) {
this.getLogger().log(Level.SEVERE, "WebSocket receive or send Message error", t);
}
/**
* 当Single模式下用户重复登陆时回调函数 默认处理逻辑关闭之前的WebSocket连接
*
*/
public void onSingleRepeatConnect() {
this._engine.node.forceCloseWebSocket(getUserid());
}
/**
* 获取Logger
*
* @return Logger Logger
*/
public Logger getLogger() {
return this._engine.logger;
}
/**
* 获取最后一次发送消息的时间
*
@@ -487,11 +703,29 @@ public abstract class WebSocket<G extends Serializable, T> {
return this._runner == null ? 0 : this._runner.lastSendTime;
}
/**
* 获取最后一次发送PING消息的时间
*
* @return long
*/
public long getLastPingTime() {
return this.pingtime;
}
/**
* 显式地关闭WebSocket
*/
public final void close() {
if (this._runner != null) this._runner.closeRunner();
if (this._runner != null) this._runner.closeRunner(CLOSECODE_FORCED);
}
/**
* 是否关闭
*
* @return boolean
*/
public final boolean isClosed() {
return this._runner != null ? this._runner.closed : true;
}
@Override

View File

@@ -10,10 +10,12 @@ import java.io.*;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.*;
import java.util.function.*;
import java.util.logging.*;
import java.util.stream.*;
import org.redkale.convert.Convert;
import static org.redkale.net.http.WebSocket.RETCODE_GROUP_EMPTY;
import static org.redkale.net.http.WebSocketServlet.*;
import org.redkale.util.*;
/**
@@ -23,18 +25,18 @@ import org.redkale.util.*;
*
* @author zhangjx
*/
public final class WebSocketEngine {
public class WebSocketEngine {
//全局自增长ID
@Comment("全局自增长ID, 为了确保在一个进程里多个WebSocketEngine定时发送ping时不会同时进行")
private static final AtomicInteger sequence = new AtomicInteger();
//Engine自增长序号ID
@Comment("Engine自增长序号ID")
private final int index;
//当前WebSocket对应的Engine
@Comment("当前WebSocket对应的Engine")
private final String engineid;
//当前WebSocket对应的Node
@Comment("当前WebSocket对应的Node")
protected final WebSocketNode node;
//HttpContext
@@ -43,61 +45,77 @@ public final class WebSocketEngine {
//Convert
protected final Convert sendConvert;
protected final boolean single; //是否单用户单连接
@Comment("是否单用户单连接")
protected final boolean single;
//在线用户ID对应的WebSocket组用于单用户单连接模式
@Comment("在线用户ID对应的WebSocket组用于单用户单连接模式")
private final Map<Serializable, WebSocket> websockets = new ConcurrentHashMap<>();
//在线用户ID对应的WebSocket组用于单用户多连接模式
@Comment("在线用户ID对应的WebSocket组用于单用户多连接模式")
private final Map<Serializable, List<WebSocket>> websockets2 = new ConcurrentHashMap<>();
//用于PING的定时器
@Comment("当前连接数")
protected final AtomicInteger currconns = new AtomicInteger();
@Comment("用于PING的定时器")
private ScheduledThreadPoolExecutor scheduler;
//日志
@Comment("日志")
protected final Logger logger;
//FINEST日志级别
protected final boolean finest;
@Comment("PING的间隔秒数")
protected int liveinterval;
private int liveinterval;
@Comment("最大连接数, 为0表示无限制")
protected int wsmaxconns;
protected WebSocketEngine(String engineid, boolean single, HttpContext context, int liveinterval, WebSocketNode node, Convert sendConvert, Logger logger) {
@Comment("最大消息体长度, 小于1表示无限制")
protected int wsmaxbody;
protected WebSocketEngine(String engineid, boolean single, HttpContext context, int liveinterval,
int wsmaxconns, int wsmaxbody, WebSocketNode node, Convert sendConvert, Logger logger) {
this.engineid = engineid;
this.single = single;
this.context = context;
this.sendConvert = sendConvert;
this.node = node;
this.liveinterval = liveinterval;
this.wsmaxconns = wsmaxconns;
this.wsmaxbody = wsmaxbody;
this.logger = logger;
this.index = sequence.getAndIncrement();
this.finest = logger.isLoggable(Level.FINEST);
}
void init(AnyValue conf) {
final int interval = conf == null ? (liveinterval < 0 ? DEFAILT_LIVEINTERVAL : liveinterval) : conf.getIntValue("liveinterval", (liveinterval < 0 ? DEFAILT_LIVEINTERVAL : liveinterval));
if (interval <= 0) return;
AnyValue props = conf;
if (conf != null && conf.getAnyValue("properties") != null) props = conf.getAnyValue("properties");
this.liveinterval = props == null ? (liveinterval < 0 ? DEFAILT_LIVEINTERVAL : liveinterval) : props.getIntValue(WEBPARAM__LIVEINTERVAL, (liveinterval < 0 ? DEFAILT_LIVEINTERVAL : liveinterval));
if (liveinterval <= 0) return;
if (props != null) this.wsmaxconns = props.getIntValue(WEBPARAM__WSMAXCONNS, this.wsmaxconns);
if (props != null) this.wsmaxbody = props.getIntValue(WEBPARAM__WSMAXBODY, this.wsmaxbody);
if (scheduler != null) return;
this.scheduler = new ScheduledThreadPoolExecutor(1, (Runnable r) -> {
final Thread t = new Thread(r, engineid + "-WebSocket-LiveInterval-Thread");
t.setDaemon(true);
return t;
});
long delay = (interval - System.currentTimeMillis() / 1000 % interval) + index * 5;
final int intervalms = interval * 1000;
long delay = (liveinterval - System.currentTimeMillis() / 1000 % liveinterval) + index * 5;
final int intervalms = liveinterval * 1000;
scheduler.scheduleWithFixedDelay(() -> {
long now = System.currentTimeMillis();
getLocalWebSockets().stream().filter(x -> (now - x.getLastSendTime()) > intervalms).forEach(x -> x.sendPing());
}, delay, interval, TimeUnit.SECONDS);
if (finest) logger.finest(this.getClass().getSimpleName() + "(" + engineid + ")" + " start keeplive(delay:" + delay + ", interval:" + interval + "s) scheduler executor");
}, delay, liveinterval, TimeUnit.SECONDS);
if (logger.isLoggable(Level.FINEST)) logger.finest(this.getClass().getSimpleName() + "(" + engineid + ")" + " start keeplive(delay:" + delay + ", wsmaxconns:" + wsmaxconns + ", interval:" + liveinterval + "s) scheduler executor");
}
void destroy(AnyValue conf) {
if (scheduler != null) scheduler.shutdownNow();
}
@Comment("添加WebSocket")
void add(WebSocket socket) {
if (single) {
currconns.incrementAndGet();
websockets.put(socket._userid, socket);
} else { //非线程安全, 在常规场景中无需锁
List<WebSocket> list = websockets2.get(socket._userid);
@@ -105,19 +123,23 @@ public final class WebSocketEngine {
list = new CopyOnWriteArrayList<>();
websockets2.put(socket._userid, list);
}
currconns.incrementAndGet();
list.add(socket);
}
if (node != null) node.connect(socket._userid);
}
@Comment("从WebSocketEngine删除指定WebSocket")
void remove(WebSocket socket) {
Serializable userid = socket._userid;
if (single) {
currconns.decrementAndGet();
websockets.remove(userid);
if (node != null) node.disconnect(userid);
} else { //非线程安全, 在常规场景中无需锁
List<WebSocket> list = websockets2.get(userid);
if (list != null) {
currconns.decrementAndGet();
list.remove(socket);
if (list.isEmpty()) {
websockets2.remove(userid);
@@ -127,24 +149,75 @@ public final class WebSocketEngine {
}
}
@Comment("更改WebSocket的userid")
CompletableFuture<Void> changeUserid(WebSocket socket, final Serializable newuserid) {
if (newuserid == null) throw new NullPointerException("newuserid is null");
final Serializable olduserid = socket._userid;
socket._userid = newuserid;
if (single) {
websockets.remove(olduserid);
websockets.put(newuserid, socket);
} else { //非线程安全, 在常规场景中无需锁
List<WebSocket> oldlist = websockets2.get(olduserid);
if (oldlist != null) {
oldlist.remove(socket);
if (oldlist.isEmpty()) websockets2.remove(olduserid);
}
List<WebSocket> newlist = websockets2.get(newuserid);
if (newlist == null) {
newlist = new CopyOnWriteArrayList<>();
websockets2.put(newuserid, newlist);
}
newlist.add(socket);
}
if (node != null) return node.changeUserid(olduserid, newuserid);
return CompletableFuture.completedFuture(null);
}
@Comment("强制关闭本地用户的WebSocket")
public int forceCloseLocalWebSocket(Serializable userid) {
if (single) {
WebSocket ws = websockets.get(userid);
if (ws == null) return 0;
ws.close();
return 1;
}
List<WebSocket> list = websockets2.get(userid);
if (list == null || list.isEmpty()) return 0;
List<WebSocket> list2 = new ArrayList<>(list);
for (WebSocket ws : list2) {
ws.close();
}
return list2.size();
}
@Comment("给所有连接用户发送消息")
public CompletableFuture<Integer> broadcastMessage(final Object message, final boolean last) {
return broadcastMessage(null, message, last);
}
@Comment("给指定WebSocket连接用户发送消息")
public CompletableFuture<Integer> broadcastMessage(final Predicate<WebSocket> predicate, final Object message, final boolean last) {
if (message instanceof CompletableFuture) {
return ((CompletableFuture) message).thenCompose((json) -> broadcastMessage(json, last));
return ((CompletableFuture) message).thenCompose((json) -> broadcastMessage(predicate, json, last));
}
final boolean more = (!(message instanceof WebSocketPacket) || ((WebSocketPacket) message).sendBuffers == null);
if (more) {
//此处的WebSocketPacket只能是包含payload或bytes内容的不能包含sendConvert、sendJson、sendBuffers
final WebSocketPacket packet = (message instanceof WebSocketPacket) ? (WebSocketPacket) message
: ((message == null || message instanceof CharSequence || message instanceof byte[])
? new WebSocketPacket((Serializable) message, last) : new WebSocketPacket(this.sendConvert, message, last));
? new WebSocketPacket((Serializable) message, last) : new WebSocketPacket(this.sendConvert, false, message, last));
packet.setSendBuffers(packet.encode(context.getBufferSupplier()));
CompletableFuture<Integer> future = null;
if (single) {
for (WebSocket websocket : websockets.values()) {
if (predicate != null && !predicate.test(websocket)) continue;
future = future == null ? websocket.sendPacket(packet) : future.thenCombine(websocket.sendPacket(packet), (a, b) -> a | (Integer) b);
}
} else {
for (List<WebSocket> list : websockets2.values()) {
for (WebSocket websocket : list) {
if (predicate != null && !predicate.test(websocket)) continue;
future = future == null ? websocket.sendPacket(packet) : future.thenCombine(websocket.sendPacket(packet), (a, b) -> a | (Integer) b);
}
}
@@ -155,11 +228,13 @@ public final class WebSocketEngine {
CompletableFuture<Integer> future = null;
if (single) {
for (WebSocket websocket : websockets.values()) {
if (predicate != null && !predicate.test(websocket)) continue;
future = future == null ? websocket.send(message, last) : future.thenCombine(websocket.send(message, last), (a, b) -> a | (Integer) b);
}
} else {
for (List<WebSocket> list : websockets2.values()) {
for (WebSocket websocket : list) {
if (predicate != null && !predicate.test(websocket)) continue;
future = future == null ? websocket.send(message, last) : future.thenCombine(websocket.send(message, last), (a, b) -> a | (Integer) b);
}
}
@@ -168,15 +243,27 @@ public final class WebSocketEngine {
}
}
@Comment("给指定用户组发送消息")
public CompletableFuture<Integer> sendMessage(final Object message, final boolean last, final Stream<? extends Serializable> userids) {
Object[] array = userids.toArray();
Serializable[] ss = new Serializable[array.length];
for (int i = 0; i < array.length; i++) {
ss[i] = (Serializable) array[i];
}
return sendMessage(message, last, ss);
}
@Comment("给指定用户组发送消息")
public CompletableFuture<Integer> sendMessage(final Object message, final boolean last, final Serializable... userids) {
if (message instanceof CompletableFuture) {
return ((CompletableFuture) message).thenCompose((json) -> sendMessage(json, last, userids));
}
final boolean more = (!(message instanceof WebSocketPacket) || ((WebSocketPacket) message).sendBuffers == null) && userids.length > 1;
if (more) {
//此处的WebSocketPacket只能是包含payload或bytes内容的不能包含sendConvert、sendJson、sendBuffers
final WebSocketPacket packet = (message instanceof WebSocketPacket) ? (WebSocketPacket) message
: ((message == null || message instanceof CharSequence || message instanceof byte[])
? new WebSocketPacket((Serializable) message, last) : new WebSocketPacket(this.sendConvert, message, last));
? new WebSocketPacket((Serializable) message, last) : new WebSocketPacket(this.sendConvert, false, message, last));
packet.setSendBuffers(packet.encode(context.getBufferSupplier()));
CompletableFuture<Integer> future = null;
if (single) {
@@ -217,21 +304,54 @@ public final class WebSocketEngine {
}
}
Collection<WebSocket> getLocalWebSockets() {
@Comment("获取最大连接数")
public int getLocalWsmaxconns() {
return this.wsmaxconns;
}
@Comment("连接数是否达到上限")
public boolean isLocalConnLimited() {
if (this.wsmaxconns < 1) return false;
return currconns.get() >= this.wsmaxconns;
}
@Comment("获取所有连接")
public Collection<WebSocket> getLocalWebSockets() {
if (single) return websockets.values();
List<WebSocket> list = new ArrayList<>();
websockets2.values().forEach(x -> list.addAll(x));
return list;
}
//适用于单用户单连接模式
@Comment("获取所有连接")
public void forEachLocalWebSocket(Consumer<WebSocket> consumer) {
if (consumer == null) return;
if (single) {
websockets.values().stream().forEach(consumer);
} else {
websockets2.values().forEach(x -> x.stream().forEach(consumer));
}
}
@Comment("获取当前连接总数")
public int getLocalWebSocketSize() {
if (single) return websockets.size();
return (int) websockets2.values().stream().mapToInt(sublist -> sublist.size()).count();
}
@Comment("获取当前用户总数")
public int getLocalUserSize() {
return single ? websockets.size() : websockets2.size();
}
@Comment("适用于单用户单连接模式")
public WebSocket findLocalWebSocket(Serializable userid) {
if (single) return websockets.get(userid);
List<WebSocket> list = websockets2.get(userid);
return (list == null || list.isEmpty()) ? null : list.get(list.size() - 1);
}
//适用于单用户多连接模式
@Comment("适用于单用户多连接模式")
public Stream<WebSocket> getLocalWebSockets(Serializable userid) {
if (single) {
WebSocket websocket = websockets.get(userid);

View File

@@ -11,8 +11,11 @@ import java.net.*;
import java.util.*;
import java.util.concurrent.*;
import java.util.logging.*;
import java.util.stream.Stream;
import javax.annotation.*;
import org.redkale.boot.*;
import org.redkale.convert.*;
import org.redkale.convert.json.JsonConvert;
import org.redkale.service.*;
import org.redkale.source.*;
import org.redkale.util.*;
@@ -26,9 +29,16 @@ import org.redkale.util.*;
*/
public abstract class WebSocketNode {
protected final Logger logger = Logger.getLogger(this.getClass().getSimpleName());
@Comment("存储用户ID的key前缀")
public static final String SOURCE_SNCP_USERID_PREFIX = "sncpws_uid:";
protected final boolean finest = logger.isLoggable(Level.FINEST);
@Comment("存储用户数的key")
public static final String SOURCE_SNCP_USERCOUNT_KEY = "sncpws_usercount";
@Comment("存储当前SNCP节点列表的key")
public static final String SOURCE_SNCP_ADDRS_KEY = "sncpws_addrs";
protected final Logger logger = Logger.getLogger(this.getClass().getSimpleName());
//"SNCP_ADDR" 如果不是分布式(没有SNCP) 值为null
@Resource(name = Application.RESNAME_SNCP_ADDR)
@@ -38,16 +48,20 @@ public abstract class WebSocketNode {
@RpcRemote
protected WebSocketNode remoteNode;
@Resource(name = "$_textconvert")
protected Convert textConvert;
//存放所有用户分布在节点上的队列信息,Set<InetSocketAddress> 为 sncpnode 的集合, key: groupid
//集合包含 localSncpAddress
//如果不是分布式(没有SNCP)sncpNodeAddresses 将不会被用到
@Resource(name = "$_nodes")
protected CacheSource<Serializable, InetSocketAddress> sncpNodeAddresses;
@Resource(name = "$")
protected CacheSource<InetSocketAddress> sncpNodeAddresses;
//当前节点的本地WebSocketEngine
protected WebSocketEngine localEngine;
public void init(AnyValue conf) {
if (sncpNodeAddresses != null) sncpNodeAddresses.initValueType(InetSocketAddress.class);
}
public void destroy(AnyValue conf) {
@@ -57,7 +71,9 @@ public abstract class WebSocketNode {
if (this.localEngine == null) return;
//关掉所有本地本地WebSocket
this.localEngine.getLocalWebSockets().forEach(g -> disconnect(g.getUserid()));
if (sncpNodeAddresses != null && localSncpAddress != null) sncpNodeAddresses.removeSetItem("redkale_sncpnodes", localSncpAddress);
if (sncpNodeAddresses != null && localSncpAddress != null) {
sncpNodeAddresses.removeSetItem(SOURCE_SNCP_ADDRS_KEY, localSncpAddress);
}
}
protected abstract CompletableFuture<List<String>> getWebSocketAddresses(@RpcTargetAddress InetSocketAddress targetAddress, Serializable userid);
@@ -70,17 +86,26 @@ public abstract class WebSocketNode {
protected abstract CompletableFuture<Void> disconnect(Serializable userid, InetSocketAddress addr);
protected abstract CompletableFuture<Void> changeUserid(Serializable fromuserid, Serializable touserid, InetSocketAddress addr);
protected abstract CompletableFuture<Integer> forceCloseWebSocket(Serializable userid, InetSocketAddress addr);
//--------------------------------------------------------------------------------
final CompletableFuture<Void> connect(final Serializable userid) {
if (finest) logger.finest(localSncpAddress + " receive websocket connect event (" + userid + " on " + this.localEngine.getEngineid() + ").");
if (logger.isLoggable(Level.FINEST)) logger.finest(localSncpAddress + " receive websocket connect event (" + userid + " on " + (this.localEngine == null ? null : this.localEngine.getEngineid()) + ").");
return connect(userid, localSncpAddress);
}
final CompletableFuture<Void> disconnect(final Serializable userid) {
if (finest) logger.finest(localSncpAddress + " receive websocket disconnect event (" + userid + " on " + this.localEngine.getEngineid() + ").");
if (logger.isLoggable(Level.FINEST)) logger.finest(localSncpAddress + " receive websocket disconnect event (" + userid + " on " + (this.localEngine == null ? null : this.localEngine.getEngineid()) + ").");
return disconnect(userid, localSncpAddress);
}
final CompletableFuture<Void> changeUserid(Serializable olduserid, final Serializable newuserid) {
if (logger.isLoggable(Level.FINEST)) logger.finest(localSncpAddress + " receive websocket changeUserid event (from " + olduserid + " to " + newuserid + " on " + (this.localEngine == null ? null : this.localEngine.getEngineid()) + ").");
return changeUserid(olduserid, newuserid, localSncpAddress);
}
//--------------------------------------------------------------------------------
/**
* 获取目标地址 <br>
@@ -110,7 +135,7 @@ public abstract class WebSocketNode {
* @return 地址列表
*/
public CompletableFuture<Collection<InetSocketAddress>> getRpcNodeAddresses(final Serializable userid) {
if (this.sncpNodeAddresses != null) return this.sncpNodeAddresses.getCollectionAsync(userid);
if (this.sncpNodeAddresses != null) return this.sncpNodeAddresses.getCollectionAsync(SOURCE_SNCP_USERID_PREFIX + userid);
List<InetSocketAddress> rs = new ArrayList<>();
rs.add(this.localSncpAddress);
return CompletableFuture.completedFuture(rs);
@@ -128,7 +153,7 @@ public abstract class WebSocketNode {
public CompletableFuture<Map<InetSocketAddress, List<String>>> getRpcNodeWebSocketAddresses(final Serializable userid) {
CompletableFuture<Collection<InetSocketAddress>> sncpFuture = getRpcNodeAddresses(userid);
return sncpFuture.thenCompose((Collection<InetSocketAddress> addrs) -> {
if (finest) logger.finest("websocket found userid:" + userid + " on " + addrs);
if (logger.isLoggable(Level.FINEST)) logger.finest("websocket found userid:" + userid + " on " + addrs);
if (addrs == null || addrs.isEmpty()) return CompletableFuture.completedFuture(new HashMap<>());
CompletableFuture<Map<InetSocketAddress, List<String>>> future = null;
for (final InetSocketAddress nodeAddress : addrs) {
@@ -140,9 +165,141 @@ public abstract class WebSocketNode {
});
}
/**
* 判断指定用户是否WebSocket在线
*
* @param userid Serializable
*
* @return boolean
*/
public CompletableFuture<Boolean> existsWebSocket(final Serializable userid) {
if (this.localEngine != null && this.sncpNodeAddresses == null) {
return CompletableFuture.completedFuture(this.localEngine.existsLocalWebSocket(userid));
}
return this.sncpNodeAddresses.existsAsync(SOURCE_SNCP_USERID_PREFIX + userid);
}
/**
* 获取在线用户总数
*
*
* @return boolean
*/
public CompletableFuture<Integer> getUserSize() {
if (this.localEngine != null && this.sncpNodeAddresses == null) {
return CompletableFuture.completedFuture(this.localEngine.getLocalUserSize());
}
return this.sncpNodeAddresses.getLongAsync(SOURCE_SNCP_USERCOUNT_KEY, 0L).thenApply(v -> v.intValue());
}
/**
* 强制关闭用户WebSocket
*
* @param userid Serializable
*
* @return int
*/
public final CompletableFuture<Integer> forceCloseWebSocket(final Serializable userid) {
CompletableFuture<Integer> localFuture = null;
if (this.localEngine != null) localFuture = CompletableFuture.completedFuture(localEngine.forceCloseLocalWebSocket(userid));
if (this.sncpNodeAddresses == null || this.remoteNode == null) {
if (logger.isLoggable(Level.FINEST)) logger.finest("websocket remote node is null");
//没有CacheSource就不会有分布式节点
return localFuture;
}
//远程节点关闭
CompletableFuture<Collection<InetSocketAddress>> addrsFuture = sncpNodeAddresses.getCollectionAsync(SOURCE_SNCP_USERID_PREFIX + userid);
CompletableFuture<Integer> remoteFuture = addrsFuture.thenCompose((Collection<InetSocketAddress> addrs) -> {
if (logger.isLoggable(Level.FINEST)) logger.finest("websocket found userid:" + userid + " on " + addrs);
if (addrs == null || addrs.isEmpty()) return CompletableFuture.completedFuture(0);
CompletableFuture<Integer> future = null;
for (InetSocketAddress addr : addrs) {
if (addr == null || addr.equals(localSncpAddress)) continue;
future = future == null ? remoteNode.forceCloseWebSocket(userid, addr)
: future.thenCombine(remoteNode.forceCloseWebSocket(userid, addr), (a, b) -> a + b);
}
return future == null ? CompletableFuture.completedFuture(0) : future;
});
return localFuture.thenCombine(remoteFuture, (a, b) -> a + b);
}
//--------------------------------------------------------------------------------
/**
* 获取本地的WebSocketEngine没有则返回null
*
*
* @return WebSocketEngine
*/
public final WebSocketEngine getLocalWebSocketEngine() {
return this.localEngine;
}
/**
* 向指定用户发送消息,先发送本地连接,再发送远程连接 <br>
* 如果当前WebSocketNode是远程模式此方法只发送远程连接
*
* @param message 消息内容
* @param userids Stream
*
* @return 为0表示成功 其他值表示部分发送异常
*/
public final CompletableFuture<Integer> sendMessage(Object message, final Stream<? extends Serializable> userids) {
return sendMessage((Convert) null, message, true, userids);
}
/**
* 向指定用户发送消息,先发送本地连接,再发送远程连接 <br>
* 如果当前WebSocketNode是远程模式此方法只发送远程连接
*
* @param message 消息内容
* @param userids Serializable[]
*
* @return 为0表示成功 其他值表示部分发送异常
*/
public final CompletableFuture<Integer> sendMessage(Object message, final Serializable... userids) {
return sendMessage(message, true, userids);
return sendMessage((Convert) null, message, true, userids);
}
/**
* 向指定用户发送消息,先发送本地连接,再发送远程连接 <br>
* 如果当前WebSocketNode是远程模式此方法只发送远程连接
*
* @param convert Convert
* @param message 消息内容
* @param userids Stream
*
* @return 为0表示成功 其他值表示部分发送异常
*/
public final CompletableFuture<Integer> sendMessage(final Convert convert, Object message, final Stream<? extends Serializable> userids) {
return sendMessage(convert, message, true, userids);
}
/**
* 向指定用户发送消息,先发送本地连接,再发送远程连接 <br>
* 如果当前WebSocketNode是远程模式此方法只发送远程连接
*
* @param convert Convert
* @param message 消息内容
* @param userids Serializable[]
*
* @return 为0表示成功 其他值表示部分发送异常
*/
public final CompletableFuture<Integer> sendMessage(final Convert convert, Object message, final Serializable... userids) {
return sendMessage(convert, message, true, userids);
}
/**
* 向指定用户发送消息,先发送本地连接,再发送远程连接 <br>
* 如果当前WebSocketNode是远程模式此方法只发送远程连接
*
* @param message 消息内容
* @param last 是否最后一条
* @param userids Stream
*
* @return 为0表示成功 其他值表示部分发送异常
*/
public final CompletableFuture<Integer> sendMessage(final Object message, final boolean last, final Stream<? extends Serializable> userids) {
return sendMessage((Convert) null, message, last, userids);
}
/**
@@ -155,9 +312,44 @@ public abstract class WebSocketNode {
*
* @return 为0表示成功 其他值表示部分发送异常
*/
//最近连接发送逻辑还没有理清楚
public final CompletableFuture<Integer> sendMessage(final Object message, final boolean last, final Serializable... userids) {
return sendMessage((Convert) null, message, last, userids);
}
/**
* 向指定用户发送消息,先发送本地连接,再发送远程连接 <br>
* 如果当前WebSocketNode是远程模式此方法只发送远程连接
*
* @param convert Convert
* @param message0 消息内容
* @param last 是否最后一条
* @param userids Stream
*
* @return 为0表示成功 其他值表示部分发送异常
*/
public final CompletableFuture<Integer> sendMessage(final Convert convert, final Object message0, final boolean last, final Stream<? extends Serializable> userids) {
Object[] array = userids.toArray();
Serializable[] ss = new Serializable[array.length];
for (int i = 0; i < array.length; i++) {
ss[i] = (Serializable) array[i];
}
return sendMessage(convert, message0, last, ss);
}
/**
* 向指定用户发送消息,先发送本地连接,再发送远程连接 <br>
* 如果当前WebSocketNode是远程模式此方法只发送远程连接
*
* @param convert Convert
* @param message0 消息内容
* @param last 是否最后一条
* @param userids Serializable[]
*
* @return 为0表示成功 其他值表示部分发送异常
*/
public final CompletableFuture<Integer> sendMessage(final Convert convert, final Object message0, final boolean last, final Serializable... userids) {
if (userids == null || userids.length < 1) return CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY);
final Object message = (convert == null || message0 instanceof WebSocketPacket) ? message0 : ((convert instanceof TextConvert) ? new WebSocketPacket(((TextConvert) convert).convertTo(message0), last) : new WebSocketPacket(((BinaryConvert) convert).convertTo(message0), last));
if (this.localEngine != null && this.sncpNodeAddresses == null) { //本地模式且没有分布式
return this.localEngine.sendMessage(message, last, userids);
}
@@ -177,7 +369,19 @@ public abstract class WebSocketNode {
* @return 为0表示成功 其他值表示部分发送异常
*/
public final CompletableFuture<Integer> broadcastMessage(final Object message) {
return broadcastMessage(message, true);
return broadcastMessage((Convert) null, message, true);
}
/**
* 广播消息, 给所有人发消息
*
* @param convert Convert
* @param message 消息内容
*
* @return 为0表示成功 其他值表示部分发送异常
*/
public final CompletableFuture<Integer> broadcastMessage(final Convert convert, final Object message) {
return broadcastMessage(convert, message, true);
}
/**
@@ -189,19 +393,34 @@ public abstract class WebSocketNode {
* @return 为0表示成功 其他值表示部分发送异常
*/
public final CompletableFuture<Integer> broadcastMessage(final Object message, final boolean last) {
return broadcastMessage((Convert) null, message, last);
}
/**
* 广播消息, 给所有人发消息
*
* @param convert Convert
* @param message0 消息内容
* @param last 是否最后一条
*
* @return 为0表示成功 其他值表示部分发送异常
*/
public final CompletableFuture<Integer> broadcastMessage(final Convert convert, final Object message0, final boolean last) {
final Object message = (convert == null || message0 instanceof WebSocketPacket) ? message0 : ((convert instanceof TextConvert) ? new WebSocketPacket(((TextConvert) convert).convertTo(message0), last) : new WebSocketPacket(((BinaryConvert) convert).convertTo(message0), last));
if (this.localEngine != null && this.sncpNodeAddresses == null) { //本地模式且没有分布式
return this.localEngine.broadcastMessage(message, last);
}
CompletableFuture<Integer> localFuture = this.localEngine == null ? null : this.localEngine.broadcastMessage(message, last);
CompletableFuture<Collection<InetSocketAddress>> addrsFuture = sncpNodeAddresses.getCollectionAsync("redkale_sncpnodes");
CompletableFuture<Collection<InetSocketAddress>> addrsFuture = sncpNodeAddresses.getCollectionAsync(SOURCE_SNCP_ADDRS_KEY);
CompletableFuture<Integer> remoteFuture = addrsFuture.thenCompose((Collection<InetSocketAddress> addrs) -> {
if (finest) logger.finest("websocket broadcast message on " + addrs);
if (logger.isLoggable(Level.FINEST)) logger.finest("websocket broadcast message on " + addrs);
if (addrs == null || addrs.isEmpty()) return CompletableFuture.completedFuture(0);
CompletableFuture<Integer> future = null;
Object remoteMessage = formatRemoteMessage(message);
for (InetSocketAddress addr : addrs) {
if (addr == null || addr.equals(localSncpAddress)) continue;
future = future == null ? remoteNode.broadcastMessage(addr, message, last)
: future.thenCombine(remoteNode.broadcastMessage(addr, message, last), (a, b) -> a | b);
future = future == null ? remoteNode.broadcastMessage(addr, remoteMessage, last)
: future.thenCombine(remoteNode.broadcastMessage(addr, remoteMessage, last), (a, b) -> a | b);
}
return future == null ? CompletableFuture.completedFuture(0) : future;
});
@@ -209,27 +428,40 @@ public abstract class WebSocketNode {
}
private CompletableFuture<Integer> sendOneMessage(final Object message, final boolean last, final Serializable userid) {
if (finest) logger.finest("websocket want send message {userid:" + userid + ", content:'" + message + "'} from locale node to locale engine");
if (message instanceof CompletableFuture) return ((CompletableFuture) message).thenApply(msg -> sendOneMessage(msg, last, userid));
if (logger.isLoggable(Level.FINEST)) logger.finest("websocket want send message {userid:" + userid + ", content:'" + JsonConvert.root().convertTo(message) + "'} from locale node to " + ((this.localEngine != null) ? "locale" : "remote") + " engine");
CompletableFuture<Integer> localFuture = null;
if (this.localEngine != null) localFuture = localEngine.sendMessage(message, last, userid);
if (this.sncpNodeAddresses == null || this.remoteNode == null) {
if (finest) logger.finest("websocket remote node is null");
if (logger.isLoggable(Level.FINEST)) logger.finest("websocket remote node is null");
//没有CacheSource就不会有分布式节点
return localFuture == null ? CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY) : localFuture;
}
//远程节点发送消息
CompletableFuture<Collection<InetSocketAddress>> addrsFuture = sncpNodeAddresses.getCollectionAsync(userid);
CompletableFuture<Collection<InetSocketAddress>> addrsFuture = sncpNodeAddresses.getCollectionAsync(SOURCE_SNCP_USERID_PREFIX + userid);
CompletableFuture<Integer> remoteFuture = addrsFuture.thenCompose((Collection<InetSocketAddress> addrs) -> {
if (finest) logger.finest("websocket found userid:" + userid + " on " + addrs);
if (addrs == null || addrs.isEmpty()) return CompletableFuture.completedFuture(0);
if (addrs == null || addrs.isEmpty()) {
if (logger.isLoggable(Level.FINER)) logger.finer("websocket not found userid:" + userid + " on any node ");
return CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY);
}
if (logger.isLoggable(Level.FINEST)) logger.finest("websocket(localaddr=" + localSncpAddress + ") found userid:" + userid + " on " + addrs);
CompletableFuture<Integer> future = null;
Object remoteMessage = formatRemoteMessage(message);
for (InetSocketAddress addr : addrs) {
if (addr == null || addr.equals(localSncpAddress)) continue;
future = future == null ? remoteNode.sendMessage(addr, message, last, userid)
: future.thenCombine(remoteNode.sendMessage(addr, message, last, userid), (a, b) -> a | b);
future = future == null ? remoteNode.sendMessage(addr, remoteMessage, last, userid)
: future.thenCombine(remoteNode.sendMessage(addr, remoteMessage, last, userid), (a, b) -> a | b);
}
return future == null ? CompletableFuture.completedFuture(0) : future;
return future == null ? CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY) : future;
});
return localFuture == null ? remoteFuture : localFuture.thenCombine(remoteFuture, (a, b) -> a | b);
}
private Object formatRemoteMessage(Object message) {
if (message instanceof WebSocketPacket) return message;
if (message instanceof byte[]) return message;
if (message instanceof CharSequence) return message;
if (textConvert != null) return ((TextConvert) textConvert).convertTo(message);
return JsonConvert.root().convertTo(message);
}
}

View File

@@ -8,6 +8,8 @@ package org.redkale.net.http;
import org.redkale.util.Utility;
import java.io.*;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.util.AbstractMap;
import java.util.function.Supplier;
import java.util.logging.*;
import org.redkale.convert.*;
@@ -21,8 +23,16 @@ import org.redkale.convert.*;
*/
public final class WebSocketPacket {
private static final Charset UTF_8 = Charset.forName("UTF-8");
static final WebSocketPacket NONE = new WebSocketPacket();
public static final WebSocketPacket DEFAULT_PING_PACKET = new WebSocketPacket(FrameType.PING, new byte[0]);
public static enum MessageType {
STRING, BYTES, OBJECT;
}
public static enum FrameType {
TEXT(0x01), BINARY(0x02), CLOSE(0x08), PING(0x09), PONG(0x0A);
@@ -57,12 +67,24 @@ public final class WebSocketPacket {
protected boolean last = true;
protected Object sendJson;
//---------------发送------------------------
Object sendJson;
Convert sendConvert;
boolean sendMapconvable;
ByteBuffer[] sendBuffers;
//---------------接收------------------------
MessageType receiveType;
int receiveCount;
int receiveLength;
Object receiveMessage;
ConvertMask receiveMasker;
ByteBuffer[] receiveBuffers;
@@ -74,49 +96,12 @@ public final class WebSocketPacket {
this(payload, true);
}
public WebSocketPacket(Serializable message, boolean fin) {
boolean bin = message != null && message.getClass() == byte[].class;
if (bin) {
this.type = FrameType.BINARY;
this.bytes = (byte[]) message;
} else {
this.type = FrameType.TEXT;
this.payload = String.valueOf(message);
}
this.last = fin;
}
public WebSocketPacket(String payload, boolean fin) {
this.type = FrameType.TEXT;
this.payload = payload;
this.last = fin;
}
public WebSocketPacket(Convert convert, Object json, boolean fin) {
this.type = (convert == null || !convert.isBinary()) ? FrameType.TEXT : FrameType.BINARY;
this.sendConvert = convert;
this.sendJson = json;
this.last = fin;
}
WebSocketPacket(ByteBuffer[] sendBuffers, FrameType type, boolean fin) {
this.type = type;
this.last = fin;
this.setSendBuffers(sendBuffers);
}
void setSendBuffers(ByteBuffer[] sendBuffers) {
this.sendBuffers = sendBuffers;
}
ByteBuffer[] duplicateSendBuffers() {
ByteBuffer[] rs = new ByteBuffer[this.sendBuffers.length];
for (int i = 0; i < this.sendBuffers.length; i++) {
rs[i] = this.sendBuffers[i].duplicate();
}
return rs;
}
public WebSocketPacket(byte[] data) {
this(FrameType.BINARY, data, true);
}
@@ -139,7 +124,46 @@ public final class WebSocketPacket {
this.last = fin;
}
public byte[] getContent() {
public WebSocketPacket(Serializable message, boolean fin) {
boolean bin = message != null && message.getClass() == byte[].class;
if (bin) {
this.type = FrameType.BINARY;
this.bytes = (byte[]) message;
} else {
this.type = FrameType.TEXT;
this.payload = String.valueOf(message);
}
this.last = fin;
}
WebSocketPacket(Convert convert, boolean mapconvable, Object json, boolean fin) {
this.type = (convert == null || !convert.isBinary()) ? FrameType.TEXT : FrameType.BINARY;
this.sendConvert = convert;
this.sendMapconvable = mapconvable;
this.sendJson = json;
this.last = fin;
if (mapconvable && !(json instanceof Object[])) throw new IllegalArgumentException();
}
WebSocketPacket(ByteBuffer[] sendBuffers, FrameType type, boolean fin) {
this.type = type;
this.last = fin;
this.setSendBuffers(sendBuffers);
}
void setSendBuffers(ByteBuffer[] sendBuffers) {
this.sendBuffers = sendBuffers;
}
ByteBuffer[] duplicateSendBuffers() {
ByteBuffer[] rs = new ByteBuffer[this.sendBuffers.length];
for (int i = 0; i < this.sendBuffers.length; i++) {
rs[i] = this.sendBuffers[i].duplicate().asReadOnlyBuffer(); //必须使用asReadOnlyBuffer 否则会导致ByteBuffer对应的byte[]被ObjectPool回收两次
}
return rs;
}
public byte[] content() {
if (this.type == FrameType.TEXT) return Utility.encodeUTF8(getPayload());
if (this.bytes == null) return new byte[0];
return this.bytes;
@@ -157,9 +181,29 @@ public final class WebSocketPacket {
return last;
}
public FrameType getType() {
return type;
}
public void setType(FrameType type) {
this.type = type;
}
public void setPayload(String payload) {
this.payload = payload;
}
public void setBytes(byte[] bytes) {
this.bytes = bytes;
}
public void setLast(boolean last) {
this.last = last;
}
@Override
public String toString() {
return this.getClass().getSimpleName() + "[type=" + type + ", last=" + last + (payload != null ? (", payload=" + payload) : "") + (bytes != null ? (", bytes=[" + bytes.length + ']') : "") + (sendJson != null ? (", json=" + sendJson) : "") + "]";
return this.getClass().getSimpleName() + "[type=" + type + ", last=" + last + (payload != null ? (", payload=" + payload) : "") + (bytes != null ? (", bytes=[" + bytes.length + ']') : (receiveLength > 0 ? (", receivemsg=" + receiveMessage) : "")) + (sendJson != null ? (", json=" + (sendMapconvable ? Utility.ofMap((Object[]) sendJson) : sendJson)) : "") + "]";
}
/**
@@ -187,7 +231,7 @@ public final class WebSocketPacket {
return supplier.get();
}
};
ByteBuffer[] buffers = this.sendConvert.convertTo(newsupplier, sendJson);
ByteBuffer[] buffers = this.sendMapconvable ? this.sendConvert.convertMapTo(newsupplier, (Object[]) sendJson) : this.sendConvert.convertTo(newsupplier, sendJson);
int len = 0;
for (ByteBuffer buf : buffers) {
len += buf.remaining();
@@ -212,7 +256,7 @@ public final class WebSocketPacket {
}
ByteBuffer buffer = supplier.get(); //确保ByteBuffer的capacity不能小于128
final byte[] content = getContent();
final byte[] content = content();
final int len = content.length;
if (len <= 0x7D) { //125
buffer.put(opcode);
@@ -266,6 +310,21 @@ public final class WebSocketPacket {
// String rs = JsonConvert.root().convertFrom(String.class, masker, buffer);
// System.out.println(rs);
// }
/**
*
* @param webSocket WebSocket
* @param readBuffer ByteBuffer
*
* @return boolean 已接收完返回true 需要继续接收body返回false
*/
boolean receiveBody(WebSocket webSocket, ByteBuffer readBuffer) {
int need = receiveLength - receiveCount;
boolean over = readBuffer.remaining() >= need;
this.receiveBuffers = Utility.append(this.receiveBuffers, readBuffer);
if (over) parseReceiveMessage(webSocket, this.receiveBuffers);
return over;
}
/**
* 消息解码 <br>
*
@@ -291,26 +350,25 @@ public final class WebSocketPacket {
* @param buffer
* @param exbuffers
*
* @return
* @return 返回NONE表示Buffer内容不够 返回this表示解析完成或部分解析完成返回null表示解析异常
*/
WebSocketPacket decode(final Logger logger, final ByteBuffer buffer, ByteBuffer... exbuffers) {
WebSocketPacket decode(final Logger logger, final WebSocket webSocket, final int wsmaxbody,
final AbstractMap.SimpleEntry<String, byte[]> halfBytes, final ByteBuffer buffer) {
//开始
final boolean debug = false; //调试开关
if (debug) {
int remain = buffer.remaining();
if (exbuffers != null) {
for (ByteBuffer b : exbuffers) {
remain += b == null ? 0 : b.remaining();
}
}
logger.log(Level.FINEST, "read websocket message's length = " + remain);
if (debug) logger.log(Level.FINEST, "read websocket message's length = " + buffer.remaining());
if (!buffer.hasRemaining()) return NONE;
if (buffer.remaining() < 2) {
byte[] bs = new byte[buffer.remaining()];
buffer.get(bs);
halfBytes.setValue(bs);
return NONE;
}
if (buffer.remaining() < 2) return null;
byte opcode = buffer.get();
final byte opcode = buffer.get(); //第一个字节
this.last = (opcode & 0b1000_0000) != 0;
this.type = FrameType.valueOf(opcode & 0xF);
if (type == FrameType.CLOSE) {
if (debug) logger.log(Level.FINEST, " receive close command from websocket client");
return this;
}
final boolean checkrsv = false;//暂时不校验
if (checkrsv && (opcode & 0b0111_0000) != 0) {
@@ -326,9 +384,23 @@ public final class WebSocketPacket {
//0xA 表示一个pong
//0x0B-0F 为以后的控制帧保留
final boolean control = (opcode & 0b0000_1000) != 0; //是否控制帧
byte lengthCode = buffer.get();
final byte crcode = buffer.get(); //第二个字节
byte lengthCode = crcode;
final boolean masked = (lengthCode & 0x80) == 0x80;
if (masked) lengthCode ^= 0x80; //mask
//判断Buffer剩余内容够不够基本信息的创建
int minBufferLength = ((lengthCode <= 0x7D) ? 0 : (lengthCode == 0x7E ? 2 : 4)) + (masked ? 4 : 0);
if (buffer.remaining() < minBufferLength) {
byte[] bs = new byte[2 + buffer.remaining()];
bs[0] = opcode;
bs[1] = crcode;
buffer.get(bs, 2, buffer.remaining());
halfBytes.setValue(bs);
return NONE;
}
int length;
if (lengthCode <= 0x7D) { //125
length = lengthCode;
@@ -343,6 +415,11 @@ public final class WebSocketPacket {
length = buffer.getInt();
}
}
if (length > wsmaxbody && wsmaxbody > 0) {
if (debug) logger.log(Level.FINE, "message length (" + length + ") too big, must less " + wsmaxbody + "");
return null;
}
this.receiveLength = length;
if (masked) {
final byte[] masks = new byte[4];
buffer.get(masks);
@@ -356,25 +433,65 @@ public final class WebSocketPacket {
}
};
}
this.receiveBuffers = Utility.append(new ByteBuffer[]{buffer}, exbuffers);
if (buffer.remaining() >= this.receiveLength) { //内容足够, 可以解析
this.parseReceiveMessage(webSocket, buffer);
this.receiveCount = this.receiveLength;
} else {
this.receiveCount = buffer.remaining();
this.receiveBuffers = buffer.hasRemaining() ? new ByteBuffer[]{buffer} : null;
}
return this;
}
byte[] getReceiveBytes() {
if (this.receiveBuffers.length == 0) return new byte[0];
if (this.receiveBuffers.length == 1 && this.receiveBuffers[0].remaining() == 0) return new byte[0];
int count = 0;
for (ByteBuffer buf : this.receiveBuffers) {
count += buf.remaining();
void parseReceiveMessage(WebSocket webSocket, ByteBuffer... buffers) {
if (this.type == FrameType.TEXT) {
Convert textConvert = webSocket.getTextConvert();
if (textConvert == null) {
this.receiveMessage = new String(this.getReceiveBytes(buffers), UTF_8);
this.receiveType = MessageType.STRING;
} else {
this.receiveMessage = textConvert.convertFrom(webSocket._messageTextType, this.receiveMasker, buffers);
this.receiveCount = this.receiveLength;
this.receiveType = MessageType.OBJECT;
}
} else if (this.type == FrameType.BINARY) {
Convert binaryConvert = webSocket.getBinaryConvert();
if (binaryConvert == null) {
this.receiveMessage = this.getReceiveBytes(buffers);
this.receiveType = MessageType.BYTES;
} else {
this.receiveMessage = binaryConvert.convertFrom(webSocket._messageTextType, this.receiveMasker, buffers);
this.receiveCount = this.receiveLength;
this.receiveType = MessageType.OBJECT;
}
} else if (this.type == FrameType.PING) {
this.receiveMessage = this.getReceiveBytes(buffers);
this.receiveType = MessageType.BYTES;
} else if (this.type == FrameType.PONG) {
this.receiveMessage = this.getReceiveBytes(buffers);
this.receiveType = MessageType.BYTES;
} else if (this.type == FrameType.CLOSE) {
this.receiveMessage = this.getReceiveBytes(buffers);
this.receiveType = MessageType.BYTES;
}
byte[] bs = new byte[count];
}
boolean isReceiveFinished() {
return this.receiveLength <= this.receiveCount;
}
byte[] getReceiveBytes(ByteBuffer... buffers) {
final int length = this.receiveLength;
if (length == 0) return new byte[0];
byte[] bs = new byte[length];
int index = 0;
for (ByteBuffer buf : this.receiveBuffers) {
int r = buf.remaining();
for (ByteBuffer buf : buffers) {
int r = Math.min(buf.remaining(), length - index);
buf.get(bs, index, r);
index += r;
if (index >= length) break;
}
this.receiveCount = index;
ConvertMask mask = this.receiveMasker;
if (mask != null) {
for (int i = 0; i < bs.length; i++) {
@@ -383,4 +500,5 @@ public final class WebSocketPacket {
}
return bs;
}
}

View File

@@ -12,11 +12,11 @@ import org.redkale.net.http.WebSocketPacket.FrameType;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.*;
import java.util.AbstractMap.SimpleEntry;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.BiConsumer;
import java.util.logging.*;
import org.redkale.convert.Convert;
/**
* WebSocket的消息接收发送器, 一个WebSocket对应一个WebSocketRunner
@@ -38,7 +38,7 @@ class WebSocketRunner implements Runnable {
private ByteBuffer readBuffer;
protected volatile boolean closed = false;
volatile boolean closed = false;
private AtomicBoolean writing = new AtomicBoolean();
@@ -64,162 +64,286 @@ class WebSocketRunner implements Runnable {
webSocket.onConnected();
channel.setReadTimeoutSecond(300); //读取超时5分钟
if (channel.isOpen()) {
final int wsmaxbody = webSocket._engine.wsmaxbody;
channel.read(readBuffer, null, new CompletionHandler<Integer, Void>() {
private ByteBuffer recentExBuffer;
//尚未解析完的数据包
private WebSocketPacket unfinishPacket;
//当接收的数据流长度大于ByteBuffer长度时 则需要额外的ByteBuffer 辅助;
private final List<ByteBuffer> readBuffers = new ArrayList<>();
private final List<ByteBuffer> exBuffers = new ArrayList<>();
private final SimpleEntry<String, byte[]> halfBytes = new SimpleEntry("", null);
@Override
public void completed(Integer count, Void attachment1) {
if (count < 1 && readBuffers.isEmpty()) {
closeRunner();
if (count < 1) {
closeRunner(0);
if (debug) context.getLogger().log(Level.FINEST, "WebSocketRunner abort on read buffer count, force to close channel, live " + (System.currentTimeMillis() - webSocket.getCreatetime()) / 1000 + " seconds");
return;
}
if (readBuffer == null) return;
if (!readBuffer.hasRemaining() && (recentExBuffer == null || !recentExBuffer.hasRemaining())) {
final ByteBuffer buffer = context.pollBuffer();
recentExBuffer = buffer;
readBuffers.add(buffer);
channel.read(buffer, null, this);
return;
}
readBuffer.flip();
ByteBuffer[] exBuffers = null;
if (!readBuffers.isEmpty()) {
exBuffers = readBuffers.toArray(new ByteBuffer[readBuffers.size()]);
readBuffers.clear();
recentExBuffer = null;
for (ByteBuffer b : exBuffers) {
b.flip();
WebSocketPacket onePacket = null;
if (unfinishPacket != null) {
if (unfinishPacket.receiveBody(webSocket, readBuffer)) { //已经接收完毕
onePacket = unfinishPacket;
unfinishPacket = null;
for (ByteBuffer b : exBuffers) {
context.offerBuffer(b);
}
exBuffers.clear();
} else { //需要继续接收
readBuffer = context.pollBuffer();
channel.read(readBuffer, null, this);
return;
}
}
final List<WebSocketPacket> packets = new ArrayList<>();
if (onePacket != null) packets.add(onePacket);
try {
WebSocketPacket packet = new WebSocketPacket().decode(context.getLogger(), readBuffer, exBuffers);
while (true) {
WebSocketPacket packet = new WebSocketPacket().decode(context.getLogger(), webSocket, wsmaxbody, halfBytes, readBuffer);
if (packet == WebSocketPacket.NONE) break; //解析完毕但是buffer有多余字节
if (packet != null && !packet.isReceiveFinished()) {
unfinishPacket = packet;
if (readBuffer.hasRemaining()) {
exBuffers.add(readBuffer);
readBuffer = context.pollBuffer();
}
break;
}
packets.add(packet);
if (packet == null || !readBuffer.hasRemaining()) break;
}
} catch (Exception e) {
context.getLogger().log(Level.SEVERE, "WebSocket parse message error", e);
webSocket.onOccurException(e, null);
}
//继续监听消息
readBuffer.clear();
if (halfBytes.getValue() != null) {
readBuffer.put(halfBytes.getValue());
halfBytes.setValue(null);
}
channel.read(readBuffer, null, this);
//消息处理
for (final WebSocketPacket packet : packets) {
if (packet == null) {
failed(null, attachment1);
if (debug) context.getLogger().log(Level.FINEST, "WebSocketRunner abort on decode WebSocketPacket, force to close channel, live " + (System.currentTimeMillis() - webSocket.getCreatetime()) / 1000 + " seconds");
return;
}
if (packet.type == FrameType.TEXT) {
Convert textConvert = webSocket.getTextConvert();
if (textConvert == null) {
byte[] message = packet.getReceiveBytes();
if (readBuffer != null) {
readBuffer.clear();
channel.read(readBuffer, null, this);
}
try {
webSocket.onMessage(new String(message, "UTF-8"), packet.last);
} catch (Exception e) {
context.getLogger().log(Level.SEVERE, "WebSocket onBinaryMessage error (" + packet + ")", e);
}
} else {
Object message = textConvert.convertFrom(webSocket._messageTextType, packet.receiveMasker, packet.receiveBuffers);
if (readBuffer != null) {
readBuffer.clear();
channel.read(readBuffer, null, this);
}
try {
try {
if (packet.receiveType == WebSocketPacket.MessageType.STRING) {
webSocket.onMessage((String) packet.receiveMessage, packet.last);
} else {
if (restMessageConsumer != null) { //主要供RestWebSocket使用
restMessageConsumer.accept(webSocket, message);
restMessageConsumer.accept(webSocket, packet.receiveMessage);
} else {
webSocket.onMessage(message, packet.last);
webSocket.onMessage(packet.receiveMessage, packet.last);
}
} catch (Exception e) {
context.getLogger().log(Level.SEVERE, "WebSocket onTextMessage error (" + packet + ")", e);
}
} catch (Exception e) {
context.getLogger().log(Level.SEVERE, "WebSocket onTextMessage error (" + packet + ")", e);
}
} else if (packet.type == FrameType.BINARY) {
Convert binaryConvert = webSocket.getBinaryConvert();
if (binaryConvert == null) {
byte[] message = packet.getReceiveBytes();
if (readBuffer != null) {
readBuffer.clear();
channel.read(readBuffer, null, this);
}
try {
webSocket.onMessage(message, packet.last);
} catch (Exception e) {
context.getLogger().log(Level.SEVERE, "WebSocket onBinaryMessage error (" + packet + ")", e);
}
} else {
Object message = binaryConvert.convertFrom(webSocket._messageTextType, packet.receiveMasker, packet.receiveBuffers);
if (readBuffer != null) {
readBuffer.clear();
channel.read(readBuffer, null, this);
}
try {
if (restMessageConsumer != null) { //主要供RestWebSocket使用
restMessageConsumer.accept(webSocket, message);
} else {
webSocket.onMessage(message, packet.last);
}
} catch (Exception e) {
context.getLogger().log(Level.SEVERE, "WebSocket onTextMessage error (" + packet + ")", e);
}
}
} else if (packet.type == FrameType.PONG) {
byte[] message = packet.getReceiveBytes();
if (readBuffer != null) {
readBuffer.clear();
channel.read(readBuffer, null, this);
}
try {
webSocket.onPong(message);
if (packet.receiveType == WebSocketPacket.MessageType.BYTES) {
webSocket.onMessage((byte[]) packet.receiveMessage, packet.last);
} else {
if (restMessageConsumer != null) { //主要供RestWebSocket使用
restMessageConsumer.accept(webSocket, packet.receiveMessage);
} else {
webSocket.onMessage(packet.receiveMessage, packet.last);
}
}
} catch (Exception e) {
context.getLogger().log(Level.SEVERE, "WebSocket onPong error (" + packet + ")", e);
context.getLogger().log(Level.SEVERE, "WebSocket onBinaryMessage error (" + packet + ")", e);
}
} else if (packet.type == FrameType.PING) {
byte[] message = packet.getReceiveBytes();
if (readBuffer != null) {
readBuffer.clear();
channel.read(readBuffer, null, this);
}
try {
webSocket.onPing(message);
webSocket.onPing((byte[]) packet.receiveMessage);
} catch (Exception e) {
context.getLogger().log(Level.SEVERE, "WebSocket onPing error (" + packet + ")", e);
}
} else if (packet.type == FrameType.PONG) {
try {
webSocket.onPong((byte[]) packet.receiveMessage);
} catch (Exception e) {
context.getLogger().log(Level.SEVERE, "WebSocket onPong error (" + packet + ")", e);
}
} else if (packet.type == FrameType.CLOSE) {
Logger logger = context.getLogger();
if (logger.isLoggable(Level.FINEST)) logger.log(Level.FINEST, "WebSocketRunner onMessage by CLOSE FrameType : " + packet);
closeRunner(0);
return;
} else {
context.getLogger().log(Level.WARNING, "WebSocketRunner onMessage by unknown FrameType : " + packet);
if (readBuffer != null) {
readBuffer.clear();
channel.read(readBuffer, null, this);
}
}
} catch (Throwable t) {
closeRunner();
if (debug) context.getLogger().log(Level.FINEST, "WebSocketRunner abort on read WebSocketPacket, force to close channel, live " + (System.currentTimeMillis() - webSocket.getCreatetime()) / 1000 + " seconds", t);
} finally {
if (exBuffers != null) {
for (ByteBuffer b : exBuffers) {
context.offerBuffer(b);
}
closeRunner(0);
return;
}
}
// if (true) return; //以下代码废弃
// try {
// WebSocketPacket packet;
// try {
// packet = new WebSocketPacket().decode(context.getLogger(), readBuffer, exBuffers);
// } catch (Exception e) { //接收的消息体解析失败
// webSocket.onOccurException(e, Utility.append(new ByteBuffer[]{readBuffer}, exBuffers == null ? new ByteBuffer[0] : exBuffers));
// if (readBuffer != null) {
// readBuffer.clear();
// channel.read(readBuffer, null, this);
// }
// return;
// }
// if (packet == null) {
// failed(null, attachment1);
// if (debug) context.getLogger().log(Level.FINEST, "WebSocketRunner abort on decode WebSocketPacket, force to close channel, live " + (System.currentTimeMillis() - webSocket.getCreatetime()) / 1000 + " seconds");
// return;
// }
//
// if (packet.type == FrameType.TEXT) {
// Convert textConvert = webSocket.getTextConvert();
// if (textConvert == null) {
// byte[] message = packet.getReceiveBytes();
// if (readBuffer != null) {
// readBuffer.clear();
// channel.read(readBuffer, null, this);
// }
// try {
// webSocket.onMessage(new String(message, "UTF-8"), packet.last);
// } catch (Exception e) {
// context.getLogger().log(Level.SEVERE, "WebSocket onBinaryMessage error (" + packet + ")", e);
// }
// } else {
// Object message;
// try {
// message = textConvert.convertFrom(webSocket._messageTextType, packet.receiveMasker, packet.receiveBuffers);
// } catch (Exception e) { //接收的消息体解析失败
// webSocket.onOccurException(e, packet.receiveBuffers);
// if (readBuffer != null) {
// readBuffer.clear();
// channel.read(readBuffer, null, this);
// }
// return;
// }
// if (readBuffer != null) {
// readBuffer.clear();
// channel.read(readBuffer, null, this);
// }
// try {
// if (restMessageConsumer != null) { //主要供RestWebSocket使用
// restMessageConsumer.accept(webSocket, message);
// } else {
// webSocket.onMessage(message, packet.last);
// }
// } catch (Exception e) {
// context.getLogger().log(Level.SEVERE, "WebSocket onTextMessage error (" + packet + ")", e);
// }
// }
// } else if (packet.type == FrameType.BINARY) {
// Convert binaryConvert = webSocket.getBinaryConvert();
// if (binaryConvert == null) {
// byte[] message = packet.getReceiveBytes();
// if (readBuffer != null) {
// readBuffer.clear();
// channel.read(readBuffer, null, this);
// }
// try {
// webSocket.onMessage(message, packet.last);
// } catch (Exception e) {
// context.getLogger().log(Level.SEVERE, "WebSocket onBinaryMessage error (" + packet + ")", e);
// }
// } else {
// Object message;
// try {
// message = binaryConvert.convertFrom(webSocket._messageTextType, packet.receiveMasker, packet.receiveBuffers);
// } catch (Exception e) { //接收的消息体解析失败
// webSocket.onOccurException(e, packet.receiveBuffers);
// if (readBuffer != null) {
// readBuffer.clear();
// channel.read(readBuffer, null, this);
// }
// return;
// }
// if (readBuffer != null) {
// readBuffer.clear();
// channel.read(readBuffer, null, this);
// }
// try {
// if (restMessageConsumer != null) { //主要供RestWebSocket使用
// restMessageConsumer.accept(webSocket, message);
// } else {
// webSocket.onMessage(message, packet.last);
// }
// } catch (Exception e) {
// context.getLogger().log(Level.SEVERE, "WebSocket onTextMessage error (" + packet + ")", e);
// }
// }
// } else if (packet.type == FrameType.PONG) {
// byte[] message = packet.getReceiveBytes();
// if (readBuffer != null) {
// readBuffer.clear();
// channel.read(readBuffer, null, this);
// }
// try {
// webSocket.onPong(message);
// } catch (Exception e) {
// context.getLogger().log(Level.SEVERE, "WebSocket onPong error (" + packet + ")", e);
// }
// } else if (packet.type == FrameType.PING) {
// byte[] message = packet.getReceiveBytes();
// if (readBuffer != null) {
// readBuffer.clear();
// channel.read(readBuffer, null, this);
// }
// try {
// webSocket.onPing(message);
// } catch (Exception e) {
// context.getLogger().log(Level.SEVERE, "WebSocket onPing error (" + packet + ")", e);
// }
// } else if (packet.type == FrameType.CLOSE) {
// Logger logger = context.getLogger();
// if (logger.isLoggable(Level.FINEST)) logger.log(Level.FINEST, "WebSocketRunner onMessage by CLOSE FrameType : " + packet);
// closeRunner(0);
// } else {
// context.getLogger().log(Level.WARNING, "WebSocketRunner onMessage by unknown FrameType : " + packet);
// if (readBuffer != null) {
// readBuffer.clear();
// channel.read(readBuffer, null, this);
// }
// }
// } catch (Throwable t) {
// closeRunner(0);
// if (debug) context.getLogger().log(Level.FINEST, "WebSocketRunner abort on read WebSocketPacket, force to close channel, live " + (System.currentTimeMillis() - webSocket.getCreatetime()) / 1000 + " seconds", t);
// } finally {
// if (exBuffers != null) {
// for (ByteBuffer b : exBuffers) {
// context.offerBuffer(b);
// }
// }
// }
}
@Override
public void failed(Throwable exc, Void attachment2) {
closeRunner();
closeRunner(0);
if (exc != null) {
context.getLogger().log(Level.FINEST, "WebSocketRunner read WebSocketPacket failed, force to close channel, live " + (System.currentTimeMillis() - webSocket.getCreatetime()) / 1000 + " seconds", exc);
}
}
});
} else {
closeRunner();
closeRunner(0);
context.getLogger().log(Level.FINEST, "WebSocketRunner abort by AsyncConnection closed");
}
} catch (Exception e) {
closeRunner();
closeRunner(0);
context.getLogger().log(Level.FINEST, "WebSocketRunner abort on read bytes from channel, force to close channel, live " + (System.currentTimeMillis() - webSocket.getCreatetime()) / 1000 + " seconds", e);
}
}
@@ -285,7 +409,7 @@ class WebSocketRunner implements Runnable {
channel.write(buffers, buffers, this);
}
} catch (Exception e) {
closeRunner();
closeRunner(0);
context.getLogger().log(Level.WARNING, "WebSocket sendMessage abort on rewrite, force to close channel, live " + (System.currentTimeMillis() - webSocket.getCreatetime()) / 1000 + " seconds", e);
}
writing.set(false);
@@ -294,7 +418,7 @@ class WebSocketRunner implements Runnable {
@Override
public void failed(Throwable exc, ByteBuffer[] attachments) {
writing.set(false);
closeRunner();
closeRunner(0);
if (exc != null) {
context.getLogger().log(Level.FINE, "WebSocket sendMessage on CompletionHandler failed, force to close channel, live " + (System.currentTimeMillis() - webSocket.getCreatetime()) / 1000 + " seconds", exc);
}
@@ -302,14 +426,18 @@ class WebSocketRunner implements Runnable {
});
} catch (Exception t) {
writing.set(false);
closeRunner();
closeRunner(0);
context.getLogger().log(Level.FINE, "WebSocket sendMessage abort, force to close channel, live " + (System.currentTimeMillis() - webSocket.getCreatetime()) / 1000 + " seconds", t);
futureResult.complete(RETCODE_SENDEXCEPTION);
}
return futureResult;
}
public void closeRunner() {
public boolean isClosed() {
return closed;
}
public void closeRunner(int code) {
if (closed) return;
synchronized (this) {
if (closed) return;
@@ -321,7 +449,7 @@ class WebSocketRunner implements Runnable {
context.offerBuffer(readBuffer);
readBuffer = null;
engine.remove(webSocket);
webSocket.onClose(0, null);
webSocket.onClose(code, null);
}
}

View File

@@ -9,6 +9,7 @@ import java.io.*;
import java.lang.reflect.*;
import java.net.*;
import java.nio.*;
import java.nio.channels.CompletionHandler;
import java.security.*;
import java.util.*;
import java.util.concurrent.CompletableFuture;
@@ -45,6 +46,12 @@ public abstract class WebSocketServlet extends HttpServlet implements Resourcabl
@Comment("WebScoket服务器给客户端进行ping操作的间隔时间, 单位: 秒")
public static final String WEBPARAM__LIVEINTERVAL = "liveinterval";
@Comment("WebScoket服务器最大连接数为0表示无限制")
public static final String WEBPARAM__WSMAXCONNS = "wsmaxconns";
@Comment("最大消息体长度, 小于1表示无限制")
public static final String WEBPARAM__WSMAXBODY = "wsmaxbody";
@Comment("WebScoket服务器给客户端进行ping操作的默认间隔时间, 单位: 秒")
public static final int DEFAILT_LIVEINTERVAL = 15;
@@ -56,10 +63,21 @@ public abstract class WebSocketServlet extends HttpServlet implements Resourcabl
protected Type messageTextType; //RestWebSocket时会被修改
//同RestWebSocket.single
protected boolean single = true; //是否单用户单连接
//同RestWebSocket.liveinterval
protected int liveinterval = DEFAILT_LIVEINTERVAL;
//同RestWebSocket.wsmaxconns
protected int wsmaxconns = 0;
//同RestWebSocket.wsmaxbody
protected int wsmaxbody = 16 * 1024;
//同RestWebSocket.anyuser
protected boolean anyuser = false;
@Resource(name = "jsonconvert")
protected Convert jsonConvert;
@@ -105,8 +123,10 @@ public abstract class WebSocketServlet extends HttpServlet implements Resourcabl
this.node = new WebSocketNodeService();
if (logger.isLoggable(Level.WARNING)) logger.warning("Not found WebSocketNode, create a default value for " + getClass().getName());
}
//存在WebSocketServlet则此WebSocketNode必须是本地模式Service
this.node.localEngine = new WebSocketEngine("WebSocketEngine-" + addr.getHostString() + ":" + addr.getPort() + "-[" + resourceName() + "]", this.single, context, liveinterval, this.node, this.sendConvert, logger);
this.node.localEngine = new WebSocketEngine("WebSocketEngine-" + addr.getHostString() + ":" + addr.getPort() + "-[" + resourceName() + "]",
this.single, context, liveinterval, wsmaxconns, wsmaxbody, this.node, this.sendConvert, logger);
this.node.init(conf);
this.node.localEngine.init(conf);
}
@@ -137,6 +157,11 @@ public abstract class WebSocketServlet extends HttpServlet implements Resourcabl
response.finish(true);
return;
}
if (this.node.localEngine.isLocalConnLimited()) {
if (debug) logger.finest("WebSocket connections limit, wsmaxconns=" + this.node.localEngine.getLocalWsmaxconns());
response.finish(true);
return;
}
final WebSocket webSocket = this.createWebSocket();
webSocket._engine = this.node.localEngine;
webSocket._messageTextType = this.messageTextType;
@@ -153,7 +178,7 @@ public abstract class WebSocketServlet extends HttpServlet implements Resourcabl
return;
}
sessionFuture.whenComplete((sessionid, ex) -> {
if (sessionid == null || ex != null) {
if ((sessionid == null && webSocket.delayPackets == null) || ex != null) {
if (debug || ex != null) logger.log(ex == null ? Level.FINEST : Level.FINE, "WebSocket connect abort, Not found sessionid or occur error. request=" + request, ex);
response.finish(true);
return;
@@ -168,30 +193,95 @@ public abstract class WebSocketServlet extends HttpServlet implements Resourcabl
response.setHeader("Connection", "Upgrade");
response.addHeader("Upgrade", "websocket");
response.addHeader("Sec-WebSocket-Accept", Base64.getEncoder().encodeToString(bytes));
response.sendBody((ByteBuffer) null, null, new AsyncHandler<Integer, Void>() {
response.sendBody((ByteBuffer) null, null, new CompletionHandler<Integer, Void>() {
WebSocketRunner temprunner = null;
@Override
public void completed(Integer result, Void attachment) {
HttpContext context = response.getContext();
CompletableFuture<Serializable> userFuture = webSocket.createUserid();
if (userFuture == null) {
if (debug) logger.finest("WebSocket connect abort, Create userid abort. request = " + request);
response.finish(true);
return;
}
userFuture.whenComplete((userid, ex2) -> {
if (userid == null || ex2 != null) {
if (debug || ex2 != null) logger.log(ex2 == null ? Level.FINEST : Level.FINE, "WebSocket connect abort, Create userid abort. request = " + request, ex2);
Runnable createUseridHandler = () -> {
CompletableFuture<Serializable> userFuture = webSocket.createUserid();
if (userFuture == null) {
if (debug) logger.finest("WebSocket connect abort, Create userid abort. request = " + request);
response.finish(true);
return;
}
webSocket._userid = userid;
WebSocketServlet.this.node.localEngine.add(webSocket);
WebSocketRunner runner = new WebSocketRunner(context, webSocket, restMessageConsumer, response.removeChannel());
webSocket._runner = runner;
context.runAsync(runner);
response.finish(true);
});
userFuture.whenComplete((userid, ex2) -> {
if ((userid == null && webSocket.delayPackets == null) || ex2 != null) {
if (debug || ex2 != null) logger.log(ex2 == null ? Level.FINEST : Level.FINE, "WebSocket connect abort, Create userid abort. request = " + request, ex2);
response.finish(true);
return;
}
Runnable runHandler = () -> {
temprunner = null;
webSocket._userid = userid;
if (single && !anyuser) {
WebSocketServlet.this.node.existsWebSocket(userid).whenComplete((rs, ex) -> {
if (rs) webSocket.onSingleRepeatConnect();
WebSocketServlet.this.node.localEngine.add(webSocket);
WebSocketRunner runner = new WebSocketRunner(context, webSocket, restMessageConsumer, response.removeChannel());
webSocket._runner = runner;
context.runAsync(runner);
response.finish(true);
});
} else {
WebSocketServlet.this.node.localEngine.add(webSocket);
WebSocketRunner runner = new WebSocketRunner(context, webSocket, restMessageConsumer, response.removeChannel());
webSocket._runner = runner;
context.runAsync(runner);
response.finish(true);
}
};
if (webSocket.delayPackets != null) { //存在待发送的消息
if (temprunner == null) temprunner = new WebSocketRunner(context, webSocket, restMessageConsumer, response.getChannel());
List<WebSocketPacket> delayPackets = webSocket.delayPackets;
webSocket.delayPackets = null;
CompletableFuture<Integer> cf = null;
for (WebSocketPacket packet : delayPackets) {
if (cf == null) {
cf = temprunner.sendMessage(packet);
} else {
cf = cf.thenCombine(temprunner.sendMessage(packet), (a, b) -> a | b);
}
}
cf.whenComplete((Integer v, Throwable t) -> {
if (userid == null || t != null || (temprunner != null && temprunner.isClosed())) {
if (t != null) logger.log(Level.FINEST, "WebSocket connect abort, Response send delayPackets abort. request = " + request, t);
response.finish(true);
} else {
runHandler.run();
}
});
} else {
runHandler.run();
}
});
};
if (webSocket.delayPackets != null) { //存在待发送的消息
if (temprunner == null) temprunner = new WebSocketRunner(context, webSocket, restMessageConsumer, response.getChannel());
List<WebSocketPacket> delayPackets = webSocket.delayPackets;
webSocket.delayPackets = null;
CompletableFuture<Integer> cf = null;
for (WebSocketPacket packet : delayPackets) {
if (cf == null) {
cf = temprunner.sendMessage(packet);
} else {
cf = cf.thenCombine(temprunner.sendMessage(packet), (a, b) -> a | b);
}
}
cf.whenComplete((Integer v, Throwable t) -> {
if (sessionid == null || t != null || (temprunner != null && temprunner.isClosed())) {
if (t != null) logger.log(Level.FINEST, "WebSocket connect abort, Response send delayPackets abort. request = " + request, t);
response.finish(true);
} else {
createUseridHandler.run();
}
});
} else {
createUseridHandler.run();
}
}
@Override

View File

@@ -8,6 +8,8 @@ package org.redkale.net.sncp;
import java.lang.annotation.Annotation;
import java.lang.reflect.*;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.CompletionHandler;
import java.security.*;
import java.util.*;
import javax.annotation.Resource;
@@ -31,6 +33,10 @@ import org.redkale.util.*;
*/
public abstract class Sncp {
public static final ByteBuffer PING_BUFFER = ByteBuffer.wrap("PING".getBytes()).asReadOnlyBuffer();
public static final ByteBuffer PONG_BUFFER = ByteBuffer.wrap("PONG".getBytes()).asReadOnlyBuffer();
static final String FIELDPREFIX = "_redkale";
static final String LOCALPREFIX = "_DynLocal";
@@ -133,12 +139,12 @@ public abstract class Sncp {
}
static void checkAsyncModifier(Class param, Method method) {
if (param == AsyncHandler.class) return;
if (param == CompletionHandler.class) return;
if (Modifier.isFinal(param.getModifiers())) {
throw new RuntimeException("AsyncHandler Type Parameter on {" + method + "} cannot final modifier");
throw new RuntimeException("CompletionHandler Type Parameter on {" + method + "} cannot final modifier");
}
if (!Modifier.isPublic(param.getModifiers())) {
throw new RuntimeException("AsyncHandler Type Parameter on {" + method + "} must be public modifier");
throw new RuntimeException("CompletionHandler Type Parameter on {" + method + "} must be public modifier");
}
if (param.isInterface()) return;
boolean constructorflag = false;
@@ -266,6 +272,7 @@ public abstract class Sncp {
protected static <T extends Service> Class<? extends T> createLocalServiceClass(ClassLoader classLoader, final String name, final Class<T> serviceImplClass) {
if (serviceImplClass == null) return null;
if (!Service.class.isAssignableFrom(serviceImplClass)) return serviceImplClass;
ResourceFactory.checkResourceName(name);
int mod = serviceImplClass.getModifiers();
if (!java.lang.reflect.Modifier.isPublic(mod)) return serviceImplClass;
if (java.lang.reflect.Modifier.isAbstract(mod)) return serviceImplClass;
@@ -382,8 +389,8 @@ public abstract class Sncp {
int varindex = 0;
boolean handlerFuncFlag = false;
for (Class pt : paramtypes) {
if (AsyncHandler.class.isAssignableFrom(pt)) {
if (handlerFuncFlag) throw new RuntimeException(method + " have more than one AsyncHandler type parameter");
if (CompletionHandler.class.isAssignableFrom(pt)) {
if (handlerFuncFlag) throw new RuntimeException(method + " have more than one CompletionHandler type parameter");
checkAsyncModifier(pt, method);
handlerFuncFlag = true;
}
@@ -478,12 +485,12 @@ public abstract class Sncp {
} else if (returnType.isPrimitive()) {
if (returnType == long.class) {
mv.visitVarInsn(LSTORE, ++varindex);
++varindex; //多加1
//++varindex; //多加1
} else if (returnType == float.class) {
mv.visitVarInsn(FSTORE, ++varindex);
} else if (returnType == double.class) {
mv.visitVarInsn(DSTORE, ++varindex);
++varindex; //多加1
//++varindex; //多加1
} else {
mv.visitVarInsn(ISTORE, ++varindex);
}
@@ -527,16 +534,8 @@ public abstract class Sncp {
mv.visitFieldInsn(GETFIELD, newDynName, FIELDPREFIX + "_client", clientDesc);
final int preparams = 3; //调用selfrunnable之前的参数个数; _client
if (index <= 5) { //第几个 SncpAction
mv.visitInsn(ICONST_0 + index);
} else {
mv.visitIntInsn(BIPUSH, index);
}
if (paramtypes.length + preparams <= 5) { //参数总数量
mv.visitInsn(ICONST_0 + paramtypes.length + preparams);
} else {
mv.visitIntInsn(BIPUSH, paramtypes.length + preparams);
}
pushInt(mv, index); //第几个 SncpAction
pushInt(mv, paramtypes.length + preparams); //参数总数量
mv.visitTypeInsn(ANEWARRAY, "java/lang/Object");
@@ -563,11 +562,7 @@ public abstract class Sncp {
final Class pt = paramtypes[j];
mv.visitInsn(DUP);
insn++;
if (j <= 2) {
mv.visitInsn(ICONST_0 + j + 3);
} else {
mv.visitIntInsn(BIPUSH, j + 3);
}
pushInt(mv, j + 3);
if (pt.isPrimitive()) {
if (pt == long.class) {
mv.visitVarInsn(LLOAD, insn++);
@@ -595,16 +590,8 @@ public abstract class Sncp {
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, newDynName, FIELDPREFIX + "_client", clientDesc);
if (index <= 5) { //第几个 SncpAction
mv.visitInsn(ICONST_0 + index);
} else {
mv.visitIntInsn(BIPUSH, index);
}
if (paramtypes.length + preparams <= 5) { //参数总数量
mv.visitInsn(ICONST_0 + paramtypes.length + preparams);
} else {
mv.visitIntInsn(BIPUSH, paramtypes.length + preparams);
}
pushInt(mv, index); //第几个 SncpAction
pushInt(mv, paramtypes.length + preparams); //参数总数量
mv.visitTypeInsn(ANEWARRAY, "java/lang/Object");
@@ -631,11 +618,7 @@ public abstract class Sncp {
final Class pt = paramtypes[j];
mv.visitInsn(DUP);
insn++;
if (j <= 2) {
mv.visitInsn(ICONST_0 + j + 3);
} else {
mv.visitIntInsn(BIPUSH, j + 3);
}
pushInt(mv, j + 3);
if (pt.isPrimitive()) {
if (pt == long.class) {
mv.visitVarInsn(LLOAD, insn++);
@@ -873,7 +856,7 @@ public abstract class Sncp {
* @param serviceTypeOrImplClass Service类
* @param transportFactory TransportFactory
* @param clientAddress 本地IP地址
* @param groups 所有的组节点,包含自身
* @param groups0 所有的组节点,包含自身
* @param conf 启动配置项
*
* @return Service的远程模式实例
@@ -886,10 +869,12 @@ public abstract class Sncp {
final Class<T> serviceTypeOrImplClass,
final TransportFactory transportFactory,
final InetSocketAddress clientAddress,
final Set<String> groups,
final Set<String> groups0,
final AnyValue conf) {
if (serviceTypeOrImplClass == null) return null;
if (!Service.class.isAssignableFrom(serviceTypeOrImplClass)) return null;
Set<String> groups = groups0 == null ? new HashSet<>() : groups0;
ResourceFactory.checkResourceName(name);
int mod = serviceTypeOrImplClass.getModifiers();
boolean realed = !(java.lang.reflect.Modifier.isAbstract(mod) || serviceTypeOrImplClass.isInterface());
if (!java.lang.reflect.Modifier.isPublic(mod)) return null;
@@ -1009,19 +994,11 @@ public abstract class Sncp {
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, newDynName, FIELDPREFIX + "_client", clientDesc);
if (index <= 5) {
mv.visitInsn(ICONST_0 + index);
} else {
mv.visitIntInsn(BIPUSH, index);
}
pushInt(mv, index);
{ //传参数
int paramlen = entry.paramTypes.length;
if (paramlen <= 5) {
mv.visitInsn(ICONST_0 + paramlen);
} else {
mv.visitIntInsn(BIPUSH, paramlen);
}
pushInt(mv, paramlen);
mv.visitTypeInsn(ANEWARRAY, "java/lang/Object");
java.lang.reflect.Type[] paramtypes = entry.paramTypes;
int insn = 0;
@@ -1029,11 +1006,7 @@ public abstract class Sncp {
final java.lang.reflect.Type pt = paramtypes[j];
mv.visitInsn(DUP);
insn++;
if (j <= 5) {
mv.visitInsn(ICONST_0 + j);
} else {
mv.visitIntInsn(BIPUSH, j);
}
pushInt(mv, j);
if (pt instanceof Class && ((Class) pt).isPrimitive()) {
if (pt == long.class) {
mv.visitVarInsn(LLOAD, insn++);
@@ -1116,4 +1089,28 @@ public abstract class Sncp {
}
}
private static void pushInt(AsmMethodVisitor mv, int num) {
if (num < 6) {
mv.visitInsn(ICONST_0 + num);
} else if (num <= Byte.MAX_VALUE) {
mv.visitIntInsn(BIPUSH, num);
} else if (num <= Short.MAX_VALUE) {
mv.visitIntInsn(SIPUSH, num);
} else {
mv.visitLdcInsn(num);
}
}
private static void pushInt(MethodVisitor mv, int num) {
if (num < 6) {
mv.visitInsn(ICONST_0 + num);
} else if (num <= Byte.MAX_VALUE) {
mv.visitIntInsn(BIPUSH, num);
} else if (num <= Short.MAX_VALUE) {
mv.visitIntInsn(SIPUSH, num);
} else {
mv.visitLdcInsn(num);
}
}
}

View File

@@ -5,6 +5,7 @@
*/
package org.redkale.net.sncp;
import java.nio.channels.CompletionHandler;
import java.util.concurrent.CompletableFuture;
import java.util.logging.Level;
import jdk.internal.org.objectweb.asm.*;
@@ -26,7 +27,7 @@ import org.redkale.util.*;
* @param <V> 结果对象的泛型
* @param <A> 附件对象的泛型
*/
public interface SncpAsyncHandler<V, A> extends AsyncHandler<V, A> {
public interface SncpAsyncHandler<V, A> extends CompletionHandler<V, A> {
public Object[] sncp_getParams();
@@ -42,9 +43,9 @@ public interface SncpAsyncHandler<V, A> extends AsyncHandler<V, A> {
* <blockquote><pre>
*
* 考虑点:
* 1、AsyncHandler子类是接口且还有其他多个方法
* 2、AsyncHandler子类是类 需要继承,且必须有空参数构造函数
* 3、AsyncHandler子类无论是接口还是类都可能存在其他泛型
* 1、CompletionHandler子类是接口且还有其他多个方法
* 2、CompletionHandler子类是类 需要继承,且必须有空参数构造函数
* 3、CompletionHandler子类无论是接口还是类都可能存在其他泛型
*
* public class XXXAsyncHandler_DyncSncpAsyncHandler_4323 extends XXXAsyncHandler implements SncpAsyncHandler {
*
@@ -91,11 +92,11 @@ public interface SncpAsyncHandler<V, A> extends AsyncHandler<V, A> {
*
* </pre></blockquote>
*
* @param handlerClass AsyncHandler类型或子类
* @param handlerClass CompletionHandler类型或子类
*
* @return Creator
*/
public static Creator<SncpAsyncHandler> createCreator(Class<? extends AsyncHandler> handlerClass) {
public static Creator<SncpAsyncHandler> createCreator(Class<? extends CompletionHandler> handlerClass) {
//-------------------------------------------------------------
final boolean handlerinterface = handlerClass.isInterface();
final String handlerClassName = handlerClass.getName().replace('.', '/');

View File

@@ -12,6 +12,7 @@ import java.nio.*;
import java.nio.channels.*;
import java.util.*;
import java.util.concurrent.*;
import java.util.function.Supplier;
import java.util.logging.*;
import javax.annotation.Resource;
import org.redkale.convert.bson.*;
@@ -33,8 +34,6 @@ public final class SncpClient {
protected static final Logger logger = Logger.getLogger(SncpClient.class.getSimpleName());
protected final boolean finest = logger.isLoggable(Level.FINEST);
protected final JsonConvert convert = JsonFactory.root().getConvert();
protected final String name;
@@ -57,6 +56,8 @@ public final class SncpClient {
protected final ExecutorService executor;
protected final Supplier<ByteBuffer> bufferSupplier;
@Resource
protected JsonConvert jsonConvert;
@@ -85,6 +86,7 @@ public final class SncpClient {
final boolean remote, final Class serviceClass, final InetSocketAddress clientAddress) {
this.remote = remote;
this.executor = factory.getExecutor();
this.bufferSupplier = factory.getBufferSupplier();
this.serviceClass = serviceClass;
this.serviceversion = 0;
this.clientAddress = clientAddress;
@@ -101,6 +103,7 @@ public final class SncpClient {
this.actions = methodens.toArray(new SncpAction[methodens.size()]);
this.addrBytes = clientAddress == null ? new byte[4] : clientAddress.getAddress().getAddress();
this.addrPort = clientAddress == null ? 0 : clientAddress.getPort();
if (this.addrBytes.length != 4) throw new RuntimeException("SNCP clientAddress only support IPv4");
}
static List<SncpAction> getSncpActions(final Class serviceClass) {
@@ -290,7 +293,7 @@ public final class SncpClient {
//只给远程模式调用的
public <T> T remote(final int index, final Object... params) {
final SncpAction action = actions[index];
final AsyncHandler handlerFunc = action.handlerFuncParamIndex >= 0 ? (AsyncHandler) params[action.handlerFuncParamIndex] : null;
final CompletionHandler handlerFunc = action.handlerFuncParamIndex >= 0 ? (CompletionHandler) params[action.handlerFuncParamIndex] : null;
if (action.handlerFuncParamIndex >= 0) params[action.handlerFuncParamIndex] = null;
final BsonReader reader = bsonConvert.pollBsonReader();
CompletableFuture<byte[]> future = remote0(handlerFunc, remoteGroupTransport, null, action, params);
@@ -336,159 +339,156 @@ public final class SncpClient {
}
}
private CompletableFuture<byte[]> remote0(final AsyncHandler handler, final Transport transport, final SocketAddress addr0, final SncpAction action, final Object... params) {
private CompletableFuture<byte[]> remote0(final CompletionHandler handler, final Transport transport, final SocketAddress addr0, final SncpAction action, final Object... params) {
final Type[] myparamtypes = action.paramTypes;
final Class[] myparamclass = action.paramClass;
if (action.addressSourceParamIndex >= 0) params[action.addressSourceParamIndex] = this.clientAddress;
if (bsonConvert == null) bsonConvert = BsonConvert.root();
final BsonWriter writer = bsonConvert.pollBsonWriter(transport.getBufferSupplier()); // 将head写入
writer.writeTo(DEFAULT_HEADER);
for (int i = 0; i < params.length; i++) { //params 可能包含: 3 个 boolean
bsonConvert.convertTo(writer, AsyncHandler.class.isAssignableFrom(myparamclass[i]) ? AsyncHandler.class : myparamtypes[i], params[i]);
bsonConvert.convertTo(writer, CompletionHandler.class.isAssignableFrom(myparamclass[i]) ? CompletionHandler.class : myparamtypes[i], params[i]);
}
final int reqBodyLength = writer.count() - HEADER_SIZE; //body总长度
final long seqid = System.nanoTime();
final DLong actionid = action.actionid;
final SocketAddress addr = addr0 == null ? (action.addressTargetParamIndex >= 0 ? (SocketAddress) params[action.addressTargetParamIndex] : null) : addr0;
final CompletableFuture<byte[]> future = new CompletableFuture();
AsyncConnection conn0;
try {
conn0 = transport.pollConnection(addr);
} catch (Exception e) {
future.completeExceptionally(e);
return future;
}
if (conn0 == null || !conn0.isOpen()) {
future.completeExceptionally(new RuntimeException("sncp " + (conn0 == null ? addr : conn0.getRemoteAddress()) + " cannot connect"));
return future;
}
final AsyncConnection conn = conn0;
final ByteBuffer[] sendBuffers = writer.toBuffers();
fillHeader(sendBuffers[0], seqid, actionid, reqBodyLength);
CompletableFuture<AsyncConnection> connFuture = transport.pollConnection(addr);
return connFuture.thenCompose(conn0 -> {
final CompletableFuture<byte[]> future = new CompletableFuture();
if (conn0 == null || !conn0.isOpen()) {
future.completeExceptionally(new RuntimeException("sncp " + (conn0 == null ? addr : conn0.getRemoteAddress()) + " cannot connect"));
return future;
}
final AsyncConnection conn = conn0;
final ByteBuffer[] sendBuffers = writer.toBuffers();
fillHeader(sendBuffers[0], seqid, actionid, reqBodyLength);
final ByteBuffer buffer = transport.pollBuffer();
conn.write(sendBuffers, sendBuffers, new CompletionHandler<Integer, ByteBuffer[]>() {
final ByteBuffer buffer = transport.pollBuffer();
conn.write(sendBuffers, sendBuffers, new CompletionHandler<Integer, ByteBuffer[]>() {
@Override
public void completed(Integer result, ByteBuffer[] attachments) {
int index = -1;
for (int i = 0; i < attachments.length; i++) {
if (attachments[i].hasRemaining()) {
index = i;
break;
} else {
transport.offerBuffer(attachments[i]);
@Override
public void completed(Integer result, ByteBuffer[] attachments) {
int index = -1;
for (int i = 0; i < attachments.length; i++) {
if (attachments[i].hasRemaining()) {
index = i;
break;
} else {
transport.offerBuffer(attachments[i]);
}
}
}
if (index == 0) {
conn.write(attachments, attachments, this);
return;
} else if (index > 0) {
ByteBuffer[] newattachs = new ByteBuffer[attachments.length - index];
System.arraycopy(attachments, index, newattachs, 0, newattachs.length);
conn.write(newattachs, newattachs, this);
return;
}
//----------------------- 读取返回结果 -------------------------------------
buffer.clear();
conn.read(buffer, null, new CompletionHandler<Integer, Void>() {
if (index == 0) {
conn.write(attachments, attachments, this);
return;
} else if (index > 0) {
ByteBuffer[] newattachs = new ByteBuffer[attachments.length - index];
System.arraycopy(attachments, index, newattachs, 0, newattachs.length);
conn.write(newattachs, newattachs, this);
return;
}
//----------------------- 读取返回结果 -------------------------------------
buffer.clear();
conn.read(buffer, null, new CompletionHandler<Integer, Void>() {
private byte[] body;
private byte[] body;
private int received;
private int received;
@Override
public void completed(Integer count, Void attachment2) {
if (count < 1 && buffer.remaining() == buffer.limit()) { //没有数据可读
future.completeExceptionally(new RuntimeException(action.method + " sncp[" + conn.getRemoteAddress() + "] remote no response data"));
transport.offerBuffer(buffer);
transport.offerConnection(true, conn);
return;
}
if (received < 1 && buffer.limit() < buffer.remaining() + HEADER_SIZE) { //header都没读全
conn.read(buffer, attachment2, this);
return;
}
buffer.flip();
if (received > 0) {
int offset = this.received;
this.received += buffer.remaining();
buffer.get(body, offset, Math.min(buffer.remaining(), this.body.length - offset));
if (this.received < this.body.length) {// 数据仍然不全,需要继续读取
@Override
public void completed(Integer count, Void attachment2) {
if (count < 1 && buffer.remaining() == buffer.limit()) { //没有数据可读
future.completeExceptionally(new RuntimeException(action.method + " sncp[" + conn.getRemoteAddress() + "] remote no response data"));
transport.offerBuffer(buffer);
transport.offerConnection(true, conn);
return;
}
if (received < 1 && buffer.limit() < buffer.remaining() + HEADER_SIZE) { //header都没读全
conn.read(buffer, attachment2, this);
return;
}
buffer.flip();
if (received > 0) {
int offset = this.received;
this.received += buffer.remaining();
buffer.get(body, offset, Math.min(buffer.remaining(), this.body.length - offset));
if (this.received < this.body.length) {// 数据仍然不全,需要继续读取
buffer.clear();
conn.read(buffer, attachment2, this);
} else {
success();
}
return;
}
checkResult(seqid, action, buffer);
final int respBodyLength = buffer.getInt();
final int retcode = buffer.getInt();
if (retcode != 0) {
logger.log(Level.SEVERE, action.method + " sncp (params: " + convert.convertTo(params) + ") deal error (retcode=" + retcode + ", retinfo=" + SncpResponse.getRetCodeInfo(retcode) + ")");
throw new RuntimeException("remote service(" + action.method + ") deal error (retcode=" + retcode + ", retinfo=" + SncpResponse.getRetCodeInfo(retcode) + ")");
}
if (respBodyLength > buffer.remaining()) { // 数据不全,需要继续读取
this.body = new byte[respBodyLength];
this.received = buffer.remaining();
buffer.get(body, 0, this.received);
buffer.clear();
conn.read(buffer, attachment2, this);
} else {
this.body = new byte[respBodyLength];
buffer.get(body, 0, respBodyLength);
success();
}
return;
}
checkResult(seqid, action, buffer);
final int respBodyLength = buffer.getInt();
final int retcode = buffer.getInt();
if (retcode != 0) {
logger.log(Level.SEVERE, action.method + " sncp (params: " + convert.convertTo(params) + ") deal error (retcode=" + retcode + ", retinfo=" + SncpResponse.getRetCodeInfo(retcode) + ")");
throw new RuntimeException("remote service(" + action.method + ") deal error (retcode=" + retcode + ", retinfo=" + SncpResponse.getRetCodeInfo(retcode) + ")");
}
if (respBodyLength > buffer.remaining()) { // 数据不全,需要继续读取
this.body = new byte[respBodyLength];
this.received = buffer.remaining();
buffer.get(body, 0, this.received);
buffer.clear();
conn.read(buffer, attachment2, this);
} else {
this.body = new byte[respBodyLength];
buffer.get(body, 0, respBodyLength);
success();
}
}
public void success() {
future.complete(this.body);
transport.offerBuffer(buffer);
transport.offerConnection(false, conn);
if (handler != null) {
final Object handlerAttach = action.handlerAttachParamIndex >= 0 ? params[action.handlerAttachParamIndex] : null;
final BsonReader reader = bsonConvert.pollBsonReader();
try {
reader.setBytes(this.body);
int i;
while ((i = (reader.readByte() & 0xff)) != 0) {
final Attribute attr = action.paramAttrs[i];
attr.set(params[i - 1], bsonConvert.convertFrom(attr.type(), reader));
public void success() {
future.complete(this.body);
transport.offerBuffer(buffer);
transport.offerConnection(false, conn);
if (handler != null) {
final Object handlerAttach = action.handlerAttachParamIndex >= 0 ? params[action.handlerAttachParamIndex] : null;
final BsonReader reader = bsonConvert.pollBsonReader();
try {
reader.setBytes(this.body);
int i;
while ((i = (reader.readByte() & 0xff)) != 0) {
final Attribute attr = action.paramAttrs[i];
attr.set(params[i - 1], bsonConvert.convertFrom(attr.type(), reader));
}
Object rs = bsonConvert.convertFrom(action.handlerFuncParamIndex >= 0 ? Object.class : action.resultTypes, reader);
handler.completed(rs, handlerAttach);
} catch (Exception e) {
handler.failed(e, handlerAttach);
} finally {
bsonConvert.offerBsonReader(reader);
}
Object rs = bsonConvert.convertFrom(action.handlerFuncParamIndex >= 0 ? Object.class : action.resultTypes, reader);
handler.completed(rs, handlerAttach);
} catch (Exception e) {
handler.failed(e, handlerAttach);
} finally {
bsonConvert.offerBsonReader(reader);
}
}
}
@Override
public void failed(Throwable exc, Void attachment2) {
logger.log(Level.SEVERE, action.method + " sncp (params: " + convert.convertTo(params) + ") remote read exec failed", exc);
future.completeExceptionally(new RuntimeException(action.method + " sncp remote exec failed"));
transport.offerBuffer(buffer);
transport.offerConnection(true, conn);
if (handler != null) {
final Object handlerAttach = action.handlerAttachParamIndex >= 0 ? params[action.handlerAttachParamIndex] : null;
handler.failed(exc, handlerAttach);
@Override
public void failed(Throwable exc, Void attachment2) {
logger.log(Level.SEVERE, action.method + " sncp (params: " + convert.convertTo(params) + ") remote read exec failed", exc);
future.completeExceptionally(new RuntimeException(action.method + " sncp remote exec failed"));
transport.offerBuffer(buffer);
transport.offerConnection(true, conn);
if (handler != null) {
final Object handlerAttach = action.handlerAttachParamIndex >= 0 ? params[action.handlerAttachParamIndex] : null;
handler.failed(exc, handlerAttach);
}
}
}
});
}
});
}
@Override
public void failed(Throwable exc, ByteBuffer[] attachment) {
logger.log(Level.SEVERE, action.method + " sncp (params: " + convert.convertTo(params) + ") remote write exec failed", exc);
transport.offerBuffer(buffer);
transport.offerConnection(true, conn);
}
@Override
public void failed(Throwable exc, ByteBuffer[] attachment) {
logger.log(Level.SEVERE, action.method + " sncp (params: " + convert.convertTo(params) + ") remote write exec failed", exc);
transport.offerBuffer(buffer);
transport.offerConnection(true, conn);
}
});
return future;
});
return future;
}
private void checkResult(long seqid, final SncpAction action, ByteBuffer buffer) {
@@ -571,12 +571,12 @@ public final class SncpClient {
if (anns.length > 0) {
Class<?>[] params = method.getParameterTypes();
for (int i = 0; i < params.length; i++) {
if (AsyncHandler.class.isAssignableFrom(params[i])) {
if (CompletionHandler.class.isAssignableFrom(params[i])) {
if (boolReturnTypeFuture) {
throw new RuntimeException(method + " have both AsyncHandler and CompletableFuture");
throw new RuntimeException(method + " have both CompletionHandler and CompletableFuture");
}
if (handlerFuncIndex >= 0) {
throw new RuntimeException(method + " have more than one AsyncHandler type parameter");
throw new RuntimeException(method + " have more than one CompletionHandler type parameter");
}
Sncp.checkAsyncModifier(params[i], method);
handlerFuncIndex = i;
@@ -617,7 +617,7 @@ public final class SncpClient {
this.handlerAttachParamIndex = handlerAttachIndex;
this.paramAttrs = hasattr ? atts : null;
if (this.handlerFuncParamIndex >= 0 && method.getReturnType() != void.class) {
throw new RuntimeException(method + " have AsyncHandler type parameter but return type is not void");
throw new RuntimeException(method + " have CompletionHandler type parameter but return type is not void");
}
}

View File

@@ -8,7 +8,7 @@ package org.redkale.net.sncp;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.logging.Logger;
import org.redkale.net.*;
import org.redkale.util.ObjectPool;
@@ -21,7 +21,7 @@ import org.redkale.util.ObjectPool;
*/
public class SncpContext extends Context {
public SncpContext(long serverStartTime, Logger logger, ExecutorService executor, int bufferCapacity, ObjectPool<ByteBuffer> bufferPool,
public SncpContext(long serverStartTime, Logger logger, ThreadPoolExecutor executor, int bufferCapacity, ObjectPool<ByteBuffer> bufferPool,
ObjectPool<Response> responsePool, int maxbody, Charset charset, InetSocketAddress address, PrepareServlet prepare,
int readTimeoutSecond, int writeTimeoutSecond) {
super(serverStartTime, logger, executor, bufferCapacity, bufferPool, responsePool, maxbody, charset,

View File

@@ -10,6 +10,7 @@ import java.io.*;
import java.lang.annotation.*;
import java.lang.reflect.*;
import java.nio.*;
import java.nio.channels.CompletionHandler;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.function.*;
@@ -40,8 +41,6 @@ public final class SncpDynServlet extends SncpServlet {
private static final Logger logger = Logger.getLogger(SncpDynServlet.class.getSimpleName());
private final boolean finest = logger.isLoggable(Level.FINEST);
private final DLong serviceid;
private final HashMap<DLong, SncpServletAction> actions = new HashMap<>();
@@ -110,7 +109,7 @@ public final class SncpDynServlet extends SncpServlet {
bufferSupplier = request.getContext().getBufferSupplier();
}
final SncpServletAction action = actions.get(request.getActionid());
//if (finest) logger.log(Level.FINEST, "sncpdyn.execute: " + request + ", " + (action == null ? "null" : action.method));
//logger.log(Level.FINEST, "sncpdyn.execute: " + request + ", " + (action == null ? "null" : action.method));
if (action == null) {
response.finish(SncpResponse.RETCODE_ILLACTIONID, null); //无效actionid
} else {
@@ -120,7 +119,7 @@ public final class SncpDynServlet extends SncpServlet {
SncpAsyncHandler handler = null;
try {
if (action.handlerFuncParamIndex >= 0) {
if (action.handlerFuncParamClass == AsyncHandler.class) {
if (action.handlerFuncParamClass == CompletionHandler.class) {
handler = new DefaultSncpAsyncHandler(action, in, out, request, response);
} else {
Creator<SncpAsyncHandler> creator = action.handlerCreator;
@@ -180,15 +179,15 @@ public final class SncpDynServlet extends SncpServlet {
protected java.lang.reflect.Type[] paramTypes; //index=0表示返回参数的type void的返回参数类型为null
protected int handlerFuncParamIndex = -1; //handlerFuncParamIndex>=0表示存在AsyncHandler参数
protected int handlerFuncParamIndex = -1; //handlerFuncParamIndex>=0表示存在CompletionHandler参数
protected boolean boolReturnTypeFuture = false; // 返回结果类型是否为 CompletableFuture
protected Class handlerFuncParamClass; //AsyncHandler参数的类型
protected Class handlerFuncParamClass; //CompletionHandler参数的类型
public abstract void action(final BsonReader in, final BsonWriter out, final SncpAsyncHandler handler) throws Throwable;
//只有同步方法才调用 (没有AsyncHandler、CompletableFuture)
//只有同步方法才调用 (没有CompletionHandler、CompletableFuture)
public final void _callParameter(final BsonWriter out, final Object... params) {
if (paramAttrs != null) {
for (int i = 1; i < paramAttrs.length; i++) {
@@ -209,10 +208,10 @@ public final class SncpDynServlet extends SncpServlet {
* return false;
* }
*
* public void insert(AsyncHandler&#60;Boolean, TestBean&#62; handler, TestBean bean, String name, int id) {
* public void insert(CompletionHandler&#60;Boolean, TestBean&#62; handler, TestBean bean, String name, int id) {
* }
*
* public void update(long show, short v2, AsyncHandler&#60;Boolean, TestBean&#62; handler, TestBean bean, String name, int id) {
* public void update(long show, short v2, CompletionHandler&#60;Boolean, TestBean&#62; handler, TestBean bean, String name, int id) {
* }
*
* public CompletableFuture&#60;String&#62; changeName(TestBean bean, String name, int id) {
@@ -243,7 +242,7 @@ public final class SncpDynServlet extends SncpServlet {
* &#064;Override
* public void action(BsonReader in, BsonWriter out, SncpAsyncHandler handler) throws Throwable {
* SncpAsyncHandler arg0 = handler;
* convert.convertFrom(AsyncHandler.class, in);
* convert.convertFrom(CompletionHandler.class, in);
* TestBean arg1 = convert.convertFrom(paramTypes[2], in);
* String arg2 = convert.convertFrom(paramTypes[3], in);
* int arg3 = convert.convertFrom(paramTypes[4], in);
@@ -261,7 +260,7 @@ public final class SncpDynServlet extends SncpServlet {
* long a1 = convert.convertFrom(paramTypes[1], in);
* short a2 = convert.convertFrom(paramTypes[2], in);
* SncpAsyncHandler a3 = handler;
* convert.convertFrom(AsyncHandler.class, in);
* convert.convertFrom(CompletionHandler.class, in);
* TestBean arg1 = convert.convertFrom(paramTypes[4], in);
* String arg2 = convert.convertFrom(paramTypes[5], in);
* int arg3 = convert.convertFrom(paramTypes[6], in);
@@ -355,12 +354,12 @@ public final class SncpDynServlet extends SncpServlet {
final Class[] paramClasses = method.getParameterTypes();
int[][] codes = new int[paramClasses.length][2];
for (int i = 0; i < paramClasses.length; i++) { //反序列化方法的每个参数
if (AsyncHandler.class.isAssignableFrom(paramClasses[i])) {
if (CompletionHandler.class.isAssignableFrom(paramClasses[i])) {
if (boolReturnTypeFuture) {
throw new RuntimeException(method + " have both AsyncHandler and CompletableFuture");
throw new RuntimeException(method + " have both CompletionHandler and CompletableFuture");
}
if (handlerFuncIndex >= 0) {
throw new RuntimeException(method + " have more than one AsyncHandler type parameter");
throw new RuntimeException(method + " have more than one CompletionHandler type parameter");
}
Sncp.checkAsyncModifier(paramClasses[i], method);
handlerFuncIndex = i;
@@ -374,7 +373,7 @@ public final class SncpDynServlet extends SncpServlet {
intconst++;
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, newDynName, "convert", Type.getDescriptor(BsonConvert.class));
mv.visitLdcInsn(Type.getType(Type.getDescriptor(AsyncHandler.class)));
mv.visitLdcInsn(Type.getType(Type.getDescriptor(CompletionHandler.class)));
mv.visitVarInsn(ALOAD, 1);
mv.visitMethodInsn(INVOKEVIRTUAL, convertName, "convertFrom", convertFromDesc, false);
mv.visitInsn(POP);
@@ -384,10 +383,15 @@ public final class SncpDynServlet extends SncpServlet {
mv.visitFieldInsn(GETFIELD, newDynName, "convert", Type.getDescriptor(BsonConvert.class));
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, newDynName, "paramTypes", "[Ljava/lang/reflect/Type;");
if (iconst > ICONST_5) {
if (intconst < 6) {
mv.visitInsn(ICONST_0 + intconst);
} else if (iconst <= Byte.MAX_VALUE) {
mv.visitIntInsn(BIPUSH, intconst);
} else if (iconst <= Short.MAX_VALUE) {
mv.visitIntInsn(SIPUSH, intconst);
} else {
mv.visitInsn(iconst); //
mv.visitLdcInsn(intconst);
}
mv.visitInsn(AALOAD);
mv.visitVarInsn(ALOAD, 1);
@@ -433,21 +437,30 @@ public final class SncpDynServlet extends SncpServlet {
}
if (boolReturnTypeFuture || handlerFuncIndex >= 0) { //调用SncpAsyncHandler.setParams(Object... params)
mv.visitVarInsn(ALOAD, 3);
if (paramClasses.length > 5) {
if (paramClasses.length < 6) {
mv.visitInsn(ICONST_0 + paramClasses.length);
} else if (paramClasses.length <= Byte.MAX_VALUE) {
mv.visitIntInsn(BIPUSH, paramClasses.length);
} else if (paramClasses.length <= Short.MAX_VALUE) {
mv.visitIntInsn(SIPUSH, paramClasses.length);
} else {
mv.visitInsn(paramClasses.length + ICONST_0);
mv.visitLdcInsn(paramClasses.length);
}
mv.visitTypeInsn(ANEWARRAY, "java/lang/Object");
int insn = 3; //action的参数个数
for (int j = 0; j < paramClasses.length; j++) {
final Class pt = paramClasses[j];
mv.visitInsn(DUP);
insn++;
if (j <= 5) {
if (j < 6) {
mv.visitInsn(ICONST_0 + j);
} else {
} else if (j <= Byte.MAX_VALUE) {
mv.visitIntInsn(BIPUSH, j);
} else if (j <= Short.MAX_VALUE) {
mv.visitIntInsn(SIPUSH, j);
} else {
mv.visitLdcInsn(j);
}
if (pt.isPrimitive()) {
if (pt == long.class) {
@@ -499,10 +512,14 @@ public final class SncpDynServlet extends SncpServlet {
//------------------------- _callParameter 方法 --------------------------------
mv.visitVarInsn(ALOAD, 0);
mv.visitVarInsn(ALOAD, 2);
if (paramClasses.length <= 5) { //参数总数量
if (paramClasses.length < 6) { //参数总数量
mv.visitInsn(ICONST_0 + paramClasses.length);
} else {
} else if (paramClasses.length <= Byte.MAX_VALUE) {
mv.visitIntInsn(BIPUSH, paramClasses.length);
} else if (paramClasses.length <= Short.MAX_VALUE) {
mv.visitIntInsn(SIPUSH, paramClasses.length);
} else {
mv.visitLdcInsn(paramClasses.length);
}
mv.visitTypeInsn(ANEWARRAY, "java/lang/Object");
int insn = 3;//action的参数个数
@@ -510,10 +527,14 @@ public final class SncpDynServlet extends SncpServlet {
final Class pt = paramClasses[j];
mv.visitInsn(DUP);
insn++;
if (j <= 5) {
if (j < 6) {
mv.visitInsn(ICONST_0 + j);
} else {
} else if (j <= Byte.MAX_VALUE) {
mv.visitIntInsn(BIPUSH, j);
} else if (j <= Short.MAX_VALUE) {
mv.visitIntInsn(SIPUSH, j);
} else {
mv.visitLdcInsn(j);
}
if (pt.isPrimitive()) {
if (pt == long.class) {

View File

@@ -8,7 +8,6 @@ package org.redkale.net.sncp;
import org.redkale.net.PrepareServlet;
import org.redkale.util.AnyValue;
import java.io.IOException;
import java.nio.ByteBuffer;
import org.redkale.service.Service;
import org.redkale.util.*;
@@ -23,7 +22,6 @@ public class SncpPrepareServlet extends PrepareServlet<DLong, SncpContext, SncpR
private final Object sncplock = new Object();
private static final ByteBuffer pongBuffer = ByteBuffer.wrap("PONG".getBytes()).asReadOnlyBuffer();
@Override
public void addServlet(SncpServlet servlet, Object attachment, AnyValue conf, DLong... mappings) {
@@ -69,7 +67,7 @@ public class SncpPrepareServlet extends PrepareServlet<DLong, SncpContext, SncpR
@Override
public void execute(SncpRequest request, SncpResponse response) throws IOException {
if (request.isPing()) {
response.finish(pongBuffer.duplicate());
response.finish(Sncp.PONG_BUFFER.duplicate());
return;
}
SncpServlet servlet = (SncpServlet) mappingServlet(request.getServiceid());

View File

@@ -7,6 +7,7 @@ package org.redkale.net.sncp;
import java.net.*;
import java.nio.*;
import java.util.logging.Level;
import org.redkale.convert.bson.*;
import org.redkale.net.*;
import org.redkale.util.*;
@@ -58,7 +59,7 @@ public final class SncpRequest extends Request<SncpContext> {
//---------------------head----------------------------------
this.seqid = buffer.getLong();
if (buffer.getChar() != HEADER_SIZE) {
context.getLogger().finest("sncp buffer header.length not " + HEADER_SIZE);
if (context.getLogger().isLoggable(Level.FINEST)) context.getLogger().finest("sncp buffer header.length not " + HEADER_SIZE);
return -1;
}
this.serviceid = DLong.read(buffer);
@@ -68,7 +69,7 @@ public final class SncpRequest extends Request<SncpContext> {
this.bodylength = buffer.getInt();
if (buffer.getInt() != 0) {
context.getLogger().finest("sncp buffer header.retcode not 0");
if (context.getLogger().isLoggable(Level.FINEST)) context.getLogger().finest("sncp buffer header.retcode not 0");
return -1;
}
//---------------------body----------------------------------

View File

@@ -49,6 +49,7 @@ public final class SncpResponse extends Response<SncpContext, SncpRequest> {
super(context, request);
this.addrBytes = context.getServerAddress().getAddress().getAddress();
this.addrPort = context.getServerAddress().getPort();
if (this.addrBytes.length != 4) throw new RuntimeException("SNCP serverAddress only support IPv4");
}
public void finish(final int retcode, final BsonWriter out) {

View File

@@ -10,8 +10,9 @@ import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
/**
* 本地模式注解。
* 声明为Local的Service只能以本地模式存在 即使配置文件中配置成远程模式也将被忽略。
* 本地模式注解。<br>
* 声明为Local的Service只能以本地模式存在 即使配置文件中配置成远程模式也将被忽略。 <br>
* Service里被标记为Local的public方法不会被重载。
*
* <p>
* 详情见: https://redkale.org

View File

@@ -0,0 +1,32 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.service;
import java.lang.annotation.*;
import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
/**
* Service类中临时缓存字段 <br>
*
* <b>注意: </b> 被标记字段的数据必须是可序列化和反序列化的, 且字段不能是static的 如果字段类型不是Map或Collection类型则不能修饰为final
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*/
@Target({FIELD})
@Retention(RUNTIME)
public @interface Persist {
/**
* 临时缓存的超时秒数,超过指定秒数的缓存数据将会被废弃, 0表示不超时 默认超时值为60秒
*
* @return int
*/
int timeout() default 60;
}

View File

@@ -5,6 +5,7 @@
*/
package org.redkale.service;
import java.util.*;
import org.redkale.convert.json.*;
/**
@@ -28,6 +29,8 @@ public class RetResult<T> {
protected T result;
protected Map<String, String> attach;
public RetResult() {
}
@@ -99,6 +102,33 @@ public class RetResult<T> {
return this;
}
/**
* 同 setAttach
*
* @param attach attach
*
* @return RetResult
*/
public RetResult<T> attach(Map<String, String> attach) {
this.attach = attach;
return this;
}
/**
* attach添加元素
*
* @param key String
* @param value String
*
* @return RetResult
*/
public RetResult<T> attach(String key, Object value) {
if (this.attach == null) this.attach = new HashMap<>();
boolean canstr = value != null && (value instanceof CharSequence || value.getClass().isPrimitive());
this.attach.put(key, value == null ? null : (canstr ? String.valueOf(value) : JsonConvert.root().convertTo(value)));
return this;
}
/**
* 结果码 0表示成功、 非0表示错误
*
@@ -148,6 +178,24 @@ public class RetResult<T> {
this.result = result;
}
/**
* 结果附件
*
* @return 结果附件
*/
public Map<String, String> getAttach() {
return attach;
}
/**
* 设置结果附件
*
* @param attach Map
*/
public void setAttach(Map<String, String> attach) {
this.attach = attach;
}
@Override
public String toString() {
return JsonConvert.root().convertTo(this);

View File

@@ -22,10 +22,10 @@ import org.redkale.util.*;
* <blockquote><pre>
* 异步方法:
* Service编写异步方法
* 1、异步方法有且仅有一个类型为AsyncHandler的参数 返回类型必须是void。若参数类型为AsyncHandler子类必须保证其子类可被继承且completed、failed可被重载且包含空参数的构造函数。
* 1、异步方法有且仅有一个类型为CompletionHandler的参数 返回类型必须是void。若参数类型为CompletionHandler子类必须保证其子类可被继承且completed、failed可被重载且包含空参数的构造函数。
* 2、异步方法返回类型是CompletableFuture。
* 例如:
* public void insertRecord(AsyncHandler&#60;Integer, Record&#62; handler, String name, &#64;RpcAttachment Record record);
* public void insertRecord(CompletionHandler&#60;Integer, Record&#62; handler, String name, &#64;RpcAttachment Record record);
*
* </pre></blockquote>
*

View File

@@ -10,6 +10,7 @@ import java.io.*;
import java.net.*;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.logging.Level;
import org.redkale.net.http.*;
import org.redkale.util.*;
@@ -60,31 +61,66 @@ public class WebSocketNodeService extends WebSocketNode implements Service {
/**
* 当用户连接到节点需要更新到CacheSource
*
* @param userid String
* @param userid Serializable
* @param sncpAddr InetSocketAddress
*
* @return 无返回值
*/
@Override
public CompletableFuture<Void> connect(Serializable userid, InetSocketAddress sncpAddr) {
CompletableFuture<Void> future = sncpNodeAddresses.appendSetItemAsync(userid, sncpAddr);
future = future.thenAccept((a) -> sncpNodeAddresses.appendSetItemAsync("redkale_sncpnodes", sncpAddr));
if (finest) logger.finest(WebSocketNodeService.class.getSimpleName() + ".event: " + userid + " connect from " + sncpAddr);
CompletableFuture<Void> future = sncpNodeAddresses.appendSetItemAsync(SOURCE_SNCP_USERID_PREFIX + userid, sncpAddr);
future = future.thenAccept((a) -> sncpNodeAddresses.incr(SOURCE_SNCP_USERCOUNT_KEY));
future = future.thenAccept((a) -> sncpNodeAddresses.appendSetItemAsync(SOURCE_SNCP_ADDRS_KEY, sncpAddr));
if (logger.isLoggable(Level.FINEST)) logger.finest(WebSocketNodeService.class.getSimpleName() + ".event: " + userid + " connect from " + sncpAddr);
return future;
}
/**
* 当用户从一个节点断掉了所有的连接需要从CacheSource中删除
*
* @param userid String
* @param userid Serializable
* @param sncpAddr InetSocketAddress
*
* @return 无返回值
*/
@Override
public CompletableFuture<Void> disconnect(Serializable userid, InetSocketAddress sncpAddr) {
CompletableFuture<Void> future = sncpNodeAddresses.removeSetItemAsync(userid, sncpAddr);
if (finest) logger.finest(WebSocketNodeService.class.getSimpleName() + ".event: " + userid + " disconnect from " + sncpAddr);
CompletableFuture<Void> future = sncpNodeAddresses.removeSetItemAsync(SOURCE_SNCP_USERID_PREFIX + userid, sncpAddr);
future = future.thenAccept((a) -> sncpNodeAddresses.decr(SOURCE_SNCP_USERCOUNT_KEY));
if (logger.isLoggable(Level.FINEST)) logger.finest(WebSocketNodeService.class.getSimpleName() + ".event: " + userid + " disconnect from " + sncpAddr);
return future;
}
/**
* 更改用户ID需要更新到CacheSource
*
* @param olduserid Serializable
* @param newuserid Serializable
* @param sncpAddr InetSocketAddress
*
* @return 无返回值
*/
@Override
public CompletableFuture<Void> changeUserid(Serializable olduserid, Serializable newuserid, InetSocketAddress sncpAddr) {
CompletableFuture<Void> future = sncpNodeAddresses.appendSetItemAsync(SOURCE_SNCP_USERID_PREFIX + newuserid, sncpAddr);
future = future.thenAccept((a) -> sncpNodeAddresses.removeSetItemAsync(SOURCE_SNCP_USERID_PREFIX + olduserid, sncpAddr));
if (logger.isLoggable(Level.FINEST)) logger.finest(WebSocketNodeService.class.getSimpleName() + ".event: " + olduserid + " changeUserid to " + newuserid + " from " + sncpAddr);
return future;
}
/**
* 强制关闭用户的WebSocket
*
* @param userid Serializable
* @param sncpAddr InetSocketAddress
*
* @return 无返回值
*/
@Override
public CompletableFuture<Integer> forceCloseWebSocket(Serializable userid, InetSocketAddress sncpAddr) {
//不能从sncpNodeAddresses中移除因为engine.forceCloseWebSocket 会调用到disconnect
if (logger.isLoggable(Level.FINEST)) logger.finest(WebSocketNodeService.class.getSimpleName() + ".event: " + userid + " forceCloseWebSocket from " + sncpAddr);
if (localEngine == null) return CompletableFuture.completedFuture(0);
return CompletableFuture.completedFuture(localEngine.forceCloseLocalWebSocket(userid));
}
}

View File

@@ -9,6 +9,7 @@ import java.io.*;
import java.lang.reflect.Type;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Consumer;
import java.util.logging.*;
import javax.annotation.Resource;
@@ -20,7 +21,6 @@ import org.redkale.util.*;
/**
* CacheSource的默认实现--内存缓存
*
* @param <K> key类型
* @param <V> value类型
* <p>
* 详情见: https://redkale.org
@@ -30,66 +30,76 @@ import org.redkale.util.*;
@Local
@AutoLoad(false)
@ResourceType(CacheSource.class)
public class CacheMemorySource<K extends Serializable, V extends Object> extends AbstractService implements CacheSource<K, V>, Service, AutoCloseable, Resourcable {
public class CacheMemorySource<V extends Object> extends AbstractService implements CacheSource<V>, Service, AutoCloseable, Resourcable {
private static final Type STRING_ENTRY_TYPE = new TypeToken<CacheEntry<String>>() {
}.getType();
private static final Type LONG_ENTRY_TYPE = new TypeToken<CacheEntry<Long>>() {
}.getType();
private static final Type ATOMIC_ENTRY_TYPE = new TypeToken<CacheEntry<AtomicLong>>() {
}.getType();
@Resource(name = "APP_HOME")
private File home;
@Resource
private JsonConvert defaultConvert;
@Resource(name = "$_convert")
private JsonConvert convert;
private boolean needStore;
private Class keyType;
private Type objValueType;
private Type setValueType;
private Type listValueType;
private ScheduledThreadPoolExecutor scheduler;
private Consumer<CacheEntry> expireHandler;
private final Logger logger = Logger.getLogger(this.getClass().getSimpleName());
protected final ConcurrentHashMap<K, CacheEntry<K, Object>> container = new ConcurrentHashMap<>();
protected final ConcurrentHashMap<String, CacheEntry<Object>> container = new ConcurrentHashMap<>();
@RpcRemote
protected CacheSource<K, V> remoteSource;
protected CacheSource<V> remoteSource;
public CacheMemorySource() {
}
public final CacheMemorySource setStoreType(Class keyType, Class valueType) {
this.keyType = keyType;
@Override
public final void initValueType(Type valueType) {
this.objValueType = valueType;
this.setValueType = TypeToken.createParameterizedType(null, CopyOnWriteArraySet.class, valueType);
this.listValueType = TypeToken.createParameterizedType(null, ConcurrentLinkedQueue.class, valueType);
this.setNeedStore(this.keyType != null && this.keyType != Serializable.class && this.objValueType != null);
return this;
this.initTransient(this.objValueType == null);
}
public final void setNeedStore(boolean needStore) {
this.needStore = needStore;
@Override
public final void initTransient(boolean flag) {
this.needStore = !flag;
}
@Override
public final String getType() {
return "memory";
}
@Override
public void init(AnyValue conf) {
if (this.convert == null) this.convert = this.defaultConvert;
if (this.convert == null) this.convert = JsonConvert.root();
final CacheMemorySource self = this;
AnyValue prop = conf == null ? null : conf.getAnyValue("property");
if (keyType == null && prop != null) {
String storeKeyStr = prop.getValue("key-type");
AnyValue prop = conf == null ? null : conf.getAnyValue("properties");
if (prop != null) {
String storeValueStr = prop.getValue("value-type");
if (storeKeyStr != null && storeValueStr != null) {
if (storeValueStr != null) {
try {
this.setStoreType(Thread.currentThread().getContextClassLoader().loadClass(storeKeyStr), Thread.currentThread().getContextClassLoader().loadClass(storeValueStr));
this.initValueType(Thread.currentThread().getContextClassLoader().loadClass(storeValueStr));
} catch (Throwable e) {
logger.log(Level.SEVERE, self.getClass().getSimpleName() + " load key & value store class (" + storeKeyStr + ", " + storeValueStr + ") error", e);
logger.log(Level.SEVERE, self.getClass().getSimpleName() + " load key & value store class (" + storeValueStr + ") error", e);
}
}
if (prop.getBoolValue("store-ignore", false)) setNeedStore(false);
this.initTransient(prop.getBoolValue("store-ignore", false));
}
String expireHandlerClass = prop == null ? null : prop.getValue("expirehandler");
if (expireHandlerClass != null) {
@@ -105,7 +115,7 @@ public class CacheMemorySource<K extends Serializable, V extends Object> extends
t.setDaemon(true);
return t;
});
final List<K> keys = new ArrayList<>();
final List<String> keys = new ArrayList<>();
scheduler.scheduleWithFixedDelay(() -> {
keys.clear();
int now = (int) (System.currentTimeMillis() / 1000);
@@ -114,12 +124,12 @@ public class CacheMemorySource<K extends Serializable, V extends Object> extends
keys.add(x.key);
}
});
for (K key : keys) {
for (String key : keys) {
CacheEntry entry = container.remove(key);
if (expireHandler != null && entry != null) expireHandler.accept(entry);
}
}, 10, 10, TimeUnit.SECONDS);
logger.finest(self.getClass().getSimpleName() + ":" + self.resourceName() + " start schedule expire executor");
if (logger.isLoggable(Level.FINEST)) logger.finest(self.getClass().getSimpleName() + ":" + self.resourceName() + " start schedule expire executor");
}
if (Sncp.isRemote(self)) return;
@@ -128,22 +138,24 @@ public class CacheMemorySource<K extends Serializable, V extends Object> extends
// TODO
if (this.needStore) {
try {
File store = new File(home, "cache/" + resourceName());
File store = home == null ? new File("cache/" + resourceName()) : new File(home, "cache/" + resourceName());
if (!store.isFile() || !store.canRead()) return;
LineNumberReader reader = new LineNumberReader(new FileReader(store));
if (this.keyType == null) this.keyType = Serializable.class;
if (this.objValueType == null) {
this.objValueType = Object.class;
this.setValueType = TypeToken.createParameterizedType(null, CopyOnWriteArraySet.class, this.objValueType);
this.listValueType = TypeToken.createParameterizedType(null, ConcurrentLinkedQueue.class, this.objValueType);
}
final Type storeObjType = TypeToken.createParameterizedType(null, CacheEntry.class, keyType, objValueType);
final Type storeSetType = TypeToken.createParameterizedType(null, CacheEntry.class, keyType, setValueType);
final Type storeListType = TypeToken.createParameterizedType(null, CacheEntry.class, keyType, listValueType);
if (this.objValueType == null) this.objValueType = Object.class;
final Type storeObjType = TypeToken.createParameterizedType(null, CacheEntry.class, objValueType);
String line;
while ((line = reader.readLine()) != null) {
if (line.isEmpty()) continue;
CacheEntry<K, Object> entry = convert.convertFrom(line.startsWith(CacheEntry.JSON_SET_KEY) ? storeSetType : (line.startsWith(CacheEntry.JSON_LIST_KEY) ? storeListType : storeObjType), line);
Type convertType = storeObjType;
if (line.startsWith("{\"cacheType\":\"" + CacheEntryType.LONG)) {
convertType = LONG_ENTRY_TYPE;
} else if (line.startsWith("{\"cacheType\":\"" + CacheEntryType.STRING)) {
convertType = STRING_ENTRY_TYPE;
} else if (line.startsWith("{\"cacheType\":\"" + CacheEntryType.ATOMIC)) {
convertType = ATOMIC_ENTRY_TYPE;
}
CacheEntry<Object> entry = convert.convertFrom(convertType, line);
if (entry.isExpired()) continue;
if (datasync && container.containsKey(entry.key)) continue; //已经同步了
container.put(entry.key, entry);
@@ -156,27 +168,84 @@ public class CacheMemorySource<K extends Serializable, V extends Object> extends
}
if (remoteSource != null && !Sncp.isRemote(this)) {
SncpClient client = Sncp.getSncpClient((Service) remoteSource);
if (client != null && client.getRemoteGroupTransport() != null) {
if (client != null && client.getRemoteGroupTransport() != null
&& client.getRemoteGroupTransport().getRemoteAddresses().length > 0) {
super.runAsync(() -> {
try {
CompletableFuture<List<CacheEntry<K, Object>>> listFuture = remoteSource.queryListAsync();
CompletableFuture<List<CacheEntry<Object>>> listFuture = remoteSource.queryListAsync();
listFuture.whenComplete((list, exp) -> {
if (exp != null) {
logger.log(Level.FINEST, CacheSource.class.getSimpleName() + "(" + resourceName() + ") queryListAsync error", exp);
if (logger.isLoggable(Level.FINEST)) logger.log(Level.FINEST, CacheSource.class.getSimpleName() + "(" + resourceName() + ") queryListAsync error", exp);
} else {
for (CacheEntry<K, Object> entry : list) {
for (CacheEntry<Object> entry : list) {
container.put(entry.key, entry);
}
}
});
} catch (Exception e) {
logger.log(Level.FINEST, CacheSource.class.getSimpleName() + "(" + resourceName() + ") queryListAsync error, maybe remote node connot connect ", e);
if (logger.isLoggable(Level.FINEST)) logger.log(Level.FINEST, CacheSource.class.getSimpleName() + "(" + resourceName() + ") queryListAsync error, maybe remote node connot connect ", e);
}
});
}
}
}
/**
* public static void main(String[] args) throws Exception {
* AnyValue.DefaultAnyValue conf = new AnyValue.DefaultAnyValue();
* conf.addValue("node", new AnyValue.DefaultAnyValue().addValue("addr", "127.0.0.1").addValue("port", "6379"));
*
* CacheMemorySource source = new CacheMemorySource();
* source.defaultConvert = JsonFactory.root().getConvert();
* source.initValueType(String.class); //value用String类型
* source.initTransient(false);
* source.init(conf);
*
* System.out.println("------------------------------------");
* source.remove("key1");
* source.remove("key2");
* source.remove("300");
* source.set("key1", "value1");
* source.setString("keystr1", "strvalue1");
* source.setLong("keylong1", 333L);
* source.set("300", "4000");
* source.getAndRefresh("key1", 3500);
* System.out.println("[有值] 300 GET : " + source.get("300"));
* System.out.println("[有值] key1 GET : " + source.get("key1"));
* System.out.println("[无值] key2 GET : " + source.get("key2"));
* System.out.println("[有值] keylong1 GET : " + source.getLong("keylong1", 0L));
* System.out.println("[有值] key1 EXISTS : " + source.exists("key1"));
* System.out.println("[无值] key2 EXISTS : " + source.exists("key2"));
*
* source.remove("keys3");
* source.appendListItem("keys3", "vals1");
* source.appendListItem("keys3", "vals2");
* System.out.println("-------- keys3 追加了两个值 --------");
* System.out.println("[两值] keys3 VALUES : " + source.getCollection("keys3"));
* System.out.println("[有值] keys3 EXISTS : " + source.exists("keys3"));
* source.removeListItem("keys3", "vals1");
* System.out.println("[一值] keys3 VALUES : " + source.getCollection("keys3"));
* source.getCollectionAndRefresh("keys3", 3000);
*
* source.remove("sets3");
* source.appendSetItem("sets3", "setvals1");
* source.appendSetItem("sets3", "setvals2");
* source.appendSetItem("sets3", "setvals1");
* System.out.println("[两值] sets3 VALUES : " + source.getCollection("sets3"));
* System.out.println("[有值] sets3 EXISTS : " + source.exists("sets3"));
* source.removeSetItem("sets3", "setvals1");
* System.out.println("[一值] sets3 VALUES : " + source.getCollection("sets3"));
* System.out.println("sets3 大小 : " + source.getCollectionSize("sets3"));
* System.out.println("all keys: " + source.queryKeys());
* System.out.println("newnum 值 : " + source.incr("newnum"));
* System.out.println("newnum 值 : " + source.decr("newnum"));
* System.out.println("------------------------------------");
* source.destroy(null);
* source.init(null);
* System.out.println("all keys: " + source.queryKeys());
* System.out.println("[有值] keylong1 GET : " + source.getLong("keylong1", 0L));
* }
*/
@Override
public void close() throws Exception { //给Application 关闭时调用
destroy(null);
@@ -185,7 +254,7 @@ public class CacheMemorySource<K extends Serializable, V extends Object> extends
@Override
public String resourceName() {
Resource res = this.getClass().getAnnotation(Resource.class);
return res == null ? null : res.name();
return res == null ? "cachememory" : res.name();
}
@Override
@@ -196,12 +265,39 @@ public class CacheMemorySource<K extends Serializable, V extends Object> extends
File store = new File(home, "cache/" + resourceName());
store.getParentFile().mkdirs();
PrintStream stream = new PrintStream(store, "UTF-8");
final Type storeObjType = TypeToken.createParameterizedType(null, CacheEntry.class, keyType, objValueType);
final Type storeSetType = TypeToken.createParameterizedType(null, CacheEntry.class, keyType, setValueType);
final Type storeListType = TypeToken.createParameterizedType(null, CacheEntry.class, keyType, listValueType);
Collection<CacheEntry<K, Object>> entrys = container.values();
final Type storeObjType = TypeToken.createParameterizedType(null, CacheEntry.class, objValueType);
final Type storeSetType = TypeToken.createParameterizedType(null, CacheEntry.class, objValueType);
final Type storeListType = TypeToken.createParameterizedType(null, CacheEntry.class, objValueType);
Collection<CacheEntry<Object>> entrys = container.values();
for (CacheEntry entry : entrys) {
stream.println(convert.convertTo(entry.isSetCacheType() ? storeSetType : (entry.isListCacheType() ? storeListType : storeObjType), entry));
Type convertType = storeObjType;
if (entry.cacheType == CacheEntryType.LONG) {
convertType = LONG_ENTRY_TYPE;
} else if (entry.cacheType == CacheEntryType.STRING) {
convertType = STRING_ENTRY_TYPE;
} else if (entry.cacheType == CacheEntryType.ATOMIC) {
convertType = ATOMIC_ENTRY_TYPE;
} else if (entry.cacheType == CacheEntryType.OBJECT) {
convertType = storeObjType;
} else if (entry.cacheType == CacheEntryType.LONG_LIST) {
convertType = LONG_ENTRY_TYPE;
} else if (entry.cacheType == CacheEntryType.LONG_SET) {
convertType = LONG_ENTRY_TYPE;
} else if (entry.cacheType == CacheEntryType.STRING_LIST) {
convertType = STRING_ENTRY_TYPE;
} else if (entry.cacheType == CacheEntryType.STRING_SET) {
convertType = STRING_ENTRY_TYPE;
} else if (entry.cacheType == CacheEntryType.OBJECT_LIST) {
convertType = storeListType;
} else if (entry.cacheType == CacheEntryType.OBJECT_SET) {
convertType = storeSetType;
}
try {
stream.println(convert.convertTo(convertType, entry));
} catch (Exception ee) {
System.err.println(storeSetType + "-----" + entry);
throw ee;
}
}
container.clear();
stream.close();
@@ -211,7 +307,7 @@ public class CacheMemorySource<K extends Serializable, V extends Object> extends
}
@Override
public boolean exists(K key) {
public boolean exists(String key) {
if (key == null) return false;
CacheEntry entry = container.get(key);
if (entry == null) return false;
@@ -219,46 +315,108 @@ public class CacheMemorySource<K extends Serializable, V extends Object> extends
}
@Override
public CompletableFuture<Boolean> existsAsync(final K key) {
public CompletableFuture<Boolean> existsAsync(final String key) {
return CompletableFuture.supplyAsync(() -> exists(key), getExecutor());
}
@Override
public V get(K key) {
public V get(String key) {
if (key == null) return null;
CacheEntry entry = container.get(key);
if (entry == null || entry.isExpired()) return null;
if (entry.isListCacheType()) return (V) (entry.listValue == null ? null : new ArrayList(entry.listValue));
if (entry.isSetCacheType()) return (V) (entry.setValue == null ? null : new HashSet(entry.setValue));
if (entry.isSetCacheType()) return (V) (entry.csetValue == null ? null : new HashSet(entry.csetValue));
return (V) entry.objectValue;
}
@Override
public CompletableFuture<V> getAsync(final K key) {
public String getString(String key) {
if (key == null) return null;
CacheEntry entry = container.get(key);
if (entry == null || entry.isExpired()) return null;
return (String) entry.objectValue;
}
@Override
public long getLong(String key, long defValue) {
if (key == null) return defValue;
CacheEntry entry = container.get(key);
if (entry == null || entry.isExpired()) return defValue;
return entry.objectValue == null ? defValue : (entry.objectValue instanceof AtomicLong ? ((AtomicLong) entry.objectValue).get() : (Long) entry.objectValue);
}
@Override
public CompletableFuture<V> getAsync(final String key) {
return CompletableFuture.supplyAsync(() -> get(key), getExecutor());
}
@Override
public CompletableFuture<String> getStringAsync(final String key) {
return CompletableFuture.supplyAsync(() -> getString(key), getExecutor());
}
@Override
public CompletableFuture<Long> getLongAsync(final String key, long defValue) {
return CompletableFuture.supplyAsync(() -> getLong(key, defValue), getExecutor());
}
@Override
@RpcMultiRun
public V getAndRefresh(K key, final int expireSeconds) {
public V getAndRefresh(String key, final int expireSeconds) {
if (key == null) return null;
CacheEntry entry = container.get(key);
if (entry == null || entry.isExpired()) return null;
entry.lastAccessed = (int) (System.currentTimeMillis() / 1000);
entry.expireSeconds = expireSeconds;
if (entry.isListCacheType()) return (V) (entry.listValue == null ? null : new ArrayList(entry.listValue));
if (entry.isSetCacheType()) return (V) (entry.setValue == null ? null : new HashSet(entry.setValue));
if (entry.isSetCacheType()) return (V) (entry.csetValue == null ? null : new HashSet(entry.csetValue));
return (V) entry.objectValue;
}
@Override
public CompletableFuture<V> getAndRefreshAsync(final K key, final int expireSeconds) {
@RpcMultiRun
public String getStringAndRefresh(String key, final int expireSeconds) {
if (key == null) return null;
CacheEntry entry = container.get(key);
if (entry == null || entry.isExpired()) return null;
entry.lastAccessed = (int) (System.currentTimeMillis() / 1000);
entry.expireSeconds = expireSeconds;
return (String) entry.objectValue;
}
@Override
@RpcMultiRun
public long getLongAndRefresh(String key, final int expireSeconds, long defValue) {
if (key == null) return defValue;
CacheEntry entry = container.get(key);
if (entry == null || entry.isExpired()) return defValue;
entry.lastAccessed = (int) (System.currentTimeMillis() / 1000);
entry.expireSeconds = expireSeconds;
return entry.objectValue == null ? defValue : (entry.objectValue instanceof AtomicLong ? ((AtomicLong) entry.objectValue).get() : (Long) entry.objectValue);
}
@Override
@RpcMultiRun
public CompletableFuture<V> getAndRefreshAsync(final String key, final int expireSeconds) {
return CompletableFuture.supplyAsync(() -> getAndRefresh(key, expireSeconds), getExecutor());
}
@Override
@RpcMultiRun
public void refresh(K key, final int expireSeconds) {
public CompletableFuture<String> getStringAndRefreshAsync(final String key, final int expireSeconds) {
return CompletableFuture.supplyAsync(() -> getStringAndRefresh(key, expireSeconds), getExecutor());
}
@Override
@RpcMultiRun
public CompletableFuture<Long> getLongAndRefreshAsync(final String key, final int expireSeconds, long defValue) {
return CompletableFuture.supplyAsync(() -> getLongAndRefresh(key, expireSeconds, defValue), getExecutor());
}
@Override
@RpcMultiRun
public void refresh(String key, final int expireSeconds) {
if (key == null) return;
CacheEntry entry = container.get(key);
if (entry == null) return;
@@ -267,17 +425,16 @@ public class CacheMemorySource<K extends Serializable, V extends Object> extends
}
@Override
public CompletableFuture<Void> refreshAsync(final K key, final int expireSeconds) {
@RpcMultiRun
public CompletableFuture<Void> refreshAsync(final String key, final int expireSeconds) {
return CompletableFuture.runAsync(() -> refresh(key, expireSeconds), getExecutor());
}
@Override
@RpcMultiRun
public void set(K key, V value) {
protected void set(CacheEntryType cacheType, String key, Object value) {
if (key == null) return;
CacheEntry entry = container.get(key);
if (entry == null) {
entry = new CacheEntry(CacheEntryType.OBJECT, key, value, null, null);
entry = new CacheEntry(cacheType, key, value, null, null);
container.putIfAbsent(key, entry);
} else {
entry.expireSeconds = 0;
@@ -287,17 +444,46 @@ public class CacheMemorySource<K extends Serializable, V extends Object> extends
}
@Override
public CompletableFuture<Void> setAsync(K key, V value) {
@RpcMultiRun
public void set(String key, V value) {
set(CacheEntryType.OBJECT, key, value);
}
@Override
@RpcMultiRun
public void setString(String key, String value) {
set(CacheEntryType.STRING, key, value);
}
@Override
@RpcMultiRun
public void setLong(String key, long value) {
set(CacheEntryType.LONG, key, value);
}
@Override
@RpcMultiRun
public CompletableFuture<Void> setAsync(String key, V value) {
return CompletableFuture.runAsync(() -> set(key, value), getExecutor());
}
@Override
@RpcMultiRun
public void set(int expireSeconds, K key, V value) {
public CompletableFuture<Void> setStringAsync(String key, String value) {
return CompletableFuture.runAsync(() -> setString(key, value), getExecutor());
}
@Override
@RpcMultiRun
public CompletableFuture<Void> setLongAsync(String key, long value) {
return CompletableFuture.runAsync(() -> setLong(key, value), getExecutor());
}
protected void set(CacheEntryType cacheType, int expireSeconds, String key, Object value) {
if (key == null) return;
CacheEntry entry = container.get(key);
if (entry == null) {
entry = new CacheEntry(CacheEntryType.OBJECT, expireSeconds, key, value, null, null);
entry = new CacheEntry(cacheType, expireSeconds, key, value, null, null);
container.putIfAbsent(key, entry);
} else {
if (expireSeconds > 0) entry.expireSeconds = expireSeconds;
@@ -307,13 +493,44 @@ public class CacheMemorySource<K extends Serializable, V extends Object> extends
}
@Override
public CompletableFuture<Void> setAsync(int expireSeconds, K key, V value) {
@RpcMultiRun
public void set(int expireSeconds, String key, V value) {
set(CacheEntryType.OBJECT, expireSeconds, key, value);
}
@Override
@RpcMultiRun
public void setString(int expireSeconds, String key, String value) {
set(CacheEntryType.STRING, expireSeconds, key, value);
}
@Override
@RpcMultiRun
public void setLong(int expireSeconds, String key, long value) {
set(CacheEntryType.LONG, expireSeconds, key, value);
}
@Override
@RpcMultiRun
public CompletableFuture<Void> setAsync(int expireSeconds, String key, V value) {
return CompletableFuture.runAsync(() -> set(expireSeconds, key, value), getExecutor());
}
@Override
@RpcMultiRun
public void setExpireSeconds(K key, int expireSeconds) {
public CompletableFuture<Void> setStringAsync(int expireSeconds, String key, String value) {
return CompletableFuture.runAsync(() -> setString(expireSeconds, key, value), getExecutor());
}
@Override
@RpcMultiRun
public CompletableFuture<Void> setLongAsync(int expireSeconds, String key, long value) {
return CompletableFuture.runAsync(() -> setLong(expireSeconds, key, value), getExecutor());
}
@Override
@RpcMultiRun
public void setExpireSeconds(String key, int expireSeconds) {
if (key == null) return;
CacheEntry entry = container.get(key);
if (entry == null) return;
@@ -321,61 +538,165 @@ public class CacheMemorySource<K extends Serializable, V extends Object> extends
}
@Override
public CompletableFuture<Void> setExpireSecondsAsync(final K key, final int expireSeconds) {
@RpcMultiRun
public CompletableFuture<Void> setExpireSecondsAsync(final String key, final int expireSeconds) {
return CompletableFuture.runAsync(() -> setExpireSeconds(key, expireSeconds), getExecutor());
}
@Override
@RpcMultiRun
public void remove(K key) {
public void remove(String key) {
if (key == null) return;
container.remove(key);
}
@Override
public CompletableFuture<Void> removeAsync(final K key) {
@RpcMultiRun
public long incr(final String key) {
return incr(key, 1);
}
@Override
@RpcMultiRun
public CompletableFuture<Long> incrAsync(final String key) {
return CompletableFuture.supplyAsync(() -> incr(key), getExecutor());
}
@Override
@RpcMultiRun
public long incr(final String key, long num) {
CacheEntry entry = container.get(key);
if (entry == null) {
synchronized (container) {
entry = container.get(key);
if (entry == null) {
entry = new CacheEntry(CacheEntryType.ATOMIC, key, new AtomicLong(), null, null);
container.put(key, entry);
}
}
}
return ((AtomicLong) entry.objectValue).addAndGet(num);
}
@Override
@RpcMultiRun
public CompletableFuture<Long> incrAsync(final String key, long num) {
return CompletableFuture.supplyAsync(() -> incr(key, num), getExecutor());
}
@Override
@RpcMultiRun
public long decr(final String key) {
return incr(key, -1);
}
@Override
@RpcMultiRun
public CompletableFuture<Long> decrAsync(final String key) {
return CompletableFuture.supplyAsync(() -> decr(key), getExecutor());
}
@Override
@RpcMultiRun
public long decr(final String key, long num) {
return incr(key, -num);
}
@Override
@RpcMultiRun
public CompletableFuture<Long> decrAsync(final String key, long num) {
return CompletableFuture.supplyAsync(() -> decr(key, num), getExecutor());
}
@Override
@RpcMultiRun
public CompletableFuture<Void> removeAsync(final String key) {
return CompletableFuture.runAsync(() -> remove(key), getExecutor());
}
@Override
public Collection<V> getCollection(final K key) {
public Collection<V> getCollection(final String key) {
return (Collection<V>) get(key);
}
@Override
public CompletableFuture<Collection<V>> getCollectionAsync(final K key) {
public Collection<String> getStringCollection(final String key) {
return (Collection<String>) get(key);
}
@Override
public Collection<Long> getLongCollection(final String key) {
return (Collection<Long>) get(key);
}
@Override
public CompletableFuture<Collection<V>> getCollectionAsync(final String key) {
return CompletableFuture.supplyAsync(() -> getCollection(key), getExecutor());
}
@Override
public long getCollectionSize(final K key) {
public CompletableFuture<Collection<String>> getStringCollectionAsync(final String key) {
return CompletableFuture.supplyAsync(() -> getStringCollection(key), getExecutor());
}
@Override
public CompletableFuture<Collection<Long>> getLongCollectionAsync(final String key) {
return CompletableFuture.supplyAsync(() -> getLongCollection(key), getExecutor());
}
@Override
public int getCollectionSize(final String key) {
Collection<V> collection = (Collection<V>) get(key);
return collection == null ? 0 : collection.size();
}
@Override
public CompletableFuture<Long> getCollectionSizeAsync(final K key) {
public CompletableFuture<Integer> getCollectionSizeAsync(final String key) {
return CompletableFuture.supplyAsync(() -> getCollectionSize(key), getExecutor());
}
@Override
public Collection<V> getCollectionAndRefresh(final K key, final int expireSeconds) {
@RpcMultiRun
public Collection<V> getCollectionAndRefresh(final String key, final int expireSeconds) {
return (Collection<V>) getAndRefresh(key, expireSeconds);
}
@Override
public CompletableFuture<Collection<V>> getCollectionAndRefreshAsync(final K key, final int expireSeconds) {
@RpcMultiRun
public Collection<String> getStringCollectionAndRefresh(final String key, final int expireSeconds) {
return (Collection<String>) getAndRefresh(key, expireSeconds);
}
@Override
@RpcMultiRun
public Collection<Long> getLongCollectionAndRefresh(final String key, final int expireSeconds) {
return (Collection<Long>) getAndRefresh(key, expireSeconds);
}
@Override
@RpcMultiRun
public CompletableFuture<Collection<V>> getCollectionAndRefreshAsync(final String key, final int expireSeconds) {
return CompletableFuture.supplyAsync(() -> getCollectionAndRefresh(key, expireSeconds), getExecutor());
}
@Override
@RpcMultiRun
public void appendListItem(K key, V value) {
public CompletableFuture<Collection<String>> getStringCollectionAndRefreshAsync(final String key, final int expireSeconds) {
return CompletableFuture.supplyAsync(() -> getStringCollectionAndRefresh(key, expireSeconds), getExecutor());
}
@Override
@RpcMultiRun
public CompletableFuture<Collection<Long>> getLongCollectionAndRefreshAsync(final String key, final int expireSeconds) {
return CompletableFuture.supplyAsync(() -> getLongCollectionAndRefresh(key, expireSeconds), getExecutor());
}
protected void appendListItem(CacheEntryType cacheType, String key, Object value) {
if (key == null) return;
CacheEntry entry = container.get(key);
if (entry == null || !entry.isListCacheType() || entry.listValue == null) {
ConcurrentLinkedQueue<V> list = new ConcurrentLinkedQueue();
entry = new CacheEntry(CacheEntryType.LIST, key, null, null, list);
ConcurrentLinkedQueue list = new ConcurrentLinkedQueue();
entry = new CacheEntry(cacheType, key, null, null, list);
CacheEntry old = container.putIfAbsent(key, entry);
if (old != null) list = old.listValue;
if (list != null) list.add(value);
@@ -385,13 +706,44 @@ public class CacheMemorySource<K extends Serializable, V extends Object> extends
}
@Override
public CompletableFuture<Void> appendListItemAsync(final K key, final V value) {
@RpcMultiRun
public void appendListItem(String key, V value) {
appendListItem(CacheEntryType.OBJECT_LIST, key, value);
}
@Override
@RpcMultiRun
public void appendStringListItem(String key, String value) {
appendListItem(CacheEntryType.STRING_LIST, key, value);
}
@Override
@RpcMultiRun
public void appendLongListItem(String key, long value) {
appendListItem(CacheEntryType.LONG_LIST, key, value);
}
@Override
@RpcMultiRun
public CompletableFuture<Void> appendListItemAsync(final String key, final V value) {
return CompletableFuture.runAsync(() -> appendListItem(key, value), getExecutor());
}
@Override
@RpcMultiRun
public void removeListItem(K key, V value) {
public CompletableFuture<Void> appendStringListItemAsync(final String key, final String value) {
return CompletableFuture.runAsync(() -> appendStringListItem(key, value), getExecutor());
}
@Override
@RpcMultiRun
public CompletableFuture<Void> appendLongListItemAsync(final String key, final long value) {
return CompletableFuture.runAsync(() -> appendLongListItem(key, value), getExecutor());
}
@Override
@RpcMultiRun
public void removeListItem(String key, V value) {
if (key == null) return;
CacheEntry entry = container.get(key);
if (entry == null || entry.listValue == null) return;
@@ -399,63 +751,163 @@ public class CacheMemorySource<K extends Serializable, V extends Object> extends
}
@Override
public CompletableFuture<Void> removeListItemAsync(final K key, final V value) {
@RpcMultiRun
public void removeStringListItem(String key, String value) {
if (key == null) return;
CacheEntry entry = container.get(key);
if (entry == null || entry.listValue == null) return;
entry.listValue.remove(value);
}
@Override
@RpcMultiRun
public void removeLongListItem(String key, long value) {
if (key == null) return;
CacheEntry entry = container.get(key);
if (entry == null || entry.listValue == null) return;
entry.listValue.remove(value);
}
@Override
@RpcMultiRun
public CompletableFuture<Void> removeListItemAsync(final String key, final V value) {
return CompletableFuture.runAsync(() -> removeListItem(key, value), getExecutor());
}
@Override
@RpcMultiRun
public void appendSetItem(K key, V value) {
public CompletableFuture<Void> removeStringListItemAsync(final String key, final String value) {
return CompletableFuture.runAsync(() -> removeStringListItem(key, value), getExecutor());
}
@Override
@RpcMultiRun
public CompletableFuture<Void> removeLongListItemAsync(final String key, final long value) {
return CompletableFuture.runAsync(() -> removeLongListItem(key, value), getExecutor());
}
protected void appendSetItem(CacheEntryType cacheType, String key, Object value) {
if (key == null) return;
CacheEntry entry = container.get(key);
if (entry == null || !entry.isSetCacheType() || entry.setValue == null) {
CopyOnWriteArraySet<V> set = new CopyOnWriteArraySet();
entry = new CacheEntry(CacheEntryType.SET, key, null, set, null);
if (entry == null || !entry.isSetCacheType() || entry.csetValue == null) {
CopyOnWriteArraySet set = new CopyOnWriteArraySet();
entry = new CacheEntry(cacheType, key, null, set, null);
CacheEntry old = container.putIfAbsent(key, entry);
if (old != null) set = old.setValue;
if (old != null) set = old.csetValue;
if (set != null) set.add(value);
} else {
entry.setValue.add(value);
entry.csetValue.add(value);
}
}
@Override
public CompletableFuture<Void> appendSetItemAsync(final K key, final V value) {
@RpcMultiRun
public void appendSetItem(String key, V value) {
appendSetItem(CacheEntryType.OBJECT_SET, key, value);
}
@Override
@RpcMultiRun
public void appendStringSetItem(String key, String value) {
appendSetItem(CacheEntryType.OBJECT_SET, key, value);
}
@Override
@RpcMultiRun
public void appendLongSetItem(String key, long value) {
appendSetItem(CacheEntryType.OBJECT_SET, key, value);
}
@Override
@RpcMultiRun
public CompletableFuture<Void> appendSetItemAsync(final String key, final V value) {
return CompletableFuture.runAsync(() -> appendSetItem(key, value), getExecutor());
}
@Override
@RpcMultiRun
public void removeSetItem(K key, V value) {
if (key == null) return;
CacheEntry entry = container.get(key);
if (entry == null || entry.setValue == null) return;
entry.setValue.remove(value);
public CompletableFuture<Void> appendStringSetItemAsync(final String key, final String value) {
return CompletableFuture.runAsync(() -> appendStringSetItem(key, value), getExecutor());
}
@Override
public CompletableFuture<Void> removeSetItemAsync(final K key, final V value) {
@RpcMultiRun
public CompletableFuture<Void> appendLongSetItemAsync(final String key, final long value) {
return CompletableFuture.runAsync(() -> appendLongSetItem(key, value), getExecutor());
}
@Override
@RpcMultiRun
public void removeSetItem(String key, V value) {
if (key == null) return;
CacheEntry entry = container.get(key);
if (entry == null || entry.csetValue == null) return;
entry.csetValue.remove(value);
}
@Override
@RpcMultiRun
public void removeStringSetItem(String key, String value) {
if (key == null) return;
CacheEntry entry = container.get(key);
if (entry == null || entry.csetValue == null) return;
entry.csetValue.remove(value);
}
@Override
@RpcMultiRun
public void removeLongSetItem(String key, long value) {
if (key == null) return;
CacheEntry entry = container.get(key);
if (entry == null || entry.csetValue == null) return;
entry.csetValue.remove(value);
}
@Override
@RpcMultiRun
public CompletableFuture<Void> removeSetItemAsync(final String key, final V value) {
return CompletableFuture.runAsync(() -> removeSetItem(key, value), getExecutor());
}
@Override
public List<K> queryKeys() {
@RpcMultiRun
public CompletableFuture<Void> removeStringSetItemAsync(final String key, final String value) {
return CompletableFuture.runAsync(() -> removeStringSetItem(key, value), getExecutor());
}
@Override
@RpcMultiRun
public CompletableFuture<Void> removeLongSetItemAsync(final String key, final long value) {
return CompletableFuture.runAsync(() -> removeLongSetItem(key, value), getExecutor());
}
@Override
public List<String> queryKeys() {
return new ArrayList<>(container.keySet());
}
@Override
public CompletableFuture<List<CacheEntry<K, Object>>> queryListAsync() {
public int getKeySize() {
return container.size();
}
@Override
public CompletableFuture<List<CacheEntry<Object>>> queryListAsync() {
return CompletableFuture.completedFuture(new ArrayList<>(container.values()));
}
@Override
public List<CacheEntry<K, Object>> queryList() {
public List<CacheEntry< Object>> queryList() {
return new ArrayList<>(container.values());
}
@Override
public CompletableFuture<List<K>> queryKeysAsync() {
public CompletableFuture<List<String>> queryKeysAsync() {
return CompletableFuture.completedFuture(new ArrayList<>(container.keySet()));
}
@Override
public CompletableFuture<Integer> getKeySizeAsync() {
return CompletableFuture.completedFuture(container.size());
}
}

View File

@@ -6,7 +6,7 @@
package org.redkale.source;
import java.beans.ConstructorProperties;
import java.io.*;
import java.lang.reflect.Type;
import java.util.*;
import java.util.concurrent.*;
import org.redkale.convert.ConvertColumn;
@@ -14,105 +14,208 @@ import org.redkale.convert.json.JsonFactory;
/**
*
* @param <K> key的类型
* @param <V> value的类型
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*/
public interface CacheSource<K extends Serializable, V extends Object> {
public interface CacheSource<V extends Object> {
public String getType();
public void initValueType(Type valueType);
public void initTransient(boolean flag);
default boolean isOpen() {
return true;
}
public boolean exists(final K key);
public boolean exists(final String key);
public V get(final K key);
public V get(final String key);
public V getAndRefresh(final K key, final int expireSeconds);
public V getAndRefresh(final String key, final int expireSeconds);
public void refresh(final K key, final int expireSeconds);
public void refresh(final String key, final int expireSeconds);
public void set(final K key, final V value);
public void set(final String key, final V value);
public void set(final int expireSeconds, final K key, final V value);
public void set(final int expireSeconds, final String key, final V value);
public void setExpireSeconds(final K key, final int expireSeconds);
public void setExpireSeconds(final String key, final int expireSeconds);
public void remove(final K key);
public void remove(final String key);
public Collection<V> getCollection(final K key);
public long incr(final String key);
public long getCollectionSize(final K key);
public long incr(final String key, long num);
public Collection<V> getCollectionAndRefresh(final K key, final int expireSeconds);
public long decr(final String key);
public void appendListItem(final K key, final V value);
public long decr(final String key, long num);
public void removeListItem(final K key, final V value);
public Collection<V> getCollection(final String key);
public void appendSetItem(final K key, final V value);
public int getCollectionSize(final String key);
public void removeSetItem(final K key, final V value);
public Collection<V> getCollectionAndRefresh(final String key, final int expireSeconds);
public List<K> queryKeys();
public void appendListItem(final String key, final V value);
public List<CacheEntry<K, Object>> queryList();
public void removeListItem(final String key, final V value);
public void appendSetItem(final String key, final V value);
public void removeSetItem(final String key, final V value);
public List<String> queryKeys();
public int getKeySize();
public List<CacheEntry<Object>> queryList();
public String getString(final String key);
public String getStringAndRefresh(final String key, final int expireSeconds);
public void setString(final String key, final String value);
public void setString(final int expireSeconds, final String key, final String value);
public Collection<String> getStringCollection(final String key);
public Collection<String> getStringCollectionAndRefresh(final String key, final int expireSeconds);
public void appendStringListItem(final String key, final String value);
public void removeStringListItem(final String key, final String value);
public void appendStringSetItem(final String key, final String value);
public void removeStringSetItem(final String key, final String value);
public long getLong(final String key, long defValue);
public long getLongAndRefresh(final String key, final int expireSeconds, long defValue);
public void setLong(final String key, final long value);
public void setLong(final int expireSeconds, final String key, final long value);
public Collection<Long> getLongCollection(final String key);
public Collection<Long> getLongCollectionAndRefresh(final String key, final int expireSeconds);
public void appendLongListItem(final String key, final long value);
public void removeLongListItem(final String key, final long value);
public void appendLongSetItem(final String key, final long value);
public void removeLongSetItem(final String key, final long value);
//---------------------- CompletableFuture 异步版 ---------------------------------
public CompletableFuture<Boolean> existsAsync(final K key);
public CompletableFuture<Boolean> existsAsync(final String key);
public CompletableFuture<V> getAsync(final K key);
public CompletableFuture<V> getAsync(final String key);
public CompletableFuture<V> getAndRefreshAsync(final K key, final int expireSeconds);
public CompletableFuture<V> getAndRefreshAsync(final String key, final int expireSeconds);
public CompletableFuture<Void> refreshAsync(final K key, final int expireSeconds);
public CompletableFuture<Void> refreshAsync(final String key, final int expireSeconds);
public CompletableFuture<Void> setAsync(final K key, final V value);
public CompletableFuture<Void> setAsync(final String key, final V value);
public CompletableFuture<Void> setAsync(final int expireSeconds, final K key, final V value);
public CompletableFuture<Void> setAsync(final int expireSeconds, final String key, final V value);
public CompletableFuture<Void> setExpireSecondsAsync(final K key, final int expireSeconds);
public CompletableFuture<Void> setExpireSecondsAsync(final String key, final int expireSeconds);
public CompletableFuture<Void> removeAsync(final K key);
public CompletableFuture<Void> removeAsync(final String key);
public CompletableFuture<Collection<V>> getCollectionAsync(final K key);
public CompletableFuture<Long> incrAsync(final String key);
public CompletableFuture<Long> getCollectionSizeAsync(final K key);
public CompletableFuture<Long> incrAsync(final String key, long num);
public CompletableFuture<Collection<V>> getCollectionAndRefreshAsync(final K key, final int expireSeconds);
public CompletableFuture<Long> decrAsync(final String key);
public CompletableFuture<Void> appendListItemAsync(final K key, final V value);
public CompletableFuture<Long> decrAsync(final String key, long num);
public CompletableFuture<Void> removeListItemAsync(final K key, final V value);
public CompletableFuture<Collection<V>> getCollectionAsync(final String key);
public CompletableFuture<Void> appendSetItemAsync(final K key, final V value);
public CompletableFuture<Integer> getCollectionSizeAsync(final String key);
public CompletableFuture<Void> removeSetItemAsync(final K key, final V value);
public CompletableFuture<Collection<V>> getCollectionAndRefreshAsync(final String key, final int expireSeconds);
public CompletableFuture<List<K>> queryKeysAsync();
public CompletableFuture<Void> appendListItemAsync(final String key, final V value);
public CompletableFuture<List<CacheEntry<K, Object>>> queryListAsync();
public CompletableFuture<Void> removeListItemAsync(final String key, final V value);
public CompletableFuture<Void> appendSetItemAsync(final String key, final V value);
public CompletableFuture<Void> removeSetItemAsync(final String key, final V value);
public CompletableFuture<List<String>> queryKeysAsync();
public CompletableFuture<Integer> getKeySizeAsync();
public CompletableFuture<List<CacheEntry< Object>>> queryListAsync();
public CompletableFuture<String> getStringAsync(final String key);
public CompletableFuture<String> getStringAndRefreshAsync(final String key, final int expireSeconds);
public CompletableFuture<Void> setStringAsync(final String key, final String value);
public CompletableFuture<Void> setStringAsync(final int expireSeconds, final String key, final String value);
public CompletableFuture<Collection<String>> getStringCollectionAsync(final String key);
public CompletableFuture<Collection<String>> getStringCollectionAndRefreshAsync(final String key, final int expireSeconds);
public CompletableFuture<Void> appendStringListItemAsync(final String key, final String value);
public CompletableFuture<Void> removeStringListItemAsync(final String key, final String value);
public CompletableFuture<Void> appendStringSetItemAsync(final String key, final String value);
public CompletableFuture<Void> removeStringSetItemAsync(final String key, final String value);
public CompletableFuture<Long> getLongAsync(final String key, long defValue);
public CompletableFuture<Long> getLongAndRefreshAsync(final String key, final int expireSeconds, long defValue);
public CompletableFuture<Void> setLongAsync(final String key, long value);
public CompletableFuture<Void> setLongAsync(final int expireSeconds, final String key, final long value);
public CompletableFuture<Collection<Long>> getLongCollectionAsync(final String key);
public CompletableFuture<Collection<Long>> getLongCollectionAndRefreshAsync(final String key, final int expireSeconds);
public CompletableFuture<Void> appendLongListItemAsync(final String key, final long value);
public CompletableFuture<Void> removeLongListItemAsync(final String key, final long value);
public CompletableFuture<Void> appendLongSetItemAsync(final String key, final long value);
public CompletableFuture<Void> removeLongSetItemAsync(final String key, final long value);
default CompletableFuture<Boolean> isOpenAsync() {
return CompletableFuture.completedFuture(true);
return CompletableFuture.completedFuture(isOpen());
}
public static enum CacheEntryType {
OBJECT, SET, LIST;
LONG, STRING, OBJECT, ATOMIC,
LONG_SET, STRING_SET, OBJECT_SET,
LONG_LIST, STRING_LIST, OBJECT_LIST;
}
public static final class CacheEntry<K extends Serializable, T> {
static final String JSON_SET_KEY = "{\"cacheType\":\"" + CacheEntryType.SET + "\"";
static final String JSON_LIST_KEY = "{\"cacheType\":\"" + CacheEntryType.LIST + "\"";
public static final class CacheEntry<T> {
final CacheEntryType cacheType;
final K key;
final String key;
//<=0表示永久保存
int expireSeconds;
@@ -121,26 +224,26 @@ public interface CacheSource<K extends Serializable, V extends Object> {
T objectValue;
CopyOnWriteArraySet<T> setValue;
CopyOnWriteArraySet<T> csetValue;
ConcurrentLinkedQueue<T> listValue;
public CacheEntry(CacheEntryType cacheType, K key, T objectValue, CopyOnWriteArraySet<T> setValue, ConcurrentLinkedQueue<T> listValue) {
this(cacheType, 0, key, objectValue, setValue, listValue);
public CacheEntry(CacheEntryType cacheType, String key, T objectValue, CopyOnWriteArraySet<T> csetValue, ConcurrentLinkedQueue<T> listValue) {
this(cacheType, 0, key, objectValue, csetValue, listValue);
}
public CacheEntry(CacheEntryType cacheType, int expireSeconds, K key, T objectValue, CopyOnWriteArraySet<T> setValue, ConcurrentLinkedQueue<T> listValue) {
this(cacheType, expireSeconds, (int) (System.currentTimeMillis() / 1000), key, objectValue, setValue, listValue);
public CacheEntry(CacheEntryType cacheType, int expireSeconds, String key, T objectValue, CopyOnWriteArraySet<T> csetValue, ConcurrentLinkedQueue<T> listValue) {
this(cacheType, expireSeconds, (int) (System.currentTimeMillis() / 1000), key, objectValue, csetValue, listValue);
}
@ConstructorProperties({"cacheType", "expireSeconds", "lastAccessed", "key", "objectValue", "setValue", "listValue"})
public CacheEntry(CacheEntryType cacheType, int expireSeconds, int lastAccessed, K key, T objectValue, CopyOnWriteArraySet<T> setValue, ConcurrentLinkedQueue<T> listValue) {
@ConstructorProperties({"cacheType", "expireSeconds", "lastAccessed", "key", "objectValue", "csetValue", "listValue"})
public CacheEntry(CacheEntryType cacheType, int expireSeconds, int lastAccessed, String key, T objectValue, CopyOnWriteArraySet<T> csetValue, ConcurrentLinkedQueue<T> listValue) {
this.cacheType = cacheType;
this.expireSeconds = expireSeconds;
this.lastAccessed = lastAccessed;
this.key = key;
this.objectValue = objectValue;
this.setValue = setValue;
this.csetValue = csetValue;
this.listValue = listValue;
}
@@ -151,12 +254,12 @@ public interface CacheSource<K extends Serializable, V extends Object> {
@ConvertColumn(ignore = true)
public boolean isListCacheType() {
return cacheType == CacheEntryType.LIST;
return cacheType == CacheEntryType.LONG_LIST || cacheType == CacheEntryType.STRING_LIST || cacheType == CacheEntryType.OBJECT_LIST;
}
@ConvertColumn(ignore = true)
public boolean isSetCacheType() {
return cacheType == CacheEntryType.SET;
return cacheType == CacheEntryType.LONG_SET || cacheType == CacheEntryType.STRING_SET || cacheType == CacheEntryType.OBJECT_SET;
}
@ConvertColumn(ignore = true)
@@ -176,7 +279,7 @@ public interface CacheSource<K extends Serializable, V extends Object> {
return lastAccessed;
}
public K getKey() {
public String getKey() {
return key;
}
@@ -184,8 +287,8 @@ public interface CacheSource<K extends Serializable, V extends Object> {
return objectValue;
}
public CopyOnWriteArraySet<T> getSetValue() {
return setValue;
public CopyOnWriteArraySet<T> getCsetValue() {
return csetValue;
}
public ConcurrentLinkedQueue<T> getListValue() {

View File

@@ -10,11 +10,12 @@ import java.net.URL;
import java.sql.*;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.*;
import java.util.logging.*;
import java.util.stream.Stream;
import javax.annotation.Resource;
import org.redkale.service.*;
import static org.redkale.source.DataSources.*;
import org.redkale.util.*;
/**
@@ -29,35 +30,77 @@ import org.redkale.util.*;
@AutoLoad(false)
@SuppressWarnings("unchecked")
@ResourceType(DataSource.class)
public class DataJdbcSource extends AbstractService implements DataSource, Service, DataCacheListener, Function<Class, EntityInfo>, AutoCloseable, Resourcable {
public class DataJdbcSource extends AbstractService implements DataSource, DataCacheListener, Function<Class, EntityInfo>, AutoCloseable, Resourcable {
private static final Flipper FLIPPER_ONE = new Flipper(1);
protected static final Flipper FLIPPER_ONE = new Flipper(1);
final Logger logger = Logger.getLogger(DataJdbcSource.class.getSimpleName());
protected final Logger logger = Logger.getLogger(DataJdbcSource.class.getSimpleName());
final AtomicBoolean debug = new AtomicBoolean(logger.isLoggable(Level.FINEST));
protected String name;
final String name;
protected URL conf;
final URL conf;
protected boolean cacheForbidden;
final boolean cacheForbidden;
protected PoolJdbcSource readPool;
private final PoolJdbcSource readPool;
private final PoolJdbcSource writePool;
protected PoolJdbcSource writePool;
@Resource(name = "$")
private DataCacheListener cacheListener;
protected DataCacheListener cacheListener;
private final BiFunction<DataSource, Class, List> fullloader = (s, t) -> querySheet(false, false, t, null, null, (FilterNode) null).list(true);
protected final BiFunction<DataSource, Class, List> fullloader = (s, t) -> querySheet(false, false, t, null, null, (FilterNode) null).list(true);
public DataJdbcSource(String unitName, Properties readprop, Properties writeprop) {
this.preConstruct(unitName, readprop, writeprop);
this.initByProperties(unitName, readprop, writeprop);
}
public DataJdbcSource() {
}
@Override
public void init(AnyValue config) { //通过空构造函数创建的对象需要调用init方法进行初始化
String unitName = config.getValue("name");
Properties readprop = new Properties();
Properties writeprop = new Properties();
for (AnyValue confs : config.getAnyValues("properties")) {
boolean write = confs.getValue("name", "").contains("write");
for (AnyValue conf : confs.getAnyValues("property")) {
String pn = conf.getValue("name");
String pv = conf.getValue("value");
if (pn == null || pv == null) continue;
(write ? writeprop : readprop).put(pn, pv);
}
}
for (AnyValue conf : config.getAnyValues("property")) {
String pn = conf.getValue("name");
String pv = conf.getValue("value");
if (pn == null || pv == null) continue;
readprop.put(pn, pv);
}
if (writeprop.isEmpty()) writeprop = readprop;
this.initByProperties(unitName, readprop, writeprop);
}
//构造前调用
protected void preConstruct(String unitName, Properties readprop, Properties writeprop) {
}
protected void initByProperties(String unitName, Properties readprop, Properties writeprop) {
this.name = unitName;
this.conf = null;
this.readPool = new PoolJdbcSource(this, "read", readprop);
this.writePool = new PoolJdbcSource(this, "write", writeprop);
this.cacheForbidden = "NONE".equalsIgnoreCase(readprop.getProperty("shared-cache-mode"));
this.cacheForbidden = "NONE".equalsIgnoreCase(readprop.getProperty(JDBC_CACHE_MODE));
}
@Local
@Override
public String getType() {
return "jdbc";
}
@Override
@@ -71,15 +114,23 @@ public class DataJdbcSource extends AbstractService implements DataSource, Servi
writePool.close();
}
private Connection createReadSQLConnection() {
public PoolJdbcSource getReadPoolJdbcSource() {
return readPool;
}
public PoolJdbcSource getWritePoolJdbcSource() {
return writePool;
}
public Connection createReadSQLConnection() {
return readPool.poll();
}
private <T> Connection createWriteSQLConnection() {
public <T> Connection createWriteSQLConnection() {
return writePool.poll();
}
private void closeSQLConnection(final Connection sqlconn) {
public void closeSQLConnection(final Connection sqlconn) {
if (sqlconn == null) return;
try {
sqlconn.close();
@@ -93,7 +144,7 @@ public class DataJdbcSource extends AbstractService implements DataSource, Servi
return loadEntityInfo(t);
}
private <T> EntityInfo<T> loadEntityInfo(Class<T> clazz) {
protected <T> EntityInfo<T> loadEntityInfo(Class<T> clazz) {
return EntityInfo.load(clazz, this.cacheForbidden, this.readPool.props, this, fullloader);
}
@@ -150,7 +201,7 @@ public class DataJdbcSource extends AbstractService implements DataSource, Servi
return CompletableFuture.runAsync(() -> insert(values), getExecutor());
}
private <T> void insert(final Connection conn, final EntityInfo<T> info, T... values) {
protected <T> void insert(final Connection conn, final EntityInfo<T> info, T... values) {
if (values.length == 0) return;
try {
if (!info.isVirtualEntity()) {
@@ -217,7 +268,7 @@ public class DataJdbcSource extends AbstractService implements DataSource, Servi
}
prestmt.close();
//------------------------------------------------------------
if (debug.get() && info.isLoggable(Level.FINEST)) { //打印调试信息
if (info.isLoggable(logger, Level.FINEST)) { //打印调试信息
char[] sqlchars = sql.toCharArray();
for (final T value : values) {
//-----------------------------
@@ -251,7 +302,7 @@ public class DataJdbcSource extends AbstractService implements DataSource, Servi
}
}
private <T> PreparedStatement createInsertPreparedStatement(final Connection conn, final String sql,
protected <T> PreparedStatement createInsertPreparedStatement(final Connection conn, final String sql,
final EntityInfo<T> info, T... values) throws SQLException {
Attribute<T, Serializable>[] attrs = info.insertAttributes;
final PreparedStatement prestmt = info.autoGenerated ? conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS) : conn.prepareStatement(sql);
@@ -328,7 +379,7 @@ public class DataJdbcSource extends AbstractService implements DataSource, Servi
return CompletableFuture.supplyAsync(() -> delete(values), getExecutor());
}
private <T> int delete(final Connection conn, final EntityInfo<T> info, T... values) {
protected <T> int delete(final Connection conn, final EntityInfo<T> info, T... values) {
if (values.length == 0) return -1;
final Attribute primary = info.getPrimary();
Serializable[] ids = new Serializable[values.length];
@@ -358,7 +409,7 @@ public class DataJdbcSource extends AbstractService implements DataSource, Servi
return CompletableFuture.supplyAsync(() -> delete(clazz, ids), getExecutor());
}
private <T> int delete(final Connection conn, final EntityInfo<T> info, Serializable... keys) {
protected <T> int delete(final Connection conn, final EntityInfo<T> info, Serializable... keys) {
if (keys.length == 0) return -1;
int c = -1;
int c2 = 0;
@@ -368,7 +419,7 @@ public class DataJdbcSource extends AbstractService implements DataSource, Servi
final Statement stmt = conn.createStatement();
for (Serializable key : keys) {
String sql = "DELETE FROM " + info.getTable(key) + " WHERE " + info.getPrimarySQLColumn() + " = " + FilterNode.formatToString(key);
if (debug.get() && info.isLoggable(Level.FINEST)) logger.finest(info.getType().getSimpleName() + " delete sql=" + sql);
if (info.isLoggable(logger, Level.FINEST)) logger.finest(info.getType().getSimpleName() + " delete sql=" + sql);
stmt.addBatch(sql);
}
int[] pc = stmt.executeBatch();
@@ -430,7 +481,7 @@ public class DataJdbcSource extends AbstractService implements DataSource, Servi
return CompletableFuture.supplyAsync(() -> delete(clazz, flipper, node), getExecutor());
}
private <T> int delete(final Connection conn, final EntityInfo<T> info, final Flipper flipper, final FilterNode node) {
protected <T> int delete(final Connection conn, final EntityInfo<T> info, final Flipper flipper, final FilterNode node) {
int c = -1;
try {
if (!info.isVirtualEntity()) {
@@ -449,7 +500,7 @@ public class DataJdbcSource extends AbstractService implements DataSource, Servi
+ ((where == null || where.length() == 0) ? (join2 == null ? "" : (" WHERE " + join2))
: (" WHERE " + where + (join2 == null ? "" : (" AND " + join2)))) + info.createSQLOrderby(flipper)
+ ((flipper == null || flipper.getLimit() < 1) ? "" : (" LIMIT " + flipper.getLimit()));
if (debug.get() && info.isLoggable(Level.FINEST)) logger.finest(info.getType().getSimpleName() + " delete sql=" + sql);
if (info.isLoggable(logger, Level.FINEST)) logger.finest(info.getType().getSimpleName() + " delete sql=" + sql);
conn.setReadOnly(false);
final Statement stmt = conn.createStatement();
c = stmt.executeUpdate(sql);
@@ -521,7 +572,7 @@ public class DataJdbcSource extends AbstractService implements DataSource, Servi
return CompletableFuture.supplyAsync(() -> update(values), getExecutor());
}
private <T> int update(final Connection conn, final EntityInfo<T> info, T... values) {
protected <T> int update(final Connection conn, final EntityInfo<T> info, T... values) {
try {
Class clazz = info.getType();
int c = -1;
@@ -531,7 +582,7 @@ public class DataJdbcSource extends AbstractService implements DataSource, Servi
conn.setReadOnly(false);
final PreparedStatement prestmt = conn.prepareStatement(updateSQL);
Attribute<T, Serializable>[] attrs = info.updateAttributes;
final boolean debugfinest = debug.get() && info.isLoggable(Level.FINEST);
final boolean debugfinest = info.isLoggable(logger, Level.FINEST);
char[] sqlchars = debugfinest ? updateSQL.toCharArray() : null;
for (final T value : values) {
int k = 0;
@@ -617,13 +668,13 @@ public class DataJdbcSource extends AbstractService implements DataSource, Servi
return CompletableFuture.supplyAsync(() -> updateColumn(clazz, id, column, value), getExecutor());
}
private <T> int updateColumn(Connection conn, final EntityInfo<T> info, Serializable id, String column, final Serializable value) {
protected <T> int updateColumn(Connection conn, final EntityInfo<T> info, Serializable id, String column, final Serializable value) {
try {
int c = -1;
if (!info.isVirtualEntity()) {
if (value instanceof byte[]) {
String sql = "UPDATE " + info.getTable(id) + " SET " + info.getSQLColumn(null, column) + " = ? WHERE " + info.getPrimarySQLColumn() + " = " + FilterNode.formatToString(id);
if (debug.get() && info.isLoggable(Level.FINEST)) logger.finest(info.getType().getSimpleName() + " update sql=" + sql);
if (info.isLoggable(logger, Level.FINEST)) logger.finest(info.getType().getSimpleName() + " update sql=" + sql);
conn.setReadOnly(false);
final PreparedStatement stmt = conn.prepareStatement(sql);
Blob blob = conn.createBlob();
@@ -634,7 +685,7 @@ public class DataJdbcSource extends AbstractService implements DataSource, Servi
} else {
String sql = "UPDATE " + info.getTable(id) + " SET " + info.getSQLColumn(null, column) + " = "
+ info.formatToString(value) + " WHERE " + info.getPrimarySQLColumn() + " = " + FilterNode.formatToString(id);
if (debug.get() && info.isLoggable(Level.FINEST)) logger.finest(info.getType().getSimpleName() + " update sql=" + sql);
if (info.isLoggable(logger, Level.FINEST)) logger.finest(info.getType().getSimpleName() + " update sql=" + sql);
conn.setReadOnly(false);
final Statement stmt = conn.createStatement();
c = stmt.executeUpdate(sql);
@@ -684,7 +735,7 @@ public class DataJdbcSource extends AbstractService implements DataSource, Servi
return CompletableFuture.supplyAsync(() -> updateColumn(clazz, column, value, node), getExecutor());
}
private <T> int updateColumn(Connection conn, final EntityInfo<T> info, String column, final Serializable value, FilterNode node) {
protected <T> int updateColumn(Connection conn, final EntityInfo<T> info, String column, final Serializable value, FilterNode node) {
try {
int c = -1;
if (!info.isVirtualEntity()) {
@@ -704,7 +755,7 @@ public class DataJdbcSource extends AbstractService implements DataSource, Servi
+ " SET " + info.getSQLColumn("a", column) + " = ?"
+ ((where == null || where.length() == 0) ? (join2 == null ? "" : (" WHERE " + join2))
: (" WHERE " + where + (join2 == null ? "" : (" AND " + join2))));
if (debug.get() && info.isLoggable(Level.FINEST)) logger.finest(info.getType().getSimpleName() + " update sql=" + sql);
if (info.isLoggable(logger, Level.FINEST)) logger.finest(info.getType().getSimpleName() + " update sql=" + sql);
conn.setReadOnly(false);
Blob blob = conn.createBlob();
blob.setBytes(1, (byte[]) value);
@@ -717,7 +768,7 @@ public class DataJdbcSource extends AbstractService implements DataSource, Servi
+ " SET " + info.getSQLColumn("a", column) + " = " + info.formatToString(value)
+ ((where == null || where.length() == 0) ? (join2 == null ? "" : (" WHERE " + join2))
: (" WHERE " + where + (join2 == null ? "" : (" AND " + join2))));
if (debug.get() && info.isLoggable(Level.FINEST)) logger.finest(info.getType().getSimpleName() + " update sql=" + sql);
if (info.isLoggable(logger, Level.FINEST)) logger.finest(info.getType().getSimpleName() + " update sql=" + sql);
conn.setReadOnly(false);
final Statement stmt = conn.createStatement();
c = stmt.executeUpdate(sql);
@@ -766,7 +817,7 @@ public class DataJdbcSource extends AbstractService implements DataSource, Servi
return CompletableFuture.supplyAsync(() -> updateColumn(clazz, id, values), getExecutor());
}
private <T> int updateColumn(final Connection conn, final EntityInfo<T> info, final Serializable id, final ColumnValue... values) {
protected <T> int updateColumn(final Connection conn, final EntityInfo<T> info, final Serializable id, final ColumnValue... values) {
if (values == null || values.length < 1) return -1;
try {
StringBuilder setsql = new StringBuilder();
@@ -794,7 +845,7 @@ public class DataJdbcSource extends AbstractService implements DataSource, Servi
int c = -1;
if (!virtual) {
String sql = "UPDATE " + info.getTable(id) + " SET " + setsql + " WHERE " + info.getPrimarySQLColumn() + " = " + FilterNode.formatToString(id);
if (debug.get() && info.isLoggable(Level.FINEST)) logger.finest(info.getType().getSimpleName() + ": " + sql);
if (info.isLoggable(logger, Level.FINEST)) logger.finest(info.getType().getSimpleName() + ": " + sql);
conn.setReadOnly(false);
if (blobs != null) {
final PreparedStatement stmt = conn.prepareStatement(sql);
@@ -882,7 +933,7 @@ public class DataJdbcSource extends AbstractService implements DataSource, Servi
return CompletableFuture.supplyAsync(() -> updateColumn(clazz, node, flipper, values), getExecutor());
}
private <T> int updateColumn(final Connection conn, final EntityInfo<T> info, final FilterNode node, final Flipper flipper, final ColumnValue... values) {
protected <T> int updateColumn(final Connection conn, final EntityInfo<T> info, final FilterNode node, final Flipper flipper, final ColumnValue... values) {
if (values == null || values.length < 1) return -1;
try {
StringBuilder setsql = new StringBuilder();
@@ -924,7 +975,7 @@ public class DataJdbcSource extends AbstractService implements DataSource, Servi
: (" WHERE " + where + (join2 == null ? "" : (" AND " + join2))));
//注LIMIT 仅支持MySQL 且在多表关联式会异常, 该BUG尚未解决
sql += info.createSQLOrderby(flipper) + ((flipper == null || flipper.getLimit() < 1) ? "" : (" LIMIT " + flipper.getLimit()));
if (debug.get() && info.isLoggable(Level.FINEST)) logger.finest(info.getType().getSimpleName() + " update sql=" + sql);
if (info.isLoggable(logger, Level.FINEST)) logger.finest(info.getType().getSimpleName() + " update sql=" + sql);
conn.setReadOnly(false);
if (blobs != null) {
final PreparedStatement stmt = conn.prepareStatement(sql);
@@ -992,7 +1043,7 @@ public class DataJdbcSource extends AbstractService implements DataSource, Servi
return CompletableFuture.supplyAsync(() -> updateColumn(bean, selects), getExecutor());
}
private <T> int updateColumns(final Connection conn, final EntityInfo<T> info, final T bean, final SelectColumn selects) {
protected <T> int updateColumns(final Connection conn, final EntityInfo<T> info, final T bean, final SelectColumn selects) {
if (bean == null || selects == null) return -1;
try {
final Class<T> clazz = (Class<T>) bean.getClass();
@@ -1020,7 +1071,7 @@ public class DataJdbcSource extends AbstractService implements DataSource, Servi
int c = -1;
if (!virtual) {
String sql = "UPDATE " + info.getTable(id) + " SET " + setsql + " WHERE " + info.getPrimarySQLColumn() + " = " + FilterNode.formatToString(id);
if (debug.get() && info.isLoggable(Level.FINEST)) logger.finest(bean.getClass().getSimpleName() + ": " + sql);
if (info.isLoggable(logger, Level.FINEST)) logger.finest(bean.getClass().getSimpleName() + ": " + sql);
conn.setReadOnly(false);
if (blobs != null) {
final PreparedStatement stmt = conn.prepareStatement(sql);
@@ -1068,7 +1119,7 @@ public class DataJdbcSource extends AbstractService implements DataSource, Servi
return CompletableFuture.supplyAsync(() -> updateColumn(bean, node, selects), getExecutor());
}
private <T> int updateColumns(final Connection conn, final EntityInfo<T> info, final T bean, final FilterNode node, final SelectColumn selects) {
protected <T> int updateColumns(final Connection conn, final EntityInfo<T> info, final T bean, final FilterNode node, final SelectColumn selects) {
if (bean == null || node == null || selects == null) return -1;
try {
final Class<T> clazz = (Class<T>) bean.getClass();
@@ -1108,7 +1159,7 @@ public class DataJdbcSource extends AbstractService implements DataSource, Servi
String sql = "UPDATE " + info.getTable(node) + " a " + (join1 == null ? "" : (", " + join1)) + " SET " + setsql
+ ((where == null || where.length() == 0) ? (join2 == null ? "" : (" WHERE " + join2))
: (" WHERE " + where + (join2 == null ? "" : (" AND " + join2))));
if (debug.get() && info.isLoggable(Level.FINEST)) logger.finest(info.getType().getSimpleName() + " update sql=" + sql);
if (info.isLoggable(logger, Level.FINEST)) logger.finest(info.getType().getSimpleName() + " update sql=" + sql);
conn.setReadOnly(false);
if (blobs != null) {
final PreparedStatement stmt = conn.prepareStatement(sql);
@@ -1271,7 +1322,7 @@ public class DataJdbcSource extends AbstractService implements DataSource, Servi
}
final String sql = "SELECT " + sb + " FROM " + info.getTable(node) + " a"
+ (join == null ? "" : join) + ((where == null || where.length() == 0) ? "" : (" WHERE " + where));
if (debug.get() && info.isLoggable(Level.FINEST)) logger.finest(entityClass.getSimpleName() + " single sql=" + sql);
if (info.isLoggable(logger, Level.FINEST)) logger.finest(entityClass.getSimpleName() + " single sql=" + sql);
conn.setReadOnly(true);
final PreparedStatement prestmt = conn.prepareStatement(sql);
@@ -1327,7 +1378,7 @@ public class DataJdbcSource extends AbstractService implements DataSource, Servi
final CharSequence where = node == null ? null : node.createSQLExpress(info, joinTabalis);
final String sql = "SELECT " + func.getColumn((column == null || column.isEmpty() ? "*" : info.getSQLColumn("a", column))) + " FROM " + info.getTable(node) + " a"
+ (join == null ? "" : join) + ((where == null || where.length() == 0) ? "" : (" WHERE " + where));
if (debug.get() && info.isLoggable(Level.FINEST)) logger.finest(entityClass.getSimpleName() + " single sql=" + sql);
if (info.isLoggable(logger, Level.FINEST)) logger.finest(entityClass.getSimpleName() + " single sql=" + sql);
conn.setReadOnly(true);
final PreparedStatement prestmt = conn.prepareStatement(sql);
Number rs = defVal;
@@ -1386,7 +1437,7 @@ public class DataJdbcSource extends AbstractService implements DataSource, Servi
final CharSequence where = node == null ? null : node.createSQLExpress(info, joinTabalis);
final String sql = "SELECT a." + sqlkey + ", " + func.getColumn((funcColumn == null || funcColumn.isEmpty() ? "*" : info.getSQLColumn("a", funcColumn)))
+ " FROM " + info.getTable(node) + " a" + (join == null ? "" : join) + ((where == null || where.length() == 0) ? "" : (" WHERE " + where)) + " GROUP BY a." + sqlkey;
if (debug.get() && info.isLoggable(Level.FINEST)) logger.finest(entityClass.getSimpleName() + " single sql=" + sql);
if (info.isLoggable(logger, Level.FINEST)) logger.finest(entityClass.getSimpleName() + " single sql=" + sql);
conn.setReadOnly(true);
final PreparedStatement prestmt = conn.prepareStatement(sql);
Map<K, N> rs = new LinkedHashMap<>();
@@ -1444,8 +1495,8 @@ public class DataJdbcSource extends AbstractService implements DataSource, Servi
final Connection conn = createReadSQLConnection();
try {
final SelectColumn sels = selects;
final String sql = "SELECT * FROM " + info.getTable(pk) + " WHERE " + info.getPrimarySQLColumn() + " = " + FilterNode.formatToString(pk);
if (debug.get() && info.isLoggable(Level.FINEST)) logger.finest(clazz.getSimpleName() + " find sql=" + sql);
final String sql = "SELECT " + info.getQueryColumns(null, selects) + " FROM " + info.getTable(pk) + " WHERE " + info.getPrimarySQLColumn() + " = " + FilterNode.formatToString(pk);
if (info.isLoggable(logger, Level.FINEST)) logger.finest(clazz.getSimpleName() + " find sql=" + sql);
conn.setReadOnly(true);
final PreparedStatement ps = conn.prepareStatement(sql, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
ps.setFetchSize(1);
@@ -1521,8 +1572,8 @@ public class DataJdbcSource extends AbstractService implements DataSource, Servi
final Map<Class, String> joinTabalis = node == null ? null : node.getJoinTabalis();
final CharSequence join = node == null ? null : node.createSQLJoin(this, false, joinTabalis, new HashSet<>(), info);
final CharSequence where = node == null ? null : node.createSQLExpress(info, joinTabalis);
final String sql = "SELECT a.* FROM " + info.getTable(node) + " a" + (join == null ? "" : join) + ((where == null || where.length() == 0) ? "" : (" WHERE " + where));
if (debug.get() && info.isLoggable(Level.FINEST)) logger.finest(clazz.getSimpleName() + " find sql=" + sql);
final String sql = "SELECT " + info.getQueryColumns("a", selects) + " FROM " + info.getTable(node) + " a" + (join == null ? "" : join) + ((where == null || where.length() == 0) ? "" : (" WHERE " + where));
if (info.isLoggable(logger, Level.FINEST)) logger.finest(clazz.getSimpleName() + " find sql=" + sql);
conn.setReadOnly(true);
final PreparedStatement ps = conn.prepareStatement(sql, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
ps.setFetchSize(1);
@@ -1589,7 +1640,7 @@ public class DataJdbcSource extends AbstractService implements DataSource, Servi
try {
final Attribute<T, Serializable> attr = info.getAttribute(column);
final String sql = "SELECT " + info.getSQLColumn(null, column) + " FROM " + info.getTable(pk) + " WHERE " + info.getPrimarySQLColumn() + " = " + FilterNode.formatToString(pk);
if (debug.get() && info.isLoggable(Level.FINEST)) logger.finest(clazz.getSimpleName() + " find sql=" + sql);
if (info.isLoggable(logger, Level.FINEST)) logger.finest(clazz.getSimpleName() + " find sql=" + sql);
conn.setReadOnly(true);
final PreparedStatement ps = conn.prepareStatement(sql, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
ps.setFetchSize(1);
@@ -1644,7 +1695,7 @@ public class DataJdbcSource extends AbstractService implements DataSource, Servi
final CharSequence join = node == null ? null : node.createSQLJoin(this, false, joinTabalis, new HashSet<>(), info);
final CharSequence where = node == null ? null : node.createSQLExpress(info, joinTabalis);
final String sql = "SELECT " + info.getSQLColumn("a", column) + " FROM " + info.getTable(node) + " a" + (join == null ? "" : join) + ((where == null || where.length() == 0) ? "" : (" WHERE " + where));
if (debug.get() && info.isLoggable(Level.FINEST)) logger.finest(clazz.getSimpleName() + " find sql=" + sql);
if (info.isLoggable(logger, Level.FINEST)) logger.finest(clazz.getSimpleName() + " find sql=" + sql);
conn.setReadOnly(true);
final PreparedStatement ps = conn.prepareStatement(sql, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
ps.setFetchSize(1);
@@ -1686,7 +1737,7 @@ public class DataJdbcSource extends AbstractService implements DataSource, Servi
}
final Connection conn = createReadSQLConnection();
final boolean log = debug.get() && info.isLoggable(Level.FINEST);
final boolean log = info.isLoggable(logger, Level.FINEST);
String logstr = null;
try {
final String sql = "SELECT COUNT(*) FROM " + info.getTable(pk) + " WHERE " + info.getPrimarySQLColumn() + " = " + FilterNode.formatToString(pk);
@@ -1732,7 +1783,7 @@ public class DataJdbcSource extends AbstractService implements DataSource, Servi
if (cache != null && cache.isFullLoaded() && (node == null || node.isCacheUseable(this))) return cache.exists(node);
final Connection conn = createReadSQLConnection();
final boolean log = debug.get() && info.isLoggable(Level.FINEST);
final boolean log = info.isLoggable(logger, Level.FINEST);
String logstr = null;
try {
final Map<Class, String> joinTabalis = node == null ? null : node.getJoinTabalis();
@@ -1877,7 +1928,7 @@ public class DataJdbcSource extends AbstractService implements DataSource, Servi
return CompletableFuture.supplyAsync(() -> queryColumnSheet(selectedColumn, clazz, flipper, node), getExecutor());
}
private <T, V extends Serializable> Sheet<V> queryColumnSheet(final boolean needtotal, final String selectedColumn, final Class<T> clazz, final Flipper flipper, final FilterNode node) {
protected <T, V extends Serializable> Sheet<V> queryColumnSheet(final boolean needtotal, final String selectedColumn, final Class<T> clazz, final Flipper flipper, final FilterNode node) {
Sheet<T> sheet = querySheet(true, needtotal, clazz, SelectColumn.createIncludes(selectedColumn), flipper, node);
final Sheet<V> rs = new Sheet<>();
if (sheet.isEmpty()) return rs;
@@ -1892,6 +1943,154 @@ public class DataJdbcSource extends AbstractService implements DataSource, Servi
return rs;
}
/**
* 查询符合过滤条件记录的Map集合, 主键值为key <br>
* 等价SQL: SELECT * FROM {table} WHERE {column} = {key} ORDER BY {flipper.sort} LIMIT {flipper.limit} <br>
*
* @param <K> 主键泛型
* @param <T> Entity泛型
* @param clazz Entity类
* @param keyStream 主键Stream
*
* @return Entity的集合
*/
@Override
public <K extends Serializable, T> Map<K, T> queryMap(final Class<T> clazz, final Stream<K> keyStream) {
return queryMap(clazz, null, keyStream);
}
@Override
public <K extends Serializable, T> CompletableFuture<Map<K, T>> queryMapAsync(final Class<T> clazz, final Stream<K> keyStream) {
return CompletableFuture.supplyAsync(() -> queryMap(clazz, null, keyStream), getExecutor());
}
/**
* 查询符合过滤条件记录的Map集合, 主键值为key <br>
* 等价SQL: SELECT * FROM {table} WHERE {column} = {key} ORDER BY {flipper.sort} LIMIT {flipper.limit} <br>
*
* @param <K> 主键泛型
* @param <T> Entity泛型
* @param clazz Entity类
* @param bean FilterBean
*
* @return Entity的集合
*/
@Override
public <K extends Serializable, T> Map<K, T> queryMap(final Class<T> clazz, final FilterBean bean) {
return queryMap(clazz, null, bean);
}
@Override
public <K extends Serializable, T> CompletableFuture<Map<K, T>> queryMapAsync(final Class<T> clazz, final FilterBean bean) {
return CompletableFuture.supplyAsync(() -> queryMap(clazz, null, bean), getExecutor());
}
/**
* 查询符合过滤条件记录的Map集合, 主键值为key <br>
* 等价SQL: SELECT * FROM {table} WHERE {column} = {key} ORDER BY {flipper.sort} LIMIT {flipper.limit} <br>
*
* @param <K> 主键泛型
* @param <T> Entity泛型
* @param clazz Entity类
* @param node FilterNode
*
* @return Entity的集合
*/
@Override
public <K extends Serializable, T> Map<K, T> queryMap(final Class<T> clazz, final FilterNode node) {
return queryMap(clazz, null, node);
}
@Override
public <K extends Serializable, T> CompletableFuture<Map<K, T>> queryMapAsync(final Class<T> clazz, final FilterNode node) {
return CompletableFuture.supplyAsync(() -> queryMap(clazz, null, node), getExecutor());
}
/**
* 查询符合过滤条件记录的Map集合, 主键值为key <br>
* 等价SQL: SELECT * FROM {table} WHERE {column} = {key} ORDER BY {flipper.sort} LIMIT {flipper.limit} <br>
*
* @param <K> 主键泛型
* @param <T> Entity泛型
* @param clazz Entity类
* @param selects 指定字段
* @param keyStream 主键Stream
*
* @return Entity的集合
*/
@Override
public <K extends Serializable, T> Map<K, T> queryMap(final Class<T> clazz, final SelectColumn selects, final Stream<K> keyStream) {
if (keyStream == null) return new LinkedHashMap<>();
final EntityInfo<T> info = loadEntityInfo(clazz);
final ArrayList<K> ids = new ArrayList<>();
keyStream.forEach(k -> ids.add(k));
final Attribute<T, Serializable> primary = info.primary;
List<T> rs = queryList(clazz, FilterNode.create(primary.field(), ids));
Map<K, T> map = new LinkedHashMap<>();
if (rs.isEmpty()) return new LinkedHashMap<>();
for (T item : rs) {
map.put((K) primary.get(item), item);
}
return map;
}
@Override
public <K extends Serializable, T> CompletableFuture<Map<K, T>> queryMapAsync(final Class<T> clazz, final SelectColumn selects, final Stream<K> keyStream) {
return CompletableFuture.supplyAsync(() -> queryMap(clazz, selects, keyStream), getExecutor());
}
/**
* 查询符合过滤条件记录的Map集合, 主键值为key <br>
* 等价SQL: SELECT * FROM {table} WHERE {column} = {key} ORDER BY {flipper.sort} LIMIT {flipper.limit} <br>
*
* @param <K> 主键泛型
* @param <T> Entity泛型
* @param clazz Entity类
* @param selects 指定字段
* @param bean FilterBean
*
* @return Entity的集合
*/
@Override
public <K extends Serializable, T> Map<K, T> queryMap(final Class<T> clazz, final SelectColumn selects, final FilterBean bean) {
return queryMap(clazz, selects, FilterNodeBean.createFilterNode(bean));
}
@Override
public <K extends Serializable, T> CompletableFuture<Map<K, T>> queryMapAsync(final Class<T> clazz, final SelectColumn selects, final FilterBean bean) {
return CompletableFuture.supplyAsync(() -> queryMap(clazz, selects, FilterNodeBean.createFilterNode(bean)), getExecutor());
}
/**
* 查询符合过滤条件记录的Map集合, 主键值为key <br>
* 等价SQL: SELECT * FROM {table} WHERE {column} = {key} ORDER BY {flipper.sort} LIMIT {flipper.limit} <br>
*
* @param <K> 主键泛型
* @param <T> Entity泛型
* @param clazz Entity类
* @param selects 指定字段
* @param node FilterNode
*
* @return Entity的集合
*/
@Override
public <K extends Serializable, T> Map<K, T> queryMap(final Class<T> clazz, final SelectColumn selects, final FilterNode node) {
final EntityInfo<T> info = loadEntityInfo(clazz);
final Attribute<T, Serializable> primary = info.primary;
List<T> rs = queryList(clazz, selects, node);
Map<K, T> map = new LinkedHashMap<>();
if (rs.isEmpty()) return new LinkedHashMap<>();
for (T item : rs) {
map.put((K) primary.get(item), item);
}
return map;
}
@Override
public <K extends Serializable, T> CompletableFuture<Map<K, T>> queryMapAsync(final Class<T> clazz, final SelectColumn selects, final FilterNode node) {
return CompletableFuture.supplyAsync(() -> queryMap(clazz, selects, node), getExecutor());
}
/**
* 根据指定字段值查询对象集合
*
@@ -2083,12 +2282,12 @@ public class DataJdbcSource extends AbstractService implements DataSource, Servi
return CompletableFuture.supplyAsync(() -> querySheet(clazz, selects, flipper, node), getExecutor());
}
private <T> Sheet<T> querySheet(final boolean readcache, final boolean needtotal, final Class<T> clazz, final SelectColumn selects, final Flipper flipper, final FilterNode node) {
protected <T> Sheet<T> querySheet(final boolean readcache, final boolean needtotal, final Class<T> clazz, final SelectColumn selects, final Flipper flipper, final FilterNode node) {
final EntityInfo<T> info = loadEntityInfo(clazz);
final EntityCache<T> cache = info.getCache();
if (readcache && cache != null && cache.isFullLoaded()) {
if (node == null || node.isCacheUseable(this)) {
if (debug.get() && info.isLoggable(Level.FINEST)) logger.finest(clazz.getSimpleName() + " cache query predicate = " + (node == null ? null : node.createPredicate(cache)));
if (info.isLoggable(logger, Level.FINEST)) logger.finest(clazz.getSimpleName() + " cache query predicate = " + (node == null ? null : node.createPredicate(cache)));
return cache.querySheet(needtotal, selects, flipper, node);
}
}
@@ -2099,9 +2298,9 @@ public class DataJdbcSource extends AbstractService implements DataSource, Servi
final Map<Class, String> joinTabalis = node == null ? null : node.getJoinTabalis();
final CharSequence join = node == null ? null : node.createSQLJoin(this, false, joinTabalis, new HashSet<>(), info);
final CharSequence where = node == null ? null : node.createSQLExpress(info, joinTabalis);
final String sql = "SELECT a.* FROM " + info.getTable(node) + " a" + (join == null ? "" : join)
final String sql = "SELECT " + info.getQueryColumns("a", selects) + " FROM " + info.getTable(node) + " a" + (join == null ? "" : join)
+ ((where == null || where.length() == 0) ? "" : (" WHERE " + where)) + info.createSQLOrderby(flipper);
if (debug.get() && info.isLoggable(Level.FINEST)) {
if (info.isLoggable(logger, Level.FINEST)) {
logger.finest(clazz.getSimpleName() + " query sql=" + sql + (flipper == null || flipper.getLimit() < 1 ? "" : (" LIMIT " + flipper.getOffset() + "," + flipper.getLimit())));
}
conn.setReadOnly(true);
@@ -2134,7 +2333,7 @@ public class DataJdbcSource extends AbstractService implements DataSource, Servi
}
}
private static StringBuilder multisplit(char ch1, char ch2, String split, StringBuilder sb, String str, int from) {
protected static StringBuilder multisplit(char ch1, char ch2, String split, StringBuilder sb, String str, int from) {
if (str == null) return sb;
int pos1 = str.indexOf(ch1, from);
if (pos1 < 0) return sb;
@@ -2145,7 +2344,7 @@ public class DataJdbcSource extends AbstractService implements DataSource, Servi
return multisplit(ch1, ch2, split, sb, str, pos2 + 1);
}
private int[] directExecute(final Connection conn, String... sqls) {
protected int[] directExecute(final Connection conn, String... sqls) {
if (sqls.length == 0) return new int[0];
try {
conn.setReadOnly(false);
@@ -2189,13 +2388,14 @@ public class DataJdbcSource extends AbstractService implements DataSource, Servi
public final void directQuery(String sql, Consumer<ResultSet> consumer) {
final Connection conn = createReadSQLConnection();
try {
if (debug.get()) logger.finest("direct query sql=" + sql);
if (logger.isLoggable(Level.FINEST)) logger.finest("direct query sql=" + sql);
conn.setReadOnly(true);
final PreparedStatement ps = conn.prepareStatement(sql, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
final ResultSet set = ps.executeQuery();
final Statement statement = conn.createStatement();
//final PreparedStatement statement = conn.prepareStatement(sql, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
final ResultSet set = statement.executeQuery(sql);// ps.executeQuery();
consumer.accept(set);
set.close();
ps.close();
statement.close();
} catch (Exception ex) {
throw new RuntimeException(ex);
} finally {

View File

@@ -8,6 +8,7 @@ package org.redkale.source;
import java.io.Serializable;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Stream;
import org.redkale.util.*;
/**
@@ -23,6 +24,13 @@ import org.redkale.util.*;
@SuppressWarnings("unchecked")
public interface DataSource {
/**
* 获取数据源类型
*
* @return String
*/
public String getType();
//----------------------insertAsync-----------------------------
/**
* 新增记录, 多对象必须是同一个Entity类 <br>
@@ -1492,6 +1500,168 @@ public interface DataSource {
*/
public <T, V extends Serializable> CompletableFuture<Sheet<V>> queryColumnSheetAsync(final String selectedColumn, final Class<T> clazz, final Flipper flipper, final FilterNode node);
/**
* 查询符合过滤条件记录的Map集合, 主键值为key <br>
* 等价SQL: SELECT * FROM {table} WHERE {column} = {key} ORDER BY {flipper.sort} LIMIT {flipper.limit} <br>
*
* @param <K> 主键泛型
* @param <T> Entity泛型
* @param clazz Entity类
* @param keyStream 主键Stream
*
* @return Entity的集合
*/
public <K extends Serializable, T> Map<K, T> queryMap(final Class<T> clazz, final Stream<K> keyStream);
/**
* 查询符合过滤条件记录的List集合 <br>
* 等价SQL: SELECT * FROM {table} WHERE {column} = {key} ORDER BY {flipper.sort} LIMIT {flipper.limit} <br>
*
* @param <K> 主键泛型
* @param <T> Entity泛型
* @param clazz Entity类
* @param keyStream 主键Stream
*
* @return Entity的集合CompletableFuture
*/
public <K extends Serializable, T> CompletableFuture<Map<K, T>> queryMapAsync(final Class<T> clazz, final Stream<K> keyStream);
/**
* 查询符合过滤条件记录的Map集合, 主键值为key <br>
* 等价SQL: SELECT * FROM {table} WHERE {column} = {key} ORDER BY {flipper.sort} LIMIT {flipper.limit} <br>
*
* @param <K> 主键泛型
* @param <T> Entity泛型
* @param clazz Entity类
* @param bean FilterBean
*
* @return Entity的集合
*/
public <K extends Serializable, T> Map<K, T> queryMap(final Class<T> clazz, final FilterBean bean);
/**
* 查询符合过滤条件记录的List集合 <br>
* 等价SQL: SELECT * FROM {table} WHERE {column} = {key} ORDER BY {flipper.sort} LIMIT {flipper.limit} <br>
*
* @param <K> 主键泛型
* @param <T> Entity泛型
* @param clazz Entity类
* @param bean FilterBean
*
* @return Entity的集合CompletableFuture
*/
public <K extends Serializable, T> CompletableFuture<Map<K, T>> queryMapAsync(final Class<T> clazz, final FilterBean bean);
/**
* 查询符合过滤条件记录的Map集合, 主键值为key <br>
* 等价SQL: SELECT * FROM {table} WHERE {column} = {key} ORDER BY {flipper.sort} LIMIT {flipper.limit} <br>
*
* @param <K> 主键泛型
* @param <T> Entity泛型
* @param clazz Entity类
* @param node FilterNode
*
* @return Entity的集合
*/
public <K extends Serializable, T> Map<K, T> queryMap(final Class<T> clazz, final FilterNode node);
/**
* 查询符合过滤条件记录的List集合 <br>
* 等价SQL: SELECT * FROM {table} WHERE {column} = {key} ORDER BY {flipper.sort} LIMIT {flipper.limit} <br>
*
* @param <K> 主键泛型
* @param <T> Entity泛型
* @param clazz Entity类
* @param node FilterNode
*
* @return Entity的集合CompletableFuture
*/
public <K extends Serializable, T> CompletableFuture<Map<K, T>> queryMapAsync(final Class<T> clazz, final FilterNode node);
/**
* 查询符合过滤条件记录的Map集合, 主键值为key <br>
* 等价SQL: SELECT * FROM {table} WHERE {column} = {key} ORDER BY {flipper.sort} LIMIT {flipper.limit} <br>
*
* @param <K> 主键泛型
* @param <T> Entity泛型
* @param clazz Entity类
* @param selects 指定字段
* @param keyStream 主键Stream
*
* @return Entity的集合
*/
public <K extends Serializable, T> Map<K, T> queryMap(final Class<T> clazz, final SelectColumn selects, final Stream<K> keyStream);
/**
* 查询符合过滤条件记录的List集合 <br>
* 等价SQL: SELECT * FROM {table} WHERE {column} = {key} ORDER BY {flipper.sort} LIMIT {flipper.limit} <br>
*
* @param <K> 主键泛型
* @param <T> Entity泛型
* @param clazz Entity类
* @param selects 指定字段
* @param keyStream 主键Stream
*
* @return Entity的集合CompletableFuture
*/
public <K extends Serializable, T> CompletableFuture<Map<K, T>> queryMapAsync(final Class<T> clazz, final SelectColumn selects, final Stream<K> keyStream);
/**
* 查询符合过滤条件记录的Map集合, 主键值为key <br>
* 等价SQL: SELECT * FROM {table} WHERE {column} = {key} ORDER BY {flipper.sort} LIMIT {flipper.limit} <br>
*
* @param <K> 主键泛型
* @param <T> Entity泛型
* @param clazz Entity类
* @param selects 指定字段
* @param bean FilterBean
*
* @return Entity的集合
*/
public <K extends Serializable, T> Map<K, T> queryMap(final Class<T> clazz, final SelectColumn selects, final FilterBean bean);
/**
* 查询符合过滤条件记录的List集合 <br>
* 等价SQL: SELECT * FROM {table} WHERE {column} = {key} ORDER BY {flipper.sort} LIMIT {flipper.limit} <br>
*
* @param <K> 主键泛型
* @param <T> Entity泛型
* @param clazz Entity类
* @param selects 指定字段
* @param bean FilterBean
*
* @return Entity的集合CompletableFuture
*/
public <K extends Serializable, T> CompletableFuture<Map<K, T>> queryMapAsync(final Class<T> clazz, final SelectColumn selects, FilterBean bean);
/**
* 查询符合过滤条件记录的Map集合, 主键值为key <br>
* 等价SQL: SELECT * FROM {table} WHERE {column} = {key} ORDER BY {flipper.sort} LIMIT {flipper.limit} <br>
*
* @param <K> 主键泛型
* @param <T> Entity泛型
* @param clazz Entity类
* @param selects 指定字段
* @param node FilterNode
*
* @return Entity的集合
*/
public <K extends Serializable, T> Map<K, T> queryMap(final Class<T> clazz, final SelectColumn selects, final FilterNode node);
/**
* 查询符合过滤条件记录的List集合 <br>
* 等价SQL: SELECT * FROM {table} WHERE {column} = {key} ORDER BY {flipper.sort} LIMIT {flipper.limit} <br>
*
* @param <K> 主键泛型
* @param <T> Entity泛型
* @param clazz Entity类
* @param selects 指定字段
* @param node FilterNode
*
* @return Entity的集合CompletableFuture
*/
public <K extends Serializable, T> CompletableFuture<Map<K, T>> queryMapAsync(final Class<T> clazz, final SelectColumn selects, final FilterNode node);
/**
* 查询符合过滤条件记录的List集合 <br>
* 等价SQL: SELECT * FROM {table} WHERE {column} = {key} ORDER BY {flipper.sort} LIMIT {flipper.limit} <br>

View File

@@ -21,6 +21,8 @@ public final class DataSources {
public static final String JDBC_DATASOURCE_CLASS = "javax.persistence.datasource";
public static final String JDBC_CACHE_MODE = "javax.persistence.cachemode";
public static final String JDBC_CONNECTIONSMAX = "javax.persistence.connections.limit";
public static final String JDBC_CONTAIN_SQLTEMPLATE = "javax.persistence.contain.sqltemplate";
@@ -44,6 +46,14 @@ public final class DataSources {
private DataSources() {
}
public static DataSource createDataSource(final String unitName, Properties prop) throws IOException {
return new DataJdbcSource(unitName, prop, prop);
}
public static DataSource createDataSource(final String unitName, Properties readprop, Properties writeprop) throws IOException {
return new DataJdbcSource(unitName, readprop, writeprop);
}
public static DataSource createDataSource(final String unitName) throws IOException {
return createDataSource(unitName, System.getProperty(DATASOURCE_CONFPATH) == null
? DataJdbcSource.class.getResource("/META-INF/persistence.xml")
@@ -82,6 +92,7 @@ public final class DataSources {
}
}
if (readprop == null) throw new IOException("Cannot find (resource.name = '" + unitName + "') DataSource");
if (writeprop == null) writeprop = readprop;
String impl = readprop.getProperty(JDBC_DATASOURCE_CLASS, DataJdbcSource.class.getName());
if (DataJdbcSource.class.getName().equals(impl)) return new DataJdbcSource(unitName, readprop, writeprop);
try {
@@ -104,7 +115,7 @@ public final class DataSources {
}
}
static Map<String, Properties> loadPersistenceXml(final InputStream in0) {
public static Map<String, Properties> loadPersistenceXml(final InputStream in0) {
final Map<String, Properties> map = new LinkedHashMap();
Properties result = new Properties();
boolean flag = false;
@@ -122,8 +133,8 @@ public final class DataSources {
String value = reader.getAttributeValue(null, "value");
if (name == null) continue;
result.put(name, value);
} else if (flag && "shared-cache-mode".equalsIgnoreCase(reader.getLocalName())) {
result.put(reader.getLocalName(), reader.getElementText());
} else if (flag && "shared-cache-mode".equalsIgnoreCase(reader.getLocalName())) { //兼容shared-cache-mode属性
result.put(JDBC_CACHE_MODE, reader.getElementText());
}
}
}

View File

@@ -286,7 +286,7 @@ public final class EntityCache<T> {
rs = rs2;
} else if (func == DISTINCTCOUNT) {
Map rs2 = new LinkedHashMap();
rs.forEach((x, y) -> rs2.put(x, ((Set) y).size()));
rs.forEach((x, y) -> rs2.put(x, ((Set) y).size() + 0L));
rs = rs2;
}
return rs;

View File

@@ -5,14 +5,13 @@
*/
package org.redkale.source;
import com.sun.istack.internal.logging.Logger;
import java.io.Serializable;
import java.lang.reflect.*;
import java.sql.*;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.*;
import java.util.logging.Level;
import java.util.logging.*;
import javax.persistence.*;
import org.redkale.util.*;
@@ -32,7 +31,7 @@ public final class EntityInfo<T> {
private static final ConcurrentHashMap<Class, EntityInfo> entityInfos = new ConcurrentHashMap<>();
//日志
private static final Logger logger = Logger.getLogger(EntityInfo.class);
private static final Logger logger = Logger.getLogger(EntityInfo.class.getSimpleName());
//Entity类名
private final Class<T> type;
@@ -194,7 +193,7 @@ public final class EntityInfo<T> {
try {
loader = type.getAnnotation(VirtualEntity.class).loader().newInstance();
} catch (Exception e) {
logger.severe(type + " init @VirtualEntity.loader error", e);
logger.log(Level.SEVERE, type + " init @VirtualEntity.loader error", e);
}
this.fullloader = loader;
} else {
@@ -207,7 +206,7 @@ public final class EntityInfo<T> {
try {
dts = (dt == null) ? null : dt.strategy().newInstance();
} catch (Exception e) {
logger.severe(type + " init DistributeTableStrategy error", e);
logger.log(Level.SEVERE, type + " init DistributeTableStrategy error", e);
}
this.tableStrategy = dts;
@@ -216,7 +215,7 @@ public final class EntityInfo<T> {
try {
cp = this.creator.getClass().getMethod("create", Object[].class).getAnnotation(Creator.ConstructorParameters.class);
} catch (Exception e) {
logger.severe(type + " cannot find ConstructorParameters Creator", e);
logger.log(Level.SEVERE, type + " cannot find ConstructorParameters Creator", e);
}
this.constructorParameters = (cp == null || cp.value().length < 1) ? null : cp.value();
Attribute idAttr0 = null;
@@ -435,6 +434,26 @@ public final class EntityInfo<T> {
return deleteSQL.replace("${newtable}", getTable(bean));
}
/**
* 获取查询字段列表
*
* @param tabalis 表别名
* @param selects 过滤字段
*
* @return String
*/
public CharSequence getQueryColumns(String tabalis, SelectColumn selects) {
if (selects == null) return tabalis == null ? "*" : (tabalis + ".*");
StringBuilder sb = new StringBuilder();
for (Attribute attr : this.attributes) {
if (!selects.test(attr.field())) continue;
if (sb.length() > 0) sb.append(',');
sb.append(getSQLColumn(tabalis, attr.field()));
}
if (sb.length() == 0) sb.append('*');
return sb;
}
/**
* 根据主键值获取Entity的表名
*
@@ -630,12 +649,13 @@ public final class EntityInfo<T> {
/**
* 判断日志级别
*
* @param l Level
* @param logger Logger
* @param l Level
*
* @return boolean
*/
public boolean isLoggable(Level l) {
return l.intValue() >= this.logLevel;
public boolean isLoggable(Logger logger, Level l) {
return logger.isLoggable(l) && l.intValue() >= this.logLevel;
}
/**

View File

@@ -55,9 +55,11 @@ public class FilterNode { //FilterNode 不能实现Serializable接口 否则
}
if (subval instanceof Range) {
exp = FilterExpress.BETWEEN;
} else if (subval instanceof Collection) {
exp = FilterExpress.IN;
} else if (subval != null && val.getClass().isArray()) {
// } else if (subval instanceof Collection) {
// exp = FilterExpress.IN;
// } else if (subval != null && val.getClass().isArray()) {
// exp = FilterExpress.IN;
} else {
exp = FilterExpress.IN;
}
} else { //空集合
@@ -280,7 +282,7 @@ public class FilterNode { //FilterNode 不能实现Serializable接口 否则
}
private static boolean needSplit(final FilterExpress express, final Object val0) {
if(val0 == null) return false;
if (val0 == null) return false;
boolean items = express != IN && express != NOTIN; //是否数组集合的表达式
if (!items) {
if (val0.getClass().isArray()) {

View File

@@ -63,7 +63,7 @@ public final class FilterNodeBean<T extends FilterBean> implements Comparable<Fi
this.nodeBeans = bean == null ? null : bean.nodeBeans;
}
private FilterNodeBean(final FilterJoinColumn joinCol, final FilterColumn filterCol, final Attribute<T, Serializable> attr) {
private FilterNodeBean(final FilterJoinColumn joinCol, final FilterColumn filterCol, final Attribute<T, Serializable> attr, final Type genericType) {
this.beanAttr = attr;
this.joinClass = joinCol == null ? null : joinCol.table();
this.joinColumns = joinCol == null ? null : joinCol.columns();
@@ -72,8 +72,13 @@ public final class FilterNodeBean<T extends FilterBean> implements Comparable<Fi
this.column = (filterCol != null && !filterCol.name().isEmpty()) ? filterCol.name() : attr.field();
FilterExpress exp = filterCol == null ? null : filterCol.express();
Class compType = type.getComponentType();
if (Collection.class.isAssignableFrom(type) && genericType instanceof ParameterizedType) {
Type pt = ((ParameterizedType) genericType).getActualTypeArguments()[0];
if (pt instanceof Class) compType = (Class) pt;
}
if ((exp == null || exp == EQUAL) && (type.isArray() || Collection.class.isAssignableFrom(type))) {
if (Range.class.isAssignableFrom(type.getComponentType())) {
if (compType != null && Range.class.isAssignableFrom(compType)) {
if (AND != exp) exp = OR;
} else if (NOTIN != exp) {
exp = IN;
@@ -189,7 +194,7 @@ public final class FilterNodeBean<T extends FilterBean> implements Comparable<Fi
fields.add(field.getName());
final Attribute<T, Serializable> beanAttr = pubmod ? Attribute.create(field) : Attribute.create(getter, null);
FilterNodeBean<T> nodeBean = new FilterNodeBean(field.getAnnotation(FilterJoinColumn.class), field.getAnnotation(FilterColumn.class), beanAttr);
FilterNodeBean<T> nodeBean = new FilterNodeBean(field.getAnnotation(FilterJoinColumn.class), field.getAnnotation(FilterColumn.class), beanAttr, field.getGenericType());
//------------------------------------
{

View File

@@ -85,6 +85,13 @@ public class PoolJdbcSource {
if (this.isOracle()) {
this.props.setProperty(JDBC_CONTAIN_SQLTEMPLATE, "INSTR(${keystr}, ${column}) > 0");
this.props.setProperty(JDBC_NOTCONTAIN_SQLTEMPLATE, "INSTR(${keystr}, ${column}) = 0");
if (!this.props.containsKey(JDBC_TABLENOTEXIST_SQLSTATES)) {
this.props.setProperty(JDBC_TABLENOTEXIST_SQLSTATES, "42000;42S02");
}
if (!this.props.containsKey(JDBC_TABLECOPY_SQLTEMPLATE)) {
//注意:此语句复制表结构会导致默认值和主键信息的丢失
this.props.setProperty(JDBC_TABLECOPY_SQLTEMPLATE, "CREATE TABLE ${newtable} AS SELECT * FROM ${oldtable} WHERE 1=2");
}
} else if (this.isSqlserver()) {
this.props.setProperty(JDBC_CONTAIN_SQLTEMPLATE, "CHARINDEX(${column}, ${keystr}) > 0");
this.props.setProperty(JDBC_NOTCONTAIN_SQLTEMPLATE, "CHARINDEX(${column}, ${keystr}) = 0");

View File

@@ -7,7 +7,7 @@ package org.redkale.util;
import java.lang.reflect.Array;
import java.util.*;
import java.util.function.BiPredicate;
import java.util.function.*;
/**
* 该类提供类似JSONObject的数据结构主要用于读取xml配置文件和http-header存储
@@ -190,6 +190,25 @@ public abstract class AnyValue {
return this;
}
@Override
public void forEach(BiConsumer<String, String> stringConsumer) {
forEach(stringConsumer, null);
}
@Override
public void forEach(BiConsumer<String, String> stringConsumer, BiConsumer<String, AnyValue> anyConsumer) {
if (stringConsumer != null) {
for (Entry<String> en : stringEntrys) {
stringConsumer.accept(en.name, en.value);
}
}
if (anyConsumer != null) {
for (Entry<AnyValue> en : (Entry[]) anyEntrys) {
anyConsumer.accept(en.name, en.value);
}
}
}
@Override
public Entry<String>[] getStringEntrys() {
return stringEntrys;
@@ -430,6 +449,10 @@ public abstract class AnyValue {
return sb.toString();
}
public abstract void forEach(BiConsumer<String, String> stringConsumer);
public abstract void forEach(BiConsumer<String, String> stringConsumer, BiConsumer<String, AnyValue> anyConsumer);
public abstract Entry<String>[] getStringEntrys();
public abstract Entry<AnyValue>[] getAnyEntrys();

View File

@@ -16,10 +16,12 @@ import java.util.function.*;
* <p>
* 详情见: https://redkale.org
*
* @deprecated 使用 java.nio.channels.CompletionHandler 代替
* @author zhangjx
* @param <V> 结果对象的泛型
* @param <A> 附件对象的泛型
*/
@Deprecated
public interface AsyncHandler<V, A> extends CompletionHandler<V, A> {
/**

View File

@@ -74,6 +74,7 @@ import static jdk.internal.org.objectweb.asm.Opcodes.*;
* @param <T> 字段依附的类
* @param <F> 字段的数据类型
*/
@SuppressWarnings("unchecked")
public interface Attribute<T, F> {
/**

View File

@@ -13,6 +13,7 @@ import java.lang.reflect.*;
import java.net.*;
import java.util.*;
import java.util.AbstractMap.SimpleEntry;
import java.util.concurrent.*;
import jdk.internal.org.objectweb.asm.*;
import jdk.internal.org.objectweb.asm.Type;
import static jdk.internal.org.objectweb.asm.Opcodes.*;
@@ -82,6 +83,7 @@ public interface Creator<T> {
String[] value();
@SuppressWarnings("unchecked")
static class CreatorInner {
static class SimpleClassVisitor extends ClassVisitor {
@@ -108,7 +110,16 @@ public interface Creator<T> {
return new MethodVisitor(Opcodes.ASM5) {
@Override
public void visitLocalVariable(String name, String description, String signature, Label start, Label end, int index) {
if (index > 0) fieldnames.add(name);
if (index < 1) return;
int size = fieldnames.size();
//index不会按顺序执行的
if (index > size) {
for (int i = size; i < index; i++) {
fieldnames.add(" ");
}
fieldnames.set(index - 1, name);
}
fieldnames.set(index - 1, name);
}
};
}
@@ -196,6 +207,8 @@ public interface Creator<T> {
clazz = (Class<T>) HashMap.class;
} else if (clazz.isAssignableFrom(HashSet.class)) {
clazz = (Class<T>) HashSet.class;
} else if (clazz.isAssignableFrom(ConcurrentHashMap.class)) {
clazz = (Class<T>) ConcurrentHashMap.class;
}
if (clazz.isInterface() || Modifier.isAbstract(clazz.getModifiers())) {
throw new RuntimeException("[" + clazz + "] is a interface or abstract class, cannot create it's Creator.");
@@ -338,8 +351,12 @@ public interface Creator<T> {
mv.visitVarInsn(ALOAD, 1);
if (i < 6) {
mv.visitInsn(iconsts[i]);
} else {
} else if (i <= Byte.MAX_VALUE) {
mv.visitIntInsn(BIPUSH, i);
} else if (i <= Short.MAX_VALUE) {
mv.visitIntInsn(SIPUSH, i);
} else {
mv.visitLdcInsn(i);
}
mv.visitInsn(AALOAD);
Label lab = new Label();
@@ -347,8 +364,12 @@ public interface Creator<T> {
mv.visitVarInsn(ALOAD, 1);
if (i < 6) {
mv.visitInsn(iconsts[i]);
} else {
} else if (i <= Byte.MAX_VALUE) {
mv.visitIntInsn(BIPUSH, i);
} else if (i <= Short.MAX_VALUE) {
mv.visitIntInsn(SIPUSH, i);
} else {
mv.visitLdcInsn(i);
}
if (pt == int.class) {
mv.visitInsn(ICONST_0);
@@ -386,8 +407,12 @@ public interface Creator<T> {
mv.visitVarInsn(ALOAD, 1);
if (i < 6) {
mv.visitInsn(iconsts[i]);
} else {
} else if (i <= Byte.MAX_VALUE) {
mv.visitIntInsn(BIPUSH, i);
} else if (i <= Short.MAX_VALUE) {
mv.visitIntInsn(SIPUSH, i);
} else {
mv.visitLdcInsn(i);
}
mv.visitInsn(AALOAD);
final Class ct = constructorParameters[i].getValue();

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