96 Commits

Author SHA1 Message Date
Redkale
44507a97a6 兼容Oracle的复制表结构 2017-07-25 11:05:49 +08:00
Redkale
f4a7f1cff6 修复负载均衡无法正确切换节点的BUG 2017-07-24 18:29:40 +08:00
Redkale
a5fcb45a88 2017-07-24 17:34:07 +08:00
Redkale
bc8b68526d 2017-07-24 17:32:45 +08:00
Redkale
180f201dc0 修复多个<services>节点且其下指定service时无法获取所有service实例的BUG 2017-07-24 17:20:16 +08:00
Redkale
9ab315a405 2017-07-24 16:10:13 +08:00
Redkale
27b4742b6d 增加 TransportStrategy 功能 2017-07-21 16:07:13 +08:00
Redkale
702220d18e 2017-07-19 10:51:17 +08:00
Redkale
414489da8e 2017-07-15 22:24:10 +08:00
Redkale
77057df25d 2017-07-11 11:59:12 +08:00
Redkale
2f98cd1ab5 2017-07-11 11:29:57 +08:00
Redkale
8809fe8ec9 2017-07-11 10:09:49 +08:00
Redkale
f9702a9517 WebSocket时request.keepAlive设置为false 2017-07-11 10:07:42 +08:00
Redkale
29e46b9b68 暂时屏蔽304 2017-07-11 09:47:40 +08:00
Redkale
f838e35413 返回304的响应中增加Content-Length值 2017-07-11 09:30:27 +08:00
Redkale
f3bb77c49b 增加获取最后一次ping的时间点 2017-07-10 22:46:16 +08:00
Redkale
12fa033e15 增加判断用户是否在线和获取在线用户总数接口 2017-07-05 15:57:22 +08:00
Redkale
f4abfafea2 2017-07-05 15:42:54 +08:00
Redkale
0918af71d2 CacheSource增加getKeySize方法 2017-07-05 15:18:16 +08:00
Redkale
275befa330 private方法改成protected,方便重载 2017-07-05 10:28:55 +08:00
Redkale
ab4cd8bcb6 2017-07-02 17:30:08 +08:00
Redkale
36c109b32f 2017-07-02 17:29:22 +08:00
Redkale
73a915665d 2017-07-02 17:00:59 +08:00
Redkale
bd6d71c94a 修复@RestWebSocket对应的WebSocketServlet.resourceName没有重载的BUG 2017-07-01 16:31:00 +08:00
Redkale
842e93507c WebSocketEngine.broadcastMessage 增加 Predicate<WebSocket> 参数 2017-07-01 15:46:51 +08:00
Redkale
76df1108d7 WebSocketEngine.broadcastMessage 增加 Predicate<WebSocket> 参数 2017-07-01 15:38:47 +08:00
Redkale
941d09cde2 2017-07-01 15:29:26 +08:00
Redkale
9dd3e1da07 允许本地模式的Service通过@Resource可以获取Application、ResourceFactory等资源 2017-06-30 13:35:44 +08:00
Redkale
2bf73245ec Redkale 1.8.1 开始 2017-06-26 09:40:30 +08:00
Redkale
b0ab792f72 2017-06-24 22:45:19 +08:00
Redkale
0df9a940c5 2017-06-24 16:24:13 +08:00
Redkale
fc35fc5abc 2017-06-24 15:26:52 +08:00
Redkale
eaa0a99933 2017-06-24 12:33:48 +08:00
Redkale
83bdb97842 2017-06-24 12:31:20 +08:00
Redkale
a71a4d0fed 2017-06-24 10:47:41 +08:00
Redkale
835435c220 2017-06-24 10:38:41 +08:00
Redkale
c524ba1797 2017-06-24 10:32:00 +08:00
Redkale
f254b48693 2017-06-24 10:09:26 +08:00
Redkale
922697eb4d 2017-06-22 14:37:42 +08:00
Redkale
450e3e3ea2 2017-06-22 09:42:24 +08:00
Redkale
b31f75f4f6 2017-06-22 00:54:51 +08:00
Redkale
1d1f18b046 2017-06-21 23:05:40 +08:00
Redkale
73f942746b 2017-06-21 17:35:18 +08:00
Redkale
b6cefe8c2d 2017-06-21 17:34:08 +08:00
Redkale
ebc0e4eb41 2017-06-21 17:32:22 +08:00
Redkale
179a7b22ea 2017-06-21 16:27:18 +08:00
Redkale
0b87d9a261 2017-06-21 16:10:15 +08:00
Redkale
685a686ead 2017-06-20 21:58:20 +08:00
Redkale
568e1cf62d 2017-06-20 21:57:29 +08:00
Redkale
1d121bd2ab 2017-06-20 13:56:45 +08:00
Redkale
2ca4bdaaec 2017-06-19 13:46:32 +08:00
Redkale
878fda30f6 2017-06-19 13:45:16 +08:00
Redkale
abb611382c 2017-06-19 13:41:43 +08:00
Redkale
b53510a26f 2017-06-19 10:07:59 +08:00
Redkale
2aee84d477 2017-06-19 09:53:29 +08:00
Redkale
0ba2e25f2e 2017-06-19 08:20:52 +08:00
Redkale
98e8a7eb05 2017-06-18 22:31:13 +08:00
Redkale
62139efca9 2017-06-18 21:58:28 +08:00
Redkale
2b62cbe455 2017-06-18 21:51:20 +08:00
Redkale
e44602fe3b 2017-06-15 23:27:56 +08:00
Redkale
276cb4da92 2017-06-15 08:08:38 +08:00
Redkale
7081f94afc 2017-06-12 14:43:32 +08:00
Redkale
2d6cefeb43 2017-06-07 09:38:57 +08:00
Redkale
ea6c703ac6 2017-06-07 07:02:16 +08:00
Redkale
d1f14962fd 2017-06-07 06:59:51 +08:00
Redkale
79ca63bf81 2017-06-06 09:44:51 +08:00
Redkale
6421bc2851 2017-06-06 09:43:31 +08:00
Redkale
43f9f50f4c 2017-06-06 09:35:41 +08:00
Redkale
5f140a8ce9 2017-06-06 09:31:27 +08:00
Redkale
c0f8cdf902 2017-06-06 09:30:46 +08:00
Redkale
8d66b1b4a7 2017-06-06 08:35:57 +08:00
Redkale
8c7ee4136c 2017-06-06 08:20:47 +08:00
Redkale
d9498c9a6c 2017-06-05 16:09:06 +08:00
Redkale
08060a8c86 2017-06-05 09:18:05 +08:00
Redkale
2bdf0e4a50 2017-06-05 08:55:37 +08:00
Redkale
eb184df100 2017-06-04 11:30:07 +08:00
Redkale
cf545a731c 2017-06-04 08:03:03 +08:00
Redkale
95e18dfd48 2017-06-04 08:00:13 +08:00
Redkale
023a9abdef 2017-06-03 22:32:47 +08:00
Redkale
7f3776c224 2017-06-03 22:12:29 +08:00
Redkale
7b15ba33e0 2017-06-03 22:05:53 +08:00
Redkale
a6c105d63d 2017-06-03 18:41:08 +08:00
Redkale
10e22b0873 2017-06-03 16:50:40 +08:00
Redkale
53a35e6397 2017-06-03 10:20:36 +08:00
Redkale
b5a3c39f4f 2017-06-03 09:51:53 +08:00
Redkale
99381d4842 2017-06-02 15:24:53 +08:00
Redkale
5f2c2a9f2c 2017-06-02 14:00:14 +08:00
Redkale
dc3f318949 2017-06-02 09:46:58 +08:00
Redkale
cfae61faea 2017-06-01 20:06:46 +08:00
Redkale
dd7626b1a3 2017-06-01 15:21:28 +08:00
Redkale
2171aa1232 2017-06-01 13:23:35 +08:00
Redkale
801ad489d2 2017-06-01 11:38:49 +08:00
Redkale
c88d0b402d 2017-05-31 21:19:51 +08:00
Redkale
542bb4353b 2017-05-31 14:07:21 +08:00
Redkale
fe1e0a845a 2017-05-31 13:41:10 +08:00
Redkale
f320f4c550 2017-05-31 12:17:10 +08:00
92 changed files with 1705 additions and 1357 deletions

View File

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

View File

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

View File

@@ -5,19 +5,16 @@
<!-- 详细配置说明见: http://redkale.org/redkale.html#redkale_confxml --> <!-- 详细配置说明见: http://redkale.org/redkale.html#redkale_confxml -->
<resources> <resources>
<!--
<properties>
<property name="system.property.convert.json.tiny" value="true"/>
</properties>
-->
</resources> </resources>
<server protocol="HTTP" host="0.0.0.0" port="6060" root="root"> <server protocol="HTTP" host="0.0.0.0" port="6060" root="root">
<services autoload="true"/> <services autoload="true"/>
<!-- base指定的自定义HttpServlet子类必须标记@HttpUserType, 不设置base则视为没有当前用户信息设置 --> <filters autoload="true"/>
<rest path="/pipes" base="org.redkale.net.http.HttpServlet"/>
<rest path="/pipes" /> <!-- base指定的自定义HttpServlet子类必须标记@HttpUserType, 不设置base则视为没有当前用户信息设置 -->
<servlets path="/pipes" autoload="true" /> <servlets path="/pipes" autoload="true" />

View File

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

View File

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

1
libs/readme.txt Normal file
View File

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

View File

@@ -37,6 +37,7 @@
threads 线程总数, 默认: <group>节点数*CPU核数*8 threads 线程总数, 默认: <group>节点数*CPU核数*8
bufferCapacity: ByteBuffer的初始化大小 默认: 8K; bufferCapacity: ByteBuffer的初始化大小 默认: 8K;
bufferPoolSize ByteBuffer池的大小默认: <group>节点数*CPU核数*8 bufferPoolSize ByteBuffer池的大小默认: <group>节点数*CPU核数*8
strategy: 远程请求的负载均衡策略, 必须是org.redkale.net.TransportStrategy的实现类
--> -->
<transport bufferCapacity="8K" bufferPoolSize="32" threads="32"/> <transport bufferCapacity="8K" bufferPoolSize="32" threads="32"/>
@@ -96,12 +97,12 @@
</resources> </resources>
<!-- <!--
protocol: required server所启动的协议Redkale内置的有HTTP、SNCP、WATCHSNCP使用TCP实现; protocol: required server所启动的协议Redkale内置的有HTTP、SNCP、WATCH。协议均使用TCP实现; WATCH服务只能存在一个。
name: 服务的名称用于监控识别一个配置文件中的server.name不能重复,命名规则: 字母、数字、下划线 name: 服务的名称用于监控识别一个配置文件中的server.name不能重复,命名规则: 字母、数字、下划线
host: 服务所占address 默认: 0.0.0.0 host: 服务所占address 默认: 0.0.0.0
port: required 服务所占端口 port: required 服务所占端口
root: 如果是web类型服务则包含页面 默认:{APP_HOME}/root root: 如果是web类型服务则包含页面 默认:{APP_HOME}/root
lib: server额外的class目录 默认为 lib: server额外的class目录 默认为${APP_HOME}/libs/*;
excludelibs: 排除lib.path与excludes中的正则表达式匹配的路径, 多个正则表达式用分号;隔开 excludelibs: 排除lib.path与excludes中的正则表达式匹配的路径, 多个正则表达式用分号;隔开
charset: 文本编码, 默认: UTF-8 charset: 文本编码, 默认: UTF-8
backlog: 默认10K backlog: 默认10K
@@ -119,8 +120,7 @@
<!-- <!--
加载所有的Service服务; 加载所有的Service服务;
在同一个进程中同一个name同一类型的Service将共用同一个实例 在同一个进程中同一个name同一类型的Service将共用同一个实例
autoload="true" 默认值. 自动加载以下目录(如果存在的话)下所有的Service类: autoload="true" 默认值. 自动加载classpath下所有的Service类
server.lib; server.lib/*; server.classes;
autoload="false" 需要显著的指定Service类 autoload="false" 需要显著的指定Service类
includes 当autoload="true" 拉取类名与includes中的正则表达式匹配的类, 多个正则表达式用分号;隔开 includes 当autoload="true" 拉取类名与includes中的正则表达式匹配的类, 多个正则表达式用分号;隔开
excludes 当autoload="true" 排除类名与excludes中的正则表达式匹配的类, 多个正则表达式用分号;隔开 excludes 当autoload="true" 排除类名与excludes中的正则表达式匹配的类, 多个正则表达式用分号;隔开
@@ -135,6 +135,7 @@
<!-- <!--
name: 显式指定name覆盖默认的空字符串值。 注意: name不能包含$符号。 name: 显式指定name覆盖默认的空字符串值。 注意: name不能包含$符号。
groups: 显式指定groups覆盖<services>节点的groups默认值。 groups: 显式指定groups覆盖<services>节点的groups默认值。
ignore: 是否禁用, 默认为false。
--> -->
<service value="com.xxx.XXX2Service" name="" groups="xxx;yyy"/> <service value="com.xxx.XXX2Service" name="" groups="xxx;yyy"/>
<!-- 给Service增加配置属性 --> <!-- 给Service增加配置属性 -->
@@ -153,7 +154,12 @@
excludes 当autoload="true" 排除类名与excludes中的正则表达式匹配的类, 多个正则表达式用分号;隔开 excludes 当autoload="true" 排除类名与excludes中的正则表达式匹配的类, 多个正则表达式用分号;隔开
--> -->
<filters autoload="true" includes="" excludes=""> <filters autoload="true" includes="" excludes="">
<!--
显著加载指定的Filter类
value=: Filter类名。必须与Server的协议层相同HTTP必须是HttpFilter
ignore: 是否禁用, 默认为false。
-->
<!-- 显著加载指定的Filter类 --> <!-- 显著加载指定的Filter类 -->
<filter value="com.xxx.XXX1Filter"/> <filter value="com.xxx.XXX1Filter"/>
@@ -240,14 +246,17 @@
<!-- <!--
加载所有的Servlet服务; 加载所有的Servlet服务;
path: servlet的ContextPath前缀 默认为空 path: servlet的ContextPath前缀 默认为空
autoload="true" 默认值. 自动加载以下目录(如果存在的话)下所有的Servlet类: autoload="true" 默认值. 自动加载classpath下所有的Servlet类
${APP_HOME}/lib; ${APP_HOME}/root/lib/*; ${APP_HOME}/root/classes;
autoload="false" 需要显著的指定Service类 autoload="false" 需要显著的指定Service类
includes 当autoload="true" 拉取类名与includes中的正则表达式匹配的类, 多个正则表达式用分号;隔开 includes 当autoload="true" 拉取类名与includes中的正则表达式匹配的类, 多个正则表达式用分号;隔开
excludes 当autoload="true" 排除类名与excludes中的正则表达式匹配的类, 多个正则表达式用分号;隔开 excludes 当autoload="true" 排除类名与excludes中的正则表达式匹配的类, 多个正则表达式用分号;隔开
--> -->
<servlets path="/pipes" autoload="true" includes="" excludes=""> <servlets path="/pipes" autoload="true" includes="" excludes="">
<!-- 显著加载指定的Servlet --> <!--
显著加载指定的Servlet类
value=: Servlet类名。必须与Server的协议层相同HTTP必须是HttpServlet
ignore: 是否禁用, 默认为false。
-->
<servlet value="com.xxx.XXX1Servlet" /> <servlet value="com.xxx.XXX1Servlet" />
<servlet value="com.xxx.XXX2Servlet" /> <servlet value="com.xxx.XXX2Servlet" />
<servlet value="com.xxx.XXX3Servlet" > <servlet value="com.xxx.XXX3Servlet" >

View File

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

View File

@@ -5,6 +5,7 @@
*/ */
package org.redkale.boot; package org.redkale.boot;
import org.redkale.util.RedkaleClassLoader;
import org.redkale.net.TransportGroupInfo; import org.redkale.net.TransportGroupInfo;
import java.io.*; import java.io.*;
import java.lang.reflect.*; import java.lang.reflect.*;
@@ -19,6 +20,7 @@ import java.util.logging.*;
import javax.annotation.Resource; import javax.annotation.Resource;
import javax.xml.parsers.*; import javax.xml.parsers.*;
import org.redkale.boot.ClassFilter.FilterEntry; import org.redkale.boot.ClassFilter.FilterEntry;
import org.redkale.convert.Convert;
import org.redkale.convert.bson.BsonFactory; import org.redkale.convert.bson.BsonFactory;
import org.redkale.convert.json.JsonFactory; import org.redkale.convert.json.JsonFactory;
import org.redkale.net.*; import org.redkale.net.*;
@@ -136,7 +138,10 @@ public final class Application {
private final CountDownLatch serversLatch; private final CountDownLatch serversLatch;
//根ClassLoader //根ClassLoader
private final NodeClassLoader classLoader; private final RedkaleClassLoader classLoader;
//Server根ClassLoader
private final RedkaleClassLoader serverClassLoader;
private Application(final AnyValue config) { private Application(final AnyValue config) {
this(false, config); this(false, config);
@@ -227,12 +232,14 @@ public final class Application {
} }
this.logger = Logger.getLogger(this.getClass().getSimpleName()); this.logger = Logger.getLogger(this.getClass().getSimpleName());
this.serversLatch = new CountDownLatch(config.getAnyValues("server").length + 1); this.serversLatch = new CountDownLatch(config.getAnyValues("server").length + 1);
this.classLoader = new RedkaleClassLoader(Thread.currentThread().getContextClassLoader());
logger.log(Level.INFO, "------------------------------- Redkale " + Redkale.getDotedVersion() + " -------------------------------"); logger.log(Level.INFO, "------------------------------- Redkale " + Redkale.getDotedVersion() + " -------------------------------");
//------------------配置 <transport> 节点 ------------------ //------------------配置 <transport> 节点 ------------------
ObjectPool<ByteBuffer> transportPool = null; ObjectPool<ByteBuffer> transportPool = null;
ExecutorService transportExec = null; ExecutorService transportExec = null;
AsynchronousChannelGroup transportGroup = null; AsynchronousChannelGroup transportGroup = null;
final AnyValue resources = config.getAnyValue("resources"); final AnyValue resources = config.getAnyValue("resources");
TransportStrategy strategy = null;
if (resources != null) { if (resources != null) {
AnyValue transportConf = resources.getAnyValue("transport"); AnyValue transportConf = resources.getAnyValue("transport");
int groupsize = resources.getAnyValues("group").length; int groupsize = resources.getAnyValues("group").length;
@@ -252,6 +259,10 @@ public final class Application {
}); });
//-----------transportChannelGroup-------------- //-----------transportChannelGroup--------------
try { try {
final String strategyClass = transportConf.getValue("strategy");
if (strategyClass != null && !strategyClass.isEmpty()) {
strategy = (TransportStrategy) classLoader.loadClass(strategyClass).newInstance();
}
final AtomicInteger counter = new AtomicInteger(); final AtomicInteger counter = new AtomicInteger();
transportExec = Executors.newFixedThreadPool(threads, (Runnable r) -> { transportExec = Executors.newFixedThreadPool(threads, (Runnable r) -> {
Thread t = new Thread(r); Thread t = new Thread(r);
@@ -266,9 +277,9 @@ public final class Application {
logger.log(Level.INFO, Transport.class.getSimpleName() + " configure bufferCapacity = " + bufferCapacity + "; bufferPoolSize = " + bufferPoolSize + "; threads = " + threads + ";"); logger.log(Level.INFO, Transport.class.getSimpleName() + " configure bufferCapacity = " + bufferCapacity + "; bufferPoolSize = " + bufferPoolSize + "; threads = " + threads + ";");
} }
} }
this.transportFactory = new TransportFactory(transportExec, transportPool, transportGroup); this.transportFactory = new TransportFactory(transportExec, transportPool, transportGroup, strategy);
this.classLoader = new NodeClassLoader(Thread.currentThread().getContextClassLoader());
Thread.currentThread().setContextClassLoader(this.classLoader); Thread.currentThread().setContextClassLoader(this.classLoader);
this.serverClassLoader = new RedkaleClassLoader(this.classLoader);
} }
public ResourceFactory getResourceFactory() { public ResourceFactory getResourceFactory() {
@@ -279,10 +290,14 @@ public final class Application {
return transportFactory; return transportFactory;
} }
public NodeClassLoader getNodeClassLoader() { public RedkaleClassLoader getClassLoader() {
return classLoader; return classLoader;
} }
public RedkaleClassLoader getServerClassLoader() {
return serverClassLoader;
}
public List<NodeServer> getNodeServers() { public List<NodeServer> getNodeServers() {
return new ArrayList<>(servers); return new ArrayList<>(servers);
} }
@@ -295,6 +310,10 @@ public final class Application {
return startTime; return startTime;
} }
public AnyValue getAppConfig() {
return config;
}
public void init() throws Exception { public void init() throws Exception {
System.setProperty("java.util.concurrent.ForkJoinPool.common.parallelism", "" + Runtime.getRuntime().availableProcessors() * 4); System.setProperty("java.util.concurrent.ForkJoinPool.common.parallelism", "" + Runtime.getRuntime().availableProcessors() * 4);
System.setProperty("convert.bson.tiny", "true"); System.setProperty("convert.bson.tiny", "true");
@@ -310,7 +329,7 @@ public final class Application {
logger.log(Level.INFO, RESNAME_APP_HOME + "= " + homepath + "\r\n" + RESNAME_APP_ADDR + "= " + this.localAddress.getHostAddress()); logger.log(Level.INFO, RESNAME_APP_HOME + "= " + homepath + "\r\n" + RESNAME_APP_ADDR + "= " + this.localAddress.getHostAddress());
String lib = config.getValue("lib", "").trim().replace("${APP_HOME}", homepath); String lib = config.getValue("lib", "").trim().replace("${APP_HOME}", homepath);
lib = lib.isEmpty() ? (homepath + "/conf") : (lib + ";" + homepath + "/conf"); lib = lib.isEmpty() ? (homepath + "/conf") : (lib + ";" + homepath + "/conf");
Server.loadLib(logger, lib); Server.loadLib(classLoader, logger, lib);
//------------------------------------------------------------------------ //------------------------------------------------------------------------
final AnyValue resources = config.getAnyValue("resources"); final AnyValue resources = config.getAnyValue("resources");
@@ -354,6 +373,8 @@ public final class Application {
this.resourceFactory.register(JsonFactory.root()); this.resourceFactory.register(JsonFactory.root());
this.resourceFactory.register(BsonFactory.root().getConvert()); this.resourceFactory.register(BsonFactory.root().getConvert());
this.resourceFactory.register(JsonFactory.root().getConvert()); this.resourceFactory.register(JsonFactory.root().getConvert());
this.resourceFactory.register("bsonconvert", Convert.class, BsonFactory.root().getConvert());
this.resourceFactory.register("jsonconvert", Convert.class, JsonFactory.root().getConvert());
//只有WatchService才能加载Application、WatchFactory //只有WatchService才能加载Application、WatchFactory
final Application application = this; final Application application = this;
this.resourceFactory.register(new ResourceFactory.ResourceLoader() { this.resourceFactory.register(new ResourceFactory.ResourceLoader() {
@@ -363,10 +384,12 @@ public final class Application {
try { try {
Resource res = field.getAnnotation(Resource.class); Resource res = field.getAnnotation(Resource.class);
if (res == null) return; if (res == null) return;
if (!(src instanceof WatchService) || Sncp.isRemote((Service) src)) return; //远程模式不得注入 if (Sncp.isRemote((Service) src)) return; //远程模式不得注入
Class type = field.getType(); Class type = field.getType();
if (type == Application.class) { if (type == Application.class) {
field.set(src, application); 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) { } else if (type == TransportFactory.class) {
field.set(src, application.transportFactory); field.set(src, application.transportFactory);
} else if (type == NodeSncpServer.class) { } else if (type == NodeSncpServer.class) {
@@ -413,7 +436,7 @@ public final class Application {
return false; 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(); initResources();
} }
@@ -440,6 +463,16 @@ public final class Application {
//------------------------------------------------------------------------ //------------------------------------------------------------------------
} }
public void restoreConfig() throws IOException {
synchronized (this) {
File confFile = new File(this.home, "conf/application.xml");
confFile.renameTo(new File(this.home, "conf/application_" + String.format("%1$tY%1$tm%1$td%1$tH%1$tM%1$tS", System.currentTimeMillis()) + ".xml"));
final PrintStream ps = new PrintStream(new FileOutputStream(confFile));
ps.append(config.toXML("application"));
ps.close();
}
}
private void startSelfServer() throws Exception { private void startSelfServer() throws Exception {
final Application application = this; final Application application = this;
new Thread() { new Thread() {
@@ -553,13 +586,12 @@ public final class Application {
others.add(entry); others.add(entry);
} }
} }
if (watchs.size() > 1) throw new RuntimeException("Found one more WATCH Server");
this.watching = !watchs.isEmpty(); this.watching = !watchs.isEmpty();
//单向SNCP服务不需要对等group
//if (!sncps.isEmpty() && globalNodes.isEmpty()) throw new RuntimeException("found SNCP Server node but not found <group> node info.");
runServers(timecd, sncps); //必须确保sncp都启动后再启动其他协议 runServers(timecd, sncps); //必须确保SNCP服务都启动后再启动其他服务
runServers(timecd, others); runServers(timecd, others);
runServers(timecd, watchs); //必须在所有server都启动后再启动 runServers(timecd, watchs); //必须在所有服务都启动后再启动WATCH服务
timecd.await(); timecd.await();
logger.info(this.getClass().getSimpleName() + " started in " + (System.currentTimeMillis() - startTime) + " ms\r\n"); logger.info(this.getClass().getSimpleName() + " started in " + (System.currentTimeMillis() - startTime) + " ms\r\n");
if (!singletonrun) this.serversLatch.await(); if (!singletonrun) this.serversLatch.await();
@@ -603,7 +635,7 @@ public final class Application {
if (!inited.get()) { if (!inited.get()) {
synchronized (nodeClasses) { synchronized (nodeClasses) {
if (!inited.getAndSet(true)) { //加载自定义的协议SOCKS if (!inited.getAndSet(true)) { //加载自定义的协议SOCKS
ClassFilter profilter = new ClassFilter(NodeProtocol.class, NodeServer.class, (Class[]) null); ClassFilter profilter = new ClassFilter(classLoader, NodeProtocol.class, NodeServer.class, (Class[]) null);
ClassFilter.Loader.load(home, serconf.getValue("excludelibs", "").split(";"), profilter); ClassFilter.Loader.load(home, serconf.getValue("excludelibs", "").split(";"), profilter);
final Set<FilterEntry<NodeServer>> entrys = profilter.getFilterEntrys(); final Set<FilterEntry<NodeServer>> entrys = profilter.getFilterEntrys();
for (FilterEntry<NodeServer> entry : entrys) { for (FilterEntry<NodeServer> entry : entrys) {

View File

@@ -58,19 +58,22 @@ public final class ClassFilter<T> {
private AnyValue conf; //基本配置信息, 当符合条件时将conf的属性赋值到FilterEntry中去。 private AnyValue conf; //基本配置信息, 当符合条件时将conf的属性赋值到FilterEntry中去。
public ClassFilter(Class<? extends Annotation> annotationClass, Class superClass, Class[] excludeSuperClasses) { private final ClassLoader classLoader;
this(annotationClass, superClass, excludeSuperClasses, null);
public ClassFilter(ClassLoader classLoader, Class<? extends Annotation> annotationClass, Class superClass, Class[] excludeSuperClasses) {
this(classLoader, annotationClass, superClass, excludeSuperClasses, null);
} }
public ClassFilter(Class<? extends Annotation> annotationClass, Class superClass, Class[] excludeSuperClasses, AnyValue conf) { public ClassFilter(ClassLoader classLoader, Class<? extends Annotation> annotationClass, Class superClass, Class[] excludeSuperClasses, AnyValue conf) {
this.annotationClass = annotationClass; this.annotationClass = annotationClass;
this.superClass = superClass; this.superClass = superClass;
this.excludeSuperClasses = excludeSuperClasses; this.excludeSuperClasses = excludeSuperClasses;
this.conf = conf; this.conf = conf;
this.classLoader = classLoader == null ? Thread.currentThread().getContextClassLoader() : classLoader;
} }
public static ClassFilter create(Class[] excludeSuperClasses, String includeregs, String excluderegs, Set<String> includeValues, Set<String> excludeValues) { public static ClassFilter create(Class[] excludeSuperClasses, String includeregs, String excluderegs, Set<String> includeValues, Set<String> excludeValues) {
ClassFilter filter = new ClassFilter(null, null, excludeSuperClasses); ClassFilter filter = new ClassFilter(null, null, null, excludeSuperClasses);
filter.setIncludePatterns(includeregs == null ? null : includeregs.split(";")); filter.setIncludePatterns(includeregs == null ? null : includeregs.split(";"));
filter.setExcludePatterns(excluderegs == null ? null : excluderegs.split(";")); filter.setExcludePatterns(excluderegs == null ? null : excluderegs.split(";"));
filter.setPrivilegeIncludes(includeValues); filter.setPrivilegeIncludes(includeValues);
@@ -96,7 +99,11 @@ public final class ClassFilter<T> {
* @return Set&lt;FilterEntry&lt;T&gt;&gt; * @return Set&lt;FilterEntry&lt;T&gt;&gt;
*/ */
public final Set<FilterEntry<T>> getFilterEntrys() { public final Set<FilterEntry<T>> getFilterEntrys() {
return entrys; HashSet<FilterEntry<T>> set = new HashSet<>();
set.addAll(entrys);
if (ors != null) ors.forEach(f -> set.addAll(f.getFilterEntrys()));
if (ands != null) ands.forEach(f -> set.addAll(f.getFilterEntrys()));
return set;
} }
/** /**
@@ -105,7 +112,11 @@ public final class ClassFilter<T> {
* @return Set&lt;FilterEntry&lt;T&gt;&gt; * @return Set&lt;FilterEntry&lt;T&gt;&gt;
*/ */
public final Set<FilterEntry<T>> getFilterExpectEntrys() { public final Set<FilterEntry<T>> getFilterExpectEntrys() {
return expectEntrys; HashSet<FilterEntry<T>> set = new HashSet<>();
set.addAll(entrys);
if (ors != null) ors.forEach(f -> set.addAll(f.getFilterExpectEntrys()));
if (ands != null) ands.forEach(f -> set.addAll(f.getFilterExpectEntrys()));
return set;
} }
/** /**
@@ -115,8 +126,8 @@ public final class ClassFilter<T> {
*/ */
public final Set<FilterEntry<T>> getAllFilterEntrys() { public final Set<FilterEntry<T>> getAllFilterEntrys() {
HashSet<FilterEntry<T>> rs = new HashSet<>(); HashSet<FilterEntry<T>> rs = new HashSet<>();
rs.addAll(entrys); rs.addAll(getFilterEntrys());
rs.addAll(expectEntrys); rs.addAll(getFilterExpectEntrys());
return rs; return rs;
} }
@@ -156,7 +167,7 @@ public final class ClassFilter<T> {
} }
if (cf == null || clazzname.startsWith("sun.")) return; if (cf == null || clazzname.startsWith("sun.")) return;
try { try {
Class clazz = Class.forName(clazzname); Class clazz = classLoader.loadClass(clazzname);
if (!cf.accept(property, clazz, autoscan)) return; if (!cf.accept(property, clazz, autoscan)) return;
if (cf.conf != null) { if (cf.conf != null) {
if (property == null) { if (property == null) {
@@ -180,7 +191,7 @@ public final class ClassFilter<T> {
} catch (Throwable cfe) { } catch (Throwable cfe) {
if (finer && !clazzname.startsWith("sun.") && !clazzname.startsWith("javax.") if (finer && !clazzname.startsWith("sun.") && !clazzname.startsWith("javax.")
&& !clazzname.startsWith("com.sun.") && !clazzname.startsWith("jdk.")) { && !clazzname.startsWith("com.sun.") && !clazzname.startsWith("jdk.")) {
//logger.log(Level.FINEST, ClassFilter.class.getSimpleName() + " filter error", cfe); logger.log(Level.FINEST, ClassFilter.class.getSimpleName() + " filter error", cfe);
} }
} }
} }
@@ -445,12 +456,12 @@ public final class ClassFilter<T> {
* @throws IOException 异常 * @throws IOException 异常
*/ */
public static void load(final File excludeFile, final String[] excludeRegs, final ClassFilter... filters) throws IOException { public static void load(final File excludeFile, final String[] excludeRegs, final ClassFilter... filters) throws IOException {
URLClassLoader loader = (URLClassLoader) Thread.currentThread().getContextClassLoader(); RedkaleClassLoader loader = (RedkaleClassLoader) Thread.currentThread().getContextClassLoader();
List<URL> urlfiles = new ArrayList<>(2); List<URL> urlfiles = new ArrayList<>(2);
List<URL> urljares = new ArrayList<>(2); List<URL> urljares = new ArrayList<>(2);
final URL exurl = excludeFile != null ? excludeFile.toURI().toURL() : null; final URL exurl = excludeFile != null ? excludeFile.toURI().toURL() : null;
final Pattern[] excludePatterns = toPattern(excludeRegs); final Pattern[] excludePatterns = toPattern(excludeRegs);
for (URL url : loader.getURLs()) { for (URL url : loader.getAllURLs()) {
if (exurl != null && exurl.sameFile(url)) continue; if (exurl != null && exurl.sameFile(url)) continue;
if (excludePatterns != null) { if (excludePatterns != null) {
boolean skip = false; boolean skip = false;
@@ -482,6 +493,12 @@ public final class ClassFilter<T> {
if (entryname.endsWith(".class") && entryname.indexOf('$') < 0) { if (entryname.endsWith(".class") && entryname.indexOf('$') < 0) {
String classname = entryname.substring(0, entryname.length() - 6); String classname = entryname.substring(0, entryname.length() - 6);
if (classname.startsWith("javax.") || classname.startsWith("com.sun.")) continue; if (classname.startsWith("javax.") || classname.startsWith("com.sun.")) continue;
//常见的jar跳过
if (classname.startsWith("com.mysql.")) break;
if (classname.startsWith("org.mariadb.")) break;
if (classname.startsWith("oracle.jdbc.")) break;
if (classname.startsWith("org.postgresql.")) break;
if (classname.startsWith("com.microsoft.sqlserver.")) break;
classes.add(classname); classes.add(classname);
if (debug) debugstr.append(classname).append("\r\n"); if (debug) debugstr.append(classname).append("\r\n");
for (final ClassFilter filter : filters) { for (final ClassFilter filter : filters) {

View File

@@ -1,28 +0,0 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.boot;
import java.net.*;
/**
*
* @author zhangjx
*/
public class NodeClassLoader extends URLClassLoader {
public NodeClassLoader(ClassLoader parent) {
super(new URL[0], parent);
}
public Class<?> loadClass(String name, byte[] b) {
return defineClass(name, b, 0, b.length);
}
@Override
public void addURL(URL url) {
super.addURL(url);
}
}

View File

@@ -109,7 +109,7 @@ public class NodeHttpServer extends NodeServer {
synchronized (regFactory) { synchronized (regFactory) {
Service nodeService = (Service) rf.find(resourceName, WebSocketNode.class); Service nodeService = (Service) rf.find(resourceName, WebSocketNode.class);
if (nodeService == null) { if (nodeService == null) {
nodeService = Sncp.createLocalService(resourceName, WebSocketNodeService.class, application.getResourceFactory(), application.getTransportFactory(), (InetSocketAddress) null, (Set<String>) null, (AnyValue) null); nodeService = Sncp.createLocalService(serverClassLoader, resourceName, WebSocketNodeService.class, application.getResourceFactory(), application.getTransportFactory(), (InetSocketAddress) null, (Set<String>) null, (AnyValue) null);
regFactory.register(resourceName, WebSocketNode.class, nodeService); regFactory.register(resourceName, WebSocketNode.class, nodeService);
} }
resourceFactory.inject(nodeService, self); resourceFactory.inject(nodeService, self);
@@ -210,8 +210,9 @@ public class NodeHttpServer extends NodeServer {
final boolean autoload = restConf.getBoolValue("autoload", true); final boolean autoload = restConf.getBoolValue("autoload", true);
{ //加载RestService { //加载RestService
String userTypeStr = restConf.getValue("usertype"); String userTypeStr = restConf.getValue("usertype");
final Class userType = userTypeStr == null ? null : Class.forName(userTypeStr); final Class userType = userTypeStr == null ? null : this.serverClassLoader.loadClass(userTypeStr);
final Class baseServletType = Class.forName(restConf.getValue("base", HttpServlet.class.getName()));
final Class baseServletType = this.serverClassLoader.loadClass(restConf.getValue("base", HttpServlet.class.getName()));
final Set<String> includeValues = new HashSet<>(); final Set<String> includeValues = new HashSet<>();
final Set<String> excludeValues = new HashSet<>(); final Set<String> excludeValues = new HashSet<>();
for (AnyValue item : restConf.getAnyValues("service")) { for (AnyValue item : restConf.getAnyValues("service")) {
@@ -238,14 +239,17 @@ public class NodeHttpServer extends NodeServer {
return; return;
} }
restedObjects.add(service); //避免重复创建Rest对象 restedObjects.add(service); //避免重复创建Rest对象
HttpServlet servlet = httpServer.addRestServlet(service, userType, baseServletType, prefix); HttpServlet servlet = httpServer.addRestServlet(serverClassLoader, service, userType, baseServletType, prefix);
if (servlet == null) return; //没有HttpMapping方法的HttpServlet调用Rest.createRestServlet就会返回null if (servlet == null) return; //没有HttpMapping方法的HttpServlet调用Rest.createRestServlet就会返回null
String prefix2 = prefix;
WebServlet ws = servlet.getClass().getAnnotation(WebServlet.class);
if (ws != null && !ws.repair()) prefix2 = "";
resourceFactory.inject(servlet, NodeHttpServer.this); resourceFactory.inject(servlet, NodeHttpServer.this);
if (finest) logger.finest(threadName + " Create RestServlet(resource.name='" + name + "') = " + servlet); if (finest) logger.finest(threadName + " Create RestServlet(resource.name='" + name + "') = " + servlet);
if (ss != null) { if (ss != null) {
String[] mappings = servlet.getClass().getAnnotation(WebServlet.class).value(); String[] mappings = servlet.getClass().getAnnotation(WebServlet.class).value();
for (int i = 0; i < mappings.length; i++) { for (int i = 0; i < mappings.length; i++) {
mappings[i] = prefix + mappings[i]; mappings[i] = prefix2 + mappings[i];
} }
ss.add(new AbstractMap.SimpleEntry<>(servlet.getClass().getName(), mappings)); ss.add(new AbstractMap.SimpleEntry<>(servlet.getClass().getName(), mappings));
} }
@@ -288,14 +292,17 @@ public class NodeHttpServer extends NodeServer {
return; return;
} }
restedObjects.add(stype); //避免重复创建Rest对象 restedObjects.add(stype); //避免重复创建Rest对象
HttpServlet servlet = httpServer.addRestWebSocketServlet(stype, prefix, en.getProperty()); HttpServlet servlet = httpServer.addRestWebSocketServlet(serverClassLoader, stype, prefix, en.getProperty());
if (servlet == null) return; //没有RestOnMessage方法的HttpServlet调用Rest.createRestWebSocketServlet就会返回null if (servlet == null) return; //没有RestOnMessage方法的HttpServlet调用Rest.createRestWebSocketServlet就会返回null
String prefix2 = prefix;
WebServlet ws = servlet.getClass().getAnnotation(WebServlet.class);
if (ws != null && !ws.repair()) prefix2 = "";
resourceFactory.inject(servlet, NodeHttpServer.this); resourceFactory.inject(servlet, NodeHttpServer.this);
if (finest) logger.finest(threadName + " " + stype.getName() + " create RestWebSocketServlet " + servlet); if (finest) logger.finest(threadName + " " + stype.getName() + " create RestWebSocketServlet " + servlet);
if (ss != null) { if (ss != null) {
String[] mappings = servlet.getClass().getAnnotation(WebServlet.class).value(); String[] mappings = servlet.getClass().getAnnotation(WebServlet.class).value();
for (int i = 0; i < mappings.length; i++) { for (int i = 0; i < mappings.length; i++) {
mappings[i] = prefix + mappings[i]; mappings[i] = prefix2 + mappings[i];
} }
ss.add(new AbstractMap.SimpleEntry<>(servlet.getClass().getName(), mappings)); ss.add(new AbstractMap.SimpleEntry<>(servlet.getClass().getName(), mappings));
} }

View File

@@ -5,6 +5,7 @@
*/ */
package org.redkale.boot; package org.redkale.boot;
import org.redkale.util.RedkaleClassLoader;
import java.io.*; import java.io.*;
import java.lang.annotation.Annotation; import java.lang.annotation.Annotation;
import java.lang.reflect.*; import java.lang.reflect.*;
@@ -54,7 +55,9 @@ public abstract class NodeServer {
protected final Server server; protected final Server server;
//ClassLoader //ClassLoader
protected final NodeClassLoader classLoader; protected RedkaleClassLoader serverClassLoader;
protected final Thread serverThread;
//当前Server的SNCP协议的组 //当前Server的SNCP协议的组
protected String sncpGroup = null; protected String sncpGroup = null;
@@ -89,35 +92,9 @@ public abstract class NodeServer {
this.resourceFactory = application.getResourceFactory().createChild(); this.resourceFactory = application.getResourceFactory().createChild();
this.server = server; this.server = server;
this.logger = Logger.getLogger(this.getClass().getSimpleName()); this.logger = Logger.getLogger(this.getClass().getSimpleName());
this.classLoader = new NodeClassLoader(Thread.currentThread().getContextClassLoader()); this.serverClassLoader = new RedkaleClassLoader(application.getServerClassLoader());
Thread.currentThread().setContextClassLoader(this.classLoader); Thread.currentThread().setContextClassLoader(this.serverClassLoader);
} this.serverThread = Thread.currentThread();
protected Consumer<Runnable> getExecutor() throws Exception {
if (server == null) return null;
final Field field = Server.class.getDeclaredField("context");
field.setAccessible(true);
return new Consumer<Runnable>() {
private Context context;
@Override
public void accept(Runnable t) {
if (context == null && server != null) {
try {
this.context = (Context) field.get(server);
} catch (Exception e) {
logger.log(Level.SEVERE, "Server (" + server.getSocketAddress() + ") cannot find Context", e);
}
}
if (context == null) {
t.run();
} else {
context.submitAsync(t);
}
}
};
} }
public static <T extends NodeServer> NodeServer create(Class<T> clazz, Application application, AnyValue serconf) { public static <T extends NodeServer> NodeServer create(Class<T> clazz, Application application, AnyValue serconf) {
@@ -156,9 +133,9 @@ public abstract class NodeServer {
resourceFactory.register(Server.RESNAME_SERVER_ROOT, File.class, myroot.getCanonicalFile()); resourceFactory.register(Server.RESNAME_SERVER_ROOT, File.class, myroot.getCanonicalFile());
resourceFactory.register(Server.RESNAME_SERVER_ROOT, Path.class, myroot.toPath()); resourceFactory.register(Server.RESNAME_SERVER_ROOT, Path.class, myroot.toPath());
final String homepath = myroot.getCanonicalPath();
//加入指定的classpath //加入指定的classpath
Server.loadLib(logger, this.serverConf.getValue("lib", "").replace("${APP_HOME}", homepath) + ";" + homepath + "/lib/*;" + homepath + "/classes"); Server.loadLib(serverClassLoader, logger, this.serverConf.getValue("lib", "${APP_HOME}/libs/*").replace("${APP_HOME}", application.getHome().getPath().replace('\\', '/')));
this.serverThread.setContextClassLoader(this.serverClassLoader);
} }
//必须要进行初始化, 构建Service时需要使用Context中的ExecutorService //必须要进行初始化, 构建Service时需要使用Context中的ExecutorService
server.init(this.serverConf); server.init(this.serverConf);
@@ -166,7 +143,7 @@ public abstract class NodeServer {
initResource(); //给 DataSource、CacheSource 注册依赖注入时的监听回调事件。 initResource(); //给 DataSource、CacheSource 注册依赖注入时的监听回调事件。
String interceptorClass = this.serverConf.getValue("interceptor", ""); String interceptorClass = this.serverConf.getValue("interceptor", "");
if (!interceptorClass.isEmpty()) { if (!interceptorClass.isEmpty()) {
Class clazz = Class.forName(interceptorClass); Class clazz = serverClassLoader.loadClass(interceptorClass);
this.interceptor = (NodeInterceptor) clazz.newInstance(); this.interceptor = (NodeInterceptor) clazz.newInstance();
} }
@@ -200,7 +177,7 @@ public abstract class NodeServer {
if (resources != null) { if (resources != null) {
for (AnyValue sourceConf : resources.getAnyValues("source")) { for (AnyValue sourceConf : resources.getAnyValues("source")) {
try { try {
Class type = Class.forName(sourceConf.getValue("value")); Class type = serverClassLoader.loadClass(sourceConf.getValue("value"));
if (!Service.class.isAssignableFrom(type)) { if (!Service.class.isAssignableFrom(type)) {
logger.log(Level.SEVERE, "load application source resource, but not Service error: " + sourceConf); logger.log(Level.SEVERE, "load application source resource, but not Service error: " + sourceConf);
} else if (CacheSource.class.isAssignableFrom(type)) { } else if (CacheSource.class.isAssignableFrom(type)) {
@@ -256,7 +233,7 @@ public abstract class NodeServer {
final Set<String> groups = new HashSet<>(); final Set<String> groups = new HashSet<>();
if (client != null && client.getSameGroup() != null) groups.add(client.getSameGroup()); if (client != null && client.getSameGroup() != null) groups.add(client.getSameGroup());
if (client != null && client.getDiffGroups() != null) groups.addAll(client.getDiffGroups()); if (client != null && client.getDiffGroups() != null) groups.addAll(client.getDiffGroups());
Service cacheListenerService = Sncp.createLocalService(resourceName, DataCacheListenerService.class, appResFactory, appTranFactory, sncpAddr, groups, Sncp.getConf((Service) src)); Service cacheListenerService = Sncp.createLocalService(serverClassLoader, resourceName, DataCacheListenerService.class, appResFactory, appTranFactory, sncpAddr, groups, Sncp.getConf((Service) src));
appResFactory.register(resourceName, DataCacheListener.class, cacheListenerService); appResFactory.register(resourceName, DataCacheListener.class, cacheListenerService);
localServices.add(cacheListenerService); localServices.add(cacheListenerService);
sncpServer.consumerAccept(cacheListenerService); sncpServer.consumerAccept(cacheListenerService);
@@ -282,11 +259,11 @@ public abstract class NodeServer {
SncpClient client = Sncp.getSncpClient(srcService); SncpClient client = Sncp.getSncpClient(srcService);
final InetSocketAddress sncpAddr = client == null ? null : client.getClientAddress(); final InetSocketAddress sncpAddr = client == null ? null : client.getClientAddress();
final AnyValue sourceConf = cacheResource.get(resourceName); final AnyValue sourceConf = cacheResource.get(resourceName);
final Class sourceType = sourceConf == null ? CacheMemorySource.class : Class.forName(sourceConf.getValue("type")); final Class sourceType = sourceConf == null ? CacheMemorySource.class : serverClassLoader.loadClass(sourceConf.getValue("type"));
final Set<String> groups = new HashSet<>(); final Set<String> groups = new HashSet<>();
if (client != null && client.getSameGroup() != null) groups.add(client.getSameGroup()); if (client != null && client.getSameGroup() != null) groups.add(client.getSameGroup());
if (client != null && client.getDiffGroups() != null) groups.addAll(client.getDiffGroups()); if (client != null && client.getDiffGroups() != null) groups.addAll(client.getDiffGroups());
final CacheSource source = (CacheSource) Sncp.createLocalService(resourceName, sourceType, appResFactory, appTranFactory, sncpAddr, groups, Sncp.getConf(srcService)); final CacheSource source = (CacheSource) Sncp.createLocalService(serverClassLoader, resourceName, sourceType, appResFactory, appTranFactory, sncpAddr, groups, Sncp.getConf(srcService));
Type genericType = field.getGenericType(); Type genericType = field.getGenericType();
ParameterizedType pt = (genericType instanceof ParameterizedType) ? (ParameterizedType) genericType : null; ParameterizedType pt = (genericType instanceof ParameterizedType) ? (ParameterizedType) genericType : null;
Type valType = pt == null ? null : pt.getActualTypeArguments()[1]; Type valType = pt == null ? null : pt.getActualTypeArguments()[1];
@@ -357,9 +334,9 @@ public abstract class NodeServer {
Service service; Service service;
boolean ws = src instanceof WebSocketServlet; boolean ws = src instanceof WebSocketServlet;
if (ws || localed) { //本地模式 if (ws || localed) { //本地模式
service = Sncp.createLocalService(resourceName, serviceImplClass, appResourceFactory, appTransportFactory, NodeServer.this.sncpAddress, groups, entry.getProperty()); service = Sncp.createLocalService(serverClassLoader, resourceName, serviceImplClass, appResourceFactory, appTransportFactory, NodeServer.this.sncpAddress, groups, entry.getProperty());
} else { } else {
service = Sncp.createRemoteService(resourceName, serviceImplClass, appTransportFactory, NodeServer.this.sncpAddress, groups, entry.getProperty()); service = Sncp.createRemoteService(serverClassLoader, resourceName, serviceImplClass, appTransportFactory, NodeServer.this.sncpAddress, groups, entry.getProperty());
} }
if (SncpClient.parseMethod(serviceImplClass).isEmpty()) return; //class没有可用的方法 通常为BaseService if (SncpClient.parseMethod(serviceImplClass).isEmpty()) return; //class没有可用的方法 通常为BaseService
@@ -449,62 +426,6 @@ public abstract class NodeServer {
maxClassNameLength = Math.max(maxClassNameLength, Sncp.getResourceType(y).getName().length() + 1); maxClassNameLength = Math.max(maxClassNameLength, Sncp.getResourceType(y).getName().length() + 1);
} }
/*
* protected List<Transport> loadTransports(final HashSet<String> groups) {
* if (groups == null) return null;
* final List<Transport> transports = new ArrayList<>();
* for (String group : groups) {
* if (this.sncpGroup == null || !this.sncpGroup.equals(group)) {
* transports.add(loadTransport(group));
* }
* }
* return transports;
* }
* protected Transport loadTransport(final HashSet<String> groups) {
* if (groups == null || groups.isEmpty()) return null;
* final String groupid = new ArrayList<>(groups).stream().sorted().collect(Collectors.joining(";")); //按字母排列顺序
* Transport transport = application.resourceFactory.find(groupid, Transport.class);
* if (transport != null) return transport;
* final List<Transport> transports = new ArrayList<>();
* for (String group : groups) {
* transports.add(loadTransport(group));
* }
* Set<InetSocketAddress> addrs = new HashSet();
* transports.forEach(t -> addrs.addAll(Arrays.asList(t.getRemoteAddresses())));
* Transport first = transports.get(0);
* TransportGroupInfo ginfo = application.transportFactory.findGroupInfo(first.getName());
* Transport newTransport = new Transport(groupid, ginfo.getProtocol(),
* ginfo.getSubprotocol(), application.transportBufferPool, application.transportChannelGroup, this.sncpAddress, addrs);
* synchronized (application.resourceFactory) {
* transport = application.resourceFactory.find(groupid, Transport.class);
* if (transport == null) {
* transport = newTransport;
* application.resourceFactory.register(groupid, transport);
* }
* }
* return transport;
* }
*
* protected Transport loadTransport(final String group) {
* if (group == null) return null;
* Transport transport;
* synchronized (application.resourceFactory) {
* transport = application.resourceFactory.find(group, Transport.class);
* if (transport != null) {
* if (this.sncpAddress != null && !this.sncpAddress.equals(transport.getClientAddress())) {
* throw new RuntimeException(transport + "repeat create on newClientAddress = " + this.sncpAddress + ", oldClientAddress = " + transport.getClientAddress());
* }
* return transport;
* }
* TransportGroupInfo ginfo = application.findGroupInfo(group);
* Set<InetSocketAddress> addrs = ginfo.copyAddresses();
* if (addrs == null) throw new RuntimeException("Not found <group> = " + group + " on <resources> ");
* transport = new Transport(group, ginfo.getProtocol(), ginfo.getSubprotocol(), application.transportBufferPool, application.transportChannelGroup, this.sncpAddress, addrs);
* application.resourceFactory.register(group, transport);
* }
* return transport;
* }
*/
protected abstract ClassFilter<Filter> createFilterClassFilter(); protected abstract ClassFilter<Filter> createFilterClassFilter();
protected abstract ClassFilter<Servlet> createServletClassFilter(); protected abstract ClassFilter<Servlet> createServletClassFilter();
@@ -514,12 +435,12 @@ public abstract class NodeServer {
} }
protected ClassFilter<Service> createServiceClassFilter() { 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, protected ClassFilter createClassFilter(final String localGroup, Class<? extends Annotation> ref,
Class inter, Class[] excludeSuperClasses, Class<? extends Annotation> ref2, String properties, String property) { Class inter, Class[] excludeSuperClasses, Class<? extends Annotation> ref2, String properties, String property) {
ClassFilter cf = new ClassFilter(ref, inter, excludeSuperClasses, null); ClassFilter cf = new ClassFilter(this.serverClassLoader, ref, inter, excludeSuperClasses, null);
if (properties == null && properties == null) { if (properties == null && properties == null) {
cf.setRefused(true); cf.setRefused(true);
return cf; return cf;
@@ -546,7 +467,7 @@ public abstract class NodeServer {
prop = new AnyValue.DefaultAnyValue(); prop = new AnyValue.DefaultAnyValue();
prop.addValue("groups", sc); prop.addValue("groups", sc);
} }
ClassFilter filter = new ClassFilter(ref, inter, excludeSuperClasses, prop); ClassFilter filter = new ClassFilter(this.serverClassLoader, ref, inter, excludeSuperClasses, prop);
for (AnyValue av : list.getAnyValues(property)) { // <service>、<filter>、<servlet> 节点 for (AnyValue av : list.getAnyValues(property)) { // <service>、<filter>、<servlet> 节点
final AnyValue[] items = av.getAnyValues("property"); final AnyValue[] items = av.getAnyValues("property");
if (av instanceof DefaultAnyValue && items.length > 0) { //存在 <property>节点 if (av instanceof DefaultAnyValue && items.length > 0) { //存在 <property>节点
@@ -570,7 +491,9 @@ public abstract class NodeServer {
dav.addValue("properties", ps); dav.addValue("properties", ps);
av = dav; av = dav;
} }
filter.filter(av, av.getValue("value"), false); if (!av.getBoolValue("ignore", false)) {
filter.filter(av, av.getValue("value"), false);
}
} }
if (list.getBoolValue("autoload", true)) { if (list.getBoolValue("autoload", true)) {
String includes = list.getValue("includes", ""); String includes = list.getValue("includes", "");
@@ -593,12 +516,22 @@ public abstract class NodeServer {
return false; return false;
} }
public boolean isWATCH() {
return false;
}
public ResourceFactory getResourceFactory() { public ResourceFactory getResourceFactory() {
return resourceFactory; return resourceFactory;
} }
public NodeClassLoader getNodeClassLoader() { public RedkaleClassLoader getServerClassLoader() {
return classLoader; return serverClassLoader;
}
public void setServerClassLoader(RedkaleClassLoader serverClassLoader) {
Objects.requireNonNull(this.serverClassLoader);
this.serverClassLoader = serverClassLoader;
this.serverThread.setContextClassLoader(serverClassLoader);
} }
public InetSocketAddress getSncpAddress() { public InetSocketAddress getSncpAddress() {

View File

@@ -107,7 +107,7 @@ public class NodeSncpServer extends NodeServer {
@Override @Override
protected ClassFilter<Filter> createFilterClassFilter() { 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 @Override

View File

@@ -37,4 +37,14 @@ public class NodeWatchServer extends NodeHttpServer {
protected ClassFilter<Servlet> createServletClassFilter() { protected ClassFilter<Servlet> createServletClassFilter() {
return createClassFilter(null, WebServlet.class, WatchServlet.class, null, null, "servlets", "servlet"); return createClassFilter(null, WebServlet.class, WatchServlet.class, null, null, "servlets", "servlet");
} }
@Override
protected ClassFilter createOtherClassFilter() {
return null;
}
@Override
public boolean isWATCH() {
return true;
}
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -129,7 +129,7 @@ public abstract class ConvertFactory<R extends Reader, W extends Writer> {
public abstract ConvertType getConvertType(); public abstract ConvertType getConvertType();
public abstract boolean isReversible(); public abstract boolean isReversible(); //是否可逆的
public abstract ConvertFactory createChild(); public abstract ConvertFactory createChild();
@@ -253,7 +253,7 @@ public abstract class ConvertFactory<R extends Reader, W extends Writer> {
Class clazz = findEntityAlias(name); Class clazz = findEntityAlias(name);
try { try {
return clazz == null ? Class.forName(name) : clazz; return clazz == null ? Thread.currentThread().getContextClassLoader().loadClass(name) : clazz;
} catch (Exception ex) { } catch (Exception ex) {
throw new ConvertException("convert entity is " + name, ex); throw new ConvertException("convert entity is " + name, ex);
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -162,7 +162,7 @@ public class JsonByteBufferReader extends JsonReader {
public final boolean hasNext() { public final boolean hasNext() {
char ch = nextGoodChar(); char ch = nextGoodChar();
if (ch == ',') return true; if (ch == ',') return true;
if (ch == '}' || ch == ']') return false; if (ch == '}' || ch == ']' || ch == 0) return false;
backChar(ch); // { [ 交由 readObjectB 或 readMapB 或 readArrayB 读取 backChar(ch); // { [ 交由 readObjectB 或 readMapB 或 readArrayB 读取
return true; return true;
} }
@@ -253,6 +253,7 @@ public class JsonByteBufferReader extends JsonReader {
throw new ConvertException("illegal escape(" + c + ") (position = " + this.position + ")"); throw new ConvertException("illegal escape(" + c + ") (position = " + this.position + ")");
} }
} else if (ch == ',' || ch == ']' || ch == '}' || ch <= ' ' || ch == ':') { // ch <= ' ' 包含 0 } else if (ch == ',' || ch == ']' || ch == '}' || ch <= ' ' || ch == ':') { // ch <= ' ' 包含 0
backChar(ch);
break; break;
} else { } else {
sb.append(ch); sb.append(ch);

View File

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

View File

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

View File

@@ -19,7 +19,7 @@ import org.redkale.util.*;
* @param <R> Request的子类型 * @param <R> Request的子类型
* @param <P> Response的子类型 * @param <P> Response的子类型
*/ */
public abstract class Filter<C extends Context, R extends Request<C>, P extends Response<C, R>> implements Comparable, Resourcable { public abstract class Filter<C extends Context, R extends Request<C>, P extends Response<C, R>> implements Comparable {
AnyValue _conf; //当前Filter的配置 AnyValue _conf; //当前Filter的配置
@@ -33,11 +33,6 @@ public abstract class Filter<C extends Context, R extends Request<C>, P extends
public void destroy(C context, AnyValue config) { public void destroy(C context, AnyValue config) {
} }
@Override
public String resourceName() {
return "";
}
/** /**
* 值越小越靠前执行 * 值越小越靠前执行
* *
@@ -48,7 +43,7 @@ public abstract class Filter<C extends Context, R extends Request<C>, P extends
} }
@Override @Override
public int compareTo(Object o) { public final int compareTo(Object o) {
if (!(o instanceof Filter)) return 1; if (!(o instanceof Filter)) return 1;
return this.getIndex() - ((Filter) o).getIndex(); return this.getIndex() - ((Filter) o).getIndex();
} }

View File

@@ -63,6 +63,24 @@ public abstract class PrepareServlet<K extends Serializable, C extends Context,
} }
} }
public boolean containsServlet(Class<? extends S> servletClass) {
synchronized (lock1) {
for (S servlet : new HashSet<>(servlets)) {
if (servlet.getClass().equals(servletClass)) return true;
}
return false;
}
}
public boolean containsServlet(String servletClassName) {
synchronized (lock1) {
for (S servlet : new HashSet<>(servlets)) {
if (servlet.getClass().getName().equals(servletClassName)) return true;
}
return false;
}
}
protected void putMapping(K key, S servlet) { protected void putMapping(K key, S servlet) {
synchronized (lock2) { synchronized (lock2) {
Map<K, S> newmappings = new HashMap<>(mappings); Map<K, S> newmappings = new HashMap<>(mappings);
@@ -132,24 +150,39 @@ public abstract class PrepareServlet<K extends Serializable, C extends Context,
filter._conf = conf; filter._conf = conf;
synchronized (filters) { synchronized (filters) {
this.filters.add(filter); this.filters.add(filter);
Collections.sort(this.filters);
} }
} }
public Filter<C, R, P> removeFilter(Class<? extends Filter<C, R, P>> filterClass) { public <T extends Filter<C, R, P>> T removeFilter(Class<T> filterClass) {
return removeFilter(f -> filterClass.equals(f.getClass())); return removeFilter(f -> filterClass.equals(f.getClass()));
} }
public Filter<C, R, P> removeFilter(String filterName) { public boolean containsFilter(Class<? extends Filter> filterClass) {
return removeFilter(f -> filterName.equals(f.resourceName())); if (this.headFilter == null || filterClass == null) return false;
Filter filter = this.headFilter;
do {
if (filter.getClass().equals(filterClass)) return true;
} while ((filter = filter._next) != null);
return false;
} }
public Filter<C, R, P> removeFilter(Predicate<Filter<C, R, P>> predicate) { public boolean containsFilter(String filterClassName) {
if (this.headFilter == null || filterClassName == null) return false;
Filter filter = this.headFilter;
do {
if (filter.getClass().getName().equals(filterClassName)) return true;
} while ((filter = filter._next) != null);
return false;
}
public <T extends Filter<C, R, P>> T removeFilter(Predicate<T> predicate) {
if (this.headFilter == null || predicate == null) return null; if (this.headFilter == null || predicate == null) return null;
synchronized (filters) { synchronized (filters) {
Filter filter = this.headFilter; Filter filter = this.headFilter;
Filter prev = null; Filter prev = null;
do { do {
if (predicate.test(filter)) break; if (predicate.test((T) filter)) break;
prev = filter; prev = filter;
} while ((filter = filter._next) != null); } while ((filter = filter._next) != null);
if (filter != null) { if (filter != null) {
@@ -161,10 +194,14 @@ public abstract class PrepareServlet<K extends Serializable, C extends Context,
filter._next = null; filter._next = null;
this.filters.remove(filter); this.filters.remove(filter);
} }
return filter; return (T) filter;
} }
} }
public <T extends Filter<C, R, P>> List<T> getFilters() {
return (List) new ArrayList<>(filters);
}
public abstract void addServlet(S servlet, Object attachment, AnyValue conf, K... mappings); public abstract void addServlet(S servlet, Object attachment, AnyValue conf, K... mappings);
public final void prepare(final ByteBuffer buffer, final R request, final P response) throws IOException { public final void prepare(final ByteBuffer buffer, final R request, final P response) throws IOException {
@@ -226,7 +263,7 @@ public abstract class PrepareServlet<K extends Serializable, C extends Context,
servlet._conf = conf; servlet._conf = conf;
} }
public Set<S> getServlets() { public List<S> getServlets() {
return new LinkedHashSet<>(servlets); return new ArrayList<>(servlets);
} }
} }

View File

@@ -170,18 +170,6 @@ public abstract class Response<C extends Context, R extends Request<C>> {
} }
} }
/**
* 使用 public void recycleListener(BiConsumer recycleListener) 代替
*
* @param recycleListener BiConsumer
*
* @deprecated
*/
@Deprecated
public void setRecycleListener(BiConsumer<R, Response<C, R>> recycleListener) {
this.recycleListener = recycleListener;
}
public void recycleListener(BiConsumer<R, Response<C, R>> recycleListener) { public void recycleListener(BiConsumer<R, Response<C, R>> recycleListener) {
this.recycleListener = recycleListener; this.recycleListener = recycleListener;
} }

View File

@@ -6,7 +6,6 @@
package org.redkale.net; package org.redkale.net;
import java.io.*; import java.io.*;
import java.lang.reflect.Method;
import java.net.*; import java.net.*;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import java.text.*; import java.text.*;
@@ -14,7 +13,7 @@ import java.util.*;
import java.util.concurrent.*; import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Logger; import java.util.logging.Logger;
import org.redkale.util.AnyValue; import org.redkale.util.*;
/** /**
* *
@@ -185,6 +184,52 @@ public abstract class Server<K extends Serializable, C extends Context, R extend
logger.info(this.getClass().getSimpleName() + " shutdown in " + e + " ms"); logger.info(this.getClass().getSimpleName() + " shutdown in " + e + " ms");
} }
/**
* 判断是否存在Filter
*
* @param <T> 泛型
* @param filterClass Filter类
*
* @return boolean
*/
public <T extends Filter> boolean containsFilter(Class<T> filterClass) {
return this.prepare.containsFilter(filterClass);
}
/**
* 判断是否存在Filter
*
* @param <T> 泛型
* @param filterClassName Filter类
*
* @return boolean
*/
public <T extends Filter> boolean containsFilter(String filterClassName) {
return this.prepare.containsFilter(filterClassName);
}
/**
* 判断是否存在Servlet
*
* @param servletClass Servlet类
*
* @return boolean
*/
public boolean containsServlet(Class<? extends S> servletClass) {
return this.prepare.containsServlet(servletClass);
}
/**
* 判断是否存在Servlet
*
* @param servletClassName Servlet类
*
* @return boolean
*/
public boolean containsServlet(String servletClassName) {
return this.prepare.containsServlet(servletClassName);
}
/** /**
* 销毁Servlet * 销毁Servlet
* *
@@ -217,7 +262,7 @@ public abstract class Server<K extends Serializable, C extends Context, R extend
return new DecimalFormat(sf); return new DecimalFormat(sf);
} }
public static URL[] loadLib(final Logger logger, final String lib) throws Exception { public static URL[] loadLib(final RedkaleClassLoader classLoader, final Logger logger, final String lib) throws Exception {
if (lib == null || lib.isEmpty()) return new URL[0]; if (lib == null || lib.isEmpty()) return new URL[0];
final Set<URL> set = new HashSet<>(); final Set<URL> set = new HashSet<>();
for (String s : lib.split(";")) { for (String s : lib.split(";")) {
@@ -235,17 +280,8 @@ public abstract class Server<K extends Serializable, C extends Context, R extend
} }
} }
if (set.isEmpty()) return new URL[0]; if (set.isEmpty()) return new URL[0];
ClassLoader cl = Thread.currentThread().getContextClassLoader(); for (URL url : set) {
if (cl instanceof URLClassLoader) { classLoader.addURL(url);
URLClassLoader loader = (URLClassLoader) cl;
final Method method = URLClassLoader.class.getDeclaredMethod("addURL", URL.class);
method.setAccessible(true);
for (URL url : set) {
method.invoke(loader, url);
//if (logger != null) logger.log(Level.INFO, "Server found ClassPath({0})", url);
}
} else {
Thread.currentThread().setContextClassLoader(new URLClassLoader(set.toArray(new URL[set.size()]), cl));
} }
List<URL> list = new ArrayList<>(set); List<URL> list = new ArrayList<>(set);
Collections.sort(list, (URL o1, URL o2) -> o1.getFile().compareTo(o2.getFile())); Collections.sort(list, (URL o1, URL o2) -> o1.getFile().compareTo(o2.getFile()));

View File

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

View File

@@ -12,6 +12,7 @@ import java.nio.channels.AsynchronousChannelGroup;
import java.util.*; import java.util.*;
import java.util.concurrent.*; import java.util.concurrent.*;
import java.util.logging.*; import java.util.logging.*;
import java.util.stream.Collectors;
import org.redkale.service.Service; import org.redkale.service.Service;
import org.redkale.util.ObjectPool; import org.redkale.util.ObjectPool;
@@ -42,10 +43,19 @@ public class TransportFactory {
protected final List<WeakReference<Service>> services = new CopyOnWriteArrayList<>(); protected final List<WeakReference<Service>> services = new CopyOnWriteArrayList<>();
public TransportFactory(ExecutorService executor, ObjectPool<ByteBuffer> bufferPool, AsynchronousChannelGroup channelGroup) { //负载均衡策略
protected final TransportStrategy strategy;
public TransportFactory(ExecutorService executor, ObjectPool<ByteBuffer> bufferPool, AsynchronousChannelGroup channelGroup,
final TransportStrategy strategy) {
this.executor = executor; this.executor = executor;
this.bufferPool = bufferPool; this.bufferPool = bufferPool;
this.channelGroup = channelGroup; this.channelGroup = channelGroup;
this.strategy = strategy;
}
public TransportFactory(ExecutorService executor, ObjectPool<ByteBuffer> bufferPool, AsynchronousChannelGroup channelGroup) {
this(executor, bufferPool, channelGroup, null);
} }
public String findGroupName(InetSocketAddress addr) { public String findGroupName(InetSocketAddress addr) {
@@ -53,14 +63,24 @@ public class TransportFactory {
return groupAddrs.get(addr); return groupAddrs.get(addr);
} }
public TransportGroupInfo findGroupInfo2(String group) { public TransportGroupInfo findGroupInfo(String group) {
if (group == null) return null; if (group == null) return null;
return groupInfos.get(group); return groupInfos.get(group);
} }
public TransportFactory addGroupInfo(String name, InetSocketAddress... addrs) { public boolean addGroupInfo(String groupName, InetSocketAddress... addrs) {
addGroupInfo(new TransportGroupInfo(name, addrs)); addGroupInfo(new TransportGroupInfo(groupName, addrs));
return this; return true;
}
public boolean removeGroupInfo(String groupName, InetSocketAddress addr) {
if (groupName == null || groupName.isEmpty() || addr == null) return false;
if (!groupName.equals(groupAddrs.get(addr))) return false;
TransportGroupInfo group = groupInfos.get(groupName);
if (group == null) return false;
group.removeAddress(addr);
groupAddrs.remove(addr);
return true;
} }
public TransportFactory addGroupInfo(String name, Set<InetSocketAddress> addrs) { public TransportFactory addGroupInfo(String name, Set<InetSocketAddress> addrs) {
@@ -116,20 +136,24 @@ public class TransportFactory {
} }
if (info == null) return null; if (info == null) return null;
if (sncpAddress != null) addresses.remove(sncpAddress); if (sncpAddress != null) addresses.remove(sncpAddress);
return new Transport("remotes", info.protocol, info.subprotocol, this.bufferPool, this.channelGroup, sncpAddress, addresses); return new Transport(groups.stream().sorted().collect(Collectors.joining(";")), info.protocol, info.subprotocol, this.bufferPool, this.channelGroup, sncpAddress, addresses, this.strategy);
} }
private Transport loadTransport(final String groupName, InetSocketAddress sncpAddress) { private Transport loadTransport(final String groupName, InetSocketAddress sncpAddress) {
if (groupName == null) return null; if (groupName == null) return null;
TransportGroupInfo info = groupInfos.get(groupName); TransportGroupInfo info = groupInfos.get(groupName);
if (info == null) return null; 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.bufferPool, this.channelGroup, sncpAddress, info.addresses, this.strategy);
} }
public ExecutorService getExecutor() { public ExecutorService getExecutor() {
return executor; return executor;
} }
public List<TransportGroupInfo> getGroupInfos() {
return new ArrayList<>(this.groupInfos.values());
}
public void addSncpService(Service service) { public void addSncpService(Service service) {
if (service == null) return; if (service == null) return;
services.add(new WeakReference<>(service)); services.add(new WeakReference<>(service));

View File

@@ -90,20 +90,34 @@ public class TransportGroupInfo {
} }
public boolean containsAddress(InetSocketAddress addr) { public boolean containsAddress(InetSocketAddress addr) {
if (this.addresses == null) return false; synchronized (this) {
return this.addresses.contains(addr); if (this.addresses == null) return false;
return this.addresses.contains(addr);
}
}
public void removeAddress(InetSocketAddress addr) {
if (addr == null) return;
synchronized (this) {
if (this.addresses == null) return;
this.addresses.remove(addr);
}
} }
public void putAddress(InetSocketAddress addr) { public void putAddress(InetSocketAddress addr) {
if (addr == null) return; if (addr == null) return;
if (this.addresses == null) this.addresses = new HashSet<>(); synchronized (this) {
this.addresses.add(addr); if (this.addresses == null) this.addresses = new HashSet<>();
this.addresses.add(addr);
}
} }
public void putAddress(Set<InetSocketAddress> addrs) { public void putAddress(Set<InetSocketAddress> addrs) {
if (addrs == null) return; if (addrs == null) return;
if (this.addresses == null) this.addresses = new HashSet<>(); synchronized (this) {
this.addresses.addAll(addrs); if (this.addresses == null) this.addresses = new HashSet<>();
this.addresses.addAll(addrs);
}
} }
@Override @Override

View File

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

View File

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

View File

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

View File

@@ -222,7 +222,7 @@ public class HttpPrepareServlet extends PrepareServlet<String, HttpContext, Http
} }
String resServlet = resConfig.getValue("servlet", HttpResourceServlet.class.getName()); String resServlet = resConfig.getValue("servlet", HttpResourceServlet.class.getName());
try { try {
this.resourceHttpServlet = (HttpServlet) Class.forName(resServlet).newInstance(); this.resourceHttpServlet = (HttpServlet) Thread.currentThread().getContextClassLoader().loadClass(resServlet).newInstance();
} catch (Throwable e) { } catch (Throwable e) {
this.resourceHttpServlet = new HttpResourceServlet(); this.resourceHttpServlet = new HttpResourceServlet();
logger.log(Level.WARNING, "init HttpResourceSerlvet(" + resServlet + ") error", e); logger.log(Level.WARNING, "init HttpResourceSerlvet(" + resServlet + ") error", e);

View File

@@ -121,7 +121,6 @@ public class HttpRequest extends Request<HttpContext> {
} else { } else {
this.requestURI = array.toDecodeString(index, offset - index, charset).trim(); this.requestURI = array.toDecodeString(index, offset - index, charset).trim();
} }
if (this.requestURI.contains("../")) return -1;
index = ++offset; index = ++offset;
this.protocol = array.toString(index, array.size() - index, charset).trim(); this.protocol = array.toString(index, array.size() - index, charset).trim();
while (readLine(buffer, array)) { while (readLine(buffer, array)) {

View File

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

View File

@@ -42,7 +42,7 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
public ByteBuffer[] execute(final HttpResponse response, final ByteBuffer[] buffers); public ByteBuffer[] execute(final HttpResponse response, final ByteBuffer[] buffers);
} }
private static final ByteBuffer buffer304 = ByteBuffer.wrap("HTTP/1.1 304 Not Modified\r\n\r\n".getBytes()).asReadOnlyBuffer(); private static final ByteBuffer buffer304 = ByteBuffer.wrap("HTTP/1.1 304 Not Modified\r\nContent-Length:0\r\n\r\n".getBytes()).asReadOnlyBuffer();
private static final ByteBuffer buffer404 = ByteBuffer.wrap("HTTP/1.1 404 Not Found\r\nContent-Length:0\r\n\r\n".getBytes()).asReadOnlyBuffer(); private static final ByteBuffer buffer404 = ByteBuffer.wrap("HTTP/1.1 404 Not Found\r\nContent-Length:0\r\n\r\n".getBytes()).asReadOnlyBuffer();
@@ -704,8 +704,8 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
final String match = request.getHeader("If-None-Match"); final String match = request.getHeader("If-None-Match");
final String etag = (file == null ? 0L : file.lastModified()) + "-" + length; final String etag = (file == null ? 0L : file.lastModified()) + "-" + length;
if (match != null && etag.equals(match)) { if (match != null && etag.equals(match)) {
finish304(); //finish304();
return; //return;
} }
this.contentLength = length; this.contentLength = length;
if (filename != null && !filename.isEmpty() && file != null) { if (filename != null && !filename.isEmpty() && file != null) {
@@ -744,7 +744,7 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
} }
private void finishFile(ByteBuffer hbuffer, File file, long offset, long length) throws IOException { private void finishFile(ByteBuffer hbuffer, File file, long offset, long length) throws IOException {
this.channel.write(hbuffer, hbuffer, new TransferFileHandler(AsynchronousFileChannel.open(file.toPath(), options, ((HttpContext) context).getExecutor()), offset, length)); this.channel.write(hbuffer, hbuffer, new TransferFileHandler(file, offset, length));
} }
private ByteBuffer createHeader() { private ByteBuffer createHeader() {
@@ -976,6 +976,8 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
protected final class TransferFileHandler implements AsyncHandler<Integer, ByteBuffer> { protected final class TransferFileHandler implements AsyncHandler<Integer, ByteBuffer> {
private final File file;
private final AsynchronousFileChannel filechannel; private final AsynchronousFileChannel filechannel;
private final long max; //需要读取的字节数, -1表示读到文件结尾 private final long max; //需要读取的字节数, -1表示读到文件结尾
@@ -988,23 +990,36 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
private boolean read = true; private boolean read = true;
public TransferFileHandler(AsynchronousFileChannel channel) { public TransferFileHandler(File file) throws IOException {
this.filechannel = channel; this.file = file;
this.max = -1; this.filechannel = AsynchronousFileChannel.open(file.toPath(), options, ((HttpContext) context).getExecutor());
this.position = 0;
this.max = file.length();
} }
public TransferFileHandler(AsynchronousFileChannel channel, long offset, long len) { public TransferFileHandler(File file, long offset, long len) throws IOException {
this.filechannel = channel; this.file = file;
this.filechannel = AsynchronousFileChannel.open(file.toPath(), options, ((HttpContext) context).getExecutor());
this.position = offset <= 0 ? 0 : offset; this.position = offset <= 0 ? 0 : offset;
this.max = len; this.max = len <= 0 ? file.length() : len;
} }
@Override @Override
public void completed(Integer result, ByteBuffer attachment) { public void completed(Integer result, ByteBuffer attachment) {
if (result < 0 || (max > 0 && count >= max)) { //(Thread.currentThread().getName() + "-----------" + file + "-------------------result: " + result + ", max = " + max + ", count = " + count);
if (result < 0 || count >= max) {
failed(null, attachment); failed(null, attachment);
return; return;
} }
if (!next && attachment.hasRemaining()) { //Header还没写完
channel.write(attachment, attachment, this);
return;
}
if (next && read && attachment.hasRemaining()) { //Buffer还没写完
channel.write(attachment, attachment, this);
return;
}
if (read) { if (read) {
read = false; read = false;
if (next) { if (next) {
@@ -1016,14 +1031,16 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
filechannel.read(attachment, position, attachment, this); filechannel.read(attachment, position, attachment, this);
} else { } else {
read = true; read = true;
if (max > 0) { count += result;
count += result; if (count > max) {
if (count > max) { attachment.limit((int) (attachment.position() + max - count));
attachment.limit((int) (attachment.position() + max - count));
}
} }
attachment.flip(); attachment.flip();
channel.write(attachment, attachment, this); if (attachment.hasRemaining()) {
channel.write(attachment, attachment, this);
} else {
failed(null, attachment);
}
} }
} }

View File

@@ -47,6 +47,10 @@ public class HttpResult<T> {
return this; return this;
} }
public HttpResult<T> cookie(String name, Serializable value) {
return cookie(new HttpCookie(name, String.valueOf(value)));
}
public HttpResult<T> cookie(HttpCookie cookie) { public HttpResult<T> cookie(HttpCookie cookie) {
if (this.cookies == null) this.cookies = new ArrayList<>(); if (this.cookies == null) this.cookies = new ArrayList<>();
this.cookies.add(cookie); this.cookies.add(cookie);

View File

@@ -38,24 +38,21 @@ public class HttpServer extends Server<String, HttpContext, HttpRequest, HttpRes
super.init(config); super.init(config);
} }
public List<HttpServlet> getHttpServlets() {
return this.prepare.getServlets();
}
public List<HttpFilter> getHttpFilters() {
return this.prepare.getFilters();
}
/** /**
* 获取静态资源HttpServlet * 获取静态资源HttpServlet
* *
* @return HttpServlet * @return HttpServlet
*/ */
public HttpServlet getResourceServlet() { public HttpResourceServlet getResourceServlet() {
return ((HttpPrepareServlet) this.prepare).resourceHttpServlet; return (HttpResourceServlet) ((HttpPrepareServlet) this.prepare).resourceHttpServlet;
}
/**
* 删除HttpFilter
*
* @param filterName HttpFilter名称
*
* @return HttpFilter
*/
public HttpFilter removeFilter(String filterName) {
return (HttpFilter) this.prepare.removeFilter(filterName);
} }
/** /**
@@ -72,7 +69,7 @@ public class HttpServer extends Server<String, HttpContext, HttpRequest, HttpRes
/** /**
* 删除HttpServlet * 删除HttpServlet
* *
* @param <T> 泛型 * @param <T> 泛型
* @param websocketOrServletType Class * @param websocketOrServletType Class
* *
* @return HttpServlet * @return HttpServlet
@@ -111,7 +108,7 @@ public class HttpServer extends Server<String, HttpContext, HttpRequest, HttpRes
* *
* @return HttpFilter * @return HttpFilter
*/ */
public <T extends HttpFilter> T removeFilter(Class<T> filterClass) { public <T extends HttpFilter> T removeHttpFilter(Class<T> filterClass) {
return (T) this.prepare.removeFilter(filterClass); return (T) this.prepare.removeFilter(filterClass);
} }
@@ -175,14 +172,15 @@ public class HttpServer extends Server<String, HttpContext, HttpRequest, HttpRes
* *
* @param <S> WebSocket * @param <S> WebSocket
* @param <T> HttpServlet * @param <T> HttpServlet
* @param classLoader ClassLoader
* @param webSocketType WebSocket的类型 * @param webSocketType WebSocket的类型
* @param prefix url前缀 * @param prefix url前缀
* @param conf 配置信息 * @param conf 配置信息
* *
* @return RestServlet * @return RestServlet
*/ */
public <S extends WebSocket, T extends HttpServlet> T addRestWebSocketServlet(final Class<S> webSocketType, final String prefix, final AnyValue conf) { public <S extends WebSocket, T extends HttpServlet> T addRestWebSocketServlet(final ClassLoader classLoader, final Class<S> webSocketType, final String prefix, final AnyValue conf) {
T servlet = Rest.createRestWebSocketServlet(webSocketType); T servlet = Rest.createRestWebSocketServlet(classLoader, webSocketType);
if (servlet != null) this.prepare.addServlet(servlet, prefix, conf); if (servlet != null) this.prepare.addServlet(servlet, prefix, conf);
return servlet; return servlet;
} }
@@ -192,6 +190,7 @@ public class HttpServer extends Server<String, HttpContext, HttpRequest, HttpRes
* *
* @param <S> Service * @param <S> Service
* @param <T> HttpServlet * @param <T> HttpServlet
* @param classLoader ClassLoader
* @param service Service对象 * @param service Service对象
* @param userType 用户数据类型 * @param userType 用户数据类型
* @param baseServletType RestServlet基类 * @param baseServletType RestServlet基类
@@ -199,8 +198,8 @@ public class HttpServer extends Server<String, HttpContext, HttpRequest, HttpRes
* *
* @return RestServlet * @return RestServlet
*/ */
public <S extends Service, T extends HttpServlet> T addRestServlet(final S service, final Class userType, final Class<T> baseServletType, final String prefix) { public <S extends Service, T extends HttpServlet> T addRestServlet(final ClassLoader classLoader, final S service, final Class userType, final Class<T> baseServletType, final String prefix) {
return addRestServlet(null, service, userType, baseServletType, prefix); return addRestServlet(classLoader, null, service, userType, baseServletType, prefix);
} }
/** /**
@@ -208,6 +207,7 @@ public class HttpServer extends Server<String, HttpContext, HttpRequest, HttpRes
* *
* @param <S> Service * @param <S> Service
* @param <T> HttpServlet * @param <T> HttpServlet
* @param classLoader ClassLoader
* @param name 资源名 * @param name 资源名
* @param service Service对象 * @param service Service对象
* @param userType 用户数据类型 * @param userType 用户数据类型
@@ -216,7 +216,7 @@ public class HttpServer extends Server<String, HttpContext, HttpRequest, HttpRes
* *
* @return RestServlet * @return RestServlet
*/ */
public <S extends Service, T extends HttpServlet> T addRestServlet(final String name, final S service, final Class userType, final Class<T> baseServletType, final String prefix) { 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; T servlet = null;
final boolean sncp = Sncp.isSncpDyn(service); final boolean sncp = Sncp.isSncpDyn(service);
final String resname = name == null ? (sncp ? Sncp.getResourceName(service) : "") : name; final String resname = name == null ? (sncp ? Sncp.getResourceName(service) : "") : name;
@@ -236,7 +236,7 @@ public class HttpServer extends Server<String, HttpContext, HttpRequest, HttpRes
} }
} }
final boolean first = servlet == null; final boolean first = servlet == null;
if (servlet == null) servlet = Rest.createRestServlet(userType, baseServletType, serviceType); if (servlet == null) servlet = Rest.createRestServlet(classLoader, userType, baseServletType, serviceType);
if (servlet == null) return null; //没有HttpMapping方法的HttpServlet调用Rest.createRestServlet就会返回null if (servlet == null) return null; //没有HttpMapping方法的HttpServlet调用Rest.createRestServlet就会返回null
try { //若提供动态变更Service服务功能则改Rest服务无法做出相应更新 try { //若提供动态变更Service服务功能则改Rest服务无法做出相应更新
Field field = servlet.getClass().getDeclaredField(Rest.REST_SERVICE_FIELD_NAME); Field field = servlet.getClass().getDeclaredField(Rest.REST_SERVICE_FIELD_NAME);

View File

@@ -27,9 +27,9 @@ import org.redkale.util.*;
*/ */
public class HttpServlet extends Servlet<HttpContext, HttpRequest, HttpResponse> { public class HttpServlet extends Servlet<HttpContext, HttpRequest, HttpResponse> {
public static final int RET_SERVER_ERROR = 1800_0001; public static final int RET_SERVER_ERROR = 1200_0001;
public static final int RET_METHOD_ERROR = 1800_0002; public static final int RET_METHOD_ERROR = 1200_0002;
String _prefix = ""; //当前HttpServlet的path前缀 String _prefix = ""; //当前HttpServlet的path前缀
@@ -214,7 +214,7 @@ public class HttpServlet extends Servlet<HttpContext, HttpRequest, HttpResponse>
int i = 0; int i = 0;
for (;;) { for (;;) {
try { try {
Class.forName(newDynName.replace('/', '.')); Thread.currentThread().getContextClassLoader().loadClass(newDynName.replace('/', '.'));
newDynName += "_" + (++i); newDynName += "_" + (++i);
} catch (Throwable ex) { } catch (Throwable ex) {
break; break;

View File

@@ -17,7 +17,7 @@ import jdk.internal.org.objectweb.asm.*;
import static jdk.internal.org.objectweb.asm.ClassWriter.COMPUTE_FRAMES; import static jdk.internal.org.objectweb.asm.ClassWriter.COMPUTE_FRAMES;
import static jdk.internal.org.objectweb.asm.Opcodes.*; import static jdk.internal.org.objectweb.asm.Opcodes.*;
import jdk.internal.org.objectweb.asm.Type; import jdk.internal.org.objectweb.asm.Type;
import org.redkale.convert.json.JsonConvert; import org.redkale.convert.json.*;
import org.redkale.service.*; import org.redkale.service.*;
import org.redkale.util.*; import org.redkale.util.*;
import org.redkale.source.Flipper; import org.redkale.source.Flipper;
@@ -35,6 +35,8 @@ public final class Rest {
static final String REST_SERVICE_FIELD_NAME = "_redkale_service"; static final String REST_SERVICE_FIELD_NAME = "_redkale_service";
static final String REST_JSONCONVERT_FIELD_PREFIX = "_redkale_jsonconvert_";
static final String REST_SERVICEMAP_FIELD_NAME = "_redkale_servicemap"; //如果只有name=""的Service资源则实例中_servicemap必须为null static final String REST_SERVICEMAP_FIELD_NAME = "_redkale_servicemap"; //如果只有name=""的Service资源则实例中_servicemap必须为null
private static final String REST_PARAMTYPES_FIELD_NAME = "_redkale_paramtypes"; //存在泛型的参数数组 Type[][] 第1维度是方法的下标 第二维度是参数的下标 private static final String REST_PARAMTYPES_FIELD_NAME = "_redkale_paramtypes"; //存在泛型的参数数组 Type[][] 第1维度是方法的下标 第二维度是参数的下标
@@ -109,6 +111,20 @@ public final class Rest {
} }
} }
static JsonConvert createJsonConvert(RestConvert[] converts) {
if (converts == null || converts.length < 1) return JsonConvert.root();
final JsonFactory childFactory = JsonFactory.root().createChild();
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());
types.add(rc.type());
}
return childFactory.getConvert();
}
static String getWebModuleName(Class<? extends Service> serviceType) { static String getWebModuleName(Class<? extends Service> serviceType) {
final RestService controller = serviceType.getAnnotation(RestService.class); final RestService controller = serviceType.getAnnotation(RestService.class);
if (controller == null) return serviceType.getSimpleName().replaceAll("Service.*$", "").toLowerCase(); if (controller == null) return serviceType.getSimpleName().replaceAll("Service.*$", "").toLowerCase();
@@ -142,7 +158,7 @@ public final class Rest {
} }
} }
static <T extends HttpServlet> T createRestWebSocketServlet(final Class<? extends WebSocket> webSocketType) { static <T extends HttpServlet> T createRestWebSocketServlet(final ClassLoader classLoader, final Class<? extends WebSocket> webSocketType) {
if (webSocketType == null) throw new RuntimeException("Rest WebSocket Class is null on createRestWebSocketServlet"); if (webSocketType == null) throw new RuntimeException("Rest WebSocket Class is null on createRestWebSocketServlet");
if (Modifier.isAbstract(webSocketType.getModifiers())) throw new RuntimeException("Rest WebSocket Class(" + webSocketType + ") cannot abstract on createRestWebSocketServlet"); if (Modifier.isAbstract(webSocketType.getModifiers())) throw new RuntimeException("Rest WebSocket Class(" + webSocketType + ") cannot abstract on createRestWebSocketServlet");
if (Modifier.isFinal(webSocketType.getModifiers())) throw new RuntimeException("Rest WebSocket Class(" + webSocketType + ") cannot final on createRestWebSocketServlet"); if (Modifier.isFinal(webSocketType.getModifiers())) throw new RuntimeException("Rest WebSocket Class(" + webSocketType + ") cannot final on createRestWebSocketServlet");
@@ -162,6 +178,7 @@ public final class Rest {
//---------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------
final Set<Field> resourcesFieldSet = new LinkedHashSet<>(); final Set<Field> resourcesFieldSet = new LinkedHashSet<>();
final ClassLoader loader = classLoader == null ? Thread.currentThread().getContextClassLoader() : classLoader;
Class clzz = webSocketType; Class clzz = webSocketType;
do { do {
for (Field field : webSocketType.getDeclaredFields()) { for (Field field : webSocketType.getDeclaredFields()) {
@@ -216,7 +233,6 @@ public final class Rest {
final String newDynConsumerSimpleName = "_DynRestOnMessageConsumer"; final String newDynConsumerSimpleName = "_DynRestOnMessageConsumer";
final String newDynConsumerFullName = newDynName + "$" + newDynConsumerSimpleName; final String newDynConsumerFullName = newDynName + "$" + newDynConsumerSimpleName;
//---------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------
ClassLoader loader = Rest.class.getClassLoader();
ClassWriter cw = new ClassWriter(COMPUTE_FRAMES); ClassWriter cw = new ClassWriter(COMPUTE_FRAMES);
FieldVisitor fv; FieldVisitor fv;
@@ -312,6 +328,13 @@ public final class Rest {
mv.visitMaxs(2, 1); mv.visitMaxs(2, 1);
mv.visitEnd(); mv.visitEnd();
} }
{ //resourceName
mv = new AsmMethodVisitor(cw.visitMethod(ACC_PUBLIC, "resourceName", "()Ljava/lang/String;", null, null));
mv.visitLdcInsn(rws.name());
mv.visitInsn(ARETURN);
mv.visitMaxs(1, 1);
mv.visitEnd();
}
RestClassLoader newLoader = new RestClassLoader(loader); RestClassLoader newLoader = new RestClassLoader(loader);
@@ -514,7 +537,7 @@ public final class Rest {
} }
} }
static <T extends HttpServlet> T createRestServlet(final Class userType0, final Class<T> baseServletType, final Class<? extends Service> serviceType) { static <T extends HttpServlet> T createRestServlet(final ClassLoader classLoader, final Class userType0, final Class<T> baseServletType, final Class<? extends Service> serviceType) {
if (baseServletType == null || serviceType == null) throw new RuntimeException(" Servlet or Service is null Class on createRestServlet"); if (baseServletType == null || serviceType == null) throw new RuntimeException(" Servlet or Service is null Class on createRestServlet");
if (!HttpServlet.class.isAssignableFrom(baseServletType)) throw new RuntimeException(baseServletType + " is not HttpServlet Class on createRestServlet"); if (!HttpServlet.class.isAssignableFrom(baseServletType)) throw new RuntimeException(baseServletType + " is not HttpServlet Class on createRestServlet");
int mod = baseServletType.getModifiers(); int mod = baseServletType.getModifiers();
@@ -525,6 +548,7 @@ public final class Rest {
final String webServletDesc = Type.getDescriptor(WebServlet.class); final String webServletDesc = Type.getDescriptor(WebServlet.class);
final String reqDesc = Type.getDescriptor(HttpRequest.class); final String reqDesc = Type.getDescriptor(HttpRequest.class);
final String respDesc = Type.getDescriptor(HttpResponse.class); final String respDesc = Type.getDescriptor(HttpResponse.class);
final String convertDesc = Type.getDescriptor(JsonConvert.class);
final String retDesc = Type.getDescriptor(RetResult.class); final String retDesc = Type.getDescriptor(RetResult.class);
final String futureDesc = Type.getDescriptor(CompletableFuture.class); final String futureDesc = Type.getDescriptor(CompletableFuture.class);
final String flipperDesc = Type.getDescriptor(Flipper.class); final String flipperDesc = Type.getDescriptor(Flipper.class);
@@ -547,7 +571,7 @@ public final class Rest {
final String supDynName = baseServletType.getName().replace('.', '/'); final String supDynName = baseServletType.getName().replace('.', '/');
final RestService controller = serviceType.getAnnotation(RestService.class); final RestService controller = serviceType.getAnnotation(RestService.class);
if (controller != null && controller.ignore()) throw new RuntimeException(serviceType + " is ignore Rest Service Class"); //标记为ignore=true不创建Servlet if (controller != null && controller.ignore()) throw new RuntimeException(serviceType + " is ignore Rest Service Class"); //标记为ignore=true不创建Servlet
ClassLoader loader = Rest.class.getClassLoader(); ClassLoader loader = classLoader == null ? Thread.currentThread().getContextClassLoader() : classLoader;
String newDynName = serviceTypeInternalName.substring(0, serviceTypeInternalName.lastIndexOf('/') + 1) + "_Dyn" + serviceType.getSimpleName().replaceAll("Service.*$", "") + "RestServlet"; String newDynName = serviceTypeInternalName.substring(0, serviceTypeInternalName.lastIndexOf('/') + 1) + "_Dyn" + serviceType.getSimpleName().replaceAll("Service.*$", "") + "RestServlet";
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
@@ -672,6 +696,7 @@ public final class Rest {
final Map<String, List<String>> asmParamMap = MethodParamClassVisitor.getMethodParamNames(serviceType); final Map<String, List<String>> asmParamMap = MethodParamClassVisitor.getMethodParamNames(serviceType);
final Map<String, java.lang.reflect.Type> bodyTypes = new HashMap<>(); final Map<String, java.lang.reflect.Type> bodyTypes = new HashMap<>();
final List<RestConvert[]> restConverts = new ArrayList<>();
for (final MappingEntry entry : entrys) { for (final MappingEntry entry : entrys) {
RestUploadFile mupload = null; RestUploadFile mupload = null;
Class muploadType = null; Class muploadType = null;
@@ -680,6 +705,9 @@ public final class Rest {
final String methodDesc = Type.getMethodDescriptor(method); final String methodDesc = Type.getMethodDescriptor(method);
final Parameter[] params = method.getParameters(); final Parameter[] params = method.getParameters();
final RestConvert[] rcs = method.getAnnotationsByType(RestConvert.class);
if (rcs != null && rcs.length > 0) restConverts.add(rcs);
mv = new AsmMethodVisitor(cw.visitMethod(ACC_PUBLIC, entry.name, "(" + reqDesc + respDesc + ")V", null, new String[]{"java/io/IOException"})); mv = new AsmMethodVisitor(cw.visitMethod(ACC_PUBLIC, entry.name, "(" + reqDesc + respDesc + ")V", null, new String[]{"java/io/IOException"}));
//mv.setDebug(true); //mv.setDebug(true);
mv.debugLine(); mv.debugLine();
@@ -725,6 +753,7 @@ public final class Rest {
RestHeader annhead = param.getAnnotation(RestHeader.class); RestHeader annhead = param.getAnnotation(RestHeader.class);
if (annhead != null) { if (annhead != null) {
if (ptype != String.class) throw new RuntimeException("@RestHeader must on String Parameter in " + method);
n = annhead.name(); n = annhead.name();
radix = annhead.radix(); radix = annhead.radix();
comment = annhead.comment(); comment = annhead.comment();
@@ -801,7 +830,7 @@ public final class Rest {
} else if (ptype == Flipper.class) { } else if (ptype == Flipper.class) {
n = "flipper"; n = "flipper";
} else { } else {
n = ("bean" + i); throw new RuntimeException("Parameter " + param.getName() + " not found name by @RestParam in " + method);
} }
} }
if (annhead == null && anncookie == null && annaddr == null && annbody == null && annfile == null if (annhead == null && anncookie == null && annaddr == null && annbody == null && annfile == null
@@ -839,7 +868,7 @@ public final class Rest {
} }
} }
av0 = mv.visitAnnotation(mappingDesc, true); av0 = mv.visitAnnotation(mappingDesc, true);
String url = "/" + defmodulename.toLowerCase() + "/" + entry.name + (reqpath ? "/" : ""); String url = (catalog.isEmpty() ? "/" : ("/" + catalog + "/")) + defmodulename.toLowerCase() + "/" + entry.name + (reqpath ? "/" : "");
av0.visit("url", url); av0.visit("url", url);
av0.visit("auth", entry.auth); av0.visit("auth", entry.auth);
av0.visit("cacheseconds", entry.cacheseconds); av0.visit("cacheseconds", entry.cacheseconds);
@@ -1292,7 +1321,7 @@ public final class Rest {
if (ru != null && field.getType() != byte[].class && field.getType() != File.class && field.getType() != File[].class) { if (ru != null && field.getType() != byte[].class && field.getType() != File.class && field.getType() != File[].class) {
throw new RuntimeException("@RestUploadFile must on byte[] or File or File[] Field in " + field); throw new RuntimeException("@RestUploadFile must on byte[] or File or File[] Field in " + field);
} }
if (ri != null && field.getType() != String.class) throw new RuntimeException("@RestURI must on String Field in " + field); if (ri != null && field.getType() != String.class) throw new RuntimeException("@RestURI must on String Field in " + field);
org.redkale.util.Attribute attr = org.redkale.util.Attribute.create(loop, field); org.redkale.util.Attribute attr = org.redkale.util.Attribute.create(loop, field);
String attrFieldName; String attrFieldName;
@@ -1488,15 +1517,29 @@ public final class Rest {
} else if (RetResult.class.isAssignableFrom(returnType)) { } else if (RetResult.class.isAssignableFrom(returnType)) {
mv.visitVarInsn(ASTORE, maxLocals); mv.visitVarInsn(ASTORE, maxLocals);
mv.visitVarInsn(ALOAD, 2); //response mv.visitVarInsn(ALOAD, 2); //response
mv.visitVarInsn(ALOAD, maxLocals); if (rcs != null && rcs.length > 0) {
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finishJson", "(" + retDesc + ")V", false); 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); mv.visitInsn(RETURN);
maxLocals++; maxLocals++;
} else if (HttpResult.class.isAssignableFrom(returnType)) { } else if (HttpResult.class.isAssignableFrom(returnType)) {
mv.visitVarInsn(ASTORE, maxLocals); mv.visitVarInsn(ASTORE, maxLocals);
mv.visitVarInsn(ALOAD, 2); //response mv.visitVarInsn(ALOAD, 2); //response
mv.visitVarInsn(ALOAD, maxLocals); if (rcs != null && rcs.length > 0) {
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finishJson", "(" + httprsDesc + ")V", false); 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); mv.visitInsn(RETURN);
maxLocals++; maxLocals++;
} else if (Number.class.isAssignableFrom(returnType) || CharSequence.class.isAssignableFrom(returnType)) { //returnType == String.class 必须放在前面 } else if (Number.class.isAssignableFrom(returnType) || CharSequence.class.isAssignableFrom(returnType)) { //returnType == String.class 必须放在前面
@@ -1510,15 +1553,29 @@ public final class Rest {
} else if (CompletableFuture.class.isAssignableFrom(returnType)) { } else if (CompletableFuture.class.isAssignableFrom(returnType)) {
mv.visitVarInsn(ASTORE, maxLocals); mv.visitVarInsn(ASTORE, maxLocals);
mv.visitVarInsn(ALOAD, 2);//response mv.visitVarInsn(ALOAD, 2);//response
mv.visitVarInsn(ALOAD, maxLocals); if (rcs != null && rcs.length > 0) {
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finishJson", "(" + futureDesc + ")V", false); 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); mv.visitInsn(RETURN);
maxLocals++; maxLocals++;
} else { } else {
mv.visitVarInsn(ASTORE, maxLocals); mv.visitVarInsn(ASTORE, maxLocals);
mv.visitVarInsn(ALOAD, 2); //response mv.visitVarInsn(ALOAD, 2); //response
mv.visitVarInsn(ALOAD, maxLocals); if (rcs != null && rcs.length > 0) {
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finishJson", "(Ljava/lang/Object;)V", false); 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 + "Ljava/lang/Object;)V", false);
} else {
mv.visitVarInsn(ALOAD, maxLocals);
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finishJson", "(Ljava/lang/Object;)V", false);
}
mv.visitInsn(RETURN); mv.visitInsn(RETURN);
maxLocals++; maxLocals++;
} }
@@ -1537,6 +1594,11 @@ public final class Rest {
fv.visitEnd(); fv.visitEnd();
} }
for (int i = 1; i <= restConverts.size(); i++) {
fv = cw.visitField(ACC_PRIVATE, REST_JSONCONVERT_FIELD_PREFIX + i, convertDesc, null, null);
fv.visitEnd();
}
//classMap.put("mappings", mappingMaps); //不显示太多信息 //classMap.put("mappings", mappingMaps); //不显示太多信息
{ //toString函数 { //toString函数
mv = new AsmMethodVisitor(cw.visitMethod(ACC_PUBLIC, "toString", "()Ljava/lang/String;", null, null)); mv = new AsmMethodVisitor(cw.visitMethod(ACC_PUBLIC, "toString", "()Ljava/lang/String;", null, null));
@@ -1561,6 +1623,11 @@ public final class Rest {
genField.setAccessible(true); genField.setAccessible(true);
genField.set(obj, en.getValue()); genField.set(obj, en.getValue());
} }
for (int i = 0; i < restConverts.size(); i++) {
Field genField = newClazz.getDeclaredField(REST_JSONCONVERT_FIELD_PREFIX + (i + 1));
genField.setAccessible(true);
genField.set(obj, createJsonConvert(restConverts.get(i)));
}
Field typesfield = newClazz.getDeclaredField(REST_PARAMTYPES_FIELD_NAME); Field typesfield = newClazz.getDeclaredField(REST_PARAMTYPES_FIELD_NAME);
typesfield.setAccessible(true); typesfield.setAccessible(true);
java.lang.reflect.Type[][] paramtypeArray = new java.lang.reflect.Type[paramtypes.size()][]; java.lang.reflect.Type[][] paramtypeArray = new java.lang.reflect.Type[paramtypes.size()][];
@@ -1611,10 +1678,10 @@ public final class Rest {
if (mapping == null) mapping = DEFAULT__MAPPING; if (mapping == null) mapping = DEFAULT__MAPPING;
this.methodidx = methodidx; this.methodidx = methodidx;
this.ignore = mapping.ignore(); this.ignore = mapping.ignore();
String n = mapping.name().toLowerCase(); String n = mapping.name();
if (n.isEmpty()) { if (n.isEmpty()) {
String t = method.getName().toLowerCase(); String t = method.getName();
int pos = t.indexOf(defmodulename.toLowerCase()); int pos = t.indexOf(defmodulename);
n = pos > 0 ? t.substring(0, pos) : t; n = pos > 0 ? t.substring(0, pos) : t;
} }
this.name = n; this.name = n;

View File

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

View File

@@ -1,26 +0,0 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.net.http;
import java.io.*;
/**
* 使用 RestServlet 代替
*
* 详情见: https://redkale.org
*
* @author zhangjx
* @deprecated
* @param <T> 当前用户对象类型
*/
@Deprecated
public class RestHttpServlet<T> extends HttpServlet {
protected T currentUser(HttpRequest req) throws IOException {
return null;
}
}

View File

@@ -1,104 +0,0 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.net.http;
import java.io.Serializable;
import java.net.HttpCookie;
import java.util.*;
/**
* 使用 HttpResult 代替
*
* <p>
* 详情见: https://redkale.org
*
* @deprecated
* @author zhangjx
* @param <T> 结果对象的类型
*/
@Deprecated
public class RestOutput<T> {
private Map<String, String> headers;
private List<HttpCookie> cookies;
private String contentType;
private T result;
private int status = 0; //不设置则为 200
private String message;
public RestOutput() {
}
public RestOutput(T result) {
this.result = result;
}
public RestOutput<T> addHeader(String name, Serializable value) {
if (this.headers == null) this.headers = new HashMap<>();
this.headers.put(name, String.valueOf(value));
return this;
}
public RestOutput<T> addCookie(HttpCookie cookie) {
if (this.cookies == null) this.cookies = new ArrayList<>();
this.cookies.add(cookie);
return this;
}
public Map<String, String> getHeaders() {
return headers;
}
public void setHeaders(Map<String, String> headers) {
this.headers = headers;
}
public List<HttpCookie> getCookies() {
return cookies;
}
public void setCookies(List<HttpCookie> cookies) {
this.cookies = cookies;
}
public String getContentType() {
return contentType;
}
public void setContentType(String contentType) {
this.contentType = contentType;
}
public T getResult() {
return result;
}
public void setResult(T result) {
this.result = result;
}
public int getStatus() {
return status;
}
public void setStatus(int status) {
this.status = status;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}

View File

@@ -37,6 +37,13 @@ public @interface RestWebSocket {
*/ */
String catalog() default ""; String catalog() default "";
/**
* 是否为二进制消息, 默认为文本消息
*
* @return boolean
*/
boolean binary() default false;
/** /**
* 是否单用户单连接, 默认单用户单连接 * 是否单用户单连接, 默认单用户单连接
* *
@@ -45,11 +52,11 @@ public @interface RestWebSocket {
boolean single() default true; boolean single() default true;
/** /**
* WebScoket服务器给客户端进行ping操作的间隔时间, 单位: 秒, 默认值:60 * WebScoket服务器给客户端进行ping操作的间隔时间, 单位: 秒, 默认值:15
* *
* @return int * @return int
*/ */
int liveinterval() default 60; int liveinterval() default WebSocketServlet.DEFAILT_LIVEINTERVAL;
/** /**
* 是否屏蔽该类的转换 * 是否屏蔽该类的转换

View File

@@ -8,30 +8,24 @@ package org.redkale.net.http;
import org.redkale.net.http.WebSocketPacket.FrameType; import org.redkale.net.http.WebSocketPacket.FrameType;
import java.io.*; import java.io.*;
import java.net.*; import java.net.*;
import java.nio.ByteBuffer;
import java.util.*; import java.util.*;
import java.util.concurrent.*; import java.util.concurrent.*;
import java.util.function.Supplier;
import java.util.stream.Stream; import java.util.stream.Stream;
import org.redkale.convert.json.JsonConvert; import org.redkale.convert.Convert;
import org.redkale.net.*;
import org.redkale.util.Comment; import org.redkale.util.Comment;
/** /**
* <blockquote><pre> * <blockquote><pre>
* 一个WebSocket连接对应一个WebSocket实体即一个WebSocket会绑定一个TCP连接。 * 一个WebSocket连接对应一个WebSocket实体即一个WebSocket会绑定一个TCP连接。
* WebSocket 有两种模式: * 协议上符合HTML5规范, 其流程顺序如下:
* 1) 普通模式: 协议上符合HTML5规范, 其流程顺序如下:
* 1.1 onOpen 若返回null视为WebSocket的连接不合法强制关闭WebSocket连接通常用于判断登录态。 * 1.1 onOpen 若返回null视为WebSocket的连接不合法强制关闭WebSocket连接通常用于判断登录态。
* 1.2 createUserid 若返回null视为WebSocket的连接不合法强制关闭WebSocket连接通常用于判断用户权限是否符合。 * 1.2 createUserid 若返回null视为WebSocket的连接不合法强制关闭WebSocket连接通常用于判断用户权限是否符合。
* 1.3 onConnected WebSocket成功连接后在准备接收数据前回调此方法。 * 1.3 onConnected WebSocket成功连接后在准备接收数据前回调此方法。
* 1.4 onMessage/onFragment+ WebSocket接收到消息后回调此消息类方法。 * 1.4 onMessage/onFragment+ WebSocket接收到消息后回调此消息类方法。
* 1.5 onClose WebSocket被关闭后回调此方法。 * 1.5 onClose WebSocket被关闭后回调此方法。
* 普通模式下 以上方法都应该被重载。 * 普通模式下 以上方法都应该被重载。
*
* 2) 原始二进制模式: 此模式有别于HTML5规范可以视为原始的TCP连接。通常用于音频视频通讯场景。其流程顺序如下:
* 2.1 onOpen 若返回null视为WebSocket的连接不合法强制关闭WebSocket连接通常用于判断登录态。
* 2.2 createWebSocketid 若返回null视为WebSocket的连接不合法强制关闭WebSocket连接通常用于判断用户权限是否符合。
* 2.3 onRead WebSocket成功连接后回调此方法 由此方法处理原始的TCP连接 需要业务代码去控制WebSocket的关闭。
* 二进制模式下 以上方法都应该被重载。
* </pre></blockquote> * </pre></blockquote>
* <p> * <p>
* 详情见: https://redkale.org * 详情见: https://redkale.org
@@ -78,12 +72,18 @@ public abstract class WebSocket<G extends Serializable, T> {
String _remoteAddr;//不可能为空 String _remoteAddr;//不可能为空
JsonConvert _jsonConvert; //不可能为空 Convert _textConvert; //不可能为空
Convert _binaryConvert; //可能为空
Convert _sendConvert; //不可能为空
java.lang.reflect.Type _messageTextType; //不可能为空 java.lang.reflect.Type _messageTextType; //不可能为空
private long createtime = System.currentTimeMillis(); private long createtime = System.currentTimeMillis();
private long pingtime;
private Map<String, Object> attributes = new HashMap<>(); //非线程安全 private Map<String, Object> attributes = new HashMap<>(); //非线程安全
protected WebSocket() { protected WebSocket() {
@@ -91,11 +91,13 @@ public abstract class WebSocket<G extends Serializable, T> {
//---------------------------------------------------------------- //----------------------------------------------------------------
public final CompletableFuture<Integer> sendPing() { public final CompletableFuture<Integer> sendPing() {
this.pingtime = System.currentTimeMillis();
//if (_engine.finest) _engine.logger.finest(this + " on "+_engine.getEngineid()+" ping..."); //if (_engine.finest) _engine.logger.finest(this + " on "+_engine.getEngineid()+" ping...");
return sendPacket(WebSocketPacket.DEFAULT_PING_PACKET); return sendPacket(WebSocketPacket.DEFAULT_PING_PACKET);
} }
public final CompletableFuture<Integer> sendPing(byte[] data) { public final CompletableFuture<Integer> sendPing(byte[] data) {
this.pingtime = System.currentTimeMillis();
return sendPacket(new WebSocketPacket(FrameType.PING, data)); return sendPacket(new WebSocketPacket(FrameType.PING, data));
} }
@@ -134,7 +136,7 @@ public abstract class WebSocket<G extends Serializable, T> {
} else if (message instanceof WebSocketPacket) { } else if (message instanceof WebSocketPacket) {
return sendPacket((WebSocketPacket) message); return sendPacket((WebSocketPacket) message);
} else { } else {
return sendPacket(new WebSocketPacket(_jsonConvert, json, last)); return sendPacket(new WebSocketPacket(getSendConvert(), json, last));
} }
}); });
} }
@@ -143,36 +145,36 @@ public abstract class WebSocket<G extends Serializable, T> {
} else if (message instanceof WebSocketPacket) { } else if (message instanceof WebSocketPacket) {
return sendPacket((WebSocketPacket) message); return sendPacket((WebSocketPacket) message);
} else { } else {
return sendPacket(new WebSocketPacket(_jsonConvert, message, last)); return sendPacket(new WebSocketPacket(getSendConvert(), message, last));
} }
} }
/** /**
* 给自身发送消息, 消息类型是JavaBean对象 * 给自身发送消息, 消息类型是JavaBean对象
* *
* @param convert JsonConvert * @param convert Convert
* @param message 不可为空, 只能是JSON对象 * @param message 不可为空, 只能是JSON对象
* *
* @return 0表示成功 非0表示错误码 * @return 0表示成功 非0表示错误码
*/ */
public final CompletableFuture<Integer> send(JsonConvert convert, Object message) { public final CompletableFuture<Integer> send(Convert convert, Object message) {
return send(convert, message, true); return send(convert, message, true);
} }
/** /**
* 给自身发送消息, 消息类型是JavaBean对象 * 给自身发送消息, 消息类型是JavaBean对象
* *
* @param convert JsonConvert * @param convert Convert
* @param message 不可为空, 只能是JavaBean对象 * @param message 不可为空, 只能是JavaBean对象
* @param last 是否最后一条 * @param last 是否最后一条
* *
* @return 0表示成功 非0表示错误码 * @return 0表示成功 非0表示错误码
*/ */
public final CompletableFuture<Integer> send(JsonConvert convert, Object message, boolean last) { public final CompletableFuture<Integer> send(Convert convert, Object message, boolean last) {
if (message instanceof CompletableFuture) { if (message instanceof CompletableFuture) {
return ((CompletableFuture) message).thenCompose((json) -> sendPacket(new WebSocketPacket(convert == null ? _jsonConvert : convert, json, last))); return ((CompletableFuture) message).thenCompose((json) -> sendPacket(new WebSocketPacket(convert == null ? getSendConvert() : convert, json, last)));
} }
return sendPacket(new WebSocketPacket(convert == null ? _jsonConvert : convert, message, last)); return sendPacket(new WebSocketPacket(convert == null ? getSendConvert() : convert, message, last));
} }
/** /**
@@ -184,7 +186,7 @@ public abstract class WebSocket<G extends Serializable, T> {
*/ */
CompletableFuture<Integer> sendPacket(WebSocketPacket packet) { CompletableFuture<Integer> sendPacket(WebSocketPacket packet) {
CompletableFuture<Integer> rs = this._runner.sendMessage(packet); CompletableFuture<Integer> rs = this._runner.sendMessage(packet);
if (_engine.finest) _engine.logger.finest("userid:" + userid() + " send websocket message(" + packet + ")" + " on " + this); if (_engine.finest) _engine.logger.finest("userid:" + getUserid() + " send websocket message(" + packet + ")" + " on " + this);
return rs == null ? CompletableFuture.completedFuture(RETCODE_WSOCKET_CLOSED) : rs; return rs == null ? CompletableFuture.completedFuture(RETCODE_WSOCKET_CLOSED) : rs;
} }
@@ -216,7 +218,7 @@ public abstract class WebSocket<G extends Serializable, T> {
return ((CompletableFuture) message).thenCompose((json) -> _engine.node.sendMessage(json, last, userids)); return ((CompletableFuture) message).thenCompose((json) -> _engine.node.sendMessage(json, last, userids));
} }
CompletableFuture<Integer> rs = _engine.node.sendMessage(message, last, userids); CompletableFuture<Integer> rs = _engine.node.sendMessage(message, last, userids);
if (_engine.finest) _engine.logger.finest("userids:" + Arrays.toString(userids) + " send websocket message(" + _jsonConvert.convertTo(message) + ")"); if (_engine.finest) _engine.logger.finest("userids:" + Arrays.toString(userids) + " send websocket message(" + message + ")");
return rs; return rs;
} }
@@ -245,7 +247,7 @@ public abstract class WebSocket<G extends Serializable, T> {
return ((CompletableFuture) message).thenCompose((json) -> _engine.node.broadcastMessage(json, last)); return ((CompletableFuture) message).thenCompose((json) -> _engine.node.broadcastMessage(json, last));
} }
CompletableFuture<Integer> rs = _engine.node.broadcastMessage(message, last); CompletableFuture<Integer> rs = _engine.node.broadcastMessage(message, last);
if (_engine.finest) _engine.logger.finest("broadcast send websocket message(" + _jsonConvert.convertTo(message) + ")"); if (_engine.finest) _engine.logger.finest("broadcast send websocket message(" + message + ")");
return rs; return rs;
} }
@@ -317,7 +319,7 @@ public abstract class WebSocket<G extends Serializable, T> {
* *
* @return userid * @return userid
*/ */
public final G userid() { public final G getUserid() {
return _userid; return _userid;
} }
@@ -348,6 +350,18 @@ public abstract class WebSocket<G extends Serializable, T> {
return _remoteAddr; return _remoteAddr;
} }
protected Convert getTextConvert() {
return _textConvert;
}
protected Convert getBinaryConvert() {
return _binaryConvert;
}
protected Convert getSendConvert() {
return _sendConvert;
}
//------------------------------------------------------------------- //-------------------------------------------------------------------
/** /**
* 获取指定userid的WebSocket数组, 没有返回null <br> * 获取指定userid的WebSocket数组, 没有返回null <br>
@@ -382,6 +396,15 @@ public abstract class WebSocket<G extends Serializable, T> {
return _engine.getLocalWebSockets(); return _engine.getLocalWebSockets();
} }
/**
* 获取ByteBuffer资源池
*
* @return Supplier
*/
protected Supplier<ByteBuffer> getByteBufferSupplier() {
return this._runner.context.getBufferSupplier();
}
//------------------------------------------------------------------- //-------------------------------------------------------------------
/** /**
* 返回sessionid, null表示连接不合法或异常,默认实现是request.sessionid(true),通常需要重写该方法 * 返回sessionid, null表示连接不合法或异常,默认实现是request.sessionid(true),通常需要重写该方法
@@ -401,14 +424,6 @@ public abstract class WebSocket<G extends Serializable, T> {
*/ */
protected abstract CompletableFuture<G> createUserid(); protected abstract CompletableFuture<G> createUserid();
/**
* 标记为WebSocketBinary才需要重写此方法
*
* @param channel 请求连接
*/
public void onRead(AsyncConnection channel) {
}
/** /**
* WebSokcet连接成功后的回调方法 * WebSokcet连接成功后的回调方法
*/ */
@@ -440,6 +455,15 @@ public abstract class WebSocket<G extends Serializable, T> {
public void onMessage(T message, boolean last) { public void onMessage(T message, boolean last) {
} }
/**
* 接收到文本消息的回调方法
*
* @param text 消息
* @param last 是否最后一条
*/
public void onMessage(String text, boolean last) {
}
/** /**
* 接收到二进制消息的回调方法 * 接收到二进制消息的回调方法
* *
@@ -467,6 +491,15 @@ public abstract class WebSocket<G extends Serializable, T> {
return this._runner == null ? 0 : this._runner.lastSendTime; return this._runner == null ? 0 : this._runner.lastSendTime;
} }
/**
* 获取最后一次发送PING消息的时间
*
* @return long
*/
public long getLastPingTime() {
return this.pingtime;
}
/** /**
* 显式地关闭WebSocket * 显式地关闭WebSocket
*/ */
@@ -476,6 +509,6 @@ public abstract class WebSocket<G extends Serializable, T> {
@Override @Override
public String toString() { public String toString() {
return this.userid() + "@" + _remoteAddr; return this.getUserid() + "@" + _remoteAddr;
} }
} }

View File

@@ -1,24 +0,0 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.net.http;
import java.lang.annotation.*;
import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
/**
* 被标记为 &#64;WebSocketBinary 的WebSocketServlet 将使用原始的TCP传输, 通常用于类似音频/视频传输场景
*
* <p> 详情见: https://redkale.org
* @author zhangjx
*/
@Inherited
@Documented
@Target({TYPE})
@Retention(RUNTIME)
public @interface WebSocketBinary {
}

View File

@@ -10,9 +10,10 @@ import java.io.*;
import java.util.*; import java.util.*;
import java.util.concurrent.*; import java.util.concurrent.*;
import java.util.concurrent.atomic.*; import java.util.concurrent.atomic.*;
import java.util.function.Predicate;
import java.util.logging.*; import java.util.logging.*;
import java.util.stream.*; import java.util.stream.*;
import org.redkale.convert.json.JsonConvert; import org.redkale.convert.Convert;
import static org.redkale.net.http.WebSocket.RETCODE_GROUP_EMPTY; import static org.redkale.net.http.WebSocket.RETCODE_GROUP_EMPTY;
import org.redkale.util.*; import org.redkale.util.*;
@@ -23,55 +24,57 @@ import org.redkale.util.*;
* *
* @author zhangjx * @author zhangjx
*/ */
public final class WebSocketEngine { public class WebSocketEngine {
//全局自增长ID @Comment("全局自增长ID, 为了确保在一个进程里多个WebSocketEngine定时发送ping时不会同时进行")
private static final AtomicInteger sequence = new AtomicInteger(); private static final AtomicInteger sequence = new AtomicInteger();
//Engine自增长序号ID @Comment("Engine自增长序号ID")
private final int index; private final int index;
//当前WebSocket对应的Engine @Comment("当前WebSocket对应的Engine")
private final String engineid; private final String engineid;
//当前WebSocket对应的Node @Comment("当前WebSocket对应的Node")
protected final WebSocketNode node; protected final WebSocketNode node;
//HttpContext //HttpContext
protected final HttpContext context; protected final HttpContext context;
//JsonConvert //Convert
protected final JsonConvert 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<>(); private final Map<Serializable, WebSocket> websockets = new ConcurrentHashMap<>();
//在线用户ID对应的WebSocket组用于单用户多连接模式 @Comment("在线用户ID对应的WebSocket组用于单用户多连接模式")
private final Map<Serializable, List<WebSocket>> websockets2 = new ConcurrentHashMap<>(); private final Map<Serializable, List<WebSocket>> websockets2 = new ConcurrentHashMap<>();
//用于PING的定时器 @Comment("用于PING的定时器")
private ScheduledThreadPoolExecutor scheduler; private ScheduledThreadPoolExecutor scheduler;
//日志 @Comment("日志")
protected final Logger logger; protected final Logger logger;
//FINEST日志级别 @Comment("日志级别")
protected final boolean finest; protected final boolean finest;
@Comment("PING的间隔秒数")
private int liveinterval; private int liveinterval;
protected WebSocketEngine(String engineid, boolean single, HttpContext context, int liveinterval, WebSocketNode node, Logger logger) { protected WebSocketEngine(String engineid, boolean single, HttpContext context, int liveinterval, WebSocketNode node, Convert sendConvert, Logger logger) {
this.engineid = engineid; this.engineid = engineid;
this.single = single; this.single = single;
this.context = context; this.context = context;
this.convert = context.getJsonConvert(); this.sendConvert = sendConvert;
this.node = node; this.node = node;
this.liveinterval = liveinterval; this.liveinterval = liveinterval;
this.logger = logger; this.logger = logger;
this.index = sequence.getAndIncrement();
this.finest = logger.isLoggable(Level.FINEST); this.finest = logger.isLoggable(Level.FINEST);
this.index = sequence.getAndIncrement();
} }
void init(AnyValue conf) { void init(AnyValue conf) {
@@ -84,8 +87,10 @@ public final class WebSocketEngine {
return t; return t;
}); });
long delay = (interval - System.currentTimeMillis() / 1000 % interval) + index * 5; long delay = (interval - System.currentTimeMillis() / 1000 % interval) + index * 5;
final int intervalms = interval * 1000;
scheduler.scheduleWithFixedDelay(() -> { scheduler.scheduleWithFixedDelay(() -> {
getLocalWebSockets().forEach(x -> x.sendPing()); long now = System.currentTimeMillis();
getLocalWebSockets().stream().filter(x -> (now - x.getLastSendTime()) > intervalms).forEach(x -> x.sendPing());
}, delay, interval, TimeUnit.SECONDS); }, delay, interval, TimeUnit.SECONDS);
if (finest) logger.finest(this.getClass().getSimpleName() + "(" + engineid + ")" + " start keeplive(delay:" + delay + ", interval:" + interval + "s) scheduler executor"); if (finest) logger.finest(this.getClass().getSimpleName() + "(" + engineid + ")" + " start keeplive(delay:" + delay + ", interval:" + interval + "s) scheduler executor");
} }
@@ -94,6 +99,7 @@ public final class WebSocketEngine {
if (scheduler != null) scheduler.shutdownNow(); if (scheduler != null) scheduler.shutdownNow();
} }
@Comment("添加WebSocket")
void add(WebSocket socket) { void add(WebSocket socket) {
if (single) { if (single) {
websockets.put(socket._userid, socket); websockets.put(socket._userid, socket);
@@ -108,6 +114,7 @@ public final class WebSocketEngine {
if (node != null) node.connect(socket._userid); if (node != null) node.connect(socket._userid);
} }
@Comment("从WebSocketEngine删除指定WebSocket")
void remove(WebSocket socket) { void remove(WebSocket socket) {
Serializable userid = socket._userid; Serializable userid = socket._userid;
if (single) { if (single) {
@@ -125,24 +132,32 @@ public final class WebSocketEngine {
} }
} }
@Comment("给所有连接用户发送消息")
public CompletableFuture<Integer> broadcastMessage(final Object message, final boolean last) { 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) { 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); final boolean more = (!(message instanceof WebSocketPacket) || ((WebSocketPacket) message).sendBuffers == null);
if (more) { if (more) {
final WebSocketPacket packet = (message instanceof WebSocketPacket) ? (WebSocketPacket) message final WebSocketPacket packet = (message instanceof WebSocketPacket) ? (WebSocketPacket) message
: ((message == null || message instanceof CharSequence || message instanceof byte[]) : ((message == null || message instanceof CharSequence || message instanceof byte[])
? new WebSocketPacket((Serializable) message, last) : new WebSocketPacket(this.convert, message, last)); ? new WebSocketPacket((Serializable) message, last) : new WebSocketPacket(this.sendConvert, message, last));
packet.setSendBuffers(packet.encode(context.getBufferSupplier())); packet.setSendBuffers(packet.encode(context.getBufferSupplier()));
CompletableFuture<Integer> future = null; CompletableFuture<Integer> future = null;
if (single) { if (single) {
for (WebSocket websocket : websockets.values()) { 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); future = future == null ? websocket.sendPacket(packet) : future.thenCombine(websocket.sendPacket(packet), (a, b) -> a | (Integer) b);
} }
} else { } else {
for (List<WebSocket> list : websockets2.values()) { for (List<WebSocket> list : websockets2.values()) {
for (WebSocket websocket : list) { 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); future = future == null ? websocket.sendPacket(packet) : future.thenCombine(websocket.sendPacket(packet), (a, b) -> a | (Integer) b);
} }
} }
@@ -153,11 +168,13 @@ public final class WebSocketEngine {
CompletableFuture<Integer> future = null; CompletableFuture<Integer> future = null;
if (single) { if (single) {
for (WebSocket websocket : websockets.values()) { 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); future = future == null ? websocket.send(message, last) : future.thenCombine(websocket.send(message, last), (a, b) -> a | (Integer) b);
} }
} else { } else {
for (List<WebSocket> list : websockets2.values()) { for (List<WebSocket> list : websockets2.values()) {
for (WebSocket websocket : list) { 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); future = future == null ? websocket.send(message, last) : future.thenCombine(websocket.send(message, last), (a, b) -> a | (Integer) b);
} }
} }
@@ -166,6 +183,7 @@ public final class WebSocketEngine {
} }
} }
@Comment("给指定用户组发送消息")
public CompletableFuture<Integer> sendMessage(final Object message, final boolean last, final Serializable... userids) { public CompletableFuture<Integer> sendMessage(final Object message, final boolean last, final Serializable... userids) {
if (message instanceof CompletableFuture) { if (message instanceof CompletableFuture) {
return ((CompletableFuture) message).thenCompose((json) -> sendMessage(json, last, userids)); return ((CompletableFuture) message).thenCompose((json) -> sendMessage(json, last, userids));
@@ -174,7 +192,7 @@ public final class WebSocketEngine {
if (more) { if (more) {
final WebSocketPacket packet = (message instanceof WebSocketPacket) ? (WebSocketPacket) message final WebSocketPacket packet = (message instanceof WebSocketPacket) ? (WebSocketPacket) message
: ((message == null || message instanceof CharSequence || message instanceof byte[]) : ((message == null || message instanceof CharSequence || message instanceof byte[])
? new WebSocketPacket((Serializable) message, last) : new WebSocketPacket(this.convert, message, last)); ? new WebSocketPacket((Serializable) message, last) : new WebSocketPacket(this.sendConvert, message, last));
packet.setSendBuffers(packet.encode(context.getBufferSupplier())); packet.setSendBuffers(packet.encode(context.getBufferSupplier()));
CompletableFuture<Integer> future = null; CompletableFuture<Integer> future = null;
if (single) { if (single) {
@@ -215,21 +233,33 @@ public final class WebSocketEngine {
} }
} }
Collection<WebSocket> getLocalWebSockets() { @Comment("获取所有连接")
public Collection<WebSocket> getLocalWebSockets() {
if (single) return websockets.values(); if (single) return websockets.values();
List<WebSocket> list = new ArrayList<>(); List<WebSocket> list = new ArrayList<>();
websockets2.values().forEach(x -> list.addAll(x)); websockets2.values().forEach(x -> list.addAll(x));
return list; return list;
} }
//适用于单用户单连接模式 @Comment("获取当前连接总数")
public int getLocalWebSocketSize() {
if (single) return websockets.size();
return (int) websockets2.values().stream().mapToInt(sublist -> sublist.size()).count();
}
@Comment("获取当前用户总数")
public int getLocalUserSize() {
return single ? websockets.size() : websockets2.size();
}
@Comment("适用于单用户单连接模式")
public WebSocket findLocalWebSocket(Serializable userid) { public WebSocket findLocalWebSocket(Serializable userid) {
if (single) return websockets.get(userid); if (single) return websockets.get(userid);
List<WebSocket> list = websockets2.get(userid); List<WebSocket> list = websockets2.get(userid);
return (list == null || list.isEmpty()) ? null : list.get(list.size() - 1); return (list == null || list.isEmpty()) ? null : list.get(list.size() - 1);
} }
//适用于单用户多连接模式 @Comment("适用于单用户多连接模式")
public Stream<WebSocket> getLocalWebSockets(Serializable userid) { public Stream<WebSocket> getLocalWebSockets(Serializable userid) {
if (single) { if (single) {
WebSocket websocket = websockets.get(userid); WebSocket websocket = websockets.get(userid);

View File

@@ -26,6 +26,12 @@ import org.redkale.util.*;
*/ */
public abstract class WebSocketNode { public abstract class WebSocketNode {
@Comment("存储当前SNCP节点列表的key")
public static final String SOURCE_SNCP_NODES_KEY = "redkale_sncpnodes";
@Comment("存储当前用户数量的key")
public static final String SOURCE_USER_COUNT_KEY = "redkale_usercount";
protected final Logger logger = Logger.getLogger(this.getClass().getSimpleName()); protected final Logger logger = Logger.getLogger(this.getClass().getSimpleName());
protected final boolean finest = logger.isLoggable(Level.FINEST); protected final boolean finest = logger.isLoggable(Level.FINEST);
@@ -56,8 +62,10 @@ public abstract class WebSocketNode {
public final void postDestroy(AnyValue conf) { public final void postDestroy(AnyValue conf) {
if (this.localEngine == null) return; if (this.localEngine == null) return;
//关掉所有本地本地WebSocket //关掉所有本地本地WebSocket
this.localEngine.getLocalWebSockets().forEach(g -> disconnect(g.userid())); 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_NODES_KEY, localSncpAddress);
}
} }
protected abstract CompletableFuture<List<String>> getWebSocketAddresses(@RpcTargetAddress InetSocketAddress targetAddress, Serializable userid); protected abstract CompletableFuture<List<String>> getWebSocketAddresses(@RpcTargetAddress InetSocketAddress targetAddress, Serializable userid);
@@ -140,7 +148,55 @@ public abstract class WebSocketNode {
}); });
} }
/**
* 判断指定用户是否WebSocket在线
*
* @param userid
*
* @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(userid);
}
/**
* 获取在线用户总数
*
*
* @return boolean
*/
public CompletableFuture<Integer> getUserSize() {
if (this.localEngine != null && this.sncpNodeAddresses == null) {
return CompletableFuture.completedFuture(this.localEngine.getLocalUserSize());
}
return this.sncpNodeAddresses.getKeySizeAsync().thenCompose(count -> {
return sncpNodeAddresses.existsAsync(SOURCE_SNCP_NODES_KEY).thenApply(exists -> exists ? (count - 1) : count);
});
}
//-------------------------------------------------------------------------------- //--------------------------------------------------------------------------------
/**
* 获取本地的WebSocketEngine没有则返回null
*
*
* @return WebSocketEngine
*/
public final WebSocketEngine getLocalWebSocketEngine() {
return this.localEngine;
}
/**
* 向指定用户发送消息,先发送本地连接,再发送远程连接 <br>
* 如果当前WebSocketNode是远程模式此方法只发送远程连接
*
* @param message 消息内容
* @param userids Serializable[]
*
* @return 为0表示成功 其他值表示部分发送异常
*/
public final CompletableFuture<Integer> sendMessage(Object message, final Serializable... userids) { public final CompletableFuture<Integer> sendMessage(Object message, final Serializable... userids) {
return sendMessage(message, true, userids); return sendMessage(message, true, userids);
} }
@@ -155,7 +211,6 @@ public abstract class WebSocketNode {
* *
* @return 为0表示成功 其他值表示部分发送异常 * @return 为0表示成功 其他值表示部分发送异常
*/ */
//最近连接发送逻辑还没有理清楚
public final CompletableFuture<Integer> sendMessage(final Object message, final boolean last, final Serializable... userids) { public final CompletableFuture<Integer> sendMessage(final Object message, final boolean last, final Serializable... userids) {
if (userids == null || userids.length < 1) return CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY); if (userids == null || userids.length < 1) return CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY);
if (this.localEngine != null && this.sncpNodeAddresses == null) { //本地模式且没有分布式 if (this.localEngine != null && this.sncpNodeAddresses == null) { //本地模式且没有分布式

View File

@@ -10,8 +10,7 @@ import java.io.*;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.util.function.Supplier; import java.util.function.Supplier;
import java.util.logging.*; import java.util.logging.*;
import org.redkale.convert.ConvertMask; import org.redkale.convert.*;
import org.redkale.convert.json.JsonConvert;
/** /**
* *
@@ -60,7 +59,7 @@ public final class WebSocketPacket {
protected Object sendJson; protected Object sendJson;
JsonConvert sendConvert; Convert sendConvert;
ByteBuffer[] sendBuffers; ByteBuffer[] sendBuffers;
@@ -93,8 +92,8 @@ public final class WebSocketPacket {
this.last = fin; this.last = fin;
} }
public WebSocketPacket(JsonConvert convert, Object json, boolean fin) { public WebSocketPacket(Convert convert, Object json, boolean fin) {
this.type = FrameType.TEXT; this.type = (convert == null || !convert.isBinary()) ? FrameType.TEXT : FrameType.BINARY;
this.sendConvert = convert; this.sendConvert = convert;
this.sendJson = json; this.sendJson = json;
this.last = fin; this.last = fin;
@@ -160,7 +159,7 @@ public final class WebSocketPacket {
@Override @Override
public String toString() { public String toString() {
return this.getClass().getSimpleName() + "[type=" + type + ", last=" + last + (payload != null ? (", payload=" + payload) : "") + (bytes != null ? (", bytes=[" + bytes.length + ']') : "") + "]"; return this.getClass().getSimpleName() + "[type=" + type + ", last=" + last + (payload != null ? (", payload=" + payload) : "") + (bytes != null ? (", bytes=[" + bytes.length + ']') : "") + (sendJson != null ? (", json=" + sendJson) : "") + "]";
} }
/** /**

View File

@@ -16,7 +16,7 @@ import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.BiConsumer; import java.util.function.BiConsumer;
import java.util.logging.*; import java.util.logging.*;
import org.redkale.convert.json.JsonConvert; import org.redkale.convert.Convert;
/** /**
* WebSocket的消息接收发送器, 一个WebSocket对应一个WebSocketRunner * WebSocket的消息接收发送器, 一个WebSocket对应一个WebSocketRunner
@@ -44,23 +44,17 @@ class WebSocketRunner implements Runnable {
private final BlockingQueue<QueueEntry> queue = new ArrayBlockingQueue(1024); private final BlockingQueue<QueueEntry> queue = new ArrayBlockingQueue(1024);
private final boolean wsbinary;
private final BiConsumer<WebSocket, Object> restMessageConsumer; //主要供RestWebSocket使用 private final BiConsumer<WebSocket, Object> restMessageConsumer; //主要供RestWebSocket使用
protected long lastSendTime; protected long lastSendTime;
protected final JsonConvert convert; WebSocketRunner(Context context, WebSocket webSocket, BiConsumer<WebSocket, Object> messageConsumer, AsyncConnection channel) {
WebSocketRunner(Context context, WebSocket webSocket, BiConsumer<WebSocket, Object> messageConsumer, AsyncConnection channel, final boolean wsbinary) {
this.context = context; this.context = context;
this.engine = webSocket._engine; this.engine = webSocket._engine;
this.webSocket = webSocket; this.webSocket = webSocket;
this.restMessageConsumer = messageConsumer; this.restMessageConsumer = messageConsumer;
this.channel = channel; this.channel = channel;
this.wsbinary = wsbinary;
this.readBuffer = context.pollBuffer(); this.readBuffer = context.pollBuffer();
this.convert = context.getJsonConvert();
} }
@Override @Override
@@ -70,10 +64,6 @@ class WebSocketRunner implements Runnable {
webSocket.onConnected(); webSocket.onConnected();
channel.setReadTimeoutSecond(300); //读取超时5分钟 channel.setReadTimeoutSecond(300); //读取超时5分钟
if (channel.isOpen()) { if (channel.isOpen()) {
if (wsbinary) {
webSocket.onRead(channel);
return;
}
channel.read(readBuffer, null, new CompletionHandler<Integer, Void>() { channel.read(readBuffer, null, new CompletionHandler<Integer, Void>() {
private ByteBuffer recentExBuffer; private ByteBuffer recentExBuffer;
@@ -118,30 +108,62 @@ class WebSocketRunner implements Runnable {
} }
if (packet.type == FrameType.TEXT) { if (packet.type == FrameType.TEXT) {
Object message = convert.convertFrom(webSocket._messageTextType, packet.receiveMasker, packet.receiveBuffers); Convert textConvert = webSocket.getTextConvert();
if (readBuffer != null) { if (textConvert == null) {
readBuffer.clear(); byte[] message = packet.getReceiveBytes();
channel.read(readBuffer, null, this); if (readBuffer != null) {
} readBuffer.clear();
try { channel.read(readBuffer, null, this);
if (restMessageConsumer != null) { //主要供RestWebSocket使用 }
restMessageConsumer.accept(webSocket, message); try {
} else { webSocket.onMessage(new String(message, "UTF-8"), packet.last);
webSocket.onMessage(message, 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 {
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);
} }
} catch (Exception e) {
context.getLogger().log(Level.SEVERE, "WebSocket onTextMessage error (" + packet + ")", e);
} }
} else if (packet.type == FrameType.BINARY) { } else if (packet.type == FrameType.BINARY) {
byte[] message = packet.getReceiveBytes(); Convert binaryConvert = webSocket.getBinaryConvert();
if (readBuffer != null) { if (binaryConvert == null) {
readBuffer.clear(); byte[] message = packet.getReceiveBytes();
channel.read(readBuffer, null, this); if (readBuffer != null) {
} readBuffer.clear();
try { channel.read(readBuffer, null, this);
webSocket.onMessage(message, packet.last); }
} catch (Exception e) { try {
context.getLogger().log(Level.SEVERE, "WebSocket onBinaryMessage error (" + packet + ")", e); 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) { } else if (packet.type == FrameType.PONG) {
byte[] message = packet.getReceiveBytes(); byte[] message = packet.getReceiveBytes();
@@ -207,7 +229,7 @@ class WebSocketRunner implements Runnable {
if (closed) return CompletableFuture.completedFuture(RETCODE_WSOCKET_CLOSED); if (closed) return CompletableFuture.completedFuture(RETCODE_WSOCKET_CLOSED);
boolean debug = true; boolean debug = true;
//System.out.println("推送消息"); //System.out.println("推送消息");
if (debug) context.getLogger().log(Level.FINEST, "send web socket message: " + packet); //if (debug) context.getLogger().log(Level.FINEST, "send web socket message: " + packet);
final CompletableFuture<Integer> futureResult = new CompletableFuture<>(); final CompletableFuture<Integer> futureResult = new CompletableFuture<>();
if (writing.getAndSet(true)) { if (writing.getAndSet(true)) {
queue.add(new QueueEntry(futureResult, packet)); queue.add(new QueueEntry(futureResult, packet));
@@ -258,7 +280,7 @@ class WebSocketRunner implements Runnable {
QueueEntry entry = queue.poll(); QueueEntry entry = queue.poll();
if (entry != null) { if (entry != null) {
future = entry.future; future = entry.future;
ByteBuffer[] buffers = packet.sendBuffers != null ? packet.duplicateSendBuffers() : packet.encode(context.getBufferSupplier()); ByteBuffer[] buffers = entry.packet.sendBuffers != null ? entry.packet.duplicateSendBuffers() : entry.packet.encode(context.getBufferSupplier());
lastSendTime = System.currentTimeMillis(); lastSendTime = System.currentTimeMillis();
channel.write(buffers, buffers, this); channel.write(buffers, buffers, this);
} }

View File

@@ -15,7 +15,7 @@ import java.util.concurrent.CompletableFuture;
import java.util.function.BiConsumer; import java.util.function.BiConsumer;
import java.util.logging.*; import java.util.logging.*;
import javax.annotation.*; import javax.annotation.*;
import org.redkale.convert.json.JsonConvert; import org.redkale.convert.Convert;
import org.redkale.service.*; import org.redkale.service.*;
import org.redkale.util.*; import org.redkale.util.*;
@@ -46,15 +46,12 @@ public abstract class WebSocketServlet extends HttpServlet implements Resourcabl
public static final String WEBPARAM__LIVEINTERVAL = "liveinterval"; public static final String WEBPARAM__LIVEINTERVAL = "liveinterval";
@Comment("WebScoket服务器给客户端进行ping操作的默认间隔时间, 单位: 秒") @Comment("WebScoket服务器给客户端进行ping操作的默认间隔时间, 单位: 秒")
public static final int DEFAILT_LIVEINTERVAL = 60; public static final int DEFAILT_LIVEINTERVAL = 15;
protected final Logger logger = Logger.getLogger(this.getClass().getSimpleName()); protected final Logger logger = Logger.getLogger(this.getClass().getSimpleName());
private final MessageDigest digest = getMessageDigest(); private final MessageDigest digest = getMessageDigest();
@Comment("是否用于二进制流传输")
protected final boolean wsbinary = getClass().getAnnotation(WebSocketBinary.class) != null;
private final BiConsumer<WebSocket, Object> restMessageConsumer = createRestOnMessageConsumer(); private final BiConsumer<WebSocket, Object> restMessageConsumer = createRestOnMessageConsumer();
protected Type messageTextType; //RestWebSocket时会被修改 protected Type messageTextType; //RestWebSocket时会被修改
@@ -63,11 +60,20 @@ public abstract class WebSocketServlet extends HttpServlet implements Resourcabl
protected int liveinterval = DEFAILT_LIVEINTERVAL; protected int liveinterval = DEFAILT_LIVEINTERVAL;
@Resource @Resource(name = "jsonconvert")
protected JsonConvert jsonConvert; //Rest.createRestWebSocketServlet 需要过滤掉已有的@Resource protected Convert jsonConvert;
@Resource(name = "$_textconvert")
protected Convert textConvert;
@Resource(name = "$_binaryconvert")
protected Convert binaryConvert;
@Resource(name = "$_sendconvert")
protected Convert sendConvert;
@Resource(name = "$") @Resource(name = "$")
protected WebSocketNode node; //Rest.createRestWebSocketServlet 需要过滤掉已有的@Resource protected WebSocketNode node;
protected WebSocketServlet() { protected WebSocketServlet() {
Type msgtype = String.class; Type msgtype = String.class;
@@ -90,6 +96,9 @@ public abstract class WebSocketServlet extends HttpServlet implements Resourcabl
@Override @Override
final void preInit(HttpContext context, AnyValue conf) { final void preInit(HttpContext context, AnyValue conf) {
if (this.textConvert == null) this.textConvert = jsonConvert;
if (this.binaryConvert == null) this.binaryConvert = jsonConvert;
if (this.sendConvert == null) this.sendConvert = jsonConvert;
InetSocketAddress addr = context.getServerAddress(); InetSocketAddress addr = context.getServerAddress();
if (this.node == null) this.node = createWebSocketNode(); if (this.node == null) this.node = createWebSocketNode();
if (this.node == null) { //没有部署SNCP即不是分布式 if (this.node == null) { //没有部署SNCP即不是分布式
@@ -97,7 +106,7 @@ public abstract class WebSocketServlet extends HttpServlet implements Resourcabl
if (logger.isLoggable(Level.WARNING)) logger.warning("Not found WebSocketNode, create a default value for " + getClass().getName()); if (logger.isLoggable(Level.WARNING)) logger.warning("Not found WebSocketNode, create a default value for " + getClass().getName());
} }
//存在WebSocketServlet则此WebSocketNode必须是本地模式Service //存在WebSocketServlet则此WebSocketNode必须是本地模式Service
this.node.localEngine = new WebSocketEngine("WebSocketEngine-" + addr.getHostString() + ":" + addr.getPort() + "-[" + resourceName() + "]", this.single, context, liveinterval, this.node, logger); this.node.localEngine = new WebSocketEngine("WebSocketEngine-" + addr.getHostString() + ":" + addr.getPort() + "-[" + resourceName() + "]", this.single, context, liveinterval, this.node, this.sendConvert, logger);
this.node.init(conf); this.node.init(conf);
this.node.localEngine.init(conf); this.node.localEngine.init(conf);
} }
@@ -131,7 +140,9 @@ public abstract class WebSocketServlet extends HttpServlet implements Resourcabl
final WebSocket webSocket = this.createWebSocket(); final WebSocket webSocket = this.createWebSocket();
webSocket._engine = this.node.localEngine; webSocket._engine = this.node.localEngine;
webSocket._messageTextType = this.messageTextType; webSocket._messageTextType = this.messageTextType;
webSocket._jsonConvert = jsonConvert; webSocket._textConvert = textConvert;
webSocket._binaryConvert = binaryConvert;
webSocket._sendConvert = sendConvert;
webSocket._remoteAddress = request.getRemoteAddress(); webSocket._remoteAddress = request.getRemoteAddress();
webSocket._remoteAddr = request.getRemoteAddr(); webSocket._remoteAddr = request.getRemoteAddr();
initRestWebSocket(webSocket); initRestWebSocket(webSocket);
@@ -176,7 +187,7 @@ public abstract class WebSocketServlet extends HttpServlet implements Resourcabl
} }
webSocket._userid = userid; webSocket._userid = userid;
WebSocketServlet.this.node.localEngine.add(webSocket); WebSocketServlet.this.node.localEngine.add(webSocket);
WebSocketRunner runner = new WebSocketRunner(context, webSocket, restMessageConsumer, response.removeChannel(), wsbinary); WebSocketRunner runner = new WebSocketRunner(context, webSocket, restMessageConsumer, response.removeChannel());
webSocket._runner = runner; webSocket._runner = runner;
context.runAsync(runner); context.runAsync(runner);
response.finish(true); response.finish(true);

View File

@@ -256,13 +256,14 @@ public abstract class Sncp {
* 创建Service的本地模式Class * 创建Service的本地模式Class
* *
* @param <T> Service子类 * @param <T> Service子类
* @param classLoader ClassLoader
* @param name 资源名 * @param name 资源名
* @param serviceImplClass Service类 * @param serviceImplClass Service类
* *
* @return Service实例 * @return Service实例
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
protected static <T extends Service> Class<? extends T> createLocalServiceClass(final String name, final Class<T> serviceImplClass) { protected static <T extends Service> Class<? extends T> createLocalServiceClass(ClassLoader classLoader, final String name, final Class<T> serviceImplClass) {
if (serviceImplClass == null) return null; if (serviceImplClass == null) return null;
if (!Service.class.isAssignableFrom(serviceImplClass)) return serviceImplClass; if (!Service.class.isAssignableFrom(serviceImplClass)) return serviceImplClass;
int mod = serviceImplClass.getModifiers(); int mod = serviceImplClass.getModifiers();
@@ -274,7 +275,7 @@ public abstract class Sncp {
final String clientDesc = Type.getDescriptor(SncpClient.class); final String clientDesc = Type.getDescriptor(SncpClient.class);
final String anyValueDesc = Type.getDescriptor(AnyValue.class); final String anyValueDesc = Type.getDescriptor(AnyValue.class);
final String sncpDynDesc = Type.getDescriptor(SncpDyn.class); final String sncpDynDesc = Type.getDescriptor(SncpDyn.class);
ClassLoader loader = Sncp.class.getClassLoader(); ClassLoader loader = classLoader == null ? Thread.currentThread().getContextClassLoader() : classLoader;
String newDynName = supDynName.substring(0, supDynName.lastIndexOf('/') + 1) + LOCALPREFIX + serviceImplClass.getSimpleName(); String newDynName = supDynName.substring(0, supDynName.lastIndexOf('/') + 1) + LOCALPREFIX + serviceImplClass.getSimpleName();
if (!name.isEmpty()) { if (!name.isEmpty()) {
boolean normal = true; boolean normal = true;
@@ -285,7 +286,7 @@ public abstract class Sncp {
newDynName += "_" + (normal ? name : hash(name)); newDynName += "_" + (normal ? name : hash(name));
} }
try { try {
return (Class<T>) Class.forName(newDynName.replace('/', '.')); return (Class<T>) loader.loadClass(newDynName.replace('/', '.'));
} catch (Throwable ex) { } catch (Throwable ex) {
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
@@ -738,7 +739,7 @@ public abstract class Sncp {
public static <T extends Service> T createSimpleLocalService(final Class<T> serviceImplClass, public static <T extends Service> T createSimpleLocalService(final Class<T> serviceImplClass,
final TransportFactory transportFactory, final InetSocketAddress clientSncpAddress, final String... groups) { final TransportFactory transportFactory, final InetSocketAddress clientSncpAddress, final String... groups) {
return createLocalService("", serviceImplClass, ResourceFactory.root(), transportFactory, clientSncpAddress, Utility.ofSet(groups), null); return createLocalService(null, "", serviceImplClass, ResourceFactory.root(), transportFactory, clientSncpAddress, Utility.ofSet(groups), null);
} }
/** /**
@@ -746,6 +747,7 @@ public abstract class Sncp {
* 创建本地模式Service实例 * 创建本地模式Service实例
* *
* @param <T> Service泛型 * @param <T> Service泛型
* @param classLoader ClassLoader
* @param name 资源名 * @param name 资源名
* @param serviceImplClass Service类 * @param serviceImplClass Service类
* @param resourceFactory ResourceFactory * @param resourceFactory ResourceFactory
@@ -758,6 +760,7 @@ public abstract class Sncp {
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public static <T extends Service> T createLocalService( public static <T extends Service> T createLocalService(
final ClassLoader classLoader,
final String name, final String name,
final Class<T> serviceImplClass, final Class<T> serviceImplClass,
final ResourceFactory resourceFactory, final ResourceFactory resourceFactory,
@@ -766,7 +769,7 @@ public abstract class Sncp {
final Set<String> groups, final Set<String> groups,
final AnyValue conf) { final AnyValue conf) {
try { try {
final Class newClazz = createLocalServiceClass(name, serviceImplClass); final Class newClazz = createLocalServiceClass(classLoader, name, serviceImplClass);
T rs = (T) newClazz.newInstance(); T rs = (T) newClazz.newInstance();
//-------------------------------------- //--------------------------------------
Service remoteService = null; Service remoteService = null;
@@ -780,7 +783,7 @@ public abstract class Sncp {
if (!field.getType().isAssignableFrom(newClazz)) continue; if (!field.getType().isAssignableFrom(newClazz)) continue;
field.setAccessible(true); field.setAccessible(true);
if (remoteService == null && clientSncpAddress != null) { if (remoteService == null && clientSncpAddress != null) {
remoteService = createRemoteService(name, serviceImplClass, transportFactory, clientSncpAddress, groups, conf); remoteService = createRemoteService(classLoader, name, serviceImplClass, transportFactory, clientSncpAddress, groups, conf);
} }
if (remoteService != null) field.set(rs, remoteService); if (remoteService != null) field.set(rs, remoteService);
} }
@@ -821,7 +824,7 @@ public abstract class Sncp {
public static <T extends Service> T createSimpleRemoteService(final Class<T> serviceImplClass, public static <T extends Service> T createSimpleRemoteService(final Class<T> serviceImplClass,
final TransportFactory transportFactory, final InetSocketAddress clientSncpAddress, final String... groups) { final TransportFactory transportFactory, final InetSocketAddress clientSncpAddress, final String... groups) {
return createRemoteService("", serviceImplClass, transportFactory, clientSncpAddress, Utility.ofSet(groups), null); return createRemoteService(null, "", serviceImplClass, transportFactory, clientSncpAddress, Utility.ofSet(groups), null);
} }
/** /**
@@ -865,6 +868,7 @@ public abstract class Sncp {
* 创建远程模式的Service实例 * 创建远程模式的Service实例
* *
* @param <T> Service泛型 * @param <T> Service泛型
* @param classLoader ClassLoader
* @param name 资源名 * @param name 资源名
* @param serviceTypeOrImplClass Service类 * @param serviceTypeOrImplClass Service类
* @param transportFactory TransportFactory * @param transportFactory TransportFactory
@@ -877,6 +881,7 @@ public abstract class Sncp {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public static <T extends Service> T createRemoteService( public static <T extends Service> T createRemoteService(
final ClassLoader classLoader,
final String name, final String name,
final Class<T> serviceTypeOrImplClass, final Class<T> serviceTypeOrImplClass,
final TransportFactory transportFactory, final TransportFactory transportFactory,
@@ -893,12 +898,12 @@ public abstract class Sncp {
final String clientDesc = Type.getDescriptor(SncpClient.class); final String clientDesc = Type.getDescriptor(SncpClient.class);
final String sncpDynDesc = Type.getDescriptor(SncpDyn.class); final String sncpDynDesc = Type.getDescriptor(SncpDyn.class);
final String anyValueDesc = Type.getDescriptor(AnyValue.class); final String anyValueDesc = Type.getDescriptor(AnyValue.class);
ClassLoader loader = Sncp.class.getClassLoader(); ClassLoader loader = classLoader == null ? Thread.currentThread().getContextClassLoader() : classLoader;
String newDynName = supDynName.substring(0, supDynName.lastIndexOf('/') + 1) + REMOTEPREFIX + serviceTypeOrImplClass.getSimpleName(); String newDynName = supDynName.substring(0, supDynName.lastIndexOf('/') + 1) + REMOTEPREFIX + serviceTypeOrImplClass.getSimpleName();
try { try {
Class newClazz = Class.forName(newDynName.replace('/', '.')); Class newClazz = loader.loadClass(newDynName.replace('/', '.'));
T rs = (T) newClazz.newInstance(); T rs = (T) newClazz.newInstance();
SncpClient client = new SncpClient(name, serviceTypeOrImplClass, rs, transportFactory, true, realed ? createLocalServiceClass(name, serviceTypeOrImplClass) : serviceTypeOrImplClass, clientAddress); SncpClient client = new SncpClient(name, serviceTypeOrImplClass, rs, transportFactory, true, realed ? createLocalServiceClass(loader, name, serviceTypeOrImplClass) : serviceTypeOrImplClass, clientAddress);
client.setRemoteGroups(groups); client.setRemoteGroups(groups);
client.setRemoteGroupTransport(transportFactory.loadRemoteTransport(clientAddress, groups)); client.setRemoteGroupTransport(transportFactory.loadRemoteTransport(clientAddress, groups));
Field c = newClazz.getDeclaredField(FIELDPREFIX + "_client"); Field c = newClazz.getDeclaredField(FIELDPREFIX + "_client");
@@ -987,7 +992,7 @@ public abstract class Sncp {
mv.visitEnd(); mv.visitEnd();
} }
int i = -1; int i = -1;
for (final SncpAction entry : SncpClient.getSncpActions(realed ? createLocalServiceClass(name, serviceTypeOrImplClass) : serviceTypeOrImplClass)) { for (final SncpAction entry : SncpClient.getSncpActions(realed ? createLocalServiceClass(loader, name, serviceTypeOrImplClass) : serviceTypeOrImplClass)) {
final int index = ++i; final int index = ++i;
final java.lang.reflect.Method method = entry.method; final java.lang.reflect.Method method = entry.method;
{ {
@@ -1091,7 +1096,7 @@ public abstract class Sncp {
}.loadClass(newDynName.replace('/', '.'), bytes); }.loadClass(newDynName.replace('/', '.'), bytes);
try { try {
T rs = (T) newClazz.newInstance(); T rs = (T) newClazz.newInstance();
SncpClient client = new SncpClient(name, serviceTypeOrImplClass, rs, transportFactory, true, realed ? createLocalServiceClass(name, serviceTypeOrImplClass) : serviceTypeOrImplClass, clientAddress); SncpClient client = new SncpClient(name, serviceTypeOrImplClass, rs, transportFactory, true, realed ? createLocalServiceClass(loader, name, serviceTypeOrImplClass) : serviceTypeOrImplClass, clientAddress);
client.setRemoteGroups(groups); client.setRemoteGroups(groups);
client.setRemoteGroupTransport(transportFactory.loadRemoteTransport(clientAddress, groups)); client.setRemoteGroupTransport(transportFactory.loadRemoteTransport(clientAddress, groups));
{ {

View File

@@ -310,7 +310,7 @@ public final class SncpDynServlet extends SncpServlet {
+ "DynAction" + serviceClass.getSimpleName() + "_" + method.getName() + "_" + actionid; + "DynAction" + serviceClass.getSimpleName() + "_" + method.getName() + "_" + actionid;
while (true) { while (true) {
try { try {
Class.forName(newDynName.replace('/', '.')); Thread.currentThread().getContextClassLoader().loadClass(newDynName.replace('/', '.'));
newDynName += "_"; newDynName += "_";
} catch (Throwable ex) { } catch (Throwable ex) {
break; break;

View File

@@ -9,7 +9,6 @@ import org.redkale.net.PrepareServlet;
import org.redkale.util.AnyValue; import org.redkale.util.AnyValue;
import java.io.IOException; import java.io.IOException;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.util.*;
import org.redkale.service.Service; import org.redkale.service.Service;
import org.redkale.util.*; import org.redkale.util.*;
@@ -55,10 +54,6 @@ public class SncpPrepareServlet extends PrepareServlet<DLong, SncpContext, SncpR
return rs; return rs;
} }
public List<SncpServlet> getSncpServlets() {
return new ArrayList<>(getServlets());
}
@Override @Override
public void init(SncpContext context, AnyValue config) { public void init(SncpContext context, AnyValue config) {
super.init(context, config); //必须要执行 super.init(context, config); //必须要执行

View File

@@ -37,15 +37,12 @@ public class SncpServer extends Server<DLong, SncpContext, SncpRequest, SncpResp
super.init(config); super.init(config);
} }
/** public List<SncpServlet> getSncpServlets() {
* 删除SncpFilter return this.prepare.getServlets();
* }
* @param filterName SncpFilter名称
* public List<SncpFilter> getSncpFilters() {
* @return SncpFilter return this.prepare.getFilters();
*/
public SncpFilter removeFilter(String filterName) {
return (SncpFilter) this.prepare.removeFilter(filterName);
} }
/** /**
@@ -56,7 +53,7 @@ public class SncpServer extends Server<DLong, SncpContext, SncpRequest, SncpResp
* *
* @return SncpFilter * @return SncpFilter
*/ */
public <T extends SncpFilter> T removeFilter(Class<T> filterClass) { public <T extends SncpFilter> T removeSncpFilter(Class<T> filterClass) {
return (T) this.prepare.removeFilter(filterClass); return (T) this.prepare.removeFilter(filterClass);
} }
@@ -88,10 +85,6 @@ public class SncpServer extends Server<DLong, SncpContext, SncpRequest, SncpResp
SncpDynServlet sds = new SncpDynServlet(BsonFactory.root().getConvert(), Sncp.getResourceName(sncpService), Sncp.getResourceType(sncpService), sncpService); SncpDynServlet sds = new SncpDynServlet(BsonFactory.root().getConvert(), Sncp.getResourceName(sncpService), Sncp.getResourceType(sncpService), sncpService);
this.prepare.addServlet(sds, null, Sncp.getConf(sncpService)); this.prepare.addServlet(sds, null, Sncp.getConf(sncpService));
} }
public List<SncpServlet> getSncpServlets() {
return ((SncpPrepareServlet) this.prepare).getSncpServlets();
}
@Override @Override
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")

View File

@@ -40,6 +40,10 @@ public abstract class SncpServlet extends Servlet<SncpContext, SncpRequest, Sncp
return serviceName; return serviceName;
} }
public Class getServiceType() {
return type;
}
public abstract DLong getServiceid(); public abstract DLong getServiceid();
protected ExecutorService getExecutor() { protected ExecutorService getExecutor() {

View File

@@ -29,35 +29,47 @@ public @interface RetLabel {
String value(); String value();
public static abstract class AbstractRetCode { public static class RetCode {
protected static final Map<Integer, String> rets = new HashMap(); protected final Map<Integer, String> rets = new HashMap();
protected static void load(Class clazz) { protected final Class clazz;
rets.putAll(RetLoader.load(clazz));
protected boolean inited;
public RetCode(Class clazz) {
this.clazz = clazz;
} }
public static RetResult retResult(int retcode) { public RetResult retResult(int retcode) {
if (retcode == 0) return RetResult.success(); if (retcode == 0) return RetResult.success();
return new RetResult(retcode, retInfo(retcode)); return new RetResult(retcode, retInfo(retcode));
} }
public static RetResult retResult(int retcode, Object... args) { public RetResult retResult(int retcode, Object... args) {
if (retcode == 0) return RetResult.success(); if (retcode == 0) return RetResult.success();
if (args == null || args.length < 1) return new RetResult(retcode, retInfo(retcode)); if (args == null || args.length < 1) return new RetResult(retcode, retInfo(retcode));
String info = MessageFormat.format(retInfo(retcode), args); String info = MessageFormat.format(retInfo(retcode), args);
return new RetResult(retcode, info); return new RetResult(retcode, info);
} }
public static RetResult set(RetResult result, int retcode, Object... args) { public RetResult set(RetResult result, int retcode, Object... args) {
if (retcode == 0) return result.retcode(0).retinfo(""); if (retcode == 0) return result.retcode(0).retinfo("");
if (args == null || args.length < 1) return result.retcode(retcode).retinfo(retInfo(retcode)); if (args == null || args.length < 1) return result.retcode(retcode).retinfo(retInfo(retcode));
String info = MessageFormat.format(retInfo(retcode), args); String info = MessageFormat.format(retInfo(retcode), args);
return result.retcode(retcode).retinfo(info); return result.retcode(retcode).retinfo(info);
} }
public static String retInfo(int retcode) { public String retInfo(int retcode) {
if (retcode == 0) return "成功"; if (retcode == 0) return "成功";
if (!this.inited) {
synchronized (this) {
if (!this.inited) {
rets.putAll(RetLoader.load(clazz));
}
this.inited = true;
}
}
return rets.getOrDefault(retcode, "未知错误"); return rets.getOrDefault(retcode, "未知错误");
} }
} }

View File

@@ -68,7 +68,7 @@ public class WebSocketNodeService extends WebSocketNode implements Service {
@Override @Override
public CompletableFuture<Void> connect(Serializable userid, InetSocketAddress sncpAddr) { public CompletableFuture<Void> connect(Serializable userid, InetSocketAddress sncpAddr) {
CompletableFuture<Void> future = sncpNodeAddresses.appendSetItemAsync(userid, sncpAddr); CompletableFuture<Void> future = sncpNodeAddresses.appendSetItemAsync(userid, sncpAddr);
future = future.thenAccept((a) -> sncpNodeAddresses.appendSetItemAsync("redkale_sncpnodes", sncpAddr)); future = future.thenAccept((a) -> sncpNodeAddresses.appendSetItemAsync(SOURCE_SNCP_NODES_KEY, sncpAddr));
if (finest) logger.finest(WebSocketNodeService.class.getSimpleName() + ".event: " + userid + " connect from " + sncpAddr); if (finest) logger.finest(WebSocketNodeService.class.getSimpleName() + ".event: " + userid + " connect from " + sncpAddr);
return future; return future;
} }

View File

@@ -13,7 +13,7 @@ import java.util.function.Consumer;
import java.util.logging.*; import java.util.logging.*;
import javax.annotation.Resource; import javax.annotation.Resource;
import org.redkale.convert.json.*; import org.redkale.convert.json.*;
import org.redkale.net.sncp.Sncp; import org.redkale.net.sncp.*;
import org.redkale.service.*; import org.redkale.service.*;
import org.redkale.util.*; import org.redkale.util.*;
@@ -84,7 +84,7 @@ public class CacheMemorySource<K extends Serializable, V extends Object> extends
String storeValueStr = prop.getValue("value-type"); String storeValueStr = prop.getValue("value-type");
if (storeKeyStr != null && storeValueStr != null) { if (storeKeyStr != null && storeValueStr != null) {
try { try {
this.setStoreType(Class.forName(storeKeyStr), Class.forName(storeValueStr)); this.setStoreType(Thread.currentThread().getContextClassLoader().loadClass(storeKeyStr), Thread.currentThread().getContextClassLoader().loadClass(storeValueStr));
} catch (Throwable e) { } 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 (" + storeKeyStr + ", " + storeValueStr + ") error", e);
} }
@@ -94,7 +94,7 @@ public class CacheMemorySource<K extends Serializable, V extends Object> extends
String expireHandlerClass = prop == null ? null : prop.getValue("expirehandler"); String expireHandlerClass = prop == null ? null : prop.getValue("expirehandler");
if (expireHandlerClass != null) { if (expireHandlerClass != null) {
try { try {
this.expireHandler = (Consumer<CacheEntry>) Class.forName(expireHandlerClass).newInstance(); this.expireHandler = (Consumer<CacheEntry>) Thread.currentThread().getContextClassLoader().loadClass(expireHandlerClass).newInstance();
} catch (Throwable e) { } catch (Throwable e) {
logger.log(Level.SEVERE, self.getClass().getSimpleName() + " new expirehandler class (" + expireHandlerClass + ") instance error", e); logger.log(Level.SEVERE, self.getClass().getSimpleName() + " new expirehandler class (" + expireHandlerClass + ") instance error", e);
} }
@@ -155,22 +155,25 @@ public class CacheMemorySource<K extends Serializable, V extends Object> extends
} }
} }
if (remoteSource != null && !Sncp.isRemote(this)) { if (remoteSource != null && !Sncp.isRemote(this)) {
super.runAsync(() -> { SncpClient client = Sncp.getSncpClient((Service) remoteSource);
try { if (client != null && client.getRemoteGroupTransport() != null) {
CompletableFuture<List<CacheEntry<K, Object>>> listFuture = remoteSource.queryListAsync(); super.runAsync(() -> {
listFuture.whenComplete((list, exp) -> { try {
if (exp != null) { CompletableFuture<List<CacheEntry<K, Object>>> listFuture = remoteSource.queryListAsync();
logger.log(Level.FINEST, CacheSource.class.getSimpleName() + "(" + resourceName() + ") queryListAsync error", exp); listFuture.whenComplete((list, exp) -> {
} else { if (exp != null) {
for (CacheEntry<K, Object> entry : list) { logger.log(Level.FINEST, CacheSource.class.getSimpleName() + "(" + resourceName() + ") queryListAsync error", exp);
container.put(entry.key, entry); } else {
for (CacheEntry<K, Object> entry : list) {
container.put(entry.key, entry);
}
} }
} });
}); } catch (Exception e) {
} catch (Exception e) { logger.log(Level.FINEST, CacheSource.class.getSimpleName() + "(" + resourceName() + ") queryListAsync error, maybe remote node connot connect ", e);
logger.log(Level.FINEST, CacheSource.class.getSimpleName() + "(" + resourceName() + ") queryListAsync error, maybe remote node connot connect ", e); }
} });
}); }
} }
} }
@@ -345,13 +348,13 @@ public class CacheMemorySource<K extends Serializable, V extends Object> extends
} }
@Override @Override
public long getCollectionSize(final K key) { public int getCollectionSize(final K key) {
Collection<V> collection = (Collection<V>) get(key); Collection<V> collection = (Collection<V>) get(key);
return collection == null ? 0 : collection.size(); return collection == null ? 0 : collection.size();
} }
@Override @Override
public CompletableFuture<Long> getCollectionSizeAsync(final K key) { public CompletableFuture<Integer> getCollectionSizeAsync(final K key) {
return CompletableFuture.supplyAsync(() -> getCollectionSize(key), getExecutor()); return CompletableFuture.supplyAsync(() -> getCollectionSize(key), getExecutor());
} }
@@ -440,6 +443,11 @@ public class CacheMemorySource<K extends Serializable, V extends Object> extends
return new ArrayList<>(container.keySet()); return new ArrayList<>(container.keySet());
} }
@Override
public int getKeySize() {
return container.size();
}
@Override @Override
public CompletableFuture<List<CacheEntry<K, Object>>> queryListAsync() { public CompletableFuture<List<CacheEntry<K, Object>>> queryListAsync() {
return CompletableFuture.completedFuture(new ArrayList<>(container.values())); return CompletableFuture.completedFuture(new ArrayList<>(container.values()));
@@ -455,4 +463,8 @@ public class CacheMemorySource<K extends Serializable, V extends Object> extends
return CompletableFuture.completedFuture(new ArrayList<>(container.keySet())); return CompletableFuture.completedFuture(new ArrayList<>(container.keySet()));
} }
@Override
public CompletableFuture<Integer> getKeySizeAsync() {
return CompletableFuture.completedFuture(container.size());
}
} }

View File

@@ -45,7 +45,7 @@ public interface CacheSource<K extends Serializable, V extends Object> {
public Collection<V> getCollection(final K key); public Collection<V> getCollection(final K key);
public long getCollectionSize(final K key); public int getCollectionSize(final K key);
public Collection<V> getCollectionAndRefresh(final K key, final int expireSeconds); public Collection<V> getCollectionAndRefresh(final K key, final int expireSeconds);
@@ -59,6 +59,8 @@ public interface CacheSource<K extends Serializable, V extends Object> {
public List<K> queryKeys(); public List<K> queryKeys();
public int getKeySize();
public List<CacheEntry<K, Object>> queryList(); public List<CacheEntry<K, Object>> queryList();
//---------------------- CompletableFuture 异步版 --------------------------------- //---------------------- CompletableFuture 异步版 ---------------------------------
@@ -80,7 +82,7 @@ public interface CacheSource<K extends Serializable, V extends Object> {
public CompletableFuture<Collection<V>> getCollectionAsync(final K key); public CompletableFuture<Collection<V>> getCollectionAsync(final K key);
public CompletableFuture<Long> getCollectionSizeAsync(final K key); public CompletableFuture<Integer> getCollectionSizeAsync(final K key);
public CompletableFuture<Collection<V>> getCollectionAndRefreshAsync(final K key, final int expireSeconds); public CompletableFuture<Collection<V>> getCollectionAndRefreshAsync(final K key, final int expireSeconds);
@@ -94,6 +96,8 @@ public interface CacheSource<K extends Serializable, V extends Object> {
public CompletableFuture<List<K>> queryKeysAsync(); public CompletableFuture<List<K>> queryKeysAsync();
public CompletableFuture<Integer> getKeySizeAsync();
public CompletableFuture<List<CacheEntry<K, Object>>> queryListAsync(); public CompletableFuture<List<CacheEntry<K, Object>>> queryListAsync();
default CompletableFuture<Boolean> isOpenAsync() { default CompletableFuture<Boolean> isOpenAsync() {

View File

@@ -31,26 +31,26 @@ import org.redkale.util.*;
@ResourceType(DataSource.class) @ResourceType(DataSource.class)
public class DataJdbcSource extends AbstractService implements DataSource, Service, DataCacheListener, Function<Class, EntityInfo>, AutoCloseable, Resourcable { public class DataJdbcSource extends AbstractService implements DataSource, Service, 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 final AtomicBoolean debug = new AtomicBoolean(logger.isLoggable(Level.FINEST));
final String name; protected final String name;
final URL conf; protected final URL conf;
final boolean cacheForbidden; protected final boolean cacheForbidden;
private final PoolJdbcSource readPool; protected final PoolJdbcSource readPool;
private final PoolJdbcSource writePool; protected final PoolJdbcSource writePool;
@Resource(name = "$") @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) { public DataJdbcSource(String unitName, Properties readprop, Properties writeprop) {
this.name = unitName; this.name = unitName;
@@ -71,15 +71,15 @@ public class DataJdbcSource extends AbstractService implements DataSource, Servi
writePool.close(); writePool.close();
} }
private Connection createReadSQLConnection() { protected Connection createReadSQLConnection() {
return readPool.poll(); return readPool.poll();
} }
private <T> Connection createWriteSQLConnection() { protected <T> Connection createWriteSQLConnection() {
return writePool.poll(); return writePool.poll();
} }
private void closeSQLConnection(final Connection sqlconn) { protected void closeSQLConnection(final Connection sqlconn) {
if (sqlconn == null) return; if (sqlconn == null) return;
try { try {
sqlconn.close(); sqlconn.close();
@@ -93,7 +93,7 @@ public class DataJdbcSource extends AbstractService implements DataSource, Servi
return loadEntityInfo(t); 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); return EntityInfo.load(clazz, this.cacheForbidden, this.readPool.props, this, fullloader);
} }
@@ -150,7 +150,7 @@ public class DataJdbcSource extends AbstractService implements DataSource, Servi
return CompletableFuture.runAsync(() -> insert(values), getExecutor()); 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; if (values.length == 0) return;
try { try {
if (!info.isVirtualEntity()) { if (!info.isVirtualEntity()) {
@@ -251,7 +251,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 { final EntityInfo<T> info, T... values) throws SQLException {
Attribute<T, Serializable>[] attrs = info.insertAttributes; Attribute<T, Serializable>[] attrs = info.insertAttributes;
final PreparedStatement prestmt = info.autoGenerated ? conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS) : conn.prepareStatement(sql); final PreparedStatement prestmt = info.autoGenerated ? conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS) : conn.prepareStatement(sql);
@@ -328,7 +328,7 @@ public class DataJdbcSource extends AbstractService implements DataSource, Servi
return CompletableFuture.supplyAsync(() -> delete(values), getExecutor()); 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; if (values.length == 0) return -1;
final Attribute primary = info.getPrimary(); final Attribute primary = info.getPrimary();
Serializable[] ids = new Serializable[values.length]; Serializable[] ids = new Serializable[values.length];
@@ -358,7 +358,7 @@ public class DataJdbcSource extends AbstractService implements DataSource, Servi
return CompletableFuture.supplyAsync(() -> delete(clazz, ids), getExecutor()); 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; if (keys.length == 0) return -1;
int c = -1; int c = -1;
int c2 = 0; int c2 = 0;
@@ -430,7 +430,7 @@ public class DataJdbcSource extends AbstractService implements DataSource, Servi
return CompletableFuture.supplyAsync(() -> delete(clazz, flipper, node), getExecutor()); 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; int c = -1;
try { try {
if (!info.isVirtualEntity()) { if (!info.isVirtualEntity()) {
@@ -521,7 +521,7 @@ public class DataJdbcSource extends AbstractService implements DataSource, Servi
return CompletableFuture.supplyAsync(() -> update(values), getExecutor()); 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 { try {
Class clazz = info.getType(); Class clazz = info.getType();
int c = -1; int c = -1;
@@ -613,11 +613,11 @@ public class DataJdbcSource extends AbstractService implements DataSource, Servi
} }
@Override @Override
public <T> CompletableFuture<Integer> updateColumnupdateColumnAsync(final Class<T> clazz, final Serializable id, final String column, final Serializable value) { public <T> CompletableFuture<Integer> updateColumnAsync(final Class<T> clazz, final Serializable id, final String column, final Serializable value) {
return CompletableFuture.supplyAsync(() -> updateColumn(clazz, id, column, value), getExecutor()); 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 { try {
int c = -1; int c = -1;
if (!info.isVirtualEntity()) { if (!info.isVirtualEntity()) {
@@ -684,7 +684,7 @@ public class DataJdbcSource extends AbstractService implements DataSource, Servi
return CompletableFuture.supplyAsync(() -> updateColumn(clazz, column, value, node), getExecutor()); 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 { try {
int c = -1; int c = -1;
if (!info.isVirtualEntity()) { if (!info.isVirtualEntity()) {
@@ -766,7 +766,7 @@ public class DataJdbcSource extends AbstractService implements DataSource, Servi
return CompletableFuture.supplyAsync(() -> updateColumn(clazz, id, values), getExecutor()); 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; if (values == null || values.length < 1) return -1;
try { try {
StringBuilder setsql = new StringBuilder(); StringBuilder setsql = new StringBuilder();
@@ -776,7 +776,7 @@ public class DataJdbcSource extends AbstractService implements DataSource, Servi
List<byte[]> blobs = null; List<byte[]> blobs = null;
for (ColumnValue col : values) { for (ColumnValue col : values) {
Attribute<T, Serializable> attr = info.getUpdateAttribute(col.getColumn()); Attribute<T, Serializable> attr = info.getUpdateAttribute(col.getColumn());
if (attr == null) continue; if (attr == null) throw new RuntimeException(info.getType() + " cannot found column " + col.getColumn());
attrs.add(attr); attrs.add(attr);
cols.add(col); cols.add(col);
if (!virtual) { if (!virtual) {
@@ -882,7 +882,7 @@ public class DataJdbcSource extends AbstractService implements DataSource, Servi
return CompletableFuture.supplyAsync(() -> updateColumn(clazz, node, flipper, values), getExecutor()); 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; if (values == null || values.length < 1) return -1;
try { try {
StringBuilder setsql = new StringBuilder(); StringBuilder setsql = new StringBuilder();
@@ -992,7 +992,7 @@ public class DataJdbcSource extends AbstractService implements DataSource, Servi
return CompletableFuture.supplyAsync(() -> updateColumn(bean, selects), getExecutor()); 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; if (bean == null || selects == null) return -1;
try { try {
final Class<T> clazz = (Class<T>) bean.getClass(); final Class<T> clazz = (Class<T>) bean.getClass();
@@ -1068,7 +1068,7 @@ public class DataJdbcSource extends AbstractService implements DataSource, Servi
return CompletableFuture.supplyAsync(() -> updateColumn(bean, node, selects), getExecutor()); 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; if (bean == null || node == null || selects == null) return -1;
try { try {
final Class<T> clazz = (Class<T>) bean.getClass(); final Class<T> clazz = (Class<T>) bean.getClass();
@@ -1266,7 +1266,7 @@ public class DataJdbcSource extends AbstractService implements DataSource, Servi
for (FilterFuncColumn ffc : columns) { for (FilterFuncColumn ffc : columns) {
for (String col : ffc.cols()) { for (String col : ffc.cols()) {
if (sb.length() > 0) sb.append(", "); if (sb.length() > 0) sb.append(", ");
sb.append(ffc.func.getColumn((col == null || col.isEmpty() ? "*" : ("a." + col)))); sb.append(ffc.func.getColumn((col == null || col.isEmpty() ? "*" : info.getSQLColumn("a", col))));
} }
} }
final String sql = "SELECT " + sb + " FROM " + info.getTable(node) + " a" final String sql = "SELECT " + sb + " FROM " + info.getTable(node) + " a"
@@ -1325,7 +1325,7 @@ public class DataJdbcSource extends AbstractService implements DataSource, Servi
final Set<String> haset = new HashSet<>(); final Set<String> haset = new HashSet<>();
final CharSequence join = node == null ? null : node.createSQLJoin(this, false, joinTabalis, haset, info); final CharSequence join = node == null ? null : node.createSQLJoin(this, false, joinTabalis, haset, info);
final CharSequence where = node == null ? null : node.createSQLExpress(info, joinTabalis); final CharSequence where = node == null ? null : node.createSQLExpress(info, joinTabalis);
final String sql = "SELECT " + func.getColumn((column == null || column.isEmpty() ? "*" : ("a." + column))) + " FROM " + info.getTable(node) + " a" 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)); + (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 (debug.get() && info.isLoggable(Level.FINEST)) logger.finest(entityClass.getSimpleName() + " single sql=" + sql);
conn.setReadOnly(true); conn.setReadOnly(true);
@@ -1384,7 +1384,7 @@ public class DataJdbcSource extends AbstractService implements DataSource, Servi
final Set<String> haset = new HashSet<>(); final Set<String> haset = new HashSet<>();
final CharSequence join = node == null ? null : node.createSQLJoin(this, false, joinTabalis, haset, info); final CharSequence join = node == null ? null : node.createSQLJoin(this, false, joinTabalis, haset, info);
final CharSequence where = node == null ? null : node.createSQLExpress(info, joinTabalis); final CharSequence where = node == null ? null : node.createSQLExpress(info, joinTabalis);
final String sql = "SELECT a." + sqlkey + ", " + func.getColumn((funcColumn == null || funcColumn.isEmpty() ? "*" : ("a." + funcColumn))) 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; + " 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 (debug.get() && info.isLoggable(Level.FINEST)) logger.finest(entityClass.getSimpleName() + " single sql=" + sql);
conn.setReadOnly(true); conn.setReadOnly(true);
@@ -1877,7 +1877,7 @@ public class DataJdbcSource extends AbstractService implements DataSource, Servi
return CompletableFuture.supplyAsync(() -> queryColumnSheet(selectedColumn, clazz, flipper, node), getExecutor()); 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); Sheet<T> sheet = querySheet(true, needtotal, clazz, SelectColumn.createIncludes(selectedColumn), flipper, node);
final Sheet<V> rs = new Sheet<>(); final Sheet<V> rs = new Sheet<>();
if (sheet.isEmpty()) return rs; if (sheet.isEmpty()) return rs;
@@ -2083,7 +2083,7 @@ public class DataJdbcSource extends AbstractService implements DataSource, Servi
return CompletableFuture.supplyAsync(() -> querySheet(clazz, selects, flipper, node), getExecutor()); 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 EntityInfo<T> info = loadEntityInfo(clazz);
final EntityCache<T> cache = info.getCache(); final EntityCache<T> cache = info.getCache();
if (readcache && cache != null && cache.isFullLoaded()) { if (readcache && cache != null && cache.isFullLoaded()) {
@@ -2134,7 +2134,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; if (str == null) return sb;
int pos1 = str.indexOf(ch1, from); int pos1 = str.indexOf(ch1, from);
if (pos1 < 0) return sb; if (pos1 < 0) return sb;
@@ -2145,7 +2145,7 @@ public class DataJdbcSource extends AbstractService implements DataSource, Servi
return multisplit(ch1, ch2, split, sb, str, pos2 + 1); 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]; if (sqls.length == 0) return new int[0];
try { try {
conn.setReadOnly(false); conn.setReadOnly(false);

View File

@@ -198,7 +198,7 @@ public interface DataSource {
* *
* @return 影响的记录条数CompletableFuture * @return 影响的记录条数CompletableFuture
*/ */
public <T> CompletableFuture<Integer> updateColumnupdateColumnAsync(final Class<T> clazz, final Serializable id, final String column, final Serializable value); public <T> CompletableFuture<Integer> updateColumnAsync(final Class<T> clazz, final Serializable id, final String column, final Serializable value);
/** /**
* 更新符合过滤条件记录的单个字段 <br> * 更新符合过滤条件记录的单个字段 <br>

View File

@@ -85,7 +85,7 @@ public final class DataSources {
String impl = readprop.getProperty(JDBC_DATASOURCE_CLASS, DataJdbcSource.class.getName()); String impl = readprop.getProperty(JDBC_DATASOURCE_CLASS, DataJdbcSource.class.getName());
if (DataJdbcSource.class.getName().equals(impl)) return new DataJdbcSource(unitName, readprop, writeprop); if (DataJdbcSource.class.getName().equals(impl)) return new DataJdbcSource(unitName, readprop, writeprop);
try { try {
Class ds = Class.forName(impl); Class ds = Thread.currentThread().getContextClassLoader().loadClass(impl);
for (Constructor d : ds.getConstructors()) { for (Constructor d : ds.getConstructors()) {
Class<?>[] paramtypes = d.getParameterTypes(); Class<?>[] paramtypes = d.getParameterTypes();
if (paramtypes.length == 1 && paramtypes[0] == Properties.class) { if (paramtypes.length == 1 && paramtypes[0] == Properties.class) {

View File

@@ -5,14 +5,13 @@
*/ */
package org.redkale.source; package org.redkale.source;
import com.sun.istack.internal.logging.Logger;
import java.io.Serializable; import java.io.Serializable;
import java.lang.reflect.*; import java.lang.reflect.*;
import java.sql.*; import java.sql.*;
import java.util.*; import java.util.*;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.function.*; import java.util.function.*;
import java.util.logging.Level; import java.util.logging.*;
import javax.persistence.*; import javax.persistence.*;
import org.redkale.util.*; 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 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类名 //Entity类名
private final Class<T> type; private final Class<T> type;
@@ -194,19 +193,20 @@ public final class EntityInfo<T> {
try { try {
loader = type.getAnnotation(VirtualEntity.class).loader().newInstance(); loader = type.getAnnotation(VirtualEntity.class).loader().newInstance();
} catch (Exception e) { } catch (Exception e) {
logger.severe(type + " init @VirtualEntity.loader error", e); logger.log(Level.SEVERE, type + " init @VirtualEntity.loader error", e);
} }
this.fullloader = loader; this.fullloader = loader;
} else { } else {
this.fullloader = fullloader; this.fullloader = fullloader;
this.table = (t == null) ? type.getSimpleName().toLowerCase() : (t.catalog().isEmpty()) ? t.name() : (t.catalog() + '.' + (t.name().isEmpty() ? type.getSimpleName().toLowerCase() : t.name())); if (t != null && !t.name().isEmpty() && t.name().indexOf('.') >= 0) throw new RuntimeException(type + " have illegal table.name on @Table");
this.table = (t == null) ? type.getSimpleName().toLowerCase() : (t.catalog().isEmpty()) ? (t.name().isEmpty() ? type.getSimpleName().toLowerCase() : t.name()) : (t.catalog() + '.' + (t.name().isEmpty() ? type.getSimpleName().toLowerCase() : t.name()));
} }
DistributeTable dt = type.getAnnotation(DistributeTable.class); DistributeTable dt = type.getAnnotation(DistributeTable.class);
DistributeTableStrategy dts = null; DistributeTableStrategy dts = null;
try { try {
dts = (dt == null) ? null : dt.strategy().newInstance(); dts = (dt == null) ? null : dt.strategy().newInstance();
} catch (Exception e) { } catch (Exception e) {
logger.severe(type + " init DistributeTableStrategy error", e); logger.log(Level.SEVERE, type + " init DistributeTableStrategy error", e);
} }
this.tableStrategy = dts; this.tableStrategy = dts;
@@ -215,7 +215,7 @@ public final class EntityInfo<T> {
try { try {
cp = this.creator.getClass().getMethod("create", Object[].class).getAnnotation(Creator.ConstructorParameters.class); cp = this.creator.getClass().getMethod("create", Object[].class).getAnnotation(Creator.ConstructorParameters.class);
} catch (Exception e) { } 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(); this.constructorParameters = (cp == null || cp.value().length < 1) ? null : cp.value();
Attribute idAttr0 = null; Attribute idAttr0 = null;

View File

@@ -44,4 +44,11 @@ public @interface FilterJoinColumn {
* @return 关联字段 * @return 关联字段
*/ */
String[] columns(); String[] columns();
/**
* 备注描述
*
* @return 备注描述
*/
String comment() default "";
} }

View File

@@ -85,6 +85,13 @@ public class PoolJdbcSource {
if (this.isOracle()) { if (this.isOracle()) {
this.props.setProperty(JDBC_CONTAIN_SQLTEMPLATE, "INSTR(${keystr}, ${column}) > 0"); this.props.setProperty(JDBC_CONTAIN_SQLTEMPLATE, "INSTR(${keystr}, ${column}) > 0");
this.props.setProperty(JDBC_NOTCONTAIN_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()) { } else if (this.isSqlserver()) {
this.props.setProperty(JDBC_CONTAIN_SQLTEMPLATE, "CHARINDEX(${column}, ${keystr}) > 0"); this.props.setProperty(JDBC_CONTAIN_SQLTEMPLATE, "CHARINDEX(${column}, ${keystr}) > 0");
this.props.setProperty(JDBC_NOTCONTAIN_SQLTEMPLATE, "CHARINDEX(${column}, ${keystr}) = 0"); this.props.setProperty(JDBC_NOTCONTAIN_SQLTEMPLATE, "CHARINDEX(${column}, ${keystr}) = 0");
@@ -129,7 +136,7 @@ public class PoolJdbcSource {
case "com.mysql.cj.jdbc.Driver": case "com.mysql.cj.jdbc.Driver":
case "com.mysql.jdbc.Driver": case "com.mysql.jdbc.Driver":
try { try {
Class.forName("com.mysql.cj.jdbc.MysqlConnectionPoolDataSource"); Thread.currentThread().getContextClassLoader().loadClass("com.mysql.cj.jdbc.MysqlConnectionPoolDataSource");
source = "com.mysql.cj.jdbc.MysqlConnectionPoolDataSource"; source = "com.mysql.cj.jdbc.MysqlConnectionPoolDataSource";
} catch (Throwable e) { } catch (Throwable e) {
source = "com.mysql.jdbc.jdbc2.optional.MysqlConnectionPoolDataSource"; source = "com.mysql.jdbc.jdbc2.optional.MysqlConnectionPoolDataSource";
@@ -146,10 +153,10 @@ public class PoolJdbcSource {
break; break;
} }
} }
final Class clazz = Class.forName(source); final Class clazz = Thread.currentThread().getContextClassLoader().loadClass(source);
Object pdsource = clazz.newInstance(); Object pdsource = clazz.newInstance();
if (source.contains(".postgresql.")) { if (source.contains(".postgresql.")) {
Class driver = Class.forName("org.postgresql.Driver"); Class driver = Thread.currentThread().getContextClassLoader().loadClass("org.postgresql.Driver");
Properties properties = (Properties) driver.getMethod("parseURL", String.class, Properties.class).invoke(null, url, new Properties()); Properties properties = (Properties) driver.getMethod("parseURL", String.class, Properties.class).invoke(null, url, new Properties());
clazz.getMethod("setServerName", String.class).invoke(pdsource, properties.getProperty("PGHOST")); clazz.getMethod("setServerName", String.class).invoke(pdsource, properties.getProperty("PGHOST"));
clazz.getMethod("setDatabaseName", String.class).invoke(pdsource, properties.getProperty("PGDBNAME")); clazz.getMethod("setDatabaseName", String.class).invoke(pdsource, properties.getProperty("PGDBNAME"));

View File

@@ -302,6 +302,18 @@ public abstract class AnyValue {
return this; return this;
} }
public DefaultAnyValue removeValue(String name, AnyValue value) {
if (name == null || value == null || this.anyEntrys == null) return this;
this.anyEntrys = Utility.remove(this.anyEntrys, (t) -> name.equals(((Entry) t).name) && ((Entry) t).getValue().equals(value));
return this;
}
public DefaultAnyValue removeValue(String name, String value) {
if (name == null || value == null || this.stringEntrys == null) return this;
this.stringEntrys = Utility.remove(this.stringEntrys, (t) -> name.equals(((Entry) t).name) && ((Entry) t).getValue().equals(value));
return this;
}
@Override @Override
public AnyValue getAnyValue(String name) { public AnyValue getAnyValue(String name) {
for (Entry<DefaultAnyValue> en : this.anyEntrys) { for (Entry<DefaultAnyValue> en : this.anyEntrys) {
@@ -533,4 +545,52 @@ public abstract class AnyValue {
return value == null ? defaultValue : value; return value == null ? defaultValue : value;
} }
@Override
public boolean equals(Object other) {
if (!(other instanceof AnyValue)) return false;
AnyValue conf = (AnyValue) other;
if (!equals(this.getStringEntrys(), conf.getStringEntrys())) return false;
return equals(this.getAnyEntrys(), conf.getAnyEntrys());
}
private static <T> boolean equals(Entry<? extends T>[] entry1, Entry<T>[] entry2) {
if ((entry1 == null || entry1.length == 0) && (entry2 == null || entry2.length == 0)) return true;
if (entry1.length != entry2.length) return false;
for (int i = 0; i < entry1.length; i++) {
if (!entry1[i].name.equals(entry2[i].name)) return false;
if (!entry1[i].getValue().equals(entry2[i].getValue())) return false;
}
return true;
}
@Override
public int hashCode() {
int hash = 7;
hash = 19 * hash + Arrays.deepHashCode(this.getStringEntrys());
hash = 19 * hash + Arrays.deepHashCode(this.getAnyEntrys());
return hash;
}
public String toXML(String rootName) {
return toXMLString(new StringBuilder("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n\r\n"), rootName, this, 0).toString();
}
protected static StringBuilder toXMLString(StringBuilder sb, String nodeName, AnyValue conf, int indent) { //indent: 缩进长度
if (indent < 0) indent = 0;
char[] chars = new char[indent];
Arrays.fill(chars, ' ');
final String space = new String(chars);
Entry<AnyValue>[] anys = conf.getAnyEntrys();
sb.append(space).append('<').append(nodeName);
for (Entry<String> en : conf.getStringEntrys()) {
sb.append(' ').append(en.name).append("=\"").append(en.value).append("\"");
}
if (anys == null || anys.length == 0) return sb.append("/>\r\n\r\n");
sb.append(">\r\n\r\n");
for (Entry<AnyValue> en : conf.getAnyEntrys()) {
toXMLString(sb, en.name, en.getValue(), indent + 4);
}
return sb.append(space).append("</").append(nodeName).append(">\r\n\r\n");
}
} }

View File

@@ -431,7 +431,7 @@ public interface Attribute<T, F> {
final String interDesc = Type.getDescriptor(clazz); final String interDesc = Type.getDescriptor(clazz);
final String columnDesc = Type.getDescriptor(column); final String columnDesc = Type.getDescriptor(column);
ClassLoader loader = Attribute.class.getClassLoader(); ClassLoader loader = Thread.currentThread().getContextClassLoader();
String newDynName = supDynName + "_Dyn_" + clazz.getSimpleName() + "_" String newDynName = supDynName + "_Dyn_" + clazz.getSimpleName() + "_"
+ fieldname.substring(fieldname.indexOf('.') + 1) + "_" + pcolumn.getSimpleName().replace("[]", "Array"); + fieldname.substring(fieldname.indexOf('.') + 1) + "_" + pcolumn.getSimpleName().replace("[]", "Array");
if (String.class.getClassLoader() != clazz.getClassLoader()) { if (String.class.getClassLoader() != clazz.getClassLoader()) {
@@ -440,7 +440,7 @@ public interface Attribute<T, F> {
+ fieldname.substring(fieldname.indexOf('.') + 1) + "_" + pcolumn.getSimpleName().replace("[]", "Array"); + fieldname.substring(fieldname.indexOf('.') + 1) + "_" + pcolumn.getSimpleName().replace("[]", "Array");
} }
try { try {
return (Attribute) Class.forName(newDynName.replace('/', '.')).newInstance(); return (Attribute) loader.loadClass(newDynName.replace('/', '.')).newInstance();
} catch (Throwable ex) { } catch (Throwable ex) {
} }
//--------------------------------------------------- //---------------------------------------------------

View File

@@ -214,14 +214,14 @@ public interface Creator<T> {
final String supDynName = Creator.class.getName().replace('.', '/'); final String supDynName = Creator.class.getName().replace('.', '/');
final String interName = clazz.getName().replace('.', '/'); final String interName = clazz.getName().replace('.', '/');
final String interDesc = Type.getDescriptor(clazz); final String interDesc = Type.getDescriptor(clazz);
ClassLoader loader = Creator.class.getClassLoader(); ClassLoader loader = Thread.currentThread().getContextClassLoader();
String newDynName = supDynName + "_" + clazz.getSimpleName() + "_" + (System.currentTimeMillis() % 10000); String newDynName = supDynName + "_" + clazz.getSimpleName() + "_" + (System.currentTimeMillis() % 10000);
if (String.class.getClassLoader() != clazz.getClassLoader()) { if (String.class.getClassLoader() != clazz.getClassLoader()) {
loader = clazz.getClassLoader(); loader = clazz.getClassLoader();
newDynName = interName + "_Dyn" + Creator.class.getSimpleName(); newDynName = interName + "_Dyn" + Creator.class.getSimpleName();
} }
try { try {
return (Creator) Class.forName(newDynName.replace('/', '.')).newInstance(); return (Creator) loader.loadClass(newDynName.replace('/', '.')).newInstance();
} catch (Throwable ex) { } catch (Throwable ex) {
} }

View File

@@ -17,7 +17,7 @@ public final class Redkale {
} }
public static String getDotedVersion() { public static String getDotedVersion() {
return "1.8.0-rc1"; return "1.8.1";
} }
public static int getMajorVersion() { public static int getMajorVersion() {

View File

@@ -0,0 +1,49 @@
/*
* 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.util;
import java.net.*;
import java.util.HashSet;
/**
*
* @author zhangjx
*/
public class RedkaleClassLoader extends URLClassLoader {
public RedkaleClassLoader(ClassLoader parent) {
super(new URL[0], parent);
}
public Class<?> loadClass(String name, byte[] b) {
return defineClass(name, b, 0, b.length);
}
@Override
public void addURL(URL url) {
super.addURL(url);
}
@Override
public URL[] getURLs() {
return super.getURLs();
}
public URL[] getAllURLs() {
ClassLoader loader = this;
HashSet<URL> set = new HashSet<>();
do {
String loaderName = loader.getClass().getName();
if (loaderName.startsWith("sun.") && loaderName.contains("ExtClassLoader")) continue;
if (loader instanceof URLClassLoader) {
for (URL url : ((URLClassLoader) loader).getURLs()) {
set.add(url);
}
}
} while ((loader = loader.getParent()) != null);
return set.toArray(new URL[set.size()]);
}
}

View File

@@ -35,13 +35,13 @@ public interface Reproduce<D, S> extends BiFunction<D, S, D> {
final String destDesc = Type.getDescriptor(destClass); final String destDesc = Type.getDescriptor(destClass);
final String srcDesc = Type.getDescriptor(srcClass); final String srcDesc = Type.getDescriptor(srcClass);
String newDynName = supDynName + "Dyn_" + destClass.getSimpleName() + "_" + srcClass.getSimpleName(); String newDynName = supDynName + "Dyn_" + destClass.getSimpleName() + "_" + srcClass.getSimpleName();
ClassLoader loader = Reproduce.class.getClassLoader(); ClassLoader loader = Thread.currentThread().getContextClassLoader();
if (String.class.getClassLoader() != destClass.getClassLoader()) { if (String.class.getClassLoader() != destClass.getClassLoader()) {
loader = destClass.getClassLoader(); loader = destClass.getClassLoader();
newDynName = destName + "_Dyn" + Reproduce.class.getSimpleName() + "_" + srcClass.getSimpleName(); newDynName = destName + "_Dyn" + Reproduce.class.getSimpleName() + "_" + srcClass.getSimpleName();
} }
try { try {
return (Reproduce) Class.forName(newDynName.replace('/', '.')).newInstance(); return (Reproduce) loader.loadClass(newDynName.replace('/', '.')).newInstance();
} catch (Throwable ex) { } catch (Throwable ex) {
} }
// ------------------------------------------------------------------------------ // ------------------------------------------------------------------------------

View File

@@ -177,11 +177,11 @@ public abstract class TypeToken<T> {
} }
private static Type createParameterizedType(final Class rawType, final Type... actualTypeArguments) { private static Type createParameterizedType(final Class rawType, final Type... actualTypeArguments) {
ClassLoader loader = TypeToken.class.getClassLoader(); ClassLoader loader = Thread.currentThread().getContextClassLoader();
String newDynName = TypeToken.class.getName().replace('.', '/') + "_Dyn" + System.currentTimeMillis(); String newDynName = TypeToken.class.getName().replace('.', '/') + "_Dyn" + System.currentTimeMillis();
for (;;) { for (;;) {
try { try {
Class.forName(newDynName.replace('/', '.')); loader.loadClass(newDynName.replace('/', '.'));
newDynName = TypeToken.class.getName().replace('.', '/') + "_Dyn" + Math.abs(System.nanoTime()); newDynName = TypeToken.class.getName().replace('.', '/') + "_Dyn" + Math.abs(System.nanoTime());
} catch (Throwable ex) { //异常说明类不存在 } catch (Throwable ex) { //异常说明类不存在
break; break;

View File

@@ -277,6 +277,19 @@ public final class Utility {
return news; return news;
} }
/**
* 将元素从数组中删除
*
* @param <T> 泛型
* @param array 原数组
* @param item 元素
*
* @return 新数组
*/
public static <T> T[] remove(final T[] array, final T item) {
return remove(array, (i) -> item.equals(item));
}
/** /**
* 将符合条件的元素从数组中删除 * 将符合条件的元素从数组中删除
* *

View File

@@ -10,8 +10,7 @@ import java.net.*;
import java.util.*; import java.util.*;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import java.util.stream.Stream; import java.util.stream.Stream;
import org.redkale.convert.json.JsonConvert; import org.redkale.convert.Convert;
import org.redkale.net.*;
import org.redkale.net.http.*; import org.redkale.net.http.*;
/** /**
@@ -27,10 +26,10 @@ public interface WebSocketDesc<G, T> {
public CompletableFuture<Integer> send(Object message, boolean last); public CompletableFuture<Integer> send(Object message, boolean last);
//给自身发送消息, 消息类型是JavaBean对象 返回结果0表示成功非0表示错误码 //给自身发送消息, 消息类型是JavaBean对象 返回结果0表示成功非0表示错误码
public CompletableFuture<Integer> send(JsonConvert convert, Object message); public CompletableFuture<Integer> send(Convert convert, Object message);
//给自身发送消息, 消息类型是JavaBean对象 返回结果0表示成功非0表示错误码 //给自身发送消息, 消息类型是JavaBean对象 返回结果0表示成功非0表示错误码
public CompletableFuture<Integer> send(JsonConvert convert, Object message, boolean last); public CompletableFuture<Integer> send(Convert convert, Object message, boolean last);
//给指定userid的WebSocket节点发送 二进制消息/文本消息/JavaBean对象消息 返回结果0表示成功非0表示错误码 //给指定userid的WebSocket节点发送 二进制消息/文本消息/JavaBean对象消息 返回结果0表示成功非0表示错误码
public CompletableFuture<Integer> sendMessage(Object message, G... userids); public CompletableFuture<Integer> sendMessage(Object message, G... userids);
@@ -79,9 +78,6 @@ public interface WebSocketDesc<G, T> {
//创建userid null表示异常 必须实现该方法 //创建userid null表示异常 必须实现该方法
/* protected abstract */ G createUserid(); /* protected abstract */ G createUserid();
//标记为@WebSocketBinary才需要重写此方法
public void onRead(AsyncConnection channel);
//WebSokcet连接成功后的回调方法 //WebSokcet连接成功后的回调方法
public void onConnected(); public void onConnected();
@@ -94,6 +90,9 @@ public interface WebSocketDesc<G, T> {
//接收到消息的回调方法 //接收到消息的回调方法
public void onMessage(T message, boolean last); public void onMessage(T message, boolean last);
//接收到文本消息的回调方法
public void onMessage(String message, boolean last);
//接收到二进制消息的回调方法 //接收到二进制消息的回调方法
public void onMessage(byte[] bytes, boolean last); public void onMessage(byte[] bytes, boolean last);

View File

@@ -23,8 +23,8 @@ public class _DynHelloRestServlet1 extends SimpleRestServlet {
HelloService service = new HelloService(); HelloService service = new HelloService();
HttpServer server = new HttpServer(); HttpServer server = new HttpServer();
System.out.println(server.addRestServlet(service, null, SimpleRestServlet.class, "/pipes")); System.out.println(server.addRestServlet(null, service, null, SimpleRestServlet.class, "/pipes"));
System.out.println(server.addRestServlet(new HelloService(3), null, SimpleRestServlet.class, "/pipes")); System.out.println(server.addRestServlet(null, new HelloService(3), null, SimpleRestServlet.class, "/pipes"));
DefaultAnyValue conf = DefaultAnyValue.create("port", "" + port); DefaultAnyValue conf = DefaultAnyValue.create("port", "" + port);
server.init(conf); server.init(conf);

View File

@@ -73,7 +73,7 @@ public class ABMainService implements Service {
HttpServer server = new HttpServer(); HttpServer server = new HttpServer();
server.getLogger().setLevel(Level.WARNING); server.getLogger().setLevel(Level.WARNING);
server.addRestServlet(service, null, HttpServlet.class, "/pipes"); server.addRestServlet(null, service, null, HttpServlet.class, "/pipes");
resFactory.inject(cservice); resFactory.inject(cservice);
resFactory.inject(bcservice); resFactory.inject(bcservice);
@@ -114,7 +114,7 @@ public class ABMainService implements Service {
HttpServer server = new HttpServer(); HttpServer server = new HttpServer();
server.getLogger().setLevel(Level.WARNING); server.getLogger().setLevel(Level.WARNING);
server.addRestServlet(service, null, HttpServlet.class, "/pipes"); server.addRestServlet(null, service, null, HttpServlet.class, "/pipes");
server.init(DefaultAnyValue.create("port", "" + abport)); server.init(DefaultAnyValue.create("port", "" + abport));
server.start(); server.start();

View File

@@ -30,15 +30,15 @@ public class ChatWebSocket extends WebSocket<Integer, Object> {
@RestOnMessage(name = "sendmessage") @RestOnMessage(name = "sendmessage")
public void onChatMessage(ChatMessage message, Map<String, String> extmap) { public void onChatMessage(ChatMessage message, Map<String, String> extmap) {
message.fromuserid = userid(); message.fromuserid = getUserid();
message.fromusername = "用户" + userid(); message.fromusername = "用户" + getUserid();
System.out.println("获取消息: message: " + message + ", map: " + extmap); System.out.println("获取消息: message: " + message + ", map: " + extmap);
super.broadcastMessage(message); super.broadcastMessage(message);
} }
@RestOnMessage(name = "joinroom") @RestOnMessage(name = "joinroom")
public void onJoinRoom(int roomid) { public void onJoinRoom(int roomid) {
service.joinRoom(userid(), roomid); service.joinRoom(getUserid(), roomid);
System.out.println("加入房间: roomid: " + roomid); System.out.println("加入房间: roomid: " + roomid);
} }