Compare commits
235 Commits
1.9.5
...
2.0.0.beta
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2b2fd9965b | ||
|
|
0938635eb2 | ||
|
|
a4a186751e | ||
|
|
ea5169b5c5 | ||
|
|
01bd195847 | ||
|
|
a72c26a935 | ||
|
|
a9900d9bfa | ||
|
|
6896401d2d | ||
|
|
886f01c9f3 | ||
|
|
59c9251d70 | ||
|
|
fad5f010d2 | ||
|
|
737c4a92b9 | ||
|
|
d3e8675948 | ||
|
|
a4ffc7a27c | ||
|
|
91bf7b1387 | ||
|
|
1396296337 | ||
|
|
e4672355fc | ||
|
|
129ed25ca4 | ||
|
|
e6d7e5fe98 | ||
|
|
73ce5fa11f | ||
|
|
ce01c3d4ce | ||
|
|
3b3de316ea | ||
|
|
8c739ce54d | ||
|
|
0aa4d6c967 | ||
|
|
d7a3f4d87d | ||
|
|
fe9e074581 | ||
|
|
40813e8752 | ||
|
|
e90f2e4142 | ||
|
|
2a19ea709b | ||
|
|
30c9be303f | ||
|
|
e101b79472 | ||
|
|
8ad919f952 | ||
|
|
294543c46e | ||
|
|
79f8363d47 | ||
|
|
6d3553b0b5 | ||
|
|
403ab4e281 | ||
|
|
87e7c43032 | ||
|
|
a321b41699 | ||
|
|
fea34863e3 | ||
|
|
bcaf7ab73e | ||
|
|
b9d7eaee1b | ||
|
|
d605045858 | ||
|
|
cf2ab617c2 | ||
|
|
204514cb08 | ||
|
|
cfe01cca75 | ||
|
|
ac294c58ae | ||
|
|
6950eb2f30 | ||
|
|
847f81374b | ||
|
|
bb09aea8bb | ||
|
|
2e1ff333f5 | ||
|
|
c14a2b4011 | ||
|
|
d9c6c3d2d0 | ||
|
|
91d4477ed9 | ||
|
|
623c0a127e | ||
|
|
bc64666700 | ||
|
|
5c7dd7d782 | ||
|
|
e78a2da6c0 | ||
|
|
065be6f3d7 | ||
|
|
874f3330b8 | ||
|
|
684edfa10b | ||
|
|
e69a120965 | ||
|
|
4a36244294 | ||
|
|
cb444be0f7 | ||
|
|
8b5cbf186f | ||
|
|
dbc6f8a196 | ||
|
|
9b94604166 | ||
|
|
cc98a85711 | ||
|
|
6855d06f55 | ||
|
|
89d90ddf5b | ||
|
|
6280111121 | ||
|
|
231d41c15f | ||
|
|
cfca7adc66 | ||
|
|
aedd215de4 | ||
|
|
df8e839580 | ||
|
|
300441b9f7 | ||
|
|
1a5e9022ae | ||
|
|
2a3b8f87d3 | ||
|
|
b7930f1ed7 | ||
|
|
a78c2145e6 | ||
|
|
ee20a34a70 | ||
|
|
33bd80c572 | ||
|
|
031309b105 | ||
|
|
bcbd981a9b | ||
|
|
b7ce390c33 | ||
|
|
c9d099c694 | ||
|
|
fcca0329c6 | ||
|
|
c82c0bc680 | ||
|
|
2921478a0a | ||
|
|
61a5420d48 | ||
|
|
702ab6ef6e | ||
|
|
2576e71a7d | ||
|
|
0d0bd78213 | ||
|
|
8d9fa8f9cf | ||
|
|
25eaf6e353 | ||
|
|
8afcaa0b34 | ||
|
|
1824f8150c | ||
|
|
2f89778fd6 | ||
|
|
f9d250b43c | ||
|
|
3a3c09d8aa | ||
|
|
09a0d4f9e2 | ||
|
|
3bba781183 | ||
|
|
6426f8fe91 | ||
|
|
5906594148 | ||
|
|
1b188c863c | ||
|
|
087b4cb571 | ||
|
|
c9496b6231 | ||
|
|
60d95d7628 | ||
|
|
dd9e7e77b5 | ||
|
|
50c9363876 | ||
|
|
938e357745 | ||
|
|
08ee51f8ab | ||
|
|
40ae555645 | ||
|
|
33f89264a6 | ||
|
|
e4ea20cc5f | ||
|
|
045b7db1af | ||
|
|
676c1b5d21 | ||
|
|
e4319246b8 | ||
|
|
32d8515bf4 | ||
|
|
d7e7113201 | ||
|
|
9493aa43a7 | ||
|
|
d07c55b831 | ||
|
|
47dab88d72 | ||
|
|
9e553aeff6 | ||
|
|
ed8e754557 | ||
|
|
651dc3df2a | ||
|
|
84e5bc3437 | ||
|
|
64fd0176ac | ||
|
|
c1f3115d4e | ||
|
|
1c70834760 | ||
|
|
7c901731bc | ||
|
|
2b2d53e515 | ||
|
|
be051ecf45 | ||
|
|
18534eb654 | ||
|
|
a5a926fd94 | ||
|
|
0cb9f2cad3 | ||
|
|
98209cc82e | ||
|
|
1879afa6a4 | ||
|
|
4a6404dfec | ||
|
|
0fa1c4a08f | ||
|
|
573d7c5776 | ||
|
|
c56c9bf260 | ||
|
|
387865789f | ||
|
|
03fcf43a89 | ||
|
|
df8090813a | ||
|
|
849b29d00f | ||
|
|
4b7f65e1c4 | ||
|
|
4545d81e50 | ||
|
|
1ac5f060a4 | ||
|
|
7e1ff8e315 | ||
|
|
4e0c1fee97 | ||
|
|
0b38f23f2d | ||
|
|
98ea6861c1 | ||
|
|
c07b628ea1 | ||
|
|
b1b979c0b5 | ||
|
|
35b708b01d | ||
|
|
229ae0d44f | ||
|
|
7d6897fa36 | ||
|
|
5851093590 | ||
|
|
4646c1d1f0 | ||
|
|
f3763dbf72 | ||
|
|
6a8c60ec78 | ||
|
|
ae437fd5d6 | ||
|
|
7251c984c8 | ||
|
|
ec449220eb | ||
|
|
78265944f0 | ||
|
|
d2791f6d1b | ||
|
|
d525d2664b | ||
|
|
a4ccea91ad | ||
|
|
750da161eb | ||
|
|
ac50312f0b | ||
|
|
8d44d48072 | ||
|
|
6c2baa1708 | ||
|
|
4525cfe594 | ||
|
|
921f96c975 | ||
|
|
29ce57d3af | ||
|
|
2ca1e6305c | ||
|
|
827b404a57 | ||
|
|
83569142c1 | ||
|
|
2da0faacc3 | ||
|
|
cf51bee2cc | ||
|
|
0dd55dc947 | ||
|
|
620fa0430c | ||
|
|
d053590257 | ||
|
|
684af3de61 | ||
|
|
6c6e26ed0b | ||
|
|
4a05bfbd08 | ||
|
|
787dc7b32f | ||
|
|
85a1f99f6e | ||
|
|
4fe8a1199e | ||
|
|
7312dbc4c5 | ||
|
|
cfecfabc92 | ||
|
|
587160c5fe | ||
|
|
ee7fe3ed33 | ||
|
|
47d4a6cc29 | ||
|
|
c1e4763369 | ||
|
|
333ba0f162 | ||
|
|
e039b0e9f6 | ||
|
|
b3c5e3beca | ||
|
|
70ba45c3bd | ||
|
|
14ae44fcac | ||
|
|
f7618f5da4 | ||
|
|
d6c6e4c02e | ||
|
|
9365052b85 | ||
|
|
ef88063094 | ||
|
|
4cbaf85eea | ||
|
|
6c07123da3 | ||
|
|
04a4ce12c7 | ||
|
|
e00ed8ae37 | ||
|
|
218a79b60a | ||
|
|
19ae86c71f | ||
|
|
9250d4e64d | ||
|
|
9f5aa58a31 | ||
|
|
c8b0a88573 | ||
|
|
7cf3f49aa1 | ||
|
|
be2c803f82 | ||
|
|
fd1197e8dc | ||
|
|
115f91b64a | ||
|
|
dfb800473a | ||
|
|
c69c1bb134 | ||
|
|
5b501c7c2f | ||
|
|
8d5ce56ec2 | ||
|
|
15c97ddc18 | ||
|
|
36d7fbf4e9 | ||
|
|
919e7aa5c6 | ||
|
|
2174de2b71 | ||
|
|
cb9e914e44 | ||
|
|
15f856b762 | ||
|
|
991dba0d62 | ||
|
|
aa2685d6e4 | ||
|
|
0952150328 | ||
|
|
e6ef4d1546 | ||
|
|
16cf85abb9 | ||
|
|
5afe0ead94 | ||
|
|
b6c933f989 | ||
|
|
feaf1a1f06 |
@@ -19,8 +19,6 @@
|
||||
</p>
|
||||
|
||||
|
||||
编译RedKale 1.8.x版本需要在源码工程中的编译器选项中加入: <b>-XDignore.symbol.file=true</b>
|
||||
|
||||
<h5>详情请访问: <a href='https://redkale.org' target='_blank'>https://redkale.org</a></h5>
|
||||
|
||||
<h5>基本文档: <a href='https://redkale.org/articles.html' target='_blank'>https://redkale.org/articles.html</a></h5>
|
||||
|
||||
@@ -14,7 +14,7 @@ fi
|
||||
|
||||
cd "$APP_HOME"
|
||||
|
||||
./bin/shutdown.sh
|
||||
"$APP_HOME"/bin/shutdown.sh
|
||||
|
||||
./bin/start.sh
|
||||
"$APP_HOME"/bin/start.sh
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
@ECHO OFF
|
||||
|
||||
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
|
||||
|
||||
@@ -24,5 +24,5 @@ done
|
||||
export CLASSPATH=$CLASSPATH:$lib
|
||||
|
||||
echo "$APP_HOME"
|
||||
nohup java -DAPP_HOME="$APP_HOME" org.redkale.boot.Application > "$APP_HOME"/log.out &
|
||||
nohup java -DAPP_HOME="$APP_HOME" org.redkale.boot.Application > "$APP_HOME"/logs.out &
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<application port="5050">
|
||||
<application port="2121">
|
||||
|
||||
<!-- 详细配置说明见: http://redkale.org/redkale.html#redkale_confxml -->
|
||||
|
||||
@@ -8,14 +8,14 @@
|
||||
|
||||
</resources>
|
||||
|
||||
<server protocol="HTTP" host="0.0.0.0" port="6060" root="root">
|
||||
<server protocol="HTTP" port="6060">
|
||||
|
||||
<request>
|
||||
<remoteaddr value="request.headers.X-RemoteAddress"/>
|
||||
</request>
|
||||
|
||||
<response>
|
||||
<defcookie domain="" path=""/>
|
||||
<defcookie domain="" path="/"/>
|
||||
<addheader name="Access-Control-Allow-Origin" value="request.headers.Origin" />
|
||||
<setheader name="Access-Control-Allow-Credentials" value="true"/>
|
||||
</response>
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
|
||||
|
||||
handlers = java.util.logging.ConsoleHandler
|
||||
# handlers = java.util.logging.FileHandler
|
||||
|
||||
############################################################
|
||||
.level = FINER
|
||||
.level = FINEST
|
||||
|
||||
java.level = INFO
|
||||
javax.level = INFO
|
||||
@@ -14,11 +15,11 @@ jdk.level = INFO
|
||||
|
||||
java.util.logging.FileHandler.level = FINER
|
||||
#10M
|
||||
java.util.logging.FileHandler.limit = 10485760
|
||||
java.util.logging.FileHandler.count = 10000
|
||||
java.util.logging.FileHandler.limit = 10M
|
||||
java.util.logging.FileHandler.count = 20
|
||||
java.util.logging.FileHandler.encoding = UTF-8
|
||||
java.util.logging.FileHandler.pattern = ${APP_HOME}/logs-%m/log-%d.log
|
||||
java.util.logging.FileHandler.unusual = ${APP_HOME}/logs-%m/log-warnerr-%d.log
|
||||
java.util.logging.FileHandler.append = true
|
||||
|
||||
java.util.logging.ConsoleHandler.level = FINER
|
||||
java.util.logging.ConsoleHandler.level = FINEST
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
<shared-cache-mode>ALL</shared-cache-mode>
|
||||
<properties>
|
||||
<property name="javax.persistence.jdbc.url" value="jdbc:oracle:thin:@localhost:1521:orcl"/>
|
||||
<property name="javax.persistence.jdbc.driver" value="oracle.jdbc.driver.OracleDriver"/>
|
||||
<property name="javax.persistence.jdbc.user" value="system"/>
|
||||
<property name="javax.persistence.jdbc.password" value="1234"/>
|
||||
</properties>
|
||||
@@ -25,8 +24,7 @@
|
||||
<persistence-unit name="user.write" transaction-type="RESOURCE_LOCAL">
|
||||
<shared-cache-mode>ALL</shared-cache-mode>
|
||||
<properties>
|
||||
<property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/user?autoReconnect=true&characterEncoding=utf8"/>
|
||||
<property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver"/>
|
||||
<property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/center?characterEncoding=utf8&useSSL=false&serverTimezone=UTC&rewriteBatchedStatements=true"/>
|
||||
<property name="javax.persistence.jdbc.user" value="root"/>
|
||||
<property name="javax.persistence.jdbc.password" value="1234"/>
|
||||
</properties>
|
||||
|
||||
@@ -63,10 +63,10 @@
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.5.1</version>
|
||||
<configuration>
|
||||
<compilerArgument>-parameters</compilerArgument>
|
||||
<encoding>UTF-8</encoding>
|
||||
<compilerArguments>
|
||||
<verbose />
|
||||
<bootclasspath>${java.home}/lib/rt.jar</bootclasspath>
|
||||
</compilerArguments>
|
||||
</configuration>
|
||||
</plugin>
|
||||
@@ -113,7 +113,7 @@
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-javadoc-plugin</artifactId>
|
||||
<version>2.10.3</version>
|
||||
<version>3.0.0</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
|
||||
@@ -125,7 +125,7 @@
|
||||
aliveTimeoutSeconds: KeepAlive读操作超时秒数, 默认30, 0表示永久不超时; -1表示禁止KeepAlive
|
||||
readTimeoutSeconds: 读操作超时秒数, 默认0, 表示永久不超时
|
||||
writeTimeoutSeconds: 写操作超时秒数, 默认0, 表示永久不超时
|
||||
netimpl: ProtocolServer的实现类。TCP情况下值也可以是aio或nio,默认值为aio;UDP情况下值也可以是bio,默认值为bio;
|
||||
netimpl: ProtocolServer的实现类。TCP情况下值可以是aio或nio,默认值为aio;UDP情况下值可以是bio,默认值为bio;
|
||||
interceptor: 启动/关闭NodeServer时被调用的拦截器实现类,必须是org.redkale.boot.NodeInterceptor的子类,默认为null
|
||||
-->
|
||||
<server protocol="HTTP" host="127.0.0.1" port="6060" root="root" lib="">
|
||||
|
||||
@@ -11,8 +11,8 @@ javax.level = INFO
|
||||
com.sun.level = INFO
|
||||
|
||||
#java.util.logging.FileHandler.level = FINE
|
||||
#10M
|
||||
java.util.logging.FileHandler.limit = 10485760
|
||||
|
||||
java.util.logging.FileHandler.limit = 20M
|
||||
java.util.logging.FileHandler.count = 100
|
||||
java.util.logging.FileHandler.encoding = UTF-8
|
||||
java.util.logging.FileHandler.pattern = ${APP_HOME}/logs-%m/log-%d.log
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
-->
|
||||
<property name="javax.persistence.datasource" value="org.redkale.source.DataJdbcSource"/>
|
||||
<!--
|
||||
是否开启缓存(标记为@Cacheable的Entity类),值目前只支持两种: ALL: 所有开启缓存。 NONE: 关闭所有缓存
|
||||
是否开启缓存(标记为@Cacheable的Entity类),值目前只支持两种: ALL: 所有开启缓存。 NONE: 关闭所有缓存, 非NONE字样统一视为ALL
|
||||
-->
|
||||
<property name="javax.persistence.cachemode" value="ALL"/>
|
||||
|
||||
@@ -20,12 +20,15 @@
|
||||
org.mariadb.jdbc.Driver —————— org.mariadb.jdbc.MySQLDataSource
|
||||
org.postgresql.Driver —————— org.postgresql.ds.PGConnectionPoolDataSource
|
||||
com.mysql.jdbc.Driver —————— com.mysql.jdbc.jdbc2.optional.MysqlConnectionPoolDataSource
|
||||
com.mysql.cj.jdbc.Driver —————— com.mysql.cj.jdbc.MysqlConnectionPoolDataSource
|
||||
oracle.jdbc.driver.OracleDriver —————— oracle.jdbc.pool.OracleConnectionPoolDataSource
|
||||
com.microsoft.sqlserver.jdbc.SQLServerDriver —————— com.microsoft.sqlserver.jdbc.SQLServerConnectionPoolDataSource
|
||||
org.h2.Driver —————— org.h2.jdbcx.JdbcDataSource
|
||||
因此 com.mysql.jdbc.Driver 会被自动转换成 com.mysql.jdbc.jdbc2.optional.MysqlConnectionPoolDataSource
|
||||
并且如果JDBC驱动是以上几个版本,javax.persistence.jdbc.driver属性都可以省略,Redkale会根据javax.persistence.jdbc.url的值来识别驱动
|
||||
-->
|
||||
<property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver"/>
|
||||
<property name="javax.persistence.jdbc.source" value="com.mysql.jdbc.jdbc2.optional.MysqlConnectionPoolDataSource"/>
|
||||
-->
|
||||
<property name="javax.persistence.jdbc.user" value="root"/>
|
||||
<property name="javax.persistence.jdbc.password" value="123456"/>
|
||||
|
||||
@@ -47,7 +50,6 @@
|
||||
<properties>
|
||||
<!-- jdbc:mysql://127.0.0.1:3306/dbim?autoReconnect=true&autoReconnectForPools=true&characterEncoding=utf8 -->
|
||||
<property name="javax.persistence.jdbc.url" value="jdbc:mysql://127.0.0.1:3306/dbim?characterEncoding=utf8"/>
|
||||
<property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver"/>
|
||||
<property name="javax.persistence.jdbc.user" value="root"/>
|
||||
<property name="javax.persistence.jdbc.password" value="123456"/>
|
||||
</properties>
|
||||
|
||||
@@ -10,7 +10,6 @@ module org.redkale {
|
||||
requires java.logging;
|
||||
requires java.xml;
|
||||
requires java.sql;
|
||||
requires java.sql.rowset;
|
||||
|
||||
requires jdk.unsupported; //sun.misc.Unsafe
|
||||
|
||||
|
||||
@@ -196,7 +196,7 @@ public final class ApiDocsService {
|
||||
final FileOutputStream out = new FileOutputStream(new File(app.getHome(), "apidoc.json"));
|
||||
out.write(json.getBytes("UTF-8"));
|
||||
out.close();
|
||||
File doctemplate = new File(app.getConf(), "apidoc-template.html");
|
||||
File doctemplate = new File(app.getConfPath(), "apidoc-template.html");
|
||||
InputStream in = null;
|
||||
if (doctemplate.isFile() && doctemplate.canRead()) {
|
||||
in = new FileInputStream(doctemplate);
|
||||
|
||||
@@ -135,7 +135,7 @@ public final class Application {
|
||||
|
||||
//--------------------------------------------------------------------------------------------
|
||||
//是否用于main方法运行
|
||||
private final boolean singletonrun;
|
||||
final boolean singletonrun;
|
||||
|
||||
//根WatchFactory
|
||||
//private final WatchFactory watchFactory = WatchFactory.root();
|
||||
@@ -143,7 +143,7 @@ public final class Application {
|
||||
private final File home;
|
||||
|
||||
//配置文件目录
|
||||
private final File conf;
|
||||
private final File confPath;
|
||||
|
||||
//日志
|
||||
private final Logger logger;
|
||||
@@ -181,9 +181,9 @@ public final class Application {
|
||||
this.home = root.getCanonicalFile();
|
||||
String confsubpath = System.getProperty(RESNAME_APP_CONF, "conf");
|
||||
if (confsubpath.charAt(0) == '/' || confsubpath.indexOf(':') > 0) {
|
||||
this.conf = new File(confsubpath).getCanonicalFile();
|
||||
this.confPath = new File(confsubpath).getCanonicalFile();
|
||||
} else {
|
||||
this.conf = new File(this.home, confsubpath).getCanonicalFile();
|
||||
this.confPath = new File(this.home, confsubpath).getCanonicalFile();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
@@ -209,7 +209,7 @@ public final class Application {
|
||||
System.setProperty(RESNAME_APP_NODE, node);
|
||||
}
|
||||
//以下是初始化日志配置
|
||||
final File logconf = new File(conf, "logging.properties");
|
||||
final File logconf = new File(confPath, "logging.properties");
|
||||
if (logconf.isFile() && logconf.canRead()) {
|
||||
try {
|
||||
final String rootpath = root.getCanonicalPath().replace('\\', '/');
|
||||
@@ -375,8 +375,8 @@ public final class Application {
|
||||
return home;
|
||||
}
|
||||
|
||||
public File getConf() {
|
||||
return conf;
|
||||
public File getConfPath() {
|
||||
return confPath;
|
||||
}
|
||||
|
||||
public long getStartTime() {
|
||||
@@ -388,8 +388,6 @@ public final class Application {
|
||||
}
|
||||
|
||||
public void init() throws Exception {
|
||||
System.setProperty("sun.nio.ch.internalThreadPoolSize", "" + Runtime.getRuntime().availableProcessors() * 4);
|
||||
System.setProperty("java.util.concurrent.ForkJoinPool.common.parallelism", "" + Runtime.getRuntime().availableProcessors() * 4);
|
||||
System.setProperty("net.transport.poolmaxconns", "100");
|
||||
System.setProperty("net.transport.pinginterval", "30");
|
||||
System.setProperty("net.transport.checkinterval", "30");
|
||||
@@ -400,11 +398,19 @@ public final class Application {
|
||||
System.setProperty("convert.bson.writer.buffer.defsize", "4096");
|
||||
System.setProperty("convert.json.writer.buffer.defsize", "4096");
|
||||
|
||||
File persist = new File(this.conf, "persistence.xml");
|
||||
File persist = new File(this.confPath, "persistence.xml");
|
||||
final String homepath = this.home.getCanonicalPath();
|
||||
final String confpath = this.conf.getCanonicalPath();
|
||||
final String confpath = this.confPath.getCanonicalPath();
|
||||
if (persist.isFile()) System.setProperty(DataSources.DATASOURCE_CONFPATH, persist.getCanonicalPath());
|
||||
logger.log(Level.INFO, "APP_JAVA = " + System.getProperty("java.version") + "\r\n" + RESNAME_APP_ADDR + " = " + this.localAddress.getHostAddress() + "\r\n" + RESNAME_APP_HOME + " = " + homepath + "\r\n" + RESNAME_APP_CONF + " = " + confpath);
|
||||
String pidstr = "";
|
||||
try { //JDK 9+
|
||||
Class phclass = Class.forName("java.lang.ProcessHandle");
|
||||
Object phobj = phclass.getMethod("current").invoke(null);
|
||||
Object pid = phclass.getMethod("pid").invoke(phobj);
|
||||
pidstr = "APP_PID = " + pid + "\r\n";
|
||||
} catch (Throwable t) {
|
||||
}
|
||||
logger.log(Level.INFO, pidstr + "APP_JAVA = " + System.getProperty("java.version") + "\r\n" + RESNAME_APP_ADDR + " = " + this.localAddress.getHostAddress() + "\r\n" + RESNAME_APP_HOME + " = " + homepath + "\r\n" + RESNAME_APP_CONF + " = " + confpath);
|
||||
String lib = config.getValue("lib", "${APP_HOME}/libs/*").trim().replace("${APP_HOME}", homepath);
|
||||
lib = lib.isEmpty() ? confpath : (lib + ";" + confpath);
|
||||
Server.loadLib(classLoader, logger, lib);
|
||||
@@ -419,13 +425,13 @@ public final class Application {
|
||||
if (dfloads != null) {
|
||||
for (String dfload : dfloads.split(";")) {
|
||||
if (dfload.trim().isEmpty()) continue;
|
||||
final File df = (dfload.indexOf('/') < 0) ? new File(conf, "/" + dfload) : new File(dfload);
|
||||
final File df = (dfload.indexOf('/') < 0) ? new File(confPath, "/" + dfload) : new File(dfload);
|
||||
if (df.isFile()) {
|
||||
Properties ps = new Properties();
|
||||
InputStream in = new FileInputStream(df);
|
||||
ps.load(in);
|
||||
in.close();
|
||||
ps.forEach((x, y) -> resourceFactory.register("property." + x, y));
|
||||
ps.forEach((x, y) -> resourceFactory.register("property." + x, y.toString().replace("${APP_HOME}", homepath)));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -433,6 +439,7 @@ public final class Application {
|
||||
String name = prop.getValue("name");
|
||||
String value = prop.getValue("value");
|
||||
if (name == null || value == null) continue;
|
||||
value = value.replace("${APP_HOME}", homepath);
|
||||
if (name.startsWith("system.property.")) {
|
||||
System.setProperty(name.substring("system.property.".length()), value);
|
||||
} else if (name.startsWith("mimetype.property.")) {
|
||||
@@ -460,7 +467,7 @@ public final class Application {
|
||||
try {
|
||||
Resource res = field.getAnnotation(Resource.class);
|
||||
if (res == null) return;
|
||||
if (Sncp.isRemote((Service) src)) return; //远程模式不得注入
|
||||
if (src instanceof Service && Sncp.isRemote((Service) src)) return; //远程模式不得注入
|
||||
Class type = field.getType();
|
||||
if (type == Application.class) {
|
||||
field.set(src, application);
|
||||
@@ -552,8 +559,8 @@ public final class Application {
|
||||
|
||||
public void restoreConfig() throws IOException {
|
||||
synchronized (this) {
|
||||
File confFile = new File(this.conf, "application.xml");
|
||||
confFile.renameTo(new File(this.conf, "application_" + String.format("%1$tY%1$tm%1$td%1$tH%1$tM%1$tS", System.currentTimeMillis()) + ".xml"));
|
||||
File confFile = new File(this.confPath, "application.xml");
|
||||
confFile.renameTo(new File(this.confPath, "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();
|
||||
@@ -805,13 +812,22 @@ public final class Application {
|
||||
sercdl.await();
|
||||
}
|
||||
|
||||
public static <T extends Service> T singleton(Class<T> serviceClass) throws Exception {
|
||||
return singleton("", serviceClass);
|
||||
public static <T extends Service> T singleton(Class<T> serviceClass, Class<? extends Service>... extServiceClasses) throws Exception {
|
||||
return singleton("", serviceClass, extServiceClasses);
|
||||
}
|
||||
|
||||
public static <T extends Service> T singleton(String name, Class<T> serviceClass) throws Exception {
|
||||
public static <T extends Service> T singleton(String name, Class<T> serviceClass, Class<? extends Service>... extServiceClasses) throws Exception {
|
||||
if (serviceClass == null) throw new IllegalArgumentException("serviceClass is null");
|
||||
final Application application = Application.create(true);
|
||||
System.setProperty("red" + "kale-singleton-serviceclass", serviceClass.getName());
|
||||
if (extServiceClasses != null && extServiceClasses.length > 0) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (Class clazz : extServiceClasses) {
|
||||
if (sb.length() > 0) sb.append(',');
|
||||
sb.append(clazz.getName());
|
||||
}
|
||||
System.setProperty("red" + "kale-singleton-extserviceclasses", sb.toString());
|
||||
}
|
||||
application.init();
|
||||
application.start();
|
||||
for (NodeServer server : application.servers) {
|
||||
@@ -871,7 +887,7 @@ public final class Application {
|
||||
return null;
|
||||
}
|
||||
|
||||
private void shutdown() throws Exception {
|
||||
public void shutdown() throws Exception {
|
||||
for (ApplicationListener listener : this.listeners) {
|
||||
try {
|
||||
listener.preShutdown(this);
|
||||
|
||||
@@ -7,10 +7,11 @@ package org.redkale.boot;
|
||||
|
||||
import java.io.*;
|
||||
import java.lang.annotation.*;
|
||||
import java.lang.reflect.*;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.net.*;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.jar.*;
|
||||
import java.util.logging.*;
|
||||
import java.util.regex.*;
|
||||
@@ -30,12 +31,14 @@ public final class ClassFilter<T> {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(ClassFilter.class.getName()); //日志对象
|
||||
|
||||
private static final boolean finer = logger.isLoggable(Level.FINER); //日志级别
|
||||
private static final boolean finest = logger.isLoggable(Level.FINEST); //日志级别
|
||||
|
||||
private final Set<FilterEntry<T>> entrys = new HashSet<>(); //符合条件的结果
|
||||
|
||||
private final Set<FilterEntry<T>> expectEntrys = new HashSet<>(); //准备符合条件的结果
|
||||
|
||||
private Predicate<String> expectPredicate;
|
||||
|
||||
private boolean refused; //是否拒绝所有数据,设置true,则其他规则失效,都是拒绝.
|
||||
|
||||
private Class superClass; //符合的父类型。不为空时,扫描结果的class必须是superClass的子类
|
||||
@@ -136,10 +139,11 @@ public final class ClassFilter<T> {
|
||||
*
|
||||
* @param property AnyValue
|
||||
* @param clazzname String
|
||||
* @param url URL
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public final void filter(AnyValue property, String clazzname) {
|
||||
filter(property, clazzname, true);
|
||||
public final void filter(AnyValue property, String clazzname, URL url) {
|
||||
filter(property, clazzname, true, url);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -150,6 +154,18 @@ public final class ClassFilter<T> {
|
||||
* @param autoscan 为true表示自动扫描的, false表示显著调用filter, AutoLoad的注解将被忽略
|
||||
*/
|
||||
public final void filter(AnyValue property, String clazzname, boolean autoscan) {
|
||||
filter(property, clazzname, autoscan, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 过滤指定的class
|
||||
*
|
||||
* @param property application.xml中对应class节点下的property属性项
|
||||
* @param clazzname class名称
|
||||
* @param autoscan 为true表示自动扫描的, false表示显著调用filter, AutoLoad的注解将被忽略
|
||||
* @param url URL
|
||||
*/
|
||||
public final void filter(AnyValue property, String clazzname, boolean autoscan, URL url) {
|
||||
boolean r = accept0(property, clazzname);
|
||||
ClassFilter cf = r ? this : null;
|
||||
if (r && ands != null) {
|
||||
@@ -165,7 +181,7 @@ public final class ClassFilter<T> {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (cf == null || clazzname.startsWith("sun.")) return;
|
||||
if (cf == null || clazzname.startsWith("sun.") || clazzname.contains("module-info")) return;
|
||||
try {
|
||||
Class clazz = classLoader.loadClass(clazzname);
|
||||
if (!cf.accept(property, clazz, autoscan)) return;
|
||||
@@ -183,15 +199,16 @@ public final class ClassFilter<T> {
|
||||
}
|
||||
|
||||
AutoLoad auto = (AutoLoad) clazz.getAnnotation(AutoLoad.class);
|
||||
if (autoscan && auto != null && !auto.value()) { //自动扫描且被标记为@AutoLoad(false)的
|
||||
if ((expectPredicate != null && expectPredicate.test(clazzname)) || (autoscan && auto != null && !auto.value())) { //自动扫描且被标记为@AutoLoad(false)的
|
||||
expectEntrys.add(new FilterEntry(clazz, autoscan, true, property));
|
||||
} else {
|
||||
entrys.add(new FilterEntry(clazz, autoscan, false, property));
|
||||
}
|
||||
} catch (Throwable cfe) {
|
||||
if (finer && !clazzname.startsWith("sun.") && !clazzname.startsWith("javax.")
|
||||
&& !clazzname.startsWith("com.sun.") && !clazzname.startsWith("jdk.")) {
|
||||
logger.log(Level.FINEST, ClassFilter.class.getSimpleName() + " filter error", cfe);
|
||||
if (finest && !clazzname.startsWith("sun.") && !clazzname.startsWith("javax.")
|
||||
&& !clazzname.startsWith("com.sun.") && !clazzname.startsWith("jdk.") && !clazzname.startsWith("META-INF")
|
||||
&& (!(cfe instanceof NoClassDefFoundError) || (cfe instanceof UnsupportedClassVersionError) || ((NoClassDefFoundError) cfe).getMessage().startsWith("java.lang.NoClassDefFoundError: java"))) {
|
||||
logger.log(Level.FINEST, ClassFilter.class.getSimpleName() + " filter error for class: " + clazzname + (url == null ? "" : (" in " + url)), cfe);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -333,6 +350,14 @@ public final class ClassFilter<T> {
|
||||
this.refused = refused;
|
||||
}
|
||||
|
||||
public Predicate<String> getExpectPredicate() {
|
||||
return expectPredicate;
|
||||
}
|
||||
|
||||
public void setExpectPredicate(Predicate<String> predicate) {
|
||||
this.expectPredicate = predicate;
|
||||
}
|
||||
|
||||
public Set<String> getPrivilegeIncludes() {
|
||||
return privilegeIncludes;
|
||||
}
|
||||
@@ -347,6 +372,7 @@ public final class ClassFilter<T> {
|
||||
|
||||
public void setPrivilegeExcludes(Set<String> privilegeExcludes) {
|
||||
this.privilegeExcludes = privilegeExcludes == null || privilegeExcludes.isEmpty() ? null : privilegeExcludes;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -502,7 +528,7 @@ public final class ClassFilter<T> {
|
||||
classes.add(classname);
|
||||
if (debug) debugstr.append(classname).append("\r\n");
|
||||
for (final ClassFilter filter : filters) {
|
||||
if (filter != null) filter.filter(null, classname);
|
||||
if (filter != null) filter.filter(null, classname, url);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -511,7 +537,7 @@ public final class ClassFilter<T> {
|
||||
} else {
|
||||
for (String classname : classes) {
|
||||
for (final ClassFilter filter : filters) {
|
||||
if (filter != null) filter.filter(null, classname);
|
||||
if (filter != null) filter.filter(null, classname, url);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -530,14 +556,14 @@ public final class ClassFilter<T> {
|
||||
classes.add(classname);
|
||||
if (debug) debugstr.append(classname).append("\r\n");
|
||||
for (final ClassFilter filter : filters) {
|
||||
if (filter != null) filter.filter(null, classname);
|
||||
if (filter != null) filter.filter(null, classname, url);
|
||||
}
|
||||
}
|
||||
cache.put(url, classes);
|
||||
} else {
|
||||
for (String classname : classes) {
|
||||
for (final ClassFilter filter : filters) {
|
||||
if (filter != null) filter.filter(null, classname);
|
||||
if (filter != null) filter.filter(null, classname, url);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -241,7 +241,21 @@ public class LogFileHandler extends Handler {
|
||||
}
|
||||
String limitstr = manager.getProperty(cname + ".limit");
|
||||
try {
|
||||
if (limitstr != null) this.limit = Math.abs(Integer.decode(limitstr));
|
||||
if (limitstr != null) {
|
||||
limitstr = limitstr.toUpperCase();
|
||||
boolean g = limitstr.indexOf('G') > 0;
|
||||
boolean m = limitstr.indexOf('M') > 0;
|
||||
boolean k = limitstr.indexOf('K') > 0;
|
||||
int ls = Math.abs(Integer.decode(limitstr.replace("G", "").replace("M", "").replace("K", "").replace("B", "")));
|
||||
if (g) {
|
||||
ls *= 1024 * 1024 * 1024;
|
||||
} else if (m) {
|
||||
ls *= 1024 * 1024;
|
||||
} else if (k) {
|
||||
ls *= 1024;
|
||||
}
|
||||
this.limit = ls;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
}
|
||||
String countstr = manager.getProperty(cname + ".count");
|
||||
|
||||
@@ -9,6 +9,7 @@ import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.*;
|
||||
import java.net.*;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.logging.Level;
|
||||
import javax.annotation.*;
|
||||
import static org.redkale.boot.Application.RESNAME_SNCP_ADDR;
|
||||
@@ -191,7 +192,7 @@ public class NodeHttpServer extends NodeServer {
|
||||
}
|
||||
int max = 0;
|
||||
if (ss != null && sb != null) {
|
||||
Collections.sort(ss, (AbstractMap.SimpleEntry<String, String[]> o1, AbstractMap.SimpleEntry<String, String[]> o2) -> o1.getKey().compareTo(o2.getKey()));
|
||||
ss.sort((AbstractMap.SimpleEntry<String, String[]> o1, AbstractMap.SimpleEntry<String, String[]> o2) -> o1.getKey().compareTo(o2.getKey()));
|
||||
for (AbstractMap.SimpleEntry<String, String[]> as : ss) {
|
||||
if (as.getKey().length() > max) max = as.getKey().length();
|
||||
}
|
||||
@@ -217,6 +218,7 @@ public class NodeHttpServer extends NodeServer {
|
||||
if (!rest) return;
|
||||
if (restConf == null) return; //不存在REST服务
|
||||
|
||||
final long starts = System.currentTimeMillis();
|
||||
String prefix0 = restConf.getValue("path", "");
|
||||
if (!prefix0.isEmpty() && prefix0.charAt(prefix0.length() - 1) == '/') prefix0 = prefix0.substring(0, prefix0.length() - 1);
|
||||
if (!prefix0.isEmpty() && prefix0.charAt(0) != '/') prefix0 = '/' + prefix0;
|
||||
@@ -243,35 +245,45 @@ public class NodeHttpServer extends NodeServer {
|
||||
|
||||
final ClassFilter restFilter = ClassFilter.create(null, restConf.getValue("includes", ""), restConf.getValue("excludes", ""), includeValues, excludeValues);
|
||||
final boolean finest = logger.isLoggable(Level.FINEST);
|
||||
super.interceptorServices.forEach((service) -> {
|
||||
final Class stype = Sncp.getServiceType(service);
|
||||
final String name = Sncp.getResourceName(service);
|
||||
RestService rs = (RestService) stype.getAnnotation(RestService.class);
|
||||
if (rs == null || rs.ignore()) return;
|
||||
final CountDownLatch scdl = new CountDownLatch(super.interceptorServices.size());
|
||||
super.interceptorServices.stream().parallel().forEach((service) -> {
|
||||
try {
|
||||
final Class stype = Sncp.getServiceType(service);
|
||||
final String name = Sncp.getResourceName(service);
|
||||
RestService rs = (RestService) stype.getAnnotation(RestService.class);
|
||||
if (rs == null || rs.ignore()) return;
|
||||
|
||||
final String stypename = stype.getName();
|
||||
if (!autoload && !includeValues.contains(stypename)) return;
|
||||
if (!restFilter.accept(stypename)) return;
|
||||
if (restedObjects.contains(service)) {
|
||||
logger.log(Level.WARNING, stype.getName() + " repeat create rest servlet, so ignore");
|
||||
return;
|
||||
}
|
||||
restedObjects.add(service); //避免重复创建Rest对象
|
||||
HttpServlet servlet = httpServer.addRestServlet(serverClassLoader, service, userType, baseServletType, prefix);
|
||||
if (servlet == null) return; //没有HttpMapping方法的HttpServlet调用Rest.createRestServlet就会返回null
|
||||
String prefix2 = prefix;
|
||||
WebServlet ws = servlet.getClass().getAnnotation(WebServlet.class);
|
||||
if (ws != null && !ws.repair()) prefix2 = "";
|
||||
resourceFactory.inject(servlet, NodeHttpServer.this);
|
||||
if (finest) logger.finest(threadName + " Create RestServlet(resource.name='" + name + "') = " + servlet);
|
||||
if (ss != null) {
|
||||
String[] mappings = servlet.getClass().getAnnotation(WebServlet.class).value();
|
||||
for (int i = 0; i < mappings.length; i++) {
|
||||
mappings[i] = prefix2 + mappings[i];
|
||||
final String stypename = stype.getName();
|
||||
if (!autoload && !includeValues.contains(stypename)) return;
|
||||
if (!restFilter.accept(stypename)) return;
|
||||
synchronized (restedObjects) {
|
||||
if (restedObjects.contains(service)) {
|
||||
logger.log(Level.WARNING, stype.getName() + " repeat create rest servlet, so ignore");
|
||||
return;
|
||||
}
|
||||
restedObjects.add(service); //避免重复创建Rest对象
|
||||
}
|
||||
ss.add(new AbstractMap.SimpleEntry<>(servlet.getClass().getName(), mappings));
|
||||
HttpServlet servlet = httpServer.addRestServlet(serverClassLoader, service, userType, baseServletType, prefix);
|
||||
if (servlet == null) return; //没有HttpMapping方法的HttpServlet调用Rest.createRestServlet就会返回null
|
||||
String prefix2 = prefix;
|
||||
WebServlet ws = servlet.getClass().getAnnotation(WebServlet.class);
|
||||
if (ws != null && !ws.repair()) prefix2 = "";
|
||||
resourceFactory.inject(servlet, NodeHttpServer.this);
|
||||
//if (finest) logger.finest(threadName + " Create RestServlet(resource.name='" + name + "') = " + servlet);
|
||||
if (ss != null) {
|
||||
String[] mappings = servlet.getClass().getAnnotation(WebServlet.class).value();
|
||||
for (int i = 0; i < mappings.length; i++) {
|
||||
mappings[i] = prefix2 + mappings[i];
|
||||
}
|
||||
synchronized (ss) {
|
||||
ss.add(new AbstractMap.SimpleEntry<>(servlet.getClass().getName() + "(rest.name='" + name + "')", mappings));
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
scdl.countDown();
|
||||
}
|
||||
});
|
||||
scdl.await();
|
||||
}
|
||||
if (webSocketFilter != null) { //加载RestWebSocket
|
||||
final Set<String> includeValues = new HashSet<>();
|
||||
@@ -328,7 +340,7 @@ public class NodeHttpServer extends NodeServer {
|
||||
}
|
||||
//输出信息
|
||||
if (ss != null && !ss.isEmpty() && sb != null) {
|
||||
Collections.sort(ss, (AbstractMap.SimpleEntry<String, String[]> o1, AbstractMap.SimpleEntry<String, String[]> o2) -> o1.getKey().compareTo(o2.getKey()));
|
||||
ss.sort((AbstractMap.SimpleEntry<String, String[]> o1, AbstractMap.SimpleEntry<String, String[]> o2) -> o1.getKey().compareTo(o2.getKey()));
|
||||
int max = 0;
|
||||
for (AbstractMap.SimpleEntry<String, String[]> as : ss) {
|
||||
if (as.getKey().length() > max) max = as.getKey().length();
|
||||
@@ -340,6 +352,7 @@ public class NodeHttpServer extends NodeServer {
|
||||
}
|
||||
sb.append(" mapping to ").append(Arrays.toString(as.getValue())).append(LINE_SEPARATOR);
|
||||
}
|
||||
sb.append(threadName).append(" All HttpServlets load cost " + (System.currentTimeMillis() - starts) + " ms" + LINE_SEPARATOR);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ import java.lang.reflect.*;
|
||||
import java.net.*;
|
||||
import java.nio.file.*;
|
||||
import java.util.*;
|
||||
import java.util.AbstractMap.SimpleEntry;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.function.*;
|
||||
import java.util.logging.*;
|
||||
@@ -153,6 +154,20 @@ public abstract class NodeServer {
|
||||
}
|
||||
|
||||
ClassFilter<Service> serviceFilter = createServiceClassFilter();
|
||||
if (application.singletonrun) { //singleton模式下只加载指定的Service
|
||||
final String ssc = System.getProperty("red" + "kale-singleton-serviceclass");
|
||||
final String extssc = System.getProperty("red" + "kale-singleton-extserviceclasses");
|
||||
if (ssc != null) {
|
||||
final List<String> sscList = new ArrayList<>();
|
||||
sscList.add(ssc);
|
||||
if (extssc != null && !extssc.isEmpty()) {
|
||||
for (String s : extssc.split(",")) {
|
||||
if (!s.isEmpty()) sscList.add(s);
|
||||
}
|
||||
}
|
||||
serviceFilter.setExpectPredicate(c -> !sscList.contains(c));
|
||||
}
|
||||
}
|
||||
ClassFilter<Filter> filterFilter = createFilterClassFilter();
|
||||
ClassFilter<Servlet> servletFilter = createServletClassFilter();
|
||||
ClassFilter otherFilter = createOtherClassFilter();
|
||||
@@ -161,9 +176,10 @@ public abstract class NodeServer {
|
||||
long e = System.currentTimeMillis() - s;
|
||||
logger.info(this.getClass().getSimpleName() + " load filter class in " + e + " ms");
|
||||
loadService(serviceFilter, otherFilter); //必须在servlet之前
|
||||
loadFilter(filterFilter, otherFilter);
|
||||
loadServlet(servletFilter, otherFilter);
|
||||
|
||||
if (!application.singletonrun) { //非singleton模式下才加载Filter、Servlet
|
||||
loadFilter(filterFilter, otherFilter);
|
||||
loadServlet(servletFilter, otherFilter);
|
||||
}
|
||||
if (this.interceptor != null) this.resourceFactory.inject(this.interceptor);
|
||||
}
|
||||
|
||||
@@ -177,19 +193,27 @@ public abstract class NodeServer {
|
||||
final ResourceFactory appResFactory = application.getResourceFactory();
|
||||
final TransportFactory appSncpTranFactory = application.getSncpTransportFactory();
|
||||
final AnyValue resources = application.config.getAnyValue("resources");
|
||||
final Map<String, AnyValue> cacheResource = new HashMap<>();
|
||||
final Map<String, AnyValue> dataResources = new HashMap<>();
|
||||
final Map<String, SimpleEntry<Class, AnyValue>> cacheResource = new HashMap<>();
|
||||
final Map<String, SimpleEntry<Class, AnyValue>> dataResources = new HashMap<>();
|
||||
if (resources != null) {
|
||||
for (AnyValue sourceConf : resources.getAnyValues("source")) {
|
||||
try {
|
||||
Class type = serverClassLoader.loadClass(sourceConf.getValue("value"));
|
||||
if (type == DataSource.class) type = DataJdbcSource.class;
|
||||
if (type == DataSource.class) {
|
||||
type = DataMemorySource.class;
|
||||
for (AnyValue itemConf : sourceConf.getAnyValues("property")) {
|
||||
if (itemConf.getValue("name", "").contains(DataSources.JDBC_URL)) {
|
||||
type = DataJdbcSource.class;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!Service.class.isAssignableFrom(type)) {
|
||||
logger.log(Level.SEVERE, "load application source resource, but not Service error: " + sourceConf);
|
||||
} else if (CacheSource.class.isAssignableFrom(type)) {
|
||||
cacheResource.put(sourceConf.getValue("name", ""), sourceConf);
|
||||
cacheResource.put(sourceConf.getValue("name", ""), new SimpleEntry(type, sourceConf));
|
||||
} else if (DataSource.class.isAssignableFrom(type)) {
|
||||
dataResources.put(sourceConf.getValue("name", ""), sourceConf);
|
||||
dataResources.put(sourceConf.getValue("name", ""), new SimpleEntry(type, sourceConf));
|
||||
} else {
|
||||
logger.log(Level.SEVERE, "load application source resource, but not CacheSource error: " + sourceConf);
|
||||
}
|
||||
@@ -226,26 +250,31 @@ public abstract class NodeServer {
|
||||
try {
|
||||
if (field.getAnnotation(Resource.class) == null) return;
|
||||
if ((src instanceof Service) && Sncp.isRemote((Service) src)) return; //远程模式不得注入 DataSource
|
||||
AnyValue sourceConf = dataResources.get(resourceName);
|
||||
SimpleEntry<Class, AnyValue> resEntry = dataResources.get(resourceName);
|
||||
AnyValue sourceConf = resEntry == null ? null : resEntry.getValue();
|
||||
DataSource source = null;
|
||||
boolean needinit = true;
|
||||
if (sourceConf != null) {
|
||||
final Class sourceType = serverClassLoader.loadClass(sourceConf.getValue("value"));
|
||||
boolean can = false;
|
||||
for (Constructor cr : sourceType.getConstructors()) {
|
||||
if (cr.getParameterCount() == 0) {
|
||||
can = true;
|
||||
break;
|
||||
final Class sourceType = resEntry.getKey();
|
||||
if (sourceType == DataJdbcSource.class) {
|
||||
source = DataSources.createDataSource(resourceName, sourceConf);
|
||||
} else {
|
||||
boolean can = false;
|
||||
for (Constructor cr : sourceType.getConstructors()) {
|
||||
if (cr.getParameterCount() == 0) {
|
||||
can = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (DataSource.class.isAssignableFrom(sourceType) && can) { // 必须有空构造函数
|
||||
final Service srcService = (Service) src;
|
||||
SncpClient client = Sncp.getSncpClient(srcService);
|
||||
final InetSocketAddress sncpAddr = client == null ? null : client.getClientAddress();
|
||||
final Set<String> groups = new HashSet<>();
|
||||
if (client != null && client.getSameGroup() != null) groups.add(client.getSameGroup());
|
||||
if (client != null && client.getDiffGroups() != null) groups.addAll(client.getDiffGroups());
|
||||
source = (DataSource) Sncp.createLocalService(serverClassLoader, resourceName, sourceType, appResFactory, appSncpTranFactory, sncpAddr, groups, Sncp.getConf(srcService));
|
||||
}
|
||||
}
|
||||
if (DataSource.class.isAssignableFrom(sourceType) && can) { // 必须有空构造函数
|
||||
final Service srcService = (Service) src;
|
||||
SncpClient client = Sncp.getSncpClient(srcService);
|
||||
final InetSocketAddress sncpAddr = client == null ? null : client.getClientAddress();
|
||||
final Set<String> groups = new HashSet<>();
|
||||
if (client != null && client.getSameGroup() != null) groups.add(client.getSameGroup());
|
||||
if (client != null && client.getDiffGroups() != null) groups.addAll(client.getDiffGroups());
|
||||
source = (DataSource) Sncp.createLocalService(serverClassLoader, resourceName, sourceType, appResFactory, appSncpTranFactory, sncpAddr, groups, Sncp.getConf(srcService));
|
||||
}
|
||||
}
|
||||
if (source == null) {
|
||||
@@ -284,6 +313,7 @@ public abstract class NodeServer {
|
||||
public void load(ResourceFactory rf, final Object src, final String resourceName, Field field, final Object attachment) {
|
||||
try {
|
||||
if (field.getAnnotation(Resource.class) == null) return;
|
||||
if (!(src instanceof Service)) throw new RuntimeException("CacheSource must be inject in Service, cannot " + src);
|
||||
if ((src instanceof Service) && Sncp.isRemote((Service) src)) return; //远程模式不需要注入 CacheSource
|
||||
final Service srcService = (Service) src;
|
||||
SncpClient client = Sncp.getSncpClient(srcService);
|
||||
@@ -292,8 +322,12 @@ public abstract class NodeServer {
|
||||
if (client != null && client.getSameGroup() != null) groups.add(client.getSameGroup());
|
||||
if (client != null && client.getDiffGroups() != null) groups.addAll(client.getDiffGroups());
|
||||
|
||||
AnyValue sourceConf = cacheResource.get(resourceName);
|
||||
if (sourceConf == null) sourceConf = dataResources.get(resourceName);
|
||||
SimpleEntry<Class, AnyValue> resEntry = cacheResource.get(resourceName);
|
||||
AnyValue sourceConf = resEntry == null ? null : resEntry.getValue();
|
||||
if (sourceConf == null) {
|
||||
SimpleEntry<Class, AnyValue> resEntry2 = dataResources.get(resourceName);
|
||||
sourceConf = resEntry2 == null ? null : resEntry2.getValue();
|
||||
}
|
||||
final Class sourceType = sourceConf == null ? CacheMemorySource.class : serverClassLoader.loadClass(sourceConf.getValue("value"));
|
||||
Object source = null;
|
||||
if (CacheSource.class.isAssignableFrom(sourceType)) { // CacheSource
|
||||
@@ -370,6 +404,7 @@ public abstract class NodeServer {
|
||||
@SuppressWarnings("unchecked")
|
||||
protected void loadService(ClassFilter<? extends Service> serviceFilter, ClassFilter otherFilter) throws Exception {
|
||||
if (serviceFilter == null) return;
|
||||
final long starts = System.currentTimeMillis();
|
||||
final String threadName = "[" + Thread.currentThread().getName() + "] ";
|
||||
final Set<FilterEntry<? extends Service>> entrys = (Set) serviceFilter.getAllFilterEntrys();
|
||||
ResourceFactory regFactory = isSNCP() ? application.getResourceFactory() : resourceFactory;
|
||||
@@ -401,6 +436,11 @@ public abstract class NodeServer {
|
||||
if (localed && (serviceImplClass.isInterface() || Modifier.isAbstract(serviceImplClass.getModifiers()))) continue; //本地模式不能实例化接口和抽象类的Service类
|
||||
final ResourceFactory.ResourceLoader resourceLoader = (ResourceFactory rf, final Object src, final String resourceName, Field field, final Object attachment) -> {
|
||||
try {
|
||||
if (SncpClient.parseMethod(serviceImplClass).isEmpty() && serviceImplClass.getAnnotation(Priority.class) == null) { //class没有可用的方法且没有标记启动优先级的, 通常为BaseService
|
||||
logger.log(Level.FINE, serviceImplClass + " cannot load because not found less one public non-final method");
|
||||
return;
|
||||
}
|
||||
|
||||
Service service;
|
||||
boolean ws = src instanceof WebSocketServlet;
|
||||
if (ws || localed) { //本地模式
|
||||
@@ -408,8 +448,6 @@ public abstract class NodeServer {
|
||||
} else {
|
||||
service = Sncp.createRemoteService(serverClassLoader, resourceName, serviceImplClass, appSncpTransFactory, NodeServer.this.sncpAddress, groups, entry.getProperty());
|
||||
}
|
||||
if (SncpClient.parseMethod(serviceImplClass).isEmpty() && serviceImplClass.getAnnotation(Priority.class) == null) return; //class没有可用的方法且没有标记启动优先级的, 通常为BaseService
|
||||
|
||||
final Class restype = Sncp.getResourceType(service);
|
||||
if (rf.find(resourceName, restype) == null) {
|
||||
regFactory.register(resourceName, restype, service);
|
||||
@@ -460,7 +498,7 @@ public abstract class NodeServer {
|
||||
}
|
||||
//----------------- init -----------------
|
||||
List<Service> swlist = new ArrayList<>(localServices);
|
||||
Collections.sort(swlist, (o1, o2) -> {
|
||||
swlist.sort((o1, o2) -> {
|
||||
Priority p1 = o1.getClass().getAnnotation(Priority.class);
|
||||
Priority p2 = o2.getClass().getAnnotation(Priority.class);
|
||||
int v = (p2 == null ? 0 : p2.value()) - (p1 == null ? 0 : p1.value());
|
||||
@@ -473,24 +511,19 @@ public abstract class NodeServer {
|
||||
localServices.addAll(swlist);
|
||||
//this.loadPersistData();
|
||||
final List<String> slist = sb == null ? null : new CopyOnWriteArrayList<>();
|
||||
CountDownLatch clds = new CountDownLatch(localServices.size());
|
||||
localServices.stream().forEach(y -> {
|
||||
try {
|
||||
long s = System.currentTimeMillis();
|
||||
y.init(Sncp.getConf(y));
|
||||
long e = System.currentTimeMillis() - s;
|
||||
String serstr = Sncp.toSimpleString(y, maxNameLength, maxClassNameLength);
|
||||
if (slist != null) slist.add(new StringBuilder().append(threadName).append(serstr).append(" load and init in ").append(e).append(" ms").append(LINE_SEPARATOR).toString());
|
||||
} finally {
|
||||
clds.countDown();
|
||||
}
|
||||
long s = System.currentTimeMillis();
|
||||
y.init(Sncp.getConf(y));
|
||||
long e = System.currentTimeMillis() - s;
|
||||
String serstr = Sncp.toSimpleString(y, maxNameLength, maxClassNameLength);
|
||||
if (slist != null) slist.add(new StringBuilder().append(threadName).append(serstr).append(" load and init in ").append(e).append(" ms").append(LINE_SEPARATOR).toString());
|
||||
});
|
||||
clds.await();
|
||||
if (slist != null && sb != null) {
|
||||
List<String> wlist = new ArrayList<>(slist); //直接使用CopyOnWriteArrayList偶尔会出现莫名的异常(CopyOnWriteArrayList源码1185行)
|
||||
for (String s : wlist) {
|
||||
sb.append(s);
|
||||
}
|
||||
sb.append(threadName).append("All Services load cost " + (System.currentTimeMillis() - starts) + " ms" + LINE_SEPARATOR);
|
||||
}
|
||||
if (sb != null && sb.length() > 0) logger.log(Level.INFO, sb.toString());
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ public class NodeSncpServer extends NodeServer {
|
||||
private NodeSncpServer(Application application, AnyValue serconf) {
|
||||
super(application, createServer(application, serconf));
|
||||
this.sncpServer = (SncpServer) this.server;
|
||||
this.consumer = sncpServer == null ? null : x -> sncpServer.addSncpServlet(x);
|
||||
this.consumer = sncpServer == null || application.singletonrun ? null : x -> sncpServer.addSncpServlet(x); //singleton模式下不生成SncpServlet
|
||||
}
|
||||
|
||||
public static NodeServer createNodeServer(Application application, AnyValue serconf) {
|
||||
|
||||
@@ -5,7 +5,10 @@
|
||||
*/
|
||||
package org.redkale.boot.watch;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.*;
|
||||
import java.util.stream.Stream;
|
||||
import javax.annotation.Resource;
|
||||
import org.redkale.boot.*;
|
||||
import org.redkale.net.Server;
|
||||
@@ -23,18 +26,16 @@ public class ServerWatchService extends AbstractWatchService {
|
||||
@Comment("不存在的Server节点")
|
||||
public static final int RET_SERVER_NOT_EXISTS = 1602_0001;
|
||||
|
||||
@Comment("更改Server监听地址端口失败")
|
||||
public static final int RET_SERVER_CHANGEPORT_ERROR = 1602_0002;
|
||||
|
||||
@Resource
|
||||
protected Application application;
|
||||
|
||||
@RestMapping(name = "info", comment = "单个Server信息查询")
|
||||
public RetResult info(@RestParam(name = "#port:") int port) {
|
||||
NodeServer node = null;
|
||||
for (NodeServer ns : application.getNodeServers()) {
|
||||
if (ns.getServer().getSocketAddress().getPort() == port) {
|
||||
node = ns;
|
||||
break;
|
||||
}
|
||||
}
|
||||
public RetResult info(@RestParam(name = "#port:") final int port) {
|
||||
Stream<NodeServer> stream = application.getNodeServers().stream();
|
||||
NodeServer node = stream.filter(ns -> ns.getServer().getSocketAddress().getPort() == port).findFirst().orElse(null);
|
||||
if (node == null) return new RetResult(RET_SERVER_NOT_EXISTS, "Server(port=" + port + ") not found");
|
||||
return new RetResult(formatToMap(node));
|
||||
}
|
||||
@@ -49,6 +50,25 @@ public class ServerWatchService extends AbstractWatchService {
|
||||
return new RetResult(rs);
|
||||
}
|
||||
|
||||
@RestMapping(name = "changeaddress", comment = "更改Server的监听地址和端口")
|
||||
public RetResult changeAddress(@RestParam(name = "#port:") final int oldport,
|
||||
@RestParam(name = "#newhost:") final String newhost, @RestParam(name = "#newport:") final int newport) {
|
||||
if (oldport < 1) return new RetResult(RET_WATCH_PARAMS_ILLEGAL, "not found param `oldport`");
|
||||
if (newport < 1) return new RetResult(RET_WATCH_PARAMS_ILLEGAL, "not found param `newport`");
|
||||
Stream<NodeServer> stream = application.getNodeServers().stream();
|
||||
NodeServer node = stream.filter(ns -> ns.getServer().getSocketAddress().getPort() == oldport).findFirst().orElse(null);
|
||||
if (node == null) return new RetResult(RET_SERVER_NOT_EXISTS, "Server(port=" + oldport + ") not found");
|
||||
final Server server = node.getServer();
|
||||
InetSocketAddress newAddr = new InetSocketAddress(newhost == null || newhost.isEmpty() ? server.getSocketAddress().getHostString() : newhost, newport);
|
||||
try {
|
||||
server.changeAddress(newAddr);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
return new RetResult(RET_SERVER_CHANGEPORT_ERROR, "changeaddress error");
|
||||
}
|
||||
return RetResult.success();
|
||||
}
|
||||
|
||||
private Map<String, Object> formatToMap(NodeServer node) {
|
||||
Server server = node.getServer();
|
||||
Map<String, Object> rs = new LinkedHashMap<>();
|
||||
|
||||
@@ -170,25 +170,29 @@ public class ServiceWatchService extends AbstractWatchService {
|
||||
}
|
||||
|
||||
@RestMapping(name = "load", auth = false, comment = "动态增加Service")
|
||||
public RetResult loadService(String type, @RestUploadFile(maxLength = 10 * 1024 * 1024, fileNameReg = "\\.jar$") byte[] jar) {
|
||||
public RetResult loadService(@RestParam(name = "type", comment = "Service的类名") String type,
|
||||
@RestUploadFile(maxLength = 10 * 1024 * 1024, fileNameReg = "\\.jar$") byte[] jar) {
|
||||
//待开发
|
||||
return RetResult.success();
|
||||
}
|
||||
|
||||
@RestMapping(name = "reload", auth = false, comment = "重新加载Service")
|
||||
public RetResult reloadService(String name, String type) {
|
||||
public RetResult reloadService(@RestParam(name = "name", comment = "Service的资源名") String name,
|
||||
@RestParam(name = "type", comment = "Service的类名") String type) {
|
||||
//待开发
|
||||
return RetResult.success();
|
||||
}
|
||||
|
||||
@RestMapping(name = "stop", auth = false, comment = "动态停止Service")
|
||||
public RetResult stopService(String name, String type) {
|
||||
public RetResult stopService(@RestParam(name = "name", comment = "Service的资源名") String name,
|
||||
@RestParam(name = "type", comment = "Service的类名") String type) {
|
||||
//待开发
|
||||
return RetResult.success();
|
||||
}
|
||||
|
||||
@RestMapping(name = "find", auth = false, comment = "查找Service")
|
||||
public RetResult find(String name, String type) {
|
||||
public RetResult find(@RestParam(name = "name", comment = "Service的资源名") String name,
|
||||
@RestParam(name = "type", comment = "Service的类名") String type) {
|
||||
//待开发
|
||||
return RetResult.success();
|
||||
}
|
||||
|
||||
@@ -5,9 +5,9 @@
|
||||
*/
|
||||
package org.redkale.boot.watch;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.*;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Properties;
|
||||
import java.util.*;
|
||||
import javax.annotation.Resource;
|
||||
import org.redkale.boot.Application;
|
||||
import org.redkale.net.http.*;
|
||||
@@ -16,6 +16,7 @@ import org.redkale.source.*;
|
||||
import org.redkale.util.*;
|
||||
|
||||
/**
|
||||
* WATCH服务, 操作DataSource源
|
||||
*
|
||||
* @author zhangjx
|
||||
*/
|
||||
@@ -31,13 +32,25 @@ public class SourceWatchService extends AbstractWatchService {
|
||||
@Comment("PoolSource调用change方法失败")
|
||||
public static final int RET_SOURCE_METHOD_INVOKE_NOT_EXISTS = 1605_0003;
|
||||
|
||||
@Resource(name = "APP_HOME")
|
||||
protected File home;
|
||||
|
||||
@Resource
|
||||
protected Application application;
|
||||
|
||||
@RestMapping(name = "change", auth = false, comment = "动态更改DataSource的配置")
|
||||
public RetResult addNode(@RestParam(name = "name", comment = "DataSource的标识") final String name,
|
||||
@RestParam(name = "properties", comment = "配置") final Properties properties) throws IOException {
|
||||
@RestParam(name = "properties", comment = "配置") Properties properties,
|
||||
@RestParam(name = "xmlpath", comment = "配置文件路径") String xmlpath) throws IOException {
|
||||
if (name == null) return new RetResult(RET_WATCH_PARAMS_ILLEGAL, "not found param (name)");
|
||||
if (properties == null && xmlpath != null) {
|
||||
File f = new File(xmlpath);
|
||||
if (!f.isFile()) f = new File(home, xmlpath);
|
||||
if (!f.isFile()) return new RetResult(RET_WATCH_PARAMS_ILLEGAL, "not found file (" + xmlpath + ")");
|
||||
FileInputStream in = new FileInputStream(f);
|
||||
Map<String, Properties> map = DataSources.loadPersistenceXml(in);
|
||||
properties = map.get(name);
|
||||
}
|
||||
if (properties == null) return new RetResult(RET_WATCH_PARAMS_ILLEGAL, "not found param (properties)");
|
||||
DataSource source = null;
|
||||
for (DataSource s : application.getDataSources()) {
|
||||
@@ -76,4 +89,34 @@ public class SourceWatchService extends AbstractWatchService {
|
||||
return new RetResult(RET_SOURCE_METHOD_INVOKE_NOT_EXISTS, "poolsource invoke method('change') error");
|
||||
}
|
||||
}
|
||||
|
||||
@RestMapping(name = "test1", auth = false, comment = "预留")
|
||||
public RetResult test1() {
|
||||
return RetResult.success();
|
||||
}
|
||||
|
||||
@RestMapping(name = "test2", auth = false, comment = "预留")
|
||||
public RetResult test2() {
|
||||
return RetResult.success();
|
||||
}
|
||||
|
||||
@RestMapping(name = "test3", auth = false, comment = "预留")
|
||||
public RetResult test3() {
|
||||
return RetResult.success();
|
||||
}
|
||||
|
||||
@RestMapping(name = "test4", auth = false, comment = "预留")
|
||||
public RetResult test4() {
|
||||
return RetResult.success();
|
||||
}
|
||||
|
||||
@RestMapping(name = "test5", auth = false, comment = "预留")
|
||||
public RetResult test5() {
|
||||
return RetResult.success();
|
||||
}
|
||||
|
||||
@RestMapping(name = "test6", auth = false, comment = "预留")
|
||||
public RetResult test6() {
|
||||
return RetResult.success();
|
||||
}
|
||||
}
|
||||
|
||||
64
src/org/redkale/convert/AnyDecoder.java
Normal file
64
src/org/redkale/convert/AnyDecoder.java
Normal file
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* To change this license header, choose License Headers in Project Properties.
|
||||
* To change this template file, choose Tools | Templates
|
||||
* and open the template in the editor.
|
||||
*/
|
||||
package org.redkale.convert;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.*;
|
||||
import org.redkale.util.*;
|
||||
import org.redkale.convert.Reader.ValueType;
|
||||
import static org.redkale.convert.Reader.ValueType.MAP;
|
||||
|
||||
/**
|
||||
* 对不明类型的对象进行反序列化。 <br>
|
||||
* <b>注意: 目前只支持文本格式</b> <br>
|
||||
* <p>
|
||||
* 详情见: https://redkale.org
|
||||
*
|
||||
* @author zhangjx
|
||||
*/
|
||||
public class AnyDecoder implements Decodeable<Reader, Object> {
|
||||
|
||||
private static final Type collectionObjectType = new TypeToken<Collection<Object>>() {
|
||||
}.getType();
|
||||
|
||||
private static final Type mapObjectType = new TypeToken<Map<String, Object>>() {
|
||||
}.getType();
|
||||
|
||||
private static final Creator<ArrayList> collectionCreator = Creator.create(ArrayList.class);
|
||||
|
||||
private static final Creator<HashMap> mapCreator = Creator.create(HashMap.class);
|
||||
|
||||
protected final Decodeable<Reader, String> stringDecoder;
|
||||
|
||||
protected final CollectionDecoder collectionDecoder;
|
||||
|
||||
protected final MapDecoder mapDecoder;
|
||||
|
||||
public AnyDecoder(final ConvertFactory factory) {
|
||||
this.stringDecoder = factory.loadDecoder(String.class);
|
||||
this.collectionDecoder = new CollectionDecoder(factory, collectionObjectType, Object.class, collectionCreator, this);
|
||||
this.mapDecoder = new MapDecoder(factory, mapObjectType, String.class, Object.class, mapCreator, stringDecoder, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object convertFrom(Reader in) {
|
||||
ValueType vt = in.readType();
|
||||
if (vt == null) return null;
|
||||
switch (vt) {
|
||||
case ARRAY:
|
||||
return this.collectionDecoder.convertFrom(in);
|
||||
case MAP:
|
||||
return this.mapDecoder.convertFrom(in);
|
||||
}
|
||||
return this.stringDecoder.convertFrom(in);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type getType() {
|
||||
return void.class;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -28,9 +28,9 @@ public class ArrayDecoder<T> implements Decodeable<Reader, T[]> {
|
||||
|
||||
protected final Class componentClass;
|
||||
|
||||
protected final Decodeable<Reader, T> decoder;
|
||||
protected final Decodeable<Reader, T> componentDecoder;
|
||||
|
||||
protected boolean inited = false;
|
||||
protected volatile boolean inited = false;
|
||||
|
||||
protected final Object lock = new Object();
|
||||
|
||||
@@ -51,7 +51,7 @@ public class ArrayDecoder<T> implements Decodeable<Reader, T[]> {
|
||||
this.componentClass = (Class) this.componentType;
|
||||
}
|
||||
factory.register(type, this);
|
||||
this.decoder = factory.loadDecoder(this.componentType);
|
||||
this.componentDecoder = factory.loadDecoder(this.componentType);
|
||||
} finally {
|
||||
inited = true;
|
||||
synchronized (lock) {
|
||||
@@ -66,14 +66,15 @@ public class ArrayDecoder<T> implements Decodeable<Reader, T[]> {
|
||||
}
|
||||
|
||||
public T[] convertFrom(Reader in, DeMember member) {
|
||||
int len = in.readArrayB(member, decoder);
|
||||
byte[] typevals = new byte[1];
|
||||
int len = in.readArrayB(member, typevals, componentDecoder);
|
||||
int contentLength = -1;
|
||||
if (len == Reader.SIGN_NULL) return null;
|
||||
if (len == Reader.SIGN_NOLENBUTBYTES) {
|
||||
contentLength = in.readMemberContentLength(member, decoder);
|
||||
contentLength = in.readMemberContentLength(member, componentDecoder);
|
||||
len = Reader.SIGN_NOLENGTH;
|
||||
}
|
||||
if (this.decoder == null) {
|
||||
if (this.componentDecoder == null) {
|
||||
if (!this.inited) {
|
||||
synchronized (lock) {
|
||||
try {
|
||||
@@ -84,7 +85,7 @@ public class ArrayDecoder<T> implements Decodeable<Reader, T[]> {
|
||||
}
|
||||
}
|
||||
}
|
||||
final Decodeable<Reader, T> localdecoder = this.decoder;
|
||||
final Decodeable<Reader, T> localdecoder = getComponentDecoder(this.componentDecoder, typevals);
|
||||
final List<T> result = new ArrayList();
|
||||
boolean first = true;
|
||||
if (len == Reader.SIGN_NOLENGTH) {
|
||||
@@ -92,7 +93,7 @@ public class ArrayDecoder<T> implements Decodeable<Reader, T[]> {
|
||||
while (hasNext(in, member, startPosition, contentLength, first)) {
|
||||
Reader itemReader = getItemReader(in, member, first);
|
||||
if (itemReader == null) break;
|
||||
result.add(readMemberValue(itemReader, member, first));
|
||||
result.add(readMemberValue(itemReader, member, localdecoder, first));
|
||||
first = false;
|
||||
}
|
||||
} else {
|
||||
@@ -109,17 +110,21 @@ public class ArrayDecoder<T> implements Decodeable<Reader, T[]> {
|
||||
return in.hasNext(startPosition, contentLength);
|
||||
}
|
||||
|
||||
protected Decodeable<Reader, T> getComponentDecoder(Decodeable<Reader, T> decoder, byte[] typevals) {
|
||||
return decoder;
|
||||
}
|
||||
|
||||
protected Reader getItemReader(Reader in, DeMember member, boolean first) {
|
||||
return in;
|
||||
}
|
||||
|
||||
protected T readMemberValue(Reader in, DeMember member, boolean first) {
|
||||
return this.decoder.convertFrom(in);
|
||||
protected T readMemberValue(Reader in, DeMember member, Decodeable<Reader, T> decoder, boolean first) {
|
||||
return decoder.convertFrom(in);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.getClass().getSimpleName() + "{componentType:" + this.componentType + ", decoder:" + this.decoder + "}";
|
||||
return this.getClass().getSimpleName() + "{componentType:" + this.componentType + ", decoder:" + this.componentDecoder + "}";
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -131,8 +136,8 @@ public class ArrayDecoder<T> implements Decodeable<Reader, T[]> {
|
||||
return componentType;
|
||||
}
|
||||
|
||||
public Decodeable<Reader, T> getDecoder() {
|
||||
return decoder;
|
||||
public Decodeable<Reader, T> getComponentDecoder() {
|
||||
return componentDecoder;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -27,9 +27,9 @@ public class ArrayEncoder<T> implements Encodeable<Writer, T[]> {
|
||||
|
||||
protected final Encodeable anyEncoder;
|
||||
|
||||
protected final Encodeable<Writer, Object> encoder;
|
||||
protected final Encodeable<Writer, Object> componentEncoder;
|
||||
|
||||
protected boolean inited = false;
|
||||
protected volatile boolean inited = false;
|
||||
|
||||
protected final Object lock = new Object();
|
||||
|
||||
@@ -45,7 +45,7 @@ public class ArrayEncoder<T> implements Encodeable<Writer, T[]> {
|
||||
throw new ConvertException("(" + type + ") is not a array type");
|
||||
}
|
||||
factory.register(type, this);
|
||||
this.encoder = factory.loadEncoder(this.componentType);
|
||||
this.componentEncoder = factory.loadEncoder(this.componentType);
|
||||
this.anyEncoder = factory.getAnyEncoder();
|
||||
} finally {
|
||||
inited = true;
|
||||
@@ -66,11 +66,11 @@ public class ArrayEncoder<T> implements Encodeable<Writer, T[]> {
|
||||
return;
|
||||
}
|
||||
if (value.length == 0) {
|
||||
out.writeArrayB(0, encoder, value);
|
||||
out.writeArrayB(0, componentEncoder, value);
|
||||
out.writeArrayE();
|
||||
return;
|
||||
}
|
||||
if (this.encoder == null) {
|
||||
if (this.componentEncoder == null) {
|
||||
if (!this.inited) {
|
||||
synchronized (lock) {
|
||||
try {
|
||||
@@ -81,12 +81,12 @@ public class ArrayEncoder<T> implements Encodeable<Writer, T[]> {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (out.writeArrayB(value.length, encoder, value) < 0) {
|
||||
if (out.writeArrayB(value.length, componentEncoder, value) < 0) {
|
||||
final Type comp = this.componentType;
|
||||
boolean first = true;
|
||||
for (Object v : value) {
|
||||
if (!first) out.writeArrayMark();
|
||||
writeMemberValue(out, member, ((v != null && (v.getClass() == comp || out.specify() == comp)) ? encoder : anyEncoder), v, first);
|
||||
writeMemberValue(out, member, ((v != null && (v.getClass() == comp || out.specify() == comp)) ? componentEncoder : anyEncoder), v, first);
|
||||
if (first) first = false;
|
||||
}
|
||||
}
|
||||
@@ -99,7 +99,7 @@ public class ArrayEncoder<T> implements Encodeable<Writer, T[]> {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.getClass().getSimpleName() + "{componentType:" + this.componentType + ", encoder:" + this.encoder + "}";
|
||||
return this.getClass().getSimpleName() + "{componentType:" + this.componentType + ", encoder:" + this.componentEncoder + "}";
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -111,8 +111,8 @@ public class ArrayEncoder<T> implements Encodeable<Writer, T[]> {
|
||||
return componentType;
|
||||
}
|
||||
|
||||
public Encodeable<Writer, Object> getEncoder() {
|
||||
return encoder;
|
||||
public Encodeable<Writer, Object> getComponentEncoder() {
|
||||
return componentEncoder;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ package org.redkale.convert;
|
||||
import org.redkale.util.Creator;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.Collection;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Collection的反序列化操作类 <br>
|
||||
@@ -29,9 +29,9 @@ public class CollectionDecoder<T> implements Decodeable<Reader, Collection<T>> {
|
||||
|
||||
protected Creator<Collection<T>> creator;
|
||||
|
||||
protected final Decodeable<Reader, T> decoder;
|
||||
protected final Decodeable<Reader, T> componentDecoder;
|
||||
|
||||
protected boolean inited = false;
|
||||
protected volatile boolean inited = false;
|
||||
|
||||
protected final Object lock = new Object();
|
||||
|
||||
@@ -43,12 +43,12 @@ public class CollectionDecoder<T> implements Decodeable<Reader, Collection<T>> {
|
||||
this.componentType = pt.getActualTypeArguments()[0];
|
||||
this.creator = factory.loadCreator((Class) pt.getRawType());
|
||||
factory.register(type, this);
|
||||
this.decoder = factory.loadDecoder(this.componentType);
|
||||
this.componentDecoder = factory.loadDecoder(this.componentType);
|
||||
} else if (factory.isReversible()) {
|
||||
this.componentType = Object.class;
|
||||
this.creator = factory.loadCreator(Object.class);
|
||||
this.creator = factory.loadCreator(type instanceof Class ? (Class) type : Collection.class);
|
||||
factory.register(type, this);
|
||||
this.decoder = factory.loadDecoder(this.componentType);
|
||||
this.componentDecoder = factory.loadDecoder(this.componentType);
|
||||
} else {
|
||||
throw new ConvertException("CollectionDecoder not support the type (" + type + ")");
|
||||
}
|
||||
@@ -60,20 +60,32 @@ public class CollectionDecoder<T> implements Decodeable<Reader, Collection<T>> {
|
||||
}
|
||||
}
|
||||
|
||||
//仅供类似JsonAnyDecoder这种动态创建使用, 不得调用 factory.register
|
||||
public CollectionDecoder(final ConvertFactory factory, Type type, Type componentType,
|
||||
Creator<Collection<T>> creator, final Decodeable<Reader, T> componentDecoder) {
|
||||
Objects.requireNonNull(componentDecoder);
|
||||
this.type = type;
|
||||
this.componentType = componentType;
|
||||
this.creator = creator;
|
||||
this.componentDecoder = componentDecoder;
|
||||
this.inited = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<T> convertFrom(Reader in) {
|
||||
return convertFrom(in, null);
|
||||
}
|
||||
|
||||
public Collection<T> convertFrom(Reader in, DeMember member) {
|
||||
int len = in.readArrayB(member, decoder);
|
||||
byte[] typevals = new byte[1];
|
||||
int len = in.readArrayB(member, typevals, componentDecoder);
|
||||
int contentLength = -1;
|
||||
if (len == Reader.SIGN_NULL) return null;
|
||||
if (len == Reader.SIGN_NOLENBUTBYTES) {
|
||||
contentLength = in.readMemberContentLength(member, decoder);
|
||||
contentLength = in.readMemberContentLength(member, componentDecoder);
|
||||
len = Reader.SIGN_NOLENGTH;
|
||||
}
|
||||
if (this.decoder == null) {
|
||||
if (this.componentDecoder == null) {
|
||||
if (!this.inited) {
|
||||
synchronized (lock) {
|
||||
try {
|
||||
@@ -84,7 +96,7 @@ public class CollectionDecoder<T> implements Decodeable<Reader, Collection<T>> {
|
||||
}
|
||||
}
|
||||
}
|
||||
final Decodeable<Reader, T> localdecoder = this.decoder;
|
||||
final Decodeable<Reader, T> localdecoder = getComponentDecoder(this.componentDecoder, typevals);
|
||||
final Collection<T> result = this.creator.create();
|
||||
boolean first = true;
|
||||
if (len == Reader.SIGN_NOLENGTH) {
|
||||
@@ -92,7 +104,7 @@ public class CollectionDecoder<T> implements Decodeable<Reader, Collection<T>> {
|
||||
while (hasNext(in, member, startPosition, contentLength, first)) {
|
||||
Reader itemReader = getItemReader(in, member, first);
|
||||
if (itemReader == null) break;
|
||||
result.add(readMemberValue(itemReader, member, first));
|
||||
result.add(readMemberValue(itemReader, member, localdecoder, first));
|
||||
first = false;
|
||||
}
|
||||
} else {
|
||||
@@ -108,12 +120,16 @@ public class CollectionDecoder<T> implements Decodeable<Reader, Collection<T>> {
|
||||
return in.hasNext(startPosition, contentLength);
|
||||
}
|
||||
|
||||
protected Decodeable<Reader, T> getComponentDecoder(Decodeable<Reader, T> decoder, byte[] typevals) {
|
||||
return decoder;
|
||||
}
|
||||
|
||||
protected Reader getItemReader(Reader in, DeMember member, boolean first) {
|
||||
return in;
|
||||
}
|
||||
|
||||
protected T readMemberValue(Reader in, DeMember member, boolean first) {
|
||||
return this.decoder.convertFrom(in);
|
||||
|
||||
protected T readMemberValue(Reader in, DeMember member, Decodeable<Reader, T> decoder, boolean first) {
|
||||
return decoder.convertFrom(in);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -125,8 +141,8 @@ public class CollectionDecoder<T> implements Decodeable<Reader, Collection<T>> {
|
||||
return componentType;
|
||||
}
|
||||
|
||||
public Decodeable<Reader, T> getDecoder() {
|
||||
return decoder;
|
||||
public Decodeable<Reader, T> getComponentDecoder() {
|
||||
return componentDecoder;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -23,9 +23,9 @@ public class CollectionEncoder<T> implements Encodeable<Writer, Collection<T>> {
|
||||
|
||||
protected final Type type;
|
||||
|
||||
protected final Encodeable<Writer, Object> encoder;
|
||||
protected final Encodeable<Writer, Object> componentEncoder;
|
||||
|
||||
protected boolean inited = false;
|
||||
protected volatile boolean inited = false;
|
||||
|
||||
protected final Object lock = new Object();
|
||||
|
||||
@@ -35,12 +35,12 @@ public class CollectionEncoder<T> implements Encodeable<Writer, Collection<T>> {
|
||||
if (type instanceof ParameterizedType) {
|
||||
Type t = ((ParameterizedType) type).getActualTypeArguments()[0];
|
||||
if (t instanceof TypeVariable) {
|
||||
this.encoder = factory.getAnyEncoder();
|
||||
this.componentEncoder = factory.getAnyEncoder();
|
||||
} else {
|
||||
this.encoder = factory.loadEncoder(t);
|
||||
this.componentEncoder = factory.loadEncoder(t);
|
||||
}
|
||||
} else {
|
||||
this.encoder = factory.getAnyEncoder();
|
||||
this.componentEncoder = factory.getAnyEncoder();
|
||||
}
|
||||
} finally {
|
||||
inited = true;
|
||||
@@ -61,11 +61,11 @@ public class CollectionEncoder<T> implements Encodeable<Writer, Collection<T>> {
|
||||
return;
|
||||
}
|
||||
if (value.isEmpty()) {
|
||||
out.writeArrayB(0, encoder, value);
|
||||
out.writeArrayB(0, componentEncoder, value);
|
||||
out.writeArrayE();
|
||||
return;
|
||||
}
|
||||
if (this.encoder == null) {
|
||||
if (this.componentEncoder == null) {
|
||||
if (!this.inited) {
|
||||
synchronized (lock) {
|
||||
try {
|
||||
@@ -76,7 +76,7 @@ public class CollectionEncoder<T> implements Encodeable<Writer, Collection<T>> {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (out.writeArrayB(value.size(), encoder, value) < 0) {
|
||||
if (out.writeArrayB(value.size(), componentEncoder, value) < 0) {
|
||||
boolean first = true;
|
||||
for (Object v : value) {
|
||||
if (!first) out.writeArrayMark();
|
||||
@@ -88,7 +88,7 @@ public class CollectionEncoder<T> implements Encodeable<Writer, Collection<T>> {
|
||||
}
|
||||
|
||||
protected void writeValue(Writer out, EnMember member, Object value) {
|
||||
encoder.convertTo(out, value);
|
||||
componentEncoder.convertTo(out, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -96,8 +96,11 @@ public class CollectionEncoder<T> implements Encodeable<Writer, Collection<T>> {
|
||||
return type;
|
||||
}
|
||||
|
||||
public Encodeable<Writer, Object> getEncoder() {
|
||||
return encoder;
|
||||
public Encodeable<Writer, Object> getComponentEncoder() {
|
||||
return componentEncoder;
|
||||
}
|
||||
|
||||
public Type getComponentType() {
|
||||
return componentEncoder == null ? null : componentEncoder.getType();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,6 +33,8 @@ public abstract class Convert<R extends Reader, W extends Writer> {
|
||||
|
||||
public abstract boolean isBinary();
|
||||
|
||||
public abstract <T> T convertFrom(final Type type, final byte[] bytes);
|
||||
|
||||
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);
|
||||
|
||||
@@ -89,4 +89,9 @@ public final class ConvertColumnEntry {
|
||||
this.index = index;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ConvertColumnEntry{" + "index=" + index + ", name=" + name + ", ignore=" + ignore + ", convertType=" + convertType + '}';
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -45,6 +45,8 @@ public abstract class ConvertFactory<R extends Reader, W extends Writer> {
|
||||
|
||||
private final ConcurrentHashMap<String, Class> entitys = new ConcurrentHashMap();
|
||||
|
||||
private final ConcurrentHashMap<Type, Map<String, SimpledCoder<R, W, ?>>> fieldCoders = new ConcurrentHashMap();
|
||||
|
||||
private final ConcurrentHashMap<Type, Decodeable<R, ?>> decoders = new ConcurrentHashMap();
|
||||
|
||||
private final ConcurrentHashMap<Type, Encodeable<W, ?>> encoders = new ConcurrentHashMap();
|
||||
@@ -89,8 +91,10 @@ public abstract class ConvertFactory<R extends Reader, W extends Writer> {
|
||||
|
||||
this.register(Number.class, NumberSimpledCoder.instance);
|
||||
this.register(String.class, StringSimpledCoder.instance);
|
||||
this.register(StringConvertWrapper.class, StringConvertWrapperSimpledCoder.instance);
|
||||
this.register(CharSequence.class, CharSequenceSimpledCoder.instance);
|
||||
this.register(java.util.Date.class, DateSimpledCoder.instance);
|
||||
this.register(java.time.Duration.class, DurationSimpledCoder.instance);
|
||||
this.register(AtomicInteger.class, AtomicIntegerSimpledCoder.instance);
|
||||
this.register(AtomicLong.class, AtomicLongSimpledCoder.instance);
|
||||
this.register(BigInteger.class, BigIntegerSimpledCoder.instance);
|
||||
@@ -217,15 +221,10 @@ public abstract class ConvertFactory<R extends Reader, W extends Writer> {
|
||||
return false;
|
||||
}
|
||||
|
||||
public ConvertColumnEntry findRef(AccessibleObject element) {
|
||||
public ConvertColumnEntry findRef(Class clazz, AccessibleObject element) {
|
||||
if (element == null) return null;
|
||||
ConvertColumnEntry en = this.columnEntrys.get(element);
|
||||
Set<String> onlyColumns = null;
|
||||
if (element instanceof Method) {
|
||||
onlyColumns = ignoreAlls.get(((Method) element).getDeclaringClass());
|
||||
} else if (element instanceof Field) {
|
||||
onlyColumns = ignoreAlls.get(((Field) element).getDeclaringClass());
|
||||
}
|
||||
Set<String> onlyColumns = ignoreAlls.get(clazz);
|
||||
if (en != null && onlyColumns == null) return en;
|
||||
final ConvertType ct = this.getConvertType();
|
||||
ConvertColumn[] ccs = element.getAnnotationsByType(ConvertColumn.class);
|
||||
@@ -252,8 +251,8 @@ public abstract class ConvertFactory<R extends Reader, W extends Writer> {
|
||||
}
|
||||
for (ConvertColumn ref : ccs) {
|
||||
if (ref.type().contains(ct)) {
|
||||
String realName = ref.name().isEmpty() ? fieldName : ref.name();
|
||||
if (onlyColumns != null && fieldName != null) {
|
||||
String realName = ref.name().isEmpty() ? fieldName : ref.name();
|
||||
if (!onlyColumns.contains(realName)) return new ConvertColumnEntry(realName, true);
|
||||
}
|
||||
ConvertColumnEntry entry = new ConvertColumnEntry(ref);
|
||||
@@ -261,7 +260,10 @@ public abstract class ConvertFactory<R extends Reader, W extends Writer> {
|
||||
entry.setIgnore(false);
|
||||
return entry;
|
||||
}
|
||||
if (skipIgnores.isEmpty()) return entry;
|
||||
if (skipIgnores.isEmpty()) {
|
||||
if (onlyColumns != null && realName != null && onlyColumns.contains(realName)) entry.setIgnore(false);
|
||||
return entry;
|
||||
}
|
||||
if (skipIgnores.contains(((Member) element).getDeclaringClass())) entry.setIgnore(false);
|
||||
return entry;
|
||||
}
|
||||
@@ -404,12 +406,27 @@ public abstract class ConvertFactory<R extends Reader, W extends Writer> {
|
||||
}
|
||||
}
|
||||
|
||||
public final void registerIgnoreAll(final Class type, Collection<String> excludeColumns) {
|
||||
Set<String> set = ignoreAlls.get(type);
|
||||
if (set == null) {
|
||||
ignoreAlls.put(type, new HashSet<>(excludeColumns));
|
||||
} else {
|
||||
set.addAll(new ArrayList(excludeColumns));
|
||||
}
|
||||
}
|
||||
|
||||
public final void register(final Class type, boolean ignore, String... columns) {
|
||||
for (String column : columns) {
|
||||
register(type, column, new ConvertColumnEntry(column, ignore));
|
||||
}
|
||||
}
|
||||
|
||||
public final void register(final Class type, boolean ignore, Collection<String> columns) {
|
||||
for (String column : columns) {
|
||||
register(type, column, new ConvertColumnEntry(column, ignore));
|
||||
}
|
||||
}
|
||||
|
||||
public final boolean register(final Class type, String column, String alias) {
|
||||
return register(type, column, new ConvertColumnEntry(alias));
|
||||
}
|
||||
@@ -495,6 +512,29 @@ public abstract class ConvertFactory<R extends Reader, W extends Writer> {
|
||||
encoders.put(clazz, encoder);
|
||||
}
|
||||
|
||||
//coder = null表示删除该字段的指定SimpledCoder
|
||||
public final <E> void register(final Class clazz, final String field, final SimpledCoder<R, W, E> coder) {
|
||||
if (field == null || clazz == null) return;
|
||||
try {
|
||||
clazz.getDeclaredField(field);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(clazz + " not found field(" + field + ")");
|
||||
}
|
||||
if (coder == null) {
|
||||
Map map = this.fieldCoders.get(clazz);
|
||||
if (map != null) map.remove(field);
|
||||
} else {
|
||||
this.fieldCoders.computeIfAbsent(clazz, c -> new ConcurrentHashMap<>()).put(field, coder);
|
||||
}
|
||||
}
|
||||
|
||||
public final <E> SimpledCoder<R, W, E> findFieldCoder(final Type clazz, final String field) {
|
||||
if (field == null) return null;
|
||||
Map<String, SimpledCoder<R, W, ?>> map = this.fieldCoders.get(clazz);
|
||||
if (map == null) return parent == null ? null : parent.findFieldCoder(clazz, field);
|
||||
return (SimpledCoder) map.get(field);
|
||||
}
|
||||
|
||||
public final <E> Decodeable<R, E> findDecoder(final Type type) {
|
||||
Decodeable<R, E> rs = (Decodeable<R, E>) decoders.get(type);
|
||||
if (rs != null) return rs;
|
||||
@@ -579,6 +619,7 @@ public abstract class ConvertFactory<R extends Reader, W extends Writer> {
|
||||
decoder = od;
|
||||
} else if (!clazz.getName().startsWith("java.")
|
||||
|| java.net.HttpCookie.class == clazz
|
||||
|| java.util.AbstractMap.SimpleEntry.class == clazz
|
||||
|| clazz.getName().startsWith("java.awt.geom.Point2D")) {
|
||||
Decodeable simpleCoder = null;
|
||||
for (final Method method : clazz.getDeclaredMethods()) {
|
||||
@@ -662,7 +703,7 @@ public abstract class ConvertFactory<R extends Reader, W extends Writer> {
|
||||
encoder = new OptionalCoder(this, type);
|
||||
} else if (clazz == Object.class) {
|
||||
return (Encodeable<W, E>) this.anyEncoder;
|
||||
} else if (!clazz.getName().startsWith("java.") || java.net.HttpCookie.class == clazz) {
|
||||
} else if (!clazz.getName().startsWith("java.") || java.net.HttpCookie.class == clazz || java.util.AbstractMap.SimpleEntry.class == clazz) {
|
||||
Encodeable simpleCoder = null;
|
||||
for (final Method method : clazz.getDeclaredMethods()) {
|
||||
if (!Modifier.isStatic(method.getModifiers())) continue;
|
||||
|
||||
@@ -8,7 +8,7 @@ package org.redkale.convert;
|
||||
import org.redkale.util.Creator;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.Map;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Map的反序列化操作类 <br>
|
||||
@@ -35,7 +35,7 @@ public class MapDecoder<K, V> implements Decodeable<Reader, Map<K, V>> {
|
||||
|
||||
protected final Decodeable<Reader, V> valueDecoder;
|
||||
|
||||
protected boolean inited = false;
|
||||
protected volatile boolean inited = false;
|
||||
|
||||
protected final Object lock = new Object();
|
||||
|
||||
@@ -74,6 +74,20 @@ public class MapDecoder<K, V> implements Decodeable<Reader, Map<K, V>> {
|
||||
}
|
||||
}
|
||||
|
||||
//仅供类似JsonAnyDecoder这种动态创建使用, 不得调用 factory.register
|
||||
public MapDecoder(final ConvertFactory factory, Type type, Type keyType, Type valueType,
|
||||
Creator<Map<K, V>> creator, final Decodeable<Reader, K> keyDecoder, Decodeable<Reader, V> valueDecoder) {
|
||||
Objects.requireNonNull(keyDecoder);
|
||||
Objects.requireNonNull(valueDecoder);
|
||||
this.type = type;
|
||||
this.keyType = keyType;
|
||||
this.valueType = valueType;
|
||||
this.creator = creator;
|
||||
this.keyDecoder = keyDecoder;
|
||||
this.valueDecoder = valueDecoder;
|
||||
this.inited = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<K, V> convertFrom(Reader in) {
|
||||
return convertFrom(in, null);
|
||||
@@ -91,7 +105,8 @@ public class MapDecoder<K, V> implements Decodeable<Reader, Map<K, V>> {
|
||||
}
|
||||
}
|
||||
}
|
||||
int len = in.readMapB(member, this.keyDecoder);
|
||||
byte[] typevals = new byte[2];
|
||||
int len = in.readMapB(member, typevals, this.keyDecoder, this.valueDecoder);
|
||||
int contentLength = -1;
|
||||
if (len == Reader.SIGN_NULL) return null;
|
||||
if (len == Reader.SIGN_NOLENBUTBYTES) {
|
||||
@@ -100,22 +115,24 @@ public class MapDecoder<K, V> implements Decodeable<Reader, Map<K, V>> {
|
||||
}
|
||||
final Map<K, V> result = this.creator.create();
|
||||
boolean first = true;
|
||||
Decodeable<Reader, K> kdecoder = getKeyDecoder(this.keyDecoder, typevals);
|
||||
Decodeable<Reader, V> vdecoder = getValueDecoder(this.valueDecoder, typevals);
|
||||
if (len == Reader.SIGN_NOLENGTH) {
|
||||
int startPosition = in.position();
|
||||
while (hasNext(in, member, startPosition, contentLength, first)) {
|
||||
Reader entryReader = getEntryReader(in, member, first);
|
||||
if (entryReader == null) break;
|
||||
K key = readKeyMember(entryReader, member, first);
|
||||
K key = readKeyMember(entryReader, member, kdecoder, first);
|
||||
entryReader.readBlank();
|
||||
V value = readValueMember(entryReader, member, first);
|
||||
V value = readValueMember(entryReader, member, vdecoder, first);
|
||||
result.put(key, value);
|
||||
first = false;
|
||||
}
|
||||
} else {
|
||||
for (int i = 0; i < len; i++) {
|
||||
K key = readKeyMember(in, member, first);
|
||||
K key = readKeyMember(in, member, kdecoder, first);
|
||||
in.readBlank();
|
||||
V value = readValueMember(in, member, first);
|
||||
V value = readValueMember(in, member, vdecoder, first);
|
||||
result.put(key, value);
|
||||
first = false;
|
||||
}
|
||||
@@ -128,16 +145,24 @@ public class MapDecoder<K, V> implements Decodeable<Reader, Map<K, V>> {
|
||||
return in.hasNext(startPosition, contentLength);
|
||||
}
|
||||
|
||||
protected Decodeable<Reader, K> getKeyDecoder(Decodeable<Reader, K> decoder, byte[] typevals) {
|
||||
return decoder;
|
||||
}
|
||||
|
||||
protected Decodeable<Reader, V> getValueDecoder(Decodeable<Reader, V> decoder, byte[] typevals) {
|
||||
return decoder;
|
||||
}
|
||||
|
||||
protected Reader getEntryReader(Reader in, DeMember member, boolean first) {
|
||||
return in;
|
||||
}
|
||||
|
||||
protected K readKeyMember(Reader in, DeMember member, boolean first) {
|
||||
return keyDecoder.convertFrom(in);
|
||||
protected K readKeyMember(Reader in, DeMember member, Decodeable<Reader, K> decoder, boolean first) {
|
||||
return decoder.convertFrom(in);
|
||||
}
|
||||
|
||||
protected V readValueMember(Reader in, DeMember member, boolean first) {
|
||||
return valueDecoder.convertFrom(in);
|
||||
protected V readValueMember(Reader in, DeMember member, Decodeable<Reader, V> decoder, boolean first) {
|
||||
return decoder.convertFrom(in);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -24,11 +24,11 @@ public class MapEncoder<K, V> implements Encodeable<Writer, Map<K, V>> {
|
||||
|
||||
protected final Type type;
|
||||
|
||||
protected final Encodeable<Writer, K> keyencoder;
|
||||
protected final Encodeable<Writer, K> keyEncoder;
|
||||
|
||||
protected final Encodeable<Writer, V> valencoder;
|
||||
protected final Encodeable<Writer, V> valueEncoder;
|
||||
|
||||
protected boolean inited = false;
|
||||
protected volatile boolean inited = false;
|
||||
|
||||
protected final Object lock = new Object();
|
||||
|
||||
@@ -37,11 +37,11 @@ public class MapEncoder<K, V> implements Encodeable<Writer, Map<K, V>> {
|
||||
try {
|
||||
if (type instanceof ParameterizedType) {
|
||||
final Type[] pt = ((ParameterizedType) type).getActualTypeArguments();
|
||||
this.keyencoder = factory.loadEncoder(pt[0]);
|
||||
this.valencoder = factory.loadEncoder(pt[1]);
|
||||
this.keyEncoder = factory.loadEncoder(pt[0]);
|
||||
this.valueEncoder = factory.loadEncoder(pt[1]);
|
||||
} else {
|
||||
this.keyencoder = factory.getAnyEncoder();
|
||||
this.valencoder = factory.getAnyEncoder();
|
||||
this.keyEncoder = factory.getAnyEncoder();
|
||||
this.valueEncoder = factory.getAnyEncoder();
|
||||
}
|
||||
} finally {
|
||||
inited = true;
|
||||
@@ -63,7 +63,7 @@ public class MapEncoder<K, V> implements Encodeable<Writer, Map<K, V>> {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.keyencoder == null || this.valencoder == null) {
|
||||
if (this.keyEncoder == null || this.valueEncoder == null) {
|
||||
if (!this.inited) {
|
||||
synchronized (lock) {
|
||||
try {
|
||||
@@ -74,21 +74,21 @@ public class MapEncoder<K, V> implements Encodeable<Writer, Map<K, V>> {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (out.writeMapB(values.size(), (Encodeable) keyencoder, (Encodeable) valencoder, value) < 0) {
|
||||
if (out.writeMapB(values.size(), (Encodeable) keyEncoder, (Encodeable) valueEncoder, value) < 0) {
|
||||
boolean first = true;
|
||||
for (Map.Entry<K, V> en : values.entrySet()) {
|
||||
if (!first) out.writeArrayMark();
|
||||
writeMemberValue(out, member, en.getKey(), en.getValue(),first);
|
||||
writeMemberValue(out, member, en.getKey(), en.getValue(), first);
|
||||
if (first) first = false;
|
||||
}
|
||||
}
|
||||
out.writeMapE();
|
||||
}
|
||||
|
||||
protected void writeMemberValue(Writer out, EnMember member, K key, V value,boolean first) {
|
||||
keyencoder.convertTo(out, key);
|
||||
protected void writeMemberValue(Writer out, EnMember member, K key, V value, boolean first) {
|
||||
keyEncoder.convertTo(out, key);
|
||||
out.writeMapMark();
|
||||
valencoder.convertTo(out, value);
|
||||
valueEncoder.convertTo(out, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -96,12 +96,20 @@ public class MapEncoder<K, V> implements Encodeable<Writer, Map<K, V>> {
|
||||
return type;
|
||||
}
|
||||
|
||||
public Encodeable<Writer, K> getKeyencoder() {
|
||||
return keyencoder;
|
||||
public Type getKeyType() {
|
||||
return keyEncoder == null ? null : keyEncoder.getType();
|
||||
}
|
||||
|
||||
public Encodeable<Writer, V> getValencoder() {
|
||||
return valencoder;
|
||||
public Type getValueType() {
|
||||
return valueEncoder == null ? null : valueEncoder.getType();
|
||||
}
|
||||
|
||||
public Encodeable<Writer, K> getKeyEncoder() {
|
||||
return keyEncoder;
|
||||
}
|
||||
|
||||
public Encodeable<Writer, V> getValueEncoder() {
|
||||
return valueEncoder;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ public class ObjectDecoder<R extends Reader, T> implements Decodeable<R, T> {
|
||||
|
||||
protected ConvertFactory factory;
|
||||
|
||||
protected boolean inited = false;
|
||||
protected volatile boolean inited = false;
|
||||
|
||||
protected final Object lock = new Object();
|
||||
|
||||
@@ -61,7 +61,10 @@ public class ObjectDecoder<R extends Reader, T> implements Decodeable<R, T> {
|
||||
public void init(final ConvertFactory factory) {
|
||||
this.factory = factory;
|
||||
try {
|
||||
if (type == Object.class) return;
|
||||
if (type == Object.class) {
|
||||
this.creatorConstructorMembers = null;
|
||||
return;
|
||||
}
|
||||
|
||||
Class clazz = null;
|
||||
if (type instanceof ParameterizedType) {
|
||||
@@ -91,10 +94,14 @@ public class ObjectDecoder<R extends Reader, T> implements Decodeable<R, T> {
|
||||
for (final Field field : clazz.getFields()) {
|
||||
if (Modifier.isStatic(field.getModifiers())) continue;
|
||||
if (factory.isConvertDisabled(field)) continue;
|
||||
ref = factory.findRef(field);
|
||||
ref = factory.findRef(clazz, field);
|
||||
if (ref != null && ref.ignore()) continue;
|
||||
Type t = TypeToken.createClassType(TypeToken.getGenericType(field.getGenericType(), this.type), this.type);
|
||||
DeMember member = new DeMember(ObjectEncoder.createAttribute(factory, clazz, field, null, null), factory.loadDecoder(t));
|
||||
Decodeable<R, ?> fieldCoder = factory.findFieldCoder(clazz, field.getName());
|
||||
if (fieldCoder == null) {
|
||||
Type t = TypeToken.createClassType(TypeToken.getGenericType(field.getGenericType(), this.type), this.type);
|
||||
fieldCoder = factory.loadDecoder(t);
|
||||
}
|
||||
DeMember member = new DeMember(ObjectEncoder.createAttribute(factory, clazz, field, null, null), fieldCoder);
|
||||
if (ref != null) member.index = ref.getIndex();
|
||||
list.add(member);
|
||||
}
|
||||
@@ -116,10 +123,15 @@ public class ObjectDecoder<R extends Reader, T> implements Decodeable<R, T> {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
ref = factory.findRef(method);
|
||||
ref = factory.findRef(clazz, method);
|
||||
if (ref != null && ref.ignore()) continue;
|
||||
Type t = TypeToken.createClassType(TypeToken.getGenericType(method.getGenericParameterTypes()[0], this.type), this.type);
|
||||
DeMember member = new DeMember(ObjectEncoder.createAttribute(factory, clazz, null, null, method), factory.loadDecoder(t));
|
||||
|
||||
Decodeable<R, ?> fieldCoder = factory.findFieldCoder(clazz, ConvertFactory.readGetSetFieldName(method));
|
||||
if (fieldCoder == null) {
|
||||
Type t = TypeToken.createClassType(TypeToken.getGenericType(method.getGenericParameterTypes()[0], this.type), this.type);
|
||||
fieldCoder = factory.loadDecoder(t);
|
||||
}
|
||||
DeMember member = new DeMember(ObjectEncoder.createAttribute(factory, clazz, null, null, method), fieldCoder);
|
||||
if (ref != null) member.index = ref.getIndex();
|
||||
list.add(member);
|
||||
}
|
||||
@@ -220,7 +232,7 @@ public class ObjectDecoder<R extends Reader, T> implements Decodeable<R, T> {
|
||||
}
|
||||
}
|
||||
if (this.creatorConstructorMembers == null) { //空构造函数
|
||||
final T result = this.creator.create();
|
||||
final T result = this.creator == null ? null : this.creator.create();
|
||||
boolean first = true;
|
||||
while (hasNext(in, first)) {
|
||||
DeMember member = in.readFieldName(members);
|
||||
@@ -261,6 +273,7 @@ public class ObjectDecoder<R extends Reader, T> implements Decodeable<R, T> {
|
||||
first = false;
|
||||
}
|
||||
in.readObjectE(typeClass);
|
||||
if (this.creator == null) return null;
|
||||
final T result = this.creator.create(constructorParams);
|
||||
for (int i = 0; i < oc; i++) {
|
||||
((Attribute) otherParams[i][0]).set(result, otherParams[i][1]);
|
||||
|
||||
@@ -32,7 +32,7 @@ public class ObjectEncoder<W extends Writer, T> implements Encodeable<W, T> {
|
||||
|
||||
protected ConvertFactory factory;
|
||||
|
||||
protected boolean inited = false;
|
||||
protected volatile boolean inited = false;
|
||||
|
||||
protected final Object lock = new Object();
|
||||
|
||||
@@ -75,10 +75,14 @@ public class ObjectEncoder<W extends Writer, T> implements Encodeable<W, T> {
|
||||
for (final Field field : clazz.getFields()) {
|
||||
if (Modifier.isStatic(field.getModifiers())) continue;
|
||||
if (factory.isConvertDisabled(field)) continue;
|
||||
ref = factory.findRef(field);
|
||||
ref = factory.findRef(clazz, field);
|
||||
if (ref != null && ref.ignore()) continue;
|
||||
Type t = TypeToken.createClassType(TypeToken.getGenericType(field.getGenericType(), this.type), this.type);
|
||||
EnMember member = new EnMember(createAttribute(factory, clazz, field, null, null), factory.loadEncoder(t));
|
||||
Encodeable<W, ?> fieldCoder = factory.findFieldCoder(clazz, field.getName());
|
||||
if (fieldCoder == null) {
|
||||
Type t = TypeToken.createClassType(TypeToken.getGenericType(field.getGenericType(), this.type), this.type);
|
||||
fieldCoder = factory.loadEncoder(t);
|
||||
}
|
||||
EnMember member = new EnMember(createAttribute(factory, clazz, field, null, null), fieldCoder);
|
||||
if (ref != null) member.index = ref.getIndex();
|
||||
list.add(member);
|
||||
}
|
||||
@@ -100,10 +104,14 @@ public class ObjectEncoder<W extends Writer, T> implements Encodeable<W, T> {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
ref = factory.findRef(method);
|
||||
ref = factory.findRef(clazz, method);
|
||||
if (ref != null && ref.ignore()) continue;
|
||||
Type t = TypeToken.createClassType(TypeToken.getGenericType(method.getGenericReturnType(), this.type), this.type);
|
||||
EnMember member = new EnMember(createAttribute(factory, clazz, null, method, null), factory.loadEncoder(t));
|
||||
Encodeable<W, ?> fieldCoder = factory.findFieldCoder(clazz, ConvertFactory.readGetSetFieldName(method));
|
||||
if (fieldCoder == null) {
|
||||
Type t = TypeToken.createClassType(TypeToken.getGenericType(method.getGenericReturnType(), this.type), this.type);
|
||||
fieldCoder = factory.loadEncoder(t);
|
||||
}
|
||||
EnMember member = new EnMember(createAttribute(factory, clazz, null, method, null), fieldCoder);
|
||||
if (ref != null) member.index = ref.getIndex();
|
||||
list.add(member);
|
||||
}
|
||||
@@ -245,24 +253,24 @@ public class ObjectEncoder<W extends Writer, T> implements Encodeable<W, T> {
|
||||
static Attribute createAttribute(final ConvertFactory factory, Class clazz, final Field field, final Method getter, final Method setter) {
|
||||
String fieldalias;
|
||||
if (field != null) { // public field
|
||||
ConvertColumnEntry ref = factory.findRef(field);
|
||||
ConvertColumnEntry ref = factory.findRef(clazz, field);
|
||||
fieldalias = ref == null || ref.name().isEmpty() ? field.getName() : ref.name();
|
||||
} else if (getter != null) {
|
||||
ConvertColumnEntry ref = factory.findRef(getter);
|
||||
ConvertColumnEntry ref = factory.findRef(clazz, getter);
|
||||
String mfieldname = ConvertFactory.readGetSetFieldName(getter);
|
||||
if (ref == null) {
|
||||
try {
|
||||
ref = factory.findRef(clazz.getDeclaredField(mfieldname));
|
||||
ref = factory.findRef(clazz, clazz.getDeclaredField(mfieldname));
|
||||
} catch (Exception e) {
|
||||
}
|
||||
}
|
||||
fieldalias = ref == null || ref.name().isEmpty() ? mfieldname : ref.name();
|
||||
} else { // setter != null
|
||||
ConvertColumnEntry ref = factory.findRef(setter);
|
||||
ConvertColumnEntry ref = factory.findRef(clazz, setter);
|
||||
String mfieldname = ConvertFactory.readGetSetFieldName(setter);
|
||||
if (ref == null) {
|
||||
try {
|
||||
ref = factory.findRef(clazz.getDeclaredField(mfieldname));
|
||||
ref = factory.findRef(clazz, clazz.getDeclaredField(mfieldname));
|
||||
} catch (Exception e) {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ public class OptionalCoder<R extends Reader, W extends Writer, T> extends Simple
|
||||
|
||||
protected final Encodeable<Writer, T> encoder;
|
||||
|
||||
private boolean inited = false;
|
||||
protected volatile boolean inited = false;
|
||||
|
||||
private final Object lock = new Object();
|
||||
|
||||
|
||||
@@ -15,6 +15,10 @@ package org.redkale.convert;
|
||||
*/
|
||||
public abstract class Reader {
|
||||
|
||||
public static enum ValueType {
|
||||
STRING, ARRAY, MAP;
|
||||
}
|
||||
|
||||
//当前对象字段名的游标
|
||||
protected int fieldIndex;
|
||||
|
||||
@@ -73,6 +77,13 @@ public abstract class Reader {
|
||||
*/
|
||||
public abstract void readBlank();
|
||||
|
||||
/**
|
||||
* 读取下个值的类型
|
||||
*
|
||||
* @return ValueType
|
||||
*/
|
||||
public abstract ValueType readType();
|
||||
|
||||
/**
|
||||
* 读取对象的类名, 返回 null 表示对象为null, 返回空字符串表示当前class与返回的class一致,返回非空字符串表示class是当前class的子类。
|
||||
*
|
||||
@@ -95,12 +106,13 @@ public abstract class Reader {
|
||||
/**
|
||||
* 读取数组的开头并返回数组的长度
|
||||
*
|
||||
* @param member DeMember
|
||||
* @param decoder Decodeable
|
||||
* @param member DeMember
|
||||
* @param typevals byte[]
|
||||
* @param componentDecoder Decodeable
|
||||
*
|
||||
* @return 返回数组的长度
|
||||
*/
|
||||
public abstract int readArrayB(DeMember member, Decodeable decoder);
|
||||
public abstract int readArrayB(DeMember member, byte[] typevals, Decodeable componentDecoder);
|
||||
|
||||
/**
|
||||
* 读取数组的尾端
|
||||
@@ -111,12 +123,14 @@ public abstract class Reader {
|
||||
/**
|
||||
* 读取map的开头并返回map的size
|
||||
*
|
||||
* @param member DeMember
|
||||
* @param keydecoder Decodeable
|
||||
* @param member DeMember
|
||||
* @param typevals byte[]
|
||||
* @param keyDecoder Decodeable
|
||||
* @param valueDecoder Decodeable
|
||||
*
|
||||
* @return 返回map的size
|
||||
*/
|
||||
public abstract int readMapB(DeMember member, Decodeable keydecoder);
|
||||
public abstract int readMapB(DeMember member, byte[] typevals, Decodeable keyDecoder, Decodeable valueDecoder);
|
||||
|
||||
/**
|
||||
* 读取数组的尾端
|
||||
|
||||
@@ -30,9 +30,9 @@ public class StreamDecoder<T> implements Decodeable<Reader, Stream<T>> {
|
||||
|
||||
protected Creator<Stream<T>> creator;
|
||||
|
||||
protected final Decodeable<Reader, T> decoder;
|
||||
protected final Decodeable<Reader, T> componentDecoder;
|
||||
|
||||
protected boolean inited = false;
|
||||
protected volatile boolean inited = false;
|
||||
|
||||
protected final Object lock = new Object();
|
||||
|
||||
@@ -44,7 +44,7 @@ public class StreamDecoder<T> implements Decodeable<Reader, Stream<T>> {
|
||||
this.componentType = pt.getActualTypeArguments()[0];
|
||||
this.creator = factory.loadCreator((Class) pt.getRawType());
|
||||
factory.register(type, this);
|
||||
this.decoder = factory.loadDecoder(this.componentType);
|
||||
this.componentDecoder = factory.loadDecoder(this.componentType);
|
||||
} else {
|
||||
throw new ConvertException("StreamDecoder not support the type (" + type + ")");
|
||||
}
|
||||
@@ -62,14 +62,15 @@ public class StreamDecoder<T> implements Decodeable<Reader, Stream<T>> {
|
||||
}
|
||||
|
||||
public Stream<T> convertFrom(Reader in, DeMember member) {
|
||||
int len = in.readArrayB(member, this.decoder);
|
||||
byte[] typevals = new byte[1];
|
||||
int len = in.readArrayB(member, typevals, this.componentDecoder);
|
||||
int contentLength = -1;
|
||||
if (len == Reader.SIGN_NULL) return null;
|
||||
if (len == Reader.SIGN_NOLENBUTBYTES) {
|
||||
contentLength = in.readMemberContentLength(member, this.decoder);
|
||||
contentLength = in.readMemberContentLength(member, this.componentDecoder);
|
||||
len = Reader.SIGN_NOLENGTH;
|
||||
}
|
||||
if (this.decoder == null) {
|
||||
if (this.componentDecoder == null) {
|
||||
if (!this.inited) {
|
||||
synchronized (lock) {
|
||||
try {
|
||||
@@ -80,7 +81,7 @@ public class StreamDecoder<T> implements Decodeable<Reader, Stream<T>> {
|
||||
}
|
||||
}
|
||||
}
|
||||
final Decodeable<Reader, T> localdecoder = this.decoder;
|
||||
final Decodeable<Reader, T> localdecoder = getComponentDecoder(this.componentDecoder, typevals);
|
||||
final List<T> result = new ArrayList();
|
||||
boolean first = true;
|
||||
if (len == Reader.SIGN_NOLENGTH) {
|
||||
@@ -88,7 +89,7 @@ public class StreamDecoder<T> implements Decodeable<Reader, Stream<T>> {
|
||||
while (hasNext(in, member, startPosition, contentLength, first)) {
|
||||
Reader itemReader = getItemReader(in, member, first);
|
||||
if (itemReader == null) break;
|
||||
result.add(readMemberValue(itemReader, member, first));
|
||||
result.add(readMemberValue(itemReader, member, localdecoder, first));
|
||||
first = false;
|
||||
}
|
||||
} else {
|
||||
@@ -104,12 +105,16 @@ public class StreamDecoder<T> implements Decodeable<Reader, Stream<T>> {
|
||||
return in.hasNext(startPosition, contentLength);
|
||||
}
|
||||
|
||||
protected Decodeable<Reader, T> getComponentDecoder(Decodeable<Reader, T> decoder, byte[] typevals) {
|
||||
return decoder;
|
||||
}
|
||||
|
||||
protected Reader getItemReader(Reader in, DeMember member, boolean first) {
|
||||
return in;
|
||||
}
|
||||
|
||||
protected T readMemberValue(Reader in, DeMember member, boolean first) {
|
||||
return this.decoder.convertFrom(in);
|
||||
protected T readMemberValue(Reader in, DeMember member, Decodeable<Reader, T> decoder, boolean first) {
|
||||
return decoder.convertFrom(in);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -121,8 +126,8 @@ public class StreamDecoder<T> implements Decodeable<Reader, Stream<T>> {
|
||||
return componentType;
|
||||
}
|
||||
|
||||
public Decodeable<Reader, T> getDecoder() {
|
||||
return decoder;
|
||||
public Decodeable<Reader, T> getComponentDecoder() {
|
||||
return componentDecoder;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -23,9 +23,9 @@ public class StreamEncoder<T> implements Encodeable<Writer, Stream<T>> {
|
||||
|
||||
protected final Type type;
|
||||
|
||||
protected final Encodeable<Writer, Object> encoder;
|
||||
protected final Encodeable<Writer, Object> componentEncoder;
|
||||
|
||||
protected boolean inited = false;
|
||||
protected volatile boolean inited = false;
|
||||
|
||||
protected final Object lock = new Object();
|
||||
|
||||
@@ -35,12 +35,12 @@ public class StreamEncoder<T> implements Encodeable<Writer, Stream<T>> {
|
||||
if (type instanceof ParameterizedType) {
|
||||
Type t = ((ParameterizedType) type).getActualTypeArguments()[0];
|
||||
if (t instanceof TypeVariable) {
|
||||
this.encoder = factory.getAnyEncoder();
|
||||
this.componentEncoder = factory.getAnyEncoder();
|
||||
} else {
|
||||
this.encoder = factory.loadEncoder(t);
|
||||
this.componentEncoder = factory.loadEncoder(t);
|
||||
}
|
||||
} else {
|
||||
this.encoder = factory.getAnyEncoder();
|
||||
this.componentEncoder = factory.getAnyEncoder();
|
||||
}
|
||||
} finally {
|
||||
inited = true;
|
||||
@@ -62,11 +62,11 @@ public class StreamEncoder<T> implements Encodeable<Writer, Stream<T>> {
|
||||
}
|
||||
Object[] array = value.toArray();
|
||||
if (array.length == 0) {
|
||||
out.writeArrayB(0, encoder, array);
|
||||
out.writeArrayB(0, componentEncoder, array);
|
||||
out.writeArrayE();
|
||||
return;
|
||||
}
|
||||
if (this.encoder == null) {
|
||||
if (this.componentEncoder == null) {
|
||||
if (!this.inited) {
|
||||
synchronized (lock) {
|
||||
try {
|
||||
@@ -77,7 +77,7 @@ public class StreamEncoder<T> implements Encodeable<Writer, Stream<T>> {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (out.writeArrayB(array.length, encoder, array) < 0) {
|
||||
if (out.writeArrayB(array.length, componentEncoder, array) < 0) {
|
||||
boolean first = true;
|
||||
for (Object v : array) {
|
||||
if (!first) out.writeArrayMark();
|
||||
@@ -89,7 +89,7 @@ public class StreamEncoder<T> implements Encodeable<Writer, Stream<T>> {
|
||||
}
|
||||
|
||||
protected void writeMemberValue(Writer out, EnMember member, Object value, boolean first) {
|
||||
encoder.convertTo(out, value);
|
||||
componentEncoder.convertTo(out, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -97,8 +97,11 @@ public class StreamEncoder<T> implements Encodeable<Writer, Stream<T>> {
|
||||
return type;
|
||||
}
|
||||
|
||||
public Encodeable<Writer, Object> getEncoder() {
|
||||
return encoder;
|
||||
public Encodeable<Writer, Object> getComponentEncoder() {
|
||||
return componentEncoder;
|
||||
}
|
||||
|
||||
public Type getComponentType() {
|
||||
return componentEncoder == null ? null : componentEncoder.getType();
|
||||
}
|
||||
}
|
||||
|
||||
63
src/org/redkale/convert/StringConvertWrapper.java
Normal file
63
src/org/redkale/convert/StringConvertWrapper.java
Normal file
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* To change this license header, choose License Headers in Project Properties.
|
||||
* To change this template file, choose Tools | Templates
|
||||
* and open the template in the editor.
|
||||
*/
|
||||
package org.redkale.convert;
|
||||
|
||||
import org.redkale.convert.json.JsonConvert;
|
||||
|
||||
/**
|
||||
* 序列化去掉引号的String对象。
|
||||
* <blockquote><pre>
|
||||
* 场景: JavaBean bean = ... ;
|
||||
* Map map = new HashMap();
|
||||
* map.put("bean", a);
|
||||
* records.add(map);
|
||||
* records需要在后期序列化写入库。 但是在这期间bean的内部字段值可能就变化了,会导致入库时并不是records.add的快照信息。
|
||||
* 所以需要使用StringConvertWrapper:
|
||||
* Map map = new HashMap();
|
||||
* map.put("bean", new StringConvertWrapper(bean.toString()));
|
||||
* records.add(map);
|
||||
* 这样既可以保持快照又不会在bean的值上面多一层引号。
|
||||
* </pre></blockquote>
|
||||
* <p>
|
||||
* 详情见: https://redkale.org
|
||||
*
|
||||
* @author zhangjx
|
||||
*/
|
||||
public class StringConvertWrapper {
|
||||
|
||||
protected String value;
|
||||
|
||||
public StringConvertWrapper() {
|
||||
}
|
||||
|
||||
public StringConvertWrapper(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public static StringConvertWrapper create(Object value) {
|
||||
return create(JsonConvert.root(), value);
|
||||
}
|
||||
|
||||
public static StringConvertWrapper create(TextConvert convert, Object value) {
|
||||
if (value == null) return new StringConvertWrapper(null);
|
||||
if (value instanceof String) return new StringConvertWrapper((String) value);
|
||||
return new StringConvertWrapper(convert.convertTo(value));
|
||||
}
|
||||
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public void setValue(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return value;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -129,13 +129,13 @@ public abstract class Writer {
|
||||
/**
|
||||
* 输出一个数组前的操作
|
||||
*
|
||||
* @param size 数组长度
|
||||
* @param encoder Encodeable
|
||||
* @param obj 对象
|
||||
* @param size 数组长度
|
||||
* @param componentEncoder Encodeable
|
||||
* @param obj 对象, 不一定是数组、Collection对象,也可能是伪Collection对象
|
||||
*
|
||||
* @return 返回-1表示还没有写入对象内容,大于-1表示已写入对象内容,返回对象内容大小
|
||||
*/
|
||||
public abstract int writeArrayB(int size, Encodeable<Writer, Object> encoder, Object obj);
|
||||
public abstract int writeArrayB(int size, Encodeable<Writer, Object> componentEncoder, Object obj);
|
||||
|
||||
/**
|
||||
* 输出数组元素间的间隔符
|
||||
@@ -155,7 +155,7 @@ public abstract class Writer {
|
||||
* @param size map大小
|
||||
* @param keyEncoder Encodeable
|
||||
* @param valueEncoder Encodeable
|
||||
* @param obj 对象
|
||||
* @param obj 对象, 不一定是Map对象,也可能是伪Map对象
|
||||
*
|
||||
* @return 返回-1表示还没有写入对象内容,大于-1表示已写入对象内容,返回对象内容大小
|
||||
*/
|
||||
@@ -256,4 +256,11 @@ public abstract class Writer {
|
||||
* @param value String值
|
||||
*/
|
||||
public abstract void writeString(String value);
|
||||
|
||||
/**
|
||||
* 写入一个StringConvertWrapper值
|
||||
*
|
||||
* @param value StringConvertWrapper值
|
||||
*/
|
||||
public abstract void writeWrapper(StringConvertWrapper value);
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ package org.redkale.convert.bson;
|
||||
import java.nio.*;
|
||||
import org.redkale.convert.*;
|
||||
import static org.redkale.convert.Reader.SIGN_NULL;
|
||||
import org.redkale.convert.ext.ByteSimpledCoder;
|
||||
import org.redkale.util.*;
|
||||
|
||||
/**
|
||||
@@ -48,18 +49,38 @@ public class BsonByteBufferReader extends BsonReader {
|
||||
return mask == null ? currentBuffer.get(currentBuffer.position()) : mask.unmask(currentBuffer.get(currentBuffer.position()));
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断下一个非空白字节是否为[
|
||||
*
|
||||
* @param member DeMember
|
||||
* @param decoder Decodeable
|
||||
* @return 数组长度或 SIGN_NULL
|
||||
*/
|
||||
@Override
|
||||
public final int readArrayB(DeMember member, Decodeable decoder) {
|
||||
public int readMapB(DeMember member, byte[] typevals, Decodeable keyDecoder, Decodeable valueDecoder) {
|
||||
short bt = readShort();
|
||||
if (bt == Reader.SIGN_NULL) return bt;
|
||||
short lt = readShort();
|
||||
byte kt = readByte();
|
||||
byte vt = readByte();
|
||||
if (typevals != null) {
|
||||
typevals[0] = kt;
|
||||
typevals[1] = vt;
|
||||
}
|
||||
return (bt & 0xffff) << 16 | (lt & 0xffff);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断下一个非空白字节是否为[
|
||||
*
|
||||
* @param member DeMember
|
||||
* @param typevals byte[]
|
||||
* @param componentDecoder Decodeable
|
||||
*
|
||||
* @return 数组长度或 SIGN_NULL
|
||||
*/
|
||||
@Override
|
||||
public final int readArrayB(DeMember member, byte[] typevals, Decodeable componentDecoder) {
|
||||
short bt = readShort();
|
||||
if (bt == Reader.SIGN_NULL) return bt;
|
||||
short lt = readShort();
|
||||
if (componentDecoder != null && componentDecoder != ByteSimpledCoder.instance) {
|
||||
byte comval = readByte();
|
||||
if (typevals != null) typevals[0] = comval;
|
||||
}
|
||||
return (bt & 0xffff) << 16 | (lt & 0xffff);
|
||||
}
|
||||
//------------------------------------------------------------
|
||||
|
||||
@@ -6,8 +6,12 @@
|
||||
package org.redkale.convert.bson;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.*;
|
||||
import java.util.stream.Stream;
|
||||
import org.redkale.convert.*;
|
||||
import org.redkale.util.AnyValue;
|
||||
import org.redkale.convert.ext.*;
|
||||
import org.redkale.util.*;
|
||||
|
||||
/**
|
||||
* BSON的ConvertFactory
|
||||
@@ -26,6 +30,16 @@ public final class BsonFactory extends ConvertFactory<BsonReader, BsonWriter> {
|
||||
|
||||
static final Encodeable objectEncoder = instance.loadEncoder(Object.class);
|
||||
|
||||
static final Decodeable skipArrayDecoder = new SkipArrayDecoder(instance, Object[].class);
|
||||
|
||||
static final Decodeable skipCollectionDecoder = new SkipCollectionDecoder(instance, new TypeToken<Collection<Object>>() {
|
||||
}.getType());
|
||||
|
||||
static final Decodeable skipStreamDecoder = new SkipStreamDecoder(instance, new TypeToken<Stream<Object>>() {
|
||||
}.getType());
|
||||
|
||||
static final Decodeable skipMapDecoder = new SkipMapDecoder(instance, Map.class);
|
||||
|
||||
static {
|
||||
instance.register(Serializable.class, objectDecoder);
|
||||
instance.register(Serializable.class, objectEncoder);
|
||||
@@ -89,4 +103,110 @@ public final class BsonFactory extends ConvertFactory<BsonReader, BsonWriter> {
|
||||
return true;
|
||||
}
|
||||
|
||||
protected static byte typeEnum(final Type type) {
|
||||
return typeEnum(TypeToken.typeToClass(type));
|
||||
}
|
||||
|
||||
protected static byte typeEnum(final Class type) {
|
||||
Objects.requireNonNull(type);
|
||||
byte typeval = 127; //字段的类型值
|
||||
if (type == boolean.class || type == Boolean.class) {
|
||||
typeval = 11;
|
||||
} else if (type == byte.class || type == Byte.class) {
|
||||
typeval = 12;
|
||||
} else if (type == short.class || type == Short.class) {
|
||||
typeval = 13;
|
||||
} else if (type == char.class || type == Character.class) {
|
||||
typeval = 14;
|
||||
} else if (type == int.class || type == Integer.class) {
|
||||
typeval = 15;
|
||||
} else if (type == long.class || type == Long.class) {
|
||||
typeval = 16;
|
||||
} else if (type == float.class || type == Float.class) {
|
||||
typeval = 17;
|
||||
} else if (type == double.class || type == Double.class) {
|
||||
typeval = 18;
|
||||
} else if (type == String.class) {
|
||||
typeval = 19;
|
||||
} else if (type == boolean[].class || type == Boolean[].class) {
|
||||
typeval = 21;
|
||||
} else if (type == byte[].class || type == Byte[].class) {
|
||||
typeval = 22;
|
||||
} else if (type == short[].class || type == Short[].class) {
|
||||
typeval = 23;
|
||||
} else if (type == char[].class || type == Character[].class) {
|
||||
typeval = 24;
|
||||
} else if (type == int[].class || type == Integer[].class) {
|
||||
typeval = 25;
|
||||
} else if (type == long[].class || type == Long[].class) {
|
||||
typeval = 26;
|
||||
} else if (type == float[].class || type == Float[].class) {
|
||||
typeval = 27;
|
||||
} else if (type == double[].class || type == Double[].class) {
|
||||
typeval = 28;
|
||||
} else if (type == String[].class) {
|
||||
typeval = 29;
|
||||
} else if (type.isArray()) {
|
||||
typeval = 81;
|
||||
} else if (Collection.class.isAssignableFrom(type)) {
|
||||
typeval = 82;
|
||||
} else if (Stream.class.isAssignableFrom(type)) {
|
||||
typeval = 83;
|
||||
} else if (Map.class.isAssignableFrom(type)) {
|
||||
typeval = 84;
|
||||
}
|
||||
return typeval;
|
||||
}
|
||||
|
||||
protected static Decodeable typeEnum(final byte typeval) {
|
||||
switch (typeval) {
|
||||
case 11:
|
||||
return BoolSimpledCoder.instance;
|
||||
case 12:
|
||||
return ByteSimpledCoder.instance;
|
||||
case 13:
|
||||
return ShortSimpledCoder.instance;
|
||||
case 14:
|
||||
return CharSimpledCoder.instance;
|
||||
case 15:
|
||||
return IntSimpledCoder.instance;
|
||||
case 16:
|
||||
return LongSimpledCoder.instance;
|
||||
case 17:
|
||||
return FloatSimpledCoder.instance;
|
||||
case 18:
|
||||
return DoubleSimpledCoder.instance;
|
||||
case 19:
|
||||
return StringSimpledCoder.instance;
|
||||
case 21:
|
||||
return BoolArraySimpledCoder.instance;
|
||||
case 22:
|
||||
return ByteArraySimpledCoder.instance;
|
||||
case 23:
|
||||
return ShortArraySimpledCoder.instance;
|
||||
case 24:
|
||||
return CharArraySimpledCoder.instance;
|
||||
case 25:
|
||||
return IntArraySimpledCoder.instance;
|
||||
case 26:
|
||||
return LongArraySimpledCoder.instance;
|
||||
case 27:
|
||||
return FloatArraySimpledCoder.instance;
|
||||
case 28:
|
||||
return DoubleArraySimpledCoder.instance;
|
||||
case 29:
|
||||
return StringArraySimpledCoder.instance;
|
||||
case 81:
|
||||
return skipArrayDecoder;
|
||||
case 82:
|
||||
return skipCollectionDecoder;
|
||||
case 83:
|
||||
return skipStreamDecoder;
|
||||
case 84:
|
||||
return skipMapDecoder;
|
||||
case 127:
|
||||
return BsonFactory.objectDecoder;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -93,53 +93,27 @@ public class BsonReader extends Reader {
|
||||
final byte val = this.typeval;
|
||||
this.typeval = 0;
|
||||
switch (val) {
|
||||
case 1: readBoolean();
|
||||
case 11: readBoolean();
|
||||
break;
|
||||
case 2: readByte();
|
||||
case 12: readByte();
|
||||
break;
|
||||
case 3: readShort();
|
||||
case 13: readShort();
|
||||
break;
|
||||
case 4: readChar();
|
||||
case 14: readChar();
|
||||
break;
|
||||
case 5: readInt();
|
||||
case 15: readInt();
|
||||
break;
|
||||
case 6: readLong();
|
||||
case 16: readLong();
|
||||
break;
|
||||
case 7: readFloat();
|
||||
case 17: readFloat();
|
||||
break;
|
||||
case 8: readDouble();
|
||||
case 18: readDouble();
|
||||
break;
|
||||
case 9: readString();
|
||||
case 19: readString();
|
||||
break;
|
||||
case 101:
|
||||
BoolArraySimpledCoder.instance.convertFrom(this);
|
||||
break;
|
||||
case 102:
|
||||
ByteArraySimpledCoder.instance.convertFrom(this);
|
||||
break;
|
||||
case 103:
|
||||
ShortArraySimpledCoder.instance.convertFrom(this);
|
||||
break;
|
||||
case 104:
|
||||
CharArraySimpledCoder.instance.convertFrom(this);
|
||||
break;
|
||||
case 105:
|
||||
IntArraySimpledCoder.instance.convertFrom(this);
|
||||
break;
|
||||
case 106:
|
||||
LongArraySimpledCoder.instance.convertFrom(this);
|
||||
break;
|
||||
case 107:
|
||||
FloatArraySimpledCoder.instance.convertFrom(this);
|
||||
break;
|
||||
case 108:
|
||||
DoubleArraySimpledCoder.instance.convertFrom(this);
|
||||
break;
|
||||
case 109:
|
||||
StringArraySimpledCoder.instance.convertFrom(this);
|
||||
break;
|
||||
case 127:
|
||||
BsonFactory.objectDecoder.convertFrom(this);
|
||||
default:
|
||||
Decodeable decoder = BsonFactory.typeEnum(val);
|
||||
if (decoder != null) decoder.convertFrom(this);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -171,8 +145,17 @@ public class BsonReader extends Reader {
|
||||
}
|
||||
|
||||
@Override
|
||||
public final int readMapB(DeMember member, Decodeable keydecoder) {
|
||||
return readArrayB(member, keydecoder);
|
||||
public int readMapB(DeMember member, byte[] typevals, Decodeable keyDecoder, Decodeable valueDecoder) {
|
||||
short bt = readShort();
|
||||
if (bt == Reader.SIGN_NULL) return bt;
|
||||
int rs = (bt & 0xffff) << 16 | ((content[++this.position] & 0xff) << 8) | (content[++this.position] & 0xff);
|
||||
byte kt = readByte();
|
||||
byte vt = readByte();
|
||||
if (typevals != null) {
|
||||
typevals[0] = kt;
|
||||
typevals[1] = vt;
|
||||
}
|
||||
return rs;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -185,10 +168,15 @@ public class BsonReader extends Reader {
|
||||
* @return 数组长度或SIGN_NULL
|
||||
*/
|
||||
@Override
|
||||
public int readArrayB(DeMember member, Decodeable decoder) {
|
||||
public int readArrayB(DeMember member, byte[] typevals, Decodeable componentDecoder) { //componentDecoder可能为null
|
||||
short bt = readShort();
|
||||
if (bt == Reader.SIGN_NULL) return bt;
|
||||
return (bt & 0xffff) << 16 | ((content[++this.position] & 0xff) << 8) | (content[++this.position] & 0xff);
|
||||
int rs = (bt & 0xffff) << 16 | ((content[++this.position] & 0xff) << 8) | (content[++this.position] & 0xff);
|
||||
if (componentDecoder != null && componentDecoder != ByteSimpledCoder.instance) {
|
||||
byte comval = readByte();
|
||||
if (typevals != null) typevals[0] = comval;
|
||||
}
|
||||
return rs;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -263,7 +251,7 @@ public class BsonReader extends Reader {
|
||||
|
||||
@Override
|
||||
public final byte[] readByteArray() {
|
||||
int len = readArrayB(null, null);
|
||||
int len = readArrayB(null, null, null);
|
||||
int contentLength = -1;
|
||||
if (len == Reader.SIGN_NULL) return null;
|
||||
if (len == Reader.SIGN_NOLENBUTBYTES) {
|
||||
@@ -358,4 +346,9 @@ public class BsonReader extends Reader {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ValueType readType() {
|
||||
throw new UnsupportedOperationException("Not supported yet.");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ package org.redkale.convert.bson;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import org.redkale.convert.*;
|
||||
import org.redkale.convert.ext.ByteSimpledCoder;
|
||||
import org.redkale.util.*;
|
||||
|
||||
/**
|
||||
@@ -201,46 +202,7 @@ public class BsonWriter extends Writer {
|
||||
Attribute attribute = member.getAttribute();
|
||||
writeByte(BsonReader.SIGN_HASNEXT);
|
||||
writeSmallString(attribute.field());
|
||||
byte typeval = 127; //字段的类型值
|
||||
final Class type = attribute.type();
|
||||
if (type == boolean.class || type == Boolean.class) {
|
||||
typeval = 1;
|
||||
} else if (type == byte.class || type == Byte.class) {
|
||||
typeval = 2;
|
||||
} else if (type == short.class || type == Short.class) {
|
||||
typeval = 3;
|
||||
} else if (type == char.class || type == Character.class) {
|
||||
typeval = 4;
|
||||
} else if (type == int.class || type == Integer.class) {
|
||||
typeval = 5;
|
||||
} else if (type == long.class || type == Long.class) {
|
||||
typeval = 6;
|
||||
} else if (type == float.class || type == Float.class) {
|
||||
typeval = 7;
|
||||
} else if (type == double.class || type == Double.class) {
|
||||
typeval = 8;
|
||||
} else if (type == String.class) {
|
||||
typeval = 9;
|
||||
} else if (type == boolean[].class || type == Boolean[].class) {
|
||||
typeval = 101;
|
||||
} else if (type == byte[].class || type == Byte[].class) {
|
||||
typeval = 102;
|
||||
} else if (type == short[].class || type == Short[].class) {
|
||||
typeval = 103;
|
||||
} else if (type == char[].class || type == Character[].class) {
|
||||
typeval = 104;
|
||||
} else if (type == int[].class || type == Integer[].class) {
|
||||
typeval = 105;
|
||||
} else if (type == long[].class || type == Long[].class) {
|
||||
typeval = 106;
|
||||
} else if (type == float[].class || type == Float[].class) {
|
||||
typeval = 107;
|
||||
} else if (type == double[].class || type == Double[].class) {
|
||||
typeval = 108;
|
||||
} else if (type == String[].class) {
|
||||
typeval = 109;
|
||||
}
|
||||
writeByte(typeval);
|
||||
writeByte(BsonFactory.typeEnum(attribute.type()));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -279,14 +241,22 @@ public class BsonWriter extends Writer {
|
||||
writeTo(bytes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void writeWrapper(StringConvertWrapper value) {
|
||||
this.writeString(value == null ? null : value.getValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void writeNull() {
|
||||
writeShort(Reader.SIGN_NULL);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final int writeArrayB(int size, Encodeable<Writer, Object> encoder, Object obj) {
|
||||
public final int writeArrayB(int size, Encodeable<Writer, Object> componentEncoder, Object obj) {
|
||||
writeInt(size);
|
||||
if (componentEncoder != null && componentEncoder != ByteSimpledCoder.instance) {
|
||||
writeByte(BsonFactory.typeEnum(componentEncoder.getType()));
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -300,7 +270,9 @@ public class BsonWriter extends Writer {
|
||||
|
||||
@Override
|
||||
public int writeMapB(int size, Encodeable<Writer, Object> keyEncoder, Encodeable<Writer, Object> valueEncoder, Object obj) {
|
||||
writeArrayB(size, valueEncoder, obj);
|
||||
writeInt(size);
|
||||
writeByte(BsonFactory.typeEnum(keyEncoder.getType()));
|
||||
writeByte(BsonFactory.typeEnum(valueEncoder.getType()));
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
33
src/org/redkale/convert/bson/SkipArrayDecoder.java
Normal file
33
src/org/redkale/convert/bson/SkipArrayDecoder.java
Normal file
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* 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.bson;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
import org.redkale.convert.*;
|
||||
|
||||
/**
|
||||
* 数组的反序列化操作类 <br>
|
||||
* 对象数组的反序列化,不包含int[]、long[]这样的primitive class数组。 <br>
|
||||
* 支持一定程度的泛型。 <br>
|
||||
*
|
||||
* <p>
|
||||
* 详情见: https://redkale.org
|
||||
*
|
||||
* @author zhangjx
|
||||
* @param <T> 反解析的数组元素类型
|
||||
*/
|
||||
public class SkipArrayDecoder<T> extends ArrayDecoder<T> {
|
||||
|
||||
public SkipArrayDecoder(final ConvertFactory factory, final Type type) {
|
||||
super(factory, type);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Decodeable<Reader, T> getComponentDecoder(Decodeable<Reader, T> decoder, byte[] typevals) {
|
||||
if (typevals != null) return BsonFactory.typeEnum(typevals[0]);
|
||||
return decoder;
|
||||
}
|
||||
}
|
||||
32
src/org/redkale/convert/bson/SkipCollectionDecoder.java
Normal file
32
src/org/redkale/convert/bson/SkipCollectionDecoder.java
Normal file
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* To change this license header, choose License Headers in Project Properties.
|
||||
* To change this template file, choose Tools | Templates
|
||||
* and open the template in the editor.
|
||||
*/
|
||||
package org.redkale.convert.bson;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
import org.redkale.convert.*;
|
||||
|
||||
/**
|
||||
* Collection的反序列化操作类 <br>
|
||||
* 支持一定程度的泛型。 <br>
|
||||
*
|
||||
* <p>
|
||||
* 详情见: https://redkale.org
|
||||
*
|
||||
* @author zhangjx
|
||||
* @param <T> 反解析的集合元素类型
|
||||
*/
|
||||
public class SkipCollectionDecoder<T> extends CollectionDecoder<T> {
|
||||
|
||||
public SkipCollectionDecoder(final ConvertFactory factory, final Type type) {
|
||||
super(factory, type);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Decodeable<Reader, T> getComponentDecoder(Decodeable<Reader, T> decoder, byte[] typevals) {
|
||||
if (typevals != null) return BsonFactory.typeEnum(typevals[0]);
|
||||
return decoder;
|
||||
}
|
||||
}
|
||||
38
src/org/redkale/convert/bson/SkipMapDecoder.java
Normal file
38
src/org/redkale/convert/bson/SkipMapDecoder.java
Normal file
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* 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.bson;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
import org.redkale.convert.*;
|
||||
|
||||
/**
|
||||
* Map的反序列化操作类 <br>
|
||||
*
|
||||
* <p>
|
||||
* 详情见: https://redkale.org
|
||||
*
|
||||
* @author zhangjx
|
||||
* @param <K> Map key的数据类型
|
||||
* @param <V> Map value的数据类型
|
||||
*/
|
||||
public class SkipMapDecoder<K, V> extends MapDecoder<K, V> {
|
||||
|
||||
public SkipMapDecoder(final ConvertFactory factory, final Type type) {
|
||||
super(factory, type);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Decodeable<Reader, K> getKeyDecoder(Decodeable<Reader, K> decoder, byte[] typevals) {
|
||||
if (typevals != null) return BsonFactory.typeEnum(typevals[0]);
|
||||
return decoder;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Decodeable<Reader, V> getValueDecoder(Decodeable<Reader, V> decoder, byte[] typevals) {
|
||||
if (typevals != null) return BsonFactory.typeEnum(typevals[1]);
|
||||
return decoder;
|
||||
}
|
||||
}
|
||||
32
src/org/redkale/convert/bson/SkipStreamDecoder.java
Normal file
32
src/org/redkale/convert/bson/SkipStreamDecoder.java
Normal file
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* To change this license header, choose License Headers in Project Properties.
|
||||
* To change this template file, choose Tools | Templates
|
||||
* and open the template in the editor.
|
||||
*/
|
||||
package org.redkale.convert.bson;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
import org.redkale.convert.*;
|
||||
|
||||
/**
|
||||
* Stream的反序列化操作类 <br>
|
||||
* 支持一定程度的泛型。 <br>
|
||||
*
|
||||
* <p>
|
||||
* 详情见: https://redkale.org
|
||||
*
|
||||
* @author zhangjx
|
||||
* @param <T> 反解析的集合元素类型
|
||||
*/
|
||||
public class SkipStreamDecoder<T> extends StreamDecoder<T> {
|
||||
|
||||
public SkipStreamDecoder(final ConvertFactory factory, final Type type) {
|
||||
super(factory, type);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Decodeable<Reader, T> getComponentDecoder(Decodeable<Reader, T> decoder, byte[] typevals) {
|
||||
if (typevals != null) return BsonFactory.typeEnum(typevals[0]);
|
||||
return decoder;
|
||||
}
|
||||
}
|
||||
@@ -42,7 +42,7 @@ public final class BoolArraySimpledCoder<R extends Reader, W extends Writer> ext
|
||||
|
||||
@Override
|
||||
public boolean[] convertFrom(R in) {
|
||||
int len = in.readArrayB(null, BoolSimpledCoder.instance);
|
||||
int len = in.readArrayB(null, null, BoolSimpledCoder.instance);
|
||||
int contentLength = -1;
|
||||
if (len == Reader.SIGN_NULL) return null;
|
||||
if (len == Reader.SIGN_NOLENBUTBYTES) {
|
||||
|
||||
@@ -43,7 +43,7 @@ public final class ByteBufferSimpledCoder<R extends Reader, W extends Writer> ex
|
||||
|
||||
@Override
|
||||
public ByteBuffer convertFrom(R in) {
|
||||
int len = in.readArrayB(null, ByteSimpledCoder.instance);
|
||||
int len = in.readArrayB(null, null, ByteSimpledCoder.instance);
|
||||
int contentLength = -1;
|
||||
if (len == Reader.SIGN_NULL) return null;
|
||||
if (len == Reader.SIGN_NOLENBUTBYTES) {
|
||||
|
||||
@@ -42,7 +42,7 @@ public final class CharArraySimpledCoder<R extends Reader, W extends Writer> ext
|
||||
|
||||
@Override
|
||||
public char[] convertFrom(R in) {
|
||||
int len = in.readArrayB(null, CharSimpledCoder.instance);
|
||||
int len = in.readArrayB(null, null, CharSimpledCoder.instance);
|
||||
int contentLength = -1;
|
||||
if (len == Reader.SIGN_NULL) return null;
|
||||
if (len == Reader.SIGN_NOLENBUTBYTES) {
|
||||
|
||||
@@ -43,7 +43,7 @@ public final class DoubleArraySimpledCoder<R extends Reader, W extends Writer> e
|
||||
|
||||
@Override
|
||||
public double[] convertFrom(R in) {
|
||||
int len = in.readArrayB(null, DoubleSimpledCoder.instance);
|
||||
int len = in.readArrayB(null, null, DoubleSimpledCoder.instance);
|
||||
int contentLength = -1;
|
||||
if (len == Reader.SIGN_NULL) return null;
|
||||
if (len == Reader.SIGN_NOLENBUTBYTES) {
|
||||
|
||||
41
src/org/redkale/convert/ext/DurationSimpledCoder.java
Normal file
41
src/org/redkale/convert/ext/DurationSimpledCoder.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.convert.ext;
|
||||
|
||||
import java.time.Duration;
|
||||
import org.redkale.convert.*;
|
||||
|
||||
/**
|
||||
* Duration 的SimpledCoder实现
|
||||
*
|
||||
* <p>
|
||||
* 详情见: https://redkale.org
|
||||
*
|
||||
* @author zhangjx
|
||||
* @param <R> Reader输入的子类型
|
||||
* @param <W> Writer输出的子类型
|
||||
*/
|
||||
public class DurationSimpledCoder<R extends Reader, W extends Writer> extends SimpledCoder<R, W, Duration> {
|
||||
|
||||
public static final DurationSimpledCoder instance = new DurationSimpledCoder();
|
||||
|
||||
@Override
|
||||
public void convertTo(W out, Duration value) {
|
||||
if (value == null) {
|
||||
out.writeNull();
|
||||
} else {
|
||||
out.writeLong(value.toNanos());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Duration convertFrom(R in) {
|
||||
String value = in.readSmallString();
|
||||
if (value == null) return null;
|
||||
return Duration.ofNanos(Long.parseLong(value));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -42,7 +42,7 @@ public final class FloatArraySimpledCoder<R extends Reader, W extends Writer> ex
|
||||
|
||||
@Override
|
||||
public float[] convertFrom(R in) {
|
||||
int len = in.readArrayB(null, FloatSimpledCoder.instance);
|
||||
int len = in.readArrayB(null, null, FloatSimpledCoder.instance);
|
||||
int contentLength = -1;
|
||||
if (len == Reader.SIGN_NULL) return null;
|
||||
if (len == Reader.SIGN_NOLENBUTBYTES) {
|
||||
|
||||
@@ -43,7 +43,7 @@ public final class IntArraySimpledCoder<R extends Reader, W extends Writer> exte
|
||||
|
||||
@Override
|
||||
public int[] convertFrom(R in) {
|
||||
int len = in.readArrayB(null, IntSimpledCoder.instance);
|
||||
int len = in.readArrayB(null, null, IntSimpledCoder.instance);
|
||||
int contentLength = -1;
|
||||
if (len == Reader.SIGN_NULL) return null;
|
||||
if (len == Reader.SIGN_NOLENBUTBYTES) {
|
||||
|
||||
@@ -43,7 +43,7 @@ public final class LongArraySimpledCoder<R extends Reader, W extends Writer> ext
|
||||
|
||||
@Override
|
||||
public long[] convertFrom(R in) {
|
||||
int len = in.readArrayB(null, LongSimpledCoder.instance);
|
||||
int len = in.readArrayB(null, null, LongSimpledCoder.instance);
|
||||
int contentLength = -1;
|
||||
if (len == Reader.SIGN_NULL) return null;
|
||||
if (len == Reader.SIGN_NOLENBUTBYTES) {
|
||||
|
||||
@@ -42,7 +42,7 @@ public final class ShortArraySimpledCoder<R extends Reader, W extends Writer> ex
|
||||
|
||||
@Override
|
||||
public short[] convertFrom(R in) {
|
||||
int len = in.readArrayB(null, ShortSimpledCoder.instance);
|
||||
int len = in.readArrayB(null, null, ShortSimpledCoder.instance);
|
||||
int contentLength = -1;
|
||||
if (len == Reader.SIGN_NULL) return null;
|
||||
if (len == Reader.SIGN_NOLENBUTBYTES) {
|
||||
|
||||
@@ -44,7 +44,7 @@ public final class StringArraySimpledCoder<R extends Reader, W extends Writer> e
|
||||
}
|
||||
|
||||
public String[] convertFrom(R in, DeMember member) {
|
||||
int len = in.readArrayB(member, StringSimpledCoder.instance);
|
||||
int len = in.readArrayB(member, null, StringSimpledCoder.instance);
|
||||
int contentLength = -1;
|
||||
if (len == Reader.SIGN_NULL) return null;
|
||||
if (len == Reader.SIGN_NOLENBUTBYTES) {
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* To change this license header, choose License Headers in Project Properties.
|
||||
* To change this template file, choose Tools | Templates
|
||||
* and open the template in the editor.
|
||||
*/
|
||||
package org.redkale.convert.ext;
|
||||
|
||||
import org.redkale.convert.*;
|
||||
|
||||
/**
|
||||
* String 的SimpledCoder实现
|
||||
*
|
||||
* <p>
|
||||
* 详情见: https://redkale.org
|
||||
*
|
||||
* @author zhangjx
|
||||
* @param <R> Reader输入的子类型
|
||||
* @param <W> Writer输出的子类型
|
||||
*/
|
||||
public final class StringConvertWrapperSimpledCoder<R extends Reader, W extends Writer> extends SimpledCoder<R, W, StringConvertWrapper> {
|
||||
|
||||
public static final StringConvertWrapperSimpledCoder instance = new StringConvertWrapperSimpledCoder();
|
||||
|
||||
@Override
|
||||
public void convertTo(W out, StringConvertWrapper value) {
|
||||
out.writeWrapper(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public StringConvertWrapper convertFrom(R in) {
|
||||
return new StringConvertWrapper(in.readString());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -126,24 +126,39 @@ public class JsonByteBufferReader extends JsonReader {
|
||||
if (ch == '{') return "";
|
||||
if (ch == 'n' && nextChar() == 'u' && nextChar() == 'l' && nextChar() == 'l') return null;
|
||||
if (ch == 'N' && nextChar() == 'U' && nextChar() == 'L' && nextChar() == 'L') return null;
|
||||
throw new ConvertException("a json object text must begin with '{' (position = " + position + ") but '" + ch + "'");
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(ch);
|
||||
char one;
|
||||
try {
|
||||
while ((one = nextChar()) != 0) sb.append(one);
|
||||
} catch (Exception e) {
|
||||
}
|
||||
throw new ConvertException("a json object text must begin with '{' (position = " + position + ") but '" + ch + "' in (" + sb + ")");
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断下一个非空白字符是否为[
|
||||
*
|
||||
* @param member DeMember
|
||||
* @param decoder Decodeable
|
||||
* @param member DeMember
|
||||
* @param typevals byte[]
|
||||
* @param decoder Decodeable
|
||||
*
|
||||
* @return SIGN_NOLENGTH 或 SIGN_NULL
|
||||
*/
|
||||
@Override
|
||||
public final int readArrayB(DeMember member, Decodeable decoder) {
|
||||
public final int readArrayB(DeMember member, byte[] typevals, Decodeable decoder) {
|
||||
char ch = nextGoodChar();
|
||||
if (ch == '[' || ch == '{') return SIGN_NOLENGTH;
|
||||
if (ch == 'n' && nextChar() == 'u' && nextChar() == 'l' && nextChar() == 'l') return SIGN_NULL;
|
||||
if (ch == 'N' && nextChar() == 'U' && nextChar() == 'L' && nextChar() == 'L') return SIGN_NULL;
|
||||
throw new ConvertException("a json array text must begin with '[' (position = " + position + ") but '" + ch + "'");
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(ch);
|
||||
char one;
|
||||
try {
|
||||
while ((one = nextChar()) != 0) sb.append(one);
|
||||
} catch (Exception e) {
|
||||
}
|
||||
throw new ConvertException("a json array text must begin with '[' (position = " + position + ") but '" + ch + "' in (" + sb + ")");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -153,7 +168,14 @@ public class JsonByteBufferReader extends JsonReader {
|
||||
public final void readBlank() {
|
||||
char ch = nextGoodChar();
|
||||
if (ch == ':') return;
|
||||
throw new ConvertException("expected a ':' but '" + ch + "'(position = " + position + ")");
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(ch);
|
||||
char one;
|
||||
try {
|
||||
while ((one = nextChar()) != 0) sb.append(one);
|
||||
} catch (Exception e) {
|
||||
}
|
||||
throw new ConvertException("expected a ':' but '" + ch + "'(position = " + position + ") in (" + sb + ")");
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -85,6 +85,11 @@ public final class JsonConvert extends TextConvert<JsonReader, JsonWriter> {
|
||||
}
|
||||
|
||||
//------------------------------ convertFrom -----------------------------------------------------------
|
||||
public <T> T convertFrom(final Type type, final byte[] bytes) {
|
||||
if (bytes == null) return null;
|
||||
return convertFrom(type, new String(bytes, StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
public <T> T convertFrom(final Type type, final String text) {
|
||||
if (text == null) return null;
|
||||
return convertFrom(type, Utility.charArray(text));
|
||||
@@ -128,6 +133,52 @@ public final class JsonConvert extends TextConvert<JsonReader, JsonWriter> {
|
||||
return rs;
|
||||
}
|
||||
|
||||
//返回非null的值是由String、ArrayList、HashMap任意组合的对象
|
||||
public <V> V convertFrom(final String text) {
|
||||
if (text == null) return null;
|
||||
return (V) convertFrom(Utility.charArray(text));
|
||||
}
|
||||
|
||||
//返回非null的值是由String、ArrayList、HashMap任意组合的对象
|
||||
public <V> V convertFrom(final char[] text) {
|
||||
if (text == null) return null;
|
||||
return (V) convertFrom(text, 0, text.length);
|
||||
}
|
||||
|
||||
//返回非null的值是由String、ArrayList、HashMap任意组合的对象
|
||||
public <V> V convertFrom(final char[] text, final int start, final int len) {
|
||||
if (text == null) return null;
|
||||
final JsonReader in = readerPool.get();
|
||||
in.setText(text, start, len);
|
||||
Object rs = new AnyDecoder(factory).convertFrom(in);
|
||||
readerPool.accept(in);
|
||||
return (V) rs;
|
||||
}
|
||||
|
||||
//返回非null的值是由String、ArrayList、HashMap任意组合的对象
|
||||
public <V> V convertFrom(final InputStream in) {
|
||||
if (in == null) return null;
|
||||
return (V) new AnyDecoder(factory).convertFrom(new JsonStreamReader(in));
|
||||
}
|
||||
|
||||
//返回非null的值是由String、ArrayList、HashMap任意组合的对象
|
||||
public <V> V convertFrom(final ByteBuffer... buffers) {
|
||||
if (buffers == null || buffers.length == 0) return null;
|
||||
return (V) new AnyDecoder(factory).convertFrom(new JsonByteBufferReader((ConvertMask) null, buffers));
|
||||
}
|
||||
|
||||
//返回非null的值是由String、ArrayList、HashMap任意组合的对象
|
||||
public <V> V convertFrom(final ConvertMask mask, final ByteBuffer... buffers) {
|
||||
if (buffers == null || buffers.length == 0) return null;
|
||||
return (V) new AnyDecoder(factory).convertFrom(new JsonByteBufferReader(mask, buffers));
|
||||
}
|
||||
|
||||
//返回非null的值是由String、ArrayList、HashMap任意组合的对象
|
||||
public <V> V convertFrom(final JsonReader reader) {
|
||||
if (reader == null) return null;
|
||||
return (V) new AnyDecoder(factory).convertFrom(reader);
|
||||
}
|
||||
|
||||
//------------------------------ convertTo -----------------------------------------------------------
|
||||
@Override
|
||||
public String convertTo(final Object value) {
|
||||
|
||||
@@ -158,6 +158,21 @@ public class JsonReader extends Reader {
|
||||
this.position--;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final ValueType readType() {
|
||||
char ch = nextGoodChar();
|
||||
if (ch == '{') {
|
||||
backChar(ch);
|
||||
return ValueType.MAP;
|
||||
}
|
||||
if (ch == '[') {
|
||||
backChar(ch);
|
||||
return ValueType.ARRAY;
|
||||
}
|
||||
backChar(ch);
|
||||
return ValueType.STRING;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断下一个非空白字符是否为{
|
||||
*
|
||||
@@ -168,6 +183,7 @@ public class JsonReader extends Reader {
|
||||
@Override
|
||||
public String readObjectB(final Class clazz) {
|
||||
this.fieldIndex = 0; //必须要重置为0
|
||||
if (this.text.length == 0) return null;
|
||||
char ch = this.text[++this.position];
|
||||
if (ch == '{') return "";
|
||||
if (ch <= ' ') {
|
||||
@@ -189,14 +205,16 @@ public class JsonReader extends Reader {
|
||||
/**
|
||||
* 判断下一个非空白字符是否为{
|
||||
*
|
||||
* @param member DeMember
|
||||
* @param keydecoder Decodeable
|
||||
* @param member DeMember
|
||||
* @param typevals byte[]
|
||||
* @param keyDecoder Decodeable
|
||||
* @param valuedecoder Decodeable
|
||||
*
|
||||
* @return SIGN_NOLENGTH 或 SIGN_NULL
|
||||
*/
|
||||
@Override
|
||||
public final int readMapB(DeMember member, Decodeable keydecoder) {
|
||||
return readArrayB(member, keydecoder);
|
||||
public final int readMapB(DeMember member, byte[] typevals, Decodeable keyDecoder, Decodeable valuedecoder) {
|
||||
return readArrayB(member, typevals, keyDecoder);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -206,13 +224,15 @@ public class JsonReader extends Reader {
|
||||
/**
|
||||
* 判断下一个非空白字符是否为[
|
||||
*
|
||||
* @param member DeMember
|
||||
* @param decoder Decodeable
|
||||
* @param member DeMember
|
||||
* @param typevals byte[]
|
||||
* @param componentDecoder Decodeable
|
||||
*
|
||||
* @return SIGN_NOLENGTH 或 SIGN_NULL
|
||||
*/
|
||||
@Override
|
||||
public int readArrayB(DeMember member, Decodeable decoder) {
|
||||
public int readArrayB(DeMember member, byte[] typevals, Decodeable componentDecoder) {
|
||||
if (this.text.length == 0) return SIGN_NULL;
|
||||
char ch = this.text[++this.position];
|
||||
if (ch == '[') return SIGN_NOLENGTH;
|
||||
if (ch == '{') return SIGN_NOLENGTH;
|
||||
@@ -333,7 +353,7 @@ public class JsonReader extends Reader {
|
||||
}
|
||||
this.position = currpos - 1;
|
||||
if (len == 4 && text0[start] == 'n' && text0[start + 1] == 'u' && text0[start + 2] == 'l' && text0[start + 3] == 'l') return null;
|
||||
return new String(text0, start, len);
|
||||
return new String(text0, start, len == eof ? (len + 1) : len);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -474,7 +494,7 @@ public class JsonReader extends Reader {
|
||||
|
||||
@Override
|
||||
public final byte[] readByteArray() {
|
||||
int len = readArrayB(null, null);
|
||||
int len = readArrayB(null, null, null);
|
||||
int contentLength = -1;
|
||||
if (len == Reader.SIGN_NULL) return null;
|
||||
if (len == Reader.SIGN_NOLENBUTBYTES) {
|
||||
|
||||
@@ -319,6 +319,11 @@ public class JsonWriter extends Writer {
|
||||
writeTo(false, String.valueOf(value));
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void writeWrapper(StringConvertWrapper value) {
|
||||
writeTo(false, String.valueOf(value));
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean needWriteClassName() {
|
||||
return false;
|
||||
@@ -346,7 +351,7 @@ public class JsonWriter extends Writer {
|
||||
}
|
||||
|
||||
@Override
|
||||
public final int writeArrayB(int size, Encodeable<Writer, Object> encoder, Object obj) {
|
||||
public final int writeArrayB(int size, Encodeable<Writer, Object> componentEncoder, Object obj) {
|
||||
writeTo('[');
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -7,13 +7,14 @@ package org.redkale.net;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.*;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.*;
|
||||
import java.nio.channels.*;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.concurrent.atomic.*;
|
||||
import java.util.function.*;
|
||||
import javax.net.ssl.SSLContext;
|
||||
import org.redkale.util.ObjectPool;
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -22,7 +23,7 @@ import javax.net.ssl.SSLContext;
|
||||
*
|
||||
* @author zhangjx
|
||||
*/
|
||||
public abstract class AsyncConnection implements AsynchronousByteChannel, AutoCloseable {
|
||||
public abstract class AsyncConnection implements ReadableByteChannel, WritableByteChannel, AutoCloseable {
|
||||
|
||||
protected SSLContext sslContext;
|
||||
|
||||
@@ -34,6 +35,12 @@ public abstract class AsyncConnection implements AsynchronousByteChannel, AutoCl
|
||||
|
||||
protected volatile long writetime;
|
||||
|
||||
protected final Supplier<ByteBuffer> bufferSupplier;
|
||||
|
||||
protected final Consumer<ByteBuffer> bufferConsumer;
|
||||
|
||||
protected ByteBuffer readBuffer;
|
||||
|
||||
//在线数
|
||||
protected AtomicLong livingCounter;
|
||||
|
||||
@@ -42,6 +49,25 @@ public abstract class AsyncConnection implements AsynchronousByteChannel, AutoCl
|
||||
|
||||
protected Consumer<AsyncConnection> beforeCloseListener;
|
||||
|
||||
//关联的事件数, 小于1表示没有事件
|
||||
protected final AtomicInteger eventing = new AtomicInteger();
|
||||
|
||||
protected AsyncConnection(Context context) {
|
||||
this(context.getBufferSupplier(), context.getBufferConsumer(), context.getSSLContext());
|
||||
}
|
||||
|
||||
protected AsyncConnection(ObjectPool<ByteBuffer> bufferPool, SSLContext sslContext) {
|
||||
this(bufferPool, bufferPool, sslContext);
|
||||
}
|
||||
|
||||
protected AsyncConnection(Supplier<ByteBuffer> bufferSupplier, Consumer<ByteBuffer> bufferConsumer, SSLContext sslContext) {
|
||||
Objects.requireNonNull(bufferSupplier);
|
||||
Objects.requireNonNull(bufferConsumer);
|
||||
this.bufferSupplier = bufferSupplier;
|
||||
this.bufferConsumer = bufferConsumer;
|
||||
this.sslContext = sslContext;
|
||||
}
|
||||
|
||||
public final long getLastReadTime() {
|
||||
return readtime;
|
||||
}
|
||||
@@ -50,6 +76,17 @@ public abstract class AsyncConnection implements AsynchronousByteChannel, AutoCl
|
||||
return writetime;
|
||||
}
|
||||
|
||||
public final int increEventing() {
|
||||
return eventing.incrementAndGet();
|
||||
}
|
||||
|
||||
public final int decreEventing() {
|
||||
return eventing.decrementAndGet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public abstract boolean isOpen();
|
||||
|
||||
public abstract boolean isTCP();
|
||||
|
||||
public abstract boolean shutdownInput();
|
||||
@@ -73,17 +110,15 @@ public abstract class AsyncConnection implements AsynchronousByteChannel, AutoCl
|
||||
public abstract void setWriteTimeoutSeconds(int writeTimeoutSeconds);
|
||||
|
||||
@Override
|
||||
public abstract Future<Integer> read(ByteBuffer dst);
|
||||
public abstract int read(ByteBuffer dst) throws IOException;
|
||||
|
||||
public abstract void read(CompletionHandler<Integer, ByteBuffer> handler);
|
||||
|
||||
public abstract void read(long timeout, TimeUnit unit, CompletionHandler<Integer, ByteBuffer> handler);
|
||||
|
||||
@Override
|
||||
public abstract <A> void read(ByteBuffer dst, A attachment, CompletionHandler<Integer, ? super A> handler);
|
||||
public abstract int write(ByteBuffer src) throws IOException;
|
||||
|
||||
public abstract <A> void read(ByteBuffer dst, long timeout, TimeUnit unit, A attachment, CompletionHandler<Integer, ? super A> handler);
|
||||
|
||||
@Override
|
||||
public abstract Future<Integer> write(ByteBuffer src);
|
||||
|
||||
@Override
|
||||
public abstract <A> void write(ByteBuffer src, A attachment, CompletionHandler<Integer, ? super A> handler);
|
||||
|
||||
public final <A> void write(ByteBuffer[] srcs, A attachment, CompletionHandler<Integer, ? super A> handler) {
|
||||
@@ -92,6 +127,36 @@ public abstract class AsyncConnection implements AsynchronousByteChannel, AutoCl
|
||||
|
||||
public abstract <A> void write(ByteBuffer[] srcs, int offset, int length, A attachment, CompletionHandler<Integer, ? super A> handler);
|
||||
|
||||
public void setReadBuffer(Buffer buffer) {
|
||||
if (this.readBuffer != null) throw new RuntimeException("repeat AsyncConnection.setReadBuffer");
|
||||
this.readBuffer = (ByteBuffer) buffer;
|
||||
}
|
||||
|
||||
public ByteBuffer pollReadBuffer() {
|
||||
ByteBuffer rs = this.readBuffer;
|
||||
if (rs != null) {
|
||||
this.readBuffer = null;
|
||||
return rs;
|
||||
}
|
||||
return bufferSupplier.get();
|
||||
}
|
||||
|
||||
public void offerBuffer(Buffer buffer) {
|
||||
if (buffer == null) return;
|
||||
bufferConsumer.accept((ByteBuffer) buffer);
|
||||
}
|
||||
|
||||
public void offerBuffer(Buffer... buffers) {
|
||||
if (buffers == null) return;
|
||||
for (Buffer buffer : buffers) {
|
||||
bufferConsumer.accept((ByteBuffer) buffer);
|
||||
}
|
||||
}
|
||||
|
||||
public ByteBuffer pollWriteBuffer() {
|
||||
return bufferSupplier.get();
|
||||
}
|
||||
|
||||
public void dispose() {//同close, 只是去掉throws IOException
|
||||
try {
|
||||
this.close();
|
||||
@@ -114,11 +179,15 @@ public abstract class AsyncConnection implements AsynchronousByteChannel, AutoCl
|
||||
livingCounter.decrementAndGet();
|
||||
livingCounter = null;
|
||||
}
|
||||
if (beforeCloseListener != null)
|
||||
if (beforeCloseListener != null) {
|
||||
try {
|
||||
beforeCloseListener.accept(this);
|
||||
} catch (Exception io) {
|
||||
}
|
||||
}
|
||||
if (this.readBuffer != null) {
|
||||
bufferConsumer.accept(this.readBuffer);
|
||||
}
|
||||
if (attributes == null) return;
|
||||
try {
|
||||
for (Object obj : attributes.values()) {
|
||||
@@ -163,6 +232,7 @@ public abstract class AsyncConnection implements AsynchronousByteChannel, AutoCl
|
||||
/**
|
||||
* 创建TCP协议客户端连接
|
||||
*
|
||||
* @param bufferPool ByteBuffer对象池
|
||||
* @param address 连接点子
|
||||
* @param group 连接AsynchronousChannelGroup
|
||||
* @param readTimeoutSeconds 读取超时秒数
|
||||
@@ -170,14 +240,31 @@ public abstract class AsyncConnection implements AsynchronousByteChannel, AutoCl
|
||||
*
|
||||
* @return 连接CompletableFuture
|
||||
*/
|
||||
public static CompletableFuture<AsyncConnection> createTCP(final AsynchronousChannelGroup group, final SocketAddress address,
|
||||
final int readTimeoutSeconds, final int writeTimeoutSeconds) {
|
||||
return createTCP(group, null, address, readTimeoutSeconds, writeTimeoutSeconds);
|
||||
public static CompletableFuture<AsyncConnection> createTCP(final ObjectPool<ByteBuffer> bufferPool, final AsynchronousChannelGroup group,
|
||||
final SocketAddress address, final int readTimeoutSeconds, final int writeTimeoutSeconds) {
|
||||
return createTCP(bufferPool, group, null, address, readTimeoutSeconds, writeTimeoutSeconds);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建TCP协议客户端连接
|
||||
*
|
||||
* @param context Context
|
||||
* @param address 连接点子
|
||||
* @param group 连接AsynchronousChannelGroup
|
||||
* @param readTimeoutSeconds 读取超时秒数
|
||||
* @param writeTimeoutSeconds 写入超时秒数
|
||||
*
|
||||
* @return 连接CompletableFuture
|
||||
*/
|
||||
public static CompletableFuture<AsyncConnection> createTCP(final Context context, final AsynchronousChannelGroup group,
|
||||
final SocketAddress address, final int readTimeoutSeconds, final int writeTimeoutSeconds) {
|
||||
return createTCP(context.getBufferSupplier(), context.getBufferConsumer(), group, context.getSSLContext(), address, readTimeoutSeconds, writeTimeoutSeconds);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建TCP协议客户端连接
|
||||
*
|
||||
* @param bufferPool ByteBuffer对象池
|
||||
* @param address 连接点子
|
||||
* @param sslContext SSLContext
|
||||
* @param group 连接AsynchronousChannelGroup
|
||||
@@ -186,7 +273,25 @@ public abstract class AsyncConnection implements AsynchronousByteChannel, AutoCl
|
||||
*
|
||||
* @return 连接CompletableFuture
|
||||
*/
|
||||
public static CompletableFuture<AsyncConnection> createTCP(final AsynchronousChannelGroup group, final SSLContext sslContext,
|
||||
public static CompletableFuture<AsyncConnection> createTCP(final ObjectPool<ByteBuffer> bufferPool, final AsynchronousChannelGroup group, final SSLContext sslContext,
|
||||
final SocketAddress address, final int readTimeoutSeconds, final int writeTimeoutSeconds) {
|
||||
return createTCP(bufferPool, bufferPool, group, sslContext, address, readTimeoutSeconds, writeTimeoutSeconds);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建TCP协议客户端连接
|
||||
*
|
||||
* @param bufferSupplier ByteBuffer生产器
|
||||
* @param bufferConsumer ByteBuffer回收器
|
||||
* @param address 连接点子
|
||||
* @param sslContext SSLContext
|
||||
* @param group 连接AsynchronousChannelGroup
|
||||
* @param readTimeoutSeconds 读取超时秒数
|
||||
* @param writeTimeoutSeconds 写入超时秒数
|
||||
*
|
||||
* @return 连接CompletableFuture
|
||||
*/
|
||||
public static CompletableFuture<AsyncConnection> createTCP(final Supplier<ByteBuffer> bufferSupplier, Consumer<ByteBuffer> bufferConsumer, final AsynchronousChannelGroup group, final SSLContext sslContext,
|
||||
final SocketAddress address, final int readTimeoutSeconds, final int writeTimeoutSeconds) {
|
||||
final CompletableFuture<AsyncConnection> future = new CompletableFuture<>();
|
||||
try {
|
||||
@@ -200,7 +305,7 @@ public abstract class AsyncConnection implements AsynchronousByteChannel, AutoCl
|
||||
channel.connect(address, null, new CompletionHandler<Void, Void>() {
|
||||
@Override
|
||||
public void completed(Void result, Void attachment) {
|
||||
future.complete(create(channel, sslContext, address, readTimeoutSeconds, writeTimeoutSeconds));
|
||||
future.complete(new TcpAioAsyncConnection(bufferSupplier, bufferConsumer, channel, sslContext, address, readTimeoutSeconds, writeTimeoutSeconds, null, null));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -214,80 +319,109 @@ public abstract class AsyncConnection implements AsynchronousByteChannel, AutoCl
|
||||
return future;
|
||||
}
|
||||
|
||||
/**
|
||||
* 通常用于 ssl socket
|
||||
*
|
||||
* @param socket Socket对象
|
||||
*
|
||||
* @return 连接对象
|
||||
*/
|
||||
public static AsyncConnection create(final Socket socket) {
|
||||
return create(socket, null, 0, 0);
|
||||
}
|
||||
|
||||
public static AsyncConnection create(final Socket socket, final SocketAddress addr0, final int readTimeoutSecond0, final int writeTimeoutSecond0) {
|
||||
return new TcpBioAsyncConnection(socket, addr0, readTimeoutSecond0, writeTimeoutSecond0, null, null);
|
||||
}
|
||||
|
||||
public static AsyncConnection create(final Socket socket, final SocketAddress addr0, final int readTimeoutSecond0,
|
||||
final int writeTimeoutSecond0, final AtomicLong livingCounter, final AtomicLong closedCounter) {
|
||||
return new TcpBioAsyncConnection(socket, addr0, readTimeoutSecond0, writeTimeoutSecond0, livingCounter, closedCounter);
|
||||
}
|
||||
|
||||
public static AsyncConnection create(final SocketChannel ch, SocketAddress addr, final Selector selector,
|
||||
// public static AsyncConnection create(final Socket socket) {
|
||||
// return create(socket, null, 0, 0);
|
||||
// }
|
||||
// public static AsyncConnection create(final Socket socket, final SocketAddress addr0, final int readTimeoutSecond0, final int writeTimeoutSecond0) {
|
||||
// return new TcpBioAsyncConnection(socket, addr0, readTimeoutSecond0, writeTimeoutSecond0, null, null);
|
||||
// }
|
||||
//
|
||||
// public static AsyncConnection create(final Socket socket, final SocketAddress addr0, final int readTimeoutSecond0,
|
||||
// final int writeTimeoutSecond0, final AtomicLong livingCounter, final AtomicLong closedCounter) {
|
||||
// return new TcpBioAsyncConnection(socket, addr0, readTimeoutSecond0, writeTimeoutSecond0, livingCounter, closedCounter);
|
||||
// }
|
||||
//
|
||||
// public static AsyncConnection create(final SocketChannel ch, SocketAddress addr, final Selector selector,
|
||||
// final int readTimeoutSeconds0, final int writeTimeoutSeconds0) {
|
||||
// return new TcpNioAsyncConnection(ch, addr, selector, readTimeoutSeconds0, writeTimeoutSeconds0, null, null);
|
||||
// }
|
||||
//
|
||||
// public static AsyncConnection create(final SocketChannel ch, final SocketAddress addr0, final Selector selector, final Context context) {
|
||||
// return new TcpNioAsyncConnection(ch, addr0, selector, context.readTimeoutSeconds, context.writeTimeoutSeconds, null, null);
|
||||
// }
|
||||
//
|
||||
// public static AsyncConnection create(final SocketChannel ch, SocketAddress addr, final Selector selector,
|
||||
// final int readTimeoutSeconds0, final int writeTimeoutSeconds0,
|
||||
// final AtomicLong livingCounter, final AtomicLong closedCounter) {
|
||||
// return new TcpNioAsyncConnection(ch, addr, selector, readTimeoutSeconds0, writeTimeoutSeconds0, livingCounter, closedCounter);
|
||||
// }
|
||||
public static AsyncConnection create(final ObjectPool<ByteBuffer> bufferPool, final DatagramChannel ch,
|
||||
SocketAddress addr, final boolean client0,
|
||||
final int readTimeoutSeconds0, final int writeTimeoutSeconds0) {
|
||||
return new TcpNioAsyncConnection(ch, addr, selector, readTimeoutSeconds0, writeTimeoutSeconds0, null, null);
|
||||
return new UdpBioAsyncConnection(bufferPool, bufferPool, ch, null, addr, client0, readTimeoutSeconds0, writeTimeoutSeconds0, null, null);
|
||||
}
|
||||
|
||||
public static AsyncConnection create(final SocketChannel ch, final SocketAddress addr0, final Selector selector, final Context context) {
|
||||
return new TcpNioAsyncConnection(ch, addr0, selector, context.readTimeoutSeconds, context.writeTimeoutSeconds, null, null);
|
||||
}
|
||||
|
||||
public static AsyncConnection create(final SocketChannel ch, SocketAddress addr, final Selector selector,
|
||||
public static AsyncConnection create(final ObjectPool<ByteBuffer> bufferPool, final DatagramChannel ch,
|
||||
SocketAddress addr, final boolean client0,
|
||||
final int readTimeoutSeconds0, final int writeTimeoutSeconds0,
|
||||
final AtomicLong livingCounter, final AtomicLong closedCounter) {
|
||||
return new TcpNioAsyncConnection(ch, addr, selector, readTimeoutSeconds0, writeTimeoutSeconds0, livingCounter, closedCounter);
|
||||
return new UdpBioAsyncConnection(bufferPool, bufferPool, ch, null, addr, client0, readTimeoutSeconds0, writeTimeoutSeconds0, livingCounter, closedCounter);
|
||||
}
|
||||
|
||||
public static AsyncConnection create(final DatagramChannel ch, SocketAddress addr,
|
||||
final boolean client0, final int readTimeoutSeconds0, final int writeTimeoutSeconds0) {
|
||||
return new UdpBioAsyncConnection(ch, addr, client0, readTimeoutSeconds0, writeTimeoutSeconds0, null, null);
|
||||
public static AsyncConnection create(final ObjectPool<ByteBuffer> bufferPool, final DatagramChannel ch, SSLContext sslContext,
|
||||
SocketAddress addr, final boolean client0,
|
||||
final int readTimeoutSeconds0, final int writeTimeoutSeconds0) {
|
||||
return new UdpBioAsyncConnection(bufferPool, bufferPool, ch, sslContext, addr, client0, readTimeoutSeconds0, writeTimeoutSeconds0, null, null);
|
||||
}
|
||||
|
||||
public static AsyncConnection create(final DatagramChannel ch, SocketAddress addr,
|
||||
final boolean client0, final int readTimeoutSeconds0, final int writeTimeoutSeconds0,
|
||||
public static AsyncConnection create(final ObjectPool<ByteBuffer> bufferPool, final DatagramChannel ch, SSLContext sslContext,
|
||||
SocketAddress addr, final boolean client0,
|
||||
final int readTimeoutSeconds0, final int writeTimeoutSeconds0,
|
||||
final AtomicLong livingCounter, final AtomicLong closedCounter) {
|
||||
return new UdpBioAsyncConnection(ch, addr, client0, readTimeoutSeconds0, writeTimeoutSeconds0, livingCounter, closedCounter);
|
||||
return new UdpBioAsyncConnection(bufferPool, bufferPool, ch, sslContext, addr, client0, readTimeoutSeconds0, writeTimeoutSeconds0, livingCounter, closedCounter);
|
||||
}
|
||||
|
||||
public static AsyncConnection create(final AsynchronousSocketChannel ch) {
|
||||
return create(ch, null, 0, 0);
|
||||
public static AsyncConnection create(final Context context, final AsynchronousSocketChannel ch) {
|
||||
return create(context, ch, (SocketAddress) null, 0, 0);
|
||||
}
|
||||
|
||||
public static AsyncConnection create(final AsynchronousSocketChannel ch, final SocketAddress addr0, final int readTimeoutSeconds, final int writeTimeoutSeconds) {
|
||||
return new TcpAioAsyncConnection(ch, null, addr0, readTimeoutSeconds, writeTimeoutSeconds, null, null);
|
||||
public static AsyncConnection create(final Context context, final AsynchronousSocketChannel ch,
|
||||
final SocketAddress addr0, final AtomicLong livingCounter, final AtomicLong closedCounter) {
|
||||
return new TcpAioAsyncConnection(context.getBufferSupplier(), context.getBufferConsumer(), ch, context.sslContext, addr0, context.readTimeoutSeconds, context.writeTimeoutSeconds, livingCounter, closedCounter);
|
||||
}
|
||||
|
||||
public static AsyncConnection create(final AsynchronousSocketChannel ch, SSLContext sslContext, final SocketAddress addr0, final int readTimeoutSeconds, final int writeTimeoutSeconds) {
|
||||
return new TcpAioAsyncConnection(ch, sslContext, addr0, readTimeoutSeconds, writeTimeoutSeconds, null, null);
|
||||
public static AsyncConnection create(final Context context, final AsynchronousSocketChannel ch,
|
||||
final SocketAddress addr0, final int readTimeoutSeconds, final int writeTimeoutSeconds) {
|
||||
return new TcpAioAsyncConnection(context.getBufferSupplier(), context.getBufferConsumer(), ch, null, addr0, readTimeoutSeconds, writeTimeoutSeconds, null, null);
|
||||
}
|
||||
|
||||
public static AsyncConnection create(final AsynchronousSocketChannel ch, final SocketAddress addr0, final Context context) {
|
||||
return new TcpAioAsyncConnection(ch, context.sslContext, addr0, context.readTimeoutSeconds, context.writeTimeoutSeconds, null, null);
|
||||
public static AsyncConnection create(final Context context, final AsynchronousSocketChannel ch, SSLContext sslContext,
|
||||
final SocketAddress addr0, final int readTimeoutSeconds, final int writeTimeoutSeconds) {
|
||||
return new TcpAioAsyncConnection(context.getBufferSupplier(), context.getBufferConsumer(), ch, sslContext, addr0, readTimeoutSeconds, writeTimeoutSeconds, null, null);
|
||||
}
|
||||
|
||||
public static AsyncConnection create(final AsynchronousSocketChannel ch, final SocketAddress addr0, final int readTimeoutSeconds,
|
||||
final int writeTimeoutSeconds, final AtomicLong livingCounter, final AtomicLong closedCounter) {
|
||||
return new TcpAioAsyncConnection(ch, null, addr0, readTimeoutSeconds, writeTimeoutSeconds, livingCounter, closedCounter);
|
||||
public static AsyncConnection create(final Context context, final AsynchronousSocketChannel ch,
|
||||
final SocketAddress addr0, final int readTimeoutSeconds, final int writeTimeoutSeconds, final AtomicLong livingCounter, final AtomicLong closedCounter) {
|
||||
return new TcpAioAsyncConnection(context.getBufferSupplier(), context.getBufferConsumer(), ch, null, addr0, readTimeoutSeconds, writeTimeoutSeconds, livingCounter, closedCounter);
|
||||
}
|
||||
|
||||
public static AsyncConnection create(final AsynchronousSocketChannel ch, SSLContext sslContext, final SocketAddress addr0, final int readTimeoutSeconds,
|
||||
final int writeTimeoutSeconds, final AtomicLong livingCounter, final AtomicLong closedCounter) {
|
||||
return new TcpAioAsyncConnection(ch, sslContext, addr0, readTimeoutSeconds, writeTimeoutSeconds, livingCounter, closedCounter);
|
||||
public static AsyncConnection create(final Context context, final AsynchronousSocketChannel ch, SSLContext sslContext,
|
||||
final SocketAddress addr0, final int readTimeoutSeconds, final int writeTimeoutSeconds, final AtomicLong livingCounter, final AtomicLong closedCounter) {
|
||||
return new TcpAioAsyncConnection(context.getBufferSupplier(), context.getBufferConsumer(), ch, sslContext, addr0, readTimeoutSeconds, writeTimeoutSeconds, livingCounter, closedCounter);
|
||||
}
|
||||
|
||||
public static AsyncConnection create(final AsynchronousSocketChannel ch, final SocketAddress addr0,
|
||||
final Context context, final AtomicLong livingCounter, final AtomicLong closedCounter) {
|
||||
return new TcpAioAsyncConnection(ch, context.sslContext, addr0, context.readTimeoutSeconds, context.writeTimeoutSeconds, livingCounter, closedCounter);
|
||||
public static AsyncConnection create(final ObjectPool<ByteBuffer> bufferPool, final AsynchronousSocketChannel ch) {
|
||||
return create(bufferPool, ch, null, 0, 0);
|
||||
}
|
||||
|
||||
public static AsyncConnection create(final ObjectPool<ByteBuffer> bufferPool, final AsynchronousSocketChannel ch,
|
||||
final SocketAddress addr0, final int readTimeoutSeconds, final int writeTimeoutSeconds) {
|
||||
return new TcpAioAsyncConnection(bufferPool, bufferPool, ch, null, addr0, readTimeoutSeconds, writeTimeoutSeconds, null, null);
|
||||
}
|
||||
|
||||
public static AsyncConnection create(final ObjectPool<ByteBuffer> bufferPool, final AsynchronousSocketChannel ch, SSLContext sslContext,
|
||||
final SocketAddress addr0, final int readTimeoutSeconds, final int writeTimeoutSeconds) {
|
||||
return new TcpAioAsyncConnection(bufferPool, bufferPool, ch, sslContext, addr0, readTimeoutSeconds, writeTimeoutSeconds, null, null);
|
||||
}
|
||||
|
||||
public static AsyncConnection create(final ObjectPool<ByteBuffer> bufferPool, final AsynchronousSocketChannel ch,
|
||||
final SocketAddress addr0, final int readTimeoutSeconds, final int writeTimeoutSeconds, final AtomicLong livingCounter, final AtomicLong closedCounter) {
|
||||
return new TcpAioAsyncConnection(bufferPool, bufferPool, ch, null, addr0, readTimeoutSeconds, writeTimeoutSeconds, livingCounter, closedCounter);
|
||||
}
|
||||
|
||||
public static AsyncConnection create(final ObjectPool<ByteBuffer> bufferPool, final AsynchronousSocketChannel ch, SSLContext sslContext,
|
||||
final SocketAddress addr0, final int readTimeoutSeconds, final int writeTimeoutSeconds, final AtomicLong livingCounter, final AtomicLong closedCounter) {
|
||||
return new TcpAioAsyncConnection(bufferPool, bufferPool, ch, sslContext, addr0, readTimeoutSeconds, writeTimeoutSeconds, livingCounter, closedCounter);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ package org.redkale.net;
|
||||
import java.net.*;
|
||||
import java.nio.*;
|
||||
import java.nio.charset.*;
|
||||
import java.util.Collection;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.function.*;
|
||||
import java.util.logging.*;
|
||||
@@ -47,27 +48,6 @@ public class Context {
|
||||
//服务的根Servlet
|
||||
protected final PrepareServlet prepare;
|
||||
|
||||
//服务的监听地址
|
||||
private final InetSocketAddress address;
|
||||
|
||||
//字符集
|
||||
protected final Charset charset;
|
||||
|
||||
//最大连接数, 为0表示没限制
|
||||
protected final int maxconns;
|
||||
|
||||
//请求内容的大小上限, 默认64K
|
||||
protected final int maxbody;
|
||||
|
||||
//keep alive IO读取的超时时间
|
||||
protected final int aliveTimeoutSeconds;
|
||||
|
||||
//IO读取的超时时间
|
||||
protected final int readTimeoutSeconds;
|
||||
|
||||
//IO写入的超时时间
|
||||
protected final int writeTimeoutSeconds;
|
||||
|
||||
//日志Logger
|
||||
protected final Logger logger;
|
||||
|
||||
@@ -80,6 +60,27 @@ public class Context {
|
||||
//依赖注入工厂类
|
||||
protected final ResourceFactory resourceFactory;
|
||||
|
||||
//最大连接数, 为0表示没限制
|
||||
protected int maxconns;
|
||||
|
||||
//请求内容的大小上限, 默认64K
|
||||
protected int maxbody;
|
||||
|
||||
//keep alive IO读取的超时时间
|
||||
protected int aliveTimeoutSeconds;
|
||||
|
||||
//IO读取的超时时间
|
||||
protected int readTimeoutSeconds;
|
||||
|
||||
//IO写入的超时时间
|
||||
protected int writeTimeoutSeconds;
|
||||
|
||||
//服务的监听地址
|
||||
protected InetSocketAddress address;
|
||||
|
||||
//字符集
|
||||
protected Charset charset;
|
||||
|
||||
public Context(ContextConfig config) {
|
||||
this(config.serverStartTime, config.logger, config.executor, config.sslContext,
|
||||
config.bufferCapacity, config.bufferPool, config.responsePool, config.maxconns, config.maxbody,
|
||||
@@ -147,6 +148,14 @@ public class Context {
|
||||
executor.execute(r);
|
||||
}
|
||||
|
||||
public int getCorePoolSize() {
|
||||
return executor.getCorePoolSize();
|
||||
}
|
||||
|
||||
public ThreadFactory getThreadFactory() {
|
||||
return executor.getThreadFactory();
|
||||
}
|
||||
|
||||
public int getBufferCapacity() {
|
||||
return bufferCapacity;
|
||||
}
|
||||
@@ -155,7 +164,7 @@ public class Context {
|
||||
return bufferPool;
|
||||
}
|
||||
|
||||
protected Consumer<ByteBuffer> getBufferConsumer() {
|
||||
public Consumer<ByteBuffer> getBufferConsumer() {
|
||||
return bufferPool;
|
||||
}
|
||||
|
||||
@@ -163,11 +172,18 @@ public class Context {
|
||||
return bufferPool.get();
|
||||
}
|
||||
|
||||
protected void offerBuffer(ByteBuffer buffer) {
|
||||
public void offerBuffer(ByteBuffer buffer) {
|
||||
bufferPool.accept(buffer);
|
||||
}
|
||||
|
||||
protected void offerBuffer(ByteBuffer... buffers) {
|
||||
public void offerBuffer(ByteBuffer... buffers) {
|
||||
if (buffers == null) return;
|
||||
for (ByteBuffer buffer : buffers) {
|
||||
bufferPool.accept(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
public void offerBuffer(Collection<ByteBuffer> buffers) {
|
||||
if (buffers == null) return;
|
||||
for (ByteBuffer buffer : buffers) {
|
||||
bufferPool.accept(buffer);
|
||||
|
||||
@@ -9,6 +9,7 @@ import java.io.IOException;
|
||||
import java.nio.*;
|
||||
import java.nio.channels.*;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.logging.*;
|
||||
import org.redkale.util.*;
|
||||
|
||||
@@ -41,26 +42,24 @@ public class PrepareRunner implements Runnable {
|
||||
@Override
|
||||
public void run() {
|
||||
final boolean keepalive = response != null;
|
||||
final PrepareServlet prepare = context.prepare;
|
||||
final ObjectPool<? extends Response> responsePool = context.responsePool;
|
||||
if (data != null) { //BIO模式的UDP连接创建AsyncConnection时已经获取到ByteBuffer数据了
|
||||
if (response == null) response = responsePool.get();
|
||||
try {
|
||||
response.init(channel);
|
||||
prepare.prepare(data, response.request, response);
|
||||
codec(data, response);
|
||||
} catch (Throwable t) {
|
||||
context.logger.log(Level.WARNING, "prepare servlet abort, forece to close channel ", t);
|
||||
context.logger.log(Level.WARNING, "prepare servlet abort, force to close channel ", t);
|
||||
response.finish(true);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (response == null) response = responsePool.get();
|
||||
final ByteBuffer buffer = response.request.pollReadBuffer();
|
||||
try {
|
||||
channel.read(buffer, keepalive ? context.getAliveTimeoutSeconds() : 0, TimeUnit.SECONDS, null,
|
||||
new CompletionHandler<Integer, Void>() {
|
||||
channel.read(keepalive ? context.getAliveTimeoutSeconds() : context.getReadTimeoutSeconds(), TimeUnit.SECONDS,
|
||||
new CompletionHandler<Integer, ByteBuffer>() {
|
||||
@Override
|
||||
public void completed(Integer count, Void attachment1) {
|
||||
public void completed(Integer count, ByteBuffer buffer) {
|
||||
if (count < 1) {
|
||||
response.request.offerReadBuffer(buffer);
|
||||
channel.dispose();// response.init(channel); 在调用之前异常
|
||||
@@ -75,39 +74,90 @@ public class PrepareRunner implements Runnable {
|
||||
// System.println(new String(bs));
|
||||
// }
|
||||
buffer.flip();
|
||||
response.init(channel);
|
||||
try {
|
||||
prepare.prepare(buffer, response.request, response);
|
||||
response.init(channel);
|
||||
codec(buffer, response);
|
||||
} catch (Throwable t) { //此处不可 context.offerBuffer(buffer); 以免prepare.prepare内部异常导致重复 offerBuffer
|
||||
context.logger.log(Level.WARNING, "prepare servlet abort, forece to close channel ", t);
|
||||
context.logger.log(Level.WARNING, "prepare servlet abort, force to close channel ", t);
|
||||
response.finish(true);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void failed(Throwable exc, Void attachment2) {
|
||||
public void failed(Throwable exc, ByteBuffer buffer) {
|
||||
response.request.offerReadBuffer(buffer);
|
||||
channel.dispose();// response.init(channel); 在调用之前异常
|
||||
response.removeChannel();
|
||||
response.finish(true);
|
||||
if (exc != null && context.logger.isLoggable(Level.FINEST)) {
|
||||
context.logger.log(Level.FINEST, "Servlet Handler read channel erroneous, forece to close channel ", exc);
|
||||
context.logger.log(Level.FINEST, "Servlet Handler read channel erroneous, force to close channel ", exc);
|
||||
}
|
||||
}
|
||||
});
|
||||
} catch (Exception te) {
|
||||
response.request.offerReadBuffer(buffer);
|
||||
channel.dispose();// response.init(channel); 在调用之前异常
|
||||
response.removeChannel();
|
||||
response.finish(true);
|
||||
if (te != null && context.logger.isLoggable(Level.FINEST)) {
|
||||
context.logger.log(Level.FINEST, "Servlet read channel erroneous, forece to close channel ", te);
|
||||
context.logger.log(Level.FINEST, "Servlet read channel erroneous, force to close channel ", te);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void prepare(ByteBuffer buffer, Request request, Response response) throws IOException {
|
||||
context.prepare.prepare(buffer, request, response);
|
||||
protected void codec(final ByteBuffer buffer, final Response response) throws IOException {
|
||||
final Request request = response.request;
|
||||
final PrepareServlet preparer = context.prepare;
|
||||
preparer.executeCounter.incrementAndGet();
|
||||
final int rs = request.readHeader(buffer);
|
||||
if (rs < 0) { //表示数据格式不正确
|
||||
channel.offerBuffer(buffer);
|
||||
if (rs != Integer.MIN_VALUE) preparer.illRequestCounter.incrementAndGet();
|
||||
response.finish(true);
|
||||
} else if (rs == 0) {
|
||||
if (buffer.hasRemaining()) {
|
||||
request.setMoredata(buffer);
|
||||
} else {
|
||||
response.request.offerReadBuffer(buffer);
|
||||
}
|
||||
preparer.prepare(request, response);
|
||||
} else {
|
||||
buffer.clear();
|
||||
channel.setReadBuffer(buffer);
|
||||
final AtomicInteger ai = new AtomicInteger(rs);
|
||||
channel.read(new CompletionHandler<Integer, ByteBuffer>() {
|
||||
|
||||
@Override
|
||||
public void completed(Integer result, ByteBuffer attachment) {
|
||||
attachment.flip();
|
||||
ai.addAndGet(-request.readBody(attachment));
|
||||
if (ai.get() > 0) {
|
||||
attachment.clear();
|
||||
channel.setReadBuffer(attachment);
|
||||
channel.read(this);
|
||||
} else {
|
||||
if (attachment.hasRemaining()) {
|
||||
request.setMoredata(attachment);
|
||||
} else {
|
||||
response.request.offerReadBuffer(attachment);
|
||||
}
|
||||
try {
|
||||
preparer.prepare(request, response);
|
||||
} catch (Throwable t) { //此处不可 context.offerBuffer(buffer); 以免preparer.prepare内部异常导致重复 offerBuffer
|
||||
context.logger.log(Level.WARNING, "prepare servlet abort, force to close channel ", t);
|
||||
response.finish(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void failed(Throwable exc, ByteBuffer attachment) {
|
||||
preparer.illRequestCounter.incrementAndGet();
|
||||
response.request.offerReadBuffer(attachment);
|
||||
response.finish(true);
|
||||
if (exc != null) request.context.logger.log(Level.FINER, "Servlet read channel erroneous, force to close channel ", exc);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
protected void initResponse(Response response, AsyncConnection channel) {
|
||||
|
||||
@@ -6,12 +6,9 @@
|
||||
package org.redkale.net;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.*;
|
||||
import java.nio.channels.*;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.atomic.*;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.logging.*;
|
||||
import org.redkale.util.*;
|
||||
|
||||
/**
|
||||
@@ -210,55 +207,11 @@ public abstract class PrepareServlet<K extends Serializable, C extends Context,
|
||||
@SuppressWarnings("unchecked")
|
||||
public abstract void addServlet(S servlet, Object attachment, AnyValue conf, K... mappings);
|
||||
|
||||
public void prepare(final ByteBuffer buffer, final R request, final P response) throws IOException {
|
||||
executeCounter.incrementAndGet();
|
||||
final int rs = request.readHeader(buffer);
|
||||
if (rs < 0) {
|
||||
request.offerReadBuffer(buffer);
|
||||
if (rs != Integer.MIN_VALUE) illRequestCounter.incrementAndGet();
|
||||
response.finish(true);
|
||||
} else if (rs == 0) {
|
||||
request.offerReadBuffer(buffer);
|
||||
request.prepare();
|
||||
response.filter = this.headFilter;
|
||||
response.servlet = this;
|
||||
response.nextEvent();
|
||||
} else {
|
||||
buffer.clear();
|
||||
final AtomicInteger ai = new AtomicInteger(rs);
|
||||
request.channel.read(buffer, buffer, new CompletionHandler<Integer, ByteBuffer>() {
|
||||
|
||||
@Override
|
||||
public void completed(Integer result, ByteBuffer attachment) {
|
||||
buffer.flip();
|
||||
ai.addAndGet(-request.readBody(buffer));
|
||||
if (ai.get() > 0) {
|
||||
buffer.clear();
|
||||
request.channel.read(buffer, buffer, this);
|
||||
} else {
|
||||
request.offerReadBuffer(buffer);
|
||||
request.prepare();
|
||||
try {
|
||||
response.filter = PrepareServlet.this.headFilter;
|
||||
response.servlet = PrepareServlet.this;
|
||||
response.nextEvent();
|
||||
} catch (Exception e) {
|
||||
illRequestCounter.incrementAndGet();
|
||||
response.finish(true);
|
||||
request.context.logger.log(Level.WARNING, "prepare servlet abort, forece to close channel ", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void failed(Throwable exc, ByteBuffer attachment) {
|
||||
illRequestCounter.incrementAndGet();
|
||||
request.offerReadBuffer(buffer);
|
||||
response.finish(true);
|
||||
if (exc != null) request.context.logger.log(Level.FINER, "Servlet read channel erroneous, forece to close channel ", exc);
|
||||
}
|
||||
});
|
||||
}
|
||||
public final void prepare(final R request, final P response) throws IOException {
|
||||
request.prepare();
|
||||
response.filter = this.headFilter;
|
||||
response.servlet = this;
|
||||
response.nextEvent();
|
||||
}
|
||||
|
||||
protected AnyValue getServletConf(Servlet servlet) {
|
||||
|
||||
@@ -73,13 +73,13 @@ public abstract class ProtocolServer {
|
||||
} else if ("aio".equalsIgnoreCase(netimpl)) {
|
||||
return new TcpAioProtocolServer(context);
|
||||
} else if ("nio".equalsIgnoreCase(netimpl)) {
|
||||
return new TcpNioProtocolServer(context);
|
||||
return null;// return new TcpNioProtocolServer(context);
|
||||
}
|
||||
} else if ("UDP".equalsIgnoreCase(protocol)) {
|
||||
if (netimpl == null || netimpl.isEmpty()) {
|
||||
return new UdpBioProtocolServer(context);
|
||||
return null;// return new UdpBioProtocolServer(context);
|
||||
} else if ("bio".equalsIgnoreCase(netimpl)) {
|
||||
return new UdpBioProtocolServer(context);
|
||||
return null;// return new UdpBioProtocolServer(context);
|
||||
}
|
||||
} else if (netimpl == null || netimpl.isEmpty()) {
|
||||
throw new RuntimeException("ProtocolServer not support protocol " + protocol);
|
||||
|
||||
@@ -31,6 +31,10 @@ public abstract class Request<C extends Context> {
|
||||
|
||||
protected boolean keepAlive;
|
||||
|
||||
protected boolean more; //pipeline模式
|
||||
|
||||
protected ByteBuffer moredata; //pipeline模式
|
||||
|
||||
protected AsyncConnection channel;
|
||||
|
||||
protected ByteBuffer readBuffer;
|
||||
@@ -50,6 +54,16 @@ public abstract class Request<C extends Context> {
|
||||
this.jsonConvert = context.getJsonConvert();
|
||||
}
|
||||
|
||||
protected void setMoredata(ByteBuffer buffer) {
|
||||
this.moredata = buffer;
|
||||
}
|
||||
|
||||
protected ByteBuffer removeMoredata() {
|
||||
ByteBuffer rs = this.moredata;
|
||||
this.moredata = null;
|
||||
return rs;
|
||||
}
|
||||
|
||||
protected ByteBuffer pollReadBuffer() {
|
||||
ByteBuffer buffer = this.readBuffer;
|
||||
this.readBuffer = null;
|
||||
@@ -90,6 +104,8 @@ public abstract class Request<C extends Context> {
|
||||
protected void recycle() {
|
||||
createtime = 0;
|
||||
keepAlive = false;
|
||||
more = false;
|
||||
moredata = null;
|
||||
attributes.clear();
|
||||
channel = null; // close it by response
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ public abstract class Response<C extends Context, R extends Request<C>> {
|
||||
|
||||
protected ByteBuffer writeBodyBuffer;
|
||||
|
||||
private boolean inited = true;
|
||||
private volatile boolean inited = true;
|
||||
|
||||
protected Object output; //输出的结果对象
|
||||
|
||||
@@ -251,7 +251,8 @@ public abstract class Response<C extends Context, R extends Request<C>> {
|
||||
}
|
||||
this.recycleListener = null;
|
||||
}
|
||||
if (request.keepAlive && channel != null) {
|
||||
if (request.more) removeChannel();
|
||||
if (request.keepAlive && !request.more && channel != null) {
|
||||
if (channel.isOpen()) {
|
||||
AsyncConnection conn = removeChannel();
|
||||
this.recycle();
|
||||
@@ -279,24 +280,44 @@ public abstract class Response<C extends Context, R extends Request<C>> {
|
||||
|
||||
public void finish(ByteBuffer buffer) {
|
||||
if (!this.inited) return; //避免重复关闭
|
||||
this.channel.write(buffer, buffer, finishHandler);
|
||||
ByteBuffer data = this.request.removeMoredata();
|
||||
final AsyncConnection conn = this.channel;
|
||||
final boolean more = data != null && this.request.keepAlive;
|
||||
this.request.more = more;
|
||||
conn.write(buffer, buffer, finishHandler);
|
||||
if (more) new PrepareRunner(this.context, conn, data, null).run();
|
||||
}
|
||||
|
||||
public void finish(boolean kill, ByteBuffer buffer) {
|
||||
if (!this.inited) return; //避免重复关闭
|
||||
if (kill) refuseAlive();
|
||||
this.channel.write(buffer, buffer, finishHandler);
|
||||
ByteBuffer data = this.request.removeMoredata();
|
||||
final AsyncConnection conn = this.channel;
|
||||
final boolean more = data != null && this.request.keepAlive;
|
||||
this.request.more = more;
|
||||
conn.write(buffer, buffer, finishHandler);
|
||||
if (more) new PrepareRunner(this.context, conn, data, null).run();
|
||||
}
|
||||
|
||||
public void finish(ByteBuffer... buffers) {
|
||||
if (!this.inited) return; //避免重复关闭
|
||||
this.channel.write(buffers, buffers, finishHandler2);
|
||||
final AsyncConnection conn = this.channel;
|
||||
ByteBuffer data = this.request.removeMoredata();
|
||||
final boolean more = data != null && this.request.keepAlive;
|
||||
this.request.more = more;
|
||||
conn.write(buffers, buffers, finishHandler2);
|
||||
if (more) new PrepareRunner(this.context, conn, data, null).run();
|
||||
}
|
||||
|
||||
public void finish(boolean kill, ByteBuffer... buffers) {
|
||||
if (!this.inited) return; //避免重复关闭
|
||||
if (kill) refuseAlive();
|
||||
this.channel.write(buffers, buffers, finishHandler2);
|
||||
final AsyncConnection conn = this.channel;
|
||||
ByteBuffer data = this.request.removeMoredata();
|
||||
final boolean more = data != null && this.request.keepAlive;
|
||||
this.request.more = more;
|
||||
conn.write(buffers, buffers, finishHandler2);
|
||||
if (more) new PrepareRunner(this.context, conn, data, null).run();
|
||||
}
|
||||
|
||||
protected <A> void send(final ByteBuffer buffer, final A attachment, final CompletionHandler<Integer, A> handler) {
|
||||
|
||||
@@ -12,8 +12,9 @@ import java.text.*;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.logging.Logger;
|
||||
import java.util.logging.*;
|
||||
import javax.net.ssl.SSLContext;
|
||||
import org.redkale.net.Filter;
|
||||
import org.redkale.util.*;
|
||||
|
||||
/**
|
||||
@@ -287,6 +288,74 @@ public abstract class Server<K extends Serializable, C extends Context, R extend
|
||||
+ ", started in " + (System.currentTimeMillis() - context.getServerStartTime()) + " ms");
|
||||
}
|
||||
|
||||
public void changeAddress(final InetSocketAddress addr) throws IOException {
|
||||
long s = System.currentTimeMillis();
|
||||
Objects.requireNonNull(addr);
|
||||
final InetSocketAddress oldAddress = context.address;
|
||||
final ProtocolServer oldServerChannel = this.serverChannel;
|
||||
context.address = addr;
|
||||
ProtocolServer newServerChannel = null;
|
||||
try {
|
||||
newServerChannel = ProtocolServer.create(this.protocol, context, this.serverClassLoader, config == null ? null : config.getValue("netimpl"));
|
||||
newServerChannel.open(config);
|
||||
newServerChannel.bind(addr, backlog);
|
||||
newServerChannel.accept();
|
||||
} catch (IOException e) {
|
||||
context.address = oldAddress;
|
||||
throw e;
|
||||
}
|
||||
this.address = context.address;
|
||||
this.serverChannel = newServerChannel;
|
||||
final String threadName = "[" + Thread.currentThread().getName() + "] ";
|
||||
logger.info(threadName + this.getClass().getSimpleName() + ("TCP".equalsIgnoreCase(protocol) ? "" : ("." + protocol))
|
||||
+ " change address listen: " + address + ", started in " + (System.currentTimeMillis() - s) + " ms");
|
||||
if (oldServerChannel != null) {
|
||||
new Thread() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
Thread.sleep(10_000);
|
||||
oldServerChannel.close();
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.WARNING, "Server.changeInetSocketAddress(addr=" + addr + ") error", e);
|
||||
}
|
||||
}
|
||||
}.start();
|
||||
}
|
||||
}
|
||||
|
||||
public void changeMaxconns(final int newmaxconns) {
|
||||
this.maxconns = newmaxconns;
|
||||
if (this.context != null) this.context.maxconns = newmaxconns;
|
||||
if (this.serverChannel != null) this.serverChannel.maxconns = newmaxconns;
|
||||
}
|
||||
|
||||
public void changeCharset(final Charset newcharset) {
|
||||
this.charset = newcharset;
|
||||
if (this.context != null) this.context.charset = newcharset;
|
||||
}
|
||||
|
||||
public void changeMaxbody(final int newmaxbody) {
|
||||
this.maxbody = newmaxbody;
|
||||
if (this.context != null) this.context.maxbody = newmaxbody;
|
||||
}
|
||||
|
||||
public void changeReadTimeoutSeconds(final int newReadTimeoutSeconds) {
|
||||
this.readTimeoutSeconds = newReadTimeoutSeconds;
|
||||
if (this.context != null) this.context.readTimeoutSeconds = newReadTimeoutSeconds;
|
||||
}
|
||||
|
||||
public void changeWriteTimeoutSeconds(final int newWriteTimeoutSeconds) {
|
||||
this.writeTimeoutSeconds = newWriteTimeoutSeconds;
|
||||
if (this.context != null) this.context.writeTimeoutSeconds = newWriteTimeoutSeconds;
|
||||
}
|
||||
|
||||
public void changeAliveTimeoutSeconds(final int newAliveTimeoutSeconds) {
|
||||
this.aliveTimeoutSeconds = newAliveTimeoutSeconds;
|
||||
if (this.context != null) this.context.aliveTimeoutSeconds = newAliveTimeoutSeconds;
|
||||
}
|
||||
|
||||
protected abstract C createContext();
|
||||
|
||||
public void shutdown() throws IOException {
|
||||
@@ -412,7 +481,7 @@ public abstract class Server<K extends Serializable, C extends Context, R extend
|
||||
classLoader.addURL(url);
|
||||
}
|
||||
List<URL> list = new ArrayList<>(set);
|
||||
Collections.sort(list, (URL o1, URL o2) -> o1.getFile().compareTo(o2.getFile()));
|
||||
list.sort((URL o1, URL o2) -> o1.getFile().compareTo(o2.getFile()));
|
||||
return list.toArray(new URL[list.size()]);
|
||||
}
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ import java.nio.channels.*;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import java.util.function.*;
|
||||
import javax.net.ssl.SSLContext;
|
||||
|
||||
/**
|
||||
@@ -35,11 +36,12 @@ public class TcpAioAsyncConnection extends AsyncConnection {
|
||||
|
||||
private BlockingQueue<WriteEntry> writeQueue;
|
||||
|
||||
public TcpAioAsyncConnection(final AsynchronousSocketChannel ch, SSLContext sslContext,
|
||||
final SocketAddress addr0, final int readTimeoutSeconds, final int writeTimeoutSeconds,
|
||||
public TcpAioAsyncConnection(Supplier<ByteBuffer> bufferSupplier, Consumer<ByteBuffer> bufferConsumer,
|
||||
final AsynchronousSocketChannel ch, final SSLContext sslContext, final SocketAddress addr0,
|
||||
final int readTimeoutSeconds, final int writeTimeoutSeconds,
|
||||
final AtomicLong livingCounter, final AtomicLong closedCounter) {
|
||||
super(bufferSupplier, bufferConsumer, sslContext);
|
||||
this.channel = ch;
|
||||
this.sslContext = sslContext;
|
||||
this.readTimeoutSeconds = readTimeoutSeconds;
|
||||
this.writeTimeoutSeconds = writeTimeoutSeconds;
|
||||
SocketAddress addr = addr0;
|
||||
@@ -91,24 +93,38 @@ public class TcpAioAsyncConnection extends AsyncConnection {
|
||||
}
|
||||
|
||||
@Override
|
||||
public <A> void read(ByteBuffer dst, A attachment, CompletionHandler<Integer, ? super A> handler) {
|
||||
public void read(CompletionHandler<Integer, ByteBuffer> handler) {
|
||||
this.readtime = System.currentTimeMillis();
|
||||
ByteBuffer dst = pollReadBuffer();
|
||||
if (readTimeoutSeconds > 0) {
|
||||
channel.read(dst, readTimeoutSeconds, TimeUnit.SECONDS, attachment, handler);
|
||||
channel.read(dst, readTimeoutSeconds, TimeUnit.SECONDS, dst, handler);
|
||||
} else {
|
||||
channel.read(dst, attachment, handler);
|
||||
channel.read(dst, dst, handler);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <A> void read(ByteBuffer dst, long timeout, TimeUnit unit, A attachment, CompletionHandler<Integer, ? super A> handler) {
|
||||
public void read(long timeout, TimeUnit unit, CompletionHandler<Integer, ByteBuffer> handler) {
|
||||
this.readtime = System.currentTimeMillis();
|
||||
channel.read(dst, timeout < 0 ? 0 : timeout, unit, attachment, handler);
|
||||
ByteBuffer dst = pollReadBuffer();
|
||||
channel.read(dst, timeout < 0 ? 0 : timeout, unit, dst, handler);
|
||||
}
|
||||
|
||||
private <A> void nextWrite(A attachment) {
|
||||
private <A> void nextWrite(Throwable exc, A attachment) {
|
||||
BlockingQueue<WriteEntry> queue = this.writeQueue;
|
||||
if (queue != null && exc != null && !isOpen()) {
|
||||
WriteEntry entry;
|
||||
while ((entry = queue.poll()) != null) {
|
||||
try {
|
||||
entry.writeHandler.failed(exc, entry.writeAttachment);
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace(System.err);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
WriteEntry entry = queue == null ? null : queue.poll();
|
||||
|
||||
if (entry != null) {
|
||||
try {
|
||||
if (entry.writeOneBuffer == null) {
|
||||
@@ -223,13 +239,21 @@ public class TcpAioAsyncConnection extends AsyncConnection {
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Future<Integer> read(ByteBuffer dst) {
|
||||
return channel.read(dst);
|
||||
public final int read(ByteBuffer dst) throws IOException {
|
||||
try {
|
||||
return channel.read(dst).get();
|
||||
} catch (InterruptedException | ExecutionException e) {
|
||||
throw new IOException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Future<Integer> write(ByteBuffer src) {
|
||||
return channel.write(src);
|
||||
public final int write(ByteBuffer src) throws IOException {
|
||||
try {
|
||||
return channel.write(src).get();
|
||||
} catch (InterruptedException | ExecutionException e) {
|
||||
throw new IOException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -300,18 +324,27 @@ public class TcpAioAsyncConnection extends AsyncConnection {
|
||||
failed(e, attachment);
|
||||
return;
|
||||
}
|
||||
nextWrite(attachment);
|
||||
writeHandler.completed(writeCount, attachment);
|
||||
try {
|
||||
writeHandler.completed(writeCount, attachment);
|
||||
} finally {
|
||||
nextWrite(null, attachment);
|
||||
}
|
||||
} else {
|
||||
nextWrite(attachment);
|
||||
writeHandler.completed(result.intValue(), attachment);
|
||||
try {
|
||||
writeHandler.completed(result.intValue(), attachment);
|
||||
} finally {
|
||||
nextWrite(null, attachment);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void failed(Throwable exc, A attachment) {
|
||||
nextWrite(attachment);
|
||||
writeHandler.failed(exc, attachment);
|
||||
try {
|
||||
writeHandler.failed(exc, attachment);
|
||||
} finally {
|
||||
nextWrite(exc, attachment);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -338,14 +371,21 @@ public class TcpAioAsyncConnection extends AsyncConnection {
|
||||
failed(e, attachment);
|
||||
return;
|
||||
}
|
||||
nextWrite(attachment);
|
||||
writeHandler.completed(result, attachment);
|
||||
try {
|
||||
writeHandler.completed(result, attachment);
|
||||
} finally {
|
||||
nextWrite(null, attachment);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void failed(Throwable exc, A attachment) {
|
||||
nextWrite(attachment);
|
||||
writeHandler.failed(exc, attachment);
|
||||
try {
|
||||
writeHandler.failed(exc, attachment);
|
||||
} finally {
|
||||
nextWrite(exc, attachment);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -32,7 +32,8 @@ public class TcpAioProtocolServer extends ProtocolServer {
|
||||
|
||||
@Override
|
||||
public void open(AnyValue config) throws IOException {
|
||||
group = AsynchronousChannelGroup.withThreadPool(context.executor);
|
||||
//group = AsynchronousChannelGroup.withThreadPool(context.executor);
|
||||
group = AsynchronousChannelGroup.withFixedThreadPool(context.executor.getCorePoolSize(), context.executor.getThreadFactory());
|
||||
this.serverChannel = AsynchronousServerSocketChannel.open(group);
|
||||
|
||||
final Set<SocketOption<?>> options = this.serverChannel.supportedOptions();
|
||||
@@ -91,13 +92,13 @@ public class TcpAioProtocolServer extends ProtocolServer {
|
||||
channel.setOption(StandardSocketOptions.SO_REUSEADDR, true);
|
||||
channel.setOption(StandardSocketOptions.SO_RCVBUF, 16 * 1024);
|
||||
channel.setOption(StandardSocketOptions.SO_SNDBUF, 16 * 1024);
|
||||
} catch (IOException e) {
|
||||
context.logger.log(Level.INFO, channel + " setOption error", e);
|
||||
|
||||
AsyncConnection conn = new TcpAioAsyncConnection(context.getBufferSupplier(), context.getBufferConsumer(), channel,
|
||||
context.getSSLContext(), null, context.readTimeoutSeconds, context.writeTimeoutSeconds, livingCounter, closedCounter);
|
||||
context.runAsync(new PrepareRunner(context, conn, null, null));
|
||||
} catch (Throwable e) {
|
||||
context.logger.log(Level.INFO, channel + " accept error", e);
|
||||
}
|
||||
AsyncConnection conn = new TcpAioAsyncConnection(channel, context.sslContext, null, context.readTimeoutSeconds, context.writeTimeoutSeconds, null, null);
|
||||
conn.livingCounter = livingCounter;
|
||||
conn.closedCounter = closedCounter;
|
||||
context.runAsync(new PrepareRunner(context, conn, null, null));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,240 +0,0 @@
|
||||
/*
|
||||
* To change this license header, choose License Headers in Project Properties.
|
||||
* To change this template file, choose Tools | Templates
|
||||
* and open the template in the editor.
|
||||
*/
|
||||
package org.redkale.net;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.*;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.*;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
/**
|
||||
*
|
||||
* <p>
|
||||
* 详情见: https://redkale.org
|
||||
*
|
||||
* @author zhangjx
|
||||
*/
|
||||
public class TcpBioAsyncConnection extends AsyncConnection {
|
||||
|
||||
static final Set<SocketOption<?>> defaultOptions = defaultOptions();
|
||||
|
||||
private static Set<SocketOption<?>> defaultOptions() {
|
||||
HashSet<SocketOption<?>> set = new HashSet<>(5);
|
||||
set.add(StandardSocketOptions.SO_SNDBUF);
|
||||
set.add(StandardSocketOptions.SO_RCVBUF);
|
||||
set.add(StandardSocketOptions.SO_KEEPALIVE);
|
||||
set.add(StandardSocketOptions.SO_REUSEADDR);
|
||||
set.add(StandardSocketOptions.TCP_NODELAY);
|
||||
return Collections.unmodifiableSet(set);
|
||||
}
|
||||
|
||||
private int readTimeoutSeconds;
|
||||
|
||||
private int writeTimeoutSeconds;
|
||||
|
||||
private final Socket socket;
|
||||
|
||||
private final ReadableByteChannel readChannel;
|
||||
|
||||
private final WritableByteChannel writeChannel;
|
||||
|
||||
private final SocketAddress remoteAddress;
|
||||
|
||||
public TcpBioAsyncConnection(final Socket socket, final SocketAddress addr0, final int readTimeoutSeconds0, final int writeTimeoutSeconds0,
|
||||
final AtomicLong livingCounter, final AtomicLong closedCounter) {
|
||||
this.socket = socket;
|
||||
ReadableByteChannel rc = null;
|
||||
WritableByteChannel wc = null;
|
||||
try {
|
||||
socket.setSoTimeout(Math.max(readTimeoutSeconds0, writeTimeoutSeconds0));
|
||||
rc = Channels.newChannel(socket.getInputStream());
|
||||
wc = Channels.newChannel(socket.getOutputStream());
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
this.readChannel = rc;
|
||||
this.writeChannel = wc;
|
||||
this.readTimeoutSeconds = readTimeoutSeconds0;
|
||||
this.writeTimeoutSeconds = writeTimeoutSeconds0;
|
||||
SocketAddress addr = addr0;
|
||||
if (addr == null) {
|
||||
try {
|
||||
addr = socket.getRemoteSocketAddress();
|
||||
} catch (Exception e) {
|
||||
//do nothing
|
||||
}
|
||||
}
|
||||
this.remoteAddress = addr;
|
||||
this.livingCounter = livingCounter;
|
||||
this.closedCounter = closedCounter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTCP() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SocketAddress getRemoteAddress() {
|
||||
return remoteAddress;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SocketAddress getLocalAddress() {
|
||||
return socket.getLocalSocketAddress();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getReadTimeoutSeconds() {
|
||||
return readTimeoutSeconds;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getWriteTimeoutSeconds() {
|
||||
return writeTimeoutSeconds;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setReadTimeoutSeconds(int readTimeoutSeconds) {
|
||||
this.readTimeoutSeconds = readTimeoutSeconds;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setWriteTimeoutSeconds(int writeTimeoutSeconds) {
|
||||
this.writeTimeoutSeconds = writeTimeoutSeconds;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shutdownInput() {
|
||||
try {
|
||||
this.socket.shutdownInput();
|
||||
return true;
|
||||
} catch (IOException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shutdownOutput() {
|
||||
try {
|
||||
this.socket.shutdownOutput();
|
||||
return true;
|
||||
} catch (IOException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> boolean setOption(SocketOption<T> name, T value) {
|
||||
try {
|
||||
if (StandardSocketOptions.SO_REUSEADDR == name) {
|
||||
this.socket.setReuseAddress((Boolean) value);
|
||||
return true;
|
||||
}
|
||||
if (StandardSocketOptions.SO_KEEPALIVE == name) {
|
||||
this.socket.setKeepAlive((Boolean) value);
|
||||
return true;
|
||||
}
|
||||
if (StandardSocketOptions.TCP_NODELAY == name) {
|
||||
this.socket.setTcpNoDelay((Boolean) value);
|
||||
return true;
|
||||
}
|
||||
if (StandardSocketOptions.SO_RCVBUF == name) {
|
||||
this.socket.setReceiveBufferSize((Integer) value);
|
||||
return true;
|
||||
}
|
||||
if (StandardSocketOptions.SO_SNDBUF == name) {
|
||||
this.socket.setSendBufferSize((Integer) value);
|
||||
return true;
|
||||
}
|
||||
} catch (IOException e) {
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<SocketOption<?>> supportedOptions() {
|
||||
return defaultOptions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <A> void write(ByteBuffer[] srcs, int offset, int length, A attachment, CompletionHandler<Integer, ? super A> handler) {
|
||||
try {
|
||||
int rs = 0;
|
||||
for (int i = offset; i < offset + length; i++) {
|
||||
rs += writeChannel.write(srcs[i]);
|
||||
}
|
||||
this.writetime = System.currentTimeMillis();
|
||||
if (handler != null) handler.completed(rs, attachment);
|
||||
} catch (IOException e) {
|
||||
if (handler != null) handler.failed(e, attachment);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <A> void read(ByteBuffer dst, A attachment, CompletionHandler<Integer, ? super A> handler) {
|
||||
try {
|
||||
int rs = readChannel.read(dst);
|
||||
this.readtime = System.currentTimeMillis();
|
||||
if (handler != null) handler.completed(rs, attachment);
|
||||
} catch (IOException e) {
|
||||
if (handler != null) handler.failed(e, attachment);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <A> void read(ByteBuffer dst, long timeout, TimeUnit unit, A attachment, CompletionHandler<Integer, ? super A> handler) {
|
||||
read(dst, attachment, handler);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<Integer> read(ByteBuffer dst) {
|
||||
try {
|
||||
int rs = readChannel.read(dst);
|
||||
this.readtime = System.currentTimeMillis();
|
||||
return CompletableFuture.completedFuture(rs);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <A> void write(ByteBuffer src, A attachment, CompletionHandler<Integer, ? super A> handler) {
|
||||
try {
|
||||
int rs = writeChannel.write(src);
|
||||
this.writetime = System.currentTimeMillis();
|
||||
if (handler != null) handler.completed(rs, attachment);
|
||||
} catch (IOException e) {
|
||||
if (handler != null) handler.failed(e, attachment);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<Integer> write(ByteBuffer src) {
|
||||
try {
|
||||
int rs = writeChannel.write(src);
|
||||
this.writetime = System.currentTimeMillis();
|
||||
return CompletableFuture.completedFuture(rs);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
super.close();
|
||||
this.socket.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOpen() {
|
||||
return !socket.isClosed();
|
||||
}
|
||||
}
|
||||
@@ -1,365 +0,0 @@
|
||||
/*
|
||||
* To change this license header, choose License Headers in Project Properties.
|
||||
* To change this template file, choose Tools | Templates
|
||||
* and open the template in the editor.
|
||||
*/
|
||||
package org.redkale.net;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.*;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.*;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
/**
|
||||
*
|
||||
* <p>
|
||||
* 详情见: https://redkale.org
|
||||
*
|
||||
* @author zhangjx
|
||||
*/
|
||||
public class TcpNioAsyncConnection extends AsyncConnection {
|
||||
|
||||
private int readTimeoutSeconds;
|
||||
|
||||
private int writeTimeoutSeconds;
|
||||
|
||||
private final Selector selector;
|
||||
|
||||
private SelectionKey key;
|
||||
|
||||
private final SocketChannel channel;
|
||||
|
||||
private final SocketAddress remoteAddress;
|
||||
|
||||
ByteBuffer readBuffer;
|
||||
|
||||
Object readAttachment;
|
||||
|
||||
CompletionHandler readHandler;
|
||||
|
||||
ByteBuffer writeOneBuffer;
|
||||
|
||||
ByteBuffer[] writeBuffers;
|
||||
|
||||
int writingCount;
|
||||
|
||||
int writeOffset;
|
||||
|
||||
int writeLength;
|
||||
|
||||
Object writeAttachment;
|
||||
|
||||
CompletionHandler writeHandler;
|
||||
|
||||
public TcpNioAsyncConnection(final SocketChannel ch, SocketAddress addr0,
|
||||
final Selector selector,
|
||||
final int readTimeoutSeconds0, final int writeTimeoutSeconds0,
|
||||
final AtomicLong livingCounter, final AtomicLong closedCounter) {
|
||||
this.channel = ch;
|
||||
this.selector = selector;
|
||||
this.readTimeoutSeconds = readTimeoutSeconds0;
|
||||
this.writeTimeoutSeconds = writeTimeoutSeconds0;
|
||||
SocketAddress addr = addr0;
|
||||
if (addr == null) {
|
||||
try {
|
||||
addr = ch.getRemoteAddress();
|
||||
} catch (Exception e) {
|
||||
//do nothing
|
||||
}
|
||||
}
|
||||
this.remoteAddress = addr;
|
||||
this.livingCounter = livingCounter;
|
||||
this.closedCounter = closedCounter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setReadTimeoutSeconds(int readTimeoutSeconds) {
|
||||
this.readTimeoutSeconds = readTimeoutSeconds;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setWriteTimeoutSeconds(int writeTimeoutSeconds) {
|
||||
this.writeTimeoutSeconds = writeTimeoutSeconds;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getReadTimeoutSeconds() {
|
||||
return this.readTimeoutSeconds;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getWriteTimeoutSeconds() {
|
||||
return this.writeTimeoutSeconds;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final SocketAddress getRemoteAddress() {
|
||||
return remoteAddress;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SocketAddress getLocalAddress() {
|
||||
try {
|
||||
return channel.getLocalAddress();
|
||||
} catch (IOException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shutdownInput() {
|
||||
try {
|
||||
this.channel.shutdownInput();
|
||||
return true;
|
||||
} catch (IOException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shutdownOutput() {
|
||||
try {
|
||||
this.channel.shutdownOutput();
|
||||
return true;
|
||||
} catch (IOException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> boolean setOption(SocketOption<T> name, T value) {
|
||||
try {
|
||||
this.channel.setOption(name, value);
|
||||
return true;
|
||||
} catch (IOException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<SocketOption<?>> supportedOptions() {
|
||||
return this.channel.supportedOptions();
|
||||
}
|
||||
|
||||
CompletionHandler removeReadHandler() {
|
||||
CompletionHandler handler = this.readHandler;
|
||||
this.readHandler = null;
|
||||
return handler;
|
||||
}
|
||||
|
||||
ByteBuffer removeReadBuffer() {
|
||||
ByteBuffer buffer = this.readBuffer;
|
||||
this.readBuffer = null;
|
||||
return buffer;
|
||||
}
|
||||
|
||||
Object removeReadAttachment() {
|
||||
Object attach = this.readAttachment;
|
||||
this.readAttachment = null;
|
||||
return attach;
|
||||
}
|
||||
|
||||
void completeRead(int rs) {
|
||||
Object attach = this.readAttachment;
|
||||
CompletionHandler handler = this.readHandler;
|
||||
this.readBuffer = null;
|
||||
this.readAttachment = null;
|
||||
this.readHandler = null;
|
||||
handler.completed(rs, attach);
|
||||
}
|
||||
|
||||
void faileRead(Throwable t) {
|
||||
Object attach = this.readAttachment;
|
||||
CompletionHandler handler = this.readHandler;
|
||||
this.readBuffer = null;
|
||||
this.readAttachment = null;
|
||||
this.readHandler = null;
|
||||
handler.failed(t, attach);
|
||||
}
|
||||
|
||||
CompletionHandler removeWriteHandler() {
|
||||
CompletionHandler handler = this.writeHandler;
|
||||
this.writeHandler = null;
|
||||
return handler;
|
||||
}
|
||||
|
||||
ByteBuffer removeWriteOneBuffer() {
|
||||
ByteBuffer buffer = this.writeOneBuffer;
|
||||
this.writeOneBuffer = null;
|
||||
return buffer;
|
||||
}
|
||||
|
||||
ByteBuffer[] removeWriteBuffers() {
|
||||
ByteBuffer[] buffers = this.writeBuffers;
|
||||
this.writeBuffers = null;
|
||||
return buffers;
|
||||
}
|
||||
|
||||
int removeWritingCount() {
|
||||
int rs = this.writingCount;
|
||||
this.writingCount = 0;
|
||||
return rs;
|
||||
}
|
||||
|
||||
int removeWriteOffset() {
|
||||
int rs = this.writeOffset;
|
||||
this.writeOffset = 0;
|
||||
return rs;
|
||||
}
|
||||
|
||||
int removeWriteLength() {
|
||||
int rs = this.writeLength;
|
||||
this.writeLength = 0;
|
||||
return rs;
|
||||
}
|
||||
|
||||
Object removeWriteAttachment() {
|
||||
Object attach = this.writeAttachment;
|
||||
this.writeAttachment = null;
|
||||
return attach;
|
||||
}
|
||||
|
||||
void completeWrite(int rs) {
|
||||
Object attach = this.writeAttachment;
|
||||
CompletionHandler handler = this.writeHandler;
|
||||
this.writeOneBuffer = null;
|
||||
this.writeBuffers = null;
|
||||
this.writeOffset = 0;
|
||||
this.writeLength = 0;
|
||||
this.writeAttachment = null;
|
||||
this.writeHandler = null;
|
||||
handler.completed(rs, attach);
|
||||
}
|
||||
|
||||
void faileWrite(Throwable t) {
|
||||
Object attach = this.writeAttachment;
|
||||
CompletionHandler handler = this.writeHandler;
|
||||
this.writeOneBuffer = null;
|
||||
this.writeBuffers = null;
|
||||
this.writeOffset = 0;
|
||||
this.writeLength = 0;
|
||||
this.writeAttachment = null;
|
||||
this.writeHandler = null;
|
||||
handler.failed(t, attach);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <A> void read(ByteBuffer dst, A attachment, CompletionHandler<Integer, ? super A> handler) {
|
||||
if (this.readHandler != null) throw new RuntimeException("pending read");
|
||||
try {
|
||||
this.readBuffer = dst;
|
||||
this.readAttachment = attachment;
|
||||
this.readHandler = handler;
|
||||
if (key == null) {
|
||||
key = channel.register(selector, SelectionKey.OP_READ);
|
||||
key.attach(this);
|
||||
} else {
|
||||
key.interestOps(SelectionKey.OP_READ);
|
||||
}
|
||||
selector.wakeup();
|
||||
} catch (Exception e) {
|
||||
faileRead(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <A> void read(ByteBuffer dst, long timeout, TimeUnit unit, A attachment, CompletionHandler<Integer, ? super A> handler) {
|
||||
read(dst, attachment, handler);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<Integer> read(ByteBuffer dst) {
|
||||
CompletableFuture future = new CompletableFuture();
|
||||
read(dst, null, new CompletionHandler<Integer, Void>() {
|
||||
@Override
|
||||
public void completed(Integer result, Void attachment) {
|
||||
future.complete(result);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void failed(Throwable exc, Void attachment) {
|
||||
future.completeExceptionally(exc);
|
||||
}
|
||||
});
|
||||
return future;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <A> void write(ByteBuffer[] srcs, int offset, int length, A attachment, CompletionHandler<Integer, ? super A> handler) {
|
||||
if (this.writeHandler != null) throw new RuntimeException("pending write");
|
||||
try {
|
||||
this.writeBuffers = srcs;
|
||||
this.writeOffset = offset;
|
||||
this.writeLength = length;
|
||||
this.writingCount = 0;
|
||||
this.writeAttachment = attachment;
|
||||
this.writeHandler = handler;
|
||||
if (key == null) {
|
||||
key = channel.register(selector, SelectionKey.OP_WRITE);
|
||||
key.attach(this);
|
||||
} else {
|
||||
key.interestOps(SelectionKey.OP_WRITE);
|
||||
}
|
||||
selector.wakeup();
|
||||
} catch (Exception e) {
|
||||
faileWrite(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <A> void write(ByteBuffer src, A attachment, CompletionHandler<Integer, ? super A> handler) {
|
||||
if (this.writeHandler != null) throw new RuntimeException("pending write");
|
||||
try {
|
||||
this.writeOneBuffer = src;
|
||||
this.writingCount = 0;
|
||||
this.writeAttachment = attachment;
|
||||
this.writeHandler = handler;
|
||||
if (key == null) {
|
||||
key = channel.register(selector, SelectionKey.OP_WRITE);
|
||||
key.attach(this);
|
||||
} else {
|
||||
key.interestOps(SelectionKey.OP_WRITE);
|
||||
}
|
||||
selector.wakeup();
|
||||
} catch (Exception e) {
|
||||
faileWrite(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<Integer> write(ByteBuffer src) {
|
||||
CompletableFuture future = new CompletableFuture();
|
||||
write(src, null, new CompletionHandler<Integer, Void>() {
|
||||
@Override
|
||||
public void completed(Integer result, Void attachment) {
|
||||
future.complete(result);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void failed(Throwable exc, Void attachment) {
|
||||
future.completeExceptionally(exc);
|
||||
}
|
||||
});
|
||||
return future;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void close() throws IOException {
|
||||
super.close();
|
||||
channel.close();
|
||||
key.cancel();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean isOpen() {
|
||||
return channel.isOpen();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean isTCP() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -1,309 +0,0 @@
|
||||
/*
|
||||
* To change this license header, choose License Headers in Project Properties.
|
||||
* To change this template file, choose Tools | Templates
|
||||
* and open the template in the editor.
|
||||
*/
|
||||
package org.redkale.net;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.*;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.*;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import org.redkale.util.AnyValue;
|
||||
|
||||
/**
|
||||
* 协议底层Server
|
||||
*
|
||||
* <p>
|
||||
* 详情见: https://redkale.org
|
||||
*
|
||||
* @author zhangjx
|
||||
*/
|
||||
public class TcpNioProtocolServer extends ProtocolServer {
|
||||
|
||||
private Selector acceptSelector;
|
||||
|
||||
private ServerSocketChannel serverChannel;
|
||||
|
||||
private NIOThreadWorker[] workers;
|
||||
|
||||
private NIOThreadWorker currWorker;
|
||||
|
||||
private boolean running;
|
||||
|
||||
public TcpNioProtocolServer(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void open(AnyValue config) throws IOException {
|
||||
acceptSelector = Selector.open();
|
||||
this.serverChannel = ServerSocketChannel.open();
|
||||
serverChannel.configureBlocking(false);
|
||||
ServerSocket socket = serverChannel.socket();
|
||||
socket.setReceiveBufferSize(16 * 1024);
|
||||
socket.setReuseAddress(true);
|
||||
|
||||
final Set<SocketOption<?>> options = this.serverChannel.supportedOptions();
|
||||
if (options.contains(StandardSocketOptions.TCP_NODELAY)) {
|
||||
this.serverChannel.setOption(StandardSocketOptions.TCP_NODELAY, true);
|
||||
}
|
||||
if (options.contains(StandardSocketOptions.SO_KEEPALIVE)) {
|
||||
this.serverChannel.setOption(StandardSocketOptions.SO_KEEPALIVE, true);
|
||||
}
|
||||
if (options.contains(StandardSocketOptions.SO_REUSEADDR)) {
|
||||
this.serverChannel.setOption(StandardSocketOptions.SO_REUSEADDR, true);
|
||||
}
|
||||
if (options.contains(StandardSocketOptions.SO_RCVBUF)) {
|
||||
this.serverChannel.setOption(StandardSocketOptions.SO_RCVBUF, 16 * 1024);
|
||||
}
|
||||
if (options.contains(StandardSocketOptions.SO_SNDBUF)) {
|
||||
this.serverChannel.setOption(StandardSocketOptions.SO_SNDBUF, 16 * 1024);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bind(SocketAddress local, int backlog) throws IOException {
|
||||
this.serverChannel.bind(local, backlog);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Set<SocketOption<?>> supportedOptions() {
|
||||
return this.serverChannel.supportedOptions();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> void setOption(SocketOption<T> name, T value) throws IOException {
|
||||
this.serverChannel.setOption(name, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void accept() throws IOException {
|
||||
this.serverChannel.register(acceptSelector, SelectionKey.OP_ACCEPT);
|
||||
final CountDownLatch cdl = new CountDownLatch(1);
|
||||
this.running = true;
|
||||
this.workers = new NIOThreadWorker[Runtime.getRuntime().availableProcessors()];
|
||||
for (int i = 0; i < workers.length; i++) {
|
||||
workers[i] = new NIOThreadWorker();
|
||||
workers[i].setDaemon(true);
|
||||
workers[i].start();
|
||||
}
|
||||
for (int i = 0; i < workers.length - 1; i++) { //构成环形
|
||||
workers[i].next = workers[i + 1];
|
||||
}
|
||||
workers[workers.length - 1].next = workers[0];
|
||||
currWorker = workers[0];
|
||||
new Thread() {
|
||||
@Override
|
||||
public void run() {
|
||||
cdl.countDown();
|
||||
while (running) {
|
||||
try {
|
||||
acceptSelector.select();
|
||||
Set<SelectionKey> selectedKeys = acceptSelector.selectedKeys();
|
||||
synchronized (selectedKeys) {
|
||||
Iterator<?> iter = selectedKeys.iterator();
|
||||
while (iter.hasNext()) {
|
||||
SelectionKey key = (SelectionKey) iter.next();
|
||||
iter.remove();
|
||||
if (key.isAcceptable()) {
|
||||
try {
|
||||
SocketChannel channel = ((ServerSocketChannel) key.channel()).accept();
|
||||
channel.configureBlocking(false);
|
||||
channel.setOption(StandardSocketOptions.TCP_NODELAY, true);
|
||||
channel.setOption(StandardSocketOptions.SO_KEEPALIVE, true);
|
||||
channel.setOption(StandardSocketOptions.SO_REUSEADDR, true);
|
||||
channel.setOption(StandardSocketOptions.SO_RCVBUF, 16 * 1024);
|
||||
channel.setOption(StandardSocketOptions.SO_SNDBUF, 16 * 1024);
|
||||
createCounter.incrementAndGet();
|
||||
livingCounter.incrementAndGet();
|
||||
currWorker.addChannel(channel);
|
||||
currWorker = currWorker.next;
|
||||
} catch (IOException io) {
|
||||
io.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
t.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}.start();
|
||||
try {
|
||||
cdl.await();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
if (!this.running) return;
|
||||
this.running = false;
|
||||
serverChannel.close();
|
||||
acceptSelector.close();
|
||||
for (NIOThreadWorker worker : workers) {
|
||||
worker.interrupt();
|
||||
}
|
||||
}
|
||||
|
||||
class NIOThreadWorker extends Thread {
|
||||
|
||||
final Selector selector;
|
||||
|
||||
NIOThreadWorker next;
|
||||
|
||||
public NIOThreadWorker() {
|
||||
try {
|
||||
this.selector = Selector.open();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void addChannel(SocketChannel channel) throws IOException {
|
||||
AsyncConnection conn = new TcpNioAsyncConnection(channel, null, selector, context.readTimeoutSeconds, context.writeTimeoutSeconds, null, null);
|
||||
context.runAsync(new PrepareRunner(context, conn, null, null));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
while (running) {
|
||||
try {
|
||||
selector.select(50);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
try {
|
||||
Set<SelectionKey> selectedKeys = selector.selectedKeys();
|
||||
synchronized (selectedKeys) {
|
||||
Iterator<?> iter = selectedKeys.iterator();
|
||||
while (iter.hasNext()) {
|
||||
SelectionKey key = (SelectionKey) iter.next();
|
||||
iter.remove();
|
||||
processKey(key);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void processKey(SelectionKey key) {
|
||||
if (key == null || !key.isValid()) return;
|
||||
SocketChannel socket = (SocketChannel) key.channel();
|
||||
TcpNioAsyncConnection conn = (TcpNioAsyncConnection) key.attachment();
|
||||
if (!socket.isOpen()) {
|
||||
if (conn == null) {
|
||||
key.cancel();
|
||||
} else {
|
||||
conn.dispose();
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (conn == null) return;
|
||||
if (key.isWritable()) {
|
||||
if (conn.writeHandler != null) writeOP(key, socket, conn);
|
||||
} else if (key.isReadable()) {
|
||||
if (conn.readHandler != null) readOP(key, socket, conn);
|
||||
}
|
||||
}
|
||||
|
||||
private void readOP(SelectionKey key, SocketChannel socket, TcpNioAsyncConnection conn) {
|
||||
final CompletionHandler handler = conn.removeReadHandler();
|
||||
final ByteBuffer buffer = conn.removeReadBuffer();
|
||||
final Object attach = conn.removeReadAttachment();
|
||||
//System.out.println(conn + "------readbuf:" + buffer + "-------handler:" + handler);
|
||||
if (handler == null || buffer == null) return;
|
||||
try {
|
||||
final int rs = socket.read(buffer);
|
||||
{ //测试
|
||||
buffer.flip();
|
||||
byte[] bs = new byte[buffer.remaining()];
|
||||
buffer.get(bs);
|
||||
//System.out.println(conn + "------readbuf:" + buffer + "-------handler:" + handler + "-------读内容: " + new String(bs));
|
||||
}
|
||||
//System.out.println(conn + "------readbuf:" + buffer + "-------handler:" + handler + "-------read: " + rs);
|
||||
context.runAsync(() -> {
|
||||
try {
|
||||
handler.completed(rs, attach);
|
||||
} catch (Throwable e) {
|
||||
handler.failed(e, attach);
|
||||
}
|
||||
});
|
||||
} catch (Throwable t) {
|
||||
context.runAsync(() -> handler.failed(t, attach));
|
||||
}
|
||||
}
|
||||
|
||||
private void writeOP(SelectionKey key, SocketChannel socket, TcpNioAsyncConnection conn) {
|
||||
final CompletionHandler handler = conn.writeHandler;
|
||||
final ByteBuffer oneBuffer = conn.removeWriteOneBuffer();
|
||||
final ByteBuffer[] buffers = conn.removeWriteBuffers();
|
||||
final Object attach = conn.removeWriteAttachment();
|
||||
final int writingCount = conn.removeWritingCount();
|
||||
final int writeOffset = conn.removeWriteOffset();
|
||||
final int writeLength = conn.removeWriteLength();
|
||||
if (handler == null || (oneBuffer == null && buffers == null)) return;
|
||||
//System.out.println(conn + "------buffers:" + Arrays.toString(buffers) + "---onebuf:" + oneBuffer + "-------handler:" + handler);
|
||||
try {
|
||||
int rs = 0;
|
||||
if (oneBuffer == null) {
|
||||
int offset = writeOffset;
|
||||
int length = writeLength;
|
||||
rs = (int) socket.write(buffers, offset, length);
|
||||
boolean over = true;
|
||||
int end = offset + length;
|
||||
for (int i = offset; i < end; i++) {
|
||||
if (buffers[i].hasRemaining()) {
|
||||
over = false;
|
||||
length -= i - offset;
|
||||
offset = i;
|
||||
}
|
||||
}
|
||||
if (!over) {
|
||||
conn.writingCount += rs;
|
||||
conn.writeHandler = handler;
|
||||
conn.writeAttachment = attach;
|
||||
conn.writeBuffers = buffers;
|
||||
conn.writeOffset = offset;
|
||||
conn.writeLength = length;
|
||||
key.interestOps(SelectionKey.OP_READ + SelectionKey.OP_WRITE);
|
||||
key.selector().wakeup();
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
rs = socket.write(oneBuffer);
|
||||
if (oneBuffer.hasRemaining()) {
|
||||
conn.writingCount += rs;
|
||||
conn.writeHandler = handler;
|
||||
conn.writeAttachment = attach;
|
||||
conn.writeOneBuffer = oneBuffer;
|
||||
key.interestOps(SelectionKey.OP_READ + SelectionKey.OP_WRITE);
|
||||
key.selector().wakeup();
|
||||
return;
|
||||
}
|
||||
}
|
||||
conn.removeWriteHandler();
|
||||
key.interestOps(SelectionKey.OP_READ); //OP_CONNECT
|
||||
final int rs0 = rs + writingCount;
|
||||
//System.out.println(conn + "------buffers:" + Arrays.toString(buffers) + "---onebuf:" + oneBuffer + "-------handler:" + handler + "-------write: " + rs);
|
||||
context.runAsync(() -> {
|
||||
try {
|
||||
handler.completed(rs0, attach);
|
||||
} catch (Throwable e) {
|
||||
handler.failed(e, attach);
|
||||
}
|
||||
});
|
||||
} catch (Throwable t) {
|
||||
context.runAsync(() -> handler.failed(t, attach));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -29,41 +29,41 @@ import org.redkale.util.*;
|
||||
* @author zhangjx
|
||||
*/
|
||||
public final class Transport {
|
||||
|
||||
|
||||
public static final String DEFAULT_PROTOCOL = "TCP";
|
||||
|
||||
|
||||
protected final AtomicInteger seq = new AtomicInteger(-1);
|
||||
|
||||
|
||||
protected final TransportFactory factory;
|
||||
|
||||
|
||||
protected final String name; //即<group>的name属性
|
||||
|
||||
protected final String subprotocol; //即<group>的subprotocol属性
|
||||
|
||||
protected final boolean tcp;
|
||||
|
||||
|
||||
protected final String protocol;
|
||||
|
||||
|
||||
protected final AsynchronousChannelGroup group;
|
||||
|
||||
|
||||
protected final InetSocketAddress clientAddress;
|
||||
|
||||
//不可能为null
|
||||
protected TransportNode[] transportNodes = new TransportNode[0];
|
||||
|
||||
|
||||
protected final ObjectPool<ByteBuffer> bufferPool;
|
||||
|
||||
|
||||
protected final SSLContext sslContext;
|
||||
|
||||
//负载均衡策略
|
||||
protected final TransportStrategy strategy;
|
||||
|
||||
|
||||
protected Transport(String name, String subprotocol, TransportFactory factory, final ObjectPool<ByteBuffer> transportBufferPool,
|
||||
final AsynchronousChannelGroup transportChannelGroup, final SSLContext sslContext, final InetSocketAddress clientAddress,
|
||||
final Collection<InetSocketAddress> addresses, final TransportStrategy strategy) {
|
||||
this(name, DEFAULT_PROTOCOL, subprotocol, factory, transportBufferPool, transportChannelGroup, sslContext, clientAddress, addresses, strategy);
|
||||
}
|
||||
|
||||
|
||||
protected Transport(String name, String protocol, String subprotocol,
|
||||
final TransportFactory factory, final ObjectPool<ByteBuffer> transportBufferPool,
|
||||
final AsynchronousChannelGroup transportChannelGroup, final SSLContext sslContext, final InetSocketAddress clientAddress,
|
||||
@@ -81,7 +81,7 @@ public final class Transport {
|
||||
this.strategy = strategy;
|
||||
updateRemoteAddresses(addresses);
|
||||
}
|
||||
|
||||
|
||||
public final InetSocketAddress[] updateRemoteAddresses(final Collection<InetSocketAddress> addresses) {
|
||||
final TransportNode[] oldNodes = this.transportNodes;
|
||||
synchronized (this) {
|
||||
@@ -109,7 +109,7 @@ public final class Transport {
|
||||
}
|
||||
return rs;
|
||||
}
|
||||
|
||||
|
||||
public final boolean addRemoteAddresses(final InetSocketAddress addr) {
|
||||
if (addr == null) return false;
|
||||
if (clientAddress != null && clientAddress.equals(addr)) return false;
|
||||
@@ -125,7 +125,7 @@ public final class Transport {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public final boolean removeRemoteAddresses(InetSocketAddress addr) {
|
||||
if (addr == null) return false;
|
||||
synchronized (this) {
|
||||
@@ -133,15 +133,15 @@ public final class Transport {
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
|
||||
public String getSubprotocol() {
|
||||
return subprotocol;
|
||||
}
|
||||
|
||||
|
||||
public void close() {
|
||||
TransportNode[] nodes = this.transportNodes;
|
||||
if (nodes == null) return;
|
||||
@@ -149,22 +149,22 @@ public final class Transport {
|
||||
if (node != null) node.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public InetSocketAddress getClientAddress() {
|
||||
return clientAddress;
|
||||
}
|
||||
|
||||
|
||||
public TransportNode[] getTransportNodes() {
|
||||
return transportNodes;
|
||||
}
|
||||
|
||||
|
||||
public TransportNode findTransportNode(SocketAddress addr) {
|
||||
for (TransportNode node : this.transportNodes) {
|
||||
if (node.address.equals(addr)) return node;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
public InetSocketAddress[] getRemoteAddresses() {
|
||||
InetSocketAddress[] rs = new InetSocketAddress[transportNodes.length];
|
||||
for (int i = 0; i < rs.length; i++) {
|
||||
@@ -172,36 +172,36 @@ public final class Transport {
|
||||
}
|
||||
return rs;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return Transport.class.getSimpleName() + "{name = " + name + ", protocol = " + protocol + ", clientAddress = " + clientAddress + ", remoteNodes = " + Arrays.toString(transportNodes) + "}";
|
||||
}
|
||||
|
||||
|
||||
public ByteBuffer pollBuffer() {
|
||||
return bufferPool.get();
|
||||
}
|
||||
|
||||
|
||||
public Supplier<ByteBuffer> getBufferSupplier() {
|
||||
return bufferPool;
|
||||
}
|
||||
|
||||
|
||||
public void offerBuffer(ByteBuffer buffer) {
|
||||
bufferPool.accept(buffer);
|
||||
}
|
||||
|
||||
|
||||
public void offerBuffer(ByteBuffer... buffers) {
|
||||
for (ByteBuffer buffer : buffers) offerBuffer(buffer);
|
||||
}
|
||||
|
||||
|
||||
public AsynchronousChannelGroup getTransportChannelGroup() {
|
||||
return group;
|
||||
}
|
||||
|
||||
|
||||
public boolean isTCP() {
|
||||
return tcp;
|
||||
}
|
||||
|
||||
|
||||
public CompletableFuture<AsyncConnection> pollConnection(SocketAddress addr0) {
|
||||
if (this.strategy != null) return strategy.pollConnection(addr0, this);
|
||||
final TransportNode[] nodes = this.transportNodes;
|
||||
@@ -215,12 +215,12 @@ public final class Transport {
|
||||
DatagramChannel channel = DatagramChannel.open();
|
||||
channel.configureBlocking(true);
|
||||
channel.connect(udpaddr);
|
||||
return CompletableFuture.completedFuture(AsyncConnection.create(channel, udpaddr, true, factory.readTimeoutSeconds, factory.writeTimeoutSeconds));
|
||||
return CompletableFuture.completedFuture(AsyncConnection.create(bufferPool, channel, sslContext, udpaddr, true, factory.readTimeoutSeconds, factory.writeTimeoutSeconds));
|
||||
}
|
||||
if (!rand) { //指定地址
|
||||
TransportNode node = findTransportNode(addr);
|
||||
if (node == null) {
|
||||
return AsyncConnection.createTCP(group, sslContext, addr, factory.readTimeoutSeconds, factory.writeTimeoutSeconds);
|
||||
return AsyncConnection.createTCP(bufferPool, group, sslContext, addr, factory.readTimeoutSeconds, factory.writeTimeoutSeconds);
|
||||
}
|
||||
final BlockingQueue<AsyncConnection> queue = node.conns;
|
||||
if (!queue.isEmpty()) {
|
||||
@@ -233,7 +233,7 @@ public final class Transport {
|
||||
}
|
||||
}
|
||||
}
|
||||
return AsyncConnection.createTCP(group, sslContext, addr, factory.readTimeoutSeconds, factory.writeTimeoutSeconds);
|
||||
return AsyncConnection.createTCP(bufferPool, group, sslContext, addr, factory.readTimeoutSeconds, factory.writeTimeoutSeconds);
|
||||
}
|
||||
|
||||
//---------------------随机取地址------------------------
|
||||
@@ -266,14 +266,14 @@ public final class Transport {
|
||||
@Override
|
||||
public void completed(Void result, TransportNode attachment) {
|
||||
attachment.disabletime = 0;
|
||||
AsyncConnection asyncConn = AsyncConnection.create(channel, attachment.address, factory.readTimeoutSeconds, factory.writeTimeoutSeconds);
|
||||
AsyncConnection asyncConn = AsyncConnection.create(bufferPool, channel, attachment.address, factory.readTimeoutSeconds, factory.writeTimeoutSeconds);
|
||||
if (future.isDone()) {
|
||||
if (!attachment.conns.offer(asyncConn)) asyncConn.dispose();
|
||||
} else {
|
||||
future.complete(asyncConn);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void failed(Throwable exc, TransportNode attachment) {
|
||||
attachment.disabletime = now;
|
||||
@@ -289,7 +289,7 @@ public final class Transport {
|
||||
future.complete(r);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
} catch (Exception e) {
|
||||
future.completeExceptionally(e);
|
||||
}
|
||||
@@ -302,7 +302,7 @@ public final class Transport {
|
||||
throw new RuntimeException("transport address = " + addr, ex);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private CompletableFuture<AsyncConnection> pollConnection0(TransportNode[] nodes, TransportNode exclude, long now) throws IOException {
|
||||
//从可用/不可用的地址列表中创建连接
|
||||
AtomicInteger count = new AtomicInteger(nodes.length);
|
||||
@@ -319,17 +319,17 @@ public final class Transport {
|
||||
public void completed(Void result, TransportNode attachment) {
|
||||
try {
|
||||
attachment.disabletime = 0;
|
||||
AsyncConnection asyncConn = AsyncConnection.create(channel, attachment.address, factory.readTimeoutSeconds, factory.writeTimeoutSeconds);
|
||||
AsyncConnection asyncConn = AsyncConnection.create(bufferPool, channel, attachment.address, factory.readTimeoutSeconds, factory.writeTimeoutSeconds);
|
||||
if (future.isDone()) {
|
||||
if (!attachment.conns.offer(asyncConn)) asyncConn.dispose();
|
||||
} else {
|
||||
future.complete(asyncConn);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
failed(e, attachment);
|
||||
failed(e, attachment);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void failed(Throwable exc, TransportNode attachment) {
|
||||
attachment.disabletime = now;
|
||||
@@ -345,7 +345,7 @@ public final class Transport {
|
||||
}
|
||||
return future;
|
||||
}
|
||||
|
||||
|
||||
public void offerConnection(final boolean forceClose, AsyncConnection conn) {
|
||||
if (this.strategy != null && strategy.offerConnection(forceClose, conn)) return;
|
||||
if (!forceClose && conn.isTCP()) {
|
||||
@@ -359,7 +359,7 @@ public final class Transport {
|
||||
conn.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public <A> void async(SocketAddress addr, final ByteBuffer buffer, A att, final CompletionHandler<Integer, A> handler) {
|
||||
pollConnection(addr).whenComplete((conn, ex) -> {
|
||||
if (ex != null) {
|
||||
@@ -367,118 +367,119 @@ public final class Transport {
|
||||
return;
|
||||
}
|
||||
conn.write(buffer, buffer, new CompletionHandler<Integer, ByteBuffer>() {
|
||||
|
||||
|
||||
@Override
|
||||
public void completed(Integer result, ByteBuffer attachment) {
|
||||
buffer.clear();
|
||||
conn.read(buffer, buffer, new CompletionHandler<Integer, ByteBuffer>() {
|
||||
|
||||
conn.setReadBuffer(buffer);
|
||||
conn.read(new CompletionHandler<Integer, ByteBuffer>() {
|
||||
|
||||
@Override
|
||||
public void completed(Integer result, ByteBuffer attachment) {
|
||||
if (handler != null) handler.completed(result, att);
|
||||
offerBuffer(buffer);
|
||||
conn.offerBuffer(attachment);
|
||||
offerConnection(false, conn);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void failed(Throwable exc, ByteBuffer attachment) {
|
||||
offerBuffer(buffer);
|
||||
conn.offerBuffer(attachment);
|
||||
offerConnection(true, conn);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void failed(Throwable exc, ByteBuffer attachment) {
|
||||
offerBuffer(buffer);
|
||||
conn.offerBuffer(attachment);
|
||||
offerConnection(true, conn);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
public static class TransportNode {
|
||||
|
||||
|
||||
protected InetSocketAddress address;
|
||||
|
||||
|
||||
protected volatile long disabletime; //不可用时的时间, 为0表示可用
|
||||
|
||||
protected final BlockingQueue<AsyncConnection> conns;
|
||||
|
||||
|
||||
protected final ConcurrentHashMap<String, Object> attributes = new ConcurrentHashMap<>();
|
||||
|
||||
|
||||
public TransportNode(int poolmaxconns, InetSocketAddress address) {
|
||||
this.address = address;
|
||||
this.disabletime = 0;
|
||||
this.conns = new ArrayBlockingQueue<>(poolmaxconns);
|
||||
}
|
||||
|
||||
|
||||
@ConstructorParameters({"poolmaxconns", "address", "disabletime"})
|
||||
public TransportNode(int poolmaxconns, InetSocketAddress address, long disabletime) {
|
||||
this.address = address;
|
||||
this.disabletime = disabletime;
|
||||
this.conns = new LinkedBlockingQueue<>(poolmaxconns);
|
||||
}
|
||||
|
||||
|
||||
public int getPoolmaxconns() {
|
||||
return this.conns.remainingCapacity() + this.conns.size();
|
||||
}
|
||||
|
||||
|
||||
public <T> T setAttribute(String name, T value) {
|
||||
attributes.put(name, value);
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> T getAttribute(String name) {
|
||||
return (T) attributes.get(name);
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> T removeAttribute(String name) {
|
||||
return (T) attributes.remove(name);
|
||||
}
|
||||
|
||||
|
||||
public TransportNode clearAttributes() {
|
||||
attributes.clear();
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public ConcurrentHashMap<String, Object> getAttributes() {
|
||||
return attributes;
|
||||
}
|
||||
|
||||
|
||||
public void setAttributes(ConcurrentHashMap<String, Object> map) {
|
||||
attributes.clear();
|
||||
if (map != null) attributes.putAll(map);
|
||||
}
|
||||
|
||||
|
||||
public InetSocketAddress getAddress() {
|
||||
return address;
|
||||
}
|
||||
|
||||
|
||||
public long getDisabletime() {
|
||||
return disabletime;
|
||||
}
|
||||
|
||||
|
||||
@ConvertDisabled
|
||||
public BlockingQueue<AsyncConnection> getConns() {
|
||||
return conns;
|
||||
}
|
||||
|
||||
|
||||
public void dispose() {
|
||||
AsyncConnection conn;
|
||||
while ((conn = conns.poll()) != null) {
|
||||
conn.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return this.address.hashCode();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) return true;
|
||||
@@ -487,7 +488,7 @@ public final class Transport {
|
||||
final TransportNode other = (TransportNode) obj;
|
||||
return this.address.equals(other.address);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return JsonConvert.root().convertTo(this);
|
||||
|
||||
@@ -393,33 +393,34 @@ public class TransportFactory {
|
||||
final BlockingQueue<AsyncConnection> localqueue = queue;
|
||||
localconn.write(sendBuffer, sendBuffer, new CompletionHandler<Integer, ByteBuffer>() {
|
||||
@Override
|
||||
public void completed(Integer result, ByteBuffer buffer) {
|
||||
if (buffer.hasRemaining()) {
|
||||
localconn.write(buffer, buffer, this);
|
||||
public void completed(Integer result, ByteBuffer wbuffer) {
|
||||
if (wbuffer.hasRemaining()) {
|
||||
localconn.write(wbuffer, wbuffer, this);
|
||||
return;
|
||||
}
|
||||
ByteBuffer pongBuffer = bufferPool.get();
|
||||
localconn.read(pongBuffer, pongBuffer, new CompletionHandler<Integer, ByteBuffer>() {
|
||||
localconn.read(new CompletionHandler<Integer, ByteBuffer>() {
|
||||
int counter = 0;
|
||||
|
||||
@Override
|
||||
public void completed(Integer result, ByteBuffer attachment) {
|
||||
public void completed(Integer result, ByteBuffer pongBuffer) {
|
||||
if (counter > 3) {
|
||||
bufferPool.accept(attachment);
|
||||
localconn.offerBuffer(pongBuffer);
|
||||
localconn.dispose();
|
||||
return;
|
||||
}
|
||||
if (pongLength > 0 && attachment.position() < pongLength) {
|
||||
if (pongLength > 0 && pongBuffer.position() < pongLength) {
|
||||
counter++;
|
||||
localconn.read(pongBuffer, pongBuffer, this);
|
||||
localconn.setReadBuffer(pongBuffer);
|
||||
localconn.read(this);
|
||||
return;
|
||||
}
|
||||
bufferPool.accept(attachment);
|
||||
localconn.offerBuffer(pongBuffer);
|
||||
localqueue.offer(localconn);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void failed(Throwable exc, ByteBuffer attachment) {
|
||||
public void failed(Throwable exc, ByteBuffer pongBuffer) {
|
||||
localconn.offerBuffer(pongBuffer);
|
||||
localconn.dispose();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -12,6 +12,8 @@ import java.nio.channels.*;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import java.util.function.*;
|
||||
import javax.net.ssl.SSLContext;
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -32,9 +34,11 @@ public class UdpBioAsyncConnection extends AsyncConnection {
|
||||
|
||||
private final boolean client;
|
||||
|
||||
public UdpBioAsyncConnection(final DatagramChannel ch, SocketAddress addr0,
|
||||
final boolean client0, final int readTimeoutSeconds0, final int writeTimeoutSeconds0,
|
||||
public UdpBioAsyncConnection(Supplier<ByteBuffer> bufferSupplier, Consumer<ByteBuffer> bufferConsumer,
|
||||
final DatagramChannel ch, final SSLContext sslContext, SocketAddress addr0, final boolean client0,
|
||||
final int readTimeoutSeconds0, final int writeTimeoutSeconds0,
|
||||
final AtomicLong livingCounter, final AtomicLong closedCounter) {
|
||||
super(bufferSupplier, bufferConsumer, sslContext);
|
||||
this.channel = ch;
|
||||
this.client = client0;
|
||||
this.readTimeoutSeconds = readTimeoutSeconds0;
|
||||
@@ -127,30 +131,27 @@ public class UdpBioAsyncConnection extends AsyncConnection {
|
||||
}
|
||||
|
||||
@Override
|
||||
public <A> void read(ByteBuffer dst, A attachment, CompletionHandler<Integer, ? super A> handler) {
|
||||
public void read(CompletionHandler<Integer, ByteBuffer> handler) {
|
||||
ByteBuffer dst = pollReadBuffer();
|
||||
try {
|
||||
int rs = channel.read(dst);
|
||||
this.readtime = System.currentTimeMillis();
|
||||
if (handler != null) handler.completed(rs, attachment);
|
||||
if (handler != null) handler.completed(rs, dst);
|
||||
} catch (IOException e) {
|
||||
if (handler != null) handler.failed(e, attachment);
|
||||
if (handler != null) handler.failed(e, dst);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <A> void read(ByteBuffer dst, long timeout, TimeUnit unit, A attachment, CompletionHandler<Integer, ? super A> handler) {
|
||||
read(dst, attachment, handler);
|
||||
public void read(long timeout, TimeUnit unit, CompletionHandler<Integer, ByteBuffer> handler) {
|
||||
read(handler);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<Integer> read(ByteBuffer dst) {
|
||||
try {
|
||||
int rs = channel.read(dst);
|
||||
this.readtime = System.currentTimeMillis();
|
||||
return CompletableFuture.completedFuture(rs);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
public int read(ByteBuffer dst) throws IOException {
|
||||
int rs = channel.read(dst);
|
||||
this.readtime = System.currentTimeMillis();
|
||||
return rs;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -165,14 +166,10 @@ public class UdpBioAsyncConnection extends AsyncConnection {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<Integer> write(ByteBuffer src) {
|
||||
try {
|
||||
int rs = channel.send(src, remoteAddress);
|
||||
this.writetime = System.currentTimeMillis();
|
||||
return CompletableFuture.completedFuture(rs);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
public int write(ByteBuffer src) throws IOException {
|
||||
int rs = channel.send(src, remoteAddress);
|
||||
this.writetime = System.currentTimeMillis();
|
||||
return rs;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -85,7 +85,8 @@ public class UdpBioProtocolServer extends ProtocolServer {
|
||||
try {
|
||||
SocketAddress address = serchannel.receive(buffer);
|
||||
buffer.flip();
|
||||
AsyncConnection conn = new UdpBioAsyncConnection(serchannel, address, false, readTimeoutSeconds, writeTimeoutSeconds, null, null);
|
||||
AsyncConnection conn = new UdpBioAsyncConnection(context.getBufferSupplier(), context.getBufferConsumer(), serchannel,
|
||||
context.getSSLContext(), address, false, readTimeoutSeconds, writeTimeoutSeconds, null, null);
|
||||
context.runAsync(new PrepareRunner(context, conn, buffer, null));
|
||||
} catch (Exception e) {
|
||||
context.offerBuffer(buffer);
|
||||
|
||||
@@ -17,6 +17,8 @@ import java.util.concurrent.*;
|
||||
*/
|
||||
public class WorkThread extends Thread {
|
||||
|
||||
protected Thread localThread;
|
||||
|
||||
private final ExecutorService executor;
|
||||
|
||||
public WorkThread(ExecutorService executor, Runnable runner) {
|
||||
@@ -32,4 +34,19 @@ public class WorkThread extends Thread {
|
||||
public ExecutorService getExecutor() {
|
||||
return executor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
this.localThread = Thread.currentThread();
|
||||
super.run();
|
||||
}
|
||||
|
||||
public boolean inSameThread() {
|
||||
return this.localThread == Thread.currentThread();
|
||||
}
|
||||
|
||||
public boolean inSameThread(Thread thread) {
|
||||
return this.localThread == thread;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -5,12 +5,10 @@
|
||||
*/
|
||||
package org.redkale.net.http;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import org.redkale.asm.MethodDebugVisitor;
|
||||
import java.nio.channels.CompletionHandler;
|
||||
import java.security.*;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.function.*;
|
||||
import org.redkale.asm.*;
|
||||
import static org.redkale.asm.Opcodes.*;
|
||||
import org.redkale.net.*;
|
||||
@@ -49,21 +47,6 @@ public class HttpContext extends Context {
|
||||
return responsePool;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Consumer<ByteBuffer> getBufferConsumer() {
|
||||
return super.getBufferConsumer();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void offerBuffer(ByteBuffer buffer) {
|
||||
super.offerBuffer(buffer);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void offerBuffer(ByteBuffer... buffers) {
|
||||
super.offerBuffer(buffers);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected <H extends CompletionHandler> Creator<H> loadAsyncHandlerCreator(Class<H> handlerClass) {
|
||||
Creator<H> creator = asyncHandlerCreators.get(handlerClass);
|
||||
|
||||
@@ -126,7 +126,8 @@ public class HttpPrepareServlet extends PrepareServlet<String, HttpContext, Http
|
||||
List<HttpServlet> list = removeHttpServlet(predicateEntry, predicateFilter);
|
||||
return list == null || list.isEmpty() ? null : list.get(0);
|
||||
}
|
||||
@SuppressWarnings("unchecked")
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T extends WebSocket> HttpServlet removeHttpServlet(Class<T> websocketOrServletType) {
|
||||
Predicate<MappingEntry> predicateEntry = (t) -> {
|
||||
Class type = t.servlet.getClass();
|
||||
@@ -151,7 +152,7 @@ public class HttpPrepareServlet extends PrepareServlet<String, HttpContext, Http
|
||||
if (forbidURIMaps != null && forbidURIMaps.containsKey(urlreg)) return false;
|
||||
if (forbidURIMaps == null) forbidURIMaps = new HashMap<>();
|
||||
String mapping = urlreg;
|
||||
if (Utility.contains(mapping, '.', '*', '{', '[', '(', '|', '^', '$', '+', '?', '\\')) { //是否是正则表达式))
|
||||
if (Utility.contains(mapping, '*', '{', '[', '(', '|', '^', '$', '+', '?', '\\')) { //是否是正则表达式))
|
||||
if (mapping.endsWith("/*")) {
|
||||
mapping = mapping.substring(0, mapping.length() - 1) + ".*";
|
||||
} else {
|
||||
@@ -253,7 +254,7 @@ public class HttpPrepareServlet extends PrepareServlet<String, HttpContext, Http
|
||||
logger.log(Level.WARNING, "init HttpRender(" + renderType + ") error", e);
|
||||
}
|
||||
}
|
||||
Collections.sort(renders, (o1, o2) -> o1.getType().isAssignableFrom(o2.getType()) ? 1 : -1);
|
||||
renders.sort((o1, o2) -> o1.getType().isAssignableFrom(o2.getType()) ? 1 : -1);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -310,7 +311,7 @@ public class HttpPrepareServlet extends PrepareServlet<String, HttpContext, Http
|
||||
}
|
||||
servlet.execute(request, response);
|
||||
} catch (Exception e) {
|
||||
request.getContext().getLogger().log(Level.WARNING, "Servlet occur, forece to close channel. request = " + request, e);
|
||||
request.getContext().getLogger().log(Level.WARNING, "Servlet occur, force to close channel. request = " + request, e);
|
||||
response.finish(500, null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -165,17 +165,18 @@ public class HttpRequest extends Request<HttpContext> {
|
||||
header.addValue(name, value);
|
||||
}
|
||||
}
|
||||
array.clear();
|
||||
if (buffer.hasRemaining()) array.write(buffer, buffer.remaining());
|
||||
if (this.contentType != null && this.contentType.contains("boundary=")) {
|
||||
this.boundary = true;
|
||||
}
|
||||
if (this.contentType != null && this.contentType.contains("boundary=")) this.boundary = true;
|
||||
if (this.boundary) this.keepAlive = false; //文件上传必须设置keepAlive为false,因为文件过大时用户不一定会skip掉多余的数据
|
||||
|
||||
array.clear();
|
||||
if (this.contentLength > 0 && (this.contentType == null || !this.boundary)) {
|
||||
if (this.contentLength > context.getMaxbody()) return -1;
|
||||
array.write(buffer, Math.min((int) this.contentLength, buffer.remaining()));
|
||||
int lr = (int) this.contentLength - array.size();
|
||||
return lr > 0 ? lr : 0;
|
||||
}
|
||||
if (buffer.hasRemaining() && (this.boundary || !this.keepAlive)) array.write(buffer, buffer.remaining()); //文件上传、HTTP1.0或Connection:close
|
||||
//暂不考虑是keep-alive且存在body却没有指定Content-Length的情况
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -161,7 +161,7 @@ public class HttpResourceServlet extends HttpServlet {
|
||||
}
|
||||
}
|
||||
|
||||
public void serRoot(String rootstr) {
|
||||
public void setRoot(String rootstr) {
|
||||
if (rootstr == null) return;
|
||||
try {
|
||||
this.root = new File(rootstr).getCanonicalFile();
|
||||
@@ -170,7 +170,7 @@ public class HttpResourceServlet extends HttpServlet {
|
||||
}
|
||||
}
|
||||
|
||||
public void serRoot(File file) {
|
||||
public void setRoot(File file) {
|
||||
if (file == null) return;
|
||||
try {
|
||||
this.root = file.getCanonicalFile();
|
||||
|
||||
@@ -259,7 +259,7 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
|
||||
return Utility.createAsyncHandler((v, a) -> {
|
||||
finish(v);
|
||||
}, (t, a) -> {
|
||||
context.getLogger().log(Level.WARNING, "Servlet occur, forece to close channel. request = " + request + ", result is CompletionHandler", (Throwable) t);
|
||||
context.getLogger().log(Level.WARNING, "Servlet occur, force to close channel. request = " + request + ", result is CompletionHandler", (Throwable) t);
|
||||
finish(500, null);
|
||||
});
|
||||
}
|
||||
@@ -471,7 +471,7 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
|
||||
} else if (obj instanceof CompletableFuture) {
|
||||
((CompletableFuture) obj).whenComplete((v, e) -> {
|
||||
if (e != null) {
|
||||
context.getLogger().log(Level.WARNING, "Servlet occur, forece to close channel. request = " + request + ", result is CompletableFuture", (Throwable) e);
|
||||
context.getLogger().log(Level.WARNING, "Servlet occur, force to close channel. request = " + request + ", result is CompletableFuture", (Throwable) e);
|
||||
finish(500, null);
|
||||
return;
|
||||
}
|
||||
@@ -489,7 +489,7 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
|
||||
try {
|
||||
finish((File) obj);
|
||||
} catch (IOException e) {
|
||||
context.getLogger().log(Level.WARNING, "HttpServlet finish File occur, forece to close channel. request = " + getRequest(), e);
|
||||
context.getLogger().log(Level.WARNING, "HttpServlet finish File occur, force to close channel. request = " + getRequest(), e);
|
||||
finish(500, null);
|
||||
}
|
||||
} else if (obj instanceof HttpResult) {
|
||||
@@ -614,15 +614,7 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
|
||||
*/
|
||||
@Override
|
||||
public void finish(final byte[] bs) {
|
||||
if (isClosed()) return; //避免重复关闭
|
||||
if (this.context.getBufferCapacity() >= bs.length) {
|
||||
ByteBuffer buffer = getBodyBufferSupplier().get();
|
||||
buffer.put(bs);
|
||||
buffer.flip();
|
||||
this.finish(false, buffer);
|
||||
} else {
|
||||
this.finish(false, ByteBuffer.wrap(bs));
|
||||
}
|
||||
this.finish(this.contentType, bs);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -633,15 +625,30 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
|
||||
*/
|
||||
public void finish(final String contentType, final byte[] bs) {
|
||||
if (isClosed()) return; //避免重复关闭
|
||||
this.contentType = contentType;
|
||||
if (this.context.getBufferCapacity() >= bs.length) {
|
||||
ByteBuffer buffer = getBodyBufferSupplier().get();
|
||||
buffer.put(bs);
|
||||
buffer.flip();
|
||||
this.finish(false, buffer);
|
||||
final byte[] content = bs == null ? new byte[0] : bs;
|
||||
if (!this.headsended) {
|
||||
this.contentType = contentType;
|
||||
this.contentLength = content.length;
|
||||
ByteBuffer headbuf = createHeader();
|
||||
if (headbuf.remaining() >= content.length) {
|
||||
headbuf.put(content);
|
||||
headbuf.flip();
|
||||
super.finish(false, headbuf);
|
||||
} else {
|
||||
headbuf.flip();
|
||||
super.finish(false, new ByteBuffer[]{headbuf, ByteBuffer.wrap(content)});
|
||||
}
|
||||
} else {
|
||||
this.finish(false, ByteBuffer.wrap(bs));
|
||||
if (this.context.getBufferCapacity() >= content.length) {
|
||||
ByteBuffer buffer = getBodyBufferSupplier().get();
|
||||
buffer.put(content);
|
||||
buffer.flip();
|
||||
this.finish(false, buffer);
|
||||
} else {
|
||||
this.finish(false, ByteBuffer.wrap(content));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -887,16 +894,21 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
|
||||
buffer.put(("HTTP/1.1 " + this.status + " " + httpCodes.get(this.status) + "\r\n").getBytes());
|
||||
}
|
||||
if (this.contentLength >= 0) buffer.put(("Content-Length: " + this.contentLength + "\r\n").getBytes());
|
||||
if (this.contentType == this.jsonContentType) {
|
||||
buffer.put(this.jsonContentTypeBytes);
|
||||
} else if (this.contentType == null || this.contentType == this.plainContentType) {
|
||||
buffer.put(this.plainContentTypeBytes);
|
||||
} else {
|
||||
buffer.put(("Content-Type: " + (this.contentType == null ? this.plainContentType : this.contentType) + "\r\n").getBytes());
|
||||
if (!this.request.isWebSocket()) {
|
||||
if (this.contentType == this.jsonContentType) {
|
||||
buffer.put(this.jsonContentTypeBytes);
|
||||
} else if (this.contentType == null || this.contentType == this.plainContentType) {
|
||||
buffer.put(this.plainContentTypeBytes);
|
||||
} else {
|
||||
buffer.put(("Content-Type: " + (this.contentType == null ? this.plainContentType : this.contentType) + "\r\n").getBytes());
|
||||
}
|
||||
}
|
||||
buffer.put(serverNameBytes);
|
||||
if (dateSupplier != null) buffer.put(dateSupplier.get());
|
||||
buffer.put(this.request.isKeepAlive() ? connectAliveBytes : connectCloseBytes);
|
||||
|
||||
if (this.header.getValue("Connection") == null) {
|
||||
buffer.put(this.request.isKeepAlive() ? connectAliveBytes : connectCloseBytes);
|
||||
}
|
||||
|
||||
if (this.defaultAddHeaders != null) {
|
||||
for (String[] headers : this.defaultAddHeaders) {
|
||||
@@ -929,7 +941,7 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
|
||||
}
|
||||
if (request.newsessionid != null) {
|
||||
String domain = defaultCookie == null ? null : defaultCookie.getDomain();
|
||||
if (domain == null) {
|
||||
if (domain == null || domain.isEmpty()) {
|
||||
domain = "";
|
||||
} else {
|
||||
domain = "Domain=" + domain + "; ";
|
||||
@@ -937,9 +949,9 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
|
||||
String path = defaultCookie == null ? null : defaultCookie.getPath();
|
||||
if (path == null || path.isEmpty()) path = "/";
|
||||
if (request.newsessionid.isEmpty()) {
|
||||
buffer.put(("Set-Cookie: " + HttpRequest.SESSIONID_NAME + "=; " + domain + "Path=" + path + "; Max-Age=0; HttpOnly\r\n").getBytes());
|
||||
buffer.put(("Set-Cookie: " + HttpRequest.SESSIONID_NAME + "=; " + domain + "Path=/; Max-Age=0; HttpOnly\r\n").getBytes());
|
||||
} else {
|
||||
buffer.put(("Set-Cookie: " + HttpRequest.SESSIONID_NAME + "=" + request.newsessionid + "; " + domain + "Path=" + path + "; HttpOnly\r\n").getBytes());
|
||||
buffer.put(("Set-Cookie: " + HttpRequest.SESSIONID_NAME + "=" + request.newsessionid + "; " + domain + "Path=/; HttpOnly\r\n").getBytes());
|
||||
}
|
||||
}
|
||||
if (this.cookies != null) {
|
||||
|
||||
@@ -256,17 +256,19 @@ public class HttpServer extends Server<String, HttpContext, HttpRequest, HttpRes
|
||||
final boolean sncp = Sncp.isSncpDyn(service);
|
||||
final String resname = name == null ? (sncp ? Sncp.getResourceName(service) : "") : name;
|
||||
final Class<S> serviceType = Sncp.getServiceType(service);
|
||||
for (final HttpServlet item : ((HttpPrepareServlet) this.prepare).getServlets()) {
|
||||
if (!(item instanceof HttpServlet)) continue;
|
||||
if (item.getClass().getAnnotation(Rest.RestDyn.class) == null) continue;
|
||||
try {
|
||||
Field field = item.getClass().getDeclaredField(Rest.REST_SERVICE_FIELD_NAME);
|
||||
if (serviceType.equals(field.getType())) {
|
||||
servlet = (T) item;
|
||||
break;
|
||||
if (name != null) {
|
||||
for (final HttpServlet item : ((HttpPrepareServlet) this.prepare).getServlets()) {
|
||||
if (!(item instanceof HttpServlet)) continue;
|
||||
if (item.getClass().getAnnotation(Rest.RestDyn.class) == null) continue;
|
||||
try {
|
||||
Field field = item.getClass().getDeclaredField(Rest.REST_SERVICE_FIELD_NAME);
|
||||
if (serviceType.equals(field.getType())) {
|
||||
servlet = (T) item;
|
||||
break;
|
||||
}
|
||||
} catch (NoSuchFieldException | SecurityException e) {
|
||||
logger.log(Level.SEVERE, "serviceType = " + serviceType + ", servletClass = " + item.getClass(), e);
|
||||
}
|
||||
} catch (NoSuchFieldException | SecurityException e) {
|
||||
logger.log(Level.SEVERE, "serviceType = " + serviceType + ", servletClass = " + item.getClass(), e);
|
||||
}
|
||||
}
|
||||
final boolean first = servlet == null;
|
||||
|
||||
@@ -34,13 +34,15 @@ public class HttpServlet extends Servlet<HttpContext, HttpRequest, HttpResponse>
|
||||
|
||||
String _prefix = ""; //当前HttpServlet的path前缀
|
||||
|
||||
private Map.Entry<String, Entry>[] mappings;
|
||||
HashMap<String, InnerActionEntry> _tmpentrys; //Rest生成时赋值, 字段名Rest有用到
|
||||
|
||||
private Map.Entry<String, InnerActionEntry>[] mappings; //字段名Rest有用到
|
||||
|
||||
//这里不能直接使用HttpServlet,会造成死循环初始化HttpServlet
|
||||
private final Servlet<HttpContext, HttpRequest, HttpResponse> authSuccessServlet = new Servlet<HttpContext, HttpRequest, HttpResponse>() {
|
||||
@Override
|
||||
public void execute(HttpRequest request, HttpResponse response) throws IOException {
|
||||
Entry entry = (Entry) request.attachment;
|
||||
InnerActionEntry entry = (InnerActionEntry) request.attachment;
|
||||
if (entry.cacheseconds > 0) {//有缓存设置
|
||||
CacheEntry ce = entry.cache.get(request.getRequestURI());
|
||||
if (ce != null && ce.time + entry.cacheseconds > System.currentTimeMillis()) { //缓存有效
|
||||
@@ -59,9 +61,9 @@ public class HttpServlet extends Servlet<HttpContext, HttpRequest, HttpResponse>
|
||||
private final Servlet<HttpContext, HttpRequest, HttpResponse> preSuccessServlet = new Servlet<HttpContext, HttpRequest, HttpResponse>() {
|
||||
@Override
|
||||
public void execute(HttpRequest request, HttpResponse response) throws IOException {
|
||||
for (Map.Entry<String, Entry> en : mappings) {
|
||||
for (Map.Entry<String, InnerActionEntry> en : mappings) {
|
||||
if (request.getRequestURI().startsWith(en.getKey())) {
|
||||
Entry entry = en.getValue();
|
||||
InnerActionEntry entry = en.getValue();
|
||||
if (!entry.checkMethod(request.getMethod())) {
|
||||
response.finishJson(new RetResult(RET_METHOD_ERROR, "Method(" + request.getMethod() + ") Error"));
|
||||
return;
|
||||
@@ -69,11 +71,11 @@ public class HttpServlet extends Servlet<HttpContext, HttpRequest, HttpResponse>
|
||||
request.attachment = entry;
|
||||
request.moduleid = entry.moduleid;
|
||||
request.actionid = entry.actionid;
|
||||
if (entry.ignore) {
|
||||
authSuccessServlet.execute(request, response);
|
||||
} else {
|
||||
if (entry.auth) {
|
||||
response.thenEvent(authSuccessServlet);
|
||||
authenticate(request, response);
|
||||
} else {
|
||||
authSuccessServlet.execute(request, response);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -84,13 +86,14 @@ public class HttpServlet extends Servlet<HttpContext, HttpRequest, HttpResponse>
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
void preInit(HttpContext context, AnyValue config) {
|
||||
if (this.mappings != null) return; //无需重复preInit
|
||||
String path = _prefix == null ? "" : _prefix;
|
||||
WebServlet ws = this.getClass().getAnnotation(WebServlet.class);
|
||||
if (ws != null && !ws.repair()) path = "";
|
||||
HashMap<String, Entry> map = load();
|
||||
HashMap<String, InnerActionEntry> map = this._tmpentrys != null ? this._tmpentrys : loadActionEntry();
|
||||
this.mappings = new Map.Entry[map.size()];
|
||||
int i = -1;
|
||||
for (Map.Entry<String, Entry> en : map.entrySet()) {
|
||||
for (Map.Entry<String, InnerActionEntry> en : map.entrySet()) {
|
||||
mappings[++i] = new AbstractMap.SimpleEntry<>(path + en.getKey(), en.getValue());
|
||||
}
|
||||
//必须要倒排序, /query /query1 /query12 确保含子集的优先匹配 /query12 /query1 /query
|
||||
@@ -163,10 +166,10 @@ public class HttpServlet extends Servlet<HttpContext, HttpRequest, HttpResponse>
|
||||
preExecute(request, response);
|
||||
}
|
||||
|
||||
private HashMap<String, Entry> load() {
|
||||
private HashMap<String, InnerActionEntry> loadActionEntry() {
|
||||
WebServlet module = this.getClass().getAnnotation(WebServlet.class);
|
||||
final int serviceid = module == null ? 0 : module.moduleid();
|
||||
final HashMap<String, Entry> map = new HashMap<>();
|
||||
final HashMap<String, InnerActionEntry> map = new HashMap<>();
|
||||
HashMap<String, Class> nameset = new HashMap<>();
|
||||
final Class selfClz = this.getClass();
|
||||
Class clz = this.getClass();
|
||||
@@ -197,13 +200,82 @@ public class HttpServlet extends Servlet<HttpContext, HttpRequest, HttpResponse>
|
||||
throw new RuntimeException(this.getClass().getSimpleName() + " have two same " + HttpMapping.class.getSimpleName() + "(" + name + ")");
|
||||
}
|
||||
nameset.put(name, clz);
|
||||
map.put(name, new Entry(serviceid, actionid, name, methods, method, createHttpServlet(method)));
|
||||
map.put(name, new InnerActionEntry(serviceid, actionid, name, methods, method, createActionServlet(method)));
|
||||
}
|
||||
} while ((clz = clz.getSuperclass()) != HttpServlet.class);
|
||||
return map;
|
||||
}
|
||||
|
||||
private HttpServlet createHttpServlet(final Method method) {
|
||||
protected static final class InnerActionEntry {
|
||||
|
||||
InnerActionEntry(int moduleid, int actionid, String name, String[] methods, Method method, HttpServlet servlet) {
|
||||
this(moduleid, actionid, name, methods, method, auth(method), cacheseconds(method), servlet);
|
||||
}
|
||||
|
||||
//供Rest类使用,参数不能随便更改
|
||||
public InnerActionEntry(int moduleid, int actionid, String name, String[] methods, Method method, boolean auth, int cacheseconds, HttpServlet servlet) {
|
||||
this.moduleid = moduleid;
|
||||
this.actionid = actionid;
|
||||
this.name = name;
|
||||
this.methods = methods;
|
||||
this.method = method; //rest构建会为null
|
||||
this.servlet = servlet;
|
||||
this.auth = auth;
|
||||
this.cacheseconds = cacheseconds;
|
||||
this.cache = cacheseconds > 0 ? new ConcurrentHashMap<>() : null;
|
||||
this.cacheHandler = cacheseconds > 0 ? (HttpResponse response, ByteBuffer[] buffers) -> {
|
||||
int status = response.getStatus();
|
||||
if (status != 200) return null;
|
||||
CacheEntry ce = new CacheEntry(response.getStatus(), response.getContentType(), buffers);
|
||||
cache.put(response.getRequest().getRequestURI(), ce);
|
||||
return ce.getBuffers();
|
||||
} : null;
|
||||
}
|
||||
|
||||
private static boolean auth(Method method) {
|
||||
HttpMapping mapping = method.getAnnotation(HttpMapping.class);
|
||||
return mapping == null || mapping.auth();
|
||||
}
|
||||
|
||||
private static int cacheseconds(Method method) {
|
||||
HttpMapping mapping = method.getAnnotation(HttpMapping.class);
|
||||
return mapping == null ? 0 : mapping.cacheseconds();
|
||||
}
|
||||
|
||||
boolean isNeedCheck() {
|
||||
return this.moduleid != 0 || this.actionid != 0;
|
||||
}
|
||||
|
||||
boolean checkMethod(final String reqMethod) {
|
||||
if (methods.length == 0) return true;
|
||||
for (String m : methods) {
|
||||
if (reqMethod.equalsIgnoreCase(m)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
final BiFunction<HttpResponse, ByteBuffer[], ByteBuffer[]> cacheHandler;
|
||||
|
||||
final ConcurrentHashMap<String, CacheEntry> cache;
|
||||
|
||||
final int cacheseconds;
|
||||
|
||||
final boolean auth;
|
||||
|
||||
final int moduleid;
|
||||
|
||||
final int actionid;
|
||||
|
||||
final String name;
|
||||
|
||||
final String[] methods;
|
||||
|
||||
final Method method;
|
||||
|
||||
final HttpServlet servlet;
|
||||
}
|
||||
|
||||
private HttpServlet createActionServlet(final Method method) {
|
||||
//------------------------------------------------------------------------------
|
||||
final String supDynName = HttpServlet.class.getName().replace('.', '/');
|
||||
final String interName = this.getClass().getName().replace('.', '/');
|
||||
@@ -282,61 +354,6 @@ public class HttpServlet extends Servlet<HttpContext, HttpRequest, HttpResponse>
|
||||
}
|
||||
}
|
||||
|
||||
private static final class Entry {
|
||||
|
||||
public Entry(int moduleid, int actionid, String name, String[] methods, Method method, HttpServlet servlet) {
|
||||
this.moduleid = moduleid;
|
||||
this.actionid = actionid;
|
||||
this.name = name;
|
||||
this.methods = methods;
|
||||
this.method = method;
|
||||
this.servlet = servlet;
|
||||
HttpMapping mapping = method.getAnnotation(HttpMapping.class);
|
||||
this.ignore = mapping == null || !mapping.auth();
|
||||
this.cacheseconds = mapping == null ? 0 : mapping.cacheseconds();
|
||||
this.cache = cacheseconds > 0 ? new ConcurrentHashMap<>() : null;
|
||||
this.cacheHandler = cacheseconds > 0 ? (HttpResponse response, ByteBuffer[] buffers) -> {
|
||||
int status = response.getStatus();
|
||||
if (status != 200) return null;
|
||||
CacheEntry ce = new CacheEntry(response.getStatus(), response.getContentType(), buffers);
|
||||
cache.put(response.getRequest().getRequestURI(), ce);
|
||||
return ce.getBuffers();
|
||||
} : null;
|
||||
}
|
||||
|
||||
public boolean isNeedCheck() {
|
||||
return this.moduleid != 0 || this.actionid != 0;
|
||||
}
|
||||
|
||||
public boolean checkMethod(final String reqMethod) {
|
||||
if (methods.length == 0) return true;
|
||||
for (String m : methods) {
|
||||
if (reqMethod.equalsIgnoreCase(m)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public final BiFunction<HttpResponse, ByteBuffer[], ByteBuffer[]> 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();
|
||||
|
||||
@@ -12,7 +12,7 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||
/**
|
||||
* 配合 HttpServlet 使用。
|
||||
* 用于指定HttpRequest.currentUser的数据类型。<br>
|
||||
* 注意: 数据类型是JavaBean,则必须要用javax.persistence.Id标记主键字段,用于确定用户ID
|
||||
* 注意: 数据类型是JavaBean
|
||||
*
|
||||
* <p>
|
||||
* 详情见: https://redkale.org
|
||||
|
||||
@@ -39,6 +39,8 @@ public final class Rest {
|
||||
|
||||
static final String REST_SERVICE_FIELD_NAME = "_redkale_service";
|
||||
|
||||
static final String REST_TOSTRINGOBJ_FIELD_NAME = "_redkale_tostringsupplier";
|
||||
|
||||
static final String REST_JSONCONVERT_FIELD_PREFIX = "_redkale_jsonconvert_";
|
||||
|
||||
static final String REST_SERVICEMAP_FIELD_NAME = "_redkale_servicemap"; //如果只有name=""的Service资源,则实例中_servicemap必须为null
|
||||
@@ -127,25 +129,37 @@ public final class Rest {
|
||||
}
|
||||
}
|
||||
|
||||
static JsonConvert createJsonConvert(RestConvert[] converts) {
|
||||
if (converts == null || converts.length < 1) return JsonConvert.root();
|
||||
static JsonConvert createJsonConvert(RestConvert[] converts, RestConvertCoder[] coders) {
|
||||
if ((converts == null || converts.length < 1) && (coders == null || coders.length < 1)) return JsonConvert.root();
|
||||
final JsonFactory childFactory = JsonFactory.create();
|
||||
List<Class> types = new ArrayList<>();
|
||||
for (RestConvert rc : converts) {
|
||||
if (rc.type() == void.class || rc.type() == Void.class) {
|
||||
return JsonFactory.create().skipAllIgnore(true).getConvert();
|
||||
Set<Class> reloadTypes = new HashSet<>();
|
||||
if (coders != null) {
|
||||
for (RestConvertCoder rcc : coders) {
|
||||
reloadTypes.add(rcc.type());
|
||||
childFactory.register(rcc.type(), rcc.field(), Creator.create(rcc.coder()).create());
|
||||
}
|
||||
if (types.contains(rc.type())) throw new RuntimeException("@RestConvert type(" + rc.type() + ") repeat");
|
||||
if (rc.skipIgnore()) {
|
||||
childFactory.registerSkipIgnore(rc.type());
|
||||
childFactory.reloadCoder(rc.type());
|
||||
} else {
|
||||
childFactory.register(rc.type(), false, rc.convertColumns());
|
||||
childFactory.register(rc.type(), true, rc.ignoreColumns());
|
||||
childFactory.reloadCoder(rc.type());
|
||||
}
|
||||
if (converts != null) {
|
||||
for (RestConvert rc : converts) {
|
||||
if (rc.type() == void.class || rc.type() == Void.class) {
|
||||
return JsonFactory.create().skipAllIgnore(true).getConvert();
|
||||
}
|
||||
if (types.contains(rc.type())) throw new RuntimeException("@RestConvert type(" + rc.type() + ") repeat");
|
||||
if (rc.skipIgnore()) {
|
||||
childFactory.registerSkipIgnore(rc.type());
|
||||
childFactory.reloadCoder(rc.type());
|
||||
} else {
|
||||
childFactory.register(rc.type(), false, rc.convertColumns());
|
||||
childFactory.register(rc.type(), true, rc.ignoreColumns());
|
||||
childFactory.reloadCoder(rc.type());
|
||||
}
|
||||
types.add(rc.type());
|
||||
childFactory.tiny(rc.tiny());
|
||||
}
|
||||
types.add(rc.type());
|
||||
childFactory.tiny(rc.tiny());
|
||||
}
|
||||
for (Class type : reloadTypes) {
|
||||
childFactory.reloadCoder(type);
|
||||
}
|
||||
return childFactory.getConvert();
|
||||
}
|
||||
@@ -239,7 +253,19 @@ public final class Rest {
|
||||
final String resourceGenericDescriptor = sb1.length() == sb2.length() ? null : sb2.toString();
|
||||
|
||||
//----------------------------------------------------------------------------------------
|
||||
final Map<String, List<String>> asmParamMap = MethodParamClassVisitor.getMethodParamNames(new HashMap<>(), webSocketType);
|
||||
boolean namePresent = false;
|
||||
try {
|
||||
Method m0 = null;
|
||||
for (Method method : webSocketType.getMethods()) {
|
||||
if (method.getParameterCount() > 0) {
|
||||
m0 = method;
|
||||
break;
|
||||
}
|
||||
}
|
||||
namePresent = m0 == null ? true : m0.getParameters()[0].isNamePresent();
|
||||
} catch (Exception e) {
|
||||
}
|
||||
final Map<String, List<String>> asmParamMap = namePresent ? null : MethodParamClassVisitor.getMethodParamNames(new HashMap<>(), webSocketType);
|
||||
final Set<String> messageNames = new HashSet<>();
|
||||
final List<Method> messageMethods = new ArrayList<>();
|
||||
for (Method method : webSocketType.getMethods()) {
|
||||
@@ -349,6 +375,10 @@ public final class Rest {
|
||||
pushInt(mv, rws.wsmaxbody());
|
||||
mv.visitFieldInsn(PUTFIELD, newDynName, "wsmaxbody", "I");
|
||||
|
||||
mv.visitVarInsn(ALOAD, 0);
|
||||
mv.visitInsn(rws.mergemsg() ? ICONST_1 : ICONST_0);
|
||||
mv.visitFieldInsn(PUTFIELD, newDynName, "mergemsg", "Z");
|
||||
|
||||
mv.visitVarInsn(ALOAD, 0);
|
||||
mv.visitInsn(rws.single() ? ICONST_1 : ICONST_0);
|
||||
mv.visitFieldInsn(PUTFIELD, newDynName, "single", "Z");
|
||||
@@ -402,7 +432,7 @@ public final class Rest {
|
||||
cw2.visitInnerClass(newDynMessageFullName + endfix, newDynName, newDynMessageSimpleName + endfix, ACC_PUBLIC + ACC_STATIC);
|
||||
Set<String> paramnames = new HashSet<>();
|
||||
String methodesc = method.getName() + ":" + Type.getMethodDescriptor(method);
|
||||
List<String> names = asmParamMap.get(methodesc);
|
||||
List<String> names = asmParamMap == null ? null : asmParamMap.get(methodesc);
|
||||
if (names != null) while (names.remove(" ")); //删掉空元素
|
||||
Parameter[] params = method.getParameters();
|
||||
final LinkedHashMap<String, Parameter> paramap = new LinkedHashMap(); //必须使用LinkedHashMap确保顺序
|
||||
@@ -676,7 +706,8 @@ public final class Rest {
|
||||
final String retDesc = Type.getDescriptor(RetResult.class);
|
||||
final String futureDesc = Type.getDescriptor(CompletableFuture.class);
|
||||
final String flipperDesc = Type.getDescriptor(Flipper.class);
|
||||
final String httprsDesc = Type.getDescriptor(HttpResult.class);
|
||||
final String httpServletName = HttpServlet.class.getName().replace('.', '/');
|
||||
final String innerEntryName = HttpServlet.InnerActionEntry.class.getName().replace('.', '/');
|
||||
final String attrDesc = Type.getDescriptor(org.redkale.util.Attribute.class);
|
||||
final String multiContextDesc = Type.getDescriptor(MultiContext.class);
|
||||
final String multiContextName = MultiContext.class.getName().replace('.', '/');
|
||||
@@ -693,12 +724,21 @@ public final class Rest {
|
||||
|
||||
HttpUserType hut = baseServletType.getAnnotation(HttpUserType.class);
|
||||
final Class userType = (userType0 == null || userType0 == Object.class) ? (hut == null ? null : hut.value()) : userType0;
|
||||
if (userType != null && (userType.isPrimitive() || userType.getName().startsWith("java.") || userType.getName().startsWith("javax."))) {
|
||||
throw new RuntimeException(HttpUserType.class.getSimpleName() + " must be a JavaBean but found " + userType);
|
||||
}
|
||||
|
||||
final String supDynName = baseServletType.getName().replace('.', '/');
|
||||
final RestService controller = serviceType.getAnnotation(RestService.class);
|
||||
if (controller != null && controller.ignore()) throw new RuntimeException(serviceType + " is ignore Rest Service Class"); //标记为ignore=true不创建Servlet
|
||||
ClassLoader loader = classLoader == null ? Thread.currentThread().getContextClassLoader() : classLoader;
|
||||
String newDynName = serviceTypeInternalName.substring(0, serviceTypeInternalName.lastIndexOf('/') + 1) + "_Dyn" + serviceType.getSimpleName().replaceAll("Service.*$", "") + "RestServlet";
|
||||
String stname = serviceType.getSimpleName();
|
||||
if (stname.startsWith("Service")) { //类似ServiceWatchService这样的类保留第一个Service字样
|
||||
stname = "Service" + stname.substring("Service".length()).replaceAll("Service.*$", "");
|
||||
} else {
|
||||
stname = stname.replaceAll("Service.*$", "");
|
||||
}
|
||||
String newDynName = serviceTypeInternalName.substring(0, serviceTypeInternalName.lastIndexOf('/') + 1) + "_Dyn" + stname + "RestServlet";
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
final String defmodulename = getWebModuleNameLowerCase(serviceType);
|
||||
@@ -750,13 +790,12 @@ public final class Rest {
|
||||
if (ignore) continue;
|
||||
|
||||
Class[] extypes = method.getExceptionTypes();
|
||||
if (extypes.length > 1) {
|
||||
if (mappings != null && mappings.length > 0) throw new RuntimeException("@" + RestMapping.class.getSimpleName() + " only for method with throws IOException");
|
||||
continue;
|
||||
}
|
||||
if (extypes.length == 1 && extypes[0] != IOException.class) {
|
||||
if (mappings != null && mappings.length > 0) throw new RuntimeException("@" + RestMapping.class.getSimpleName() + " only for method with throws IOException");
|
||||
continue;
|
||||
if (extypes.length > 0) {
|
||||
for (Class exp : extypes) {
|
||||
if (!RuntimeException.class.isAssignableFrom(exp) && !IOException.class.isAssignableFrom(exp)) {
|
||||
throw new RuntimeException("@" + RestMapping.class.getSimpleName() + " only for method(" + method + ") with throws IOException");
|
||||
}
|
||||
}
|
||||
}
|
||||
paramtypes.add(TypeToken.getGenericType(method.getGenericParameterTypes(), serviceType));
|
||||
if (mappings.length == 0) { //没有Mapping,设置一个默认值
|
||||
@@ -774,9 +813,10 @@ public final class Rest {
|
||||
}
|
||||
if (entrys.isEmpty()) return null; //没有可HttpMapping的方法
|
||||
|
||||
RestClassLoader newLoader = new RestClassLoader(loader);
|
||||
final int moduleid = controller == null ? 0 : controller.moduleid();
|
||||
{ //注入 @WebServlet 注解
|
||||
String urlpath = "";
|
||||
int moduleid = controller == null ? 0 : controller.moduleid();
|
||||
boolean repair = controller == null ? true : controller.repair();
|
||||
String comment = controller == null ? "" : controller.comment();
|
||||
av0 = cw.visitAnnotation(webServletDesc, true);
|
||||
@@ -812,7 +852,13 @@ public final class Rest {
|
||||
classMap.put("repair", repair);
|
||||
//classMap.put("comment", comment); //不显示太多信息
|
||||
}
|
||||
{ //内部类
|
||||
cw.visitInnerClass(innerEntryName, httpServletName, HttpServlet.InnerActionEntry.class.getSimpleName(), ACC_PROTECTED + ACC_FINAL + ACC_STATIC);
|
||||
|
||||
for (final MappingEntry entry : entrys) {
|
||||
cw.visitInnerClass(newDynName + "$" + entry.newActionClassName, newDynName, entry.newActionClassName, ACC_PRIVATE + ACC_STATIC);
|
||||
}
|
||||
}
|
||||
{ //注入 @Resource private XXXService _service;
|
||||
fv = cw.visitField(ACC_PRIVATE, REST_SERVICE_FIELD_NAME, serviceDesc, null, null);
|
||||
av0 = fv.visitAnnotation(resDesc, true);
|
||||
@@ -835,6 +881,10 @@ public final class Rest {
|
||||
fv = cw.visitField(ACC_PRIVATE, REST_PARAMTYPES_FIELD_NAME, "[[Ljava/lang/reflect/Type;", null, null);
|
||||
fv.visitEnd();
|
||||
}
|
||||
{ //_redkale_tostringsupplier字段 Supplier<String>
|
||||
fv = cw.visitField(ACC_PRIVATE, REST_TOSTRINGOBJ_FIELD_NAME, "Ljava/util/function/Supplier;", "Ljava/util/function/Supplier<Ljava/lang/String;>;", null);
|
||||
fv.visitEnd();
|
||||
}
|
||||
{ //构造函数
|
||||
mv = new MethodDebugVisitor(cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null));
|
||||
//mv.setDebug(true);
|
||||
@@ -846,10 +896,22 @@ public final class Rest {
|
||||
}
|
||||
|
||||
//将每个Service可转换的方法生成HttpServlet对应的HttpMapping方法
|
||||
final Map<String, List<String>> asmParamMap = MethodParamClassVisitor.getMethodParamNames(new HashMap<>(), serviceType);
|
||||
boolean namePresent = false;
|
||||
try {
|
||||
Method m0 = null;
|
||||
for (final MappingEntry entry : entrys) {
|
||||
if (entry.mappingMethod.getParameterCount() > 0) {
|
||||
m0 = entry.mappingMethod;
|
||||
break;
|
||||
}
|
||||
}
|
||||
namePresent = m0 == null ? true : m0.getParameters()[0].isNamePresent();
|
||||
} catch (Exception e) {
|
||||
}
|
||||
final Map<String, List<String>> asmParamMap = namePresent ? null : MethodParamClassVisitor.getMethodParamNames(new HashMap<>(), serviceType);
|
||||
final Map<String, java.lang.reflect.Type> bodyTypes = new HashMap<>();
|
||||
|
||||
final List<RestConvert[]> restConverts = new ArrayList<>();
|
||||
final List<Object[]> restConverts = new ArrayList<>();
|
||||
for (final MappingEntry entry : entrys) {
|
||||
RestUploadFile mupload = null;
|
||||
Class muploadType = null;
|
||||
@@ -859,9 +921,12 @@ public final class Rest {
|
||||
final Parameter[] params = method.getParameters();
|
||||
|
||||
final RestConvert[] rcs = method.getAnnotationsByType(RestConvert.class);
|
||||
if (rcs != null && rcs.length > 0) restConverts.add(rcs);
|
||||
final RestConvertCoder[] rcc = method.getAnnotationsByType(RestConvertCoder.class);
|
||||
if ((rcs != null && rcs.length > 0) || (rcc != null && rcc.length > 0)) {
|
||||
restConverts.add(new Object[]{rcs, rcc});
|
||||
}
|
||||
|
||||
mv = new MethodDebugVisitor(cw.visitMethod(ACC_PUBLIC, entry.name.replace('/', '$').replace('.', '_'), "(" + reqDesc + respDesc + ")V", null, new String[]{"java/io/IOException"}));
|
||||
mv = new MethodDebugVisitor(cw.visitMethod(ACC_PUBLIC, entry.newMethodName, "(" + reqDesc + respDesc + ")V", null, new String[]{"java/io/IOException"}));
|
||||
//mv.setDebug(true);
|
||||
mv.debugLine();
|
||||
|
||||
@@ -1042,6 +1107,7 @@ public final class Rest {
|
||||
mappingMap.put("comment", entry.comment);
|
||||
mappingMap.put("methods", entry.methods);
|
||||
mappingMap.put("result", grt == returnType ? returnType.getName() : String.valueOf(grt));
|
||||
entry.mappingurl = url;
|
||||
}
|
||||
|
||||
{ // 设置 Annotation
|
||||
@@ -1695,8 +1761,99 @@ public final class Rest {
|
||||
mv.visitMaxs(maxStack, maxLocals);
|
||||
mappingMap.put("params", paramMaps);
|
||||
mappingMaps.add(mappingMap);
|
||||
|
||||
{ //_Dync_XXX__HttpServlet.class
|
||||
ClassWriter cw2 = new ClassWriter(COMPUTE_FRAMES);
|
||||
cw2.visit(V1_8, ACC_SUPER, newDynName + "$" + entry.newActionClassName, null, httpServletName, null);
|
||||
|
||||
cw2.visitInnerClass(newDynName + "$" + entry.newActionClassName, newDynName, entry.newActionClassName, ACC_PRIVATE + ACC_STATIC);
|
||||
{
|
||||
fv = cw2.visitField(0, "servlet", "L" + newDynName + ";", null, null);
|
||||
fv.visitEnd();
|
||||
}
|
||||
{
|
||||
mv = new MethodDebugVisitor(cw2.visitMethod(0, "<init>", "(L" + newDynName + ";)V", null, null));
|
||||
mv.visitVarInsn(ALOAD, 0);
|
||||
mv.visitMethodInsn(INVOKESPECIAL, httpServletName, "<init>", "()V", false);
|
||||
mv.visitVarInsn(ALOAD, 0);
|
||||
mv.visitVarInsn(ALOAD, 1);
|
||||
mv.visitFieldInsn(PUTFIELD, newDynName + "$" + entry.newActionClassName, "servlet", "L" + newDynName + ";");
|
||||
mv.visitInsn(RETURN);
|
||||
mv.visitMaxs(2, 2);
|
||||
mv.visitEnd();
|
||||
}
|
||||
if (false) {
|
||||
mv = new MethodDebugVisitor(cw2.visitMethod(ACC_SYNTHETIC, "<init>", "(L" + newDynName + ";L" + newDynName + "$" + entry.newActionClassName + ";)V", null, null));
|
||||
mv.visitVarInsn(ALOAD, 0);
|
||||
mv.visitVarInsn(ALOAD, 1);
|
||||
mv.visitMethodInsn(INVOKESPECIAL, newDynName + "$" + entry.newActionClassName, "<init>", "L" + newDynName + ";", false);
|
||||
mv.visitInsn(RETURN);
|
||||
mv.visitMaxs(2, 3);
|
||||
mv.visitEnd();
|
||||
}
|
||||
{
|
||||
mv = new MethodDebugVisitor(cw2.visitMethod(ACC_PUBLIC, "execute", "(" + reqDesc + respDesc + ")V", null, new String[]{"java/io/IOException"}));
|
||||
mv.visitVarInsn(ALOAD, 0);
|
||||
mv.visitFieldInsn(GETFIELD, newDynName + "$" + entry.newActionClassName, "servlet", "L" + newDynName + ";");
|
||||
mv.visitVarInsn(ALOAD, 1);
|
||||
mv.visitVarInsn(ALOAD, 2);
|
||||
mv.visitMethodInsn(INVOKEVIRTUAL, newDynName, entry.newMethodName, "(" + reqDesc + respDesc + ")V", false);
|
||||
mv.visitInsn(RETURN);
|
||||
mv.visitMaxs(3, 3);
|
||||
mv.visitEnd();
|
||||
}
|
||||
cw2.visitEnd();
|
||||
newLoader.addClass((newDynName + "$" + entry.newActionClassName).replace('/', '.'), cw2.toByteArray());
|
||||
}
|
||||
} // end for each
|
||||
|
||||
// HashMap<String, InnerActionEntry> _createRestInnerActionEntry() {
|
||||
// HashMap<String, InnerActionEntry> map = new HashMap<>();
|
||||
// map.put("asyncfind3", new InnerActionEntry(100000,200000,"asyncfind3", new String[]{},null,false,0, new _Dync_asyncfind3_HttpServlet()));
|
||||
// map.put("asyncfind3", new InnerActionEntry(1,2,"asyncfind2", new String[]{"GET", "POST"},null,true,0, new _Dync_asyncfind2_HttpServlet()));
|
||||
// return map;
|
||||
// }
|
||||
{ //_createRestInnerActionEntry 方法
|
||||
mv = new MethodDebugVisitor(cw.visitMethod(0, "_createRestInnerActionEntry", "()Ljava/util/HashMap;", "()Ljava/util/HashMap<Ljava/lang/String;L" + innerEntryName + ";>;", null));
|
||||
//mv.setDebug(true);
|
||||
mv.visitTypeInsn(NEW, "java/util/HashMap");
|
||||
mv.visitInsn(DUP);
|
||||
mv.visitMethodInsn(INVOKESPECIAL, "java/util/HashMap", "<init>", "()V", false);
|
||||
mv.visitVarInsn(ASTORE, 1);
|
||||
|
||||
for (final MappingEntry entry : entrys) {
|
||||
mv.visitVarInsn(ALOAD, 1);
|
||||
mv.visitLdcInsn(entry.mappingurl); //name
|
||||
mv.visitTypeInsn(NEW, innerEntryName); //new InnerActionEntry
|
||||
mv.visitInsn(DUP);
|
||||
pushInt(mv, moduleid); //moduleid
|
||||
pushInt(mv, entry.actionid); //actionid
|
||||
mv.visitLdcInsn(entry.mappingurl); //name
|
||||
pushInt(mv, entry.methods.length); //methods
|
||||
mv.visitTypeInsn(ANEWARRAY, "java/lang/String");
|
||||
for (int i = 0; i < entry.methods.length; i++) {
|
||||
mv.visitInsn(DUP);
|
||||
pushInt(mv, i);
|
||||
mv.visitLdcInsn(entry.methods[i]);
|
||||
mv.visitInsn(AASTORE);
|
||||
}
|
||||
mv.visitInsn(ACONST_NULL); //method
|
||||
mv.visitInsn(entry.auth ? ICONST_1 : ICONST_0); //auth
|
||||
pushInt(mv, entry.cacheseconds); //cacheseconds
|
||||
mv.visitTypeInsn(NEW, newDynName + "$" + entry.newActionClassName);
|
||||
mv.visitInsn(DUP);
|
||||
mv.visitVarInsn(ALOAD, 0);
|
||||
mv.visitMethodInsn(INVOKESPECIAL, newDynName + "$" + entry.newActionClassName, "<init>", "(L" + newDynName + ";)V", false);
|
||||
mv.visitMethodInsn(INVOKESPECIAL, innerEntryName, "<init>", "(IILjava/lang/String;[Ljava/lang/String;Ljava/lang/reflect/Method;ZILorg/redkale/net/http/HttpServlet;)V", false);
|
||||
mv.visitMethodInsn(INVOKEVIRTUAL, "java/util/HashMap", "put", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;", false);
|
||||
mv.visitInsn(POP);
|
||||
}
|
||||
mv.visitVarInsn(ALOAD, 1);
|
||||
mv.visitInsn(ARETURN);
|
||||
mv.visitMaxs(2, 2);
|
||||
mv.visitEnd();
|
||||
}
|
||||
|
||||
for (Map.Entry<String, java.lang.reflect.Type> en : bodyTypes.entrySet()) {
|
||||
fv = cw.visitField(ACC_PRIVATE, en.getKey(), "Ljava/lang/reflect/Type;", null, null);
|
||||
fv.visitEnd();
|
||||
@@ -1712,20 +1869,24 @@ public final class Rest {
|
||||
fv.visitEnd();
|
||||
}
|
||||
|
||||
//classMap.put("mappings", mappingMaps); //不显示太多信息
|
||||
{ //toString函数
|
||||
//classMap.put("mappings", mappingMaps); //不显示太多信息
|
||||
{ //toString函数
|
||||
mv = new MethodDebugVisitor(cw.visitMethod(ACC_PUBLIC, "toString", "()Ljava/lang/String;", null, null));
|
||||
//mv.setDebug(true);
|
||||
mv.visitLdcInsn(JsonConvert.root().convertTo(classMap));
|
||||
mv.visitVarInsn(ALOAD, 0);
|
||||
mv.visitFieldInsn(GETFIELD, newDynName, REST_TOSTRINGOBJ_FIELD_NAME, "Ljava/util/function/Supplier;");
|
||||
mv.visitMethodInsn(INVOKEINTERFACE, "java/util/function/Supplier", "get", "()Ljava/lang/Object;", true);
|
||||
mv.visitTypeInsn(CHECKCAST, "java/lang/String");
|
||||
mv.visitInsn(ARETURN);
|
||||
mv.visitMaxs(1, 1);
|
||||
mv.visitEnd();
|
||||
}
|
||||
|
||||
cw.visitEnd();
|
||||
|
||||
Class<?> newClazz = new RestClassLoader(loader).loadClass(newDynName.replace('/', '.'), cw.toByteArray());
|
||||
newLoader.addClass(newDynName.replace('/', '.'), cw.toByteArray());
|
||||
try {
|
||||
Class<?> newClazz = newLoader.findClass(newDynName.replace('/', '.'));
|
||||
|
||||
T obj = ((Class<T>) newClazz).getDeclaredConstructor().newInstance();
|
||||
for (Map.Entry<String, org.redkale.util.Attribute> en : restAttributes.entrySet()) {
|
||||
Field attrField = newClazz.getDeclaredField(en.getKey());
|
||||
@@ -1740,7 +1901,9 @@ public final class Rest {
|
||||
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)));
|
||||
Object[] rc = restConverts.get(i);
|
||||
|
||||
genField.set(obj, createJsonConvert((RestConvert[]) rc[0], (RestConvertCoder[]) rc[1]));
|
||||
}
|
||||
Field typesfield = newClazz.getDeclaredField(REST_PARAMTYPES_FIELD_NAME);
|
||||
typesfield.setAccessible(true);
|
||||
@@ -1748,8 +1911,18 @@ public final class Rest {
|
||||
paramtypeArray = paramtypes.toArray(paramtypeArray);
|
||||
typesfield.set(obj, paramtypeArray);
|
||||
|
||||
Field tostringfield = newClazz.getDeclaredField(REST_TOSTRINGOBJ_FIELD_NAME);
|
||||
tostringfield.setAccessible(true);
|
||||
java.util.function.Supplier<String> sSupplier = () -> JsonConvert.root().convertTo(classMap);
|
||||
tostringfield.set(obj, sSupplier);
|
||||
|
||||
Method restactMethod = newClazz.getDeclaredMethod("_createRestInnerActionEntry");
|
||||
restactMethod.setAccessible(true);
|
||||
Field tmpentrysfield = HttpServlet.class.getDeclaredField("_tmpentrys");
|
||||
tmpentrysfield.setAccessible(true);
|
||||
tmpentrysfield.set(obj, restactMethod.invoke(obj));
|
||||
return obj;
|
||||
} catch (Exception e) {
|
||||
} catch (Throwable e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
@@ -1791,13 +1964,26 @@ public final class Rest {
|
||||
|
||||
private static class RestClassLoader extends ClassLoader {
|
||||
|
||||
private Map<String, byte[]> classes = new HashMap<>();
|
||||
|
||||
public RestClassLoader(ClassLoader parent) {
|
||||
super(parent);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Class<?> findClass(String name) throws ClassNotFoundException {
|
||||
byte[] classData = classes.get(name);
|
||||
if (classData == null) return super.findClass(name);
|
||||
return super.defineClass(name, classData, 0, classData.length);
|
||||
}
|
||||
|
||||
public final Class<?> loadClass(String name, byte[] b) {
|
||||
return defineClass(name, b, 0, b.length);
|
||||
}
|
||||
|
||||
public final void addClass(String name, byte[] b) {
|
||||
classes.put(name, b);
|
||||
}
|
||||
}
|
||||
|
||||
private static class MappingEntry {
|
||||
@@ -1839,6 +2025,8 @@ public final class Rest {
|
||||
}
|
||||
}
|
||||
this.existsPound = pound;
|
||||
this.newMethodName = this.name.replace('/', '$').replace('.', '_');
|
||||
this.newActionClassName = "_Dyn_" + this.newMethodName + "_ActionHttpServlet";
|
||||
}
|
||||
|
||||
public final int methodidx; // _paramtypes 的下标,从0开始
|
||||
@@ -1847,6 +2035,10 @@ public final class Rest {
|
||||
|
||||
public final boolean ignore;
|
||||
|
||||
public final String newMethodName;
|
||||
|
||||
public final String newActionClassName;
|
||||
|
||||
public final String name;
|
||||
|
||||
public final String comment;
|
||||
@@ -1861,6 +2053,8 @@ public final class Rest {
|
||||
|
||||
public final boolean existsPound; //是否包含#的参数
|
||||
|
||||
String mappingurl; //在生成方法时赋值, 供_createRestInnerActionEntry使用
|
||||
|
||||
@RestMapping()
|
||||
void mapping() { //用于获取Mapping 默认值
|
||||
}
|
||||
|
||||
43
src/org/redkale/net/http/RestConvertCoder.java
Normal file
43
src/org/redkale/net/http/RestConvertCoder.java
Normal file
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* To change this license header, choose License Headers in Project Properties.
|
||||
* To change this template file, choose Tools | Templates
|
||||
* and open the template in the editor.
|
||||
*/
|
||||
package org.redkale.net.http;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
import static java.lang.annotation.ElementType.METHOD;
|
||||
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||
import org.redkale.convert.*;
|
||||
|
||||
/**
|
||||
* 指定class某个字段的自定义序列化和反序列化策略。 <br>
|
||||
* 只能依附在Service实现类的public方法上, 当方法的返回值以JSON输出时对指定类型的转换设定。 <br>
|
||||
*
|
||||
* <p>
|
||||
* 详情见: https://redkale.org
|
||||
*
|
||||
* @author zhangjx
|
||||
*/
|
||||
@Inherited
|
||||
@Documented
|
||||
@Target({METHOD})
|
||||
@Retention(RUNTIME)
|
||||
@Repeatable(RestConvertCoder.RestConvertCoders.class)
|
||||
public @interface RestConvertCoder {
|
||||
|
||||
Class type();
|
||||
|
||||
String field();
|
||||
|
||||
Class<? extends SimpledCoder> coder();
|
||||
|
||||
@Inherited
|
||||
@Documented
|
||||
@Target({METHOD})
|
||||
@Retention(RUNTIME)
|
||||
@interface RestConvertCoders {
|
||||
|
||||
RestConvertCoder[] value();
|
||||
}
|
||||
}
|
||||
@@ -60,6 +60,13 @@ public @interface RestWebSocket {
|
||||
*/
|
||||
boolean anyuser() default false;
|
||||
|
||||
/**
|
||||
* 接收客户端的分包(last=false)消息时是否自动合并包
|
||||
*
|
||||
* @return 默认true
|
||||
*/
|
||||
boolean mergemsg() default true;
|
||||
|
||||
/**
|
||||
* WebScoket服务器给客户端进行ping操作的间隔时间, 单位: 秒, 默认值:15秒
|
||||
*
|
||||
|
||||
@@ -37,9 +37,20 @@ import org.redkale.util.Comment;
|
||||
*/
|
||||
public abstract class WebSocket<G extends Serializable, T> {
|
||||
|
||||
@Comment("强制关闭结果码")
|
||||
public static final int CLOSECODE_FORCED = 1;
|
||||
//--------------------------- CLOSECODE -------------------------------
|
||||
@Comment("服务器主动关闭")
|
||||
public static final int CLOSECODE_SERVERCLOSE = 3001;
|
||||
|
||||
@Comment("客户端主动关闭")
|
||||
public static final int CLOSECODE_CLIENTCLOSE = 3002;
|
||||
|
||||
@Comment("异常关闭")
|
||||
public static final int CLOSECODE_WSEXCEPTION = 3003;
|
||||
|
||||
@Comment("异常数据强制关闭")
|
||||
public static final int CLOSECODE_ILLPACKET = 3004;
|
||||
|
||||
//---------------------------- RETCODE --------------------------------
|
||||
@Comment("消息不合法")
|
||||
public static final int RETCODE_SEND_ILLPACKET = 1 << 1; //2
|
||||
|
||||
@@ -464,6 +475,35 @@ public abstract class WebSocket<G extends Serializable, T> {
|
||||
return rs;
|
||||
}
|
||||
|
||||
/**
|
||||
* 给指定userid的WebSocket节点发送操作
|
||||
*
|
||||
* @param action 操作参数
|
||||
* @param userids Serializable[]
|
||||
*
|
||||
* @return 为0表示成功, 其他值表示异常
|
||||
*/
|
||||
public final CompletableFuture<Integer> sendAction(final WebSocketAction action, Serializable... userids) {
|
||||
if (_engine.node == null) return CompletableFuture.completedFuture(RETCODE_NODESERVICE_NULL);
|
||||
CompletableFuture<Integer> rs = _engine.node.sendAction(action, userids);
|
||||
if (_engine.logger.isLoggable(Level.FINEST)) _engine.logger.finest("userids:" + Arrays.toString(userids) + " send websocket action(" + action + ")");
|
||||
return rs;
|
||||
}
|
||||
|
||||
/**
|
||||
* 广播操作, 给所有人发操作指令
|
||||
*
|
||||
* @param action 操作参数
|
||||
*
|
||||
* @return 为0表示成功, 其他值表示部分发送异常
|
||||
*/
|
||||
public final CompletableFuture<Integer> broadcastAction(final WebSocketAction action) {
|
||||
if (_engine.node == null) return CompletableFuture.completedFuture(RETCODE_NODESERVICE_NULL);
|
||||
CompletableFuture<Integer> rs = _engine.node.broadcastAction(action);
|
||||
if (_engine.logger.isLoggable(Level.FINEST)) _engine.logger.finest("broadcast send websocket action(" + action + ")");
|
||||
return rs;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户在线的SNCP节点地址列表,不是分布式则返回元素数量为1,且元素值为null的列表<br>
|
||||
* InetSocketAddress 为 SNCP节点地址
|
||||
@@ -500,7 +540,7 @@ public abstract class WebSocket<G extends Serializable, T> {
|
||||
*/
|
||||
public CompletableFuture<Void> changeUserid(final G newuserid) {
|
||||
if (newuserid == null) throw new NullPointerException("newuserid is null");
|
||||
return _engine.changeUserid(this, newuserid);
|
||||
return _engine.changeLocalUserid(this, newuserid);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -518,26 +558,26 @@ public abstract class WebSocket<G extends Serializable, T> {
|
||||
/**
|
||||
* 获取当前WebSocket下的属性,非线程安全
|
||||
*
|
||||
* @param <T> 属性值的类型
|
||||
* @param <V> 属性值的类型
|
||||
* @param name 属性名
|
||||
*
|
||||
* @return 属性值
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public final <T> T getAttribute(String name) {
|
||||
return attributes == null ? null : (T) attributes.get(name);
|
||||
public final <V> V getAttribute(String name) {
|
||||
return attributes == null ? null : (V) attributes.get(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 移出当前WebSocket下的属性,非线程安全
|
||||
*
|
||||
* @param <T> 属性值的类型
|
||||
* @param <V> 属性值的类型
|
||||
* @param name 属性名
|
||||
*
|
||||
* @return 属性值
|
||||
*/
|
||||
public final <T> T removeAttribute(String name) {
|
||||
return attributes == null ? null : (T) attributes.remove(name);
|
||||
public final <V> V removeAttribute(String name) {
|
||||
return attributes == null ? null : (V) attributes.remove(name);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -672,6 +712,18 @@ public abstract class WebSocket<G extends Serializable, T> {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* WebSocket.broadcastAction时的操作
|
||||
*
|
||||
* @param action 操作参数
|
||||
*
|
||||
* @return CompletableFuture
|
||||
*
|
||||
*/
|
||||
protected CompletableFuture<Integer> action(WebSocketAction action) {
|
||||
return CompletableFuture.completedFuture(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* WebSokcet连接成功后的回调方法
|
||||
*/
|
||||
@@ -803,7 +855,7 @@ public abstract class WebSocket<G extends Serializable, T> {
|
||||
*/
|
||||
public final void close() {
|
||||
if (this._runner != null) {
|
||||
CompletableFuture<Void> future = this._runner.closeRunner(CLOSECODE_FORCED, "user close");
|
||||
CompletableFuture<Void> future = this._runner.closeRunner(CLOSECODE_SERVERCLOSE, "user close");
|
||||
if (future != null) future.join();
|
||||
}
|
||||
}
|
||||
|
||||
58
src/org/redkale/net/http/WebSocketAction.java
Normal file
58
src/org/redkale/net/http/WebSocketAction.java
Normal file
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* To change this license header, choose License Headers in Project Properties.
|
||||
* To change this template file, choose Tools | Templates
|
||||
* and open the template in the editor.
|
||||
*/
|
||||
package org.redkale.net.http;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Map;
|
||||
import org.redkale.convert.json.JsonConvert;
|
||||
|
||||
/**
|
||||
* WebSocket.broadcastAction时的参数
|
||||
*
|
||||
* <p>
|
||||
* 详情见: https://redkale.org
|
||||
*
|
||||
* @author zhangjx
|
||||
*/
|
||||
public class WebSocketAction implements Serializable {
|
||||
|
||||
protected String action;
|
||||
|
||||
protected Map<String, String> attach;
|
||||
|
||||
public WebSocketAction() {
|
||||
}
|
||||
|
||||
public WebSocketAction(String action) {
|
||||
this.action = action;
|
||||
}
|
||||
|
||||
public WebSocketAction(String action, Map<String, String> attach) {
|
||||
this.action = action;
|
||||
this.attach = attach;
|
||||
}
|
||||
|
||||
public String getAction() {
|
||||
return action;
|
||||
}
|
||||
|
||||
public void setAction(String action) {
|
||||
this.action = action;
|
||||
}
|
||||
|
||||
public Map<String, String> getAttach() {
|
||||
return attach;
|
||||
}
|
||||
|
||||
public void setAttach(Map<String, String> attach) {
|
||||
this.attach = attach;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return JsonConvert.root().convertTo(this);
|
||||
}
|
||||
}
|
||||
@@ -76,11 +76,14 @@ public class WebSocketEngine {
|
||||
@Comment("最大消息体长度, 小于1表示无限制")
|
||||
protected int wsmaxbody;
|
||||
|
||||
@Comment("接收客户端的分包(last=false)消息时是否自动合并包")
|
||||
protected boolean mergemsg = true;
|
||||
|
||||
@Comment("加密解密器")
|
||||
protected Cryptor cryptor;
|
||||
|
||||
protected WebSocketEngine(String engineid, boolean single, HttpContext context, int liveinterval,
|
||||
int wsmaxconns, int wsthreads, int wsmaxbody, Cryptor cryptor, WebSocketNode node, Convert sendConvert, Logger logger) {
|
||||
protected WebSocketEngine(String engineid, boolean single, HttpContext context, int liveinterval, int wsmaxconns,
|
||||
int wsthreads, int wsmaxbody, boolean mergemsg, Cryptor cryptor, WebSocketNode node, Convert sendConvert, Logger logger) {
|
||||
this.engineid = engineid;
|
||||
this.single = single;
|
||||
this.context = context;
|
||||
@@ -90,6 +93,7 @@ public class WebSocketEngine {
|
||||
this.wsmaxconns = wsmaxconns;
|
||||
this.wsthreads = wsthreads;
|
||||
this.wsmaxbody = wsmaxbody;
|
||||
this.mergemsg = mergemsg;
|
||||
this.cryptor = cryptor;
|
||||
this.logger = logger;
|
||||
this.index = sequence.getAndIncrement();
|
||||
@@ -127,7 +131,7 @@ public class WebSocketEngine {
|
||||
}
|
||||
|
||||
@Comment("添加WebSocket")
|
||||
void add(WebSocket socket) {
|
||||
CompletableFuture<Void> addLocal(WebSocket socket) {
|
||||
if (single) {
|
||||
currconns.incrementAndGet();
|
||||
websockets.put(socket._userid, socket);
|
||||
@@ -140,11 +144,12 @@ public class WebSocketEngine {
|
||||
currconns.incrementAndGet();
|
||||
list.add(socket);
|
||||
}
|
||||
if (node != null) node.connect(socket._userid);
|
||||
if (node != null) return node.connect(socket._userid);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Comment("从WebSocketEngine删除指定WebSocket")
|
||||
CompletableFuture<Void> removeThenClose(WebSocket socket) {
|
||||
CompletableFuture<Void> removeLocalThenClose(WebSocket socket) {
|
||||
Serializable userid = socket._userid;
|
||||
if (single) {
|
||||
currconns.decrementAndGet();
|
||||
@@ -165,7 +170,7 @@ public class WebSocketEngine {
|
||||
}
|
||||
|
||||
@Comment("更改WebSocket的userid")
|
||||
CompletableFuture<Void> changeUserid(WebSocket socket, final Serializable newuserid) {
|
||||
CompletableFuture<Void> changeLocalUserid(WebSocket socket, final Serializable newuserid) {
|
||||
if (newuserid == null) throw new NullPointerException("newuserid is null");
|
||||
final Serializable olduserid = socket._userid;
|
||||
socket._userid = newuserid;
|
||||
@@ -207,20 +212,20 @@ public class WebSocketEngine {
|
||||
}
|
||||
|
||||
@Comment("给所有连接用户发送消息")
|
||||
public CompletableFuture<Integer> broadcastMessage(final Object message, final boolean last) {
|
||||
return broadcastMessage((Predicate) null, message, last);
|
||||
public CompletableFuture<Integer> broadcastLocalMessage(final Object message, final boolean last) {
|
||||
return WebSocketEngine.this.broadcastLocalMessage((Predicate) null, message, last);
|
||||
}
|
||||
|
||||
@Comment("给指定WebSocket连接用户发送消息")
|
||||
public CompletableFuture<Integer> broadcastMessage(final WebSocketRange wsrange, final Object message, final boolean last) {
|
||||
public CompletableFuture<Integer> broadcastLocalMessage(final WebSocketRange wsrange, final Object message, final boolean last) {
|
||||
Predicate<WebSocket> predicate = wsrange == null ? null : (ws) -> ws.predicate(wsrange);
|
||||
return broadcastMessage(predicate, message, last);
|
||||
return WebSocketEngine.this.broadcastLocalMessage(predicate, message, last);
|
||||
}
|
||||
|
||||
@Comment("给指定WebSocket连接用户发送消息")
|
||||
public CompletableFuture<Integer> broadcastMessage(final Predicate<WebSocket> predicate, final Object message, final boolean last) {
|
||||
public CompletableFuture<Integer> broadcastLocalMessage(final Predicate<WebSocket> predicate, final Object message, final boolean last) {
|
||||
if (message instanceof CompletableFuture) {
|
||||
return ((CompletableFuture) message).thenCompose((json) -> broadcastMessage(predicate, json, last));
|
||||
return ((CompletableFuture) message).thenCompose((json) -> WebSocketEngine.this.broadcastLocalMessage(predicate, json, last));
|
||||
}
|
||||
final boolean more = (!(message instanceof WebSocketPacket) || ((WebSocketPacket) message).sendBuffers == null);
|
||||
if (more) {
|
||||
@@ -243,7 +248,7 @@ public class WebSocketEngine {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (future != null) future = future.whenComplete((rs, ex) -> context.offerBuffer(packet.sendBuffers));
|
||||
if (future != null) future.whenComplete((rs, ex) -> context.offerBuffer(packet.sendBuffers));
|
||||
return future == null ? CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY) : future;
|
||||
} else {
|
||||
CompletableFuture<Integer> future = null;
|
||||
@@ -265,19 +270,19 @@ public class WebSocketEngine {
|
||||
}
|
||||
|
||||
@Comment("给指定用户组发送消息")
|
||||
public CompletableFuture<Integer> sendMessage(final Object message, final boolean last, final Stream<? extends Serializable> userids) {
|
||||
public CompletableFuture<Integer> sendLocalMessage(final Object message, final boolean last, final Stream<? extends Serializable> userids) {
|
||||
Object[] array = userids.toArray();
|
||||
Serializable[] ss = new Serializable[array.length];
|
||||
for (int i = 0; i < array.length; i++) {
|
||||
ss[i] = (Serializable) array[i];
|
||||
}
|
||||
return sendMessage(message, last, ss);
|
||||
return WebSocketEngine.this.sendLocalMessage(message, last, ss);
|
||||
}
|
||||
|
||||
@Comment("给指定用户组发送消息")
|
||||
public CompletableFuture<Integer> sendMessage(final Object message, final boolean last, final Serializable... userids) {
|
||||
public CompletableFuture<Integer> sendLocalMessage(final Object message, final boolean last, final Serializable... userids) {
|
||||
if (message instanceof CompletableFuture) {
|
||||
return ((CompletableFuture) message).thenCompose((json) -> sendMessage(json, last, userids));
|
||||
return ((CompletableFuture) message).thenCompose((json) -> WebSocketEngine.this.sendLocalMessage(json, last, userids));
|
||||
}
|
||||
final boolean more = (!(message instanceof WebSocketPacket) || ((WebSocketPacket) message).sendBuffers == null) && userids.length > 1;
|
||||
if (more) {
|
||||
@@ -302,7 +307,7 @@ public class WebSocketEngine {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (future != null) future = future.whenComplete((rs, ex) -> context.offerBuffer(packet.sendBuffers));
|
||||
if (future != null) future.whenComplete((rs, ex) -> context.offerBuffer(packet.sendBuffers));
|
||||
return future == null ? CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY) : future;
|
||||
} else {
|
||||
CompletableFuture<Integer> future = null;
|
||||
@@ -325,6 +330,54 @@ public class WebSocketEngine {
|
||||
}
|
||||
}
|
||||
|
||||
@Comment("给指定WebSocket连接用户发起操作指令")
|
||||
public CompletableFuture<Integer> broadcastLocalAction(final WebSocketAction action) {
|
||||
CompletableFuture<Integer> future = null;
|
||||
if (single) {
|
||||
for (WebSocket websocket : websockets.values()) {
|
||||
future = future == null ? websocket.action(action) : future.thenCombine(websocket.action(action), (a, b) -> a | (Integer) b);
|
||||
}
|
||||
} else {
|
||||
for (List<WebSocket> list : websockets2.values()) {
|
||||
for (WebSocket websocket : list) {
|
||||
future = future == null ? websocket.action(action) : future.thenCombine(websocket.action(action), (a, b) -> a | (Integer) b);
|
||||
}
|
||||
}
|
||||
}
|
||||
return future == null ? CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY) : future;
|
||||
}
|
||||
|
||||
@Comment("给指定用户组发送操作")
|
||||
public CompletableFuture<Integer> sendLocalAction(final WebSocketAction action, final Stream<? extends Serializable> userids) {
|
||||
Object[] array = userids.toArray();
|
||||
Serializable[] ss = new Serializable[array.length];
|
||||
for (int i = 0; i < array.length; i++) {
|
||||
ss[i] = (Serializable) array[i];
|
||||
}
|
||||
return WebSocketEngine.this.sendLocalAction(action, ss);
|
||||
}
|
||||
|
||||
@Comment("给指定用户组发送操作")
|
||||
public CompletableFuture<Integer> sendLocalAction(final WebSocketAction action, final Serializable... userids) {
|
||||
CompletableFuture<Integer> future = null;
|
||||
if (single) {
|
||||
for (Serializable userid : userids) {
|
||||
WebSocket websocket = websockets.get(userid);
|
||||
if (websocket == null) continue;
|
||||
future = future == null ? websocket.action(action) : future.thenCombine(websocket.action(action), (a, b) -> a | (Integer) b);
|
||||
}
|
||||
} else {
|
||||
for (Serializable userid : userids) {
|
||||
List<WebSocket> list = websockets2.get(userid);
|
||||
if (list == null) continue;
|
||||
for (WebSocket websocket : list) {
|
||||
future = future == null ? websocket.action(action) : future.thenCombine(websocket.action(action), (a, b) -> a | (Integer) b);
|
||||
}
|
||||
}
|
||||
}
|
||||
return future == null ? CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY) : future;
|
||||
}
|
||||
|
||||
@Comment("获取最大连接数")
|
||||
public int getLocalWsmaxconns() {
|
||||
return this.wsmaxconns;
|
||||
|
||||
@@ -59,8 +59,14 @@ public abstract class WebSocketNode {
|
||||
|
||||
protected Semaphore semaphore;
|
||||
|
||||
private int tryAcquireSeconds = 12;
|
||||
|
||||
public void init(AnyValue conf) {
|
||||
if (sncpNodeAddresses != null) sncpNodeAddresses.initValueType(InetSocketAddress.class);
|
||||
this.tryAcquireSeconds = Integer.getInteger("WebSocketNode.tryAcquireSeconds", 12);
|
||||
|
||||
if (sncpNodeAddresses != null && "memory".equals(sncpNodeAddresses.getType())) {
|
||||
sncpNodeAddresses.initValueType(InetSocketAddress.class);
|
||||
}
|
||||
if (localEngine != null) {
|
||||
int wsthreads = localEngine.wsthreads;
|
||||
if (wsthreads == 0) wsthreads = Runtime.getRuntime().availableProcessors() * 8;
|
||||
@@ -77,28 +83,34 @@ public abstract class WebSocketNode {
|
||||
}
|
||||
|
||||
@Local
|
||||
public final void postDestroy(AnyValue conf) {
|
||||
protected void postDestroy(AnyValue conf) {
|
||||
if (this.localEngine == null) return;
|
||||
//关掉所有本地本地WebSocket
|
||||
this.localEngine.getLocalWebSockets().forEach(g -> g.close());
|
||||
if (sncpNodeAddresses != null && localSncpAddress != null) {
|
||||
sncpNodeAddresses.removeSetItem(SOURCE_SNCP_ADDRS_KEY, localSncpAddress);
|
||||
sncpNodeAddresses.removeSetItem(SOURCE_SNCP_ADDRS_KEY, InetSocketAddress.class, localSncpAddress);
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract CompletableFuture<List<String>> getWebSocketAddresses(@RpcTargetAddress InetSocketAddress targetAddress, Serializable userid);
|
||||
|
||||
protected abstract CompletableFuture<Integer> sendMessage(@RpcTargetAddress InetSocketAddress targetAddress, Object message, boolean last, Serializable userid);
|
||||
protected abstract CompletableFuture<Integer> sendMessage(@RpcTargetAddress InetSocketAddress targetAddress, Object message, boolean last, Serializable... userids);
|
||||
|
||||
protected abstract CompletableFuture<Integer> broadcastMessage(@RpcTargetAddress InetSocketAddress targetAddress, WebSocketRange wsrange, Object message, boolean last);
|
||||
|
||||
protected abstract CompletableFuture<Void> connect(Serializable userid, InetSocketAddress addr);
|
||||
protected abstract CompletableFuture<Integer> sendAction(@RpcTargetAddress InetSocketAddress targetAddress, WebSocketAction action, Serializable... userids);
|
||||
|
||||
protected abstract CompletableFuture<Void> disconnect(Serializable userid, InetSocketAddress addr);
|
||||
protected abstract CompletableFuture<Integer> broadcastAction(@RpcTargetAddress InetSocketAddress targetAddress, WebSocketAction action);
|
||||
|
||||
protected abstract CompletableFuture<Void> changeUserid(Serializable fromuserid, Serializable touserid, InetSocketAddress addr);
|
||||
protected abstract CompletableFuture<Void> connect(Serializable userid, InetSocketAddress sncpAddr);
|
||||
|
||||
protected abstract CompletableFuture<Integer> forceCloseWebSocket(Serializable userid, InetSocketAddress addr);
|
||||
protected abstract CompletableFuture<Void> disconnect(Serializable userid, InetSocketAddress sncpAddr);
|
||||
|
||||
protected abstract CompletableFuture<Void> changeUserid(Serializable fromuserid, Serializable touserid, InetSocketAddress sncpAddr);
|
||||
|
||||
protected abstract CompletableFuture<Boolean> existsWebSocket(Serializable userid, @RpcTargetAddress InetSocketAddress targetAddress);
|
||||
|
||||
protected abstract CompletableFuture<Integer> forceCloseWebSocket(Serializable userid, @RpcTargetAddress InetSocketAddress targetAddress);
|
||||
|
||||
//--------------------------------------------------------------------------------
|
||||
final CompletableFuture<Void> connect(final Serializable userid) {
|
||||
@@ -147,7 +159,7 @@ public abstract class WebSocketNode {
|
||||
public CompletableFuture<Collection<InetSocketAddress>> getRpcNodeAddresses(final Serializable userid) {
|
||||
if (this.sncpNodeAddresses != null) {
|
||||
tryAcquireSemaphore();
|
||||
CompletableFuture<Collection<InetSocketAddress>> result = this.sncpNodeAddresses.getCollectionAsync(SOURCE_SNCP_USERID_PREFIX + userid);
|
||||
CompletableFuture<Collection<InetSocketAddress>> result = this.sncpNodeAddresses.getCollectionAsync(SOURCE_SNCP_USERID_PREFIX + userid, InetSocketAddress.class);
|
||||
if (semaphore != null) result.whenComplete((r, e) -> releaseSemaphore());
|
||||
return result;
|
||||
}
|
||||
@@ -180,23 +192,6 @@ public abstract class WebSocketNode {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断指定用户是否WebSocket在线
|
||||
*
|
||||
* @param userid Serializable
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public CompletableFuture<Boolean> existsWebSocket(final Serializable userid) {
|
||||
if (this.localEngine != null && this.sncpNodeAddresses == null) {
|
||||
return CompletableFuture.completedFuture(this.localEngine.existsLocalWebSocket(userid));
|
||||
}
|
||||
tryAcquireSemaphore();
|
||||
CompletableFuture<Boolean> rs = this.sncpNodeAddresses.existsAsync(SOURCE_SNCP_USERID_PREFIX + userid);
|
||||
if (semaphore != null) rs.whenComplete((r, e) -> releaseSemaphore());
|
||||
return rs;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取在线用户总数
|
||||
*
|
||||
@@ -213,6 +208,40 @@ public abstract class WebSocketNode {
|
||||
return rs;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断指定用户是否WebSocket在线
|
||||
*
|
||||
* @param userid Serializable
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
@Local
|
||||
public CompletableFuture<Boolean> existsWebSocket(final Serializable userid) {
|
||||
CompletableFuture<Boolean> localFuture = null;
|
||||
if (this.localEngine != null) localFuture = CompletableFuture.completedFuture(localEngine.existsLocalWebSocket(userid));
|
||||
if (this.sncpNodeAddresses == null || this.remoteNode == null) {
|
||||
if (logger.isLoggable(Level.FINEST)) logger.finest("websocket remote node is null");
|
||||
//没有CacheSource就不会有分布式节点
|
||||
return localFuture;
|
||||
}
|
||||
//远程节点关闭
|
||||
tryAcquireSemaphore();
|
||||
CompletableFuture<Collection<InetSocketAddress>> addrsFuture = sncpNodeAddresses.getCollectionAsync(SOURCE_SNCP_USERID_PREFIX + userid, InetSocketAddress.class);
|
||||
if (semaphore != null) addrsFuture.whenComplete((r, e) -> releaseSemaphore());
|
||||
CompletableFuture<Boolean> remoteFuture = addrsFuture.thenCompose((Collection<InetSocketAddress> addrs) -> {
|
||||
if (logger.isLoggable(Level.FINEST)) logger.finest("websocket found userid:" + userid + " on " + addrs);
|
||||
if (addrs == null || addrs.isEmpty()) return CompletableFuture.completedFuture(false);
|
||||
CompletableFuture<Boolean> future = null;
|
||||
for (InetSocketAddress addr : addrs) {
|
||||
if (addr == null || addr.equals(localSncpAddress)) continue;
|
||||
future = future == null ? remoteNode.existsWebSocket(userid, addr)
|
||||
: future.thenCombine(remoteNode.existsWebSocket(userid, addr), (a, b) -> a | b);
|
||||
}
|
||||
return future == null ? CompletableFuture.completedFuture(false) : future;
|
||||
});
|
||||
return localFuture == null ? remoteFuture : localFuture.thenCombine(remoteFuture, (a, b) -> a | b);
|
||||
}
|
||||
|
||||
/**
|
||||
* 强制关闭用户WebSocket
|
||||
*
|
||||
@@ -231,7 +260,7 @@ public abstract class WebSocketNode {
|
||||
}
|
||||
//远程节点关闭
|
||||
tryAcquireSemaphore();
|
||||
CompletableFuture<Collection<InetSocketAddress>> addrsFuture = sncpNodeAddresses.getCollectionAsync(SOURCE_SNCP_USERID_PREFIX + userid);
|
||||
CompletableFuture<Collection<InetSocketAddress>> addrsFuture = sncpNodeAddresses.getCollectionAsync(SOURCE_SNCP_USERID_PREFIX + userid, InetSocketAddress.class);
|
||||
if (semaphore != null) addrsFuture.whenComplete((r, e) -> releaseSemaphore());
|
||||
CompletableFuture<Integer> remoteFuture = addrsFuture.thenCompose((Collection<InetSocketAddress> addrs) -> {
|
||||
if (logger.isLoggable(Level.FINEST)) logger.finest("websocket found userid:" + userid + " on " + addrs);
|
||||
@@ -244,7 +273,7 @@ public abstract class WebSocketNode {
|
||||
}
|
||||
return future == null ? CompletableFuture.completedFuture(0) : future;
|
||||
});
|
||||
return localFuture.thenCombine(remoteFuture, (a, b) -> a + b);
|
||||
return localFuture == null ? remoteFuture : localFuture.thenCombine(remoteFuture, (a, b) -> a + b);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------
|
||||
@@ -385,15 +414,97 @@ public abstract class WebSocketNode {
|
||||
if (message0 instanceof CompletableFuture) return ((CompletableFuture) message0).thenApply(msg -> sendMessage(convert, msg, last, userids));
|
||||
final Object message = (convert == null || message0 instanceof WebSocketPacket) ? message0 : ((convert instanceof TextConvert) ? new WebSocketPacket(((TextConvert) convert).convertTo(message0), last) : new WebSocketPacket(((BinaryConvert) convert).convertTo(message0), last));
|
||||
if (this.localEngine != null && this.sncpNodeAddresses == null) { //本地模式且没有分布式
|
||||
return this.localEngine.sendMessage(message, last, userids);
|
||||
return this.localEngine.sendLocalMessage(message, last, userids);
|
||||
}
|
||||
final Object remoteMessage = formatRemoteMessage(message);
|
||||
CompletableFuture<Integer> future = null;
|
||||
for (Serializable userid : userids) {
|
||||
future = future == null ? sendOneMessage(remoteMessage, last, userid)
|
||||
: future.thenCombine(sendOneMessage(remoteMessage, last, userid), (a, b) -> a | b);
|
||||
CompletableFuture<Integer> rsfuture;
|
||||
if (userids.length == 1) {
|
||||
rsfuture = sendOneUserMessage(remoteMessage, last, userids[0]);
|
||||
} else {
|
||||
String[] keys = new String[userids.length];
|
||||
final Map<String, Serializable> keyuser = new HashMap<>();
|
||||
for (int i = 0; i < userids.length; i++) {
|
||||
keys[i] = SOURCE_SNCP_USERID_PREFIX + userids[i];
|
||||
keyuser.put(keys[i], userids[i]);
|
||||
}
|
||||
tryAcquireSemaphore();
|
||||
CompletableFuture<Map<String, Collection<InetSocketAddress>>> addrsFuture = sncpNodeAddresses.getCollectionMapAsync(InetSocketAddress.class, keys);
|
||||
if (semaphore != null) addrsFuture.whenComplete((r, e) -> releaseSemaphore());
|
||||
rsfuture = addrsFuture.thenCompose((Map<String, Collection<InetSocketAddress>> addrs) -> {
|
||||
if (addrs == null || addrs.isEmpty()) {
|
||||
if (logger.isLoggable(Level.FINER)) logger.finer("websocket not found userids:" + JsonConvert.root().convertTo(userids) + " on any node ");
|
||||
return CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY);
|
||||
}
|
||||
Map<InetSocketAddress, List<Serializable>> addrUsers = new HashMap<>();
|
||||
addrs.forEach((key, as) -> {
|
||||
for (InetSocketAddress a : as) {
|
||||
addrUsers.computeIfAbsent(a, k -> new ArrayList<>()).add(keyuser.get(key));
|
||||
}
|
||||
});
|
||||
if (logger.isLoggable(Level.FINEST)) {
|
||||
logger.finest("websocket(localaddr=" + localSncpAddress + ", userids=" + JsonConvert.root().convertTo(userids) + ") found message-addr-userids: " + addrUsers);
|
||||
}
|
||||
CompletableFuture<Integer> future = null;
|
||||
for (Map.Entry<InetSocketAddress, List<Serializable>> en : addrUsers.entrySet()) {
|
||||
Serializable[] us = en.getValue().toArray(new Serializable[en.getValue().size()]);
|
||||
future = future == null ? sendOneAddrMessage(en.getKey(), remoteMessage, last, us)
|
||||
: future.thenCombine(sendOneAddrMessage(en.getKey(), remoteMessage, last, us), (a, b) -> a | b);
|
||||
}
|
||||
return future == null ? CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY) : future;
|
||||
});
|
||||
}
|
||||
return future == null ? CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY) : future;
|
||||
return rsfuture == null ? CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY) : rsfuture;
|
||||
}
|
||||
|
||||
protected CompletableFuture<Integer> sendOneUserMessage(final Object message, final boolean last, final Serializable userid) {
|
||||
if (message instanceof CompletableFuture) return ((CompletableFuture) message).thenApply(msg -> sendOneUserMessage(msg, last, userid));
|
||||
if (logger.isLoggable(Level.FINEST)) {
|
||||
logger.finest("websocket want send message {userid:" + userid + ", content:'" + (message instanceof WebSocketPacket ? ((WebSocketPacket) message).toSimpleString() : JsonConvert.root().convertTo(message)) + "'} from locale node to " + ((this.localEngine != null) ? "locale" : "remote") + " engine");
|
||||
}
|
||||
CompletableFuture<Integer> localFuture = null;
|
||||
if (this.localEngine != null) localFuture = localEngine.sendLocalMessage(message, last, userid);
|
||||
if (this.sncpNodeAddresses == null || this.remoteNode == null) {
|
||||
if (logger.isLoggable(Level.FINEST)) logger.finest("websocket remote node is null");
|
||||
//没有CacheSource就不会有分布式节点
|
||||
return localFuture == null ? CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY) : localFuture;
|
||||
}
|
||||
//远程节点发送消息
|
||||
final Object remoteMessage = formatRemoteMessage(message);
|
||||
tryAcquireSemaphore();
|
||||
CompletableFuture<Collection<InetSocketAddress>> addrsFuture = sncpNodeAddresses.getCollectionAsync(SOURCE_SNCP_USERID_PREFIX + userid, InetSocketAddress.class);
|
||||
if (semaphore != null) addrsFuture.whenComplete((r, e) -> releaseSemaphore());
|
||||
CompletableFuture<Integer> remoteFuture = addrsFuture.thenCompose((Collection<InetSocketAddress> addrs) -> {
|
||||
if (addrs == null || addrs.isEmpty()) {
|
||||
if (logger.isLoggable(Level.FINER)) logger.finer("websocket not found userid:" + userid + " on any node ");
|
||||
return CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY);
|
||||
}
|
||||
if (logger.isLoggable(Level.FINEST)) logger.finest("websocket(localaddr=" + localSncpAddress + ") found userid:" + userid + " on " + addrs);
|
||||
CompletableFuture<Integer> future = null;
|
||||
for (InetSocketAddress addr : addrs) {
|
||||
if (addr == null || addr.equals(localSncpAddress)) continue;
|
||||
future = future == null ? remoteNode.sendMessage(addr, remoteMessage, last, userid)
|
||||
: future.thenCombine(remoteNode.sendMessage(addr, remoteMessage, last, userid), (a, b) -> a | b);
|
||||
}
|
||||
return future == null ? CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY) : future;
|
||||
});
|
||||
return localFuture == null ? remoteFuture : localFuture.thenCombine(remoteFuture, (a, b) -> a | b);
|
||||
}
|
||||
|
||||
protected CompletableFuture<Integer> sendOneAddrMessage(final InetSocketAddress sncpAddr, final Object message, final boolean last, final Serializable... userids) {
|
||||
if (message instanceof CompletableFuture) return ((CompletableFuture) message).thenApply(msg -> sendOneAddrMessage(sncpAddr, msg, last, userids));
|
||||
if (logger.isLoggable(Level.FINEST)) {
|
||||
logger.finest("websocket want send message {userids:" + JsonConvert.root().convertTo(userids) + ", sncpaddr:" + sncpAddr + ", content:'" + (message instanceof WebSocketPacket ? ((WebSocketPacket) message).toSimpleString() : JsonConvert.root().convertTo(message)) + "'} from locale node to " + ((this.localEngine != null) ? "locale" : "remote") + " engine");
|
||||
}
|
||||
if (Objects.equals(sncpAddr, this.localSncpAddress)) {
|
||||
return this.localEngine == null ? CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY) : localEngine.sendLocalMessage(message, last, userids);
|
||||
}
|
||||
if (this.sncpNodeAddresses == null || this.remoteNode == null) {
|
||||
if (logger.isLoggable(Level.FINEST)) logger.finest("websocket remote node is null");
|
||||
//没有CacheSource就不会有分布式节点
|
||||
return CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY);
|
||||
}
|
||||
final Object remoteMessage = formatRemoteMessage(message);
|
||||
return remoteNode.sendMessage(sncpAddr, remoteMessage, last, userids);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -504,12 +615,12 @@ public abstract class WebSocketNode {
|
||||
if (message0 instanceof CompletableFuture) return ((CompletableFuture) message0).thenApply(msg -> broadcastMessage(wsrange, convert, msg, last));
|
||||
final Object message = (convert == null || message0 instanceof WebSocketPacket) ? message0 : ((convert instanceof TextConvert) ? new WebSocketPacket(((TextConvert) convert).convertTo(message0), last) : new WebSocketPacket(((BinaryConvert) convert).convertTo(message0), last));
|
||||
if (this.localEngine != null && this.sncpNodeAddresses == null) { //本地模式且没有分布式
|
||||
return this.localEngine.broadcastMessage(wsrange, message, last);
|
||||
return this.localEngine.broadcastLocalMessage(wsrange, message, last);
|
||||
}
|
||||
final Object remoteMessage = formatRemoteMessage(message);
|
||||
CompletableFuture<Integer> localFuture = this.localEngine == null ? null : this.localEngine.broadcastMessage(wsrange, message, last);
|
||||
CompletableFuture<Integer> localFuture = this.localEngine == null ? null : this.localEngine.broadcastLocalMessage(wsrange, message, last);
|
||||
tryAcquireSemaphore();
|
||||
CompletableFuture<Collection<InetSocketAddress>> addrsFuture = sncpNodeAddresses.getCollectionAsync(SOURCE_SNCP_ADDRS_KEY);
|
||||
CompletableFuture<Collection<InetSocketAddress>> addrsFuture = sncpNodeAddresses.getCollectionAsync(SOURCE_SNCP_ADDRS_KEY, InetSocketAddress.class);
|
||||
if (semaphore != null) addrsFuture.whenComplete((r, e) -> releaseSemaphore());
|
||||
CompletableFuture<Integer> remoteFuture = addrsFuture.thenCompose((Collection<InetSocketAddress> addrs) -> {
|
||||
if (logger.isLoggable(Level.FINEST)) logger.finest("websocket broadcast message (" + remoteMessage + ") on " + addrs);
|
||||
@@ -525,22 +636,104 @@ public abstract class WebSocketNode {
|
||||
return localFuture == null ? remoteFuture : localFuture.thenCombine(remoteFuture, (a, b) -> a | b);
|
||||
}
|
||||
|
||||
protected CompletableFuture<Integer> sendOneMessage(final Object message, final boolean last, final Serializable userid) {
|
||||
if (message instanceof CompletableFuture) return ((CompletableFuture) message).thenApply(msg -> sendOneMessage(msg, last, userid));
|
||||
/**
|
||||
* 广播操作, 给所有人发操作
|
||||
*
|
||||
* @param action 操作参数
|
||||
*
|
||||
* @return 为0表示成功, 其他值表示部分发送异常
|
||||
*/
|
||||
@Local
|
||||
public CompletableFuture<Integer> broadcastAction(final WebSocketAction action) {
|
||||
if (this.localEngine != null && this.sncpNodeAddresses == null) { //本地模式且没有分布式
|
||||
return this.localEngine.broadcastLocalAction(action);
|
||||
}
|
||||
CompletableFuture<Integer> localFuture = this.localEngine == null ? null : this.localEngine.broadcastLocalAction(action);
|
||||
tryAcquireSemaphore();
|
||||
CompletableFuture<Collection<InetSocketAddress>> addrsFuture = sncpNodeAddresses.getCollectionAsync(SOURCE_SNCP_ADDRS_KEY, InetSocketAddress.class);
|
||||
if (semaphore != null) addrsFuture.whenComplete((r, e) -> releaseSemaphore());
|
||||
CompletableFuture<Integer> remoteFuture = addrsFuture.thenCompose((Collection<InetSocketAddress> addrs) -> {
|
||||
if (logger.isLoggable(Level.FINEST)) logger.finest("websocket broadcast action (" + action + ") on " + addrs);
|
||||
if (addrs == null || addrs.isEmpty()) return CompletableFuture.completedFuture(0);
|
||||
CompletableFuture<Integer> future = null;
|
||||
for (InetSocketAddress addr : addrs) {
|
||||
if (addr == null || addr.equals(localSncpAddress)) continue;
|
||||
future = future == null ? remoteNode.broadcastAction(addr, action)
|
||||
: future.thenCombine(remoteNode.broadcastAction(addr, action), (a, b) -> a | b);
|
||||
}
|
||||
return future == null ? CompletableFuture.completedFuture(0) : future;
|
||||
});
|
||||
return localFuture == null ? remoteFuture : localFuture.thenCombine(remoteFuture, (a, b) -> a | b);
|
||||
}
|
||||
|
||||
/**
|
||||
* 向指定用户发送操作,先发送本地连接,再发送远程连接 <br>
|
||||
* 如果当前WebSocketNode是远程模式,此方法只发送远程连接
|
||||
*
|
||||
* @param action 操作参数
|
||||
* @param userids Serializable[]
|
||||
*
|
||||
* @return 为0表示成功, 其他值表示部分发送异常
|
||||
*/
|
||||
@Local
|
||||
public CompletableFuture<Integer> sendAction(final WebSocketAction action, final Serializable... userids) {
|
||||
if (userids == null || userids.length < 1) return CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY);
|
||||
if (this.localEngine != null && this.sncpNodeAddresses == null) { //本地模式且没有分布式
|
||||
return this.localEngine.sendLocalAction(action, userids);
|
||||
}
|
||||
CompletableFuture<Integer> rsfuture;
|
||||
if (userids.length == 1) {
|
||||
rsfuture = sendOneUserAction(action, userids[0]);
|
||||
} else {
|
||||
String[] keys = new String[userids.length];
|
||||
final Map<String, Serializable> keyuser = new HashMap<>();
|
||||
for (int i = 0; i < userids.length; i++) {
|
||||
keys[i] = SOURCE_SNCP_USERID_PREFIX + userids[i];
|
||||
keyuser.put(keys[i], userids[i]);
|
||||
}
|
||||
tryAcquireSemaphore();
|
||||
CompletableFuture<Map<String, Collection<InetSocketAddress>>> addrsFuture = sncpNodeAddresses.getCollectionMapAsync(InetSocketAddress.class, keys);
|
||||
if (semaphore != null) addrsFuture.whenComplete((r, e) -> releaseSemaphore());
|
||||
rsfuture = addrsFuture.thenCompose((Map<String, Collection<InetSocketAddress>> addrs) -> {
|
||||
if (addrs == null || addrs.isEmpty()) {
|
||||
if (logger.isLoggable(Level.FINER)) logger.finer("websocket not found userids:" + JsonConvert.root().convertTo(userids) + " on any node ");
|
||||
return CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY);
|
||||
}
|
||||
Map<InetSocketAddress, List<Serializable>> addrUsers = new HashMap<>();
|
||||
addrs.forEach((key, as) -> {
|
||||
for (InetSocketAddress a : as) {
|
||||
addrUsers.computeIfAbsent(a, k -> new ArrayList<>()).add(keyuser.get(key));
|
||||
}
|
||||
});
|
||||
if (logger.isLoggable(Level.FINEST)) {
|
||||
logger.finest("websocket(localaddr=" + localSncpAddress + ", userids=" + JsonConvert.root().convertTo(userids) + ") found action-userid-addrs: " + addrUsers);
|
||||
}
|
||||
CompletableFuture<Integer> future = null;
|
||||
for (Map.Entry<InetSocketAddress, List<Serializable>> en : addrUsers.entrySet()) {
|
||||
Serializable[] us = en.getValue().toArray(new Serializable[en.getValue().size()]);
|
||||
future = future == null ? sendOneAddrAction(en.getKey(), action, us)
|
||||
: future.thenCombine(sendOneAddrAction(en.getKey(), action, us), (a, b) -> a | b);
|
||||
}
|
||||
return future == null ? CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY) : future;
|
||||
});
|
||||
}
|
||||
return rsfuture == null ? CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY) : rsfuture;
|
||||
}
|
||||
|
||||
protected CompletableFuture<Integer> sendOneUserAction(final WebSocketAction action, final Serializable userid) {
|
||||
if (logger.isLoggable(Level.FINEST)) {
|
||||
logger.finest("websocket want send message {userid:" + userid + ", content:'" + (message instanceof WebSocketPacket ? ((WebSocketPacket) message).toSimpleString() : JsonConvert.root().convertTo(message)) + "'} from locale node to " + ((this.localEngine != null) ? "locale" : "remote") + " engine");
|
||||
logger.finest("websocket want send action {userid:" + userid + ", action:" + action + "} from locale node to " + ((this.localEngine != null) ? "locale" : "remote") + " engine");
|
||||
}
|
||||
CompletableFuture<Integer> localFuture = null;
|
||||
if (this.localEngine != null) localFuture = localEngine.sendMessage(message, last, userid);
|
||||
if (this.localEngine != null) localFuture = localEngine.sendLocalAction(action, userid);
|
||||
if (this.sncpNodeAddresses == null || this.remoteNode == null) {
|
||||
if (logger.isLoggable(Level.FINEST)) logger.finest("websocket remote node is null");
|
||||
//没有CacheSource就不会有分布式节点
|
||||
return localFuture == null ? CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY) : localFuture;
|
||||
}
|
||||
//远程节点发送消息
|
||||
final Object remoteMessage = formatRemoteMessage(message);
|
||||
//远程节点发送操作
|
||||
tryAcquireSemaphore();
|
||||
CompletableFuture<Collection<InetSocketAddress>> addrsFuture = sncpNodeAddresses.getCollectionAsync(SOURCE_SNCP_USERID_PREFIX + userid);
|
||||
CompletableFuture<Collection<InetSocketAddress>> addrsFuture = sncpNodeAddresses.getCollectionAsync(SOURCE_SNCP_USERID_PREFIX + userid, InetSocketAddress.class);
|
||||
if (semaphore != null) addrsFuture.whenComplete((r, e) -> releaseSemaphore());
|
||||
CompletableFuture<Integer> remoteFuture = addrsFuture.thenCompose((Collection<InetSocketAddress> addrs) -> {
|
||||
if (addrs == null || addrs.isEmpty()) {
|
||||
@@ -551,14 +744,29 @@ public abstract class WebSocketNode {
|
||||
CompletableFuture<Integer> future = null;
|
||||
for (InetSocketAddress addr : addrs) {
|
||||
if (addr == null || addr.equals(localSncpAddress)) continue;
|
||||
future = future == null ? remoteNode.sendMessage(addr, remoteMessage, last, userid)
|
||||
: future.thenCombine(remoteNode.sendMessage(addr, remoteMessage, last, userid), (a, b) -> a | b);
|
||||
future = future == null ? remoteNode.sendAction(addr, action, userid)
|
||||
: future.thenCombine(remoteNode.sendAction(addr, action, userid), (a, b) -> a | b);
|
||||
}
|
||||
return future == null ? CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY) : future;
|
||||
});
|
||||
return localFuture == null ? remoteFuture : localFuture.thenCombine(remoteFuture, (a, b) -> a | b);
|
||||
}
|
||||
|
||||
protected CompletableFuture<Integer> sendOneAddrAction(final InetSocketAddress sncpAddr, final WebSocketAction action, final Serializable... userids) {
|
||||
if (logger.isLoggable(Level.FINEST)) {
|
||||
logger.finest("websocket want send action {userids:" + JsonConvert.root().convertTo(userids) + ", sncpaddr:" + sncpAddr + ", action:" + action + " from locale node to " + ((this.localEngine != null) ? "locale" : "remote") + " engine");
|
||||
}
|
||||
if (Objects.equals(sncpAddr, this.localSncpAddress)) {
|
||||
return this.localEngine == null ? CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY) : localEngine.sendLocalAction(action, userids);
|
||||
}
|
||||
if (this.sncpNodeAddresses == null || this.remoteNode == null) {
|
||||
if (logger.isLoggable(Level.FINEST)) logger.finest("websocket remote node is null");
|
||||
//没有CacheSource就不会有分布式节点
|
||||
return CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY);
|
||||
}
|
||||
return remoteNode.sendAction(sncpAddr, action, userids);
|
||||
}
|
||||
|
||||
protected Object formatRemoteMessage(Object message) {
|
||||
if (message instanceof WebSocketPacket) return message;
|
||||
if (message instanceof byte[]) return message;
|
||||
@@ -571,7 +779,7 @@ public abstract class WebSocketNode {
|
||||
protected boolean tryAcquireSemaphore() {
|
||||
if (this.semaphore == null) return true;
|
||||
try {
|
||||
return this.semaphore.tryAcquire(6, TimeUnit.SECONDS);
|
||||
return this.semaphore.tryAcquire(tryAcquireSeconds, TimeUnit.SECONDS);
|
||||
} catch (Exception e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ import java.util.function.*;
|
||||
import java.util.logging.*;
|
||||
import org.redkale.convert.*;
|
||||
import org.redkale.net.Cryptor;
|
||||
import org.redkale.util.*;
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -24,6 +25,8 @@ import org.redkale.net.Cryptor;
|
||||
*/
|
||||
public final class WebSocketPacket {
|
||||
|
||||
public static final Object MESSAGE_NIL = new Object();
|
||||
|
||||
static final WebSocketPacket NONE = new WebSocketPacket();
|
||||
|
||||
public static final WebSocketPacket DEFAULT_PING_PACKET = new WebSocketPacket(FrameType.PING, new byte[0]);
|
||||
@@ -34,7 +37,7 @@ public final class WebSocketPacket {
|
||||
|
||||
public static enum FrameType {
|
||||
|
||||
TEXT(0x01), BINARY(0x02), CLOSE(0x08), PING(0x09), PONG(0x0A);
|
||||
SERIES(0x00), TEXT(0x01), BINARY(0x02), CLOSE(0x08), PING(0x09), PONG(0x0A);
|
||||
|
||||
private final int value;
|
||||
|
||||
@@ -48,6 +51,7 @@ public final class WebSocketPacket {
|
||||
|
||||
public static FrameType valueOf(int v) {
|
||||
switch (v) {
|
||||
case 0x00: return SERIES;
|
||||
case 0x01: return TEXT;
|
||||
case 0x02: return BINARY;
|
||||
case 0x08: return CLOSE;
|
||||
@@ -344,11 +348,19 @@ public final class WebSocketPacket {
|
||||
*
|
||||
* @return boolean 已接收完返回true, 需要继续接收body返回false;
|
||||
*/
|
||||
boolean receiveBody(WebSocket webSocket, ByteBuffer readBuffer) {
|
||||
boolean receiveBody(final Logger logger, WebSocketRunner runner, WebSocket webSocket, ByteBuffer readBuffer) {
|
||||
final boolean debug = false; //调试开关
|
||||
int need = receiveLength - receiveCount;
|
||||
boolean over = readBuffer.remaining() >= need;
|
||||
final int remain = readBuffer.remaining();
|
||||
boolean over = remain >= need;
|
||||
this.receiveBuffers = Utility.append(this.receiveBuffers, readBuffer);
|
||||
if (over) parseReceiveMessage(webSocket, this.receiveBuffers);
|
||||
if (debug) logger.finest("receiveBody: receiveLength=" + receiveLength + ", this.receiveCount=" + this.receiveCount + ", readBuffer=" + remain);
|
||||
if (over) {
|
||||
this.receiveCount = this.receiveLength;
|
||||
parseReceiveMessage(logger, runner, webSocket, this.receiveBuffers);
|
||||
} else {
|
||||
this.receiveCount += remain;
|
||||
}
|
||||
return over;
|
||||
}
|
||||
|
||||
@@ -379,7 +391,7 @@ public final class WebSocketPacket {
|
||||
*
|
||||
* @return 返回NONE表示Buffer内容不够; 返回this表示解析完成或部分解析完成;返回null表示解析异常;
|
||||
*/
|
||||
WebSocketPacket decode(final Logger logger, final WebSocket webSocket, final int wsmaxbody,
|
||||
WebSocketPacket decode(final Logger logger, final WebSocketRunner runner, final WebSocket webSocket, final int wsmaxbody,
|
||||
final AbstractMap.SimpleEntry<String, byte[]> halfBytes, final ByteBuffer buffer) {
|
||||
//开始
|
||||
final boolean debug = false; //调试开关
|
||||
@@ -394,9 +406,13 @@ public final class WebSocketPacket {
|
||||
final byte opcode = buffer.get(); //第一个字节
|
||||
this.last = (opcode & 0b1000_0000) != 0;
|
||||
this.type = FrameType.valueOf(opcode & 0xF);
|
||||
|
||||
if (type == FrameType.CLOSE) {
|
||||
if (debug) logger.log(Level.FINEST, " receive close command from websocket client");
|
||||
}
|
||||
if (type == null) {
|
||||
logger.log(Level.SEVERE, " receive unknown frametype(opcode=" + (opcode & 0xF) + ") from websocket client");
|
||||
}
|
||||
final boolean checkrsv = false;//暂时不校验
|
||||
if (checkrsv && (opcode & 0b0111_0000) != 0) {
|
||||
if (debug) logger.log(Level.FINE, "rsv1 rsv2 rsv3 must be 0, but not (" + opcode + ")");
|
||||
@@ -438,15 +454,18 @@ public final class WebSocketPacket {
|
||||
}
|
||||
if (lengthCode == 0x7E) {//0x7E=126
|
||||
length = (int) buffer.getChar();
|
||||
} else if (lengthCode == 0x7F) {//0x7E=127
|
||||
length = (int) buffer.getLong();
|
||||
} else {
|
||||
length = buffer.getInt();
|
||||
}
|
||||
}
|
||||
if (length > wsmaxbody && wsmaxbody > 0) {
|
||||
if (debug) logger.log(Level.FINE, "message length (" + length + ") too big, must less " + wsmaxbody + "");
|
||||
logger.log(Level.WARNING, "message length (" + length + ") too big, must less " + wsmaxbody + "");
|
||||
return null;
|
||||
}
|
||||
this.receiveLength = length;
|
||||
if (debug) logger.finest("this.receiveLength: " + length + ", code=" + lengthCode + ", last=" + last);
|
||||
if (masked) {
|
||||
final byte[] masks = new byte[4];
|
||||
buffer.get(masks);
|
||||
@@ -461,7 +480,7 @@ public final class WebSocketPacket {
|
||||
};
|
||||
}
|
||||
if (buffer.remaining() >= this.receiveLength) { //内容足够, 可以解析
|
||||
this.parseReceiveMessage(webSocket, buffer);
|
||||
this.parseReceiveMessage(logger, runner, webSocket, buffer);
|
||||
this.receiveCount = this.receiveLength;
|
||||
} else {
|
||||
this.receiveCount = buffer.remaining();
|
||||
@@ -470,38 +489,77 @@ public final class WebSocketPacket {
|
||||
return this;
|
||||
}
|
||||
|
||||
void parseReceiveMessage(WebSocket webSocket, ByteBuffer... buffers) {
|
||||
void parseReceiveMessage(final Logger logger, WebSocketRunner runner, WebSocket webSocket, ByteBuffer... buffers) {
|
||||
if (webSocket._engine.cryptor != null) {
|
||||
HttpContext context = webSocket._engine.context;
|
||||
buffers = webSocket._engine.cryptor.decrypt(buffers, context.getBufferSupplier(), context.getBufferConsumer());
|
||||
}
|
||||
if (this.type == FrameType.TEXT) {
|
||||
FrameType selfType = this.type;
|
||||
final boolean series = selfType == FrameType.SERIES;
|
||||
if (series) {
|
||||
selfType = runner.currSeriesMergeFrameType;
|
||||
this.type = selfType;
|
||||
} else if (!this.last && (selfType == FrameType.TEXT || selfType == FrameType.BINARY)) {
|
||||
runner.currSeriesMergeFrameType = selfType;
|
||||
}
|
||||
|
||||
if (selfType == FrameType.TEXT) {
|
||||
Convert textConvert = webSocket.getTextConvert();
|
||||
if (textConvert == null) {
|
||||
if (textConvert == null || (!runner.mergemsg && (series || !this.last))) {
|
||||
this.receiveMessage = new String(this.getReceiveBytes(buffers), StandardCharsets.UTF_8);
|
||||
this.receiveType = MessageType.STRING;
|
||||
} else {
|
||||
this.receiveMessage = textConvert.convertFrom(webSocket._messageTextType, this.receiveMasker, buffers);
|
||||
if (this.last || !runner.mergemsg) {
|
||||
if (runner.currSeriesMergeMessage == null) {
|
||||
this.receiveMessage = textConvert.convertFrom(webSocket._messageTextType, this.receiveMasker, buffers);
|
||||
} else {
|
||||
runner.currSeriesMergeMessage.write(this.getReceiveBytes(buffers));
|
||||
try {
|
||||
this.receiveMessage = textConvert.convertFrom(webSocket._messageTextType, runner.currSeriesMergeMessage.getBytes());
|
||||
} finally {
|
||||
runner.currSeriesMergeMessage = null;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (runner.currSeriesMergeMessage == null) runner.currSeriesMergeMessage = new ByteArray();
|
||||
runner.currSeriesMergeMessage.write(this.getReceiveBytes(buffers));
|
||||
this.receiveMessage = MESSAGE_NIL;
|
||||
}
|
||||
this.receiveCount = this.receiveLength;
|
||||
this.receiveType = MessageType.OBJECT;
|
||||
}
|
||||
} else if (this.type == FrameType.BINARY) {
|
||||
} else if (selfType == FrameType.BINARY) {
|
||||
Convert binaryConvert = webSocket.getBinaryConvert();
|
||||
if (binaryConvert == null) {
|
||||
if (binaryConvert == null || (!runner.mergemsg && (series || !this.last))) {
|
||||
this.receiveMessage = this.getReceiveBytes(buffers);
|
||||
this.receiveType = MessageType.BYTES;
|
||||
} else {
|
||||
this.receiveMessage = binaryConvert.convertFrom(webSocket._messageTextType, this.receiveMasker, buffers);
|
||||
if (this.last || !runner.mergemsg) {
|
||||
if (runner.currSeriesMergeMessage == null) {
|
||||
this.receiveMessage = binaryConvert.convertFrom(webSocket._messageTextType, this.receiveMasker, buffers);
|
||||
} else {
|
||||
runner.currSeriesMergeMessage.write(this.getReceiveBytes(buffers));
|
||||
try {
|
||||
this.receiveMessage = binaryConvert.convertFrom(webSocket._messageTextType, runner.currSeriesMergeMessage.getBytes());
|
||||
} finally {
|
||||
runner.currSeriesMergeMessage = null;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (runner.currSeriesMergeMessage == null) runner.currSeriesMergeMessage = new ByteArray();
|
||||
runner.currSeriesMergeMessage.write(this.getReceiveBytes(buffers));
|
||||
this.receiveMessage = MESSAGE_NIL;
|
||||
}
|
||||
this.receiveCount = this.receiveLength;
|
||||
this.receiveType = MessageType.OBJECT;
|
||||
}
|
||||
} else if (this.type == FrameType.PING) {
|
||||
} else if (selfType == FrameType.PING) {
|
||||
this.receiveMessage = this.getReceiveBytes(buffers);
|
||||
this.receiveType = MessageType.BYTES;
|
||||
} else if (this.type == FrameType.PONG) {
|
||||
} else if (selfType == FrameType.PONG) {
|
||||
this.receiveMessage = this.getReceiveBytes(buffers);
|
||||
this.receiveType = MessageType.BYTES;
|
||||
} else if (this.type == FrameType.CLOSE) {
|
||||
} else if (selfType == FrameType.CLOSE) {
|
||||
this.receiveMessage = this.getReceiveBytes(buffers);
|
||||
this.receiveType = MessageType.BYTES;
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ import java.util.AbstractMap.SimpleEntry;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.logging.*;
|
||||
import org.redkale.util.ByteArray;
|
||||
|
||||
/**
|
||||
* WebSocket的消息接收发送器, 一个WebSocket对应一个WebSocketRunner
|
||||
@@ -34,10 +35,14 @@ class WebSocketRunner implements Runnable {
|
||||
|
||||
protected final HttpContext context;
|
||||
|
||||
private ByteBuffer readBuffer;
|
||||
protected final boolean mergemsg;
|
||||
|
||||
volatile boolean closed = false;
|
||||
|
||||
FrameType currSeriesMergeFrameType;
|
||||
|
||||
ByteArray currSeriesMergeMessage;
|
||||
|
||||
private final BiConsumer<WebSocket, Object> restMessageConsumer; //主要供RestWebSocket使用
|
||||
|
||||
protected long lastSendTime;
|
||||
@@ -48,20 +53,21 @@ class WebSocketRunner implements Runnable {
|
||||
this.context = context;
|
||||
this.engine = webSocket._engine;
|
||||
this.webSocket = webSocket;
|
||||
this.mergemsg = webSocket._engine.mergemsg;
|
||||
this.restMessageConsumer = messageConsumer;
|
||||
this.channel = channel;
|
||||
this.readBuffer = context.pollBuffer();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
final boolean debug = context.getLogger().isLoggable(Level.FINEST);
|
||||
final WebSocketRunner self = this;
|
||||
try {
|
||||
webSocket.onConnected();
|
||||
channel.setReadTimeoutSeconds(300); //读取超时5分钟
|
||||
if (channel.isOpen()) {
|
||||
final int wsmaxbody = webSocket._engine.wsmaxbody;
|
||||
channel.read(readBuffer, null, new CompletionHandler<Integer, Void>() {
|
||||
channel.read(new CompletionHandler<Integer, ByteBuffer>() {
|
||||
|
||||
//尚未解析完的数据包
|
||||
private WebSocketPacket unfinishPacket;
|
||||
@@ -72,31 +78,27 @@ class WebSocketRunner implements Runnable {
|
||||
private final SimpleEntry<String, byte[]> halfBytes = new SimpleEntry("", null);
|
||||
|
||||
@Override
|
||||
public void completed(Integer count, Void attachment1) {
|
||||
public void completed(Integer count, ByteBuffer readBuffer) {
|
||||
if (count < 1) {
|
||||
if (debug) context.getLogger().log(Level.FINEST, "WebSocketRunner(userid=" + webSocket.getUserid() + ") abort on read buffer count, force to close channel, live " + (System.currentTimeMillis() - webSocket.getCreatetime()) / 1000 + " seconds");
|
||||
closeRunner(0, "read buffer count is " + count);
|
||||
closeRunner(CLOSECODE_ILLPACKET, "read buffer count is " + count);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
ByteBuffer readBuf = readBuffer;
|
||||
if (readBuf == null) return; //关闭后readBuffer为null
|
||||
lastReadTime = System.currentTimeMillis();
|
||||
readBuf.flip();
|
||||
readBuffer.flip();
|
||||
|
||||
WebSocketPacket onePacket = null;
|
||||
if (unfinishPacket != null) {
|
||||
if (unfinishPacket.receiveBody(webSocket, readBuf)) { //已经接收完毕
|
||||
if (unfinishPacket.receiveBody(context.getLogger(), self, webSocket, readBuffer)) { //已经接收完毕
|
||||
onePacket = unfinishPacket;
|
||||
unfinishPacket = null;
|
||||
for (ByteBuffer b : exBuffers) {
|
||||
context.offerBuffer(b);
|
||||
}
|
||||
exBuffers.clear();
|
||||
} else { //需要继续接收
|
||||
readBuf = context.pollBuffer();
|
||||
readBuffer = readBuf;
|
||||
channel.read(readBuf, null, this);
|
||||
} else { //需要继续接收, 此处不能回收readBuffer
|
||||
channel.read(this);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -105,39 +107,43 @@ class WebSocketRunner implements Runnable {
|
||||
if (onePacket != null) packets.add(onePacket);
|
||||
try {
|
||||
while (true) {
|
||||
WebSocketPacket packet = new WebSocketPacket().decode(context.getLogger(), webSocket, wsmaxbody, halfBytes, readBuf);
|
||||
WebSocketPacket packet = new WebSocketPacket().decode(context.getLogger(), self, webSocket, wsmaxbody, halfBytes, readBuffer);
|
||||
if (packet == WebSocketPacket.NONE) break; //解析完毕但是buffer有多余字节
|
||||
if (packet != null && !packet.isReceiveFinished()) {
|
||||
unfinishPacket = packet;
|
||||
if (readBuf.hasRemaining()) {
|
||||
exBuffers.add(readBuf);
|
||||
readBuf = context.pollBuffer();
|
||||
readBuffer = readBuf;
|
||||
if (readBuffer.hasRemaining()) {
|
||||
exBuffers.add(readBuffer);
|
||||
}
|
||||
break;
|
||||
}
|
||||
packets.add(packet);
|
||||
if (packet == null || !readBuf.hasRemaining()) break;
|
||||
if (packet == null || !readBuffer.hasRemaining()) break;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
context.getLogger().log(Level.SEVERE, "WebSocket parse message error", e);
|
||||
webSocket.onOccurException(e, null);
|
||||
}
|
||||
//继续监听消息
|
||||
readBuf.clear();
|
||||
if (readBuffer.hasRemaining()) { //exBuffers缓存了
|
||||
readBuffer = context.pollBuffer();
|
||||
} else {
|
||||
readBuffer.clear();
|
||||
}
|
||||
if (halfBytes.getValue() != null) {
|
||||
readBuf.put(halfBytes.getValue());
|
||||
readBuffer.put(halfBytes.getValue());
|
||||
halfBytes.setValue(null);
|
||||
}
|
||||
channel.read(readBuf, null, this);
|
||||
channel.setReadBuffer(readBuffer);
|
||||
channel.read(this);
|
||||
|
||||
//消息处理
|
||||
for (final WebSocketPacket packet : packets) {
|
||||
if (packet == null) {
|
||||
if (debug) context.getLogger().log(Level.FINEST, "WebSocketRunner abort on decode WebSocketPacket, force to close channel, live " + (System.currentTimeMillis() - webSocket.getCreatetime()) / 1000 + " seconds");
|
||||
failed(null, attachment1);
|
||||
failed(null, readBuffer);
|
||||
return;
|
||||
}
|
||||
if (packet.receiveMessage == WebSocketPacket.MESSAGE_NIL) continue; //last=false && mergemsg=true 的粘包
|
||||
|
||||
if (packet.type == FrameType.TEXT) {
|
||||
try {
|
||||
@@ -175,34 +181,34 @@ class WebSocketRunner implements Runnable {
|
||||
}
|
||||
} else if (packet.type == FrameType.PONG) {
|
||||
try {
|
||||
if (debug) context.getLogger().log(Level.FINEST, "WebSocketRunner onMessage by PONG FrameType : " + packet);
|
||||
//if (debug) context.getLogger().log(Level.FINEST, "WebSocketRunner onMessage by PONG FrameType : " + packet);
|
||||
webSocket.onPong((byte[]) packet.receiveMessage);
|
||||
} catch (Exception e) {
|
||||
context.getLogger().log(Level.SEVERE, "WebSocket onPong error (" + packet + ")", e);
|
||||
}
|
||||
} else if (packet.type == FrameType.CLOSE) {
|
||||
if (debug) context.getLogger().log(Level.FINEST, "WebSocketRunner onMessage by CLOSE FrameType : " + packet);
|
||||
closeRunner(0, "received CLOSE frame-type message");
|
||||
closeRunner(CLOSECODE_CLIENTCLOSE, "received CLOSE frame-type message");
|
||||
return;
|
||||
} else {
|
||||
context.getLogger().log(Level.WARNING, "WebSocketRunner onMessage by unknown FrameType : " + packet);
|
||||
closeRunner(0, "received unknown frame-type message");
|
||||
closeRunner(CLOSECODE_ILLPACKET, "received unknown frame-type message");
|
||||
return;
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
context.getLogger().log(Level.WARNING, "WebSocketRunner(userid=" + webSocket.getUserid() + ") onMessage by received error", e);
|
||||
closeRunner(0, "websocket-received error");
|
||||
closeRunner(CLOSECODE_WSEXCEPTION, "websocket-received error");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void failed(Throwable exc, Void attachment2) {
|
||||
public void failed(Throwable exc, ByteBuffer attachment2) {
|
||||
if (exc != null) {
|
||||
if (debug) context.getLogger().log(Level.FINEST, "WebSocketRunner read WebSocketPacket failed, force to close channel, live " + (System.currentTimeMillis() - webSocket.getCreatetime()) / 1000 + " seconds", exc);
|
||||
closeRunner(0, "read websocket-packet failed");
|
||||
closeRunner(CLOSECODE_WSEXCEPTION, "read websocket-packet failed");
|
||||
} else {
|
||||
closeRunner(RETCODE_ILLEGALBUFFER, "decode websocket-packet error");
|
||||
closeRunner(CLOSECODE_WSEXCEPTION, "decode websocket-packet error");
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -212,7 +218,7 @@ class WebSocketRunner implements Runnable {
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
if (debug) context.getLogger().log(Level.FINEST, "WebSocketRunner abort on read bytes from channel, force to close channel, live " + (System.currentTimeMillis() - webSocket.getCreatetime()) / 1000 + " seconds", e);
|
||||
closeRunner(0, "read bytes from channel error");
|
||||
closeRunner(CLOSECODE_WSEXCEPTION, "read bytes from channel error");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -277,8 +283,8 @@ class WebSocketRunner implements Runnable {
|
||||
public void failed(Throwable exc, ByteBuffer[] attachments) {
|
||||
future.complete(RETCODE_SENDEXCEPTION);
|
||||
closeRunner(RETCODE_SENDEXCEPTION, "websocket send message failed on CompletionHandler");
|
||||
if (exc != null) {
|
||||
context.getLogger().log(Level.FINE, "WebSocket sendMessage on CompletionHandler failed, force to close channel, live " + (System.currentTimeMillis() - webSocket.getCreatetime()) / 1000 + " seconds", exc);
|
||||
if (exc != null && context.getLogger().isLoggable(Level.FINER)) {
|
||||
context.getLogger().log(Level.FINER, "WebSocket sendMessage on CompletionHandler failed, force to close channel, live " + (System.currentTimeMillis() - webSocket.getCreatetime()) / 1000 + " seconds", exc);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -286,7 +292,9 @@ class WebSocketRunner implements Runnable {
|
||||
} catch (Exception t) {
|
||||
futureResult.complete(RETCODE_SENDEXCEPTION);
|
||||
closeRunner(RETCODE_SENDEXCEPTION, "websocket send message failed on channel.write");
|
||||
context.getLogger().log(Level.FINE, "WebSocket sendMessage abort, force to close channel, live " + (System.currentTimeMillis() - webSocket.getCreatetime()) / 1000 + " seconds", t);
|
||||
if (t != null && context.getLogger().isLoggable(Level.FINER)) {
|
||||
context.getLogger().log(Level.FINER, "WebSocket sendMessage abort, force to close channel, live " + (System.currentTimeMillis() - webSocket.getCreatetime()) / 1000 + " seconds", t);
|
||||
}
|
||||
|
||||
}
|
||||
return futureResult;
|
||||
@@ -301,26 +309,11 @@ class WebSocketRunner implements Runnable {
|
||||
synchronized (this) {
|
||||
if (closed) return null;
|
||||
closed = true;
|
||||
CompletableFuture<Void> future = engine.removeLocalThenClose(webSocket);
|
||||
channel.dispose();
|
||||
context.offerBuffer(readBuffer);
|
||||
readBuffer = null;
|
||||
CompletableFuture<Void> future = engine.removeThenClose(webSocket);
|
||||
webSocket.onClose(code, reason);
|
||||
return future;
|
||||
}
|
||||
}
|
||||
|
||||
private static final class QueueEntry {
|
||||
|
||||
public final CompletableFuture<Integer> future;
|
||||
|
||||
public final WebSocketPacket packet;
|
||||
|
||||
public QueueEntry(CompletableFuture<Integer> future, WebSocketPacket packet) {
|
||||
this.future = future;
|
||||
this.packet = packet;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -56,6 +56,9 @@ public abstract class WebSocketServlet extends HttpServlet implements Resourcabl
|
||||
@Comment("最大消息体长度, 小于1表示无限制")
|
||||
public static final String WEBPARAM__WSMAXBODY = "wsmaxbody";
|
||||
|
||||
@Comment("接收客户端的分包(last=false)消息时是否自动合并包")
|
||||
public static final String WEBPARAM__WSMERGEMSG = "wsmergemsg";
|
||||
|
||||
@Comment("加密解密器")
|
||||
public static final String WEBPARAM__CRYPTOR = "cryptor";
|
||||
|
||||
@@ -88,6 +91,9 @@ public abstract class WebSocketServlet extends HttpServlet implements Resourcabl
|
||||
//同RestWebSocket.anyuser
|
||||
protected boolean anyuser = false;
|
||||
|
||||
//同RestWebSocket.mergemsg
|
||||
protected boolean mergemsg = true;
|
||||
|
||||
//同RestWebSocket.cryptor, 变量名不可改, 被Rest.createRestWebSocketServlet用到
|
||||
protected Cryptor cryptor;
|
||||
|
||||
@@ -106,6 +112,9 @@ public abstract class WebSocketServlet extends HttpServlet implements Resourcabl
|
||||
@Resource(name = "$")
|
||||
protected WebSocketNode node;
|
||||
|
||||
@Resource(name = "SERVER_RESFACTORY")
|
||||
protected ResourceFactory resourceFactory;
|
||||
|
||||
protected WebSocketServlet() {
|
||||
Type msgtype = String.class;
|
||||
try {
|
||||
@@ -145,6 +154,7 @@ public abstract class WebSocketServlet extends HttpServlet implements Resourcabl
|
||||
if (cryptorClass != null && !cryptorClass.isEmpty()) {
|
||||
try {
|
||||
this.cryptor = (Cryptor) Thread.currentThread().getContextClassLoader().loadClass(cryptorClass).getDeclaredConstructor().newInstance();
|
||||
if (resourceFactory != null && this.cryptor != null) resourceFactory.inject(this.cryptor);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
@@ -153,7 +163,7 @@ public abstract class WebSocketServlet extends HttpServlet implements Resourcabl
|
||||
}
|
||||
//存在WebSocketServlet,则此WebSocketNode必须是本地模式Service
|
||||
this.node.localEngine = new WebSocketEngine("WebSocketEngine-" + addr.getHostString() + ":" + addr.getPort() + "-[" + resourceName() + "]",
|
||||
this.single, context, liveinterval, wsmaxconns, wsthreads, wsmaxbody, this.cryptor, this.node, this.sendConvert, logger);
|
||||
this.single, context, liveinterval, wsmaxconns, wsthreads, wsmaxbody, mergemsg, this.cryptor, this.node, this.sendConvert, logger);
|
||||
this.node.init(conf);
|
||||
this.node.localEngine.init(conf);
|
||||
|
||||
@@ -251,7 +261,7 @@ public abstract class WebSocketServlet extends HttpServlet implements Resourcabl
|
||||
CompletableFuture<Boolean> rcFuture = webSocket.onSingleRepeatConnect();
|
||||
Consumer<Boolean> task = (oldkilled) -> {
|
||||
if (oldkilled) {
|
||||
WebSocketServlet.this.node.localEngine.add(webSocket);
|
||||
WebSocketServlet.this.node.localEngine.addLocal(webSocket);
|
||||
WebSocketRunner runner = new WebSocketRunner(context, webSocket, restMessageConsumer, response.removeChannel());
|
||||
webSocket._runner = runner;
|
||||
context.runAsync(runner);
|
||||
@@ -272,7 +282,7 @@ public abstract class WebSocketServlet extends HttpServlet implements Resourcabl
|
||||
});
|
||||
}
|
||||
} else {
|
||||
WebSocketServlet.this.node.localEngine.add(webSocket);
|
||||
WebSocketServlet.this.node.localEngine.addLocal(webSocket);
|
||||
WebSocketRunner runner = new WebSocketRunner(context, webSocket, restMessageConsumer, response.removeChannel());
|
||||
webSocket._runner = runner;
|
||||
context.runAsync(runner);
|
||||
@@ -280,7 +290,7 @@ public abstract class WebSocketServlet extends HttpServlet implements Resourcabl
|
||||
}
|
||||
});
|
||||
} else {
|
||||
WebSocketServlet.this.node.localEngine.add(webSocket);
|
||||
WebSocketServlet.this.node.localEngine.addLocal(webSocket);
|
||||
WebSocketRunner runner = new WebSocketRunner(context, webSocket, restMessageConsumer, response.removeChannel());
|
||||
webSocket._runner = runner;
|
||||
context.runAsync(runner);
|
||||
|
||||
@@ -307,7 +307,7 @@ public final class SncpClient {
|
||||
byte i;
|
||||
while ((i = reader.readByte()) != 0) {
|
||||
final Attribute attr = action.paramAttrs[i];
|
||||
attr.set(params[i - 1], bsonConvert.convertFrom(attr.type(), reader));
|
||||
attr.set(params[i - 1], bsonConvert.convertFrom(attr.genericType(), reader));
|
||||
}
|
||||
Object rs = bsonConvert.convertFrom(Object.class, reader);
|
||||
|
||||
@@ -327,7 +327,7 @@ public final class SncpClient {
|
||||
byte i;
|
||||
while ((i = reader.readByte()) != 0) {
|
||||
final Attribute attr = action.paramAttrs[i];
|
||||
attr.set(params[i - 1], bsonConvert.convertFrom(attr.type(), reader));
|
||||
attr.set(params[i - 1], bsonConvert.convertFrom(attr.genericType(), reader));
|
||||
}
|
||||
return bsonConvert.convertFrom(action.handlerFuncParamIndex >= 0 ? Object.class : action.resultTypes, reader);
|
||||
} catch (RpcRemoteException re) {
|
||||
@@ -370,7 +370,6 @@ public final class SncpClient {
|
||||
final ByteBuffer[] sendBuffers = writer.toBuffers();
|
||||
fillHeader(sendBuffers[0], seqid, actionid, reqBodyLength);
|
||||
|
||||
final ByteBuffer buffer = transport.pollBuffer();
|
||||
conn.write(sendBuffers, sendBuffers, new CompletionHandler<Integer, ByteBuffer[]>() {
|
||||
|
||||
@Override
|
||||
@@ -393,25 +392,25 @@ public final class SncpClient {
|
||||
conn.write(newattachs, newattachs, this);
|
||||
return;
|
||||
}
|
||||
//----------------------- 读取返回结果 -------------------------------------
|
||||
buffer.clear();
|
||||
conn.read(buffer, null, new CompletionHandler<Integer, Void>() {
|
||||
//----------------------- 读取返回结果 -------------------------------------
|
||||
conn.read(new CompletionHandler<Integer, ByteBuffer>() {
|
||||
|
||||
private byte[] body;
|
||||
|
||||
private int received;
|
||||
|
||||
@Override
|
||||
public void completed(Integer count, Void attachment2) {
|
||||
public void completed(Integer count, ByteBuffer buffer) {
|
||||
try {
|
||||
if (count < 1 && buffer.remaining() == buffer.limit()) { //没有数据可读
|
||||
future.completeExceptionally(new RpcRemoteException(action.method + " sncp[" + conn.getRemoteAddress() + "] remote no response data"));
|
||||
transport.offerBuffer(buffer);
|
||||
conn.offerBuffer(buffer);
|
||||
transport.offerConnection(true, conn);
|
||||
return;
|
||||
}
|
||||
if (received < 1 && buffer.limit() < buffer.remaining() + HEADER_SIZE) { //header都没读全
|
||||
conn.read(buffer, attachment2, this);
|
||||
conn.setReadBuffer(buffer);
|
||||
conn.read(this);
|
||||
return;
|
||||
}
|
||||
buffer.flip();
|
||||
@@ -421,8 +420,10 @@ public final class SncpClient {
|
||||
buffer.get(body, offset, Math.min(buffer.remaining(), this.body.length - offset));
|
||||
if (this.received < this.body.length) {// 数据仍然不全,需要继续读取
|
||||
buffer.clear();
|
||||
conn.read(buffer, attachment2, this);
|
||||
conn.setReadBuffer(buffer);
|
||||
conn.read(this);
|
||||
} else {
|
||||
conn.offerBuffer(buffer);
|
||||
success();
|
||||
}
|
||||
return;
|
||||
@@ -441,10 +442,12 @@ public final class SncpClient {
|
||||
this.received = buffer.remaining();
|
||||
buffer.get(body, 0, this.received);
|
||||
buffer.clear();
|
||||
conn.read(buffer, attachment2, this);
|
||||
conn.setReadBuffer(buffer);
|
||||
conn.read(this);
|
||||
} else {
|
||||
this.body = new byte[respBodyLength];
|
||||
buffer.get(body, 0, respBodyLength);
|
||||
conn.offerBuffer(buffer);
|
||||
success();
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
@@ -461,7 +464,6 @@ public final class SncpClient {
|
||||
@SuppressWarnings("unchecked")
|
||||
public void success() {
|
||||
future.complete(this.body);
|
||||
transport.offerBuffer(buffer);
|
||||
transport.offerConnection(false, conn);
|
||||
if (handler != null) {
|
||||
final Object handlerAttach = action.handlerAttachParamIndex >= 0 ? params[action.handlerAttachParamIndex] : null;
|
||||
@@ -471,7 +473,7 @@ public final class SncpClient {
|
||||
int i;
|
||||
while ((i = (reader.readByte() & 0xff)) != 0) {
|
||||
final Attribute attr = action.paramAttrs[i];
|
||||
attr.set(params[i - 1], bsonConvert.convertFrom(attr.type(), reader));
|
||||
attr.set(params[i - 1], bsonConvert.convertFrom(attr.genericType(), reader));
|
||||
}
|
||||
Object rs = bsonConvert.convertFrom(action.handlerFuncParamIndex >= 0 ? Object.class : action.resultTypes, reader);
|
||||
handler.completed(rs, handlerAttach);
|
||||
@@ -484,9 +486,9 @@ public final class SncpClient {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void failed(Throwable exc, Void attachment2) {
|
||||
public void failed(Throwable exc, ByteBuffer attachment2) {
|
||||
future.completeExceptionally(new RuntimeException(action.method + " sncp remote exec failed"));
|
||||
transport.offerBuffer(buffer);
|
||||
conn.offerBuffer(attachment2);
|
||||
transport.offerConnection(true, conn);
|
||||
if (handler != null) {
|
||||
final Object handlerAttach = action.handlerAttachParamIndex >= 0 ? params[action.handlerAttachParamIndex] : null;
|
||||
@@ -500,7 +502,6 @@ public final class SncpClient {
|
||||
@Override
|
||||
public void failed(Throwable exc, ByteBuffer[] attachment) {
|
||||
future.completeExceptionally(new RuntimeException(action.method + " sncp remote exec failed"));
|
||||
transport.offerBuffer(buffer);
|
||||
transport.offerConnection(true, conn);
|
||||
if (handler != null) {
|
||||
final Object handlerAttach = action.handlerAttachParamIndex >= 0 ? params[action.handlerAttachParamIndex] : null;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user