Compare commits
96 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
44507a97a6 | ||
|
|
f4a7f1cff6 | ||
|
|
a5fcb45a88 | ||
|
|
bc8b68526d | ||
|
|
180f201dc0 | ||
|
|
9ab315a405 | ||
|
|
27b4742b6d | ||
|
|
702220d18e | ||
|
|
414489da8e | ||
|
|
77057df25d | ||
|
|
2f98cd1ab5 | ||
|
|
8809fe8ec9 | ||
|
|
f9702a9517 | ||
|
|
29e46b9b68 | ||
|
|
f838e35413 | ||
|
|
f3bb77c49b | ||
|
|
12fa033e15 | ||
|
|
f4abfafea2 | ||
|
|
0918af71d2 | ||
|
|
275befa330 | ||
|
|
ab4cd8bcb6 | ||
|
|
36c109b32f | ||
|
|
73a915665d | ||
|
|
bd6d71c94a | ||
|
|
842e93507c | ||
|
|
76df1108d7 | ||
|
|
941d09cde2 | ||
|
|
9dd3e1da07 | ||
|
|
2bf73245ec | ||
|
|
b0ab792f72 | ||
|
|
0df9a940c5 | ||
|
|
fc35fc5abc | ||
|
|
eaa0a99933 | ||
|
|
83bdb97842 | ||
|
|
a71a4d0fed | ||
|
|
835435c220 | ||
|
|
c524ba1797 | ||
|
|
f254b48693 | ||
|
|
922697eb4d | ||
|
|
450e3e3ea2 | ||
|
|
b31f75f4f6 | ||
|
|
1d1f18b046 | ||
|
|
73f942746b | ||
|
|
b6cefe8c2d | ||
|
|
ebc0e4eb41 | ||
|
|
179a7b22ea | ||
|
|
0b87d9a261 | ||
|
|
685a686ead | ||
|
|
568e1cf62d | ||
|
|
1d121bd2ab | ||
|
|
2ca4bdaaec | ||
|
|
878fda30f6 | ||
|
|
abb611382c | ||
|
|
b53510a26f | ||
|
|
2aee84d477 | ||
|
|
0ba2e25f2e | ||
|
|
98e8a7eb05 | ||
|
|
62139efca9 | ||
|
|
2b62cbe455 | ||
|
|
e44602fe3b | ||
|
|
276cb4da92 | ||
|
|
7081f94afc | ||
|
|
2d6cefeb43 | ||
|
|
ea6c703ac6 | ||
|
|
d1f14962fd | ||
|
|
79ca63bf81 | ||
|
|
6421bc2851 | ||
|
|
43f9f50f4c | ||
|
|
5f140a8ce9 | ||
|
|
c0f8cdf902 | ||
|
|
8d66b1b4a7 | ||
|
|
8c7ee4136c | ||
|
|
d9498c9a6c | ||
|
|
08060a8c86 | ||
|
|
2bdf0e4a50 | ||
|
|
eb184df100 | ||
|
|
cf545a731c | ||
|
|
95e18dfd48 | ||
|
|
023a9abdef | ||
|
|
7f3776c224 | ||
|
|
7b15ba33e0 | ||
|
|
a6c105d63d | ||
|
|
10e22b0873 | ||
|
|
53a35e6397 | ||
|
|
b5a3c39f4f | ||
|
|
99381d4842 | ||
|
|
5f2c2a9f2c | ||
|
|
dc3f318949 | ||
|
|
cfae61faea | ||
|
|
dd7626b1a3 | ||
|
|
2171aa1232 | ||
|
|
801ad489d2 | ||
|
|
c88d0b402d | ||
|
|
542bb4353b | ||
|
|
fe1e0a845a | ||
|
|
f320f4c550 |
@@ -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>
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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" />
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
1
libs/readme.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<EFBFBD>Լ<EFBFBD><EFBFBD><EFBFBD>ҵ<EFBFBD><EFBFBD>jarĬ<EFBFBD>Ϸ<EFBFBD><EFBFBD>ڴ˴<EFBFBD>
|
||||||
@@ -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、WATCH,SNCP使用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增加配置属性 -->
|
||||||
@@ -154,6 +155,11 @@
|
|||||||
-->
|
-->
|
||||||
<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" >
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
@@ -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<FilterEntry<T>>
|
* @return Set<FilterEntry<T>>
|
||||||
*/
|
*/
|
||||||
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<FilterEntry<T>>
|
* @return Set<FilterEntry<T>>
|
||||||
*/
|
*/
|
||||||
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) {
|
||||||
|
|||||||
@@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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() {
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
17
src/org/redkale/boot/watch/AbstractWatchService.java
Normal file
17
src/org/redkale/boot/watch/AbstractWatchService.java
Normal 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 {
|
||||||
|
|
||||||
|
}
|
||||||
50
src/org/redkale/boot/watch/FilterWatchService.java
Normal file
50
src/org/redkale/boot/watch/FilterWatchService.java
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
17
src/org/redkale/boot/watch/ServerWatchService.java
Normal file
17
src/org/redkale/boot/watch/ServerWatchService.java
Normal 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 {
|
||||||
|
|
||||||
|
}
|
||||||
39
src/org/redkale/boot/watch/ServiceWatchService.java
Normal file
39
src/org/redkale/boot/watch/ServiceWatchService.java
Normal 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();
|
||||||
|
// }
|
||||||
|
}
|
||||||
39
src/org/redkale/boot/watch/ServletWatchService.java
Normal file
39
src/org/redkale/boot/watch/ServletWatchService.java
Normal 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();
|
||||||
|
// }
|
||||||
|
}
|
||||||
26
src/org/redkale/boot/watch/SourceWatchService.java
Normal file
26
src/org/redkale/boot/watch/SourceWatchService.java
Normal 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;
|
||||||
|
|
||||||
|
}
|
||||||
138
src/org/redkale/boot/watch/TransportWatchService.java
Normal file
138
src/org/redkale/boot/watch/TransportWatchService.java
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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();
|
||||||
}
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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();
|
|
||||||
}
|
|
||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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++) {
|
||||||
|
|||||||
@@ -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();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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()));
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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));
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
21
src/org/redkale/net/TransportStrategy.java
Normal file
21
src/org/redkale/net/TransportStrategy.java
Normal 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);
|
||||||
|
}
|
||||||
@@ -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();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -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 {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 配合 @WebParam 使用。
|
|
||||||
* 用于对@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>
|
|
||||||
* @Override
|
|
||||||
* public void preExecute(final HttpRequest request, final HttpResponse response, HttpServlet next) throws IOException {
|
|
||||||
* if (finer) response.setRecycleListener((req, resp) -> { //记录处理时间比较长的请求
|
|
||||||
* long e = System.currentTimeMillis() - ((HttpRequest) req).getCreatetime();
|
|
||||||
* if (e > 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>
|
|
||||||
* 用户登录或权限验证, 没有注解为@AuthIgnore 的方法会执行authenticate方法, 若验证成功则必须调用next.execute(request, response);进行下一步操作, 例如: <br>
|
|
||||||
* <blockquote><pre>
|
|
||||||
* @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,来自@WebServlet.moduleid()
|
|
||||||
* @param actionid 操作ID,来自@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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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);
|
||||||
|
|||||||
@@ -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)) {
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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);
|
||||||
@@ -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;
|
||||||
|
|||||||
41
src/org/redkale/net/http/RestConvert.java
Normal file
41
src/org/redkale/net/http/RestConvert.java
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -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;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 是否屏蔽该类的转换
|
* 是否屏蔽该类的转换
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 被标记为 @WebSocketBinary 的WebSocketServlet 将使用原始的TCP传输, 通常用于类似音频/视频传输场景
|
|
||||||
*
|
|
||||||
* <p> 详情见: https://redkale.org
|
|
||||||
* @author zhangjx
|
|
||||||
*/
|
|
||||||
@Inherited
|
|
||||||
@Documented
|
|
||||||
@Target({TYPE})
|
|
||||||
@Retention(RUNTIME)
|
|
||||||
public @interface WebSocketBinary {
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -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);
|
||||||
|
|||||||
@@ -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) { //本地模式且没有分布式
|
||||||
|
|||||||
@@ -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) : "") + "]";
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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));
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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); //必须要执行
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -89,10 +86,6 @@ public class SncpServer extends Server<DLong, SncpContext, SncpRequest, SncpResp
|
|||||||
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")
|
||||||
protected SncpContext createContext() {
|
protected SncpContext createContext() {
|
||||||
|
|||||||
@@ -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() {
|
||||||
|
|||||||
@@ -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, "未知错误");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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() {
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -44,4 +44,11 @@ public @interface FilterJoinColumn {
|
|||||||
* @return 关联字段
|
* @return 关联字段
|
||||||
*/
|
*/
|
||||||
String[] columns();
|
String[] columns();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 备注描述
|
||||||
|
*
|
||||||
|
* @return 备注描述
|
||||||
|
*/
|
||||||
|
String comment() default "";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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"));
|
||||||
|
|||||||
@@ -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");
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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) {
|
||||||
}
|
}
|
||||||
//---------------------------------------------------
|
//---------------------------------------------------
|
||||||
|
|||||||
@@ -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) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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() {
|
||||||
|
|||||||
49
src/org/redkale/util/RedkaleClassLoader.java
Normal file
49
src/org/redkale/util/RedkaleClassLoader.java
Normal 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()]);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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) {
|
||||||
}
|
}
|
||||||
// ------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 将符合条件的元素从数组中删除
|
* 将符合条件的元素从数组中删除
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user