Compare commits
174 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 | ||
|
|
6b54c6e886 | ||
|
|
a95b69ab79 | ||
|
|
a2492ae248 | ||
|
|
0db1c4413c | ||
|
|
8718bca6e8 | ||
|
|
6486863d00 | ||
|
|
bfc2397dbf | ||
|
|
0cfdb9795b | ||
|
|
d327757d59 | ||
|
|
e88c4fa2e3 | ||
|
|
69a0071e17 | ||
|
|
d3cfe809e7 | ||
|
|
0ca6e5401a | ||
|
|
8ff2b60ed4 | ||
|
|
1dbe125e27 | ||
|
|
32a595262d | ||
|
|
01431a93a4 | ||
|
|
00a12d8113 | ||
|
|
0f2474386c | ||
|
|
5de52ed6e8 | ||
|
|
099b3fb7f3 | ||
|
|
ed85e33403 | ||
|
|
daff7d095f | ||
|
|
a8ff82229f | ||
|
|
2874e7d195 | ||
|
|
8baea2657d | ||
|
|
03e9a5cbcb | ||
|
|
8d2f2a28af | ||
|
|
4a4bade180 | ||
|
|
28e72f9883 | ||
|
|
681e3967d2 | ||
|
|
640dcbc8d7 | ||
|
|
19276ecb91 | ||
|
|
9f9078cdc5 | ||
|
|
9bd1e0c97b | ||
|
|
c2c8f700a2 | ||
|
|
86895eb5ef | ||
|
|
e8dacd6b70 | ||
|
|
03fcf1dc95 | ||
|
|
953b2be224 | ||
|
|
55645ba2a0 | ||
|
|
858c6a0aaf | ||
|
|
85d4023fa2 | ||
|
|
607fc8e45f | ||
|
|
511a966ce5 | ||
|
|
6d135efbc6 | ||
|
|
5dff6310aa | ||
|
|
194a06c748 | ||
|
|
1ce97aa20d | ||
|
|
1c88fb0355 | ||
|
|
7b81c42377 | ||
|
|
1afa38a947 | ||
|
|
eedb418299 | ||
|
|
9efc14e627 | ||
|
|
0dc3d65307 | ||
|
|
e3ddbe609c | ||
|
|
f115934499 | ||
|
|
f5e290a47a | ||
|
|
29cd395888 | ||
|
|
9ca7855756 | ||
|
|
1f1ab112eb | ||
|
|
b172b66263 | ||
|
|
b08f9f5757 | ||
|
|
32e65ff304 | ||
|
|
40126c923e | ||
|
|
83ae5b0a13 | ||
|
|
aea6a2839d | ||
|
|
6211d9ff18 | ||
|
|
0e7fab71b8 | ||
|
|
a106c8ed60 | ||
|
|
ae52d0ab3d | ||
|
|
c993cd5b5a | ||
|
|
9bdd83cc6e | ||
|
|
1b6a23c1a9 | ||
|
|
2703d94b83 | ||
|
|
4cb51ecbe6 | ||
|
|
0cf64121ae | ||
|
|
58eb3c5d64 | ||
|
|
3c645b4c7f | ||
|
|
a540eed0ed |
@@ -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
|
||||
|
||||
@@ -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,7 +8,7 @@
|
||||
|
||||
</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"/>
|
||||
|
||||
@@ -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>
|
||||
@@ -26,7 +25,6 @@
|
||||
<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.user" value="root"/>
|
||||
<property name="javax.persistence.jdbc.password" value="1234"/>
|
||||
</properties>
|
||||
|
||||
@@ -63,6 +63,7 @@
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.5.1</version>
|
||||
<configuration>
|
||||
<compilerArgument>-parameters</compilerArgument>
|
||||
<encoding>UTF-8</encoding>
|
||||
<compilerArguments>
|
||||
<verbose />
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -89,6 +89,8 @@ public final class ApiDocsService {
|
||||
for (final Class rtype : action.results()) {
|
||||
results.add(rtype.getName());
|
||||
if (typesmap.containsKey(rtype.getName())) continue;
|
||||
if(rtype.getName().startsWith("java.")) continue;
|
||||
if(rtype.getName().startsWith("javax.")) continue;
|
||||
final boolean filter = FilterBean.class.isAssignableFrom(rtype);
|
||||
final Map<String, Map<String, Object>> typemap = new LinkedHashMap<>();
|
||||
Class loop = rtype;
|
||||
@@ -136,6 +138,8 @@ public final class ApiDocsService {
|
||||
paramsList.add(parammap);
|
||||
if (ptype.isPrimitive() || ptype == String.class) continue;
|
||||
if (typesmap.containsKey(ptype.getName())) continue;
|
||||
if(ptype.getName().startsWith("java.")) continue;
|
||||
if(ptype.getName().startsWith("javax.")) continue;
|
||||
|
||||
final Map<String, Map<String, Object>> typemap = new LinkedHashMap<>();
|
||||
Class loop = ptype;
|
||||
@@ -192,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.getHome(), "conf/apidoc-template.html");
|
||||
File doctemplate = new File(app.getConf(), "apidoc-template.html");
|
||||
InputStream in = null;
|
||||
if (doctemplate.isFile() && doctemplate.canRead()) {
|
||||
in = new FileInputStream(doctemplate);
|
||||
|
||||
@@ -61,6 +61,11 @@ public final class Application {
|
||||
*/
|
||||
public static final String RESNAME_APP_HOME = "APP_HOME";
|
||||
|
||||
/**
|
||||
* 当前进程的配置目录,如果不是绝对路径则视为HOME目录下的相对路径 类型:String、File、Path
|
||||
*/
|
||||
public static final String RESNAME_APP_CONF = "APP_CONF";
|
||||
|
||||
/**
|
||||
* application.xml 文件中resources节点的内容, 类型: AnyValue
|
||||
*/
|
||||
@@ -137,6 +142,9 @@ public final class Application {
|
||||
//进程根目录
|
||||
private final File home;
|
||||
|
||||
//配置文件目录
|
||||
private final File conf;
|
||||
|
||||
//日志
|
||||
private final Logger logger;
|
||||
|
||||
@@ -162,6 +170,7 @@ public final class Application {
|
||||
private Application(final boolean singletonrun, final AnyValue config) {
|
||||
this.singletonrun = singletonrun;
|
||||
this.config = config;
|
||||
System.setProperty("redkale.version", Redkale.getDotedVersion());
|
||||
|
||||
final File root = new File(System.getProperty(RESNAME_APP_HOME));
|
||||
this.resourceFactory.register(RESNAME_APP_TIME, long.class, this.startTime);
|
||||
@@ -170,6 +179,12 @@ public final class Application {
|
||||
try {
|
||||
this.resourceFactory.register(RESNAME_APP_HOME, root.getCanonicalPath());
|
||||
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();
|
||||
} else {
|
||||
this.conf = new File(this.home, confsubpath).getCanonicalFile();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
@@ -194,7 +209,7 @@ public final class Application {
|
||||
System.setProperty(RESNAME_APP_NODE, node);
|
||||
}
|
||||
//以下是初始化日志配置
|
||||
final File logconf = new File(root, "conf/logging.properties");
|
||||
final File logconf = new File(conf, "logging.properties");
|
||||
if (logconf.isFile() && logconf.canRead()) {
|
||||
try {
|
||||
final String rootpath = root.getCanonicalPath().replace('\\', '/');
|
||||
@@ -348,10 +363,22 @@ public final class Application {
|
||||
return new ArrayList<>(servers);
|
||||
}
|
||||
|
||||
public List<DataSource> getDataSources() {
|
||||
return new ArrayList<>(dataSources);
|
||||
}
|
||||
|
||||
public List<CacheSource> getCacheSources() {
|
||||
return new ArrayList<>(cacheSources);
|
||||
}
|
||||
|
||||
public File getHome() {
|
||||
return home;
|
||||
}
|
||||
|
||||
public File getConf() {
|
||||
return conf;
|
||||
}
|
||||
|
||||
public long getStartTime() {
|
||||
return startTime;
|
||||
}
|
||||
@@ -361,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");
|
||||
@@ -373,12 +398,21 @@ 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.home, "conf/persistence.xml");
|
||||
File persist = new File(this.conf, "persistence.xml");
|
||||
final String homepath = this.home.getCanonicalPath();
|
||||
final String confpath = this.conf.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);
|
||||
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() ? (homepath + "/conf") : (lib + ";" + homepath + "/conf");
|
||||
lib = lib.isEmpty() ? confpath : (lib + ";" + confpath);
|
||||
Server.loadLib(classLoader, logger, lib);
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
@@ -391,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(home, "conf/" + dfload) : new File(dfload);
|
||||
final File df = (dfload.indexOf('/') < 0) ? new File(conf, "/" + 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)));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -405,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.")) {
|
||||
@@ -524,8 +559,8 @@ public final class Application {
|
||||
|
||||
public void restoreConfig() throws IOException {
|
||||
synchronized (this) {
|
||||
File confFile = new File(this.home, "conf/application.xml");
|
||||
confFile.renameTo(new File(this.home, "conf/application_" + String.format("%1$tY%1$tm%1$td%1$tH%1$tM%1$tS", System.currentTimeMillis()) + ".xml"));
|
||||
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"));
|
||||
final PrintStream ps = new PrintStream(new FileOutputStream(confFile));
|
||||
ps.append(config.toXML("application"));
|
||||
ps.close();
|
||||
@@ -653,19 +688,21 @@ public final class Application {
|
||||
runServers(timecd, watchs); //必须在所有服务都启动后再启动WATCH服务
|
||||
timecd.await();
|
||||
//if (!singletonrun) signalHandle();
|
||||
if (!singletonrun) clearPersistData();
|
||||
//if (!singletonrun) clearPersistData();
|
||||
logger.info(this.getClass().getSimpleName() + " started in " + (System.currentTimeMillis() - startTime) + " ms\r\n");
|
||||
if (!singletonrun) this.serversLatch.await();
|
||||
}
|
||||
|
||||
private void clearPersistData() {
|
||||
File cachedir = new File(home, "cache");
|
||||
if (!cachedir.isDirectory()) return;
|
||||
for (File file : cachedir.listFiles()) {
|
||||
if (file.getName().startsWith("persist-")) file.delete();
|
||||
}
|
||||
}
|
||||
|
||||
// private void clearPersistData() {
|
||||
// File cachedir = new File(home, "cache");
|
||||
// if (!cachedir.isDirectory()) return;
|
||||
// File[] lfs = cachedir.listFiles();
|
||||
// if (lfs != null) {
|
||||
// for (File file : lfs) {
|
||||
// if (file.getName().startsWith("persist-")) file.delete();
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// private void signalHandle() {
|
||||
// //http://www.comptechdoc.org/os/linux/programming/linux_pgsignals.html
|
||||
// String[] sigs = new String[]{"HUP", "TERM", "INT", "QUIT", "KILL", "TSTP", "USR1", "USR2", "STOP"};
|
||||
@@ -796,8 +833,15 @@ public final class Application {
|
||||
public static Application create(final boolean singleton) throws IOException {
|
||||
final String home = new File(System.getProperty(RESNAME_APP_HOME, "")).getCanonicalPath().replace('\\', '/');
|
||||
System.setProperty(RESNAME_APP_HOME, home);
|
||||
File appfile = new File(home, "conf/application.xml");
|
||||
return new Application(singleton, load(new FileInputStream(appfile)));
|
||||
String confsubpath = System.getProperty(RESNAME_APP_CONF, "conf");
|
||||
File appfile;
|
||||
if (confsubpath.charAt(0) == '/' || confsubpath.indexOf(':') > 0) {
|
||||
appfile = new File(confsubpath).getCanonicalFile();
|
||||
} else {
|
||||
appfile = new File(new File(home), confsubpath);
|
||||
}
|
||||
File appconf = new File(appfile, "application.xml");
|
||||
return new Application(singleton, load(new FileInputStream(appconf)));
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
|
||||
@@ -7,7 +7,7 @@ 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.*;
|
||||
@@ -30,7 +30,7 @@ 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<>(); //符合条件的结果
|
||||
|
||||
@@ -136,10 +136,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 +151,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 +178,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;
|
||||
@@ -189,9 +202,10 @@ public final class ClassFilter<T> {
|
||||
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) || ((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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -347,6 +361,7 @@ public final class ClassFilter<T> {
|
||||
|
||||
public void setPrivilegeExcludes(Set<String> privilegeExcludes) {
|
||||
this.privilegeExcludes = privilegeExcludes == null || privilegeExcludes.isEmpty() ? null : privilegeExcludes;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -502,7 +517,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 +526,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 +545,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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -550,7 +565,9 @@ public final class ClassFilter<T> {
|
||||
files.add(root);
|
||||
} else if (root.isDirectory()) {
|
||||
if (exclude != null && exclude.equals(root)) return;
|
||||
for (File f : root.listFiles()) {
|
||||
File[] lfs = root.listFiles();
|
||||
if (lfs == null) throw new RuntimeException("File(" + root + ") cannot listFiles()");
|
||||
for (File f : lfs) {
|
||||
loadClassFiles(exclude, f, files);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
@@ -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<>();
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -370,6 +370,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 +402,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 +414,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);
|
||||
@@ -473,24 +477,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());
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@
|
||||
html.push(' <tr>');
|
||||
html.push('<td style="color:#ff00ff;">' + action.url + '</td>');
|
||||
html.push('<td>' + action.comment + '</td>');
|
||||
html.push('<td class="s">模块ID: ' + servlet.moduleid + '<br/>操作ID: ' + action.actionid + '<br/>需鉴权: ' + (action.auth ? '<font style="font-weight:bold;color:green;">true</font>' : '<font color=red>false</font>') + '</td>');
|
||||
html.push('<td class="s" style="width:80px;">模块ID: ' + servlet.moduleid + '<br/>操作ID: ' + action.actionid + '<br/>需鉴权: ' + (action.auth ? '<font style="font-weight:bold;color:green;">true</font>' : '<font color=red>false</font>') + '</td>');
|
||||
var paramshtml = [];
|
||||
paramshtml.push('<table class="subtable">');
|
||||
for (var p = 0; p < action.params.length; p++) {
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
package org.redkale.boot.watch;
|
||||
|
||||
import org.redkale.service.AbstractService;
|
||||
import org.redkale.util.Comment;
|
||||
import org.redkale.watch.WatchService;
|
||||
|
||||
/**
|
||||
@@ -14,4 +15,9 @@ import org.redkale.watch.WatchService;
|
||||
*/
|
||||
public abstract class AbstractWatchService extends AbstractService implements WatchService {
|
||||
|
||||
@Comment("缺少参数")
|
||||
public static final int RET_WATCH_PARAMS_ILLEGAL = 1600_0001;
|
||||
|
||||
@Comment("执行异常")
|
||||
public static final int RET_WATCH_RUN_EXCEPTION = 1600_0002;
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ public class FilterWatchService extends AbstractWatchService {
|
||||
public static final int RET_FILTER_JAR_ILLEGAL = 1601_0005;
|
||||
|
||||
@Resource
|
||||
private Application application;
|
||||
protected Application application;
|
||||
|
||||
@RestMapping(name = "addfilter", auth = false, comment = "动态增加Filter")
|
||||
public RetResult addFilter(@RestUploadFile(maxLength = 10 * 1024 * 1024, fileNameReg = "\\.jar$") byte[] jar,
|
||||
@@ -47,4 +47,34 @@ public class FilterWatchService extends AbstractWatchService {
|
||||
}
|
||||
return RetResult.success();
|
||||
}
|
||||
|
||||
@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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ public class ServerWatchService extends AbstractWatchService {
|
||||
public static final int RET_SERVER_NOT_EXISTS = 1602_0001;
|
||||
|
||||
@Resource
|
||||
private Application application;
|
||||
protected Application application;
|
||||
|
||||
@RestMapping(name = "info", comment = "单个Server信息查询")
|
||||
public RetResult info(@RestParam(name = "#port:") int port) {
|
||||
|
||||
@@ -5,10 +5,14 @@
|
||||
*/
|
||||
package org.redkale.boot.watch;
|
||||
|
||||
import java.lang.reflect.*;
|
||||
import java.util.*;
|
||||
import javax.annotation.Resource;
|
||||
import org.redkale.boot.Application;
|
||||
import org.redkale.net.TransportFactory;
|
||||
import org.redkale.boot.*;
|
||||
import org.redkale.convert.json.JsonConvert;
|
||||
import org.redkale.net.http.*;
|
||||
import org.redkale.service.RetResult;
|
||||
import org.redkale.util.*;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
@@ -19,21 +23,173 @@ import org.redkale.net.http.*;
|
||||
@RestService(name = "service", catalog = "watch", repair = false)
|
||||
public class ServiceWatchService extends AbstractWatchService {
|
||||
|
||||
@Resource
|
||||
private Application application;
|
||||
@Comment("没有找到目标Service")
|
||||
public static final int RET_SERVICE_DEST_NOT_EXISTS = 1603_0001;
|
||||
|
||||
@Resource
|
||||
private TransportFactory transportFactory;
|
||||
protected Application application;
|
||||
|
||||
// @RestMapping(name = "load", auth = false, comment = "动态增加Service")
|
||||
// public RetResult loadService(String type, @RestUploadFile(maxLength = 10 * 1024 * 1024, fileNameReg = "\\.jar$") byte[] jar) {
|
||||
// //待开发
|
||||
// return RetResult.success();
|
||||
// }
|
||||
//
|
||||
// @RestMapping(name = "stop", auth = false, comment = "动态停止Service")
|
||||
// public RetResult stopService(String name, String type) {
|
||||
// //待开发
|
||||
// return RetResult.success();
|
||||
// }
|
||||
@RestConvert(type = void.class)
|
||||
@RestMapping(name = "setfield", auth = false, comment = "设置Service中指定字段的内容")
|
||||
public RetResult setfield(@RestParam(name = "name", comment = "Service的资源名") String name,
|
||||
@RestParam(name = "type", comment = "Service的类名") String type,
|
||||
@RestParam(name = "field", comment = "字段名") String field,
|
||||
@RestParam(name = "value", comment = "字段值") String value) {
|
||||
if (name == null) name = "";
|
||||
if (type == null) type = "";
|
||||
if (field == null) field = "";
|
||||
type = type.trim();
|
||||
field = field.trim();
|
||||
if (type.isEmpty()) return new RetResult(RET_WATCH_PARAMS_ILLEGAL, "not found param `type`");
|
||||
if (field.isEmpty()) return new RetResult(RET_WATCH_PARAMS_ILLEGAL, "not found param `field`");
|
||||
Object dest = findService(name, type);
|
||||
Class clazz = dest.getClass();
|
||||
Throwable t = null;
|
||||
try {
|
||||
Field fieldObj = null;
|
||||
do {
|
||||
try {
|
||||
fieldObj = clazz.getDeclaredField(field);
|
||||
break;
|
||||
} catch (Exception e) {
|
||||
if (t == null) t = e;
|
||||
}
|
||||
} while ((clazz = clazz.getSuperclass()) != Object.class);
|
||||
if (fieldObj == null) return new RetResult(RET_WATCH_RUN_EXCEPTION, "run exception (" + String.valueOf(t) + ")");
|
||||
fieldObj.setAccessible(true);
|
||||
fieldObj.set(dest, JsonConvert.root().convertFrom(fieldObj.getGenericType(), value));
|
||||
return RetResult.success();
|
||||
} catch (Throwable t2) {
|
||||
return new RetResult(RET_WATCH_RUN_EXCEPTION, "run exception (" + t2.toString() + ")");
|
||||
}
|
||||
}
|
||||
|
||||
@RestConvert(type = void.class)
|
||||
@RestMapping(name = "getfield", auth = false, comment = "查询Service中指定字段的内容")
|
||||
public RetResult getfield(@RestParam(name = "name", comment = "Service的资源名") String name,
|
||||
@RestParam(name = "type", comment = "Service的类名") String type,
|
||||
@RestParam(name = "field", comment = "字段名") String field) {
|
||||
if (name == null) name = "";
|
||||
if (type == null) type = "";
|
||||
if (field == null) field = "";
|
||||
type = type.trim();
|
||||
field = field.trim();
|
||||
if (type.isEmpty()) return new RetResult(RET_WATCH_PARAMS_ILLEGAL, "not found param `type`");
|
||||
if (field.isEmpty()) return new RetResult(RET_WATCH_PARAMS_ILLEGAL, "not found param `field`");
|
||||
Object dest = findService(name, type);
|
||||
Class clazz = dest.getClass();
|
||||
Throwable t = null;
|
||||
try {
|
||||
Field fieldObj = null;
|
||||
do {
|
||||
try {
|
||||
fieldObj = clazz.getDeclaredField(field);
|
||||
break;
|
||||
} catch (Exception e) {
|
||||
if (t == null) t = e;
|
||||
}
|
||||
} while ((clazz = clazz.getSuperclass()) != Object.class);
|
||||
if (fieldObj == null) return new RetResult(RET_WATCH_RUN_EXCEPTION, "run exception (" + String.valueOf(t) + ")");
|
||||
fieldObj.setAccessible(true);
|
||||
return new RetResult(fieldObj.get(dest));
|
||||
} catch (Throwable t2) {
|
||||
return new RetResult(RET_WATCH_RUN_EXCEPTION, "run exception (" + t2.toString() + ")");
|
||||
}
|
||||
}
|
||||
|
||||
@RestConvert(type = void.class)
|
||||
@RestMapping(name = "runmethod", auth = false, comment = "调用Service中指定方法")
|
||||
public RetResult runmethod(@RestParam(name = "name", comment = "Service的资源名") String name,
|
||||
@RestParam(name = "type", comment = "Service的类名") String type,
|
||||
@RestParam(name = "method", comment = "Service的方法名") String method,
|
||||
@RestParam(name = "params", comment = "方法的参数值") List<String> params,
|
||||
@RestParam(name = "paramtypes", comment = "方法的参数数据类型") List<String> paramtypes) {
|
||||
if (name == null) name = "";
|
||||
if (type == null) type = "";
|
||||
if (method == null) method = "";
|
||||
type = type.trim();
|
||||
method = method.trim();
|
||||
if (type.isEmpty()) return new RetResult(RET_WATCH_PARAMS_ILLEGAL, "not found param `type`");
|
||||
if (method.isEmpty()) return new RetResult(RET_WATCH_PARAMS_ILLEGAL, "not found param `method`");
|
||||
Object dest = findService(name, type);
|
||||
Class clazz = dest.getClass();
|
||||
Throwable t = null;
|
||||
final int paramcount = params == null ? 0 : params.size();
|
||||
if (paramtypes != null && paramcount != paramtypes.size()) return new RetResult(RET_WATCH_PARAMS_ILLEGAL, "params.size not equals to paramtypes.size");
|
||||
try {
|
||||
Method methodObj = null;
|
||||
do {
|
||||
try {
|
||||
for (Method m : clazz.getDeclaredMethods()) {
|
||||
if (m.getName().equals(method) && m.getParameterCount() == paramcount) {
|
||||
boolean flag = true;
|
||||
if (paramtypes != null) {
|
||||
Class[] pts = m.getParameterTypes();
|
||||
for (int i = 0; i < pts.length; i++) {
|
||||
if (!pts[i].getName().endsWith(paramtypes.get(i))) {
|
||||
flag = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (flag) {
|
||||
methodObj = m;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (methodObj != null) break;
|
||||
} catch (Exception e) {
|
||||
if (t == null) t = e;
|
||||
}
|
||||
} while ((clazz = clazz.getSuperclass()) != Object.class);
|
||||
if (methodObj == null) return new RetResult(RET_WATCH_RUN_EXCEPTION, "run exception (" + (t == null ? ("not found method(" + method + ")") : String.valueOf(t)) + ")");
|
||||
methodObj.setAccessible(true);
|
||||
if (paramcount < 1) return new RetResult(methodObj.invoke(dest));
|
||||
Object[] paramObjs = new Object[paramcount];
|
||||
Type[] pts = methodObj.getGenericParameterTypes();
|
||||
for (int i = 0; i < paramObjs.length; i++) {
|
||||
paramObjs[i] = JsonConvert.root().convertFrom(pts[i], params.get(i));
|
||||
}
|
||||
return new RetResult(methodObj.invoke(dest, paramObjs));
|
||||
} catch (Throwable t2) {
|
||||
return new RetResult(RET_WATCH_RUN_EXCEPTION, "run exception (" + t2.toString() + ")");
|
||||
}
|
||||
}
|
||||
|
||||
protected Object findService(String name, String type) {
|
||||
Object dest = null;
|
||||
for (NodeServer ns : application.getNodeServers()) {
|
||||
ResourceFactory resFactory = ns.getResourceFactory();
|
||||
List list = resFactory.query((n, s) -> name.equals(n) && s != null && s.getClass().getName().endsWith(type));
|
||||
if (list == null || list.isEmpty()) continue;
|
||||
dest = list.get(0);
|
||||
}
|
||||
if (dest == null) return new RetResult(RET_SERVICE_DEST_NOT_EXISTS, "not found servie (name=" + name + ", type=" + type + ")");
|
||||
return dest;
|
||||
}
|
||||
|
||||
@RestMapping(name = "load", auth = false, comment = "动态增加Service")
|
||||
public RetResult loadService(String type, @RestUploadFile(maxLength = 10 * 1024 * 1024, fileNameReg = "\\.jar$") byte[] jar) {
|
||||
//待开发
|
||||
return RetResult.success();
|
||||
}
|
||||
|
||||
@RestMapping(name = "reload", auth = false, comment = "重新加载Service")
|
||||
public RetResult reloadService(String name, String type) {
|
||||
//待开发
|
||||
return RetResult.success();
|
||||
}
|
||||
|
||||
@RestMapping(name = "stop", auth = false, comment = "动态停止Service")
|
||||
public RetResult stopService(String name, String type) {
|
||||
//待开发
|
||||
return RetResult.success();
|
||||
}
|
||||
|
||||
@RestMapping(name = "find", auth = false, comment = "查找Service")
|
||||
public RetResult find(String name, String type) {
|
||||
//待开发
|
||||
return RetResult.success();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,10 +20,10 @@ import org.redkale.net.http.*;
|
||||
public class ServletWatchService extends AbstractWatchService {
|
||||
|
||||
@Resource
|
||||
private Application application;
|
||||
protected Application application;
|
||||
|
||||
@Resource
|
||||
private TransportFactory transportFactory;
|
||||
protected TransportFactory transportFactory;
|
||||
//
|
||||
// @RestMapping(name = "load", auth = false, comment = "动态增加Servlet")
|
||||
// public RetResult loadServlet(String type, @RestUploadFile(maxLength = 10 * 1024 * 1024, fileNameReg = "\\.jar$") byte[] jar) {
|
||||
|
||||
@@ -5,10 +5,15 @@
|
||||
*/
|
||||
package org.redkale.boot.watch;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Properties;
|
||||
import javax.annotation.Resource;
|
||||
import org.redkale.boot.Application;
|
||||
import org.redkale.net.TransportFactory;
|
||||
import org.redkale.net.http.RestService;
|
||||
import org.redkale.net.http.*;
|
||||
import org.redkale.service.*;
|
||||
import org.redkale.source.*;
|
||||
import org.redkale.util.*;
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -17,10 +22,58 @@ import org.redkale.net.http.RestService;
|
||||
@RestService(name = "source", catalog = "watch", repair = false)
|
||||
public class SourceWatchService extends AbstractWatchService {
|
||||
|
||||
@Resource
|
||||
private Application application;
|
||||
@Comment("不存在的Source")
|
||||
public static final int RET_SOURCE_NOT_EXISTS = 1605_0001;
|
||||
|
||||
@Comment("Source不支持getReadPoolSource/getWritePoolSource方法")
|
||||
public static final int RET_SOURCE_CHANGE_METHOD_NOT_EXISTS = 1605_0002;
|
||||
|
||||
@Comment("PoolSource调用change方法失败")
|
||||
public static final int RET_SOURCE_METHOD_INVOKE_NOT_EXISTS = 1605_0003;
|
||||
|
||||
@Resource
|
||||
private TransportFactory transportFactory;
|
||||
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 {
|
||||
if (name == null) return new RetResult(RET_WATCH_PARAMS_ILLEGAL, "not found param (name)");
|
||||
if (properties == null) return new RetResult(RET_WATCH_PARAMS_ILLEGAL, "not found param (properties)");
|
||||
DataSource source = null;
|
||||
for (DataSource s : application.getDataSources()) {
|
||||
String resName = ((Resourcable) s).resourceName();
|
||||
if (resName == null) continue;
|
||||
if (!resName.equals(name)) continue;
|
||||
source = s;
|
||||
break;
|
||||
}
|
||||
if (source == null) return new RetResult(RET_SOURCE_NOT_EXISTS, "not found source (name = " + name + ")");
|
||||
Method readPoolMethod = null;
|
||||
Method writePoolMethod = null;
|
||||
Class stype = source.getClass();
|
||||
do {
|
||||
for (Method m : stype.getDeclaredMethods()) {
|
||||
if (!PoolSource.class.isAssignableFrom(m.getReturnType())) continue;
|
||||
if (m.getParameterCount() != 0) continue;
|
||||
if (m.getName().equals("getReadPoolSource")) {
|
||||
readPoolMethod = m;
|
||||
} else if (m.getName().equals("getWritePoolSource")) {
|
||||
writePoolMethod = m;
|
||||
}
|
||||
}
|
||||
} while ((stype = stype.getSuperclass()) != Object.class);
|
||||
if (readPoolMethod == null) return new RetResult(RET_SOURCE_CHANGE_METHOD_NOT_EXISTS, "not found source method(getReadPoolSource)");
|
||||
if (writePoolMethod == null) return new RetResult(RET_SOURCE_CHANGE_METHOD_NOT_EXISTS, "not found source method(getWritePoolSource)");
|
||||
readPoolMethod.setAccessible(true);
|
||||
writePoolMethod.setAccessible(true);
|
||||
try {
|
||||
PoolSource readPoolSource = (PoolSource) readPoolMethod.invoke(source);
|
||||
PoolSource writePoolSource = (PoolSource) writePoolMethod.invoke(source);
|
||||
readPoolSource.change(properties);
|
||||
writePoolSource.change(properties);
|
||||
return RetResult.success();
|
||||
} catch (Exception e) {
|
||||
return new RetResult(RET_SOURCE_METHOD_INVOKE_NOT_EXISTS, "poolsource invoke method('change') error");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,10 +36,10 @@ public class TransportWatchService extends AbstractWatchService {
|
||||
public static final int RET_TRANSPORT_ADDR_EXISTS = 1606_0003;
|
||||
|
||||
@Resource
|
||||
private Application application;
|
||||
protected Application application;
|
||||
|
||||
@Resource
|
||||
private TransportFactory transportFactory;
|
||||
protected TransportFactory transportFactory;
|
||||
|
||||
@RestMapping(name = "listnodes", auth = false, comment = "获取所有Node节点")
|
||||
public List<TransportGroupInfo> listNodes() {
|
||||
@@ -135,4 +135,24 @@ public class TransportWatchService extends AbstractWatchService {
|
||||
}
|
||||
return RetResult.success();
|
||||
}
|
||||
|
||||
@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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,16 +42,17 @@ public final class AnyEncoder<T> implements Encodeable<Writer, T> {
|
||||
out.writeNull();
|
||||
} else {
|
||||
int count = values.length - values.length % 2;
|
||||
out.writeMapB(count / 2);
|
||||
for (int i = 0; i < count; i += 2) {
|
||||
if (i > 0) out.writeArrayMark();
|
||||
this.convertTo(out, (T) values[i]);
|
||||
out.writeMapMark();
|
||||
Object val = values[i + 1];
|
||||
if (val instanceof CompletableFuture) {
|
||||
this.convertTo(out, (T) ((CompletableFuture) val).join());
|
||||
} else {
|
||||
this.convertTo(out, (T) val);
|
||||
if (out.writeMapB(count / 2, (Encodeable) this, (Encodeable) this, values) < 0) {
|
||||
for (int i = 0; i < count; i += 2) {
|
||||
if (i > 0) out.writeArrayMark();
|
||||
this.convertTo(out, (T) values[i]);
|
||||
out.writeMapMark();
|
||||
Object val = values[i + 1];
|
||||
if (val instanceof CompletableFuture) {
|
||||
this.convertTo(out, (T) ((CompletableFuture) val).join());
|
||||
} else {
|
||||
this.convertTo(out, (T) val);
|
||||
}
|
||||
}
|
||||
}
|
||||
out.writeMapE();
|
||||
|
||||
@@ -20,19 +20,19 @@ import java.util.*;
|
||||
* @param <T> 反解析的数组元素类型
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public final class ArrayDecoder<T> implements Decodeable<Reader, T[]> {
|
||||
public class ArrayDecoder<T> implements Decodeable<Reader, T[]> {
|
||||
|
||||
private final Type type;
|
||||
protected final Type type;
|
||||
|
||||
private final Type componentType;
|
||||
protected final Type componentType;
|
||||
|
||||
private final Class componentClass;
|
||||
protected final Class componentClass;
|
||||
|
||||
protected final Decodeable<Reader, T> decoder;
|
||||
|
||||
private boolean inited = false;
|
||||
protected boolean inited = false;
|
||||
|
||||
private final Object lock = new Object();
|
||||
protected final Object lock = new Object();
|
||||
|
||||
public ArrayDecoder(final ConvertFactory factory, final Type type) {
|
||||
this.type = type;
|
||||
@@ -62,8 +62,17 @@ public final class ArrayDecoder<T> implements Decodeable<Reader, T[]> {
|
||||
|
||||
@Override
|
||||
public T[] convertFrom(Reader in) {
|
||||
final int len = in.readArrayB();
|
||||
return convertFrom(in, null);
|
||||
}
|
||||
|
||||
public T[] convertFrom(Reader in, DeMember member) {
|
||||
int len = in.readArrayB(member, decoder);
|
||||
int contentLength = -1;
|
||||
if (len == Reader.SIGN_NULL) return null;
|
||||
if (len == Reader.SIGN_NOLENBUTBYTES) {
|
||||
contentLength = in.readMemberContentLength(member, decoder);
|
||||
len = Reader.SIGN_NOLENGTH;
|
||||
}
|
||||
if (this.decoder == null) {
|
||||
if (!this.inited) {
|
||||
synchronized (lock) {
|
||||
@@ -77,9 +86,14 @@ public final class ArrayDecoder<T> implements Decodeable<Reader, T[]> {
|
||||
}
|
||||
final Decodeable<Reader, T> localdecoder = this.decoder;
|
||||
final List<T> result = new ArrayList();
|
||||
boolean first = true;
|
||||
if (len == Reader.SIGN_NOLENGTH) {
|
||||
while (in.hasNext()) {
|
||||
result.add(localdecoder.convertFrom(in));
|
||||
int startPosition = in.position();
|
||||
while (hasNext(in, member, startPosition, contentLength, first)) {
|
||||
Reader itemReader = getItemReader(in, member, first);
|
||||
if (itemReader == null) break;
|
||||
result.add(readMemberValue(itemReader, member, first));
|
||||
first = false;
|
||||
}
|
||||
} else {
|
||||
for (int i = 0; i < len; i++) {
|
||||
@@ -91,6 +105,18 @@ public final class ArrayDecoder<T> implements Decodeable<Reader, T[]> {
|
||||
return result.toArray(rs);
|
||||
}
|
||||
|
||||
protected boolean hasNext(Reader in, DeMember member, int startPosition, int contentLength, boolean first) {
|
||||
return in.hasNext(startPosition, contentLength);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.getClass().getSimpleName() + "{componentType:" + this.componentType + ", decoder:" + this.decoder + "}";
|
||||
@@ -101,4 +127,12 @@ public final class ArrayDecoder<T> implements Decodeable<Reader, T[]> {
|
||||
return type;
|
||||
}
|
||||
|
||||
public Type getComponentType() {
|
||||
return componentType;
|
||||
}
|
||||
|
||||
public Decodeable<Reader, T> getDecoder() {
|
||||
return decoder;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -19,19 +19,19 @@ import java.lang.reflect.*;
|
||||
* @param <T> 序列化的数组元素类型
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public final class ArrayEncoder<T> implements Encodeable<Writer, T[]> {
|
||||
public class ArrayEncoder<T> implements Encodeable<Writer, T[]> {
|
||||
|
||||
private final Type type;
|
||||
protected final Type type;
|
||||
|
||||
private final Type componentType;
|
||||
protected final Type componentType;
|
||||
|
||||
private final Encodeable anyEncoder;
|
||||
protected final Encodeable anyEncoder;
|
||||
|
||||
private final Encodeable<Writer, Object> encoder;
|
||||
protected final Encodeable<Writer, Object> encoder;
|
||||
|
||||
private boolean inited = false;
|
||||
protected boolean inited = false;
|
||||
|
||||
private final Object lock = new Object();
|
||||
protected final Object lock = new Object();
|
||||
|
||||
public ArrayEncoder(final ConvertFactory factory, final Type type) {
|
||||
this.type = type;
|
||||
@@ -57,12 +57,16 @@ public final class ArrayEncoder<T> implements Encodeable<Writer, T[]> {
|
||||
|
||||
@Override
|
||||
public void convertTo(Writer out, T[] value) {
|
||||
convertTo(out, null, value);
|
||||
}
|
||||
|
||||
public void convertTo(Writer out, EnMember member, T[] value) {
|
||||
if (value == null) {
|
||||
out.writeNull();
|
||||
return;
|
||||
}
|
||||
if (value.length == 0) {
|
||||
out.writeArrayB(0);
|
||||
out.writeArrayB(0, encoder, value);
|
||||
out.writeArrayE();
|
||||
return;
|
||||
}
|
||||
@@ -77,17 +81,22 @@ public final class ArrayEncoder<T> implements Encodeable<Writer, T[]> {
|
||||
}
|
||||
}
|
||||
}
|
||||
out.writeArrayB(value.length);
|
||||
final Type comp = this.componentType;
|
||||
boolean first = true;
|
||||
for (Object v : value) {
|
||||
if (!first) out.writeArrayMark();
|
||||
((v != null && v.getClass() == comp) ? encoder : anyEncoder).convertTo(out, v);
|
||||
if (first) first = false;
|
||||
if (out.writeArrayB(value.length, encoder, 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);
|
||||
if (first) first = false;
|
||||
}
|
||||
}
|
||||
out.writeArrayE();
|
||||
}
|
||||
|
||||
protected void writeMemberValue(Writer out, EnMember member, Encodeable<Writer, Object> encoder, Object value, boolean first) {
|
||||
encoder.convertTo(out, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.getClass().getSimpleName() + "{componentType:" + this.componentType + ", encoder:" + this.encoder + "}";
|
||||
@@ -97,4 +106,13 @@ public final class ArrayEncoder<T> implements Encodeable<Writer, T[]> {
|
||||
public Type getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public Type getComponentType() {
|
||||
return componentType;
|
||||
}
|
||||
|
||||
public Encodeable<Writer, Object> getEncoder() {
|
||||
return encoder;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -21,19 +21,19 @@ import java.util.Collection;
|
||||
* @param <T> 反解析的集合元素类型
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public final class CollectionDecoder<T> implements Decodeable<Reader, Collection<T>> {
|
||||
public class CollectionDecoder<T> implements Decodeable<Reader, Collection<T>> {
|
||||
|
||||
private final Type type;
|
||||
protected final Type type;
|
||||
|
||||
private final Type componentType;
|
||||
protected final Type componentType;
|
||||
|
||||
protected Creator<Collection<T>> creator;
|
||||
|
||||
protected final Decodeable<Reader, T> decoder;
|
||||
|
||||
private boolean inited = false;
|
||||
protected boolean inited = false;
|
||||
|
||||
private final Object lock = new Object();
|
||||
protected final Object lock = new Object();
|
||||
|
||||
public CollectionDecoder(final ConvertFactory factory, final Type type) {
|
||||
this.type = type;
|
||||
@@ -44,9 +44,9 @@ public final class CollectionDecoder<T> implements Decodeable<Reader, Collection
|
||||
this.creator = factory.loadCreator((Class) pt.getRawType());
|
||||
factory.register(type, this);
|
||||
this.decoder = factory.loadDecoder(this.componentType);
|
||||
} else if(factory.isReversible()){
|
||||
} 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);
|
||||
} else {
|
||||
@@ -62,8 +62,17 @@ public final class CollectionDecoder<T> implements Decodeable<Reader, Collection
|
||||
|
||||
@Override
|
||||
public Collection<T> convertFrom(Reader in) {
|
||||
final int len = in.readArrayB();
|
||||
return convertFrom(in, null);
|
||||
}
|
||||
|
||||
public Collection<T> convertFrom(Reader in, DeMember member) {
|
||||
int len = in.readArrayB(member, decoder);
|
||||
int contentLength = -1;
|
||||
if (len == Reader.SIGN_NULL) return null;
|
||||
if (len == Reader.SIGN_NOLENBUTBYTES) {
|
||||
contentLength = in.readMemberContentLength(member, decoder);
|
||||
len = Reader.SIGN_NOLENGTH;
|
||||
}
|
||||
if (this.decoder == null) {
|
||||
if (!this.inited) {
|
||||
synchronized (lock) {
|
||||
@@ -77,9 +86,14 @@ public final class CollectionDecoder<T> implements Decodeable<Reader, Collection
|
||||
}
|
||||
final Decodeable<Reader, T> localdecoder = this.decoder;
|
||||
final Collection<T> result = this.creator.create();
|
||||
boolean first = true;
|
||||
if (len == Reader.SIGN_NOLENGTH) {
|
||||
while (in.hasNext()) {
|
||||
result.add(localdecoder.convertFrom(in));
|
||||
int startPosition = in.position();
|
||||
while (hasNext(in, member, startPosition, contentLength, first)) {
|
||||
Reader itemReader = getItemReader(in, member, first);
|
||||
if (itemReader == null) break;
|
||||
result.add(readMemberValue(itemReader, member, first));
|
||||
first = false;
|
||||
}
|
||||
} else {
|
||||
for (int i = 0; i < len; i++) {
|
||||
@@ -90,9 +104,29 @@ public final class CollectionDecoder<T> implements Decodeable<Reader, Collection
|
||||
return result;
|
||||
}
|
||||
|
||||
protected boolean hasNext(Reader in, DeMember member, int startPosition, int contentLength, boolean first) {
|
||||
return in.hasNext(startPosition, contentLength);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public Type getComponentType() {
|
||||
return componentType;
|
||||
}
|
||||
|
||||
public Decodeable<Reader, T> getDecoder() {
|
||||
return decoder;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -19,15 +19,15 @@ import java.util.Collection;
|
||||
* @param <T> 序列化的集合元素类型
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public final class CollectionEncoder<T> implements Encodeable<Writer, Collection<T>> {
|
||||
public class CollectionEncoder<T> implements Encodeable<Writer, Collection<T>> {
|
||||
|
||||
private final Type type;
|
||||
protected final Type type;
|
||||
|
||||
private final Encodeable<Writer, Object> encoder;
|
||||
protected final Encodeable<Writer, Object> encoder;
|
||||
|
||||
private boolean inited = false;
|
||||
protected boolean inited = false;
|
||||
|
||||
private final Object lock = new Object();
|
||||
protected final Object lock = new Object();
|
||||
|
||||
public CollectionEncoder(final ConvertFactory factory, final Type type) {
|
||||
this.type = type;
|
||||
@@ -52,12 +52,16 @@ public final class CollectionEncoder<T> implements Encodeable<Writer, Collection
|
||||
|
||||
@Override
|
||||
public void convertTo(Writer out, Collection<T> value) {
|
||||
convertTo(out, null, value);
|
||||
}
|
||||
|
||||
public void convertTo(Writer out, EnMember member, Collection<T> value) {
|
||||
if (value == null) {
|
||||
out.writeNull();
|
||||
return;
|
||||
}
|
||||
if (value.isEmpty()) {
|
||||
out.writeArrayB(0);
|
||||
out.writeArrayB(0, encoder, value);
|
||||
out.writeArrayE();
|
||||
return;
|
||||
}
|
||||
@@ -72,18 +76,28 @@ public final class CollectionEncoder<T> implements Encodeable<Writer, Collection
|
||||
}
|
||||
}
|
||||
}
|
||||
out.writeArrayB(value.size());
|
||||
boolean first = true;
|
||||
for (Object v : value) {
|
||||
if (!first) out.writeArrayMark();
|
||||
encoder.convertTo(out, v);
|
||||
if (first) first = false;
|
||||
if (out.writeArrayB(value.size(), encoder, value) < 0) {
|
||||
boolean first = true;
|
||||
for (Object v : value) {
|
||||
if (!first) out.writeArrayMark();
|
||||
writeValue(out, member, v);
|
||||
if (first) first = false;
|
||||
}
|
||||
}
|
||||
out.writeArrayE();
|
||||
}
|
||||
|
||||
protected void writeValue(Writer out, EnMember member, Object value) {
|
||||
encoder.convertTo(out, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public Encodeable<Writer, Object> getEncoder() {
|
||||
return encoder;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
@@ -139,10 +141,56 @@ public abstract class ConvertFactory<R extends Reader, W extends Writer> {
|
||||
|
||||
public abstract boolean isReversible(); //是否可逆的
|
||||
|
||||
public abstract boolean isFieldSort(); //当ConvertColumn.index相同时是否按字段名称排序
|
||||
|
||||
public abstract ConvertFactory createChild();
|
||||
|
||||
public abstract ConvertFactory createChild(boolean tiny);
|
||||
|
||||
protected SimpledCoder createEnumSimpledCoder(Class enumClass) {
|
||||
return new EnumSimpledCoder(enumClass);
|
||||
}
|
||||
|
||||
protected ObjectDecoder createObjectDecoder(Type type) {
|
||||
return new ObjectDecoder(type);
|
||||
}
|
||||
|
||||
protected ObjectEncoder createObjectEncoder(Type type) {
|
||||
return new ObjectEncoder(type);
|
||||
}
|
||||
|
||||
protected <E> Decodeable<R, E> createMapDecoder(Type type) {
|
||||
return new MapDecoder(this, type);
|
||||
}
|
||||
|
||||
protected <E> Encodeable<W, E> createMapEncoder(Type type) {
|
||||
return new MapEncoder(this, type);
|
||||
}
|
||||
|
||||
protected <E> Decodeable<R, E> createArrayDecoder(Type type) {
|
||||
return new ArrayDecoder(this, type);
|
||||
}
|
||||
|
||||
protected <E> Encodeable<W, E> createArrayEncoder(Type type) {
|
||||
return new ArrayEncoder(this, type);
|
||||
}
|
||||
|
||||
protected <E> Decodeable<R, E> createCollectionDecoder(Type type) {
|
||||
return new CollectionDecoder(this, type);
|
||||
}
|
||||
|
||||
protected <E> Encodeable<W, E> createCollectionEncoder(Type type) {
|
||||
return new CollectionEncoder(this, type);
|
||||
}
|
||||
|
||||
protected <E> Decodeable<R, E> createStreamDecoder(Type type) {
|
||||
return new StreamDecoder(this, type);
|
||||
}
|
||||
|
||||
protected <E> Encodeable<W, E> createStreamEncoder(Type type) {
|
||||
return new StreamEncoder(this, type);
|
||||
}
|
||||
|
||||
public Convert getConvert() {
|
||||
return convert;
|
||||
}
|
||||
@@ -449,6 +497,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;
|
||||
@@ -464,7 +535,7 @@ public abstract class ConvertFactory<R extends Reader, W extends Writer> {
|
||||
public final <E> Decodeable<R, E> loadDecoder(final Type type) {
|
||||
Decodeable<R, E> decoder = findDecoder(type);
|
||||
if (decoder != null) return decoder;
|
||||
if (type instanceof GenericArrayType) return new ArrayDecoder(this, type);
|
||||
if (type instanceof GenericArrayType) return createArrayDecoder(type);
|
||||
Class clazz;
|
||||
if (type instanceof ParameterizedType) {
|
||||
final ParameterizedType pts = (ParameterizedType) type;
|
||||
@@ -496,8 +567,7 @@ public abstract class ConvertFactory<R extends Reader, W extends Writer> {
|
||||
} else {
|
||||
throw new ConvertException("not support the type (" + type + ")");
|
||||
}
|
||||
decoder = findDecoder(clazz);
|
||||
if (decoder != null) return decoder;
|
||||
//此处不能再findDecoder,否则type与class不一致, 如: RetResult 和 RetResult<Integer>
|
||||
return createDecoder(type, clazz);
|
||||
}
|
||||
|
||||
@@ -518,19 +588,19 @@ public abstract class ConvertFactory<R extends Reader, W extends Writer> {
|
||||
Decodeable<R, E> decoder = null;
|
||||
ObjectDecoder od = null;
|
||||
if (clazz.isEnum()) {
|
||||
decoder = new EnumSimpledCoder(clazz);
|
||||
decoder = createEnumSimpledCoder(clazz);
|
||||
} else if (clazz.isArray()) {
|
||||
decoder = new ArrayDecoder(this, type);
|
||||
decoder = createArrayDecoder(type);
|
||||
} else if (Collection.class.isAssignableFrom(clazz)) {
|
||||
decoder = new CollectionDecoder(this, type);
|
||||
decoder = createCollectionDecoder(type);
|
||||
} else if (Stream.class.isAssignableFrom(clazz)) {
|
||||
decoder = new StreamDecoder(this, type);
|
||||
decoder = createStreamDecoder(type);
|
||||
} else if (Map.class.isAssignableFrom(clazz)) {
|
||||
decoder = new MapDecoder(this, type);
|
||||
decoder = createMapDecoder(type);
|
||||
} else if (Optional.class == clazz) {
|
||||
decoder = new OptionalCoder(this, type);
|
||||
} else if (clazz == Object.class) {
|
||||
od = new ObjectDecoder(type);
|
||||
od = createObjectDecoder(type);
|
||||
decoder = od;
|
||||
} else if (!clazz.getName().startsWith("java.")
|
||||
|| java.net.HttpCookie.class == clazz
|
||||
@@ -550,7 +620,7 @@ public abstract class ConvertFactory<R extends Reader, W extends Writer> {
|
||||
}
|
||||
}
|
||||
if (simpleCoder == null) {
|
||||
od = new ObjectDecoder(type);
|
||||
od = createObjectDecoder(type);
|
||||
decoder = od;
|
||||
} else {
|
||||
decoder = simpleCoder;
|
||||
@@ -565,7 +635,7 @@ public abstract class ConvertFactory<R extends Reader, W extends Writer> {
|
||||
public final <E> Encodeable<W, E> loadEncoder(final Type type) {
|
||||
Encodeable<W, E> encoder = findEncoder(type);
|
||||
if (encoder != null) return encoder;
|
||||
if (type instanceof GenericArrayType) return new ArrayEncoder(this, type);
|
||||
if (type instanceof GenericArrayType) return createArrayEncoder(type);
|
||||
Class clazz;
|
||||
if (type instanceof ParameterizedType) {
|
||||
final ParameterizedType pts = (ParameterizedType) type;
|
||||
@@ -583,8 +653,7 @@ public abstract class ConvertFactory<R extends Reader, W extends Writer> {
|
||||
} else {
|
||||
throw new ConvertException("not support the type (" + type + ")");
|
||||
}
|
||||
encoder = findEncoder(clazz);
|
||||
if (encoder != null) return encoder;
|
||||
//此处不能再findEncoder,否则type与class不一致, 如: RetResult 和 RetResult<Integer>
|
||||
return createEncoder(type, clazz);
|
||||
}
|
||||
|
||||
@@ -605,15 +674,15 @@ public abstract class ConvertFactory<R extends Reader, W extends Writer> {
|
||||
Encodeable<W, E> encoder = null;
|
||||
ObjectEncoder oe = null;
|
||||
if (clazz.isEnum()) {
|
||||
encoder = new EnumSimpledCoder(clazz);
|
||||
encoder = createEnumSimpledCoder(clazz);
|
||||
} else if (clazz.isArray()) {
|
||||
encoder = new ArrayEncoder(this, type);
|
||||
encoder = createArrayEncoder(type);
|
||||
} else if (Collection.class.isAssignableFrom(clazz)) {
|
||||
encoder = new CollectionEncoder(this, type);
|
||||
encoder = createCollectionEncoder(type);
|
||||
} else if (Stream.class.isAssignableFrom(clazz)) {
|
||||
encoder = new StreamEncoder(this, type);
|
||||
encoder = createStreamEncoder(type);
|
||||
} else if (Map.class.isAssignableFrom(clazz)) {
|
||||
encoder = new MapEncoder(this, type);
|
||||
encoder = createMapEncoder(type);
|
||||
} else if (Optional.class == clazz) {
|
||||
encoder = new OptionalCoder(this, type);
|
||||
} else if (clazz == Object.class) {
|
||||
@@ -634,7 +703,7 @@ public abstract class ConvertFactory<R extends Reader, W extends Writer> {
|
||||
}
|
||||
}
|
||||
if (simpleCoder == null) {
|
||||
oe = new ObjectEncoder(type);
|
||||
oe = createObjectEncoder(type);
|
||||
encoder = oe;
|
||||
} else {
|
||||
encoder = simpleCoder;
|
||||
|
||||
@@ -20,10 +20,12 @@ import org.redkale.util.Attribute;
|
||||
* @param <F> 字段的数据类型
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public final class DeMember<R extends Reader, T, F> implements Comparable<DeMember<R, T, F>> {
|
||||
public final class DeMember<R extends Reader, T, F> {
|
||||
|
||||
protected int index;
|
||||
|
||||
protected int position; //从1开始
|
||||
|
||||
protected final Attribute<T, F> attribute;
|
||||
|
||||
protected Decodeable<R, F> decoder;
|
||||
@@ -70,15 +72,23 @@ public final class DeMember<R extends Reader, T, F> implements Comparable<DeMemb
|
||||
return this.attribute;
|
||||
}
|
||||
|
||||
public Decodeable<R, F> getDecoder() {
|
||||
return decoder;
|
||||
}
|
||||
|
||||
public int getIndex() {
|
||||
return this.index;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final int compareTo(DeMember<R, T, F> o) {
|
||||
public int getPosition() {
|
||||
return this.position;
|
||||
}
|
||||
|
||||
public int compareTo(boolean fieldSort, DeMember<R, T, F> o) {
|
||||
if (o == null) return -1;
|
||||
if (this.index != o.index) return (this.index == 0 ? Integer.MAX_VALUE : this.index) - (o.index == 0 ? Integer.MAX_VALUE : o.index);
|
||||
return this.attribute.field().compareTo(o.attribute.field());
|
||||
if (this.index != 0) throw new RuntimeException("fields (" + attribute.field() + ", " + o.attribute.field() + ") have same ConvertColumn.index(" + this.index + ") in " + attribute.declaringClass());
|
||||
return fieldSort ? this.attribute.field().compareTo(o.attribute.field()) : 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -86,7 +96,7 @@ public final class DeMember<R extends Reader, T, F> implements Comparable<DeMemb
|
||||
if (this == obj) return true;
|
||||
if (!(obj instanceof DeMember)) return false;
|
||||
DeMember other = (DeMember) obj;
|
||||
return compareTo(other) == 0;
|
||||
return compareTo(true, other) == 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -20,7 +20,7 @@ import org.redkale.util.Attribute;
|
||||
* @param <F> 字段的数据类型
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public final class EnMember<W extends Writer, T, F> implements Comparable<EnMember<W, T, F>> {
|
||||
public final class EnMember<W extends Writer, T, F> {
|
||||
|
||||
final Attribute<T, F> attribute;
|
||||
|
||||
@@ -33,6 +33,8 @@ public final class EnMember<W extends Writer, T, F> implements Comparable<EnMemb
|
||||
|
||||
protected int index;
|
||||
|
||||
protected int position; //从1开始
|
||||
|
||||
public EnMember(Attribute<T, F> attribute, Encodeable<W, F> encoder) {
|
||||
this.attribute = attribute;
|
||||
this.encoder = encoder;
|
||||
@@ -63,15 +65,35 @@ public final class EnMember<W extends Writer, T, F> implements Comparable<EnMemb
|
||||
return attribute.field().equals(name);
|
||||
}
|
||||
|
||||
public Attribute<T, F> getAttribute() {
|
||||
return attribute;
|
||||
}
|
||||
|
||||
public Encodeable<W, F> getEncoder() {
|
||||
return encoder;
|
||||
}
|
||||
|
||||
public boolean isStringType() {
|
||||
return istring;
|
||||
}
|
||||
|
||||
public boolean isBoolType() {
|
||||
return isbool;
|
||||
}
|
||||
|
||||
public int getIndex() {
|
||||
return this.index;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final int compareTo(EnMember<W, T, F> o) {
|
||||
public int getPosition() {
|
||||
return this.position;
|
||||
}
|
||||
|
||||
public int compareTo(boolean fieldSort, EnMember<W, T, F> o) {
|
||||
if (o == null) return -1;
|
||||
if (this.index != o.index) return (this.index == 0 ? Integer.MAX_VALUE : this.index) - (o.index == 0 ? Integer.MAX_VALUE : o.index);
|
||||
return this.attribute.field().compareTo(o.attribute.field());
|
||||
if (this.index != 0) throw new RuntimeException("fields (" + attribute.field() + ", " + o.attribute.field() + ") have same ConvertColumn.index(" + this.index + ") in " + attribute.declaringClass());
|
||||
return fieldSort ? this.attribute.field().compareTo(o.attribute.field()) : 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -79,7 +101,7 @@ public final class EnMember<W extends Writer, T, F> implements Comparable<EnMemb
|
||||
if (this == obj) return true;
|
||||
if (!(obj instanceof EnMember)) return false;
|
||||
EnMember other = (EnMember) obj;
|
||||
return compareTo(other) == 0;
|
||||
return compareTo(true, other) == 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -21,13 +21,13 @@ import java.util.Map;
|
||||
* @param <V> Map value的数据类型
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public final class MapDecoder<K, V> implements Decodeable<Reader, Map<K, V>> {
|
||||
public class MapDecoder<K, V> implements Decodeable<Reader, Map<K, V>> {
|
||||
|
||||
private final Type type;
|
||||
protected final Type type;
|
||||
|
||||
private final Type keyType;
|
||||
protected final Type keyType;
|
||||
|
||||
private final Type valueType;
|
||||
protected final Type valueType;
|
||||
|
||||
protected Creator<Map<K, V>> creator;
|
||||
|
||||
@@ -35,14 +35,21 @@ public final class MapDecoder<K, V> implements Decodeable<Reader, Map<K, V>> {
|
||||
|
||||
protected final Decodeable<Reader, V> valueDecoder;
|
||||
|
||||
private boolean inited = false;
|
||||
protected boolean inited = false;
|
||||
|
||||
private final Object lock = new Object();
|
||||
protected final Object lock = new Object();
|
||||
|
||||
public MapDecoder(final ConvertFactory factory, final Type type) {
|
||||
this.type = type;
|
||||
try {
|
||||
if (type instanceof ParameterizedType) {
|
||||
if (type == java.util.Properties.class) {
|
||||
this.keyType = String.class;
|
||||
this.valueType = String.class;
|
||||
this.creator = factory.loadCreator(java.util.Properties.class);
|
||||
factory.register(type, this);
|
||||
this.keyDecoder = factory.loadDecoder(String.class);
|
||||
this.valueDecoder = factory.loadDecoder(String.class);
|
||||
} else if (type instanceof ParameterizedType) {
|
||||
final ParameterizedType pt = (ParameterizedType) type;
|
||||
this.keyType = pt.getActualTypeArguments()[0];
|
||||
this.valueType = pt.getActualTypeArguments()[1];
|
||||
@@ -69,6 +76,10 @@ public final class MapDecoder<K, V> implements Decodeable<Reader, Map<K, V>> {
|
||||
|
||||
@Override
|
||||
public Map<K, V> convertFrom(Reader in) {
|
||||
return convertFrom(in, null);
|
||||
}
|
||||
|
||||
public Map<K, V> convertFrom(Reader in, DeMember member) {
|
||||
if (this.keyDecoder == null || this.valueDecoder == null) {
|
||||
if (!this.inited) {
|
||||
synchronized (lock) {
|
||||
@@ -80,31 +91,74 @@ public final class MapDecoder<K, V> implements Decodeable<Reader, Map<K, V>> {
|
||||
}
|
||||
}
|
||||
}
|
||||
final int len = in.readMapB();
|
||||
int len = in.readMapB(member, this.keyDecoder);
|
||||
int contentLength = -1;
|
||||
if (len == Reader.SIGN_NULL) return null;
|
||||
if (len == Reader.SIGN_NOLENBUTBYTES) {
|
||||
contentLength = in.readMemberContentLength(member, null);
|
||||
len = Reader.SIGN_NOLENGTH;
|
||||
}
|
||||
final Map<K, V> result = this.creator.create();
|
||||
boolean first = true;
|
||||
if (len == Reader.SIGN_NOLENGTH) {
|
||||
while (in.hasNext()) {
|
||||
K key = keyDecoder.convertFrom(in);
|
||||
in.readBlank();
|
||||
V value = valueDecoder.convertFrom(in);
|
||||
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);
|
||||
entryReader.readBlank();
|
||||
V value = readValueMember(entryReader, member, first);
|
||||
result.put(key, value);
|
||||
first = false;
|
||||
}
|
||||
} else {
|
||||
for (int i = 0; i < len; i++) {
|
||||
K key = keyDecoder.convertFrom(in);
|
||||
K key = readKeyMember(in, member, first);
|
||||
in.readBlank();
|
||||
V value = valueDecoder.convertFrom(in);
|
||||
V value = readValueMember(in, member, first);
|
||||
result.put(key, value);
|
||||
first = false;
|
||||
}
|
||||
}
|
||||
in.readMapE();
|
||||
return result;
|
||||
}
|
||||
|
||||
protected boolean hasNext(Reader in, DeMember member, int startPosition, int contentLength, boolean first) {
|
||||
return in.hasNext(startPosition, contentLength);
|
||||
}
|
||||
|
||||
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 V readValueMember(Reader in, DeMember member, boolean first) {
|
||||
return valueDecoder.convertFrom(in);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type getType() {
|
||||
return this.type;
|
||||
}
|
||||
|
||||
public Type getKeyType() {
|
||||
return keyType;
|
||||
}
|
||||
|
||||
public Type getValueType() {
|
||||
return valueType;
|
||||
}
|
||||
|
||||
public Decodeable<Reader, K> getKeyDecoder() {
|
||||
return keyDecoder;
|
||||
}
|
||||
|
||||
public Decodeable<Reader, V> getValueDecoder() {
|
||||
return valueDecoder;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -20,17 +20,17 @@ import java.util.Map;
|
||||
* @param <V> Map value的数据类型
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public final class MapEncoder<K, V> implements Encodeable<Writer, Map<K, V>> {
|
||||
public class MapEncoder<K, V> implements Encodeable<Writer, Map<K, V>> {
|
||||
|
||||
private final Type type;
|
||||
protected final Type type;
|
||||
|
||||
private final Encodeable<Writer, K> keyencoder;
|
||||
protected final Encodeable<Writer, K> keyencoder;
|
||||
|
||||
private final Encodeable<Writer, V> valencoder;
|
||||
protected final Encodeable<Writer, V> valencoder;
|
||||
|
||||
private boolean inited = false;
|
||||
protected boolean inited = false;
|
||||
|
||||
private final Object lock = new Object();
|
||||
protected final Object lock = new Object();
|
||||
|
||||
public MapEncoder(final ConvertFactory factory, final Type type) {
|
||||
this.type = type;
|
||||
@@ -53,6 +53,10 @@ public final class MapEncoder<K, V> implements Encodeable<Writer, Map<K, V>> {
|
||||
|
||||
@Override
|
||||
public void convertTo(Writer out, Map<K, V> value) {
|
||||
convertTo(out, null, value);
|
||||
}
|
||||
|
||||
public void convertTo(Writer out, EnMember member, Map<K, V> value) {
|
||||
final Map<K, V> values = value;
|
||||
if (values == null) {
|
||||
out.writeNull();
|
||||
@@ -70,20 +74,34 @@ public final class MapEncoder<K, V> implements Encodeable<Writer, Map<K, V>> {
|
||||
}
|
||||
}
|
||||
}
|
||||
out.writeMapB(values.size());
|
||||
boolean first = true;
|
||||
for (Map.Entry<K, V> en : values.entrySet()) {
|
||||
if (!first) out.writeArrayMark();
|
||||
this.keyencoder.convertTo(out, en.getKey());
|
||||
out.writeMapMark();
|
||||
this.valencoder.convertTo(out, en.getValue());
|
||||
if (first) first = false;
|
||||
if (out.writeMapB(values.size(), (Encodeable) keyencoder, (Encodeable) valencoder, 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);
|
||||
if (first) first = false;
|
||||
}
|
||||
}
|
||||
out.writeMapE();
|
||||
}
|
||||
|
||||
protected void writeMemberValue(Writer out, EnMember member, K key, V value,boolean first) {
|
||||
keyencoder.convertTo(out, key);
|
||||
out.writeMapMark();
|
||||
valencoder.convertTo(out, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public Encodeable<Writer, K> getKeyencoder() {
|
||||
return keyencoder;
|
||||
}
|
||||
|
||||
public Encodeable<Writer, V> getValencoder() {
|
||||
return valencoder;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -7,9 +7,7 @@ package org.redkale.convert;
|
||||
|
||||
import org.redkale.util.Creator;
|
||||
import java.lang.reflect.*;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.*;
|
||||
import org.redkale.util.*;
|
||||
|
||||
/**
|
||||
@@ -23,7 +21,7 @@ import org.redkale.util.*;
|
||||
* @param <T> 反解析的数据类型
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public final class ObjectDecoder<R extends Reader, T> implements Decodeable<R, T> {
|
||||
public class ObjectDecoder<R extends Reader, T> implements Decodeable<R, T> {
|
||||
|
||||
protected final Type type;
|
||||
|
||||
@@ -33,13 +31,13 @@ public final class ObjectDecoder<R extends Reader, T> implements Decodeable<R, T
|
||||
|
||||
protected DeMember<R, T, ?>[] creatorConstructorMembers = new DeMember[0];
|
||||
|
||||
protected DeMember<R, T, ?>[] members;
|
||||
protected DeMember[] members;
|
||||
|
||||
protected ConvertFactory factory;
|
||||
|
||||
private boolean inited = false;
|
||||
protected boolean inited = false;
|
||||
|
||||
private final Object lock = new Object();
|
||||
protected final Object lock = new Object();
|
||||
|
||||
protected ObjectDecoder(Type type) {
|
||||
this.type = ((type instanceof Class) && ((Class) type).isInterface()) ? Object.class : type;
|
||||
@@ -86,7 +84,7 @@ public final class ObjectDecoder<R extends Reader, T> implements Decodeable<R, T
|
||||
this.creator = factory.loadCreator(clazz);
|
||||
if (this.creator == null) throw new ConvertException("Cannot create a creator for " + clazz);
|
||||
}
|
||||
final Set<DeMember> list = new HashSet();
|
||||
final Set<DeMember> list = new LinkedHashSet();
|
||||
final String[] cps = ObjectEncoder.findConstructorProperties(this.creator);
|
||||
try {
|
||||
ConvertColumnEntry ref;
|
||||
@@ -95,8 +93,12 @@ public final class ObjectDecoder<R extends Reader, T> implements Decodeable<R, T
|
||||
if (factory.isConvertDisabled(field)) continue;
|
||||
ref = factory.findRef(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);
|
||||
}
|
||||
@@ -120,8 +122,13 @@ public final class ObjectDecoder<R extends Reader, T> implements Decodeable<R, T
|
||||
}
|
||||
ref = factory.findRef(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);
|
||||
}
|
||||
@@ -156,7 +163,20 @@ public final class ObjectDecoder<R extends Reader, T> implements Decodeable<R, T
|
||||
}
|
||||
}
|
||||
this.members = list.toArray(new DeMember[list.size()]);
|
||||
Arrays.sort(this.members);
|
||||
Arrays.sort(this.members, (a, b) -> a.compareTo(factory.isFieldSort(), b));
|
||||
Set<Integer> pos = new HashSet<>();
|
||||
for (int i = 0; i < this.members.length; i++) {
|
||||
if (this.members[i].index > 0) pos.add(this.members[i].index);
|
||||
}
|
||||
int pidx = 0;
|
||||
for (DeMember member : this.members) {
|
||||
if (member.index > 0) {
|
||||
member.position = member.index;
|
||||
} else {
|
||||
while (pos.contains(++pidx));
|
||||
member.position = pidx;
|
||||
}
|
||||
}
|
||||
|
||||
if (cps != null) {
|
||||
final String[] fields = cps;
|
||||
@@ -190,7 +210,7 @@ public final class ObjectDecoder<R extends Reader, T> implements Decodeable<R, T
|
||||
* @return 反解析后的对象结果
|
||||
*/
|
||||
@Override
|
||||
public final T convertFrom(final R in) {
|
||||
public T convertFrom(final R in) {
|
||||
final String clazz = in.readObjectB(typeClass);
|
||||
if (clazz == null) return null;
|
||||
if (!clazz.isEmpty()) return (T) factory.loadDecoder(factory.getEntityAlias(clazz)).convertFrom(in);
|
||||
@@ -210,14 +230,16 @@ public final class ObjectDecoder<R extends Reader, T> implements Decodeable<R, T
|
||||
}
|
||||
if (this.creatorConstructorMembers == null) { //空构造函数
|
||||
final T result = this.creator.create();
|
||||
while (in.hasNext()) {
|
||||
boolean first = true;
|
||||
while (hasNext(in, first)) {
|
||||
DeMember member = in.readFieldName(members);
|
||||
in.readBlank();
|
||||
if (member == null) {
|
||||
in.skipValue(); //跳过不存在的属性的值
|
||||
} else {
|
||||
member.read(in, result);
|
||||
readMemberValue(in, member, result, first);
|
||||
}
|
||||
first = false;
|
||||
}
|
||||
in.readObjectE(typeClass);
|
||||
return result;
|
||||
@@ -226,13 +248,14 @@ public final class ObjectDecoder<R extends Reader, T> implements Decodeable<R, T
|
||||
final Object[] constructorParams = new Object[fields.length];
|
||||
final Object[][] otherParams = new Object[this.members.length][2];
|
||||
int oc = 0;
|
||||
while (in.hasNext()) {
|
||||
boolean first = true;
|
||||
while (hasNext(in, first)) {
|
||||
DeMember member = in.readFieldName(members);
|
||||
in.readBlank();
|
||||
if (member == null) {
|
||||
in.skipValue(); //跳过不存在的属性的值
|
||||
} else {
|
||||
Object val = member.read(in);
|
||||
Object val = readMemberValue(in, member, first);
|
||||
boolean flag = true;
|
||||
for (int i = 0; i < fields.length; i++) {
|
||||
if (member == fields[i]) {
|
||||
@@ -242,7 +265,9 @@ public final class ObjectDecoder<R extends Reader, T> implements Decodeable<R, T
|
||||
}
|
||||
}
|
||||
if (flag) otherParams[oc++] = new Object[]{member.attribute, val};
|
||||
|
||||
}
|
||||
first = false;
|
||||
}
|
||||
in.readObjectE(typeClass);
|
||||
final T result = this.creator.create(constructorParams);
|
||||
@@ -253,11 +278,27 @@ public final class ObjectDecoder<R extends Reader, T> implements Decodeable<R, T
|
||||
}
|
||||
}
|
||||
|
||||
protected boolean hasNext(R in, boolean first) {
|
||||
return in.hasNext();
|
||||
}
|
||||
|
||||
protected Object readMemberValue(R in, DeMember member, boolean first) {
|
||||
return member.read(in);
|
||||
}
|
||||
|
||||
protected void readMemberValue(R in, DeMember member, T result, boolean first) {
|
||||
member.read(in, result);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Type getType() {
|
||||
public Type getType() {
|
||||
return this.type;
|
||||
}
|
||||
|
||||
public DeMember[] getMembers() {
|
||||
return Arrays.copyOf(members, members.length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ObjectDecoder{" + "type=" + type + ", members=" + Arrays.toString(members) + '}';
|
||||
|
||||
@@ -20,7 +20,7 @@ import org.redkale.util.*;
|
||||
* @param <T> 序列化的数据类型
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public final class ObjectEncoder<W extends Writer, T> implements Encodeable<W, T> {
|
||||
public class ObjectEncoder<W extends Writer, T> implements Encodeable<W, T> {
|
||||
|
||||
static final Type[] TYPEZERO = new Type[0];
|
||||
|
||||
@@ -32,9 +32,9 @@ public final class ObjectEncoder<W extends Writer, T> implements Encodeable<W, T
|
||||
|
||||
protected ConvertFactory factory;
|
||||
|
||||
private boolean inited = false;
|
||||
protected boolean inited = false;
|
||||
|
||||
private final Object lock = new Object();
|
||||
protected final Object lock = new Object();
|
||||
|
||||
protected ObjectEncoder(Type type) {
|
||||
this.type = type;
|
||||
@@ -61,7 +61,7 @@ public final class ObjectEncoder<W extends Writer, T> implements Encodeable<W, T
|
||||
if (type == Object.class) return;
|
||||
//if (!(type instanceof Class)) throw new ConvertException("[" + type + "] is no a class");
|
||||
final Class clazz = this.typeClass;
|
||||
final Set<EnMember> list = new HashSet();
|
||||
final Set<EnMember> list = new LinkedHashSet();
|
||||
final boolean reversible = factory.isReversible();
|
||||
Creator creator = null;
|
||||
try {
|
||||
@@ -77,8 +77,12 @@ public final class ObjectEncoder<W extends Writer, T> implements Encodeable<W, T
|
||||
if (factory.isConvertDisabled(field)) continue;
|
||||
ref = factory.findRef(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);
|
||||
}
|
||||
@@ -102,13 +106,30 @@ public final class ObjectEncoder<W extends Writer, T> implements Encodeable<W, T
|
||||
}
|
||||
ref = factory.findRef(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);
|
||||
}
|
||||
this.members = list.toArray(new EnMember[list.size()]);
|
||||
Arrays.sort(this.members);
|
||||
Arrays.sort(this.members, (a, b) -> a.compareTo(factory.isFieldSort(), b));
|
||||
Set<Integer> pos = new HashSet<>();
|
||||
for (int i = 0; i < this.members.length; i++) {
|
||||
if (this.members[i].index > 0) pos.add(this.members[i].index);
|
||||
}
|
||||
int pidx = 0;
|
||||
for (EnMember member : this.members) {
|
||||
if (member.index > 0) {
|
||||
member.position = member.index;
|
||||
} else {
|
||||
while (pos.contains(++pidx));
|
||||
member.position = pidx;
|
||||
}
|
||||
}
|
||||
|
||||
} catch (Exception ex) {
|
||||
throw new ConvertException(ex);
|
||||
@@ -122,7 +143,7 @@ public final class ObjectEncoder<W extends Writer, T> implements Encodeable<W, T
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void convertTo(W out, T value) {
|
||||
public void convertTo(W out, T value) {
|
||||
if (value == null) {
|
||||
out.writeObjectNull(null);
|
||||
return;
|
||||
@@ -136,24 +157,29 @@ public final class ObjectEncoder<W extends Writer, T> implements Encodeable<W, T
|
||||
}
|
||||
}
|
||||
}
|
||||
if (value.getClass() != this.typeClass) {
|
||||
if (value.getClass() != this.typeClass && !this.type.equals(out.specify())) {
|
||||
final Class clz = value.getClass();
|
||||
if (out.needWriteClassName()) out.writeClassName(factory.getEntityAlias(clz));
|
||||
factory.loadEncoder(clz).convertTo(out, value);
|
||||
return;
|
||||
}
|
||||
out.writeObjectB(value);
|
||||
for (EnMember member : members) {
|
||||
out.writeObjectField(member, value);
|
||||
if (out.writeObjectB(value) < 0) {
|
||||
for (EnMember member : members) {
|
||||
out.writeObjectField(member, value);
|
||||
}
|
||||
}
|
||||
out.writeObjectE(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Type getType() {
|
||||
public Type getType() {
|
||||
return this.type;
|
||||
}
|
||||
|
||||
public EnMember[] getMembers() {
|
||||
return Arrays.copyOf(members, members.length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ObjectEncoder{" + "type=" + type + ", members=" + Arrays.toString(members) + '}';
|
||||
|
||||
@@ -22,12 +22,46 @@ public abstract class Reader {
|
||||
|
||||
public static final short SIGN_NOLENGTH = -2;
|
||||
|
||||
public static final short SIGN_NOLENBUTBYTES = -3; //目前只适合于protobuf的boolean[]...double[]类型
|
||||
|
||||
/**
|
||||
* 是否还存在下个元素或字段
|
||||
* 是否还存在下个元素或字段 <br>
|
||||
* 注意: 主要用于Array、Collection、Stream或Map等集合对象
|
||||
*
|
||||
* @param startPosition 起始位置
|
||||
* @param contentLength 内容大小, 不确定的传-1
|
||||
*
|
||||
* @return 是否还存在下个元素或字段
|
||||
*/
|
||||
public abstract boolean hasNext();
|
||||
public abstract boolean hasNext(int startPosition, int contentLength);
|
||||
|
||||
/**
|
||||
* 是否还存在下个元素或字段
|
||||
*
|
||||
*
|
||||
* @return 是否还存在下个元素或字段
|
||||
*/
|
||||
public boolean hasNext() {
|
||||
return hasNext(-1, -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前位置
|
||||
*
|
||||
* @return 当前位置
|
||||
*/
|
||||
public abstract int position();
|
||||
|
||||
/**
|
||||
* 读取字段值内容的字节数 <br>
|
||||
* 只有在readXXXB方法返回SIGN_NOLENBUTBYTES值才会调用此方法
|
||||
*
|
||||
* @param member DeMember
|
||||
* @param decoder Decodeable
|
||||
*
|
||||
* @return 内容大小, 不确定返回-1
|
||||
*/
|
||||
public abstract int readMemberContentLength(DeMember member, Decodeable decoder);
|
||||
|
||||
/**
|
||||
* 跳过值(不包含值前面的字段)
|
||||
@@ -61,9 +95,12 @@ public abstract class Reader {
|
||||
/**
|
||||
* 读取数组的开头并返回数组的长度
|
||||
*
|
||||
* @param member DeMember
|
||||
* @param decoder Decodeable
|
||||
*
|
||||
* @return 返回数组的长度
|
||||
*/
|
||||
public abstract int readArrayB();
|
||||
public abstract int readArrayB(DeMember member, Decodeable decoder);
|
||||
|
||||
/**
|
||||
* 读取数组的尾端
|
||||
@@ -74,9 +111,12 @@ public abstract class Reader {
|
||||
/**
|
||||
* 读取map的开头并返回map的size
|
||||
*
|
||||
* @param member DeMember
|
||||
* @param keydecoder Decodeable
|
||||
*
|
||||
* @return 返回map的size
|
||||
*/
|
||||
public abstract int readMapB();
|
||||
public abstract int readMapB(DeMember member, Decodeable keydecoder);
|
||||
|
||||
/**
|
||||
* 读取数组的尾端
|
||||
@@ -107,6 +147,13 @@ public abstract class Reader {
|
||||
*/
|
||||
public abstract byte readByte();
|
||||
|
||||
/**
|
||||
* 读取byte[]
|
||||
*
|
||||
* @return byte[]
|
||||
*/
|
||||
public abstract byte[] readByteArray();
|
||||
|
||||
/**
|
||||
* 读取一个char值
|
||||
*
|
||||
|
||||
@@ -22,7 +22,7 @@ import java.lang.reflect.Type;
|
||||
*/
|
||||
public abstract class SimpledCoder<R extends Reader, W extends Writer, T> implements Decodeable<R, T>, Encodeable<W, T> {
|
||||
|
||||
private Type type;
|
||||
protected Type type;
|
||||
|
||||
@Override
|
||||
public abstract void convertTo(final W out, final T value);
|
||||
|
||||
@@ -22,19 +22,19 @@ import java.util.stream.Stream;
|
||||
* @param <T> 反解析的集合元素类型
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public final class StreamDecoder<T> implements Decodeable<Reader, Stream<T>> {
|
||||
public class StreamDecoder<T> implements Decodeable<Reader, Stream<T>> {
|
||||
|
||||
private final Type type;
|
||||
protected final Type type;
|
||||
|
||||
private final Type componentType;
|
||||
protected final Type componentType;
|
||||
|
||||
protected Creator<Stream<T>> creator;
|
||||
|
||||
protected final Decodeable<Reader, T> decoder;
|
||||
|
||||
private boolean inited = false;
|
||||
protected boolean inited = false;
|
||||
|
||||
private final Object lock = new Object();
|
||||
protected final Object lock = new Object();
|
||||
|
||||
public StreamDecoder(final ConvertFactory factory, final Type type) {
|
||||
this.type = type;
|
||||
@@ -58,8 +58,17 @@ public final class StreamDecoder<T> implements Decodeable<Reader, Stream<T>> {
|
||||
|
||||
@Override
|
||||
public Stream<T> convertFrom(Reader in) {
|
||||
final int len = in.readArrayB();
|
||||
return convertFrom(in, null);
|
||||
}
|
||||
|
||||
public Stream<T> convertFrom(Reader in, DeMember member) {
|
||||
int len = in.readArrayB(member, this.decoder);
|
||||
int contentLength = -1;
|
||||
if (len == Reader.SIGN_NULL) return null;
|
||||
if (len == Reader.SIGN_NOLENBUTBYTES) {
|
||||
contentLength = in.readMemberContentLength(member, this.decoder);
|
||||
len = Reader.SIGN_NOLENGTH;
|
||||
}
|
||||
if (this.decoder == null) {
|
||||
if (!this.inited) {
|
||||
synchronized (lock) {
|
||||
@@ -73,9 +82,14 @@ public final class StreamDecoder<T> implements Decodeable<Reader, Stream<T>> {
|
||||
}
|
||||
final Decodeable<Reader, T> localdecoder = this.decoder;
|
||||
final List<T> result = new ArrayList();
|
||||
boolean first = true;
|
||||
if (len == Reader.SIGN_NOLENGTH) {
|
||||
while (in.hasNext()) {
|
||||
result.add(localdecoder.convertFrom(in));
|
||||
int startPosition = in.position();
|
||||
while (hasNext(in, member, startPosition, contentLength, first)) {
|
||||
Reader itemReader = getItemReader(in, member, first);
|
||||
if (itemReader == null) break;
|
||||
result.add(readMemberValue(itemReader, member, first));
|
||||
first = false;
|
||||
}
|
||||
} else {
|
||||
for (int i = 0; i < len; i++) {
|
||||
@@ -86,9 +100,29 @@ public final class StreamDecoder<T> implements Decodeable<Reader, Stream<T>> {
|
||||
return result.stream();
|
||||
}
|
||||
|
||||
protected boolean hasNext(Reader in, DeMember member, int startPosition, int contentLength, boolean first) {
|
||||
return in.hasNext(startPosition, contentLength);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public Type getComponentType() {
|
||||
return componentType;
|
||||
}
|
||||
|
||||
public Decodeable<Reader, T> getDecoder() {
|
||||
return decoder;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -19,15 +19,15 @@ import java.util.stream.Stream;
|
||||
* @param <T> 序列化的集合元素类型
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public final class StreamEncoder<T> implements Encodeable<Writer, Stream<T>> {
|
||||
public class StreamEncoder<T> implements Encodeable<Writer, Stream<T>> {
|
||||
|
||||
private final Type type;
|
||||
protected final Type type;
|
||||
|
||||
private final Encodeable<Writer, Object> encoder;
|
||||
protected final Encodeable<Writer, Object> encoder;
|
||||
|
||||
private boolean inited = false;
|
||||
protected boolean inited = false;
|
||||
|
||||
private final Object lock = new Object();
|
||||
protected final Object lock = new Object();
|
||||
|
||||
public StreamEncoder(final ConvertFactory factory, final Type type) {
|
||||
this.type = type;
|
||||
@@ -52,13 +52,17 @@ public final class StreamEncoder<T> implements Encodeable<Writer, Stream<T>> {
|
||||
|
||||
@Override
|
||||
public void convertTo(Writer out, Stream<T> value) {
|
||||
convertTo(out, null, value);
|
||||
}
|
||||
|
||||
public void convertTo(Writer out, EnMember member, Stream<T> value) {
|
||||
if (value == null) {
|
||||
out.writeNull();
|
||||
return;
|
||||
}
|
||||
Object[] array = value.toArray();
|
||||
if (array.length == 0) {
|
||||
out.writeArrayB(0);
|
||||
out.writeArrayB(0, encoder, array);
|
||||
out.writeArrayE();
|
||||
return;
|
||||
}
|
||||
@@ -73,18 +77,28 @@ public final class StreamEncoder<T> implements Encodeable<Writer, Stream<T>> {
|
||||
}
|
||||
}
|
||||
}
|
||||
out.writeArrayB(array.length);
|
||||
boolean first = true;
|
||||
for (Object v : array) {
|
||||
if (!first) out.writeArrayMark();
|
||||
encoder.convertTo(out, v);
|
||||
if (first) first = false;
|
||||
if (out.writeArrayB(array.length, encoder, array) < 0) {
|
||||
boolean first = true;
|
||||
for (Object v : array) {
|
||||
if (!first) out.writeArrayMark();
|
||||
writeMemberValue(out, member, v, first);
|
||||
if (first) first = false;
|
||||
}
|
||||
}
|
||||
out.writeArrayE();
|
||||
}
|
||||
|
||||
protected void writeMemberValue(Writer out, EnMember member, Object value, boolean first) {
|
||||
encoder.convertTo(out, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public Encodeable<Writer, Object> getEncoder() {
|
||||
return encoder;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
*/
|
||||
package org.redkale.convert;
|
||||
|
||||
import org.redkale.util.Attribute;
|
||||
import java.lang.reflect.*;
|
||||
|
||||
/**
|
||||
* 序列化的数据输出流
|
||||
@@ -20,6 +20,33 @@ public abstract class Writer {
|
||||
//当前对象输出字段名之前是否需要分隔符, JSON字段间的分隔符为,逗号
|
||||
protected boolean comma;
|
||||
|
||||
//convertTo时是否以指定Type的ObjectEncoder进行处理
|
||||
protected Type specify;
|
||||
|
||||
/**
|
||||
* 设置specify
|
||||
*
|
||||
* @param value Type
|
||||
*/
|
||||
public void specify(Type value) {
|
||||
if (value instanceof GenericArrayType) {
|
||||
this.specify = ((GenericArrayType) value).getGenericComponentType();
|
||||
} else if (value instanceof Class && ((Class) value).isArray()) {
|
||||
this.specify = ((Class) value).getComponentType();
|
||||
} else {
|
||||
this.specify = value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回specify
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public Type specify() {
|
||||
return this.specify;
|
||||
}
|
||||
|
||||
/**
|
||||
* 当tiny=true时, 字符串为空、boolean为false的字段值都会被跳过, 不会输出。
|
||||
*
|
||||
@@ -51,9 +78,12 @@ public abstract class Writer {
|
||||
* 注: 覆盖此方法必须要先调用父方法 super.writeObjectB(obj);
|
||||
*
|
||||
* @param obj 写入的对象
|
||||
*
|
||||
* @return 返回-1表示还没有写入对象内容,大于-1表示已写入对象内容,返回对象内容大小
|
||||
*/
|
||||
public void writeObjectB(Object obj) {
|
||||
public int writeObjectB(Object obj) {
|
||||
this.comma = false;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -74,7 +104,7 @@ public abstract class Writer {
|
||||
* @param obj 写入的对象
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public final void writeObjectField(final EnMember member, Object obj) {
|
||||
public void writeObjectField(final EnMember member, Object obj) {
|
||||
Object value = member.attribute.get(obj);
|
||||
if (value == null) return;
|
||||
if (tiny()) {
|
||||
@@ -84,7 +114,7 @@ public abstract class Writer {
|
||||
if (!((Boolean) value)) return;
|
||||
}
|
||||
}
|
||||
this.writeFieldName(member.attribute);
|
||||
this.writeFieldName(member);
|
||||
member.encoder.convertTo(this, value);
|
||||
this.comma = true;
|
||||
}
|
||||
@@ -99,9 +129,13 @@ public abstract class Writer {
|
||||
/**
|
||||
* 输出一个数组前的操作
|
||||
*
|
||||
* @param size 数组长度
|
||||
* @param size 数组长度
|
||||
* @param encoder Encodeable
|
||||
* @param obj 对象
|
||||
*
|
||||
* @return 返回-1表示还没有写入对象内容,大于-1表示已写入对象内容,返回对象内容大小
|
||||
*/
|
||||
public abstract void writeArrayB(int size);
|
||||
public abstract int writeArrayB(int size, Encodeable<Writer, Object> encoder, Object obj);
|
||||
|
||||
/**
|
||||
* 输出数组元素间的间隔符
|
||||
@@ -118,9 +152,14 @@ public abstract class Writer {
|
||||
/**
|
||||
* 输出一个Map前的操作
|
||||
*
|
||||
* @param size map大小
|
||||
* @param size map大小
|
||||
* @param keyEncoder Encodeable
|
||||
* @param valueEncoder Encodeable
|
||||
* @param obj 对象
|
||||
*
|
||||
* @return 返回-1表示还没有写入对象内容,大于-1表示已写入对象内容,返回对象内容大小
|
||||
*/
|
||||
public abstract void writeMapB(int size);
|
||||
public abstract int writeMapB(int size, Encodeable<Writer, Object> keyEncoder, Encodeable<Writer, Object> valueEncoder, Object obj);
|
||||
|
||||
/**
|
||||
* 输出一个Map中key与value间的间隔符
|
||||
@@ -137,9 +176,9 @@ public abstract class Writer {
|
||||
/**
|
||||
* 输出一个字段名
|
||||
*
|
||||
* @param attribute 字段的Attribute对象
|
||||
* @param member 字段的EnMember对象
|
||||
*/
|
||||
public abstract void writeFieldName(Attribute attribute);
|
||||
public abstract void writeFieldName(EnMember member);
|
||||
|
||||
/**
|
||||
* 写入一个boolean值
|
||||
@@ -155,6 +194,13 @@ public abstract class Writer {
|
||||
*/
|
||||
public abstract void writeByte(byte value);
|
||||
|
||||
/**
|
||||
* 写入byte[]
|
||||
*
|
||||
* @param values byte[]
|
||||
*/
|
||||
public abstract void writeByteArray(byte[] values);
|
||||
|
||||
/**
|
||||
* 写入一个char值
|
||||
*
|
||||
|
||||
@@ -51,10 +51,12 @@ public class BsonByteBufferReader extends BsonReader {
|
||||
/**
|
||||
* 判断下一个非空白字节是否为[
|
||||
*
|
||||
* @param member DeMember
|
||||
* @param decoder Decodeable
|
||||
* @return 数组长度或 SIGN_NULL
|
||||
*/
|
||||
@Override
|
||||
public final int readArrayB() {
|
||||
public final int readArrayB(DeMember member, Decodeable decoder) {
|
||||
short bt = readShort();
|
||||
if (bt == Reader.SIGN_NULL) return bt;
|
||||
short lt = readShort();
|
||||
|
||||
@@ -132,6 +132,7 @@ public class BsonByteBufferWriter extends BsonWriter {
|
||||
@Override
|
||||
protected boolean recycle() {
|
||||
this.index = 0;
|
||||
this.specify = null;
|
||||
this.buffers = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -84,4 +84,9 @@ public final class BsonFactory extends ConvertFactory<BsonReader, BsonWriter> {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFieldSort() {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -171,8 +171,8 @@ public class BsonReader extends Reader {
|
||||
}
|
||||
|
||||
@Override
|
||||
public final int readMapB() {
|
||||
return readArrayB();
|
||||
public final int readMapB(DeMember member, Decodeable keydecoder) {
|
||||
return readArrayB(member, keydecoder);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -185,7 +185,7 @@ public class BsonReader extends Reader {
|
||||
* @return 数组长度或SIGN_NULL
|
||||
*/
|
||||
@Override
|
||||
public int readArrayB() {
|
||||
public int readArrayB(DeMember member, Decodeable decoder) {
|
||||
short bt = readShort();
|
||||
if (bt == Reader.SIGN_NULL) return bt;
|
||||
return (bt & 0xffff) << 16 | ((content[++this.position] & 0xff) << 8) | (content[++this.position] & 0xff);
|
||||
@@ -202,13 +202,26 @@ public class BsonReader extends Reader {
|
||||
public final void readBlank() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public int position() {
|
||||
return this.position;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int readMemberContentLength(DeMember member, Decodeable decoder) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断对象是否存在下一个属性或者数组是否存在下一个元素
|
||||
*
|
||||
* @param startPosition 起始位置
|
||||
* @param contentLength 内容大小, 不确定的传-1
|
||||
*
|
||||
* @return 是否存在
|
||||
*/
|
||||
@Override
|
||||
public final boolean hasNext() {
|
||||
public boolean hasNext(int startPosition, int contentLength) {
|
||||
byte b = readByte();
|
||||
if (b == SIGN_HASNEXT) return true;
|
||||
if (b != SIGN_NONEXT) throw new ConvertException("hasNext option must be (" + (SIGN_HASNEXT)
|
||||
@@ -248,6 +261,41 @@ public class BsonReader extends Reader {
|
||||
return content[++this.position];
|
||||
}
|
||||
|
||||
@Override
|
||||
public final byte[] readByteArray() {
|
||||
int len = readArrayB(null, null);
|
||||
int contentLength = -1;
|
||||
if (len == Reader.SIGN_NULL) return null;
|
||||
if (len == Reader.SIGN_NOLENBUTBYTES) {
|
||||
contentLength = readMemberContentLength(null, null);
|
||||
len = Reader.SIGN_NOLENGTH;
|
||||
}
|
||||
if (len == Reader.SIGN_NOLENGTH) {
|
||||
int size = 0;
|
||||
byte[] data = new byte[8];
|
||||
int startPosition = position();
|
||||
while (hasNext(startPosition, contentLength)) {
|
||||
if (size >= data.length) {
|
||||
byte[] newdata = new byte[data.length + 4];
|
||||
System.arraycopy(data, 0, newdata, 0, size);
|
||||
data = newdata;
|
||||
}
|
||||
data[size++] = readByte();
|
||||
}
|
||||
readArrayE();
|
||||
byte[] newdata = new byte[size];
|
||||
System.arraycopy(data, 0, newdata, 0, size);
|
||||
return newdata;
|
||||
} else {
|
||||
byte[] values = new byte[len];
|
||||
for (int i = 0; i < values.length; i++) {
|
||||
values[i] = readByte();
|
||||
}
|
||||
readArrayE();
|
||||
return values;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public char readChar() {
|
||||
return (char) ((0xff00 & (content[++this.position] << 8)) | (0xff & content[++this.position]));
|
||||
|
||||
@@ -98,6 +98,7 @@ public class BsonWriter extends Writer {
|
||||
|
||||
protected boolean recycle() {
|
||||
this.count = 0;
|
||||
this.specify = null;
|
||||
if (this.content.length > defaultSize) {
|
||||
this.content = new byte[defaultSize];
|
||||
}
|
||||
@@ -124,6 +125,22 @@ public class BsonWriter extends Writer {
|
||||
writeTo(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void writeByteArray(byte[] values) {
|
||||
if (values == null) {
|
||||
writeNull();
|
||||
return;
|
||||
}
|
||||
writeArrayB(values.length, null, values);
|
||||
boolean flag = false;
|
||||
for (byte v : values) {
|
||||
if (flag) writeArrayMark();
|
||||
writeByte(v);
|
||||
flag = true;
|
||||
}
|
||||
writeArrayE();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void writeChar(final char value) {
|
||||
writeTo((byte) ((value & 0xFF00) >> 8), (byte) (value & 0xFF));
|
||||
@@ -166,10 +183,11 @@ public class BsonWriter extends Writer {
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void writeObjectB(Object obj) {
|
||||
public final int writeObjectB(Object obj) {
|
||||
super.writeObjectB(obj);
|
||||
writeSmallString("");
|
||||
writeShort(BsonReader.SIGN_OBJECTB);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -179,7 +197,8 @@ public class BsonWriter extends Writer {
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void writeFieldName(Attribute attribute) {
|
||||
public final void writeFieldName(EnMember member) {
|
||||
Attribute attribute = member.getAttribute();
|
||||
writeByte(BsonReader.SIGN_HASNEXT);
|
||||
writeSmallString(attribute.field());
|
||||
byte typeval = 127; //字段的类型值
|
||||
@@ -266,8 +285,9 @@ public class BsonWriter extends Writer {
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void writeArrayB(int size) {
|
||||
public final int writeArrayB(int size, Encodeable<Writer, Object> encoder, Object obj) {
|
||||
writeInt(size);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -279,8 +299,9 @@ public class BsonWriter extends Writer {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeMapB(int size) {
|
||||
writeArrayB(size);
|
||||
public int writeMapB(int size, Encodeable<Writer, Object> keyEncoder, Encodeable<Writer, Object> valueEncoder, Object obj) {
|
||||
writeArrayB(size, valueEncoder, obj);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -29,24 +29,31 @@ public final class BoolArraySimpledCoder<R extends Reader, W extends Writer> ext
|
||||
out.writeNull();
|
||||
return;
|
||||
}
|
||||
out.writeArrayB(values.length);
|
||||
boolean flag = false;
|
||||
for (boolean v : values) {
|
||||
if (flag) out.writeArrayMark();
|
||||
out.writeBoolean(v);
|
||||
flag = true;
|
||||
if (out.writeArrayB(values.length, BoolSimpledCoder.instance, values) < 0) {
|
||||
boolean flag = false;
|
||||
for (boolean v : values) {
|
||||
if (flag) out.writeArrayMark();
|
||||
out.writeBoolean(v);
|
||||
flag = true;
|
||||
}
|
||||
}
|
||||
out.writeArrayE();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean[] convertFrom(R in) {
|
||||
int len = in.readArrayB();
|
||||
int len = in.readArrayB(null, BoolSimpledCoder.instance);
|
||||
int contentLength = -1;
|
||||
if (len == Reader.SIGN_NULL) return null;
|
||||
if (len == Reader.SIGN_NOLENBUTBYTES) {
|
||||
contentLength = in.readMemberContentLength(null, BoolSimpledCoder.instance);
|
||||
len = Reader.SIGN_NOLENGTH;
|
||||
}
|
||||
if (len == Reader.SIGN_NOLENGTH) {
|
||||
int size = 0;
|
||||
boolean[] data = new boolean[8];
|
||||
while (in.hasNext()) {
|
||||
int startPosition = in.position();
|
||||
while (in.hasNext(startPosition, contentLength)) {
|
||||
if (size >= data.length) {
|
||||
boolean[] newdata = new boolean[data.length + 4];
|
||||
System.arraycopy(data, 0, newdata, 0, size);
|
||||
|
||||
@@ -11,7 +11,7 @@ import org.redkale.convert.Writer;
|
||||
|
||||
/**
|
||||
* byte[] 的SimpledCoder实现
|
||||
*
|
||||
*
|
||||
* <p>
|
||||
* 详情见: https://redkale.org
|
||||
*
|
||||
@@ -25,47 +25,12 @@ public final class ByteArraySimpledCoder<R extends Reader, W extends Writer> ext
|
||||
|
||||
@Override
|
||||
public void convertTo(W out, byte[] values) {
|
||||
if (values == null) {
|
||||
out.writeNull();
|
||||
return;
|
||||
}
|
||||
out.writeArrayB(values.length);
|
||||
boolean flag = false;
|
||||
for (byte v : values) {
|
||||
if (flag) out.writeArrayMark();
|
||||
out.writeByte(v);
|
||||
flag = true;
|
||||
}
|
||||
out.writeArrayE();
|
||||
out.writeByteArray(values);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] convertFrom(R in) {
|
||||
int len = in.readArrayB();
|
||||
if (len == Reader.SIGN_NULL) return null;
|
||||
if (len == Reader.SIGN_NOLENGTH) {
|
||||
int size = 0;
|
||||
byte[] data = new byte[8];
|
||||
while (in.hasNext()) {
|
||||
if (size >= data.length) {
|
||||
byte[] newdata = new byte[data.length + 4];
|
||||
System.arraycopy(data, 0, newdata, 0, size);
|
||||
data = newdata;
|
||||
}
|
||||
data[size++] = in.readByte();
|
||||
}
|
||||
in.readArrayE();
|
||||
byte[] newdata = new byte[size];
|
||||
System.arraycopy(data, 0, newdata, 0, size);
|
||||
return newdata;
|
||||
} else {
|
||||
byte[] values = new byte[len];
|
||||
for (int i = 0; i < values.length; i++) {
|
||||
values[i] = in.readByte();
|
||||
}
|
||||
in.readArrayE();
|
||||
return values;
|
||||
}
|
||||
return in.readByteArray();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -30,24 +30,31 @@ public final class ByteBufferSimpledCoder<R extends Reader, W extends Writer> ex
|
||||
out.writeNull();
|
||||
return;
|
||||
}
|
||||
out.writeArrayB(value.remaining());
|
||||
boolean flag = false;
|
||||
for (byte v : value.array()) {
|
||||
if (flag) out.writeArrayMark();
|
||||
out.writeByte(v);
|
||||
flag = true;
|
||||
if (out.writeArrayB(value.remaining(), ByteSimpledCoder.instance, value) < 0) {
|
||||
boolean flag = false;
|
||||
for (byte v : value.array()) {
|
||||
if (flag) out.writeArrayMark();
|
||||
out.writeByte(v);
|
||||
flag = true;
|
||||
}
|
||||
}
|
||||
out.writeArrayE();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ByteBuffer convertFrom(R in) {
|
||||
int len = in.readArrayB();
|
||||
int len = in.readArrayB(null, ByteSimpledCoder.instance);
|
||||
int contentLength = -1;
|
||||
if (len == Reader.SIGN_NULL) return null;
|
||||
if (len == Reader.SIGN_NOLENBUTBYTES) {
|
||||
contentLength = in.readMemberContentLength(null, ByteSimpledCoder.instance);
|
||||
len = Reader.SIGN_NOLENGTH;
|
||||
}
|
||||
if (len == Reader.SIGN_NOLENGTH) {
|
||||
int size = 0;
|
||||
byte[] data = new byte[8];
|
||||
while (in.hasNext()) {
|
||||
int startPosition = in.position();
|
||||
while (in.hasNext(startPosition, contentLength)) {
|
||||
if (size >= data.length) {
|
||||
byte[] newdata = new byte[data.length + 4];
|
||||
System.arraycopy(data, 0, newdata, 0, size);
|
||||
|
||||
@@ -12,7 +12,9 @@ import org.redkale.convert.Writer;
|
||||
/**
|
||||
* char[] 的SimpledCoder实现
|
||||
*
|
||||
* <p> 详情见: https://redkale.org
|
||||
* <p>
|
||||
* 详情见: https://redkale.org
|
||||
*
|
||||
* @author zhangjx
|
||||
* @param <R> Reader输入的子类型
|
||||
* @param <W> Writer输出的子类型
|
||||
@@ -27,24 +29,31 @@ public final class CharArraySimpledCoder<R extends Reader, W extends Writer> ext
|
||||
out.writeNull();
|
||||
return;
|
||||
}
|
||||
out.writeArrayB(values.length);
|
||||
boolean flag = false;
|
||||
for (char v : values) {
|
||||
if (flag) out.writeArrayMark();
|
||||
out.writeChar(v);
|
||||
flag = true;
|
||||
if (out.writeArrayB(values.length, CharSimpledCoder.instance, values) < 0) {
|
||||
boolean flag = false;
|
||||
for (char v : values) {
|
||||
if (flag) out.writeArrayMark();
|
||||
out.writeChar(v);
|
||||
flag = true;
|
||||
}
|
||||
}
|
||||
out.writeArrayE();
|
||||
}
|
||||
|
||||
@Override
|
||||
public char[] convertFrom(R in) {
|
||||
int len = in.readArrayB();
|
||||
int len = in.readArrayB(null, CharSimpledCoder.instance);
|
||||
int contentLength = -1;
|
||||
if (len == Reader.SIGN_NULL) return null;
|
||||
if (len == Reader.SIGN_NOLENBUTBYTES) {
|
||||
contentLength = in.readMemberContentLength(null, CharSimpledCoder.instance);
|
||||
len = Reader.SIGN_NOLENGTH;
|
||||
}
|
||||
if (len == Reader.SIGN_NOLENGTH) {
|
||||
int size = 0;
|
||||
char[] data = new char[8];
|
||||
while (in.hasNext()) {
|
||||
int startPosition = in.position();
|
||||
while (in.hasNext(startPosition, contentLength)) {
|
||||
if (size >= data.length) {
|
||||
char[] newdata = new char[data.length + 4];
|
||||
System.arraycopy(data, 0, newdata, 0, size);
|
||||
|
||||
@@ -30,24 +30,31 @@ public final class DoubleArraySimpledCoder<R extends Reader, W extends Writer> e
|
||||
out.writeNull();
|
||||
return;
|
||||
}
|
||||
out.writeArrayB(values.length);
|
||||
boolean flag = false;
|
||||
for (double v : values) {
|
||||
if (flag) out.writeArrayMark();
|
||||
out.writeDouble(v);
|
||||
flag = true;
|
||||
if (out.writeArrayB(values.length, DoubleSimpledCoder.instance, values) < 0) {
|
||||
boolean flag = false;
|
||||
for (double v : values) {
|
||||
if (flag) out.writeArrayMark();
|
||||
out.writeDouble(v);
|
||||
flag = true;
|
||||
}
|
||||
}
|
||||
out.writeArrayE();
|
||||
}
|
||||
|
||||
@Override
|
||||
public double[] convertFrom(R in) {
|
||||
int len = in.readArrayB();
|
||||
int len = in.readArrayB(null, DoubleSimpledCoder.instance);
|
||||
int contentLength = -1;
|
||||
if (len == Reader.SIGN_NULL) return null;
|
||||
if (len == Reader.SIGN_NOLENBUTBYTES) {
|
||||
contentLength = in.readMemberContentLength(null, DoubleSimpledCoder.instance);
|
||||
len = Reader.SIGN_NOLENGTH;
|
||||
}
|
||||
if (len == Reader.SIGN_NOLENGTH) {
|
||||
int size = 0;
|
||||
double[] data = new double[8];
|
||||
while (in.hasNext()) {
|
||||
int startPosition = in.position();
|
||||
while (in.hasNext(startPosition, contentLength)) {
|
||||
if (size >= data.length) {
|
||||
double[] newdata = new double[data.length + 4];
|
||||
System.arraycopy(data, 0, newdata, 0, size);
|
||||
|
||||
@@ -45,4 +45,8 @@ public final class EnumSimpledCoder<R extends Reader, W extends Writer, E extend
|
||||
return (E) Enum.valueOf(type, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<E> getType() {
|
||||
return type;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,9 @@ import org.redkale.convert.Writer;
|
||||
/**
|
||||
* float[] 的SimpledCoder实现
|
||||
*
|
||||
* <p> 详情见: https://redkale.org
|
||||
* <p>
|
||||
* 详情见: https://redkale.org
|
||||
*
|
||||
* @author zhangjx
|
||||
* @param <R> Reader输入的子类型
|
||||
* @param <W> Writer输出的子类型
|
||||
@@ -27,24 +29,31 @@ public final class FloatArraySimpledCoder<R extends Reader, W extends Writer> ex
|
||||
out.writeNull();
|
||||
return;
|
||||
}
|
||||
out.writeArrayB(values.length);
|
||||
boolean flag = false;
|
||||
for (float v : values) {
|
||||
if (flag) out.writeArrayMark();
|
||||
out.writeFloat(v);
|
||||
flag = true;
|
||||
if (out.writeArrayB(values.length, FloatSimpledCoder.instance, values) < 0) {
|
||||
boolean flag = false;
|
||||
for (float v : values) {
|
||||
if (flag) out.writeArrayMark();
|
||||
out.writeFloat(v);
|
||||
flag = true;
|
||||
}
|
||||
}
|
||||
out.writeArrayE();
|
||||
}
|
||||
|
||||
@Override
|
||||
public float[] convertFrom(R in) {
|
||||
int len = in.readArrayB();
|
||||
int len = in.readArrayB(null, FloatSimpledCoder.instance);
|
||||
int contentLength = -1;
|
||||
if (len == Reader.SIGN_NULL) return null;
|
||||
if (len == Reader.SIGN_NOLENBUTBYTES) {
|
||||
contentLength = in.readMemberContentLength(null, FloatSimpledCoder.instance);
|
||||
len = Reader.SIGN_NOLENGTH;
|
||||
}
|
||||
if (len == Reader.SIGN_NOLENGTH) {
|
||||
int size = 0;
|
||||
float[] data = new float[8];
|
||||
while (in.hasNext()) {
|
||||
int startPosition = in.position();
|
||||
while (in.hasNext(startPosition, contentLength)) {
|
||||
if (size >= data.length) {
|
||||
float[] newdata = new float[data.length + 4];
|
||||
System.arraycopy(data, 0, newdata, 0, size);
|
||||
|
||||
@@ -30,24 +30,31 @@ public final class IntArraySimpledCoder<R extends Reader, W extends Writer> exte
|
||||
out.writeNull();
|
||||
return;
|
||||
}
|
||||
out.writeArrayB(values.length);
|
||||
boolean flag = false;
|
||||
for (int v : values) {
|
||||
if (flag) out.writeArrayMark();
|
||||
out.writeInt(v);
|
||||
flag = true;
|
||||
if (out.writeArrayB(values.length, IntSimpledCoder.instance, values) < 0) {
|
||||
boolean flag = false;
|
||||
for (int v : values) {
|
||||
if (flag) out.writeArrayMark();
|
||||
out.writeInt(v);
|
||||
flag = true;
|
||||
}
|
||||
}
|
||||
out.writeArrayE();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int[] convertFrom(R in) {
|
||||
int len = in.readArrayB();
|
||||
int len = in.readArrayB(null, IntSimpledCoder.instance);
|
||||
int contentLength = -1;
|
||||
if (len == Reader.SIGN_NULL) return null;
|
||||
if (len == Reader.SIGN_NOLENBUTBYTES) {
|
||||
contentLength = in.readMemberContentLength(null, IntSimpledCoder.instance);
|
||||
len = Reader.SIGN_NOLENGTH;
|
||||
}
|
||||
if (len == Reader.SIGN_NOLENGTH) {
|
||||
int size = 0;
|
||||
int[] data = new int[8];
|
||||
while (in.hasNext()) {
|
||||
int startPosition = in.position();
|
||||
while (in.hasNext(startPosition, contentLength)) {
|
||||
if (size >= data.length) {
|
||||
int[] newdata = new int[data.length + 4];
|
||||
System.arraycopy(data, 0, newdata, 0, size);
|
||||
|
||||
@@ -30,24 +30,31 @@ public final class LongArraySimpledCoder<R extends Reader, W extends Writer> ext
|
||||
out.writeNull();
|
||||
return;
|
||||
}
|
||||
out.writeArrayB(values.length);
|
||||
boolean flag = false;
|
||||
for (long v : values) {
|
||||
if (flag) out.writeArrayMark();
|
||||
out.writeLong(v);
|
||||
flag = true;
|
||||
if (out.writeArrayB(values.length, LongSimpledCoder.instance, values) < 0) {
|
||||
boolean flag = false;
|
||||
for (long v : values) {
|
||||
if (flag) out.writeArrayMark();
|
||||
out.writeLong(v);
|
||||
flag = true;
|
||||
}
|
||||
}
|
||||
out.writeArrayE();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long[] convertFrom(R in) {
|
||||
int len = in.readArrayB();
|
||||
int len = in.readArrayB(null, LongSimpledCoder.instance);
|
||||
int contentLength = -1;
|
||||
if (len == Reader.SIGN_NULL) return null;
|
||||
if (len == Reader.SIGN_NOLENBUTBYTES) {
|
||||
contentLength = in.readMemberContentLength(null, LongSimpledCoder.instance);
|
||||
len = Reader.SIGN_NOLENGTH;
|
||||
}
|
||||
if (len == Reader.SIGN_NOLENGTH) {
|
||||
int size = 0;
|
||||
long[] data = new long[8];
|
||||
while (in.hasNext()) {
|
||||
int startPosition = in.position();
|
||||
while (in.hasNext(startPosition, contentLength)) {
|
||||
if (size >= data.length) {
|
||||
long[] newdata = new long[data.length + 4];
|
||||
System.arraycopy(data, 0, newdata, 0, size);
|
||||
|
||||
@@ -12,7 +12,9 @@ import org.redkale.convert.Writer;
|
||||
/**
|
||||
* short[] 的SimpledCoder实现
|
||||
*
|
||||
* <p> 详情见: https://redkale.org
|
||||
* <p>
|
||||
* 详情见: https://redkale.org
|
||||
*
|
||||
* @author zhangjx
|
||||
* @param <R> Reader输入的子类型
|
||||
* @param <W> Writer输出的子类型
|
||||
@@ -27,24 +29,31 @@ public final class ShortArraySimpledCoder<R extends Reader, W extends Writer> ex
|
||||
out.writeNull();
|
||||
return;
|
||||
}
|
||||
out.writeArrayB(values.length);
|
||||
boolean flag = false;
|
||||
for (short v : values) {
|
||||
if (flag) out.writeArrayMark();
|
||||
out.writeShort(v);
|
||||
flag = true;
|
||||
if (out.writeArrayB(values.length, ShortSimpledCoder.instance, values) < 0) {
|
||||
boolean flag = false;
|
||||
for (short v : values) {
|
||||
if (flag) out.writeArrayMark();
|
||||
out.writeShort(v);
|
||||
flag = true;
|
||||
}
|
||||
}
|
||||
out.writeArrayE();
|
||||
}
|
||||
|
||||
@Override
|
||||
public short[] convertFrom(R in) {
|
||||
int len = in.readArrayB();
|
||||
int len = in.readArrayB(null, ShortSimpledCoder.instance);
|
||||
int contentLength = -1;
|
||||
if (len == Reader.SIGN_NULL) return null;
|
||||
if (len == Reader.SIGN_NOLENBUTBYTES) {
|
||||
contentLength = in.readMemberContentLength(null, ShortSimpledCoder.instance);
|
||||
len = Reader.SIGN_NOLENGTH;
|
||||
}
|
||||
if (len == Reader.SIGN_NOLENGTH) {
|
||||
int size = 0;
|
||||
short[] data = new short[8];
|
||||
while (in.hasNext()) {
|
||||
int startPosition = in.position();
|
||||
while (in.hasNext(startPosition, contentLength)) {
|
||||
if (size >= data.length) {
|
||||
short[] newdata = new short[data.length + 4];
|
||||
System.arraycopy(data, 0, newdata, 0, size);
|
||||
|
||||
@@ -5,14 +5,14 @@
|
||||
*/
|
||||
package org.redkale.convert.ext;
|
||||
|
||||
import org.redkale.convert.Reader;
|
||||
import org.redkale.convert.SimpledCoder;
|
||||
import org.redkale.convert.Writer;
|
||||
import org.redkale.convert.*;
|
||||
|
||||
/**
|
||||
* String[] 的SimpledCoder实现
|
||||
*
|
||||
* <p> 详情见: https://redkale.org
|
||||
* <p>
|
||||
* 详情见: https://redkale.org
|
||||
*
|
||||
* @author zhangjx
|
||||
* @param <R> Reader输入的子类型
|
||||
* @param <W> Writer输出的子类型
|
||||
@@ -27,24 +27,35 @@ public final class StringArraySimpledCoder<R extends Reader, W extends Writer> e
|
||||
out.writeNull();
|
||||
return;
|
||||
}
|
||||
out.writeArrayB(values.length);
|
||||
boolean flag = false;
|
||||
for (String v : values) {
|
||||
if (flag) out.writeArrayMark();
|
||||
out.writeString(v);
|
||||
flag = true;
|
||||
if (out.writeArrayB(values.length, StringSimpledCoder.instance, values) < 0) {
|
||||
boolean flag = false;
|
||||
for (String v : values) {
|
||||
if (flag) out.writeArrayMark();
|
||||
out.writeString(v);
|
||||
flag = true;
|
||||
}
|
||||
}
|
||||
out.writeArrayE();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] convertFrom(R in) {
|
||||
int len = in.readArrayB();
|
||||
return convertFrom(in, null);
|
||||
}
|
||||
|
||||
public String[] convertFrom(R in, DeMember member) {
|
||||
int len = in.readArrayB(member, StringSimpledCoder.instance);
|
||||
int contentLength = -1;
|
||||
if (len == Reader.SIGN_NULL) return null;
|
||||
if (len == Reader.SIGN_NOLENBUTBYTES) {
|
||||
contentLength = in.readMemberContentLength(null, StringSimpledCoder.instance);
|
||||
len = Reader.SIGN_NOLENGTH;
|
||||
}
|
||||
if (len == Reader.SIGN_NOLENGTH) {
|
||||
int size = 0;
|
||||
String[] data = new String[8];
|
||||
while (in.hasNext()) {
|
||||
int startPosition = in.position();
|
||||
while (in.hasNext(startPosition, contentLength)) {
|
||||
if (size >= data.length) {
|
||||
String[] newdata = new String[data.length + 4];
|
||||
System.arraycopy(data, 0, newdata, 0, size);
|
||||
|
||||
@@ -126,21 +126,38 @@ 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
|
||||
*
|
||||
* @return SIGN_NOLENGTH 或 SIGN_NULL
|
||||
*/
|
||||
@Override
|
||||
public final int readArrayB() {
|
||||
public final int readArrayB(DeMember member, 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 + ")");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -150,16 +167,26 @@ 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 + ")");
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断对象是否存在下一个属性或者数组是否存在下一个元素
|
||||
*
|
||||
* @param startPosition 起始位置
|
||||
* @param contentLength 内容大小, 不确定的传-1
|
||||
*
|
||||
* @return 是否存在
|
||||
*/
|
||||
@Override
|
||||
public final boolean hasNext() {
|
||||
public boolean hasNext(int startPosition, int contentLength) {
|
||||
char ch = nextGoodChar();
|
||||
if (ch == ',') return true;
|
||||
if (ch == '}' || ch == ']' || ch == 0) return false;
|
||||
@@ -272,8 +299,10 @@ public class JsonByteBufferReader extends JsonReader {
|
||||
@Override
|
||||
public final int readInt() {
|
||||
char firstchar = nextGoodChar();
|
||||
boolean quote = false;
|
||||
if (firstchar == '"' || firstchar == '\'') {
|
||||
firstchar = nextChar();
|
||||
quote = true;
|
||||
firstchar = nextGoodChar();
|
||||
if (firstchar == '"' || firstchar == '\'') return 0;
|
||||
}
|
||||
int value = 0;
|
||||
@@ -288,6 +317,7 @@ public class JsonByteBufferReader extends JsonReader {
|
||||
if (ch >= '0' && ch <= '9') {
|
||||
value = (value << 3) + (value << 1) + (ch - '0');
|
||||
} else if (ch == '"' || ch == '\'') {
|
||||
} else if (quote && ch <= ' ') {
|
||||
} else if (ch == ',' || ch == '}' || ch == ']' || ch <= ' ' || ch == ':') {
|
||||
backChar(ch);
|
||||
break;
|
||||
@@ -306,8 +336,10 @@ public class JsonByteBufferReader extends JsonReader {
|
||||
@Override
|
||||
public final long readLong() {
|
||||
char firstchar = nextGoodChar();
|
||||
boolean quote = false;
|
||||
if (firstchar == '"' || firstchar == '\'') {
|
||||
firstchar = nextChar();
|
||||
quote = true;
|
||||
firstchar = nextGoodChar();
|
||||
if (firstchar == '"' || firstchar == '\'') return 0L;
|
||||
}
|
||||
long value = 0;
|
||||
@@ -322,6 +354,7 @@ public class JsonByteBufferReader extends JsonReader {
|
||||
if (ch >= '0' && ch <= '9') {
|
||||
value = (value << 3) + (value << 1) + (ch - '0');
|
||||
} else if (ch == '"' || ch == '\'') {
|
||||
} else if (quote && ch <= ' ') {
|
||||
} else if (ch == ',' || ch == '}' || ch == ']' || ch <= ' ' || ch == ':') {
|
||||
backChar(ch);
|
||||
break;
|
||||
|
||||
@@ -49,6 +49,7 @@ public class JsonByteBufferWriter extends JsonWriter {
|
||||
@Override
|
||||
protected boolean recycle() {
|
||||
this.index = 0;
|
||||
this.specify = null;
|
||||
this.charset = null;
|
||||
this.buffers = null;
|
||||
return false;
|
||||
|
||||
@@ -80,8 +80,8 @@ public final class JsonConvert extends TextConvert<JsonReader, JsonWriter> {
|
||||
return writerPool.get().tiny(tiny);
|
||||
}
|
||||
|
||||
public void offerJsonWriter(final JsonWriter out) {
|
||||
if (out != null) writerPool.accept(out);
|
||||
public void offerJsonWriter(final JsonWriter writer) {
|
||||
if (writer != null) writerPool.accept(writer);
|
||||
}
|
||||
|
||||
//------------------------------ convertFrom -----------------------------------------------------------
|
||||
@@ -139,20 +139,21 @@ public final class JsonConvert extends TextConvert<JsonReader, JsonWriter> {
|
||||
public String convertTo(final Type type, final Object value) {
|
||||
if (type == null) return null;
|
||||
if (value == null) return "null";
|
||||
final JsonWriter out = writerPool.get().tiny(tiny);
|
||||
factory.loadEncoder(type).convertTo(out, value);
|
||||
String result = out.toString();
|
||||
writerPool.accept(out);
|
||||
final JsonWriter writer = writerPool.get().tiny(tiny);
|
||||
writer.specify(type);
|
||||
factory.loadEncoder(type).convertTo(writer, value);
|
||||
String result = writer.toString();
|
||||
writerPool.accept(writer);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String convertMapTo(final Object... values) {
|
||||
if (values == null) return "null";
|
||||
final JsonWriter out = writerPool.get().tiny(tiny);
|
||||
((AnyEncoder) factory.getAnyEncoder()).convertMapTo(out, values);
|
||||
String result = out.toString();
|
||||
writerPool.accept(out);
|
||||
final JsonWriter writer = writerPool.get().tiny(tiny);
|
||||
((AnyEncoder) factory.getAnyEncoder()).convertMapTo(writer, values);
|
||||
String result = writer.toString();
|
||||
writerPool.accept(writer);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -170,6 +171,7 @@ public final class JsonConvert extends TextConvert<JsonReader, JsonWriter> {
|
||||
new JsonStreamWriter(tiny, out).writeNull();
|
||||
} else {
|
||||
final JsonWriter writer = writerPool.get().tiny(tiny);
|
||||
writer.specify(type);
|
||||
factory.loadEncoder(type).convertTo(writer, value);
|
||||
byte[] bs = writer.toBytes();
|
||||
writerPool.accept(writer);
|
||||
@@ -216,6 +218,7 @@ public final class JsonConvert extends TextConvert<JsonReader, JsonWriter> {
|
||||
if (value == null) {
|
||||
out.writeNull();
|
||||
} else {
|
||||
out.specify(type);
|
||||
factory.loadEncoder(type).convertTo(out, value);
|
||||
}
|
||||
return out.toBuffers();
|
||||
@@ -246,6 +249,7 @@ public final class JsonConvert extends TextConvert<JsonReader, JsonWriter> {
|
||||
if (value == null) {
|
||||
writer.writeNull();
|
||||
} else {
|
||||
writer.specify(type);
|
||||
factory.loadEncoder(type).convertTo(writer, value);
|
||||
}
|
||||
}
|
||||
@@ -265,14 +269,15 @@ public final class JsonConvert extends TextConvert<JsonReader, JsonWriter> {
|
||||
|
||||
public JsonWriter convertToWriter(final Type type, final Object value) {
|
||||
if (type == null) return null;
|
||||
final JsonWriter out = writerPool.get().tiny(tiny);
|
||||
factory.loadEncoder(type).convertTo(out, value);
|
||||
return out;
|
||||
final JsonWriter writer = writerPool.get().tiny(tiny);
|
||||
writer.specify(type);
|
||||
factory.loadEncoder(type).convertTo(writer, value);
|
||||
return writer;
|
||||
}
|
||||
|
||||
public JsonWriter convertMapToWriter(final Object... values) {
|
||||
final JsonWriter out = writerPool.get().tiny(tiny);
|
||||
((AnyEncoder) factory.getAnyEncoder()).convertMapTo(out, values);
|
||||
return out;
|
||||
final JsonWriter writer = writerPool.get().tiny(tiny);
|
||||
((AnyEncoder) factory.getAnyEncoder()).convertMapTo(writer, values);
|
||||
return writer;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,4 +85,9 @@ public final class JsonFactory extends ConvertFactory<JsonReader, JsonWriter> {
|
||||
public boolean isReversible() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFieldSort() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -189,11 +189,14 @@ public class JsonReader extends Reader {
|
||||
/**
|
||||
* 判断下一个非空白字符是否为{
|
||||
*
|
||||
* @param member DeMember
|
||||
* @param keydecoder Decodeable
|
||||
*
|
||||
* @return SIGN_NOLENGTH 或 SIGN_NULL
|
||||
*/
|
||||
@Override
|
||||
public final int readMapB() {
|
||||
return readArrayB();
|
||||
public final int readMapB(DeMember member, Decodeable keydecoder) {
|
||||
return readArrayB(member, keydecoder);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -203,10 +206,13 @@ public class JsonReader extends Reader {
|
||||
/**
|
||||
* 判断下一个非空白字符是否为[
|
||||
*
|
||||
* @param member DeMember
|
||||
* @param decoder Decodeable
|
||||
*
|
||||
* @return SIGN_NOLENGTH 或 SIGN_NULL
|
||||
*/
|
||||
@Override
|
||||
public int readArrayB() {
|
||||
public int readArrayB(DeMember member, Decodeable decoder) {
|
||||
char ch = this.text[++this.position];
|
||||
if (ch == '[') return SIGN_NOLENGTH;
|
||||
if (ch == '{') return SIGN_NOLENGTH;
|
||||
@@ -244,13 +250,26 @@ public class JsonReader extends Reader {
|
||||
throw new ConvertException("'" + new String(text) + "'expected a ':' but '" + ch + "'(position = " + position + ") in (" + new String(this.text) + ")");
|
||||
}
|
||||
|
||||
@Override
|
||||
public int position() {
|
||||
return this.position;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int readMemberContentLength(DeMember member, Decodeable decoder) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断对象是否存在下一个属性或者数组是否存在下一个元素
|
||||
*
|
||||
* @param startPosition 起始位置
|
||||
* @param contentLength 内容大小, 不确定的传-1
|
||||
*
|
||||
* @return 是否存在
|
||||
*/
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
public boolean hasNext(int startPosition, int contentLength) {
|
||||
char ch = this.text[++this.position];
|
||||
if (ch == ',') return true;
|
||||
if (ch == '}' || ch == ']') return false;
|
||||
@@ -335,8 +354,16 @@ public class JsonReader extends Reader {
|
||||
if (firstchar > ' ') break;
|
||||
}
|
||||
}
|
||||
boolean quote = false;
|
||||
if (firstchar == '"' || firstchar == '\'') {
|
||||
quote = true;
|
||||
firstchar = text0[++currpos];
|
||||
if (firstchar <= ' ') {
|
||||
for (;;) {
|
||||
firstchar = text0[++currpos];
|
||||
if (firstchar > ' ') break;
|
||||
}
|
||||
}
|
||||
if (firstchar == '"' || firstchar == '\'') {
|
||||
this.position = currpos;
|
||||
return 0;
|
||||
@@ -352,7 +379,8 @@ public class JsonReader extends Reader {
|
||||
if (currpos == eof) break;
|
||||
char ch = text0[++currpos];
|
||||
int val = digits[ch];
|
||||
if (val == -3) break;
|
||||
if (quote && val == -3) continue;
|
||||
if (val <= -3) break;
|
||||
if (val == -1) throw new ConvertException("illegal escape(" + ch + ") (position = " + currpos + ") but '" + ch + "' in (" + new String(this.text) + ")");
|
||||
if (val != -2) value = value * 10 + val;
|
||||
}
|
||||
@@ -377,8 +405,16 @@ public class JsonReader extends Reader {
|
||||
if (firstchar > ' ') break;
|
||||
}
|
||||
}
|
||||
boolean quote = false;
|
||||
if (firstchar == '"' || firstchar == '\'') {
|
||||
quote = true;
|
||||
firstchar = text0[++currpos];
|
||||
if (firstchar <= ' ') {
|
||||
for (;;) {
|
||||
firstchar = text0[++currpos];
|
||||
if (firstchar > ' ') break;
|
||||
}
|
||||
}
|
||||
if (firstchar == '"' || firstchar == '\'') {
|
||||
this.position = currpos;
|
||||
return 0L;
|
||||
@@ -394,7 +430,8 @@ public class JsonReader extends Reader {
|
||||
if (currpos == eof) break;
|
||||
char ch = text0[++currpos];
|
||||
int val = digits[ch];
|
||||
if (val == -3) break;
|
||||
if (quote && val == -3) continue;
|
||||
if (val <= -3) break;
|
||||
if (val == -1) throw new ConvertException("illegal escape(" + ch + ") (position = " + currpos + ") but '" + ch + "' in (" + new String(this.text) + ")");
|
||||
if (val != -2) value = value * 10 + val;
|
||||
}
|
||||
@@ -435,6 +472,41 @@ public class JsonReader extends Reader {
|
||||
return (byte) readInt();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final byte[] readByteArray() {
|
||||
int len = readArrayB(null, null);
|
||||
int contentLength = -1;
|
||||
if (len == Reader.SIGN_NULL) return null;
|
||||
if (len == Reader.SIGN_NOLENBUTBYTES) {
|
||||
contentLength = readMemberContentLength(null, null);
|
||||
len = Reader.SIGN_NOLENGTH;
|
||||
}
|
||||
if (len == Reader.SIGN_NOLENGTH) {
|
||||
int size = 0;
|
||||
byte[] data = new byte[8];
|
||||
int startPosition = position();
|
||||
while (hasNext(startPosition, contentLength)) {
|
||||
if (size >= data.length) {
|
||||
byte[] newdata = new byte[data.length + 4];
|
||||
System.arraycopy(data, 0, newdata, 0, size);
|
||||
data = newdata;
|
||||
}
|
||||
data[size++] = readByte();
|
||||
}
|
||||
readArrayE();
|
||||
byte[] newdata = new byte[size];
|
||||
System.arraycopy(data, 0, newdata, 0, size);
|
||||
return newdata;
|
||||
} else {
|
||||
byte[] values = new byte[len];
|
||||
for (int i = 0; i < values.length; i++) {
|
||||
values[i] = readByte();
|
||||
}
|
||||
readArrayE();
|
||||
return values;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public final char readChar() {
|
||||
return (char) readInt();
|
||||
@@ -448,6 +520,7 @@ public class JsonReader extends Reader {
|
||||
@Override
|
||||
public final float readFloat() {
|
||||
String chars = readSmallString();
|
||||
if (chars != null) chars = chars.trim();
|
||||
if (chars == null || chars.isEmpty()) return 0.f;
|
||||
return Float.parseFloat(chars);
|
||||
}
|
||||
@@ -455,6 +528,7 @@ public class JsonReader extends Reader {
|
||||
@Override
|
||||
public final double readDouble() {
|
||||
String chars = readSmallString();
|
||||
if (chars != null) chars = chars.trim();
|
||||
if (chars == null || chars.isEmpty()) return 0.0;
|
||||
return Double.parseDouble(chars);
|
||||
}
|
||||
@@ -590,7 +664,8 @@ public class JsonReader extends Reader {
|
||||
digits[i] = i - 'A' + 10;
|
||||
}
|
||||
digits['"'] = digits['\''] = -2; //-2 跳过
|
||||
digits[','] = digits['}'] = digits[']'] = digits[' '] = digits['\t'] = digits['\r'] = digits['\n'] = digits[':'] = -3; //-3退出
|
||||
digits[' '] = digits['\t'] = digits['\r'] = digits['\n'] = -3; //-3可能跳过
|
||||
digits[','] = digits['}'] = digits[']'] = digits[':'] = -4; //-4退出
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
package org.redkale.convert.json;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import org.redkale.convert.Writer;
|
||||
import org.redkale.convert.*;
|
||||
import org.redkale.util.*;
|
||||
|
||||
/**
|
||||
@@ -99,6 +99,7 @@ public class JsonWriter extends Writer {
|
||||
|
||||
protected boolean recycle() {
|
||||
this.count = 0;
|
||||
this.specify = null;
|
||||
if (this.content.length > defaultSize) {
|
||||
this.content = new char[defaultSize];
|
||||
}
|
||||
@@ -156,9 +157,9 @@ public class JsonWriter extends Writer {
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void writeFieldName(Attribute attribute) {
|
||||
public final void writeFieldName(EnMember member) {
|
||||
if (this.comma) writeTo(',');
|
||||
writeTo(true, attribute.field());
|
||||
writeTo(true, member.getAttribute().field());
|
||||
writeTo(':');
|
||||
}
|
||||
|
||||
@@ -187,6 +188,22 @@ public class JsonWriter extends Writer {
|
||||
writeInt(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void writeByteArray(byte[] values) {
|
||||
if (values == null) {
|
||||
writeNull();
|
||||
return;
|
||||
}
|
||||
writeArrayB(values.length, null, values);
|
||||
boolean flag = false;
|
||||
for (byte v : values) {
|
||||
if (flag) writeArrayMark();
|
||||
writeByte(v);
|
||||
flag = true;
|
||||
}
|
||||
writeArrayE();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void writeChar(char value) {
|
||||
writeInt(value);
|
||||
@@ -312,9 +329,10 @@ public class JsonWriter extends Writer {
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void writeObjectB(Object obj) {
|
||||
public final int writeObjectB(Object obj) {
|
||||
super.writeObjectB(obj);
|
||||
writeTo('{');
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -328,8 +346,9 @@ public class JsonWriter extends Writer {
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void writeArrayB(int size) {
|
||||
public final int writeArrayB(int size, Encodeable<Writer, Object> encoder, Object obj) {
|
||||
writeTo('[');
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -343,8 +362,9 @@ public class JsonWriter extends Writer {
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void writeMapB(int size) {
|
||||
public final int writeMapB(int size, Encodeable<Writer, Object> keyEncoder, Encodeable<Writer, Object> valueEncoder, Object obj) {
|
||||
writeTo('{');
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -11,7 +11,7 @@ import java.nio.ByteBuffer;
|
||||
import java.nio.channels.*;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import java.util.concurrent.atomic.*;
|
||||
import java.util.function.Consumer;
|
||||
import javax.net.ssl.SSLContext;
|
||||
|
||||
@@ -42,6 +42,9 @@ public abstract class AsyncConnection implements AsynchronousByteChannel, AutoCl
|
||||
|
||||
protected Consumer<AsyncConnection> beforeCloseListener;
|
||||
|
||||
//关联的事件数, 小于1表示没有事件
|
||||
protected final AtomicInteger eventing = new AtomicInteger();
|
||||
|
||||
public final long getLastReadTime() {
|
||||
return readtime;
|
||||
}
|
||||
@@ -50,6 +53,14 @@ public abstract class AsyncConnection implements AsynchronousByteChannel, AutoCl
|
||||
return writetime;
|
||||
}
|
||||
|
||||
public final int increEventing() {
|
||||
return eventing.incrementAndGet();
|
||||
}
|
||||
|
||||
public final int decreEventing() {
|
||||
return eventing.decrementAndGet();
|
||||
}
|
||||
|
||||
public abstract boolean isTCP();
|
||||
|
||||
public abstract boolean shutdownInput();
|
||||
|
||||
@@ -5,10 +5,8 @@
|
||||
*/
|
||||
package org.redkale.net;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.*;
|
||||
import java.nio.*;
|
||||
import java.nio.channels.AsynchronousChannelGroup;
|
||||
import java.nio.charset.*;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.function.*;
|
||||
@@ -145,10 +143,6 @@ public class Context {
|
||||
return executor.submit(r);
|
||||
}
|
||||
|
||||
public AsynchronousChannelGroup createAsynchronousChannelGroup() throws IOException {
|
||||
return AsynchronousChannelGroup.withThreadPool(executor);
|
||||
}
|
||||
|
||||
public void runAsync(Runnable r) {
|
||||
executor.execute(r);
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
*/
|
||||
package org.redkale.net;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.*;
|
||||
import java.nio.channels.*;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
@@ -20,7 +21,7 @@ import org.redkale.util.*;
|
||||
* @author zhangjx
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public final class PrepareRunner implements Runnable {
|
||||
public class PrepareRunner implements Runnable {
|
||||
|
||||
private final AsyncConnection channel;
|
||||
|
||||
@@ -105,4 +106,39 @@ public final class PrepareRunner implements Runnable {
|
||||
}
|
||||
}
|
||||
|
||||
protected void prepare(ByteBuffer buffer, Request request, Response response) throws IOException {
|
||||
context.prepare.prepare(buffer, request, response);
|
||||
}
|
||||
|
||||
protected void initResponse(Response response, AsyncConnection channel) {
|
||||
response.init(channel);
|
||||
}
|
||||
|
||||
protected Response pollResponse() {
|
||||
return context.responsePool.get();
|
||||
}
|
||||
|
||||
protected Request pollRequest(Response response) {
|
||||
return response.request;
|
||||
}
|
||||
|
||||
protected AsyncConnection removeChannel(Response response) {
|
||||
return response.removeChannel();
|
||||
}
|
||||
|
||||
protected ByteBuffer pollReadBuffer(Request request) {
|
||||
return request.pollReadBuffer();
|
||||
}
|
||||
|
||||
protected ByteBuffer pollReadBuffer(Response response) {
|
||||
return response.request.pollReadBuffer();
|
||||
}
|
||||
|
||||
protected void offerReadBuffer(Request request, ByteBuffer buffer) {
|
||||
request.offerReadBuffer(buffer);
|
||||
}
|
||||
|
||||
protected void offerReadBuffer(Response response, ByteBuffer buffer) {
|
||||
response.request.offerReadBuffer(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -210,7 +210,7 @@ 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 final void prepare(final ByteBuffer buffer, final R request, final P response) throws IOException {
|
||||
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) {
|
||||
@@ -218,7 +218,11 @@ public abstract class PrepareServlet<K extends Serializable, C extends Context,
|
||||
if (rs != Integer.MIN_VALUE) illRequestCounter.incrementAndGet();
|
||||
response.finish(true);
|
||||
} else if (rs == 0) {
|
||||
request.offerReadBuffer(buffer);
|
||||
if (buffer.hasRemaining()) {
|
||||
request.setMoredata(buffer);
|
||||
} else {
|
||||
request.offerReadBuffer(buffer);
|
||||
}
|
||||
request.prepare();
|
||||
response.filter = this.headFilter;
|
||||
response.servlet = this;
|
||||
@@ -236,7 +240,11 @@ public abstract class PrepareServlet<K extends Serializable, C extends Context,
|
||||
buffer.clear();
|
||||
request.channel.read(buffer, buffer, this);
|
||||
} else {
|
||||
request.offerReadBuffer(buffer);
|
||||
if (buffer.hasRemaining()) {
|
||||
request.setMoredata(buffer);
|
||||
} else {
|
||||
request.offerReadBuffer(buffer);
|
||||
}
|
||||
request.prepare();
|
||||
try {
|
||||
response.filter = PrepareServlet.this.headFilter;
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -396,7 +396,9 @@ public abstract class Server<K extends Serializable, C extends Context, R extend
|
||||
if (s.endsWith("*")) {
|
||||
File root = new File(s.substring(0, s.length() - 1));
|
||||
if (root.isDirectory()) {
|
||||
for (File f : root.listFiles()) {
|
||||
File[] lfs = root.listFiles();
|
||||
if (lfs == null) throw new RuntimeException("File(" + root + ") cannot listFiles()");
|
||||
for (File f : lfs) {
|
||||
set.add(f.toURI().toURL());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,6 +23,8 @@ import javax.net.ssl.SSLContext;
|
||||
*/
|
||||
public class TcpAioAsyncConnection extends AsyncConnection {
|
||||
|
||||
private final Semaphore semaphore = new Semaphore(1);
|
||||
|
||||
private int readTimeoutSeconds;
|
||||
|
||||
private int writeTimeoutSeconds;
|
||||
@@ -31,6 +33,8 @@ public class TcpAioAsyncConnection extends AsyncConnection {
|
||||
|
||||
private final SocketAddress remoteAddress;
|
||||
|
||||
private BlockingQueue<WriteEntry> writeQueue;
|
||||
|
||||
public TcpAioAsyncConnection(final AsynchronousSocketChannel ch, SSLContext sslContext,
|
||||
final SocketAddress addr0, final int readTimeoutSeconds, final int writeTimeoutSeconds,
|
||||
final AtomicLong livingCounter, final AtomicLong closedCounter) {
|
||||
@@ -102,33 +106,86 @@ public class TcpAioAsyncConnection extends AsyncConnection {
|
||||
channel.read(dst, timeout < 0 ? 0 : timeout, unit, attachment, handler);
|
||||
}
|
||||
|
||||
private <A> void nextWrite(A attachment) {
|
||||
BlockingQueue<WriteEntry> queue = this.writeQueue;
|
||||
WriteEntry entry = queue == null ? null : queue.poll();
|
||||
if (entry != null) {
|
||||
try {
|
||||
if (entry.writeOneBuffer == null) {
|
||||
write(false, entry.writeBuffers, entry.writeOffset, entry.writeLength, entry.writeAttachment, entry.writeHandler);
|
||||
} else {
|
||||
write(false, entry.writeOneBuffer, entry.writeAttachment, entry.writeHandler);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
entry.writeHandler.failed(e, entry.writeAttachment);
|
||||
}
|
||||
} else {
|
||||
semaphore.release();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <A> void write(ByteBuffer src, A attachment, CompletionHandler<Integer, ? super A> handler) {
|
||||
write(true, src, attachment, handler);
|
||||
}
|
||||
|
||||
private <A> void write(boolean acquire, ByteBuffer src, A attachment, CompletionHandler<Integer, ? super A> handler) {
|
||||
if (acquire && !semaphore.tryAcquire()) {
|
||||
if (this.writeQueue == null) {
|
||||
synchronized (semaphore) {
|
||||
if (this.writeQueue == null) {
|
||||
this.writeQueue = new LinkedBlockingDeque<>();
|
||||
}
|
||||
}
|
||||
}
|
||||
this.writeQueue.add(new WriteEntry(src, attachment, handler));
|
||||
return;
|
||||
}
|
||||
WriteOneCompletionHandler newHandler = new WriteOneCompletionHandler(src, handler);
|
||||
if (!channel.isOpen()) {
|
||||
newHandler.failed(new ClosedChannelException(), attachment);
|
||||
return;
|
||||
}
|
||||
this.writetime = System.currentTimeMillis();
|
||||
if (writeTimeoutSeconds > 0) {
|
||||
channel.write(src, writeTimeoutSeconds, TimeUnit.SECONDS, attachment, handler);
|
||||
} else {
|
||||
channel.write(src, attachment, handler);
|
||||
try {
|
||||
if (writeTimeoutSeconds > 0) {
|
||||
channel.write(src, writeTimeoutSeconds, TimeUnit.SECONDS, attachment, newHandler);
|
||||
} else {
|
||||
channel.write(src, attachment, newHandler);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
newHandler.failed(e, attachment);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <A> void write(ByteBuffer[] srcs, int offset, int length, A attachment, final CompletionHandler<Integer, ? super A> handler) {
|
||||
write(true, srcs, offset, length, attachment, handler);
|
||||
}
|
||||
|
||||
private <A> void write(boolean acquire, ByteBuffer[] srcs, int offset, int length, A attachment, final CompletionHandler<Integer, ? super A> handler) {
|
||||
if (acquire && !semaphore.tryAcquire()) {
|
||||
if (this.writeQueue == null) {
|
||||
synchronized (semaphore) {
|
||||
if (this.writeQueue == null) {
|
||||
this.writeQueue = new LinkedBlockingDeque<>();
|
||||
}
|
||||
}
|
||||
}
|
||||
this.writeQueue.add(new WriteEntry(srcs, offset, length, attachment, handler));
|
||||
return;
|
||||
}
|
||||
WriteMoreCompletionHandler newHandler = new WriteMoreCompletionHandler(srcs, offset, length, handler);
|
||||
if (!channel.isOpen()) {
|
||||
newHandler.failed(new ClosedChannelException(), attachment);
|
||||
return;
|
||||
}
|
||||
this.writetime = System.currentTimeMillis();
|
||||
channel.write(srcs, offset, length, writeTimeoutSeconds > 0 ? writeTimeoutSeconds : 60, TimeUnit.SECONDS,
|
||||
attachment, new CompletionHandler<Long, A>() {
|
||||
|
||||
@Override
|
||||
public void completed(Long result, A attachment) {
|
||||
handler.completed(result.intValue(), attachment);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void failed(Throwable exc, A attachment) {
|
||||
handler.failed(exc, attachment);
|
||||
}
|
||||
|
||||
});
|
||||
try {
|
||||
channel.write(srcs, offset, length, writeTimeoutSeconds > 0 ? writeTimeoutSeconds : 60, TimeUnit.SECONDS, attachment, newHandler);
|
||||
} catch (Exception e) {
|
||||
newHandler.failed(e, attachment);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -179,6 +236,17 @@ public class TcpAioAsyncConnection extends AsyncConnection {
|
||||
public final void close() throws IOException {
|
||||
super.close();
|
||||
channel.close();
|
||||
BlockingQueue<WriteEntry> queue = this.writeQueue;
|
||||
if (queue == null) return;
|
||||
WriteEntry entry;
|
||||
Exception ex = null;
|
||||
while ((entry = queue.poll()) != null) {
|
||||
if (ex == null) ex = new ClosedChannelException();
|
||||
try {
|
||||
entry.writeHandler.failed(ex, entry.writeAttachment);
|
||||
} catch (Exception e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -191,4 +259,125 @@ public class TcpAioAsyncConnection extends AsyncConnection {
|
||||
return true;
|
||||
}
|
||||
|
||||
private class WriteMoreCompletionHandler<A> implements CompletionHandler<Long, A> {
|
||||
|
||||
private final CompletionHandler<Integer, A> writeHandler;
|
||||
|
||||
private final ByteBuffer[] writeBuffers;
|
||||
|
||||
private int writeOffset;
|
||||
|
||||
private int writeLength;
|
||||
|
||||
private int writeCount;
|
||||
|
||||
public WriteMoreCompletionHandler(ByteBuffer[] buffers, int offset, int length, CompletionHandler handler) {
|
||||
this.writeBuffers = buffers;
|
||||
this.writeOffset = offset;
|
||||
this.writeLength = length;
|
||||
this.writeHandler = handler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void completed(Long result, A attachment) {
|
||||
if (result >= 0) {
|
||||
writeCount += result;
|
||||
try {
|
||||
int index = -1;
|
||||
for (int i = writeOffset; i < (writeOffset + writeLength); i++) {
|
||||
if (writeBuffers[i].hasRemaining()) {
|
||||
index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (index >= 0) {
|
||||
writeOffset += index;
|
||||
writeLength -= index;
|
||||
channel.write(writeBuffers, writeOffset, writeLength, writeTimeoutSeconds > 0 ? writeTimeoutSeconds : 60, TimeUnit.SECONDS, attachment, this);
|
||||
return;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
failed(e, attachment);
|
||||
return;
|
||||
}
|
||||
nextWrite(attachment);
|
||||
writeHandler.completed(writeCount, attachment);
|
||||
} else {
|
||||
nextWrite(attachment);
|
||||
writeHandler.completed(result.intValue(), attachment);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void failed(Throwable exc, A attachment) {
|
||||
nextWrite(attachment);
|
||||
writeHandler.failed(exc, attachment);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private class WriteOneCompletionHandler<A> implements CompletionHandler<Integer, A> {
|
||||
|
||||
private final CompletionHandler writeHandler;
|
||||
|
||||
private final ByteBuffer writeOneBuffer;
|
||||
|
||||
public WriteOneCompletionHandler(ByteBuffer buffer, CompletionHandler handler) {
|
||||
this.writeOneBuffer = buffer;
|
||||
this.writeHandler = handler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void completed(Integer result, A attachment) {
|
||||
try {
|
||||
if (writeOneBuffer.hasRemaining()) {
|
||||
channel.write(writeOneBuffer, attachment, this);
|
||||
return;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
failed(e, attachment);
|
||||
return;
|
||||
}
|
||||
nextWrite(attachment);
|
||||
writeHandler.completed(result, attachment);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void failed(Throwable exc, A attachment) {
|
||||
nextWrite(attachment);
|
||||
writeHandler.failed(exc, attachment);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class WriteEntry {
|
||||
|
||||
ByteBuffer writeOneBuffer;
|
||||
|
||||
ByteBuffer[] writeBuffers;
|
||||
|
||||
int writingCount;
|
||||
|
||||
int writeOffset;
|
||||
|
||||
int writeLength;
|
||||
|
||||
Object writeAttachment;
|
||||
|
||||
CompletionHandler writeHandler;
|
||||
|
||||
public WriteEntry(ByteBuffer writeOneBuffer, Object writeAttachment, CompletionHandler writeHandler) {
|
||||
this.writeOneBuffer = writeOneBuffer;
|
||||
this.writeAttachment = writeAttachment;
|
||||
this.writeHandler = writeHandler;
|
||||
}
|
||||
|
||||
public WriteEntry(ByteBuffer[] writeBuffers, int writeOffset, int writeLength, Object writeAttachment, CompletionHandler writeHandler) {
|
||||
this.writeBuffers = writeBuffers;
|
||||
this.writeOffset = writeOffset;
|
||||
this.writeLength = writeLength;
|
||||
this.writeAttachment = writeAttachment;
|
||||
this.writeHandler = writeHandler;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -22,17 +22,17 @@ import java.util.concurrent.atomic.AtomicLong;
|
||||
*/
|
||||
public class TcpNioAsyncConnection extends AsyncConnection {
|
||||
|
||||
private int readTimeoutSeconds;
|
||||
protected int readTimeoutSeconds;
|
||||
|
||||
private int writeTimeoutSeconds;
|
||||
protected int writeTimeoutSeconds;
|
||||
|
||||
private final Selector selector;
|
||||
protected final Selector selector;
|
||||
|
||||
private SelectionKey key;
|
||||
protected SelectionKey key;
|
||||
|
||||
private final SocketChannel channel;
|
||||
protected final SocketChannel channel;
|
||||
|
||||
private final SocketAddress remoteAddress;
|
||||
protected final SocketAddress remoteAddress;
|
||||
|
||||
ByteBuffer readBuffer;
|
||||
|
||||
@@ -362,4 +362,5 @@ public class TcpNioAsyncConnection extends AsyncConnection {
|
||||
public final boolean isTCP() {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ import java.net.*;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.*;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.*;
|
||||
import org.redkale.util.AnyValue;
|
||||
|
||||
/**
|
||||
@@ -27,9 +27,9 @@ public class TcpNioProtocolServer extends ProtocolServer {
|
||||
|
||||
private ServerSocketChannel serverChannel;
|
||||
|
||||
private NIOThreadWorker[] workers;
|
||||
private NioThreadWorker[] workers;
|
||||
|
||||
private NIOThreadWorker currWorker;
|
||||
private NioThreadWorker currWorker;
|
||||
|
||||
private boolean running;
|
||||
|
||||
@@ -82,11 +82,11 @@ public class TcpNioProtocolServer extends ProtocolServer {
|
||||
@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()];
|
||||
this.workers = new NioThreadWorker[Runtime.getRuntime().availableProcessors()];
|
||||
final CountDownLatch wkcdl = new CountDownLatch(workers.length);
|
||||
for (int i = 0; i < workers.length; i++) {
|
||||
workers[i] = new NIOThreadWorker();
|
||||
workers[i] = new NioThreadWorker(wkcdl, i + 1, workers.length);
|
||||
workers[i].setDaemon(true);
|
||||
workers[i].start();
|
||||
}
|
||||
@@ -95,6 +95,12 @@ public class TcpNioProtocolServer extends ProtocolServer {
|
||||
}
|
||||
workers[workers.length - 1].next = workers[0];
|
||||
currWorker = workers[0];
|
||||
try {
|
||||
wkcdl.await(3, TimeUnit.SECONDS);
|
||||
} catch (Exception e) {
|
||||
throw new IOException(e);
|
||||
}
|
||||
final CountDownLatch cdl = new CountDownLatch(1);
|
||||
new Thread() {
|
||||
@Override
|
||||
public void run() {
|
||||
@@ -111,12 +117,6 @@ public class TcpNioProtocolServer extends ProtocolServer {
|
||||
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);
|
||||
@@ -134,45 +134,91 @@ public class TcpNioProtocolServer extends ProtocolServer {
|
||||
}
|
||||
}.start();
|
||||
try {
|
||||
cdl.await();
|
||||
cdl.await(3, TimeUnit.SECONDS);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
throw new IOException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
if (!this.running) return;
|
||||
this.running = false;
|
||||
serverChannel.close();
|
||||
acceptSelector.close();
|
||||
for (NIOThreadWorker worker : workers) {
|
||||
for (NioThreadWorker worker : workers) {
|
||||
worker.interrupt();
|
||||
}
|
||||
this.running = false;
|
||||
}
|
||||
|
||||
class NIOThreadWorker extends Thread {
|
||||
class NioThreadWorker extends Thread {
|
||||
|
||||
final Selector selector;
|
||||
|
||||
NIOThreadWorker next;
|
||||
final CountDownLatch cdl;
|
||||
|
||||
public NIOThreadWorker() {
|
||||
private final Queue<TcpNioAsyncConnection> connected;
|
||||
|
||||
private final CopyOnWriteArrayList<TcpNioAsyncConnection> done;
|
||||
|
||||
protected volatile Thread ownerThread;
|
||||
|
||||
NioThreadWorker next;
|
||||
|
||||
public NioThreadWorker(final CountDownLatch cdl, int idx, int count) {
|
||||
this.cdl = cdl;
|
||||
String idxstr = "000000" + idx;
|
||||
this.setName("NioThreadWorker:" + context.getServerAddress().getPort() + "-" + idxstr.substring(idxstr.length() - ("" + count).length()));
|
||||
try {
|
||||
this.selector = Selector.open();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
this.connected = new ArrayBlockingQueue<>(1000000);
|
||||
this.done = new CopyOnWriteArrayList<>();
|
||||
}
|
||||
|
||||
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));
|
||||
public boolean addChannel(SocketChannel channel) throws IOException {
|
||||
TcpNioAsyncConnection conn = new TcpNioAsyncConnection(channel, null, selector, context.readTimeoutSeconds, context.writeTimeoutSeconds, null, null);
|
||||
return connected.add(conn);
|
||||
}
|
||||
|
||||
protected void processConnected() {
|
||||
TcpNioAsyncConnection schannel;
|
||||
try {
|
||||
while ((schannel = connected.poll()) != null) {
|
||||
SocketChannel channel = schannel.channel;
|
||||
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);
|
||||
channel.register(selector, SelectionKey.OP_READ).attach(schannel);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
// do nothing
|
||||
}
|
||||
synchronized (done) {
|
||||
for (TcpNioAsyncConnection conn : done) {
|
||||
if (conn.key != null && conn.key.isValid()) {
|
||||
conn.key.interestOps(SelectionKey.OP_WRITE);
|
||||
}
|
||||
}
|
||||
done.clear();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isSameThread() {
|
||||
return this.ownerThread == Thread.currentThread();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
this.ownerThread = Thread.currentThread();
|
||||
if (cdl != null) cdl.countDown();
|
||||
while (running) {
|
||||
processConnected();
|
||||
try {
|
||||
selector.select(50);
|
||||
} catch (IOException e) {
|
||||
@@ -207,13 +253,28 @@ public class TcpNioProtocolServer extends ProtocolServer {
|
||||
return;
|
||||
}
|
||||
if (conn == null) return;
|
||||
if (key.isWritable()) {
|
||||
if (conn.writeHandler != null) writeOP(key, socket, conn);
|
||||
} else if (key.isReadable()) {
|
||||
if (key.isReadable()) {
|
||||
if (conn.readHandler != null) readOP(key, socket, conn);
|
||||
} else if (key.isWritable()) {
|
||||
if (conn.writeHandler != null) writeOP(key, socket, conn);
|
||||
}
|
||||
}
|
||||
|
||||
private void closeOP(SelectionKey key) {
|
||||
if (key == null) return;
|
||||
TcpNioAsyncConnection conn = (TcpNioAsyncConnection) key.attachment();
|
||||
try {
|
||||
if (key.isValid()) {
|
||||
SocketChannel socketChannel = (SocketChannel) key.channel();
|
||||
socketChannel.close();
|
||||
key.attach(null);
|
||||
key.cancel();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
}
|
||||
conn.dispose();
|
||||
}
|
||||
|
||||
private void readOP(SelectionKey key, SocketChannel socket, TcpNioAsyncConnection conn) {
|
||||
final CompletionHandler handler = conn.removeReadHandler();
|
||||
final ByteBuffer buffer = conn.removeReadBuffer();
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -27,7 +27,6 @@ import org.redkale.util.*;
|
||||
import org.redkale.source.Flipper;
|
||||
|
||||
/**
|
||||
* 以find开头的方法且参数只有一个且参数类型为primitive class或String,则RestParam值默认为#
|
||||
* <p>
|
||||
* 详情见: https://redkale.org
|
||||
*
|
||||
@@ -40,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
|
||||
@@ -128,22 +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 (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());
|
||||
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());
|
||||
}
|
||||
types.add(rc.type());
|
||||
childFactory.tiny(rc.tiny());
|
||||
}
|
||||
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());
|
||||
}
|
||||
}
|
||||
for (Class type : reloadTypes) {
|
||||
childFactory.reloadCoder(type);
|
||||
}
|
||||
return childFactory.getConvert();
|
||||
}
|
||||
@@ -237,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()) {
|
||||
@@ -401,6 +429,7 @@ public final class Rest {
|
||||
Set<String> paramnames = new HashSet<>();
|
||||
String methodesc = method.getName() + ":" + Type.getMethodDescriptor(method);
|
||||
List<String> names = asmParamMap.get(methodesc);
|
||||
if (names != null) while (names.remove(" ")); //删掉空元素
|
||||
Parameter[] params = method.getParameters();
|
||||
final LinkedHashMap<String, Parameter> paramap = new LinkedHashMap(); //必须使用LinkedHashMap确保顺序
|
||||
for (int j = 0; j < params.length; j++) { //字段列表
|
||||
@@ -673,7 +702,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('.', '/');
|
||||
@@ -690,6 +720,9 @@ 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);
|
||||
@@ -747,13 +780,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,设置一个默认值
|
||||
@@ -771,9 +803,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);
|
||||
@@ -809,7 +842,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);
|
||||
@@ -832,6 +871,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);
|
||||
@@ -843,10 +886,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;
|
||||
@@ -856,10 +911,13 @@ 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, "(" + reqDesc + respDesc + ")V", null, new String[]{"java/io/IOException"}));
|
||||
//mv.setDebug(true);
|
||||
mv = new MethodDebugVisitor(cw.visitMethod(ACC_PUBLIC, entry.newMethodName, "(" + reqDesc + respDesc + ")V", null, new String[]{"java/io/IOException"}));
|
||||
//mv.setDebug(true);
|
||||
mv.debugLine();
|
||||
|
||||
mv.visitVarInsn(ALOAD, 0);
|
||||
@@ -891,6 +949,7 @@ public final class Rest {
|
||||
int maxLocals = 4;
|
||||
|
||||
List<String> asmParamNames = asmParamMap == null ? null : asmParamMap.get(method.getName() + ":" + Type.getMethodDescriptor(method));
|
||||
if (asmParamNames != null) while (asmParamNames.remove(" ")); //删掉空元素
|
||||
List<Object[]> paramlist = new ArrayList<>();
|
||||
//解析方法中的每个参数
|
||||
for (int i = 0; i < params.length; i++) {
|
||||
@@ -983,10 +1042,6 @@ public final class Rest {
|
||||
throw new RuntimeException("Parameter " + param.getName() + " not found name by @RestParam in " + method);
|
||||
}
|
||||
}
|
||||
if (annhead == null && anncookie == null && annaddr == null && annbody == null && annfile == null
|
||||
&& (entry.name.startsWith("find") || entry.name.startsWith("delete")) && params.length == 1) {
|
||||
if (ptype.isPrimitive() || ptype == String.class) n = "#";
|
||||
}
|
||||
if (annhead == null && anncookie == null && annsid == null && annaddr == null && annbody == null && annfile == null
|
||||
&& !ptype.isPrimitive() && ptype != String.class && ptype != Flipper.class && !CompletionHandler.class.isAssignableFrom(ptype)
|
||||
&& !ptype.getName().startsWith("java") && n.charAt(0) != '#' && !"&".equals(n)) { //判断Json对象是否包含@RestUploadFile
|
||||
@@ -1042,6 +1097,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 +1751,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,19 +1859,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());
|
||||
@@ -1739,7 +1891,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);
|
||||
@@ -1747,8 +1901,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);
|
||||
}
|
||||
}
|
||||
@@ -1790,13 +1954,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 {
|
||||
@@ -1821,7 +1998,7 @@ public final class Rest {
|
||||
int pos = t.indexOf(defmodulename);
|
||||
n = pos > 0 ? t.substring(0, pos) : t;
|
||||
}
|
||||
this.name = n;
|
||||
this.name = n.trim();
|
||||
this.mappingMethod = method;
|
||||
this.methods = mapping.methods();
|
||||
this.auth = mapping.auth();
|
||||
@@ -1837,13 +2014,9 @@ public final class Rest {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!pound && params.length == 1) {
|
||||
Class ptype = method.getParameterTypes()[0];
|
||||
if (this.name.startsWith("find") || this.name.startsWith("delete")) {
|
||||
if (ptype.isPrimitive() || ptype == String.class) pound = true;
|
||||
}
|
||||
}
|
||||
this.existsPound = pound;
|
||||
this.newMethodName = this.name.replace('/', '$').replace('.', '_');
|
||||
this.newActionClassName = "_Dyn_" + this.newMethodName + "_ActionHttpServlet";
|
||||
}
|
||||
|
||||
public final int methodidx; // _paramtypes 的下标,从0开始
|
||||
@@ -1852,6 +2025,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;
|
||||
@@ -1866,6 +2043,8 @@ public final class Rest {
|
||||
|
||||
public final boolean existsPound; //是否包含#的参数
|
||||
|
||||
String mappingurl; //在生成方法时赋值, 供_createRestInnerActionEntry使用
|
||||
|
||||
@RestMapping()
|
||||
void mapping() { //用于获取Mapping 默认值
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||
|
||||
/**
|
||||
* 只能依附在Service实现类的public方法上, 当方法的返回值以JSON输出时对指定类型的转换设定。 <br>
|
||||
* 注意: 如果 type() == void.class 则无视其他参数固定返回 JsonFactory.create().skipAllIgnore(true).getConvert();
|
||||
*
|
||||
* <p>
|
||||
* 详情见: https://redkale.org
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
@@ -32,7 +32,6 @@ public @interface RestParam {
|
||||
* name='&'表示当前用户; <br>
|
||||
* name='#'表示截取uri最后一段; <br>
|
||||
* name='#xxx:'表示从uri中/pipes/xxx:v/截取xxx:的值 <br>
|
||||
* 若方法名以find、delete开头且方法的参数只有一个且参数类型是基本数据类型或String,则默认值为"#" <br>
|
||||
*
|
||||
* @return String
|
||||
*/
|
||||
|
||||
@@ -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节点地址
|
||||
@@ -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连接成功后的回调方法
|
||||
*/
|
||||
@@ -802,7 +854,10 @@ public abstract class WebSocket<G extends Serializable, T> {
|
||||
* 显式地关闭WebSocket
|
||||
*/
|
||||
public final void close() {
|
||||
if (this._runner != null) this._runner.closeRunner(CLOSECODE_FORCED, "user close");
|
||||
if (this._runner != null) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -144,12 +144,12 @@ public class WebSocketEngine {
|
||||
}
|
||||
|
||||
@Comment("从WebSocketEngine删除指定WebSocket")
|
||||
void removeThenClose(WebSocket socket) {
|
||||
CompletableFuture<Void> removeThenClose(WebSocket socket) {
|
||||
Serializable userid = socket._userid;
|
||||
if (single) {
|
||||
currconns.decrementAndGet();
|
||||
websockets.remove(userid);
|
||||
if (node != null) node.disconnect(userid);
|
||||
if (node != null) return node.disconnect(userid);
|
||||
} else { //非线程安全, 在常规场景中无需锁
|
||||
List<WebSocket> list = websockets2.get(userid);
|
||||
if (list != null) {
|
||||
@@ -157,10 +157,11 @@ public class WebSocketEngine {
|
||||
list.remove(socket);
|
||||
if (list.isEmpty()) {
|
||||
websockets2.remove(userid);
|
||||
if (node != null) node.disconnect(userid);
|
||||
if (node != null) return node.disconnect(userid);
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Comment("更改WebSocket的userid")
|
||||
@@ -242,7 +243,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;
|
||||
@@ -301,7 +302,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;
|
||||
@@ -324,6 +325,54 @@ public class WebSocketEngine {
|
||||
}
|
||||
}
|
||||
|
||||
@Comment("给指定WebSocket连接用户发起操作指令")
|
||||
public CompletableFuture<Integer> broadcastAction(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> sendAction(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 sendAction(action, ss);
|
||||
}
|
||||
|
||||
@Comment("给指定用户组发送操作")
|
||||
public CompletableFuture<Integer> sendAction(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;
|
||||
|
||||
@@ -60,7 +60,9 @@ public abstract class WebSocketNode {
|
||||
protected Semaphore semaphore;
|
||||
|
||||
public void init(AnyValue conf) {
|
||||
if (sncpNodeAddresses != null) sncpNodeAddresses.initValueType(InetSocketAddress.class);
|
||||
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;
|
||||
@@ -80,9 +82,9 @@ public abstract class WebSocketNode {
|
||||
public final void postDestroy(AnyValue conf) {
|
||||
if (this.localEngine == null) return;
|
||||
//关掉所有本地本地WebSocket
|
||||
this.localEngine.getLocalWebSockets().forEach(g -> disconnect(g.getUserid()).join());
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,13 +94,17 @@ public abstract class WebSocketNode {
|
||||
|
||||
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 userid);
|
||||
|
||||
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<Integer> forceCloseWebSocket(Serializable userid, @RpcTargetAddress InetSocketAddress targetAddress);
|
||||
|
||||
//--------------------------------------------------------------------------------
|
||||
final CompletableFuture<Void> connect(final Serializable userid) {
|
||||
@@ -147,7 +153,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;
|
||||
}
|
||||
@@ -221,7 +227,7 @@ public abstract class WebSocketNode {
|
||||
* @return int
|
||||
*/
|
||||
@Local
|
||||
public final CompletableFuture<Integer> forceCloseWebSocket(final Serializable userid) {
|
||||
public CompletableFuture<Integer> forceCloseWebSocket(final Serializable userid) {
|
||||
CompletableFuture<Integer> localFuture = null;
|
||||
if (this.localEngine != null) localFuture = CompletableFuture.completedFuture(localEngine.forceCloseLocalWebSocket(userid));
|
||||
if (this.sncpNodeAddresses == null || this.remoteNode == null) {
|
||||
@@ -231,7 +237,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 +250,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);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------
|
||||
@@ -380,7 +386,7 @@ public abstract class WebSocketNode {
|
||||
* @return 为0表示成功, 其他值表示部分发送异常
|
||||
*/
|
||||
@Local
|
||||
public final CompletableFuture<Integer> sendMessage(final Convert convert, final Object message0, final boolean last, final Serializable... userids) {
|
||||
public CompletableFuture<Integer> sendMessage(final Convert convert, final Object message0, final boolean last, final Serializable... userids) {
|
||||
if (userids == null || userids.length < 1) return CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY);
|
||||
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));
|
||||
@@ -500,7 +506,7 @@ public abstract class WebSocketNode {
|
||||
* @return 为0表示成功, 其他值表示部分发送异常
|
||||
*/
|
||||
@Local
|
||||
public final CompletableFuture<Integer> broadcastMessage(final WebSocketRange wsrange, final Convert convert, final Object message0, final boolean last) {
|
||||
public CompletableFuture<Integer> broadcastMessage(final WebSocketRange wsrange, final Convert convert, final Object message0, final boolean last) {
|
||||
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) { //本地模式且没有分布式
|
||||
@@ -509,7 +515,7 @@ public abstract class WebSocketNode {
|
||||
final Object remoteMessage = formatRemoteMessage(message);
|
||||
CompletableFuture<Integer> localFuture = this.localEngine == null ? null : this.localEngine.broadcastMessage(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,7 +531,7 @@ public abstract class WebSocketNode {
|
||||
return localFuture == null ? remoteFuture : localFuture.thenCombine(remoteFuture, (a, b) -> a | b);
|
||||
}
|
||||
|
||||
private CompletableFuture<Integer> sendOneMessage(final Object message, final boolean last, final Serializable userid) {
|
||||
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));
|
||||
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");
|
||||
@@ -540,7 +546,7 @@ public abstract class WebSocketNode {
|
||||
//远程节点发送消息
|
||||
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()) {
|
||||
@@ -559,7 +565,91 @@ public abstract class WebSocketNode {
|
||||
return localFuture == null ? remoteFuture : localFuture.thenCombine(remoteFuture, (a, b) -> a | b);
|
||||
}
|
||||
|
||||
private Object formatRemoteMessage(Object message) {
|
||||
/**
|
||||
* 广播操作, 给所有人发操作
|
||||
*
|
||||
* @param action 操作参数
|
||||
*
|
||||
* @return 为0表示成功, 其他值表示部分发送异常
|
||||
*/
|
||||
@Local
|
||||
public CompletableFuture<Integer> broadcastAction(final WebSocketAction action) {
|
||||
if (this.localEngine != null && this.sncpNodeAddresses == null) { //本地模式且没有分布式
|
||||
return this.localEngine.broadcastAction(action);
|
||||
}
|
||||
CompletableFuture<Integer> localFuture = this.localEngine == null ? null : this.localEngine.broadcastAction(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.sendAction(action, userids);
|
||||
}
|
||||
CompletableFuture<Integer> future = null;
|
||||
for (Serializable userid : userids) {
|
||||
future = future == null ? sendOneAction(action, userid) : future.thenCombine(sendOneAction(action, userid), (a, b) -> a | b);
|
||||
}
|
||||
return future == null ? CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY) : future;
|
||||
}
|
||||
|
||||
protected CompletableFuture<Integer> sendOneAction(final WebSocketAction action, final Serializable userid) {
|
||||
if (logger.isLoggable(Level.FINEST)) {
|
||||
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.sendAction(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;
|
||||
}
|
||||
//远程节点发送操作
|
||||
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.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 Object formatRemoteMessage(Object message) {
|
||||
if (message instanceof WebSocketPacket) return message;
|
||||
if (message instanceof byte[]) return message;
|
||||
if (message instanceof CharSequence) return message;
|
||||
|
||||
@@ -13,7 +13,6 @@ import java.nio.channels.*;
|
||||
import java.util.*;
|
||||
import java.util.AbstractMap.SimpleEntry;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.logging.*;
|
||||
|
||||
@@ -39,10 +38,6 @@ class WebSocketRunner implements Runnable {
|
||||
|
||||
volatile boolean closed = false;
|
||||
|
||||
private final AtomicBoolean writing = new AtomicBoolean();
|
||||
|
||||
private final BlockingQueue<QueueEntry> writeQueue = new ArrayBlockingQueue(512);
|
||||
|
||||
private final BiConsumer<WebSocket, Object> restMessageConsumer; //主要供RestWebSocket使用
|
||||
|
||||
protected long lastSendTime;
|
||||
@@ -79,8 +74,8 @@ class WebSocketRunner implements Runnable {
|
||||
@Override
|
||||
public void completed(Integer count, Void attachment1) {
|
||||
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);
|
||||
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(CLOSECODE_ILLPACKET, "read buffer count is " + count);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
@@ -187,17 +182,17 @@ class WebSocketRunner implements Runnable {
|
||||
}
|
||||
} 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");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -205,9 +200,9 @@ class WebSocketRunner implements Runnable {
|
||||
public void failed(Throwable exc, Void 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");
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -217,7 +212,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");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -228,12 +223,6 @@ class WebSocketRunner implements Runnable {
|
||||
//System.out.println("推送消息");
|
||||
final CompletableFuture<Integer> futureResult = new CompletableFuture<>();
|
||||
try {
|
||||
synchronized (writing) {
|
||||
if (writing.getAndSet(true)) {
|
||||
writeQueue.add(new QueueEntry(futureResult, packet));
|
||||
return futureResult;
|
||||
}
|
||||
}
|
||||
ByteBuffer[] buffers = packet.sendBuffers != null ? packet.duplicateSendBuffers() : packet.encode(this.context.getBufferSupplier(), this.context.getBufferConsumer(), webSocket._engine.cryptor);
|
||||
//if (debug) context.getLogger().log(Level.FINEST, "wsrunner.sending websocket message: " + packet);
|
||||
|
||||
@@ -277,18 +266,6 @@ class WebSocketRunner implements Runnable {
|
||||
}
|
||||
}
|
||||
}
|
||||
QueueEntry entry = null;
|
||||
synchronized (writing) {
|
||||
entry = writeQueue.poll();
|
||||
if (entry == null) writing.set(false);
|
||||
}
|
||||
if (entry != null) {
|
||||
future = entry.future;
|
||||
ByteBuffer[] buffers = entry.packet.sendBuffers != null ? entry.packet.duplicateSendBuffers() : entry.packet.encode(context.getBufferSupplier(), context.getBufferConsumer(), webSocket._engine.cryptor);
|
||||
lastSendTime = System.currentTimeMillis();
|
||||
//if (debug) context.getLogger().log(Level.FINEST, "wsrunner.sending websocket message: " + entry.packet);
|
||||
channel.write(buffers, buffers, this);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
future.complete(RETCODE_SENDEXCEPTION);
|
||||
closeRunner(RETCODE_SENDEXCEPTION, "websocket send message failed on rewrite");
|
||||
@@ -298,7 +275,6 @@ class WebSocketRunner implements Runnable {
|
||||
|
||||
@Override
|
||||
public void failed(Throwable exc, ByteBuffer[] attachments) {
|
||||
writing.set(false);
|
||||
future.complete(RETCODE_SENDEXCEPTION);
|
||||
closeRunner(RETCODE_SENDEXCEPTION, "websocket send message failed on CompletionHandler");
|
||||
if (exc != null) {
|
||||
@@ -308,7 +284,6 @@ class WebSocketRunner implements Runnable {
|
||||
}
|
||||
});
|
||||
} catch (Exception t) {
|
||||
writing.set(false);
|
||||
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);
|
||||
@@ -321,21 +296,17 @@ class WebSocketRunner implements Runnable {
|
||||
return closed;
|
||||
}
|
||||
|
||||
public void closeRunner(int code, String reason) {
|
||||
if (closed) return;
|
||||
public CompletableFuture<Void> closeRunner(int code, String reason) {
|
||||
if (closed) return null;
|
||||
synchronized (this) {
|
||||
if (closed) return;
|
||||
if (closed) return null;
|
||||
closed = true;
|
||||
channel.dispose();
|
||||
context.offerBuffer(readBuffer);
|
||||
readBuffer = null;
|
||||
engine.removeThenClose(webSocket);
|
||||
CompletableFuture<Void> future = engine.removeThenClose(webSocket);
|
||||
webSocket.onClose(code, reason);
|
||||
QueueEntry entry = writeQueue.poll();
|
||||
while (entry != null) {
|
||||
entry.future.complete(RETCODE_WSOCKET_CLOSED);
|
||||
entry = writeQueue.poll();
|
||||
}
|
||||
return future;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -330,9 +330,11 @@ public final class SncpClient {
|
||||
attr.set(params[i - 1], bsonConvert.convertFrom(attr.type(), reader));
|
||||
}
|
||||
return bsonConvert.convertFrom(action.handlerFuncParamIndex >= 0 ? Object.class : action.resultTypes, reader);
|
||||
} catch (RpcRemoteException re) {
|
||||
throw re;
|
||||
} catch (InterruptedException | ExecutionException | TimeoutException e) {
|
||||
logger.log(Level.SEVERE, actions[index].method + " sncp (params: " + jsonConvert.convertTo(params) + ") remote error", e);
|
||||
throw new RuntimeException(actions[index].method + " sncp remote error", e);
|
||||
//logger.log(Level.SEVERE, actions[index].method + " sncp (params: " + jsonConvert.convertTo(params) + ") remote error", e);
|
||||
throw new RpcRemoteException(actions[index].method + " sncp remote error", e);
|
||||
} finally {
|
||||
bsonConvert.offerBsonReader(reader);
|
||||
}
|
||||
@@ -403,7 +405,7 @@ public final class SncpClient {
|
||||
public void completed(Integer count, Void attachment2) {
|
||||
try {
|
||||
if (count < 1 && buffer.remaining() == buffer.limit()) { //没有数据可读
|
||||
future.completeExceptionally(new RuntimeException(action.method + " sncp[" + conn.getRemoteAddress() + "] remote no response data"));
|
||||
future.completeExceptionally(new RpcRemoteException(action.method + " sncp[" + conn.getRemoteAddress() + "] remote no response data"));
|
||||
transport.offerBuffer(buffer);
|
||||
transport.offerConnection(true, conn);
|
||||
return;
|
||||
|
||||
@@ -53,6 +53,7 @@ public final class SncpRequest extends Request<SncpContext> {
|
||||
@Override
|
||||
protected int readHeader(ByteBuffer buffer) {
|
||||
if (buffer.remaining() < HEADER_SIZE) {
|
||||
if (buffer.hasRemaining()) buffer.get(new byte[buffer.remaining()]);
|
||||
this.ping = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -129,7 +129,7 @@ public class RetResult<T> {
|
||||
*/
|
||||
public RetResult<T> attach(String key, Object value) {
|
||||
if (this.attach == null) this.attach = new HashMap<>();
|
||||
boolean canstr = value != null && (value instanceof CharSequence || value.getClass().isPrimitive());
|
||||
boolean canstr = value != null && (value instanceof CharSequence || value instanceof Number || value.getClass().isPrimitive());
|
||||
this.attach.put(key, value == null ? null : (canstr ? String.valueOf(value) : JsonConvert.root().convertTo(value)));
|
||||
return this;
|
||||
}
|
||||
|
||||
33
src/org/redkale/service/RpcRemoteException.java
Normal file
33
src/org/redkale/service/RpcRemoteException.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.service;
|
||||
|
||||
/**
|
||||
* 供RPC协议使用
|
||||
*
|
||||
* <p>
|
||||
* 详情见: https://redkale.org
|
||||
*
|
||||
* @author zhangjx
|
||||
*/
|
||||
public class RpcRemoteException extends RuntimeException {
|
||||
|
||||
public RpcRemoteException() {
|
||||
super();
|
||||
}
|
||||
|
||||
public RpcRemoteException(String s) {
|
||||
super(s);
|
||||
}
|
||||
|
||||
public RpcRemoteException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public RpcRemoteException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
}
|
||||
@@ -56,17 +56,29 @@ public class WebSocketNodeService extends WebSocketNode implements Service {
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Integer> sendMessage(@RpcTargetAddress InetSocketAddress addr, Object message, boolean last, Serializable userid) {
|
||||
public CompletableFuture<Integer> sendMessage(@RpcTargetAddress InetSocketAddress targetAddress, Object message, boolean last, Serializable userid) {
|
||||
if (this.localEngine == null) return CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY);
|
||||
return this.localEngine.sendMessage(message, last, userid);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Integer> broadcastMessage(@RpcTargetAddress InetSocketAddress addr, final WebSocketRange wsrange, Object message, boolean last) {
|
||||
public CompletableFuture<Integer> broadcastMessage(@RpcTargetAddress InetSocketAddress targetAddress, final WebSocketRange wsrange, Object message, boolean last) {
|
||||
if (this.localEngine == null) return CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY);
|
||||
return this.localEngine.broadcastMessage(wsrange, message, last);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Integer> sendAction(@RpcTargetAddress InetSocketAddress targetAddress, final WebSocketAction action, Serializable userid) {
|
||||
if (this.localEngine == null) return CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY);
|
||||
return this.localEngine.sendAction(action, userid);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Integer> broadcastAction(@RpcTargetAddress InetSocketAddress targetAddress, final WebSocketAction action) {
|
||||
if (this.localEngine == null) return CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY);
|
||||
return this.localEngine.broadcastAction(action);
|
||||
}
|
||||
|
||||
/**
|
||||
* 当用户连接到节点,需要更新到CacheSource
|
||||
*
|
||||
@@ -78,8 +90,8 @@ public class WebSocketNodeService extends WebSocketNode implements Service {
|
||||
@Override
|
||||
public CompletableFuture<Void> connect(Serializable userid, InetSocketAddress sncpAddr) {
|
||||
tryAcquireSemaphore();
|
||||
CompletableFuture<Void> future = sncpNodeAddresses.appendSetItemAsync(SOURCE_SNCP_USERID_PREFIX + userid, sncpAddr);
|
||||
future = future.thenAccept((a) -> sncpNodeAddresses.appendSetItemAsync(SOURCE_SNCP_ADDRS_KEY, sncpAddr));
|
||||
CompletableFuture<Void> future = sncpNodeAddresses.appendSetItemAsync(SOURCE_SNCP_USERID_PREFIX + userid, InetSocketAddress.class, sncpAddr);
|
||||
future = future.thenAccept((a) -> sncpNodeAddresses.appendSetItemAsync(SOURCE_SNCP_ADDRS_KEY, InetSocketAddress.class, sncpAddr));
|
||||
if (semaphore != null) future.whenComplete((r, e) -> releaseSemaphore());
|
||||
if (logger.isLoggable(Level.FINEST)) logger.finest(WebSocketNodeService.class.getSimpleName() + ".event: " + userid + " connect from " + sncpAddr);
|
||||
return future;
|
||||
@@ -96,7 +108,7 @@ public class WebSocketNodeService extends WebSocketNode implements Service {
|
||||
@Override
|
||||
public CompletableFuture<Void> disconnect(Serializable userid, InetSocketAddress sncpAddr) {
|
||||
tryAcquireSemaphore();
|
||||
CompletableFuture<Void> future = sncpNodeAddresses.removeSetItemAsync(SOURCE_SNCP_USERID_PREFIX + userid, sncpAddr);
|
||||
CompletableFuture<Void> future = sncpNodeAddresses.removeSetItemAsync(SOURCE_SNCP_USERID_PREFIX + userid, InetSocketAddress.class, sncpAddr);
|
||||
if (semaphore != null) future.whenComplete((r, e) -> releaseSemaphore());
|
||||
if (logger.isLoggable(Level.FINEST)) logger.finest(WebSocketNodeService.class.getSimpleName() + ".event: " + userid + " disconnect from " + sncpAddr);
|
||||
return future;
|
||||
@@ -114,8 +126,8 @@ public class WebSocketNodeService extends WebSocketNode implements Service {
|
||||
@Override
|
||||
public CompletableFuture<Void> changeUserid(Serializable olduserid, Serializable newuserid, InetSocketAddress sncpAddr) {
|
||||
tryAcquireSemaphore();
|
||||
CompletableFuture<Void> future = sncpNodeAddresses.appendSetItemAsync(SOURCE_SNCP_USERID_PREFIX + newuserid, sncpAddr);
|
||||
future = future.thenAccept((a) -> sncpNodeAddresses.removeSetItemAsync(SOURCE_SNCP_USERID_PREFIX + olduserid, sncpAddr));
|
||||
CompletableFuture<Void> future = sncpNodeAddresses.appendSetItemAsync(SOURCE_SNCP_USERID_PREFIX + newuserid, InetSocketAddress.class, sncpAddr);
|
||||
future = future.thenAccept((a) -> sncpNodeAddresses.removeSetItemAsync(SOURCE_SNCP_USERID_PREFIX + olduserid, InetSocketAddress.class, sncpAddr));
|
||||
if (semaphore != null) future.whenComplete((r, e) -> releaseSemaphore());
|
||||
if (logger.isLoggable(Level.FINEST)) logger.finest(WebSocketNodeService.class.getSimpleName() + ".event: " + olduserid + " changeUserid to " + newuserid + " from " + sncpAddr);
|
||||
return future;
|
||||
@@ -124,15 +136,15 @@ public class WebSocketNodeService extends WebSocketNode implements Service {
|
||||
/**
|
||||
* 强制关闭用户的WebSocket
|
||||
*
|
||||
* @param userid Serializable
|
||||
* @param sncpAddr InetSocketAddress
|
||||
* @param userid Serializable
|
||||
* @param targetAddress InetSocketAddress
|
||||
*
|
||||
* @return 无返回值
|
||||
*/
|
||||
@Override
|
||||
public CompletableFuture<Integer> forceCloseWebSocket(Serializable userid, InetSocketAddress sncpAddr) {
|
||||
public CompletableFuture<Integer> forceCloseWebSocket(Serializable userid, @RpcTargetAddress InetSocketAddress targetAddress) {
|
||||
//不能从sncpNodeAddresses中移除,因为engine.forceCloseWebSocket 会调用到disconnect
|
||||
if (logger.isLoggable(Level.FINEST)) logger.finest(WebSocketNodeService.class.getSimpleName() + ".event: " + userid + " forceCloseWebSocket from " + sncpAddr);
|
||||
if (logger.isLoggable(Level.FINEST)) logger.finest(WebSocketNodeService.class.getSimpleName() + ".event: " + userid + " forceCloseWebSocket from " + targetAddress);
|
||||
if (localEngine == null) return CompletableFuture.completedFuture(0);
|
||||
return CompletableFuture.completedFuture(localEngine.forceCloseLocalWebSocket(userid));
|
||||
}
|
||||
|
||||
@@ -27,9 +27,9 @@ import org.redkale.util.*;
|
||||
*
|
||||
* @author zhangjx
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
@Local
|
||||
@AutoLoad(false)
|
||||
@SuppressWarnings("unchecked")
|
||||
@ResourceType(CacheSource.class)
|
||||
public class CacheMemorySource<V extends Object> extends AbstractService implements CacheSource<V>, Service, AutoCloseable, Resourcable {
|
||||
|
||||
@@ -340,6 +340,11 @@ public class CacheMemorySource<V extends Object> extends AbstractService impleme
|
||||
return (V) entry.objectValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T get(final String key, final Type type) {
|
||||
return (T) get(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getString(String key) {
|
||||
if (key == null) return null;
|
||||
@@ -361,6 +366,11 @@ public class CacheMemorySource<V extends Object> extends AbstractService impleme
|
||||
return CompletableFuture.supplyAsync(() -> get(key), getExecutor());
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> CompletableFuture<T> getAsync(final String key, final Type type) {
|
||||
return CompletableFuture.supplyAsync(() -> (T) get(key), getExecutor());
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<String> getStringAsync(final String key) {
|
||||
return CompletableFuture.supplyAsync(() -> getString(key), getExecutor());
|
||||
@@ -385,6 +395,11 @@ public class CacheMemorySource<V extends Object> extends AbstractService impleme
|
||||
return (V) entry.objectValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T getAndRefresh(final String key, final int expireSeconds, final Type type) {
|
||||
return (T) getAndRefresh(key, expireSeconds);
|
||||
}
|
||||
|
||||
@Override
|
||||
@RpcMultiRun
|
||||
@SuppressWarnings("unchecked")
|
||||
@@ -415,6 +430,11 @@ public class CacheMemorySource<V extends Object> extends AbstractService impleme
|
||||
return CompletableFuture.supplyAsync(() -> getAndRefresh(key, expireSeconds), getExecutor());
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> CompletableFuture<T> getAndRefreshAsync(final String key, final int expireSeconds, final Type type) {
|
||||
return CompletableFuture.supplyAsync(() -> getAndRefresh(key, expireSeconds, type), getExecutor());
|
||||
}
|
||||
|
||||
@Override
|
||||
@RpcMultiRun
|
||||
public CompletableFuture<String> getStringAndRefreshAsync(final String key, final int expireSeconds) {
|
||||
@@ -462,6 +482,11 @@ public class CacheMemorySource<V extends Object> extends AbstractService impleme
|
||||
set(CacheEntryType.OBJECT, key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> void set(String key, Type type, T value) {
|
||||
set(CacheEntryType.OBJECT, key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
@RpcMultiRun
|
||||
public void setString(String key, String value) {
|
||||
@@ -480,6 +505,11 @@ public class CacheMemorySource<V extends Object> extends AbstractService impleme
|
||||
return CompletableFuture.runAsync(() -> set(key, value), getExecutor()).whenComplete(futureCompleteConsumer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> CompletableFuture<Void> setAsync(String key, Type type, T value) {
|
||||
return CompletableFuture.runAsync(() -> set(key, type, value), getExecutor()).whenComplete(futureCompleteConsumer);
|
||||
}
|
||||
|
||||
@Override
|
||||
@RpcMultiRun
|
||||
public CompletableFuture<Void> setStringAsync(String key, String value) {
|
||||
@@ -511,6 +541,11 @@ public class CacheMemorySource<V extends Object> extends AbstractService impleme
|
||||
set(CacheEntryType.OBJECT, expireSeconds, key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> void set(final int expireSeconds, String key, Type type, T value) {
|
||||
set(CacheEntryType.OBJECT, expireSeconds, key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
@RpcMultiRun
|
||||
public void setString(int expireSeconds, String key, String value) {
|
||||
@@ -529,6 +564,11 @@ public class CacheMemorySource<V extends Object> extends AbstractService impleme
|
||||
return CompletableFuture.runAsync(() -> set(expireSeconds, key, value), getExecutor()).whenComplete(futureCompleteConsumer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> CompletableFuture<Void> setAsync(int expireSeconds, String key, Type type, T value) {
|
||||
return CompletableFuture.runAsync(() -> set(expireSeconds, key, type, value), getExecutor()).whenComplete(futureCompleteConsumer);
|
||||
}
|
||||
|
||||
@Override
|
||||
@RpcMultiRun
|
||||
public CompletableFuture<Void> setStringAsync(int expireSeconds, String key, String value) {
|
||||
@@ -632,6 +672,11 @@ public class CacheMemorySource<V extends Object> extends AbstractService impleme
|
||||
return (Collection<V>) get(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Collection<T> getCollection(final String key, final Type componentType) {
|
||||
return (Collection<T>) get(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<String> getStringCollection(final String key) {
|
||||
return (Collection<String>) get(key);
|
||||
@@ -647,6 +692,11 @@ public class CacheMemorySource<V extends Object> extends AbstractService impleme
|
||||
return CompletableFuture.supplyAsync(() -> getCollection(key), getExecutor());
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Collection<V>> getCollectionAsync(final String key, final Type componentType) {
|
||||
return CompletableFuture.supplyAsync(() -> getCollection(key, componentType), getExecutor());
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Collection<String>> getStringCollectionAsync(final String key) {
|
||||
return CompletableFuture.supplyAsync(() -> getStringCollection(key), getExecutor());
|
||||
@@ -674,6 +724,11 @@ public class CacheMemorySource<V extends Object> extends AbstractService impleme
|
||||
return (Collection<V>) getAndRefresh(key, expireSeconds);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Collection<T> getCollectionAndRefresh(final String key, final int expireSeconds, final Type componentType) {
|
||||
return (Collection<T>) getAndRefresh(key, expireSeconds, componentType);
|
||||
}
|
||||
|
||||
@Override
|
||||
@RpcMultiRun
|
||||
public Collection<String> getStringCollectionAndRefresh(final String key, final int expireSeconds) {
|
||||
@@ -686,11 +741,22 @@ public class CacheMemorySource<V extends Object> extends AbstractService impleme
|
||||
return list != null && list.contains(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> boolean existsSetItem(final String key, final Type type, final T value) {
|
||||
Collection list = getCollection(key);
|
||||
return list != null && list.contains(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Boolean> existsSetItemAsync(final String key, final V value) {
|
||||
return CompletableFuture.supplyAsync(() -> existsSetItem(key, value), getExecutor());
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> CompletableFuture<Boolean> existsSetItemAsync(final String key, final Type type, final T value) {
|
||||
return CompletableFuture.supplyAsync(() -> existsSetItem(key, type, value), getExecutor());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean existsStringSetItem(final String key, final String value) {
|
||||
Collection<String> list = getStringCollection(key);
|
||||
@@ -725,6 +791,11 @@ public class CacheMemorySource<V extends Object> extends AbstractService impleme
|
||||
return CompletableFuture.supplyAsync(() -> getCollectionAndRefresh(key, expireSeconds), getExecutor());
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> CompletableFuture<Collection<T>> getCollectionAndRefreshAsync(final String key, final int expireSeconds, final Type componentType) {
|
||||
return CompletableFuture.supplyAsync(() -> getCollectionAndRefresh(key, expireSeconds, componentType), getExecutor());
|
||||
}
|
||||
|
||||
@Override
|
||||
@RpcMultiRun
|
||||
public CompletableFuture<Collection<String>> getStringCollectionAndRefreshAsync(final String key, final int expireSeconds) {
|
||||
@@ -757,6 +828,11 @@ public class CacheMemorySource<V extends Object> extends AbstractService impleme
|
||||
appendListItem(CacheEntryType.OBJECT_LIST, key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> void appendListItem(String key, Type componentType, T value) {
|
||||
appendListItem(CacheEntryType.OBJECT_LIST, key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
@RpcMultiRun
|
||||
public void appendStringListItem(String key, String value) {
|
||||
@@ -775,6 +851,11 @@ public class CacheMemorySource<V extends Object> extends AbstractService impleme
|
||||
return CompletableFuture.runAsync(() -> appendListItem(key, value), getExecutor()).whenComplete(futureCompleteConsumer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> CompletableFuture<Void> appendListItemAsync(final String key, final Type componentType, final T value) {
|
||||
return CompletableFuture.runAsync(() -> appendListItem(key, componentType, value), getExecutor()).whenComplete(futureCompleteConsumer);
|
||||
}
|
||||
|
||||
@Override
|
||||
@RpcMultiRun
|
||||
public CompletableFuture<Void> appendStringListItemAsync(final String key, final String value) {
|
||||
@@ -796,6 +877,14 @@ public class CacheMemorySource<V extends Object> extends AbstractService impleme
|
||||
entry.listValue.remove(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> void removeListItem(String key, final Type componentType, T value) {
|
||||
if (key == null) return;
|
||||
CacheEntry entry = container.get(key);
|
||||
if (entry == null || entry.listValue == null) return;
|
||||
entry.listValue.remove(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
@RpcMultiRun
|
||||
public void removeStringListItem(String key, String value) {
|
||||
@@ -820,6 +909,11 @@ public class CacheMemorySource<V extends Object> extends AbstractService impleme
|
||||
return CompletableFuture.runAsync(() -> removeListItem(key, value), getExecutor()).whenComplete(futureCompleteConsumer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> CompletableFuture<Void> removeListItemAsync(final String key, final Type componentType, T value) {
|
||||
return CompletableFuture.runAsync(() -> removeListItem(key, componentType, value), getExecutor()).whenComplete(futureCompleteConsumer);
|
||||
}
|
||||
|
||||
@Override
|
||||
@RpcMultiRun
|
||||
public CompletableFuture<Void> removeStringListItemAsync(final String key, final String value) {
|
||||
@@ -852,6 +946,11 @@ public class CacheMemorySource<V extends Object> extends AbstractService impleme
|
||||
appendSetItem(CacheEntryType.OBJECT_SET, key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> void appendSetItem(String key, final Type componentType, T value) {
|
||||
appendSetItem(CacheEntryType.OBJECT_SET, key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
@RpcMultiRun
|
||||
public void appendStringSetItem(String key, String value) {
|
||||
@@ -870,6 +969,11 @@ public class CacheMemorySource<V extends Object> extends AbstractService impleme
|
||||
return CompletableFuture.runAsync(() -> appendSetItem(key, value), getExecutor()).whenComplete(futureCompleteConsumer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> CompletableFuture<Void> appendSetItemAsync(final String key, final Type componentType, T value) {
|
||||
return CompletableFuture.runAsync(() -> appendSetItem(key, componentType, value), getExecutor()).whenComplete(futureCompleteConsumer);
|
||||
}
|
||||
|
||||
@Override
|
||||
@RpcMultiRun
|
||||
public CompletableFuture<Void> appendStringSetItemAsync(final String key, final String value) {
|
||||
@@ -891,6 +995,14 @@ public class CacheMemorySource<V extends Object> extends AbstractService impleme
|
||||
entry.csetValue.remove(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> void removeSetItem(String key, Type type, T value) {
|
||||
if (key == null) return;
|
||||
CacheEntry entry = container.get(key);
|
||||
if (entry == null || entry.csetValue == null) return;
|
||||
entry.csetValue.remove(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
@RpcMultiRun
|
||||
public void removeStringSetItem(String key, String value) {
|
||||
@@ -915,6 +1027,11 @@ public class CacheMemorySource<V extends Object> extends AbstractService impleme
|
||||
return CompletableFuture.runAsync(() -> removeSetItem(key, value), getExecutor()).whenComplete(futureCompleteConsumer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> CompletableFuture<Void> removeSetItemAsync(final String key, final Type componentType, final T value) {
|
||||
return CompletableFuture.runAsync(() -> removeSetItem(key, componentType, value), getExecutor()).whenComplete(futureCompleteConsumer);
|
||||
}
|
||||
|
||||
@Override
|
||||
@RpcMultiRun
|
||||
public CompletableFuture<Void> removeStringSetItemAsync(final String key, final String value) {
|
||||
|
||||
@@ -42,6 +42,8 @@ public interface CacheSource<V extends Object> {
|
||||
|
||||
public V get(final String key);
|
||||
|
||||
public <T> T get(final String key, final Type type);
|
||||
|
||||
default V getIfAbsent(final String key, Function<String, ? extends V> mappingFunction) {
|
||||
V rs = get(key);
|
||||
if (rs == null) {
|
||||
@@ -53,6 +55,8 @@ public interface CacheSource<V extends Object> {
|
||||
|
||||
public V getAndRefresh(final String key, final int expireSeconds);
|
||||
|
||||
public <T> T getAndRefresh(final String key, final int expireSeconds, final Type type);
|
||||
|
||||
default V getAndRefreshIfAbsent(final String key, final int expireSeconds, Function<String, ? extends V> mappingFunction) {
|
||||
V rs = getAndRefresh(key, expireSeconds);
|
||||
if (rs == null) {
|
||||
@@ -66,8 +70,12 @@ public interface CacheSource<V extends Object> {
|
||||
|
||||
public void set(final String key, final V value);
|
||||
|
||||
public <T> void set(final String key, final Type type, final T value);
|
||||
|
||||
public void set(final int expireSeconds, final String key, final V value);
|
||||
|
||||
public <T> void set(final int expireSeconds, final String key, final Type type, final T value);
|
||||
|
||||
public void setExpireSeconds(final String key, final int expireSeconds);
|
||||
|
||||
public void remove(final String key);
|
||||
@@ -82,10 +90,14 @@ public interface CacheSource<V extends Object> {
|
||||
|
||||
public Collection<V> getCollection(final String key);
|
||||
|
||||
public <T> Collection<T> getCollection(final String key, final Type componentType);
|
||||
|
||||
public int getCollectionSize(final String key);
|
||||
|
||||
public Collection<V> getCollectionAndRefresh(final String key, final int expireSeconds);
|
||||
|
||||
public <T> Collection<T> getCollectionAndRefresh(final String key, final int expireSeconds, final Type componentType);
|
||||
|
||||
public void appendListItem(final String key, final V value);
|
||||
|
||||
public void removeListItem(final String key, final V value);
|
||||
@@ -96,6 +108,16 @@ public interface CacheSource<V extends Object> {
|
||||
|
||||
public void removeSetItem(final String key, final V value);
|
||||
|
||||
public <T> void appendListItem(final String key, final Type componentType, final T value);
|
||||
|
||||
public <T> void removeListItem(final String key, final Type componentType, final T value);
|
||||
|
||||
public <T> boolean existsSetItem(final String key, final Type componentType, final T value);
|
||||
|
||||
public <T> void appendSetItem(final String key, final Type componentType, final T value);
|
||||
|
||||
public <T> void removeSetItem(final String key, final Type componentType, final T value);
|
||||
|
||||
public List<String> queryKeys();
|
||||
|
||||
public List<String> queryKeysStartsWith(String startsWith);
|
||||
@@ -153,6 +175,8 @@ public interface CacheSource<V extends Object> {
|
||||
//---------------------- CompletableFuture 异步版 ---------------------------------
|
||||
public CompletableFuture<Boolean> existsAsync(final String key);
|
||||
|
||||
public <T> CompletableFuture<T> getAsync(final String key, final Type type);
|
||||
|
||||
public CompletableFuture<V> getAsync(final String key);
|
||||
|
||||
default CompletableFuture<V> getIfAbsentAsync(final String key, Function<String, ? extends V> mappingFunction) {
|
||||
@@ -170,6 +194,8 @@ public interface CacheSource<V extends Object> {
|
||||
|
||||
public CompletableFuture<V> getAndRefreshAsync(final String key, final int expireSeconds);
|
||||
|
||||
public <T> CompletableFuture<T> getAndRefreshAsync(final String key, final int expireSeconds, final Type type);
|
||||
|
||||
default CompletableFuture<V> getAndRefreshIfAbsentAsync(final String key, final int expireSeconds, Function<String, ? extends V> mappingFunction) {
|
||||
return getAndRefreshAsync(key, expireSeconds).thenCompose((V rs) -> {
|
||||
if (rs == null) {
|
||||
@@ -187,8 +213,12 @@ public interface CacheSource<V extends Object> {
|
||||
|
||||
public CompletableFuture<Void> setAsync(final String key, final V value);
|
||||
|
||||
public <T> CompletableFuture<Void> setAsync(final String key, final Type type, final T value);
|
||||
|
||||
public CompletableFuture<Void> setAsync(final int expireSeconds, final String key, final V value);
|
||||
|
||||
public <T> CompletableFuture<Void> setAsync(final int expireSeconds, final String key, final Type type, final T value);
|
||||
|
||||
public CompletableFuture<Void> setExpireSecondsAsync(final String key, final int expireSeconds);
|
||||
|
||||
public CompletableFuture<Void> removeAsync(final String key);
|
||||
@@ -203,10 +233,14 @@ public interface CacheSource<V extends Object> {
|
||||
|
||||
public CompletableFuture<Collection<V>> getCollectionAsync(final String key);
|
||||
|
||||
public <T> CompletableFuture<Collection<T>> getCollectionAsync(final String key, final Type componentType);
|
||||
|
||||
public CompletableFuture<Integer> getCollectionSizeAsync(final String key);
|
||||
|
||||
public CompletableFuture<Collection<V>> getCollectionAndRefreshAsync(final String key, final int expireSeconds);
|
||||
|
||||
public <T> CompletableFuture<Collection<T>> getCollectionAndRefreshAsync(final String key, final int expireSeconds, final Type componentType);
|
||||
|
||||
public CompletableFuture<Void> appendListItemAsync(final String key, final V value);
|
||||
|
||||
public CompletableFuture<Void> removeListItemAsync(final String key, final V value);
|
||||
@@ -217,6 +251,16 @@ public interface CacheSource<V extends Object> {
|
||||
|
||||
public CompletableFuture<Void> removeSetItemAsync(final String key, final V value);
|
||||
|
||||
public <T> CompletableFuture<Void> appendListItemAsync(final String key, final Type componentType, final T value);
|
||||
|
||||
public <T> CompletableFuture<Void> removeListItemAsync(final String key, final Type componentType, final T value);
|
||||
|
||||
public <T> CompletableFuture<Boolean> existsSetItemAsync(final String key, final Type componentType, final T value);
|
||||
|
||||
public <T> CompletableFuture<Void> appendSetItemAsync(final String key, final Type componentType, final T value);
|
||||
|
||||
public <T> CompletableFuture<Void> removeSetItemAsync(final String key, final Type componentType, final T value);
|
||||
|
||||
public CompletableFuture<List<String>> queryKeysAsync();
|
||||
|
||||
public CompletableFuture<List<String>> queryKeysStartsWithAsync(String startsWith);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -11,7 +11,7 @@ import java.sql.*;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.concurrent.atomic.*;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.*;
|
||||
import java.util.logging.Level;
|
||||
import org.redkale.service.Local;
|
||||
import org.redkale.util.*;
|
||||
@@ -45,8 +45,8 @@ public class DataJdbcSource extends DataSqlSource<Connection> {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PoolSource<Connection> createPoolSource(DataSource source, String rwtype, ArrayBlockingQueue queue, Properties prop) {
|
||||
return new PoolJdbcSource(this.name, this.persistxml, rwtype, queue, prop, this.logger);
|
||||
protected PoolSource<Connection> createPoolSource(DataSource source, String rwtype, ArrayBlockingQueue queue, Semaphore semaphore, Properties prop) {
|
||||
return new PoolJdbcSource(this.name, this.persistxml, rwtype, queue, semaphore, prop, this.logger);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -63,7 +63,12 @@ public class DataJdbcSource extends DataSqlSource<Connection> {
|
||||
conn.setAutoCommit(true);
|
||||
PreparedStatement prestmt = createInsertPreparedStatement(conn, sql, info, values);
|
||||
try {
|
||||
prestmt.executeBatch();
|
||||
int[] cs = prestmt.executeBatch();
|
||||
int c1 = 0;
|
||||
for (int cc : cs) {
|
||||
c1 += cc;
|
||||
}
|
||||
c = c1;
|
||||
} catch (SQLException se) {
|
||||
if (info.tableStrategy == null || !info.isTableNotExist(se)) throw se;
|
||||
synchronized (info.tables) {
|
||||
@@ -142,7 +147,8 @@ public class DataJdbcSource extends DataSqlSource<Connection> {
|
||||
sb.append(ch);
|
||||
}
|
||||
}
|
||||
logger.finest(info.getType().getSimpleName() + " insert sql=" + sb.toString().replaceAll("(\r|\n)", "\\n"));
|
||||
String debugsql = sb.toString();
|
||||
if (info.isLoggable(logger, Level.FINEST, debugsql)) logger.finest(info.getType().getSimpleName() + " insert sql=" + debugsql.replaceAll("(\r|\n)", "\\n"));
|
||||
}
|
||||
} //打印结束
|
||||
return CompletableFuture.completedFuture(c);
|
||||
@@ -190,7 +196,7 @@ public class DataJdbcSource extends DataSqlSource<Connection> {
|
||||
conn.setReadOnly(false);
|
||||
conn.setAutoCommit(true);
|
||||
sql += ((flipper == null || flipper.getLimit() < 1) ? "" : (" LIMIT " + flipper.getLimit()));
|
||||
if (info.isLoggable(logger, Level.FINEST)) logger.finest(info.getType().getSimpleName() + " delete sql=" + sql);
|
||||
if (info.isLoggable(logger, Level.FINEST, sql)) logger.finest(info.getType().getSimpleName() + " delete sql=" + sql);
|
||||
final Statement stmt = conn.createStatement();
|
||||
int c = stmt.executeUpdate(sql);
|
||||
stmt.close();
|
||||
@@ -251,7 +257,8 @@ public class DataJdbcSource extends DataSqlSource<Connection> {
|
||||
sb.append(ch);
|
||||
}
|
||||
}
|
||||
logger.finest(info.getType().getSimpleName() + " update sql=" + sb.toString().replaceAll("(\r|\n)", "\\n"));
|
||||
String debugsql = sb.toString();
|
||||
if (info.isLoggable(logger, Level.FINEST, debugsql)) logger.finest(info.getType().getSimpleName() + " update sql=" + debugsql.replaceAll("(\r|\n)", "\\n"));
|
||||
} //打印结束
|
||||
}
|
||||
int[] pc = prestmt.executeBatch();
|
||||
@@ -289,7 +296,7 @@ public class DataJdbcSource extends DataSqlSource<Connection> {
|
||||
prestmt.close();
|
||||
return CompletableFuture.completedFuture(c);
|
||||
} else {
|
||||
if (info.isLoggable(logger, Level.FINEST)) logger.finest(info.getType().getSimpleName() + " update sql=" + sql);
|
||||
if (info.isLoggable(logger, Level.FINEST, sql)) logger.finest(info.getType().getSimpleName() + " update sql=" + sql);
|
||||
final Statement stmt = conn.createStatement();
|
||||
int c = stmt.executeUpdate(sql);
|
||||
stmt.close();
|
||||
@@ -453,7 +460,7 @@ public class DataJdbcSource extends DataSqlSource<Connection> {
|
||||
boolean rs = set.next() ? (set.getInt(1) > 0) : false;
|
||||
set.close();
|
||||
ps.close();
|
||||
if (info.isLoggable(logger, Level.FINEST)) logger.finest(info.getType().getSimpleName() + " exists (" + rs + ") sql=" + sql);
|
||||
if (info.isLoggable(logger, Level.FINEST, sql)) logger.finest(info.getType().getSimpleName() + " exists (" + rs + ") sql=" + sql);
|
||||
return CompletableFuture.completedFuture(rs);
|
||||
} catch (SQLException e) {
|
||||
if (info.tableStrategy != null && info.isTableNotExist(e)) return CompletableFuture.completedFuture(false);
|
||||
@@ -476,10 +483,11 @@ public class DataJdbcSource extends DataSqlSource<Connection> {
|
||||
final Map<Class, String> joinTabalis = node == null ? null : node.getJoinTabalis();
|
||||
final CharSequence join = node == null ? null : node.createSQLJoin(this, false, joinTabalis, new HashSet<>(), info);
|
||||
final CharSequence where = node == null ? null : node.createSQLExpress(info, joinTabalis);
|
||||
if ("mysql".equals(this.readPool.getDbtype()) || "postgresql".equals(this.readPool.getDbtype())) {
|
||||
final String dbtype = this.readPool.getDbtype();
|
||||
if ("mysql".equals(dbtype) || "postgresql".equals(dbtype)) {
|
||||
final String listsql = "SELECT " + info.getQueryColumns("a", selects) + " FROM " + info.getTable(node) + " a" + (join == null ? "" : join)
|
||||
+ ((where == null || where.length() == 0) ? "" : (" WHERE " + where)) + createSQLOrderby(info, flipper) + (flipper == null || flipper.getLimit() < 1 ? "" : (" LIMIT " + flipper.getLimit() + " OFFSET " + flipper.getOffset()));
|
||||
if (info.isLoggable(logger, Level.FINEST)) {
|
||||
if (info.isLoggable(logger, Level.FINEST, listsql)) {
|
||||
logger.finest(info.getType().getSimpleName() + " query sql=" + listsql);
|
||||
}
|
||||
PreparedStatement ps = conn.prepareStatement(listsql, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
|
||||
@@ -490,18 +498,22 @@ public class DataJdbcSource extends DataSqlSource<Connection> {
|
||||
set.close();
|
||||
ps.close();
|
||||
long total = list.size();
|
||||
final String countsql = "SELECT COUNT(*) FROM " + info.getTable(node) + " a" + (join == null ? "" : join)
|
||||
+ ((where == null || where.length() == 0) ? "" : (" WHERE " + where));
|
||||
ps = conn.prepareStatement(countsql, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
|
||||
set = ps.executeQuery();
|
||||
if (set.next()) total = set.getLong(1);
|
||||
set.close();
|
||||
ps.close();
|
||||
if (needtotal) {
|
||||
final String countsql = "SELECT COUNT(*) FROM " + info.getTable(node) + " a" + (join == null ? "" : join) + ((where == null || where.length() == 0) ? "" : (" WHERE " + where));
|
||||
if (info.isLoggable(logger, Level.FINEST, countsql)) {
|
||||
logger.finest(info.getType().getSimpleName() + " query countsql=" + countsql);
|
||||
}
|
||||
ps = conn.prepareStatement(countsql, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
|
||||
set = ps.executeQuery();
|
||||
if (set.next()) total = set.getLong(1);
|
||||
set.close();
|
||||
ps.close();
|
||||
}
|
||||
return CompletableFuture.completedFuture(new Sheet<>(total, list));
|
||||
}
|
||||
final String sql = "SELECT " + info.getQueryColumns("a", selects) + " FROM " + info.getTable(node) + " a" + (join == null ? "" : join)
|
||||
+ ((where == null || where.length() == 0) ? "" : (" WHERE " + where)) + info.createSQLOrderby(flipper);
|
||||
if (info.isLoggable(logger, Level.FINEST)) {
|
||||
if (info.isLoggable(logger, Level.FINEST, sql)) {
|
||||
logger.finest(info.getType().getSimpleName() + " query sql=" + sql + (flipper == null || flipper.getLimit() < 1 ? "" : (" LIMIT " + flipper.getLimit() + " OFFSET " + flipper.getOffset())));
|
||||
}
|
||||
conn.setReadOnly(true);
|
||||
@@ -582,12 +594,15 @@ public class DataJdbcSource extends DataSqlSource<Connection> {
|
||||
* 直接本地执行SQL语句进行查询,远程模式不可用 <br>
|
||||
* 通常用于复杂的关联查询 <br>
|
||||
*
|
||||
* @param sql SQL语句
|
||||
* @param consumer 回调函数
|
||||
* @param <V> 泛型
|
||||
* @param sql SQL语句
|
||||
* @param handler 回调函数
|
||||
*
|
||||
* @return 结果
|
||||
*/
|
||||
@Local
|
||||
@Override
|
||||
public void directQuery(String sql, Consumer<ResultSet> consumer) {
|
||||
public <V> V directQuery(String sql, Function<ResultSet, V> handler) {
|
||||
final Connection conn = readPool.poll();
|
||||
try {
|
||||
if (logger.isLoggable(Level.FINEST)) logger.finest("direct query sql=" + sql);
|
||||
@@ -595,9 +610,10 @@ public class DataJdbcSource extends DataSqlSource<Connection> {
|
||||
final Statement statement = conn.createStatement();
|
||||
//final PreparedStatement statement = conn.prepareStatement(sql, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
|
||||
final ResultSet set = statement.executeQuery(sql);// ps.executeQuery();
|
||||
consumer.accept(set);
|
||||
V rs = handler.apply(set);
|
||||
set.close();
|
||||
statement.close();
|
||||
return rs;
|
||||
} catch (Exception ex) {
|
||||
throw new RuntimeException(ex);
|
||||
} finally {
|
||||
|
||||
141
src/org/redkale/source/DataMemorySource.java
Normal file
141
src/org/redkale/source/DataMemorySource.java
Normal file
@@ -0,0 +1,141 @@
|
||||
/*
|
||||
* 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.source;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.net.URL;
|
||||
import java.sql.ResultSet;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.function.*;
|
||||
import org.redkale.service.Local;
|
||||
import org.redkale.util.*;
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @author zhangjx
|
||||
*/
|
||||
/**
|
||||
* DataSource的Memory实现类 <br>
|
||||
* 注意: javax.persistence.jdbc.url 需要指定为 memory:source
|
||||
*
|
||||
* <p>
|
||||
* 详情见: https://redkale.org
|
||||
*
|
||||
* @author zhangjx
|
||||
*/
|
||||
@Local
|
||||
@AutoLoad(false)
|
||||
@SuppressWarnings("unchecked")
|
||||
@ResourceType(DataSource.class)
|
||||
public class DataMemorySource extends DataSqlSource<Void> {
|
||||
|
||||
public DataMemorySource(String unitName, URL persistxml, Properties readprop, Properties writeprop) {
|
||||
super(unitName, persistxml, readprop, writeprop);
|
||||
this.cacheForbidden = false;
|
||||
}
|
||||
|
||||
@Local
|
||||
@Override
|
||||
public String getType() {
|
||||
return "memory";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isOnlyCache(EntityInfo info) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Local
|
||||
@Override
|
||||
public int directExecute(String sql) {
|
||||
throw new UnsupportedOperationException("Not supported yet.");
|
||||
}
|
||||
|
||||
@Local
|
||||
@Override
|
||||
public int[] directExecute(String... sqls) {
|
||||
throw new UnsupportedOperationException("Not supported yet.");
|
||||
}
|
||||
|
||||
@Local
|
||||
@Override
|
||||
public <V> V directQuery(String sql, Function<ResultSet, V> handler) {
|
||||
throw new UnsupportedOperationException("Not supported yet.");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isAsync() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String prepareParamSign(int index) {
|
||||
throw new UnsupportedOperationException("Not supported yet.");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PoolSource<Void> createPoolSource(DataSource source, String rwtype, ArrayBlockingQueue queue, Semaphore semaphore, Properties prop) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected <T> CompletableFuture<Integer> insertDB(EntityInfo<T> info, T... values) {
|
||||
return CompletableFuture.completedFuture(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected <T> CompletableFuture<Integer> deleteDB(EntityInfo<T> info, Flipper flipper, String sql) {
|
||||
return CompletableFuture.completedFuture(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected <T> CompletableFuture<Integer> updateDB(EntityInfo<T> info, T... values) {
|
||||
return CompletableFuture.completedFuture(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected <T> CompletableFuture<Integer> updateDB(EntityInfo<T> info, Flipper flipper, String sql, boolean prepared, Object... params) {
|
||||
return CompletableFuture.completedFuture(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected <T, N extends Number> CompletableFuture<Map<String, N>> getNumberMapDB(EntityInfo<T> info, String sql, FilterFuncColumn... columns) {
|
||||
return CompletableFuture.completedFuture(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected <T> CompletableFuture<Number> getNumberResultDB(EntityInfo<T> info, String sql, Number defVal, String column) {
|
||||
return CompletableFuture.completedFuture(defVal);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected <T, K extends Serializable, N extends Number> CompletableFuture<Map<K, N>> queryColumnMapDB(EntityInfo<T> info, String sql, String keyColumn) {
|
||||
return CompletableFuture.completedFuture(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected <T> CompletableFuture<T> findDB(EntityInfo<T> info, String sql, boolean onlypk, SelectColumn selects) {
|
||||
return CompletableFuture.completedFuture(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected <T> CompletableFuture<Serializable> findColumnDB(EntityInfo<T> info, String sql, boolean onlypk, String column, Serializable defValue) {
|
||||
return CompletableFuture.completedFuture(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected <T> CompletableFuture<Boolean> existsDB(EntityInfo<T> info, String sql, boolean onlypk) {
|
||||
return CompletableFuture.completedFuture(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected <T> CompletableFuture<Sheet<T>> querySheetDB(EntityInfo<T> info, boolean needtotal, SelectColumn selects, Flipper flipper, FilterNode node) {
|
||||
return CompletableFuture.completedFuture(new Sheet<>());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -23,7 +23,7 @@ public final class DataSources {
|
||||
|
||||
public static final String JDBC_CACHE_MODE = "javax.persistence.cachemode";
|
||||
|
||||
public static final String JDBC_CONNECTIONSMAX = "javax.persistence.connections.limit";
|
||||
public static final String JDBC_CONNECTIONS_LIMIT = "javax.persistence.connections.limit";
|
||||
|
||||
public static final String JDBC_CONNECTIONSCAPACITY = "javax.persistence.connections.bufcapacity";
|
||||
|
||||
@@ -47,6 +47,8 @@ public final class DataSources {
|
||||
|
||||
public static final String JDBC_PWD = "javax.persistence.jdbc.password";
|
||||
|
||||
public static final String JDBC_ENCODING = "javax.persistence.jdbc.encoding";
|
||||
|
||||
public static final String JDBC_DRIVER = "javax.persistence.jdbc.driver";
|
||||
|
||||
public static final String JDBC_SOURCE = "javax.persistence.jdbc.source";
|
||||
@@ -54,11 +56,11 @@ public final class DataSources {
|
||||
private DataSources() {
|
||||
}
|
||||
|
||||
public static DataSource createDataSource(final String unitName, Properties prop) throws IOException {
|
||||
public static DataSource createDataSource2(final String unitName, Properties prop) throws IOException {
|
||||
return new DataJdbcSource(unitName, null, prop, prop);
|
||||
}
|
||||
|
||||
public static DataSource createDataSource(final String unitName, Properties readprop, Properties writeprop) throws IOException {
|
||||
public static DataSource createDataSource2(final String unitName, Properties readprop, Properties writeprop) throws IOException {
|
||||
return new DataJdbcSource(unitName, null, readprop, writeprop);
|
||||
}
|
||||
|
||||
@@ -70,7 +72,7 @@ public final class DataSources {
|
||||
|
||||
public static DataSource createDataSource(final String unitName, URL persistxml) throws IOException {
|
||||
if (persistxml == null) persistxml = DataSources.class.getResource("/persistence.xml");
|
||||
InputStream in = persistxml.openStream();
|
||||
InputStream in = persistxml == null ? null : persistxml.openStream();
|
||||
if (in == null) return null;
|
||||
Map<String, Properties> map = loadPersistenceXml(in);
|
||||
Properties readprop = null;
|
||||
@@ -101,6 +103,8 @@ public final class DataSources {
|
||||
}
|
||||
if (readprop == null) throw new IOException("Cannot find (resource.name = '" + unitName + "') DataSource");
|
||||
if (writeprop == null) writeprop = readprop;
|
||||
if (readprop.getProperty(JDBC_URL, "").startsWith("memory:source")) return new DataMemorySource(unitName, persistxml, readprop, writeprop);
|
||||
|
||||
String impl = readprop.getProperty(JDBC_DATASOURCE_CLASS, DataJdbcSource.class.getName());
|
||||
if (DataJdbcSource.class.getName().equals(impl)) {
|
||||
try {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user