Compare commits
131 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
921aedaf9d | ||
|
|
0f545923b2 | ||
|
|
5b32a91874 | ||
|
|
25b2528416 | ||
|
|
d29c95c38f | ||
|
|
a661ab2ff5 | ||
|
|
ac08ebee75 | ||
|
|
8e1a287ed1 | ||
|
|
9b656b3970 | ||
|
|
a1c0bbf413 | ||
|
|
2ad8c5d425 | ||
|
|
75aaa980cf | ||
|
|
7c55326d23 | ||
|
|
aa3ade5912 | ||
|
|
c692deebe9 | ||
|
|
9ff161c97d | ||
|
|
5b1f820621 | ||
|
|
30b2cffcb8 | ||
|
|
709439bfca | ||
|
|
34adb238f7 | ||
|
|
76c54f8d54 | ||
|
|
6196c05f12 | ||
|
|
37df0af56c | ||
|
|
8a5e1252ab | ||
|
|
ae5430af42 | ||
|
|
13cf188e25 | ||
|
|
41d3dea1ac | ||
|
|
be816088f5 | ||
|
|
5bef900b76 | ||
|
|
52f61b0f96 | ||
|
|
b5a4646e3b | ||
|
|
d055d5c824 | ||
|
|
f14ef05c88 | ||
|
|
426506324f | ||
|
|
7a5e58a112 | ||
|
|
2e0c58cbea | ||
|
|
9ded3fbb9a | ||
|
|
acbc1032e6 | ||
|
|
d8186b00ba | ||
|
|
767adcbefe | ||
|
|
67df072275 | ||
|
|
dab70af4d4 | ||
|
|
b42826692d | ||
|
|
e2ab4b20c9 | ||
|
|
511ee8a6df | ||
|
|
463269a796 | ||
|
|
2d4b865432 | ||
|
|
1da73429f7 | ||
|
|
e97e6b8262 | ||
|
|
e6bc34d6f8 | ||
|
|
f1c4ac9e67 | ||
|
|
de6c6076e4 | ||
|
|
a36e3d3819 | ||
|
|
043b847f05 | ||
|
|
65bc8192f0 | ||
|
|
71e0b60200 | ||
|
|
99dc7ac189 | ||
|
|
8605c44f14 | ||
|
|
c47e301263 | ||
|
|
6c11ef70d4 | ||
|
|
dddb2397c4 | ||
|
|
f66803b9fd | ||
|
|
0801ce8cad | ||
|
|
5a690912c1 | ||
|
|
a3dddd20c7 | ||
|
|
7200a74b18 | ||
|
|
3847cf47e4 | ||
|
|
0b5a79c1d8 | ||
|
|
685667fd69 | ||
|
|
72e81bd034 | ||
|
|
39eadcd08b | ||
|
|
a46f79a448 | ||
|
|
5ddca03fb6 | ||
|
|
0bd5787992 | ||
|
|
bf88c4da06 | ||
|
|
5e72e782cc | ||
|
|
3c0609fa0e | ||
|
|
945f9f9ef5 | ||
|
|
7c5ac7970e | ||
|
|
68f0c1de29 | ||
|
|
337a2a3038 | ||
|
|
2a5e0e82be | ||
|
|
e0b2120ee5 | ||
|
|
ac5662114a | ||
|
|
f2963e01e0 | ||
|
|
fc54fc3f24 | ||
|
|
41990e7e6a | ||
|
|
01e8649e2a | ||
|
|
71ead72dec | ||
|
|
da600ecf20 | ||
|
|
d46807a585 | ||
|
|
571d13075b | ||
|
|
3ad8eeaae6 | ||
|
|
e540128154 | ||
|
|
c6d83440bb | ||
|
|
97f43a4d8d | ||
|
|
6fddd8b53b | ||
|
|
98a6c3ef79 | ||
|
|
6e70f2043e | ||
|
|
123b94398a | ||
|
|
40c19c1521 | ||
|
|
7089fae390 | ||
|
|
936fe8d1ab | ||
|
|
7780e0090c | ||
|
|
1a7dc97335 | ||
|
|
9af8c863b1 | ||
|
|
2c13224fcc | ||
|
|
5e13575e84 | ||
|
|
eabdc13c53 | ||
|
|
7261bcfadb | ||
|
|
0569e87d4e | ||
|
|
5c339f6f66 | ||
|
|
44eada2697 | ||
|
|
3e0b9daeaf | ||
|
|
3061422d83 | ||
|
|
4280464f85 | ||
|
|
2b1d09b027 | ||
|
|
4f3c2e071a | ||
|
|
d2aacf7b8c | ||
|
|
fbb1fb5a5f | ||
|
|
c9187a78bc | ||
|
|
d984ab2a8f | ||
|
|
ad2a3f0d54 | ||
|
|
e559379294 | ||
|
|
e125aa9885 | ||
|
|
8026ebb215 | ||
|
|
45b04da483 | ||
|
|
0da5867b70 | ||
|
|
b1f51b1c30 | ||
|
|
054253fb90 | ||
|
|
141041dbba |
@@ -25,4 +25,5 @@
|
||||
|
||||
<h5>基本文档: <a href='https://redkale.org/articles.html' target='_blank'>https://redkale.org/articles.html</a></h5>
|
||||
|
||||
<h5>欢迎加入Redkale QQ群: 527523235</h5>
|
||||
|
||||
|
||||
@@ -4,15 +4,20 @@ export LC_ALL="zh_CN.UTF-8"
|
||||
|
||||
APP_HOME=`dirname "$0"`
|
||||
|
||||
cd "$APP_HOME"/..
|
||||
|
||||
APP_HOME=`pwd`
|
||||
|
||||
if [ ! -f "$APP_HOME"/conf/application.xml ]; then
|
||||
APP_HOME="$APP_HOME"/..
|
||||
fi
|
||||
|
||||
lib='.'
|
||||
lib="$APP_HOME"/lib
|
||||
for jar in `ls $APP_HOME/lib/*.jar`
|
||||
do
|
||||
lib=$lib:$jar
|
||||
done
|
||||
export CLASSPATH=$CLASSPATH:$lib
|
||||
|
||||
echo "$APP_HOME"
|
||||
java -DCMD=SHUTDOWN -DAPP_HOME="$APP_HOME" org.redkale.boot.Application
|
||||
|
||||
@@ -8,6 +8,10 @@ export LC_ALL="zh_CN.UTF-8"
|
||||
|
||||
APP_HOME=`dirname "$0"`
|
||||
|
||||
cd "$APP_HOME"/..
|
||||
|
||||
APP_HOME=`pwd`
|
||||
|
||||
if [ ! -f "$APP_HOME"/conf/application.xml ]; then
|
||||
APP_HOME="$APP_HOME"/..
|
||||
fi
|
||||
|
||||
@@ -71,7 +71,13 @@
|
||||
<source name="redis" value="org.redkalex.cache.RedisCacheSource" xxx="16">
|
||||
<node addr="127.0.0.1" port="7070"/>
|
||||
</source>
|
||||
|
||||
|
||||
<!--
|
||||
Application启动的监听事件,可配置多个节点
|
||||
value: 类名,必须是ApplicationListener的子类
|
||||
-->
|
||||
<listener value="org.redkalex.xxx.XXXApplicationListener"/>
|
||||
|
||||
<!--
|
||||
【节点全局唯一】
|
||||
全局的参数配置, 可以通过@Resource(name="property.xxxxxx") 进行注入<property>的信息, 被注解的字段类型只能是String、primitive class
|
||||
@@ -107,6 +113,7 @@
|
||||
charset: 文本编码, 默认: UTF-8
|
||||
backlog: 默认10K
|
||||
threads: 线程总数, 默认: CPU核数*16
|
||||
maxconns:最大连接数, 小于1表示无限制, 默认: 0
|
||||
maxbody: request.body最大值, 默认: 64K
|
||||
bufferCapacity: ByteBuffer的初始化大小, 默认: 8K; 如果是HTTP协议则默认: 16K + 16B (兼容HTTP 2.0、WebSocket)
|
||||
bufferPoolSize: ByteBuffer池的大小,默认: CPU核数*512
|
||||
|
||||
29
src/module-info.java
Normal file
29
src/module-info.java
Normal file
@@ -0,0 +1,29 @@
|
||||
/**
|
||||
* <p>
|
||||
* see: https://redkale.org
|
||||
*
|
||||
* @author zhangjx
|
||||
*
|
||||
module org.redkale {
|
||||
|
||||
requires java.se;
|
||||
requires jdk.unsupported;
|
||||
|
||||
exports javax.annotation;
|
||||
exports javax.persistence;
|
||||
exports org.redkale.boot;
|
||||
exports org.redkale.boot.watch;
|
||||
exports org.redkale.convert;
|
||||
exports org.redkale.convert.bson;
|
||||
exports org.redkale.convert.ext;
|
||||
exports org.redkale.convert.json;
|
||||
exports org.redkale.net;
|
||||
exports org.redkale.net.http;
|
||||
exports org.redkale.net.sncp;
|
||||
exports org.redkale.service;
|
||||
exports org.redkale.source;
|
||||
exports org.redkale.util;
|
||||
exports org.redkale.watch;
|
||||
|
||||
}
|
||||
*/
|
||||
26
src/org/redkale/asm/asm.txt
Normal file
26
src/org/redkale/asm/asm.txt
Normal file
@@ -0,0 +1,26 @@
|
||||
need copy classes:
|
||||
|
||||
AnnotationVisitor.java
|
||||
AnnotationWriter.java
|
||||
Attribute.java
|
||||
ByteVector.java
|
||||
ClassReader.java
|
||||
ClassVisitor.java
|
||||
ClassWriter.java
|
||||
Context.java
|
||||
CurrentFrame.java
|
||||
Edge.java
|
||||
FieldVisitor.java
|
||||
FieldWriter.java
|
||||
Frame.java
|
||||
Handle.java
|
||||
Handler.java
|
||||
Item.java
|
||||
Label.java
|
||||
MethodVisitor.java
|
||||
MethodWriter.java
|
||||
ModuleVisitor.java
|
||||
ModuleWriter.java
|
||||
Opcodes.java
|
||||
Type.java
|
||||
TypePath.java
|
||||
@@ -32,7 +32,6 @@ import org.redkale.util.AnyValue.DefaultAnyValue;
|
||||
import org.redkale.util.*;
|
||||
import org.redkale.watch.*;
|
||||
import org.w3c.dom.*;
|
||||
import sun.misc.Signal;
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -105,8 +104,8 @@ public final class Application {
|
||||
//NodeServer 资源
|
||||
final List<NodeServer> servers = new CopyOnWriteArrayList<>();
|
||||
|
||||
//传输端的TransportFactory
|
||||
final TransportFactory transportFactory;
|
||||
//SNCP传输端的TransportFactory, 注意: 只给SNCP使用
|
||||
final TransportFactory sncpTransportFactory;
|
||||
|
||||
//全局根ResourceFactory
|
||||
final ResourceFactory resourceFactory = ResourceFactory.root();
|
||||
@@ -132,6 +131,9 @@ public final class Application {
|
||||
//日志
|
||||
private final Logger logger;
|
||||
|
||||
//监听事件
|
||||
private final List<ApplicationListener> listeners = new CopyOnWriteArrayList<>();
|
||||
|
||||
//服务启动时间
|
||||
private final long startTime = System.currentTimeMillis();
|
||||
|
||||
@@ -234,27 +236,30 @@ public final class Application {
|
||||
this.logger = Logger.getLogger(this.getClass().getSimpleName());
|
||||
this.serversLatch = new CountDownLatch(config.getAnyValues("server").length + 1);
|
||||
this.classLoader = new RedkaleClassLoader(Thread.currentThread().getContextClassLoader());
|
||||
logger.log(Level.INFO, "------------------------------- Redkale " + Redkale.getDotedVersion() + " -------------------------------");
|
||||
logger.log(Level.INFO, "------------------------- Redkale " + Redkale.getDotedVersion() + " -------------------------");
|
||||
//------------------配置 <transport> 节点 ------------------
|
||||
ObjectPool<ByteBuffer> transportPool = null;
|
||||
ExecutorService transportExec = null;
|
||||
AsynchronousChannelGroup transportGroup = null;
|
||||
final AnyValue resources = config.getAnyValue("resources");
|
||||
TransportStrategy strategy = null;
|
||||
int bufferCapacity = 8 * 1024;
|
||||
int bufferPoolSize = Runtime.getRuntime().availableProcessors() * 16;
|
||||
AtomicLong createBufferCounter = new AtomicLong();
|
||||
AtomicLong cycleBufferCounter = new AtomicLong();
|
||||
if (resources != null) {
|
||||
AnyValue transportConf = resources.getAnyValue("transport");
|
||||
int groupsize = resources.getAnyValues("group").length;
|
||||
if (groupsize > 0 && transportConf == null) transportConf = new DefaultAnyValue();
|
||||
if (transportConf != null) {
|
||||
//--------------transportBufferPool-----------
|
||||
AtomicLong createBufferCounter = new AtomicLong();
|
||||
AtomicLong cycleBufferCounter = new AtomicLong();
|
||||
final int bufferCapacity = Math.max(parseLenth(transportConf.getValue("bufferCapacity"), 8 * 1024), 4 * 1024);
|
||||
final int bufferPoolSize = parseLenth(transportConf.getValue("bufferPoolSize"), groupsize * Runtime.getRuntime().availableProcessors() * 8);
|
||||
bufferCapacity = Math.max(parseLenth(transportConf.getValue("bufferCapacity"), bufferCapacity), 4 * 1024);
|
||||
bufferPoolSize = parseLenth(transportConf.getValue("bufferPoolSize"), groupsize * Runtime.getRuntime().availableProcessors() * 8);
|
||||
final int threads = parseLenth(transportConf.getValue("threads"), groupsize * Runtime.getRuntime().availableProcessors() * 8);
|
||||
final int capacity = bufferCapacity;
|
||||
transportPool = new ObjectPool<>(createBufferCounter, cycleBufferCounter, bufferPoolSize,
|
||||
(Object... params) -> ByteBuffer.allocateDirect(bufferCapacity), null, (e) -> {
|
||||
if (e == null || e.isReadOnly() || e.capacity() != bufferCapacity) return false;
|
||||
(Object... params) -> ByteBuffer.allocateDirect(capacity), null, (e) -> {
|
||||
if (e == null || e.isReadOnly() || e.capacity() != capacity) return false;
|
||||
e.clear();
|
||||
return true;
|
||||
});
|
||||
@@ -292,7 +297,18 @@ public final class Application {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
this.transportFactory = new TransportFactory(transportExec, transportPool, transportGroup, strategy);
|
||||
if (transportPool == null) {
|
||||
final int capacity = bufferCapacity;
|
||||
transportPool = new ObjectPool<>(createBufferCounter, cycleBufferCounter, bufferPoolSize,
|
||||
(Object... params) -> ByteBuffer.allocateDirect(capacity), null, (e) -> {
|
||||
if (e == null || e.isReadOnly() || e.capacity() != capacity) return false;
|
||||
e.clear();
|
||||
return true;
|
||||
});
|
||||
}
|
||||
this.sncpTransportFactory = TransportFactory.create(transportExec, transportPool, transportGroup, strategy);
|
||||
DefaultAnyValue tarnsportConf = DefaultAnyValue.create(TransportFactory.NAME_PINGINTERVAL, System.getProperty("net.transport.pinginterval", "30"));
|
||||
this.sncpTransportFactory.init(tarnsportConf, Sncp.PING_BUFFER, Sncp.PONG_BUFFER.remaining());
|
||||
Thread.currentThread().setContextClassLoader(this.classLoader);
|
||||
this.serverClassLoader = new RedkaleClassLoader(this.classLoader);
|
||||
}
|
||||
@@ -301,8 +317,8 @@ public final class Application {
|
||||
return resourceFactory;
|
||||
}
|
||||
|
||||
public TransportFactory getTransportFactory() {
|
||||
return transportFactory;
|
||||
public TransportFactory getSncpTransportFactory() {
|
||||
return sncpTransportFactory;
|
||||
}
|
||||
|
||||
public RedkaleClassLoader getClassLoader() {
|
||||
@@ -341,8 +357,8 @@ public final class Application {
|
||||
File persist = new File(this.home, "conf/persistence.xml");
|
||||
final String homepath = this.home.getCanonicalPath();
|
||||
if (persist.isFile()) System.setProperty(DataSources.DATASOURCE_CONFPATH, persist.getCanonicalPath());
|
||||
logger.log(Level.INFO, RESNAME_APP_HOME + "= " + homepath + "\r\n" + RESNAME_APP_ADDR + "= " + this.localAddress.getHostAddress());
|
||||
String lib = config.getValue("lib", "").trim().replace("${APP_HOME}", homepath);
|
||||
logger.log(Level.INFO, "APP_JAVA = " + System.getProperty("java.version") + "\r\n" + RESNAME_APP_ADDR + " = " + this.localAddress.getHostAddress() + "\r\n" + RESNAME_APP_HOME + " = " + homepath);
|
||||
String lib = config.getValue("lib", "${APP_HOME}/libs/*").trim().replace("${APP_HOME}", homepath);
|
||||
lib = lib.isEmpty() ? (homepath + "/conf") : (lib + ";" + homepath + "/conf");
|
||||
Server.loadLib(classLoader, logger, lib);
|
||||
|
||||
@@ -406,7 +422,7 @@ public final class Application {
|
||||
} else if (type == ResourceFactory.class) {
|
||||
field.set(src, res.name().equalsIgnoreCase("server") ? rf : (res.name().isEmpty() ? application.resourceFactory : null));
|
||||
} else if (type == TransportFactory.class) {
|
||||
field.set(src, application.transportFactory);
|
||||
field.set(src, application.sncpTransportFactory);
|
||||
} else if (type == NodeSncpServer.class) {
|
||||
NodeServer server = null;
|
||||
for (NodeServer ns : application.getNodeServers()) {
|
||||
@@ -472,7 +488,16 @@ public final class Application {
|
||||
final InetSocketAddress addr = new InetSocketAddress(node.getValue("addr"), node.getIntValue("port"));
|
||||
ginfo.putAddress(addr);
|
||||
}
|
||||
transportFactory.addGroupInfo(ginfo);
|
||||
sncpTransportFactory.addGroupInfo(ginfo);
|
||||
}
|
||||
for (AnyValue conf : resources.getAnyValues("listener")) {
|
||||
final String listenClass = conf.getValue("value", "");
|
||||
if (listenClass.isEmpty()) continue;
|
||||
Class clazz = Class.forName(listenClass);
|
||||
if (!ApplicationListener.class.isAssignableFrom(clazz)) continue;
|
||||
ApplicationListener listener = (ApplicationListener) clazz.newInstance();
|
||||
listener.init(config);
|
||||
this.listeners.add(listener);
|
||||
}
|
||||
}
|
||||
//------------------------------------------------------------------------
|
||||
@@ -622,36 +647,35 @@ public final class Application {
|
||||
}
|
||||
}
|
||||
|
||||
private void signalHandle() {
|
||||
//http://www.comptechdoc.org/os/linux/programming/linux_pgsignals.html
|
||||
String[] sigs = new String[]{"HUP", "TERM", "INT", "QUIT", "KILL", "TSTP", "USR1", "USR2", "STOP"};
|
||||
List<sun.misc.Signal> list = new ArrayList<>();
|
||||
for (String sig : sigs) {
|
||||
try {
|
||||
list.add(new sun.misc.Signal(sig));
|
||||
} catch (Exception e) {
|
||||
}
|
||||
}
|
||||
sun.misc.SignalHandler handler = new sun.misc.SignalHandler() {
|
||||
|
||||
private volatile boolean runed;
|
||||
|
||||
@Override
|
||||
public void handle(Signal sig) {
|
||||
if (runed) return;
|
||||
runed = true;
|
||||
logger.info(Application.this.getClass().getSimpleName() + " stoped\r\n");
|
||||
System.exit(0);
|
||||
}
|
||||
};
|
||||
for (Signal sig : list) {
|
||||
try {
|
||||
Signal.handle(sig, handler);
|
||||
} catch (Exception e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// private void signalHandle() {
|
||||
// //http://www.comptechdoc.org/os/linux/programming/linux_pgsignals.html
|
||||
// String[] sigs = new String[]{"HUP", "TERM", "INT", "QUIT", "KILL", "TSTP", "USR1", "USR2", "STOP"};
|
||||
// List<sun.misc.Signal> list = new ArrayList<>();
|
||||
// for (String sig : sigs) {
|
||||
// try {
|
||||
// list.add(new sun.misc.Signal(sig));
|
||||
// } catch (Exception e) {
|
||||
// }
|
||||
// }
|
||||
// sun.misc.SignalHandler handler = new sun.misc.SignalHandler() {
|
||||
//
|
||||
// private volatile boolean runed;
|
||||
//
|
||||
// @Override
|
||||
// public void handle(Signal sig) {
|
||||
// if (runed) return;
|
||||
// runed = true;
|
||||
// logger.info(Application.this.getClass().getSimpleName() + " stoped\r\n");
|
||||
// System.exit(0);
|
||||
// }
|
||||
// };
|
||||
// for (Signal sig : list) {
|
||||
// try {
|
||||
// Signal.handle(sig, handler);
|
||||
// } catch (Exception e) {
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
@SuppressWarnings("unchecked")
|
||||
private void runServers(CountDownLatch timecd, final List<AnyValue> serconfs) throws Exception {
|
||||
this.servicecdl = new CountDownLatch(serconfs.size());
|
||||
@@ -771,6 +795,9 @@ public final class Application {
|
||||
application.init();
|
||||
application.startSelfServer();
|
||||
try {
|
||||
for (ApplicationListener listener : application.listeners) {
|
||||
listener.preStart(application);
|
||||
}
|
||||
application.start();
|
||||
} catch (Exception e) {
|
||||
application.logger.log(Level.SEVERE, "Application start error", e);
|
||||
@@ -789,6 +816,14 @@ public final class Application {
|
||||
}
|
||||
|
||||
private void shutdown() throws Exception {
|
||||
for (ApplicationListener listener : this.listeners) {
|
||||
try {
|
||||
listener.preShutdown(this);
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.WARNING, listener.getClass() + " preShutdown erroneous", e);
|
||||
}
|
||||
}
|
||||
|
||||
servers.stream().forEach((server) -> {
|
||||
try {
|
||||
server.shutdown();
|
||||
@@ -815,7 +850,7 @@ public final class Application {
|
||||
logger.log(Level.FINER, source.getClass() + " close CacheSource erroneous", e);
|
||||
}
|
||||
}
|
||||
this.transportFactory.shutdownNow();
|
||||
this.sncpTransportFactory.shutdownNow();
|
||||
}
|
||||
|
||||
private static int parseLenth(String value, int defValue) {
|
||||
|
||||
45
src/org/redkale/boot/ApplicationListener.java
Normal file
45
src/org/redkale/boot/ApplicationListener.java
Normal file
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* To change this license header, choose License Headers in Project Properties.
|
||||
* To change this template file, choose Tools | Templates
|
||||
* and open the template in the editor.
|
||||
*/
|
||||
package org.redkale.boot;
|
||||
|
||||
import org.redkale.util.AnyValue;
|
||||
|
||||
/**
|
||||
* Application启动和关闭时的监听事件 <br>
|
||||
* 只能通过application.xml配置
|
||||
*
|
||||
* <p>
|
||||
* 详情见: https://redkale.org
|
||||
*
|
||||
* @author zhangjx
|
||||
*/
|
||||
public interface ApplicationListener {
|
||||
|
||||
/**
|
||||
* 初始化方法
|
||||
*
|
||||
* @param config 配置参数
|
||||
*/
|
||||
default void init(AnyValue config) {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Application 在运行start前调用
|
||||
*
|
||||
* @param application Application
|
||||
*/
|
||||
default void preStart(Application application) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Application 在运行shutdown前调用
|
||||
*
|
||||
* @param application Application
|
||||
*/
|
||||
default void preShutdown(Application application) {
|
||||
}
|
||||
}
|
||||
@@ -7,10 +7,11 @@ package org.redkale.boot;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.*;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.*;
|
||||
import java.util.*;
|
||||
import java.util.logging.Level;
|
||||
import javax.annotation.*;
|
||||
import static org.redkale.boot.Application.RESNAME_SNCP_ADDR;
|
||||
import org.redkale.boot.ClassFilter.FilterEntry;
|
||||
import org.redkale.net.*;
|
||||
import org.redkale.net.http.*;
|
||||
@@ -108,8 +109,13 @@ public class NodeHttpServer extends NodeServer {
|
||||
if (loader != null) loader.load(sncpResFactory, src, resourceName, field, attachment);
|
||||
synchronized (regFactory) {
|
||||
Service nodeService = (Service) rf.find(resourceName, WebSocketNode.class);
|
||||
if (sncpResFactory != null && resourceFactory.find(RESNAME_SNCP_ADDR, String.class) == null) {
|
||||
resourceFactory.register(RESNAME_SNCP_ADDR, InetSocketAddress.class, sncpResFactory.find(RESNAME_SNCP_ADDR, InetSocketAddress.class));
|
||||
resourceFactory.register(RESNAME_SNCP_ADDR, SocketAddress.class, sncpResFactory.find(RESNAME_SNCP_ADDR, SocketAddress.class));
|
||||
resourceFactory.register(RESNAME_SNCP_ADDR, String.class, sncpResFactory.find(RESNAME_SNCP_ADDR, String.class));
|
||||
}
|
||||
if (nodeService == null) {
|
||||
nodeService = Sncp.createLocalService(serverClassLoader, resourceName, WebSocketNodeService.class, application.getResourceFactory(), application.getTransportFactory(), (InetSocketAddress) null, (Set<String>) null, (AnyValue) null);
|
||||
nodeService = Sncp.createLocalService(serverClassLoader, resourceName, WebSocketNodeService.class, application.getResourceFactory(), application.getSncpTransportFactory(), (InetSocketAddress) null, (Set<String>) null, (AnyValue) null);
|
||||
regFactory.register(resourceName, WebSocketNode.class, nodeService);
|
||||
}
|
||||
resourceFactory.inject(nodeService, self);
|
||||
|
||||
@@ -111,7 +111,7 @@ public abstract class NodeServer {
|
||||
if (isSNCP()) { // SNCP协议
|
||||
String host = this.serverConf.getValue("host", isWATCH() ? "127.0.0.1" : "0.0.0.0").replace("0.0.0.0", "");
|
||||
this.sncpAddress = new InetSocketAddress(host.isEmpty() ? application.localAddress.getHostAddress() : host, this.serverConf.getIntValue("port"));
|
||||
this.sncpGroup = application.transportFactory.findGroupName(this.sncpAddress);
|
||||
this.sncpGroup = application.sncpTransportFactory.findGroupName(this.sncpAddress);
|
||||
//单向SNCP服务不需要对等group
|
||||
//if (this.sncpGroup == null) throw new RuntimeException("Server (" + String.valueOf(config).replaceAll("\\s+", " ") + ") not found <group> info");
|
||||
}
|
||||
@@ -135,7 +135,7 @@ public abstract class NodeServer {
|
||||
resourceFactory.register(Server.RESNAME_SERVER_ROOT, Path.class, myroot.toPath());
|
||||
|
||||
//加入指定的classpath
|
||||
Server.loadLib(serverClassLoader, logger, this.serverConf.getValue("lib", "${APP_HOME}/libs/*").replace("${APP_HOME}", application.getHome().getPath().replace('\\', '/')));
|
||||
Server.loadLib(serverClassLoader, logger, this.serverConf.getValue("lib", "").replace("${APP_HOME}", application.getHome().getPath().replace('\\', '/')));
|
||||
this.serverThread.setContextClassLoader(this.serverClassLoader);
|
||||
}
|
||||
//必须要进行初始化, 构建Service时需要使用Context中的ExecutorService
|
||||
@@ -171,7 +171,7 @@ public abstract class NodeServer {
|
||||
final NodeServer self = this;
|
||||
//---------------------------------------------------------------------------------------------
|
||||
final ResourceFactory appResFactory = application.getResourceFactory();
|
||||
final TransportFactory appTranFactory = application.getTransportFactory();
|
||||
final TransportFactory appSncpTranFactory = application.getSncpTransportFactory();
|
||||
final AnyValue resources = application.config.getAnyValue("resources");
|
||||
final Map<String, AnyValue> cacheResource = new HashMap<>();
|
||||
final Map<String, AnyValue> dataResources = new HashMap<>();
|
||||
@@ -221,7 +221,25 @@ public abstract class NodeServer {
|
||||
try {
|
||||
if (field.getAnnotation(Resource.class) == null) return;
|
||||
if ((src instanceof Service) && Sncp.isRemote((Service) src)) return; //远程模式不得注入 DataSource
|
||||
DataSource source = DataSources.createDataSource(resourceName);
|
||||
AnyValue sourceConf = dataResources.get(resourceName);
|
||||
DataSource source = null;
|
||||
boolean needinit = true;
|
||||
if (sourceConf != null) {
|
||||
final Class sourceType = serverClassLoader.loadClass(sourceConf.getValue("value"));
|
||||
if (DataSource.class.isAssignableFrom(sourceType)) { // DataSource
|
||||
final Service srcService = (Service) src;
|
||||
SncpClient client = Sncp.getSncpClient(srcService);
|
||||
final InetSocketAddress sncpAddr = client == null ? null : client.getClientAddress();
|
||||
final Set<String> groups = new HashSet<>();
|
||||
if (client != null && client.getSameGroup() != null) groups.add(client.getSameGroup());
|
||||
if (client != null && client.getDiffGroups() != null) groups.addAll(client.getDiffGroups());
|
||||
source = (DataSource) Sncp.createLocalService(serverClassLoader, resourceName, sourceType, appResFactory, appSncpTranFactory, sncpAddr, groups, Sncp.getConf(srcService));
|
||||
}
|
||||
}
|
||||
if (source == null) {
|
||||
source = DataSources.createDataSource(resourceName); //从persistence.xml配置中创建
|
||||
needinit = false;
|
||||
}
|
||||
application.dataSources.add(source);
|
||||
appResFactory.register(resourceName, DataSource.class, source);
|
||||
|
||||
@@ -232,7 +250,7 @@ public abstract class NodeServer {
|
||||
final Set<String> groups = new HashSet<>();
|
||||
if (client != null && client.getSameGroup() != null) groups.add(client.getSameGroup());
|
||||
if (client != null && client.getDiffGroups() != null) groups.addAll(client.getDiffGroups());
|
||||
Service cacheListenerService = Sncp.createLocalService(serverClassLoader, resourceName, DataCacheListenerService.class, appResFactory, appTranFactory, sncpAddr, groups, Sncp.getConf((Service) src));
|
||||
Service cacheListenerService = Sncp.createLocalService(serverClassLoader, resourceName, DataCacheListenerService.class, appResFactory, appSncpTranFactory, sncpAddr, groups, Sncp.getConf((Service) src));
|
||||
appResFactory.register(resourceName, DataCacheListener.class, cacheListenerService);
|
||||
localServices.add(cacheListenerService);
|
||||
sncpServer.consumerAccept(cacheListenerService);
|
||||
@@ -242,7 +260,7 @@ public abstract class NodeServer {
|
||||
field.set(src, source);
|
||||
rf.inject(source, self); // 给其可能包含@Resource的字段赋值;
|
||||
//NodeServer.this.watchFactory.inject(src);
|
||||
if (source instanceof Service) ((Service) source).init(null);
|
||||
if (source instanceof Service && needinit) ((Service) source).init(sourceConf);
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.SEVERE, "DataSource inject error", e);
|
||||
}
|
||||
@@ -264,20 +282,16 @@ public abstract class NodeServer {
|
||||
AnyValue sourceConf = cacheResource.get(resourceName);
|
||||
if (sourceConf == null) sourceConf = dataResources.get(resourceName);
|
||||
final Class sourceType = sourceConf == null ? CacheMemorySource.class : serverClassLoader.loadClass(sourceConf.getValue("value"));
|
||||
Object source;
|
||||
if (DataSource.class.isAssignableFrom(sourceType)) { // DataSource
|
||||
source = (DataSource) Sncp.createLocalService(serverClassLoader, resourceName, sourceType, appResFactory, appTranFactory, sncpAddr, groups, Sncp.getConf(srcService));
|
||||
application.dataSources.add((DataSource) source);
|
||||
appResFactory.register(resourceName, DataSource.class, source);
|
||||
} else { // CacheSource
|
||||
source = (CacheSource) Sncp.createLocalService(serverClassLoader, resourceName, sourceType, appResFactory, appTranFactory, sncpAddr, groups, Sncp.getConf(srcService));
|
||||
Object source = null;
|
||||
if (CacheSource.class.isAssignableFrom(sourceType)) { // CacheSource
|
||||
source = (CacheSource) Sncp.createLocalService(serverClassLoader, resourceName, sourceType, appResFactory, appSncpTranFactory, sncpAddr, groups, Sncp.getConf(srcService));
|
||||
Type genericType = field.getGenericType();
|
||||
ParameterizedType pt = (genericType instanceof ParameterizedType) ? (ParameterizedType) genericType : null;
|
||||
Type valType = pt == null ? null : pt.getActualTypeArguments()[1];
|
||||
if (sourceType == CacheMemorySource.class) {
|
||||
CacheMemorySource memorySource = (CacheMemorySource) source;
|
||||
memorySource.setStoreType(pt == null ? Serializable.class : (Class) pt.getActualTypeArguments()[0], valType instanceof Class ? (Class) valType : Object.class);
|
||||
if (field.getAnnotation(Transient.class) != null) memorySource.setNeedStore(false); //必须在setStoreType之后
|
||||
Type valType = pt == null ? null : pt.getActualTypeArguments()[0];
|
||||
if (CacheSource.class.isAssignableFrom(sourceType)) {
|
||||
CacheSource cacheSource = (CacheSource) source;
|
||||
cacheSource.initValueType(valType instanceof Class ? (Class) valType : Object.class);
|
||||
cacheSource.initTransient(field.getAnnotation(Transient.class) != null); //必须在initValueType之后
|
||||
}
|
||||
application.cacheSources.add((CacheSource) source);
|
||||
appResFactory.register(resourceName, genericType, source);
|
||||
@@ -311,7 +325,7 @@ public abstract class NodeServer {
|
||||
final Set<FilterEntry<? extends Service>> entrys = (Set) serviceFilter.getAllFilterEntrys();
|
||||
ResourceFactory regFactory = isSNCP() ? application.getResourceFactory() : resourceFactory;
|
||||
final ResourceFactory appResourceFactory = application.getResourceFactory();
|
||||
final TransportFactory appTransportFactory = application.getTransportFactory();
|
||||
final TransportFactory appSncpTransFactory = application.getSncpTransportFactory();
|
||||
for (FilterEntry<? extends Service> entry : entrys) { //service实现类
|
||||
final Class<? extends Service> serviceImplClass = entry.getType();
|
||||
if (Modifier.isFinal(serviceImplClass.getModifiers())) continue; //修饰final的类跳过
|
||||
@@ -342,9 +356,9 @@ public abstract class NodeServer {
|
||||
Service service;
|
||||
boolean ws = src instanceof WebSocketServlet;
|
||||
if (ws || localed) { //本地模式
|
||||
service = Sncp.createLocalService(serverClassLoader, resourceName, serviceImplClass, appResourceFactory, appTransportFactory, NodeServer.this.sncpAddress, groups, entry.getProperty());
|
||||
service = Sncp.createLocalService(serverClassLoader, resourceName, serviceImplClass, appResourceFactory, appSncpTransFactory, NodeServer.this.sncpAddress, groups, entry.getProperty());
|
||||
} else {
|
||||
service = Sncp.createRemoteService(serverClassLoader, resourceName, serviceImplClass, appTransportFactory, NodeServer.this.sncpAddress, groups, entry.getProperty());
|
||||
service = Sncp.createRemoteService(serverClassLoader, resourceName, serviceImplClass, appSncpTransFactory, NodeServer.this.sncpAddress, groups, entry.getProperty());
|
||||
}
|
||||
if (SncpClient.parseMethod(serviceImplClass).isEmpty() && serviceImplClass.getAnnotation(Priority.class) == null) return; //class没有可用的方法且没有标记启动优先级的, 通常为BaseService
|
||||
|
||||
|
||||
@@ -50,7 +50,7 @@ public final class CollectionDecoder<T> implements Decodeable<Reader, Collection
|
||||
factory.register(type, this);
|
||||
this.decoder = factory.loadDecoder(this.componentType);
|
||||
} else {
|
||||
throw new ConvertException("collectiondecoder not support the type (" + type + ")");
|
||||
throw new ConvertException("CollectionDecoder not support the type (" + type + ")");
|
||||
}
|
||||
} finally {
|
||||
inited = true;
|
||||
|
||||
@@ -13,9 +13,9 @@ import java.nio.ByteBuffer;
|
||||
import java.nio.channels.CompletionHandler;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.atomic.*;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Stream;
|
||||
import org.redkale.convert.ext.InetAddressSimpledCoder.InetSocketAddressSimpledCoder;
|
||||
import java.util.stream.*;
|
||||
import org.redkale.convert.ext.*;
|
||||
import org.redkale.util.*;
|
||||
|
||||
@@ -88,16 +88,17 @@ public abstract class ConvertFactory<R extends Reader, W extends Writer> {
|
||||
this.register(String.class, StringSimpledCoder.instance);
|
||||
this.register(CharSequence.class, CharSequenceSimpledCoder.instance);
|
||||
this.register(java.util.Date.class, DateSimpledCoder.instance);
|
||||
this.register(AtomicInteger.class, AtomicIntegerSimpledCoder.instance);
|
||||
this.register(AtomicLong.class, AtomicLongSimpledCoder.instance);
|
||||
this.register(BigInteger.class, BigIntegerSimpledCoder.instance);
|
||||
this.register(BigDecimal.class, BigDecimalSimpledCoder.instance);
|
||||
this.register(InetAddress.class, InetAddressSimpledCoder.instance);
|
||||
this.register(DLong.class, DLongSimpledCoder.instance);
|
||||
this.register(Class.class, TypeSimpledCoder.instance);
|
||||
this.register(InetSocketAddress.class, InetSocketAddressSimpledCoder.instance);
|
||||
this.register(InetSocketAddress.class, InetAddressSimpledCoder.InetSocketAddressSimpledCoder.instance);
|
||||
this.register(Pattern.class, PatternSimpledCoder.instance);
|
||||
this.register(File.class, FileSimpledCoder.instance);
|
||||
this.register(CompletionHandler.class, CompletionHandlerSimpledCoder.instance);
|
||||
this.register(AsyncHandler.class, AsyncHandlerSimpledCoder.instance);
|
||||
this.register(URL.class, URLSimpledCoder.instance);
|
||||
this.register(URI.class, URISimpledCoder.instance);
|
||||
//---------------------------------------------------------
|
||||
@@ -107,9 +108,12 @@ public abstract class ConvertFactory<R extends Reader, W extends Writer> {
|
||||
this.register(short[].class, ShortArraySimpledCoder.instance);
|
||||
this.register(char[].class, CharArraySimpledCoder.instance);
|
||||
this.register(int[].class, IntArraySimpledCoder.instance);
|
||||
this.register(IntStream.class, IntArraySimpledCoder.IntStreamSimpledCoder.instance);
|
||||
this.register(long[].class, LongArraySimpledCoder.instance);
|
||||
this.register(LongStream.class, LongArraySimpledCoder.LongStreamSimpledCoder.instance);
|
||||
this.register(float[].class, FloatArraySimpledCoder.instance);
|
||||
this.register(double[].class, DoubleArraySimpledCoder.instance);
|
||||
this.register(DoubleStream.class, DoubleArraySimpledCoder.DoubleStreamSimpledCoder.instance);
|
||||
this.register(String[].class, StringArraySimpledCoder.instance);
|
||||
//---------------------------------------------------------
|
||||
this.register(AnyValue.class, Creator.create(AnyValue.DefaultAnyValue.class));
|
||||
@@ -373,12 +377,17 @@ public abstract class ConvertFactory<R extends Reader, W extends Writer> {
|
||||
encoders.put(clazz, coder);
|
||||
}
|
||||
|
||||
public final <E> void register(final Type clazz, final Decodeable<R, E> decoder, final Encodeable<W, E> encoder) {
|
||||
decoders.put(clazz, decoder);
|
||||
encoders.put(clazz, encoder);
|
||||
}
|
||||
|
||||
public final <E> void register(final Type clazz, final Decodeable<R, E> decoder) {
|
||||
decoders.put(clazz, decoder);
|
||||
}
|
||||
|
||||
public final <E> void register(final Type clazz, final Encodeable<W, E> printer) {
|
||||
encoders.put(clazz, printer);
|
||||
public final <E> void register(final Type clazz, final Encodeable<W, E> encoder) {
|
||||
encoders.put(clazz, encoder);
|
||||
}
|
||||
|
||||
public final <E> Decodeable<R, E> findDecoder(final Type type) {
|
||||
@@ -459,6 +468,8 @@ public abstract class ConvertFactory<R extends Reader, W extends Writer> {
|
||||
decoder = new StreamDecoder(this, type);
|
||||
} else if (Map.class.isAssignableFrom(clazz)) {
|
||||
decoder = new MapDecoder(this, type);
|
||||
} else if (Optional.class == clazz) {
|
||||
decoder = new OptionalCoder(this, type);
|
||||
} else if (clazz == Object.class) {
|
||||
od = new ObjectDecoder(type);
|
||||
decoder = od;
|
||||
@@ -544,6 +555,8 @@ public abstract class ConvertFactory<R extends Reader, W extends Writer> {
|
||||
encoder = new StreamEncoder(this, type);
|
||||
} else if (Map.class.isAssignableFrom(clazz)) {
|
||||
encoder = new MapEncoder(this, type);
|
||||
} else if (Optional.class == clazz) {
|
||||
encoder = new OptionalCoder(this, type);
|
||||
} else if (clazz == Object.class) {
|
||||
return (Encodeable<W, E>) this.anyEncoder;
|
||||
} else if (!clazz.getName().startsWith("java.") || java.net.HttpCookie.class == clazz) {
|
||||
|
||||
@@ -50,6 +50,12 @@ public final class MapDecoder<K, V> implements Decodeable<Reader, Map<K, V>> {
|
||||
factory.register(type, this);
|
||||
this.keyDecoder = factory.loadDecoder(this.keyType);
|
||||
this.valueDecoder = factory.loadDecoder(this.valueType);
|
||||
} else if (factory.isReversible()) {
|
||||
this.keyType = Object.class;
|
||||
this.valueType = Object.class;
|
||||
this.creator = factory.loadCreator((Class) type);
|
||||
this.keyDecoder = factory.loadDecoder(this.keyType);
|
||||
this.valueDecoder = factory.loadDecoder(this.valueType);
|
||||
} else {
|
||||
throw new ConvertException("mapdecoder not support the type (" + type + ")");
|
||||
}
|
||||
|
||||
107
src/org/redkale/convert/OptionalCoder.java
Normal file
107
src/org/redkale/convert/OptionalCoder.java
Normal file
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
* To change this license header, choose License Headers in Project Properties.
|
||||
* To change this template file, choose Tools | Templates
|
||||
* and open the template in the editor.
|
||||
*/
|
||||
package org.redkale.convert;
|
||||
|
||||
import java.lang.reflect.*;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Optional 的SimpledCoder实现
|
||||
*
|
||||
* <p>
|
||||
* 详情见: https://redkale.org
|
||||
*
|
||||
* @author zhangjx
|
||||
* @param <R> Reader输入的子类型
|
||||
* @param <W> Writer输出的子类型
|
||||
*/
|
||||
public class OptionalCoder<R extends Reader, W extends Writer, T> extends SimpledCoder<R, W, Optional<T>> {
|
||||
|
||||
private final Type type;
|
||||
|
||||
private final Type componentType;
|
||||
|
||||
protected final Class componentClass;
|
||||
|
||||
protected final Decodeable<Reader, T> decoder;
|
||||
|
||||
protected final Encodeable<Writer, T> encoder;
|
||||
|
||||
private boolean inited = false;
|
||||
|
||||
private final Object lock = new Object();
|
||||
|
||||
public OptionalCoder(final ConvertFactory factory, final Type type) {
|
||||
this.type = type;
|
||||
try {
|
||||
if (type instanceof ParameterizedType) {
|
||||
final ParameterizedType pt = (ParameterizedType) type;
|
||||
this.componentType = pt.getActualTypeArguments()[0];
|
||||
factory.register(type, this);
|
||||
this.decoder = factory.loadDecoder(this.componentType);
|
||||
if (this.componentType instanceof TypeVariable) {
|
||||
this.encoder = factory.getAnyEncoder();
|
||||
this.componentClass = Object.class;
|
||||
} else {
|
||||
if (componentType instanceof ParameterizedType) {
|
||||
final ParameterizedType pt2 = (ParameterizedType) componentType;
|
||||
this.componentClass = (Class) pt2.getRawType();
|
||||
} else {
|
||||
this.componentClass = (Class) componentType;
|
||||
}
|
||||
this.encoder = factory.loadEncoder(this.componentType);
|
||||
}
|
||||
} else {
|
||||
this.componentType = Object.class;
|
||||
this.componentClass = Object.class;
|
||||
this.decoder = factory.loadDecoder(this.componentType);
|
||||
this.encoder = factory.getAnyEncoder();
|
||||
}
|
||||
} finally {
|
||||
inited = true;
|
||||
synchronized (lock) {
|
||||
lock.notifyAll();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void convertTo(W out, Optional<T> value) {
|
||||
if (value == null || !value.isPresent()) {
|
||||
out.writeObjectNull(null);
|
||||
return;
|
||||
}
|
||||
if (this.encoder == null) {
|
||||
if (!this.inited) {
|
||||
synchronized (lock) {
|
||||
try {
|
||||
lock.wait();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
this.encoder.convertTo(out, value.get());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<T> convertFrom(R in) {
|
||||
if (this.decoder == null) {
|
||||
if (!this.inited) {
|
||||
synchronized (lock) {
|
||||
try {
|
||||
lock.wait();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return Optional.ofNullable(this.decoder.convertFrom(in));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -73,7 +73,7 @@ public final class BsonConvert extends BinaryConvert<BsonReader, BsonWriter> {
|
||||
}
|
||||
|
||||
public void offerBsonReader(final BsonReader in) {
|
||||
if (in != null) readerPool.offer(in);
|
||||
if (in != null) readerPool.accept(in);
|
||||
}
|
||||
|
||||
//------------------------------ writer -----------------------------------------------------------
|
||||
@@ -90,7 +90,7 @@ public final class BsonConvert extends BinaryConvert<BsonReader, BsonWriter> {
|
||||
}
|
||||
|
||||
public void offerBsonWriter(final BsonWriter out) {
|
||||
if (out != null) writerPool.offer(out);
|
||||
if (out != null) writerPool.accept(out);
|
||||
}
|
||||
|
||||
//------------------------------ convertFrom -----------------------------------------------------------
|
||||
@@ -106,7 +106,7 @@ public final class BsonConvert extends BinaryConvert<BsonReader, BsonWriter> {
|
||||
in.setBytes(bytes, start, len);
|
||||
@SuppressWarnings("unchecked")
|
||||
T rs = (T) factory.loadDecoder(type).convertFrom(in);
|
||||
readerPool.offer(in);
|
||||
readerPool.accept(in);
|
||||
return rs;
|
||||
}
|
||||
|
||||
@@ -145,7 +145,7 @@ public final class BsonConvert extends BinaryConvert<BsonReader, BsonWriter> {
|
||||
final BsonWriter out = writerPool.get().tiny(tiny);
|
||||
out.writeNull();
|
||||
byte[] result = out.toArray();
|
||||
writerPool.offer(out);
|
||||
writerPool.accept(out);
|
||||
return result;
|
||||
}
|
||||
return convertTo(value.getClass(), value);
|
||||
@@ -157,7 +157,7 @@ public final class BsonConvert extends BinaryConvert<BsonReader, BsonWriter> {
|
||||
final BsonWriter out = writerPool.get().tiny(tiny);
|
||||
factory.loadEncoder(type).convertTo(out, value);
|
||||
byte[] result = out.toArray();
|
||||
writerPool.offer(out);
|
||||
writerPool.accept(out);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -167,7 +167,7 @@ public final class BsonConvert extends BinaryConvert<BsonReader, BsonWriter> {
|
||||
final BsonWriter out = writerPool.get().tiny(tiny);
|
||||
((AnyEncoder) factory.getAnyEncoder()).convertMapTo(out, values);
|
||||
byte[] result = out.toArray();
|
||||
writerPool.offer(out);
|
||||
writerPool.accept(out);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
/*
|
||||
* To change this license header, choose License Headers in Project Properties.
|
||||
* To change this template file, choose Tools | Templates
|
||||
* and open the template in the editor.
|
||||
*/
|
||||
package org.redkale.convert.ext;
|
||||
|
||||
import org.redkale.convert.*;
|
||||
import org.redkale.util.AsyncHandler;
|
||||
|
||||
/**
|
||||
* AsyncHandlerSimpledCoder 的SimpledCoder实现, 只输出null
|
||||
*
|
||||
* <p>
|
||||
* 详情见: https://redkale.org
|
||||
*
|
||||
* @author zhangjx
|
||||
* @param <R> Reader输入的子类型
|
||||
* @param <W> Writer输出的子类型
|
||||
*/
|
||||
public final class AsyncHandlerSimpledCoder<R extends Reader, W extends Writer> extends SimpledCoder<R, W, AsyncHandler> {
|
||||
|
||||
public static final AsyncHandlerSimpledCoder instance = new AsyncHandlerSimpledCoder();
|
||||
|
||||
@Override
|
||||
public void convertTo(W out, AsyncHandler value) {
|
||||
out.writeObjectNull(AsyncHandler.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AsyncHandler convertFrom(R in) {
|
||||
in.readObjectB(AsyncHandler.class);
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
35
src/org/redkale/convert/ext/AtomicIntegerSimpledCoder.java
Normal file
35
src/org/redkale/convert/ext/AtomicIntegerSimpledCoder.java
Normal file
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* To change this license header, choose License Headers in Project Properties.
|
||||
* To change this template file, choose Tools | Templates
|
||||
* and open the template in the editor.
|
||||
*/
|
||||
package org.redkale.convert.ext;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import org.redkale.convert.*;
|
||||
|
||||
/**
|
||||
* AtomicInteger 的SimpledCoder实现
|
||||
*
|
||||
* <p>
|
||||
* 详情见: https://redkale.org
|
||||
*
|
||||
* @author zhangjx
|
||||
* @param <R> Reader输入的子类型
|
||||
* @param <W> Writer输出的子类型
|
||||
*/
|
||||
public class AtomicIntegerSimpledCoder<R extends Reader, W extends Writer> extends SimpledCoder<R, W, AtomicInteger> {
|
||||
|
||||
public static final AtomicIntegerSimpledCoder instance = new AtomicIntegerSimpledCoder();
|
||||
|
||||
@Override
|
||||
public void convertTo(W out, AtomicInteger value) {
|
||||
out.writeInt(value == null ? 0 : value.get());
|
||||
}
|
||||
|
||||
@Override
|
||||
public AtomicInteger convertFrom(R in) {
|
||||
return new AtomicInteger(in.readInt());
|
||||
}
|
||||
|
||||
}
|
||||
35
src/org/redkale/convert/ext/AtomicLongSimpledCoder.java
Normal file
35
src/org/redkale/convert/ext/AtomicLongSimpledCoder.java
Normal file
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* To change this license header, choose License Headers in Project Properties.
|
||||
* To change this template file, choose Tools | Templates
|
||||
* and open the template in the editor.
|
||||
*/
|
||||
package org.redkale.convert.ext;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import org.redkale.convert.*;
|
||||
|
||||
/**
|
||||
* AtomicLong 的SimpledCoder实现
|
||||
*
|
||||
* <p>
|
||||
* 详情见: https://redkale.org
|
||||
*
|
||||
* @author zhangjx
|
||||
* @param <R> Reader输入的子类型
|
||||
* @param <W> Writer输出的子类型
|
||||
*/
|
||||
public final class AtomicLongSimpledCoder<R extends Reader, W extends Writer> extends SimpledCoder<R, W, AtomicLong> {
|
||||
|
||||
public static final AtomicLongSimpledCoder instance = new AtomicLongSimpledCoder();
|
||||
|
||||
@Override
|
||||
public void convertTo(W out, AtomicLong value) {
|
||||
out.writeLong(value == null ? 0 : value.get());
|
||||
}
|
||||
|
||||
@Override
|
||||
public AtomicLong convertFrom(R in) {
|
||||
return new AtomicLong(in.readLong());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -5,6 +5,7 @@
|
||||
*/
|
||||
package org.redkale.convert.ext;
|
||||
|
||||
import java.util.stream.DoubleStream;
|
||||
import org.redkale.convert.Reader;
|
||||
import org.redkale.convert.SimpledCoder;
|
||||
import org.redkale.convert.Writer;
|
||||
@@ -12,7 +13,9 @@ import org.redkale.convert.Writer;
|
||||
/**
|
||||
* double[] 的SimpledCoder实现
|
||||
*
|
||||
* <p> 详情见: https://redkale.org
|
||||
* <p>
|
||||
* 详情见: https://redkale.org
|
||||
*
|
||||
* @author zhangjx
|
||||
* @param <R> Reader输入的子类型
|
||||
* @param <W> Writer输出的子类型
|
||||
@@ -66,4 +69,24 @@ public final class DoubleArraySimpledCoder<R extends Reader, W extends Writer> e
|
||||
}
|
||||
}
|
||||
|
||||
public final static class DoubleStreamSimpledCoder<R extends Reader, W extends Writer> extends SimpledCoder<R, W, DoubleStream> {
|
||||
|
||||
public static final DoubleStreamSimpledCoder instance = new DoubleStreamSimpledCoder();
|
||||
|
||||
@Override
|
||||
public void convertTo(W out, DoubleStream values) {
|
||||
if (values == null) {
|
||||
out.writeNull();
|
||||
return;
|
||||
}
|
||||
DoubleArraySimpledCoder.instance.convertTo(out, values.toArray());
|
||||
}
|
||||
|
||||
@Override
|
||||
public DoubleStream convertFrom(R in) {
|
||||
double[] value = DoubleArraySimpledCoder.instance.convertFrom(in);
|
||||
return value == null ? null : DoubleStream.of(value);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
*/
|
||||
package org.redkale.convert.ext;
|
||||
|
||||
import java.util.stream.IntStream;
|
||||
import org.redkale.convert.Reader;
|
||||
import org.redkale.convert.SimpledCoder;
|
||||
import org.redkale.convert.Writer;
|
||||
@@ -12,7 +13,9 @@ import org.redkale.convert.Writer;
|
||||
/**
|
||||
* int[] 的SimpledCoder实现
|
||||
*
|
||||
* <p> 详情见: https://redkale.org
|
||||
* <p>
|
||||
* 详情见: https://redkale.org
|
||||
*
|
||||
* @author zhangjx
|
||||
* @param <R> Reader输入的子类型
|
||||
* @param <W> Writer输出的子类型
|
||||
@@ -66,4 +69,24 @@ public final class IntArraySimpledCoder<R extends Reader, W extends Writer> exte
|
||||
}
|
||||
}
|
||||
|
||||
public final static class IntStreamSimpledCoder<R extends Reader, W extends Writer> extends SimpledCoder<R, W, IntStream> {
|
||||
|
||||
public static final IntStreamSimpledCoder instance = new IntStreamSimpledCoder();
|
||||
|
||||
@Override
|
||||
public void convertTo(W out, IntStream values) {
|
||||
if (values == null) {
|
||||
out.writeNull();
|
||||
return;
|
||||
}
|
||||
IntArraySimpledCoder.instance.convertTo(out, values.toArray());
|
||||
}
|
||||
|
||||
@Override
|
||||
public IntStream convertFrom(R in) {
|
||||
int[] value = IntArraySimpledCoder.instance.convertFrom(in);
|
||||
return value == null ? null : IntStream.of(value);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
*/
|
||||
package org.redkale.convert.ext;
|
||||
|
||||
import java.util.stream.LongStream;
|
||||
import org.redkale.convert.Reader;
|
||||
import org.redkale.convert.SimpledCoder;
|
||||
import org.redkale.convert.Writer;
|
||||
@@ -68,4 +69,24 @@ public final class LongArraySimpledCoder<R extends Reader, W extends Writer> ext
|
||||
}
|
||||
}
|
||||
|
||||
public final static class LongStreamSimpledCoder<R extends Reader, W extends Writer> extends SimpledCoder<R, W, LongStream> {
|
||||
|
||||
public static final LongStreamSimpledCoder instance = new LongStreamSimpledCoder();
|
||||
|
||||
@Override
|
||||
public void convertTo(W out, LongStream values) {
|
||||
if (values == null) {
|
||||
out.writeNull();
|
||||
return;
|
||||
}
|
||||
LongArraySimpledCoder.instance.convertTo(out, values.toArray());
|
||||
}
|
||||
|
||||
@Override
|
||||
public LongStream convertFrom(R in) {
|
||||
long[] value = LongArraySimpledCoder.instance.convertFrom(in);
|
||||
return value == null ? null : LongStream.of(value);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ import org.redkale.util.*;
|
||||
@SuppressWarnings("unchecked")
|
||||
public final class JsonConvert extends TextConvert<JsonReader, JsonWriter> {
|
||||
|
||||
public static final Type TYPE_MAP_STRING_STRING = new TypeToken<java.util.LinkedHashMap<String, String>>() {
|
||||
public static final Type TYPE_MAP_STRING_STRING = new TypeToken<java.util.HashMap<String, String>>() {
|
||||
}.getType();
|
||||
|
||||
private static final ObjectPool<JsonReader> readerPool = JsonReader.createPool(Integer.getInteger("convert.json.pool.size", 16));
|
||||
@@ -60,7 +60,7 @@ public final class JsonConvert extends TextConvert<JsonReader, JsonWriter> {
|
||||
}
|
||||
|
||||
public void offerJsonReader(final JsonReader in) {
|
||||
if (in != null) readerPool.offer(in);
|
||||
if (in != null) readerPool.accept(in);
|
||||
}
|
||||
|
||||
//------------------------------ writer -----------------------------------------------------------
|
||||
@@ -81,7 +81,7 @@ public final class JsonConvert extends TextConvert<JsonReader, JsonWriter> {
|
||||
}
|
||||
|
||||
public void offerJsonWriter(final JsonWriter out) {
|
||||
if (out != null) writerPool.offer(out);
|
||||
if (out != null) writerPool.accept(out);
|
||||
}
|
||||
|
||||
//------------------------------ convertFrom -----------------------------------------------------------
|
||||
@@ -100,7 +100,7 @@ public final class JsonConvert extends TextConvert<JsonReader, JsonWriter> {
|
||||
final JsonReader in = readerPool.get();
|
||||
in.setText(text, start, len);
|
||||
T rs = (T) factory.loadDecoder(type).convertFrom(in);
|
||||
readerPool.offer(in);
|
||||
readerPool.accept(in);
|
||||
return rs;
|
||||
}
|
||||
|
||||
@@ -142,7 +142,7 @@ public final class JsonConvert extends TextConvert<JsonReader, JsonWriter> {
|
||||
final JsonWriter out = writerPool.get().tiny(tiny);
|
||||
factory.loadEncoder(type).convertTo(out, value);
|
||||
String result = out.toString();
|
||||
writerPool.offer(out);
|
||||
writerPool.accept(out);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -152,7 +152,7 @@ public final class JsonConvert extends TextConvert<JsonReader, JsonWriter> {
|
||||
final JsonWriter out = writerPool.get().tiny(tiny);
|
||||
((AnyEncoder) factory.getAnyEncoder()).convertMapTo(out, values);
|
||||
String result = out.toString();
|
||||
writerPool.offer(out);
|
||||
writerPool.accept(out);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -160,7 +160,7 @@ public final class JsonConvert extends TextConvert<JsonReader, JsonWriter> {
|
||||
if (value == null) {
|
||||
new JsonStreamWriter(tiny, out).writeNull();
|
||||
} else {
|
||||
factory.loadEncoder(value.getClass()).convertTo(new JsonStreamWriter(tiny, out), value);
|
||||
convertTo(out, value.getClass(), value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -169,7 +169,15 @@ public final class JsonConvert extends TextConvert<JsonReader, JsonWriter> {
|
||||
if (value == null) {
|
||||
new JsonStreamWriter(tiny, out).writeNull();
|
||||
} else {
|
||||
factory.loadEncoder(type).convertTo(new JsonStreamWriter(tiny, out), value);
|
||||
final JsonWriter writer = writerPool.get().tiny(tiny);
|
||||
factory.loadEncoder(type).convertTo(writer, value);
|
||||
byte[] bs = writer.toBytes();
|
||||
writerPool.accept(writer);
|
||||
try {
|
||||
out.write(bs);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -177,7 +185,15 @@ public final class JsonConvert extends TextConvert<JsonReader, JsonWriter> {
|
||||
if (values == null) {
|
||||
new JsonStreamWriter(tiny, out).writeNull();
|
||||
} else {
|
||||
((AnyEncoder) factory.getAnyEncoder()).convertMapTo(new JsonStreamWriter(tiny, out), values);
|
||||
final JsonWriter writer = writerPool.get().tiny(tiny);
|
||||
((AnyEncoder) factory.getAnyEncoder()).convertMapTo(writer, values);
|
||||
byte[] bs = writer.toBytes();
|
||||
writerPool.accept(writer);
|
||||
try {
|
||||
out.write(bs);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -162,6 +162,7 @@ public class JsonReader extends Reader {
|
||||
* 判断下一个非空白字符是否为{
|
||||
*
|
||||
* @param clazz 类名
|
||||
*
|
||||
* @return 返回 null 表示对象为null, 返回空字符串表示当前class与返回的class一致,返回非空字符串表示class是当前class的子类。
|
||||
*/
|
||||
@Override
|
||||
@@ -404,7 +405,7 @@ public class JsonReader extends Reader {
|
||||
@Override
|
||||
public final DeMember readFieldName(final DeMember[] members) {
|
||||
final String exceptedfield = this.readSmallString();
|
||||
if(exceptedfield == null) return null;
|
||||
if (exceptedfield == null) return null;
|
||||
final int len = members.length;
|
||||
if (this.fieldIndex >= len) this.fieldIndex = 0;
|
||||
for (int k = this.fieldIndex; k < len; k++) {
|
||||
@@ -475,12 +476,22 @@ public class JsonReader extends Reader {
|
||||
}
|
||||
}
|
||||
if (expected != '"' && expected != '\'') {
|
||||
if (expected == 'n' && text0.length > currpos + 3) {
|
||||
if (expected == 'n' && text0.length > currpos + 3 && (text0[1 + currpos] == 'u' && text0[2 + currpos] == 'l' && text0[3 + currpos] == 'l')) {
|
||||
if (text0[++currpos] == 'u' && text0[++currpos] == 'l' && text0[++currpos] == 'l') {
|
||||
this.position = currpos;
|
||||
if (text0.length > currpos + 4) {
|
||||
char ch = text0[currpos + 1];
|
||||
if (ch == ',' || ch <= ' ' || ch == '}' || ch == ']' || ch == ':') return null;
|
||||
final int start = currpos - 3;
|
||||
for (;;) {
|
||||
if (currpos >= text0.length) break;
|
||||
ch = text0[currpos];
|
||||
if (ch == ',' || ch <= ' ' || ch == '}' || ch == ']' || ch == ':') break;
|
||||
currpos++;
|
||||
}
|
||||
if (currpos == start) throw new ConvertException("expected a string after a key but '" + text0[position] + "' (position = " + position + ") in (" + new String(this.text) + ")");
|
||||
this.position = currpos - 1;
|
||||
return new String(text0, start, currpos - start);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
@@ -488,6 +499,7 @@ public class JsonReader extends Reader {
|
||||
} else {
|
||||
final int start = currpos;
|
||||
for (;;) {
|
||||
if (currpos >= text0.length) break;
|
||||
char ch = text0[currpos];
|
||||
if (ch == ',' || ch <= ' ' || ch == '}' || ch == ']' || ch == ':') break;
|
||||
currpos++;
|
||||
|
||||
@@ -59,6 +59,7 @@ public class JsonWriter extends Writer {
|
||||
* 返回指定至少指定长度的缓冲区
|
||||
*
|
||||
* @param len
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
private char[] expand(int len) {
|
||||
@@ -108,6 +109,10 @@ public class JsonWriter extends Writer {
|
||||
return new ByteBuffer[]{ByteBuffer.wrap(Utility.encodeUTF8(content, 0, count))};
|
||||
}
|
||||
|
||||
public byte[] toBytes() {
|
||||
return Utility.encodeUTF8(content, 0, count);
|
||||
}
|
||||
|
||||
public int count() {
|
||||
return this.count;
|
||||
}
|
||||
|
||||
@@ -26,11 +26,23 @@ public abstract class AsyncConnection implements AsynchronousByteChannel, AutoCl
|
||||
|
||||
protected Object subobject; //用于存储绑定在Connection上的对象, 同attributes, 只绑定单个对象时尽量使用subobject而非attributes
|
||||
|
||||
protected volatile long readtime;
|
||||
|
||||
protected volatile long writetime;
|
||||
|
||||
//关闭数
|
||||
AtomicLong closedCounter = new AtomicLong();
|
||||
AtomicLong closedCounter;
|
||||
|
||||
//在线数
|
||||
AtomicLong livingCounter = new AtomicLong();
|
||||
AtomicLong livingCounter;
|
||||
|
||||
public final long getLastReadTime() {
|
||||
return readtime;
|
||||
}
|
||||
|
||||
public final long getLastWriteTime() {
|
||||
return writetime;
|
||||
}
|
||||
|
||||
public abstract boolean isTCP();
|
||||
|
||||
@@ -110,40 +122,58 @@ public abstract class AsyncConnection implements AsynchronousByteChannel, AutoCl
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------------------------------------------------------
|
||||
public static AsyncConnection create(final String protocol, final AsynchronousChannelGroup group, final SocketAddress address) throws IOException {
|
||||
return create(protocol, group, address, 0, 0);
|
||||
/**
|
||||
* 创建TCP协议客户端连接
|
||||
*
|
||||
* @param address 连接点子
|
||||
* @param group 连接AsynchronousChannelGroup
|
||||
* @param readTimeoutSecond 读取超时秒数
|
||||
* @param writeTimeoutSecond 写入超时秒数
|
||||
*
|
||||
* @return 连接CompletableFuture
|
||||
*/
|
||||
public static CompletableFuture<AsyncConnection> createTCP(final AsynchronousChannelGroup group, final SocketAddress address,
|
||||
final int readTimeoutSecond, final int writeTimeoutSecond) {
|
||||
return createTCP(group, address, false, readTimeoutSecond, writeTimeoutSecond);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建客户端连接
|
||||
* 创建TCP协议客户端连接
|
||||
*
|
||||
* @param protocol 连接类型 只能是TCP或UDP
|
||||
* @param address 连接点子
|
||||
* @param group 连接AsynchronousChannelGroup
|
||||
* @param readTimeoutSecond0 读取超时秒数
|
||||
* @param writeTimeoutSecond0 写入超时秒数
|
||||
* @param address 连接点子
|
||||
* @param group 连接AsynchronousChannelGroup
|
||||
* @param noDelay TcpNoDelay
|
||||
* @param readTimeoutSecond 读取超时秒数
|
||||
* @param writeTimeoutSecond 写入超时秒数
|
||||
*
|
||||
* @return 连接
|
||||
* @throws java.io.IOException 异常
|
||||
* @return 连接CompletableFuture
|
||||
*/
|
||||
public static AsyncConnection create(final String protocol, final AsynchronousChannelGroup group, final SocketAddress address,
|
||||
final int readTimeoutSecond0, final int writeTimeoutSecond0) throws IOException {
|
||||
if ("TCP".equalsIgnoreCase(protocol)) {
|
||||
AsynchronousSocketChannel channel = AsynchronousSocketChannel.open(group);
|
||||
try {
|
||||
channel.connect(address).get(3, TimeUnit.SECONDS);
|
||||
} catch (Exception e) {
|
||||
throw new IOException("AsyncConnection connect " + address, e);
|
||||
}
|
||||
return create(channel, address, readTimeoutSecond0, writeTimeoutSecond0);
|
||||
} else if ("UDP".equalsIgnoreCase(protocol)) {
|
||||
DatagramChannel channel = DatagramChannel.open();
|
||||
channel.configureBlocking(true);
|
||||
channel.connect(address);
|
||||
return create(channel, address, true, readTimeoutSecond0, writeTimeoutSecond0);
|
||||
} else {
|
||||
throw new RuntimeException("AsyncConnection not support protocol " + protocol);
|
||||
public static CompletableFuture<AsyncConnection> createTCP(final AsynchronousChannelGroup group, final SocketAddress address,
|
||||
final boolean noDelay, final int readTimeoutSecond, final int writeTimeoutSecond) {
|
||||
final CompletableFuture future = new CompletableFuture();
|
||||
try {
|
||||
final AsynchronousSocketChannel channel = AsynchronousSocketChannel.open(group);
|
||||
channel.connect(address, null, new CompletionHandler<Void, Void>() {
|
||||
@Override
|
||||
public void completed(Void result, Void attachment) {
|
||||
if (noDelay) {
|
||||
try {
|
||||
channel.setOption(StandardSocketOptions.TCP_NODELAY, true);
|
||||
} catch (IOException e) {
|
||||
}
|
||||
}
|
||||
future.complete(create(channel, address, readTimeoutSecond, writeTimeoutSecond));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void failed(Throwable exc, Void attachment) {
|
||||
future.completeExceptionally(exc);
|
||||
}
|
||||
});
|
||||
} catch (IOException e) {
|
||||
future.completeExceptionally(e);
|
||||
}
|
||||
return future;
|
||||
}
|
||||
|
||||
private static class BIOUDPAsyncConnection extends AsyncConnection {
|
||||
@@ -209,6 +239,7 @@ public abstract class AsyncConnection implements AsynchronousByteChannel, AutoCl
|
||||
rs += channel.send(srcs[i], remoteAddress);
|
||||
if (i != offset) Thread.sleep(10);
|
||||
}
|
||||
this.writetime = System.currentTimeMillis();
|
||||
if (handler != null) handler.completed(rs, attachment);
|
||||
} catch (Exception e) {
|
||||
if (handler != null) handler.failed(e, attachment);
|
||||
@@ -219,6 +250,7 @@ public abstract class AsyncConnection implements AsynchronousByteChannel, AutoCl
|
||||
public <A> void read(ByteBuffer dst, A attachment, CompletionHandler<Integer, ? super A> handler) {
|
||||
try {
|
||||
int rs = channel.read(dst);
|
||||
this.readtime = System.currentTimeMillis();
|
||||
if (handler != null) handler.completed(rs, attachment);
|
||||
} catch (IOException e) {
|
||||
if (handler != null) handler.failed(e, attachment);
|
||||
@@ -229,6 +261,7 @@ public abstract class AsyncConnection implements AsynchronousByteChannel, AutoCl
|
||||
public Future<Integer> read(ByteBuffer dst) {
|
||||
try {
|
||||
int rs = channel.read(dst);
|
||||
this.readtime = System.currentTimeMillis();
|
||||
return CompletableFuture.completedFuture(rs);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
@@ -239,6 +272,7 @@ public abstract class AsyncConnection implements AsynchronousByteChannel, AutoCl
|
||||
public <A> void write(ByteBuffer src, A attachment, CompletionHandler<Integer, ? super A> handler) {
|
||||
try {
|
||||
int rs = channel.send(src, remoteAddress);
|
||||
this.writetime = System.currentTimeMillis();
|
||||
if (handler != null) handler.completed(rs, attachment);
|
||||
} catch (IOException e) {
|
||||
if (handler != null) handler.failed(e, attachment);
|
||||
@@ -249,6 +283,7 @@ public abstract class AsyncConnection implements AsynchronousByteChannel, AutoCl
|
||||
public Future<Integer> write(ByteBuffer src) {
|
||||
try {
|
||||
int rs = channel.send(src, remoteAddress);
|
||||
this.writetime = System.currentTimeMillis();
|
||||
return CompletableFuture.completedFuture(rs);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
@@ -359,6 +394,7 @@ public abstract class AsyncConnection implements AsynchronousByteChannel, AutoCl
|
||||
for (int i = offset; i < offset + length; i++) {
|
||||
rs += writeChannel.write(srcs[i]);
|
||||
}
|
||||
this.writetime = System.currentTimeMillis();
|
||||
if (handler != null) handler.completed(rs, attachment);
|
||||
} catch (IOException e) {
|
||||
if (handler != null) handler.failed(e, attachment);
|
||||
@@ -369,6 +405,7 @@ public abstract class AsyncConnection implements AsynchronousByteChannel, AutoCl
|
||||
public <A> void read(ByteBuffer dst, A attachment, CompletionHandler<Integer, ? super A> handler) {
|
||||
try {
|
||||
int rs = readChannel.read(dst);
|
||||
this.readtime = System.currentTimeMillis();
|
||||
if (handler != null) handler.completed(rs, attachment);
|
||||
} catch (IOException e) {
|
||||
if (handler != null) handler.failed(e, attachment);
|
||||
@@ -379,6 +416,7 @@ public abstract class AsyncConnection implements AsynchronousByteChannel, AutoCl
|
||||
public Future<Integer> read(ByteBuffer dst) {
|
||||
try {
|
||||
int rs = readChannel.read(dst);
|
||||
this.readtime = System.currentTimeMillis();
|
||||
return CompletableFuture.completedFuture(rs);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
@@ -389,6 +427,7 @@ public abstract class AsyncConnection implements AsynchronousByteChannel, AutoCl
|
||||
public <A> void write(ByteBuffer src, A attachment, CompletionHandler<Integer, ? super A> handler) {
|
||||
try {
|
||||
int rs = writeChannel.write(src);
|
||||
this.writetime = System.currentTimeMillis();
|
||||
if (handler != null) handler.completed(rs, attachment);
|
||||
} catch (IOException e) {
|
||||
if (handler != null) handler.failed(e, attachment);
|
||||
@@ -399,6 +438,7 @@ public abstract class AsyncConnection implements AsynchronousByteChannel, AutoCl
|
||||
public Future<Integer> write(ByteBuffer src) {
|
||||
try {
|
||||
int rs = writeChannel.write(src);
|
||||
this.writetime = System.currentTimeMillis();
|
||||
return CompletableFuture.completedFuture(rs);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
@@ -459,6 +499,7 @@ public abstract class AsyncConnection implements AsynchronousByteChannel, AutoCl
|
||||
|
||||
@Override
|
||||
public <A> void read(ByteBuffer dst, A attachment, CompletionHandler<Integer, ? super A> handler) {
|
||||
this.readtime = System.currentTimeMillis();
|
||||
if (readTimeoutSecond > 0) {
|
||||
channel.read(dst, readTimeoutSecond, TimeUnit.SECONDS, attachment, handler);
|
||||
} else {
|
||||
@@ -468,6 +509,7 @@ public abstract class AsyncConnection implements AsynchronousByteChannel, AutoCl
|
||||
|
||||
@Override
|
||||
public <A> void write(ByteBuffer src, A attachment, CompletionHandler<Integer, ? super A> handler) {
|
||||
this.writetime = System.currentTimeMillis();
|
||||
if (writeTimeoutSecond > 0) {
|
||||
channel.write(src, writeTimeoutSecond, TimeUnit.SECONDS, attachment, handler);
|
||||
} else {
|
||||
@@ -477,6 +519,7 @@ public abstract class AsyncConnection implements AsynchronousByteChannel, AutoCl
|
||||
|
||||
@Override
|
||||
public <A> void write(ByteBuffer[] srcs, int offset, int length, A attachment, final CompletionHandler<Integer, ? super A> handler) {
|
||||
this.writetime = System.currentTimeMillis();
|
||||
channel.write(srcs, offset, length, writeTimeoutSecond > 0 ? writeTimeoutSecond : 60, TimeUnit.SECONDS,
|
||||
attachment, new CompletionHandler<Long, A>() {
|
||||
|
||||
@@ -559,8 +602,8 @@ public abstract class AsyncConnection implements AsynchronousByteChannel, AutoCl
|
||||
return create(ch, null, 0, 0);
|
||||
}
|
||||
|
||||
public static AsyncConnection create(final AsynchronousSocketChannel ch, final SocketAddress addr0, final int readTimeoutSecond0, final int writeTimeoutSecond0) {
|
||||
return new AIOTCPAsyncConnection(ch, addr0, readTimeoutSecond0, writeTimeoutSecond0);
|
||||
public static AsyncConnection create(final AsynchronousSocketChannel ch, final SocketAddress addr0, final int readTimeoutSecond, final int writeTimeoutSecond) {
|
||||
return new AIOTCPAsyncConnection(ch, addr0, readTimeoutSecond, writeTimeoutSecond);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -5,8 +5,10 @@
|
||||
*/
|
||||
package org.redkale.net;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.*;
|
||||
import java.nio.*;
|
||||
import java.nio.channels.AsynchronousChannelGroup;
|
||||
import java.nio.charset.*;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.function.*;
|
||||
@@ -107,6 +109,10 @@ public class Context {
|
||||
return executor.submit(r);
|
||||
}
|
||||
|
||||
public AsynchronousChannelGroup createAsynchronousChannelGroup() throws IOException {
|
||||
return AsynchronousChannelGroup.withThreadPool(executor);
|
||||
}
|
||||
|
||||
public void runAsync(Runnable r) {
|
||||
executor.execute(r);
|
||||
}
|
||||
@@ -124,13 +130,13 @@ public class Context {
|
||||
}
|
||||
|
||||
public void offerBuffer(ByteBuffer buffer) {
|
||||
bufferPool.offer(buffer);
|
||||
bufferPool.accept(buffer);
|
||||
}
|
||||
|
||||
public void offerBuffer(ByteBuffer... buffers) {
|
||||
if (buffers == null) return;
|
||||
for (ByteBuffer buffer : buffers) {
|
||||
bufferPool.offer(buffer);
|
||||
bufferPool.accept(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ public abstract class Filter<C extends Context, R extends Request<C>, P extends
|
||||
}
|
||||
|
||||
@Override
|
||||
public final int compareTo(Object o) {
|
||||
public int compareTo(Object o) {
|
||||
if (!(o instanceof Filter)) return 1;
|
||||
Priority p1 = this.getClass().getAnnotation(Priority.class);
|
||||
Priority p2 = o.getClass().getAnnotation(Priority.class);
|
||||
|
||||
@@ -32,6 +32,9 @@ public abstract class ProtocolServer {
|
||||
//在线数
|
||||
protected final AtomicLong livingCounter = new AtomicLong();
|
||||
|
||||
//最大连接数,小于1表示无限制
|
||||
protected int maxconns;
|
||||
|
||||
public abstract void open() throws IOException;
|
||||
|
||||
public abstract void bind(SocketAddress local, int backlog) throws IOException;
|
||||
@@ -42,6 +45,10 @@ public abstract class ProtocolServer {
|
||||
|
||||
public abstract void accept();
|
||||
|
||||
public void setMaxconns(int maxconns) {
|
||||
this.maxconns = maxconns;
|
||||
}
|
||||
|
||||
public abstract void close() throws IOException;
|
||||
|
||||
public abstract AsynchronousChannelGroup getChannelGroup();
|
||||
@@ -198,6 +205,13 @@ public abstract class ProtocolServer {
|
||||
@Override
|
||||
public void completed(final AsynchronousSocketChannel channel, Void attachment) {
|
||||
serchannel.accept(null, this);
|
||||
if (maxconns > 0 && livingCounter.get() >= maxconns) {
|
||||
try {
|
||||
channel.close();
|
||||
} catch (Exception e) {
|
||||
}
|
||||
return;
|
||||
}
|
||||
createCounter.incrementAndGet();
|
||||
livingCounter.incrementAndGet();
|
||||
AsyncConnection conn = AsyncConnection.create(channel, null, context.readTimeoutSecond, context.writeTimeoutSecond);
|
||||
|
||||
@@ -195,7 +195,7 @@ public abstract class Response<C extends Context, R extends Request<C>> {
|
||||
if (!this.inited) return; //避免重复关闭
|
||||
//System.println("耗时: " + (System.currentTimeMillis() - request.createtime));
|
||||
if (kill) refuseAlive();
|
||||
this.context.responsePool.offer(this);
|
||||
this.context.responsePool.accept(this);
|
||||
}
|
||||
|
||||
public void finish(final byte[] bs) {
|
||||
|
||||
@@ -88,6 +88,9 @@ public abstract class Server<K extends Serializable, C extends Context, R extend
|
||||
//IO写入 的超时秒数,小于1视为不设置
|
||||
protected int writeTimeoutSecond;
|
||||
|
||||
//最大连接数
|
||||
protected int maxconns;
|
||||
|
||||
protected Server(long serverStartTime, String protocol, PrepareServlet<K, C, R, P, S> servlet) {
|
||||
this.serverStartTime = serverStartTime;
|
||||
this.protocol = protocol;
|
||||
@@ -99,6 +102,7 @@ public abstract class Server<K extends Serializable, C extends Context, R extend
|
||||
this.config = config;
|
||||
this.address = new InetSocketAddress(config.getValue("host", "0.0.0.0"), config.getIntValue("port", 80));
|
||||
this.charset = Charset.forName(config.getValue("charset", "UTF-8"));
|
||||
this.maxconns = config.getIntValue("maxconns", 0);
|
||||
this.readTimeoutSecond = config.getIntValue("readTimeoutSecond", 0);
|
||||
this.writeTimeoutSecond = config.getIntValue("writeTimeoutSecond", 0);
|
||||
this.backlog = parseLenth(config.getValue("backlog"), 8 * 1024);
|
||||
@@ -187,7 +191,8 @@ public abstract class Server<K extends Serializable, C extends Context, R extend
|
||||
this.serverChannel.setOption(StandardSocketOptions.TCP_NODELAY, true);
|
||||
}
|
||||
serverChannel.bind(address, backlog);
|
||||
serverChannel.accept();
|
||||
serverChannel.setMaxconns(this.maxconns);
|
||||
serverChannel.accept();
|
||||
final String threadName = "[" + Thread.currentThread().getName() + "] ";
|
||||
logger.info(threadName + this.getClass().getSimpleName() + ("TCP".equalsIgnoreCase(protocol) ? "" : ("." + protocol)) + " listen: " + address
|
||||
+ ", threads: " + threads + ", bufferCapacity: " + bufferCapacity + ", bufferPoolSize: " + bufferPoolSize + ", responsePoolSize: " + responsePoolSize
|
||||
|
||||
@@ -5,12 +5,14 @@
|
||||
*/
|
||||
package org.redkale.net;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.net.*;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.*;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.logging.Level;
|
||||
import org.redkale.convert.*;
|
||||
import org.redkale.convert.json.JsonConvert;
|
||||
import org.redkale.util.*;
|
||||
@@ -42,6 +44,8 @@ public final class Transport {
|
||||
supportTcpNoDelay = tcpNoDelay;
|
||||
}
|
||||
|
||||
protected final TransportFactory factory;
|
||||
|
||||
protected final String name; //即<group>的name属性
|
||||
|
||||
protected final String subprotocol; //即<group>的subprotocol属性
|
||||
@@ -63,18 +67,21 @@ public final class Transport {
|
||||
|
||||
protected final ConcurrentHashMap<SocketAddress, BlockingQueue<AsyncConnection>> connPool = new ConcurrentHashMap<>();
|
||||
|
||||
public Transport(String name, String subprotocol, final ObjectPool<ByteBuffer> transportBufferPool,
|
||||
protected Transport(String name, String subprotocol, TransportFactory factory, final ObjectPool<ByteBuffer> transportBufferPool,
|
||||
final AsynchronousChannelGroup transportChannelGroup, final InetSocketAddress clientAddress,
|
||||
final Collection<InetSocketAddress> addresses, final TransportStrategy strategy) {
|
||||
this(name, DEFAULT_PROTOCOL, subprotocol, transportBufferPool, transportChannelGroup, clientAddress, addresses, strategy);
|
||||
this(name, DEFAULT_PROTOCOL, subprotocol, factory, transportBufferPool, transportChannelGroup, clientAddress, addresses, strategy);
|
||||
}
|
||||
|
||||
public Transport(String name, String protocol, String subprotocol, final ObjectPool<ByteBuffer> transportBufferPool,
|
||||
protected Transport(String name, String protocol, String subprotocol,
|
||||
final TransportFactory factory, final ObjectPool<ByteBuffer> transportBufferPool,
|
||||
final AsynchronousChannelGroup transportChannelGroup, final InetSocketAddress clientAddress,
|
||||
final Collection<InetSocketAddress> addresses, final TransportStrategy strategy) {
|
||||
this.name = name;
|
||||
this.subprotocol = subprotocol == null ? "" : subprotocol.trim();
|
||||
this.protocol = protocol;
|
||||
this.factory = factory;
|
||||
factory.transportReferences.add(new WeakReference<>(this));
|
||||
this.tcp = "TCP".equalsIgnoreCase(protocol);
|
||||
this.group = transportChannelGroup;
|
||||
this.bufferPool = transportBufferPool;
|
||||
@@ -171,7 +178,7 @@ public final class Transport {
|
||||
}
|
||||
|
||||
public void offerBuffer(ByteBuffer buffer) {
|
||||
bufferPool.offer(buffer);
|
||||
bufferPool.accept(buffer);
|
||||
}
|
||||
|
||||
public void offerBuffer(ByteBuffer... buffers) {
|
||||
@@ -182,7 +189,7 @@ public final class Transport {
|
||||
return tcp;
|
||||
}
|
||||
|
||||
public AsyncConnection pollConnection(SocketAddress addr) {
|
||||
public CompletableFuture<AsyncConnection> pollConnection(SocketAddress addr) {
|
||||
if (this.strategy != null) return strategy.pollConnection(addr, this);
|
||||
if (addr == null && this.transportAddres.length == 1) addr = this.transportAddres[0].address;
|
||||
final boolean rand = addr == null;
|
||||
@@ -201,7 +208,7 @@ public final class Transport {
|
||||
if (!queue.isEmpty()) {
|
||||
AsyncConnection conn;
|
||||
while ((conn = queue.poll()) != null) {
|
||||
if (conn.isOpen()) return conn;
|
||||
if (conn.isOpen()) return CompletableFuture.completedFuture(conn);
|
||||
}
|
||||
}
|
||||
tryed = true;
|
||||
@@ -237,21 +244,16 @@ public final class Transport {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
channel = AsynchronousSocketChannel.open(group);
|
||||
if (supportTcpNoDelay) channel.setOption(StandardSocketOptions.TCP_NODELAY, true);
|
||||
channel.connect(addr).get(2, TimeUnit.SECONDS);
|
||||
return AsyncConnection.createTCP(group, addr, supportTcpNoDelay, 6, 6);
|
||||
}
|
||||
if (channel == null) return null;
|
||||
return AsyncConnection.create(channel, addr, 3000, 3000);
|
||||
if (channel == null) return CompletableFuture.completedFuture(null);
|
||||
return CompletableFuture.completedFuture(AsyncConnection.create(channel, addr, 6, 6));
|
||||
} else { // UDP
|
||||
if (rand) addr = this.transportAddres[0].address;
|
||||
DatagramChannel channel = DatagramChannel.open();
|
||||
channel.configureBlocking(true);
|
||||
channel.connect(addr);
|
||||
return AsyncConnection.create(channel, addr, true, 3000, 3000);
|
||||
// AsyncDatagramChannel channel = AsyncDatagramChannel.open(group);
|
||||
// channel.connect(addr);
|
||||
// return AsyncConnection.create(channel, addr, true, 3000, 3000);
|
||||
return CompletableFuture.completedFuture(AsyncConnection.create(channel, addr, true, 6, 6));
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
throw new RuntimeException("transport address = " + addr, ex);
|
||||
@@ -274,35 +276,40 @@ public final class Transport {
|
||||
}
|
||||
|
||||
public <A> void async(SocketAddress addr, final ByteBuffer buffer, A att, final CompletionHandler<Integer, A> handler) {
|
||||
final AsyncConnection conn = pollConnection(addr);
|
||||
conn.write(buffer, buffer, new CompletionHandler<Integer, ByteBuffer>() {
|
||||
|
||||
@Override
|
||||
public void completed(Integer result, ByteBuffer attachment) {
|
||||
buffer.clear();
|
||||
conn.read(buffer, buffer, new CompletionHandler<Integer, ByteBuffer>() {
|
||||
|
||||
@Override
|
||||
public void completed(Integer result, ByteBuffer attachment) {
|
||||
if (handler != null) handler.completed(result, att);
|
||||
offerBuffer(buffer);
|
||||
offerConnection(false, conn);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void failed(Throwable exc, ByteBuffer attachment) {
|
||||
offerBuffer(buffer);
|
||||
offerConnection(true, conn);
|
||||
}
|
||||
});
|
||||
|
||||
pollConnection(addr).whenComplete((conn, ex) -> {
|
||||
if (ex != null) {
|
||||
factory.getLogger().log(Level.WARNING, Transport.class.getSimpleName() + " async error", ex);
|
||||
return;
|
||||
}
|
||||
conn.write(buffer, buffer, new CompletionHandler<Integer, ByteBuffer>() {
|
||||
|
||||
@Override
|
||||
public void failed(Throwable exc, ByteBuffer attachment) {
|
||||
offerBuffer(buffer);
|
||||
offerConnection(true, conn);
|
||||
}
|
||||
@Override
|
||||
public void completed(Integer result, ByteBuffer attachment) {
|
||||
buffer.clear();
|
||||
conn.read(buffer, buffer, new CompletionHandler<Integer, ByteBuffer>() {
|
||||
|
||||
@Override
|
||||
public void completed(Integer result, ByteBuffer attachment) {
|
||||
if (handler != null) handler.completed(result, att);
|
||||
offerBuffer(buffer);
|
||||
offerConnection(false, conn);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void failed(Throwable exc, ByteBuffer attachment) {
|
||||
offerBuffer(buffer);
|
||||
offerConnection(true, conn);
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void failed(Throwable exc, ByteBuffer attachment) {
|
||||
offerBuffer(buffer);
|
||||
offerConnection(true, conn);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -5,18 +5,23 @@
|
||||
*/
|
||||
package org.redkale.net;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.AsynchronousChannelGroup;
|
||||
import java.nio.channels.*;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.concurrent.atomic.*;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.logging.*;
|
||||
import java.util.stream.Collectors;
|
||||
import org.redkale.service.Service;
|
||||
import org.redkale.util.ObjectPool;
|
||||
import org.redkale.util.*;
|
||||
|
||||
/**
|
||||
* System.getProperty("net.transport.pinginterval", "30") 心跳周期,默认30秒
|
||||
*
|
||||
* <p>
|
||||
* 详情见: https://redkale.org
|
||||
*
|
||||
@@ -24,6 +29,8 @@ import org.redkale.util.ObjectPool;
|
||||
*/
|
||||
public class TransportFactory {
|
||||
|
||||
public static final String NAME_PINGINTERVAL = "pinginterval";
|
||||
|
||||
protected static final Logger logger = Logger.getLogger(TransportFactory.class.getSimpleName());
|
||||
|
||||
//传输端的线程池
|
||||
@@ -43,10 +50,24 @@ public class TransportFactory {
|
||||
|
||||
protected final List<WeakReference<Service>> services = new CopyOnWriteArrayList<>();
|
||||
|
||||
protected final List<WeakReference<Transport>> transportReferences = new CopyOnWriteArrayList<>();
|
||||
|
||||
//心跳周期, 单位:秒
|
||||
protected int pinginterval;
|
||||
|
||||
//ping的定时器
|
||||
private ScheduledThreadPoolExecutor pingScheduler;
|
||||
|
||||
//ping的内容
|
||||
private ByteBuffer pingBuffer;
|
||||
|
||||
//pong的数据长度, 小于0表示不进行判断
|
||||
protected int pongLength;
|
||||
|
||||
//负载均衡策略
|
||||
protected final TransportStrategy strategy;
|
||||
|
||||
public TransportFactory(ExecutorService executor, ObjectPool<ByteBuffer> bufferPool, AsynchronousChannelGroup channelGroup,
|
||||
protected TransportFactory(ExecutorService executor, ObjectPool<ByteBuffer> bufferPool, AsynchronousChannelGroup channelGroup,
|
||||
final TransportStrategy strategy) {
|
||||
this.executor = executor;
|
||||
this.bufferPool = bufferPool;
|
||||
@@ -54,10 +75,79 @@ public class TransportFactory {
|
||||
this.strategy = strategy;
|
||||
}
|
||||
|
||||
public TransportFactory(ExecutorService executor, ObjectPool<ByteBuffer> bufferPool, AsynchronousChannelGroup channelGroup) {
|
||||
protected TransportFactory(ExecutorService executor, ObjectPool<ByteBuffer> bufferPool, AsynchronousChannelGroup channelGroup) {
|
||||
this(executor, bufferPool, channelGroup, null);
|
||||
}
|
||||
|
||||
public void init(AnyValue conf, ByteBuffer pingBuffer, int pongLength) {
|
||||
if (conf != null) {
|
||||
this.pinginterval = conf.getIntValue(NAME_PINGINTERVAL, 0);
|
||||
}
|
||||
if (this.pinginterval > 0) {
|
||||
if (this.pingScheduler == null && pingBuffer != null) {
|
||||
this.pingBuffer = pingBuffer.asReadOnlyBuffer();
|
||||
this.pongLength = pongLength;
|
||||
this.pingScheduler = new ScheduledThreadPoolExecutor(1, (Runnable r) -> {
|
||||
final Thread t = new Thread(r, this.getClass().getSimpleName() + "-TransportFactoryPingTask-Thread");
|
||||
t.setDaemon(true);
|
||||
return t;
|
||||
});
|
||||
pingScheduler.scheduleAtFixedRate(() -> {
|
||||
pings();
|
||||
}, pinginterval, pinginterval, TimeUnit.SECONDS);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static TransportFactory create(int threads) {
|
||||
return create(threads, threads * 2, 8 * 1024);
|
||||
}
|
||||
|
||||
public static TransportFactory create(int threads, int bufferPoolSize, int bufferCapacity) {
|
||||
final ObjectPool<ByteBuffer> transportPool = new ObjectPool<>(new AtomicLong(), new AtomicLong(), bufferPoolSize,
|
||||
(Object... params) -> ByteBuffer.allocateDirect(bufferCapacity), null, (e) -> {
|
||||
if (e == null || e.isReadOnly() || e.capacity() != bufferCapacity) return false;
|
||||
e.clear();
|
||||
return true;
|
||||
});
|
||||
final AtomicInteger counter = new AtomicInteger();
|
||||
ExecutorService transportExec = Executors.newFixedThreadPool(threads, (Runnable r) -> {
|
||||
Thread t = new Thread(r);
|
||||
t.setDaemon(true);
|
||||
t.setName("Transport-Thread-" + counter.incrementAndGet());
|
||||
return t;
|
||||
});
|
||||
AsynchronousChannelGroup transportGroup = null;
|
||||
try {
|
||||
transportGroup = AsynchronousChannelGroup.withCachedThreadPool(transportExec, 1);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return create(transportExec, transportPool, transportGroup);
|
||||
}
|
||||
|
||||
public static TransportFactory create(ExecutorService executor, ObjectPool<ByteBuffer> bufferPool, AsynchronousChannelGroup channelGroup) {
|
||||
return new TransportFactory(executor, bufferPool, channelGroup, null);
|
||||
}
|
||||
|
||||
public static TransportFactory create(ExecutorService executor, ObjectPool<ByteBuffer> bufferPool, AsynchronousChannelGroup channelGroup,
|
||||
final TransportStrategy strategy) {
|
||||
return new TransportFactory(executor, bufferPool, channelGroup, strategy);
|
||||
}
|
||||
|
||||
public Transport createTransportTCP(String name, final InetSocketAddress clientAddress, final Collection<InetSocketAddress> addresses) {
|
||||
return new Transport(name, "TCP", "", this, this.bufferPool, this.channelGroup, clientAddress, addresses, strategy);
|
||||
}
|
||||
|
||||
public Transport createTransport(String name, String protocol, final InetSocketAddress clientAddress, final Collection<InetSocketAddress> addresses) {
|
||||
return new Transport(name, protocol, "", this, this.bufferPool, this.channelGroup, clientAddress, addresses, strategy);
|
||||
}
|
||||
|
||||
public Transport createTransport(String name, String protocol, String subprotocol,
|
||||
final InetSocketAddress clientAddress, final Collection<InetSocketAddress> addresses) {
|
||||
return new Transport(name, protocol, subprotocol, this, this.bufferPool, this.channelGroup, clientAddress, addresses, strategy);
|
||||
}
|
||||
|
||||
public String findGroupName(InetSocketAddress addr) {
|
||||
if (addr == null) return null;
|
||||
return groupAddrs.get(addr);
|
||||
@@ -134,26 +224,34 @@ public class TransportFactory {
|
||||
if (info == null) continue;
|
||||
addresses.addAll(info.addresses);
|
||||
}
|
||||
if (info == null) return null;
|
||||
if (info == null) info = new TransportGroupInfo("TCP");
|
||||
if (sncpAddress != null) addresses.remove(sncpAddress);
|
||||
return new Transport(groups.stream().sorted().collect(Collectors.joining(";")), info.protocol, info.subprotocol, this.bufferPool, this.channelGroup, sncpAddress, addresses, this.strategy);
|
||||
return new Transport(groups.stream().sorted().collect(Collectors.joining(";")), info.protocol, info.subprotocol, this, this.bufferPool, this.channelGroup, sncpAddress, addresses, this.strategy);
|
||||
}
|
||||
|
||||
private Transport loadTransport(final String groupName, InetSocketAddress sncpAddress) {
|
||||
if (groupName == null) return null;
|
||||
TransportGroupInfo info = groupInfos.get(groupName);
|
||||
if (info == null) return null;
|
||||
return new Transport(groupName, info.protocol, info.subprotocol, this.bufferPool, this.channelGroup, sncpAddress, info.addresses, this.strategy);
|
||||
return new Transport(groupName, info.protocol, info.subprotocol, this, this.bufferPool, this.channelGroup, sncpAddress, info.addresses, this.strategy);
|
||||
}
|
||||
|
||||
public ExecutorService getExecutor() {
|
||||
return executor;
|
||||
}
|
||||
|
||||
public Supplier<ByteBuffer> getBufferSupplier() {
|
||||
return bufferPool;
|
||||
}
|
||||
|
||||
public List<TransportGroupInfo> getGroupInfos() {
|
||||
return new ArrayList<>(this.groupInfos.values());
|
||||
}
|
||||
|
||||
public Logger getLogger() {
|
||||
return logger;
|
||||
}
|
||||
|
||||
public void addSncpService(Service service) {
|
||||
if (service == null) return;
|
||||
services.add(new WeakReference<>(service));
|
||||
@@ -169,6 +267,7 @@ public class TransportFactory {
|
||||
}
|
||||
|
||||
public void shutdownNow() {
|
||||
if (this.pingScheduler != null) this.pingScheduler.shutdownNow();
|
||||
try {
|
||||
this.channelGroup.shutdownNow();
|
||||
} catch (Exception e) {
|
||||
@@ -176,6 +275,73 @@ public class TransportFactory {
|
||||
}
|
||||
}
|
||||
|
||||
private void pings() {
|
||||
long timex = System.currentTimeMillis() - (this.pinginterval < 15 ? this.pinginterval : (this.pinginterval - 3)) * 1000;
|
||||
List<WeakReference> nulllist = new ArrayList<>();
|
||||
for (WeakReference<Transport> ref : transportReferences) {
|
||||
Transport transport = ref.get();
|
||||
if (transport == null) {
|
||||
nulllist.add(ref);
|
||||
continue;
|
||||
}
|
||||
List<BlockingQueue<AsyncConnection>> list = new ArrayList<>(transport.getAsyncConnectionPool().values());
|
||||
for (final BlockingQueue<AsyncConnection> queue : list) {
|
||||
AsyncConnection conn;
|
||||
while ((conn = queue.poll()) != null) {
|
||||
if (conn.getLastWriteTime() > timex && false) { //最近几秒内已经进行过IO操作
|
||||
queue.offer(conn);
|
||||
} else { //超过一定时间的连接需要进行ping处理
|
||||
ByteBuffer sendBuffer = pingBuffer.duplicate();
|
||||
final AsyncConnection localconn = conn;
|
||||
final BlockingQueue<AsyncConnection> localqueue = queue;
|
||||
localconn.write(sendBuffer, sendBuffer, new CompletionHandler<Integer, ByteBuffer>() {
|
||||
@Override
|
||||
public void completed(Integer result, ByteBuffer buffer) {
|
||||
if (buffer.hasRemaining()) {
|
||||
localconn.write(buffer, buffer, this);
|
||||
return;
|
||||
}
|
||||
ByteBuffer pongBuffer = bufferPool.get();
|
||||
localconn.read(pongBuffer, pongBuffer, new CompletionHandler<Integer, ByteBuffer>() {
|
||||
int counter = 0;
|
||||
|
||||
@Override
|
||||
public void completed(Integer result, ByteBuffer attachment) {
|
||||
if (counter > 3) {
|
||||
bufferPool.accept(attachment);
|
||||
localconn.dispose();
|
||||
return;
|
||||
}
|
||||
if (pongLength > 0 && attachment.position() < pongLength) {
|
||||
counter++;
|
||||
localconn.read(pongBuffer, pongBuffer, this);
|
||||
return;
|
||||
}
|
||||
bufferPool.accept(attachment);
|
||||
localqueue.offer(localconn);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void failed(Throwable exc, ByteBuffer attachment) {
|
||||
localconn.dispose();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void failed(Throwable exc, ByteBuffer buffer) {
|
||||
localconn.dispose();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for (WeakReference ref : nulllist) {
|
||||
transportReferences.remove(ref);
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean checkName(String name) { //不能含特殊字符
|
||||
if (name.isEmpty()) return false;
|
||||
if (name.charAt(0) >= '0' && name.charAt(0) <= '9') return false;
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
package org.redkale.net;
|
||||
|
||||
import java.net.SocketAddress;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
/**
|
||||
* 远程请求的负载均衡策略
|
||||
@@ -17,5 +18,5 @@ import java.net.SocketAddress;
|
||||
*/
|
||||
public interface TransportStrategy {
|
||||
|
||||
public AsyncConnection pollConnection(SocketAddress addr, Transport transport);
|
||||
public CompletableFuture<AsyncConnection> pollConnection(SocketAddress addr, Transport transport);
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ package org.redkale.net.http;
|
||||
|
||||
import java.net.*;
|
||||
import java.nio.*;
|
||||
import java.nio.channels.CompletionHandler;
|
||||
import java.nio.charset.*;
|
||||
import java.security.*;
|
||||
import java.util.concurrent.*;
|
||||
@@ -54,7 +55,7 @@ public class HttpContext extends Context {
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected <H extends AsyncHandler> Creator<H> loadAsyncHandlerCreator(Class<H> handlerClass) {
|
||||
protected <H extends CompletionHandler> Creator<H> loadAsyncHandlerCreator(Class<H> handlerClass) {
|
||||
Creator<H> creator = asyncHandlerCreators.get(handlerClass);
|
||||
if (creator == null) {
|
||||
creator = createAsyncHandlerCreator(handlerClass);
|
||||
@@ -64,14 +65,14 @@ public class HttpContext extends Context {
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private <H extends AsyncHandler> Creator<H> createAsyncHandlerCreator(Class<H> handlerClass) {
|
||||
private <H extends CompletionHandler> Creator<H> createAsyncHandlerCreator(Class<H> handlerClass) {
|
||||
//生成规则与SncpAsyncHandler.Factory 很类似
|
||||
//-------------------------------------------------------------
|
||||
final boolean handlerinterface = handlerClass.isInterface();
|
||||
final String handlerClassName = handlerClass.getName().replace('.', '/');
|
||||
final String handlerName = AsyncHandler.class.getName().replace('.', '/');
|
||||
final String handlerDesc = Type.getDescriptor(AsyncHandler.class);
|
||||
final String newDynName = handlerClass.getName().replace('.', '/') + "_Dync" + AsyncHandler.class.getSimpleName() + "_" + (System.currentTimeMillis() % 10000);
|
||||
final String handlerName = CompletionHandler.class.getName().replace('.', '/');
|
||||
final String handlerDesc = Type.getDescriptor(CompletionHandler.class);
|
||||
final String newDynName = handlerClass.getName().replace('.', '/') + "_DyncAsyncHandler_" + (System.currentTimeMillis() % 10000);
|
||||
|
||||
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
|
||||
FieldVisitor fv;
|
||||
@@ -157,7 +158,7 @@ public class HttpContext extends Context {
|
||||
}
|
||||
cw.visitEnd();
|
||||
byte[] bytes = cw.toByteArray();
|
||||
Class<AsyncHandler> newHandlerClazz = (Class<AsyncHandler>) new ClassLoader(handlerClass.getClassLoader()) {
|
||||
Class<CompletionHandler> newHandlerClazz = (Class<CompletionHandler>) new ClassLoader(handlerClass.getClassLoader()) {
|
||||
public final Class<?> loadClass(String name, byte[] b) {
|
||||
return defineClass(name, b, 0, b.length);
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import java.net.*;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.Channels;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.*;
|
||||
import org.redkale.convert.json.JsonConvert;
|
||||
import org.redkale.net.*;
|
||||
import org.redkale.util.*;
|
||||
@@ -518,8 +519,8 @@ public class HttpRequest extends Request<HttpContext> {
|
||||
* @return cookie值
|
||||
*/
|
||||
public String getCookie(String name, String dfvalue) {
|
||||
for (HttpCookie cookie : getCookies()) {
|
||||
if (name.equals(cookie.getName())) return cookie.getValue();
|
||||
for (HttpCookie c : getCookies()) {
|
||||
if (name.equals(c.getName())) return c.getValue();
|
||||
}
|
||||
return dfvalue;
|
||||
}
|
||||
@@ -897,6 +898,20 @@ public class HttpRequest extends Request<HttpContext> {
|
||||
return header;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将请求Header转换成Map
|
||||
*
|
||||
* @param map Map
|
||||
*
|
||||
* @return Map
|
||||
*/
|
||||
public Map<String, String> getHeadersToMap(Map<String, String> map) {
|
||||
if (map == null) map = new LinkedHashMap<>();
|
||||
final Map<String, String> map0 = map;
|
||||
header.forEach((k, v) -> map0.put(k, v));
|
||||
return map0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有的header名
|
||||
*
|
||||
@@ -1105,6 +1120,52 @@ public class HttpRequest extends Request<HttpContext> {
|
||||
return params;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将请求参数转换成Map
|
||||
*
|
||||
* @param map Map
|
||||
*
|
||||
* @return Map
|
||||
*/
|
||||
public Map<String, String> getParametersToMap(Map<String, String> map) {
|
||||
if (map == null) map = new LinkedHashMap<>();
|
||||
final Map<String, String> map0 = map;
|
||||
getParameters().forEach((k, v) -> map0.put(k, v));
|
||||
return map0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将请求参数转换成String, 字符串格式为: bean1={}&id=13&name=xxx <br>
|
||||
* 不会返回null,没有参数返回空字符串
|
||||
*
|
||||
*
|
||||
* @return String
|
||||
*/
|
||||
public String getParametersToString() {
|
||||
return getParametersToString(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将请求参数转换成String, 字符串格式为: bean1={}&id=13&name=xxx <br>
|
||||
* 不会返回null,没有参数返回空字符串
|
||||
*
|
||||
* @param prefix 拼接前缀, 如果无参数,返回的字符串不会含有拼接前缀
|
||||
*
|
||||
* @return String
|
||||
*/
|
||||
public String getParametersToString(String prefix) {
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
getParameters().forEach((k, v) -> {
|
||||
if (sb.length() > 0) sb.append('&');
|
||||
try {
|
||||
sb.append(k).append('=').append(URLEncoder.encode(v, "UTF-8"));
|
||||
} catch (IOException ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
});
|
||||
return (sb.length() > 0 && prefix != null) ? (prefix + sb) : sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有参数名
|
||||
*
|
||||
|
||||
@@ -83,8 +83,6 @@ public class HttpResourceServlet extends HttpServlet {
|
||||
}
|
||||
}
|
||||
|
||||
protected final boolean finest = logger.isLoggable(Level.FINEST);
|
||||
|
||||
protected final LongAdder cachedLength = new LongAdder();
|
||||
|
||||
//缓存总大小, 默认0
|
||||
@@ -193,7 +191,7 @@ public class HttpResourceServlet extends HttpServlet {
|
||||
public void execute(HttpRequest request, HttpResponse response) throws IOException {
|
||||
String uri = request.getRequestURI();
|
||||
if (uri.contains("../")) {
|
||||
if (finest) logger.log(Level.FINEST, "Not found resource (404) be " + uri + ", request = " + request);
|
||||
if (logger.isLoggable(Level.FINEST)) logger.log(Level.FINEST, "Not found resource (404) be " + uri + ", request = " + request);
|
||||
response.finish404();
|
||||
return;
|
||||
}
|
||||
@@ -220,7 +218,7 @@ public class HttpResourceServlet extends HttpServlet {
|
||||
entry = files.computeIfAbsent(uri, x -> createFileEntry(x));
|
||||
}
|
||||
if (entry == null) {
|
||||
if (finest) logger.log(Level.FINEST, "Not found resource (404), request = " + request);
|
||||
if (logger.isLoggable(Level.FINER)) logger.log(Level.FINER, "Not found resource (404), request = " + request);
|
||||
response.finish404();
|
||||
} else {
|
||||
//file = null 表示资源内容在内存而不是在File中
|
||||
|
||||
@@ -16,6 +16,7 @@ import java.util.*;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import java.util.logging.Level;
|
||||
import org.redkale.convert.*;
|
||||
import org.redkale.convert.json.JsonConvert;
|
||||
import org.redkale.net.*;
|
||||
import org.redkale.util.AnyValue.DefaultAnyValue;
|
||||
@@ -48,7 +49,7 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
|
||||
|
||||
protected static final byte[] LINE = new byte[]{'\r', '\n'};
|
||||
|
||||
protected static final byte[] serverNameBytes = ("Server: redkale/" + Redkale.getDotedVersion() + "\r\n").getBytes();
|
||||
protected static final byte[] serverNameBytes = ("Server: " + System.getProperty("http.response.header.server", "redkale" + "/" + Redkale.getDotedVersion()) + "\r\n").getBytes();
|
||||
|
||||
private static final Set<OpenOption> options = new HashSet<>();
|
||||
|
||||
@@ -147,6 +148,10 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
|
||||
return super.removeChannel();
|
||||
}
|
||||
|
||||
protected AsyncConnection getChannel() {
|
||||
return channel;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean recycle() {
|
||||
boolean rs = super.recycle();
|
||||
@@ -220,38 +225,32 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建AsyncHandler实例
|
||||
* 创建CompletionHandler实例
|
||||
*
|
||||
* @return AsyncHandler
|
||||
* @return CompletionHandler
|
||||
*/
|
||||
public AsyncHandler createAsyncHandler() {
|
||||
return AsyncHandler.create((v, a) -> {
|
||||
if (v instanceof org.redkale.service.RetResult) {
|
||||
finishJson((org.redkale.service.RetResult) v);
|
||||
} else if (v instanceof CharSequence) {
|
||||
finish(String.valueOf(v));
|
||||
} else {
|
||||
finishJson(v);
|
||||
}
|
||||
public CompletionHandler createAsyncHandler() {
|
||||
return Utility.createAsyncHandler((v, a) -> {
|
||||
finish(v);
|
||||
}, (t, a) -> {
|
||||
request.getContext().getLogger().log(Level.WARNING, "Servlet occur, forece to close channel. request = " + request, t);
|
||||
request.getContext().getLogger().log(Level.WARNING, "Servlet occur, forece to close channel. request = " + request + ", result is CompletionHandler", (Throwable) t);
|
||||
finish(500, null);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建AsyncHandler子类的实例 <br>
|
||||
* 创建CompletionHandler子类的实例 <br>
|
||||
*
|
||||
* 传入的AsyncHandler子类必须是public,且保证其子类可被继承且completed、failed可被重载且包含空参数的构造函数。
|
||||
* 传入的CompletionHandler子类必须是public,且保证其子类可被继承且completed、failed可被重载且包含空参数的构造函数。
|
||||
*
|
||||
* @param <H> 泛型
|
||||
* @param handlerClass AsyncHandler子类
|
||||
* @param handlerClass CompletionHandler子类
|
||||
*
|
||||
* @return AsyncHandler AsyncHandler
|
||||
* @return CompletionHandler
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public <H extends AsyncHandler> H createAsyncHandler(Class<H> handlerClass) {
|
||||
if (handlerClass == null || handlerClass == AsyncHandler.class) return (H) createAsyncHandler();
|
||||
public <H extends CompletionHandler> H createAsyncHandler(Class<H> handlerClass) {
|
||||
if (handlerClass == null || handlerClass == CompletionHandler.class) return (H) createAsyncHandler();
|
||||
return context.loadAsyncHandlerCreator(handlerClass).create(createAsyncHandler());
|
||||
}
|
||||
|
||||
@@ -376,7 +375,7 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
|
||||
* @param future 输出对象的句柄
|
||||
*/
|
||||
public void finishJson(final CompletableFuture future) {
|
||||
finishJson(request.getJsonConvert(), future);
|
||||
finish(request.getJsonConvert(), (Type) null, future);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -387,20 +386,7 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public void finishJson(final JsonConvert convert, final CompletableFuture future) {
|
||||
future.whenComplete((v, e) -> {
|
||||
if (e != null) {
|
||||
context.getLogger().log(Level.WARNING, "Servlet occur, forece to close channel. request = " + request, e);
|
||||
finish(500, null);
|
||||
return;
|
||||
}
|
||||
if (v instanceof CharSequence) {
|
||||
finish(v.toString());
|
||||
} else if (v instanceof org.redkale.service.RetResult) {
|
||||
finishJson(convert, (org.redkale.service.RetResult) v);
|
||||
} else {
|
||||
finishJson(convert, v);
|
||||
}
|
||||
});
|
||||
finish(convert, (Type) null, future);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -412,60 +398,86 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public void finishJson(final JsonConvert convert, final Type type, final CompletableFuture future) {
|
||||
future.whenComplete((v, e) -> {
|
||||
if (e != null) {
|
||||
context.getLogger().log(Level.WARNING, "Servlet occur, forece to close channel. request = " + request, e);
|
||||
finish(500, null);
|
||||
return;
|
||||
}
|
||||
if (v instanceof CharSequence) {
|
||||
finish(v.toString());
|
||||
} else if (v instanceof HttpResult) {
|
||||
finishJson(convert, (HttpResult) v);
|
||||
} else if (v instanceof org.redkale.service.RetResult) {
|
||||
finishJson(convert, (org.redkale.service.RetResult) v);
|
||||
} else {
|
||||
finishJson(convert, type, v);
|
||||
}
|
||||
});
|
||||
finish(convert, type, future);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将HttpResult的结果对象以JSON格式输出
|
||||
* 将结果对象输出
|
||||
*
|
||||
* @param result HttpResult对象
|
||||
*/
|
||||
public void finishJson(final HttpResult result) {
|
||||
finishJson(request.getJsonConvert(), result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将HttpResult的结果对象以JSON格式输出
|
||||
*
|
||||
* @param convert 指定的JsonConvert
|
||||
* @param result HttpResult对象
|
||||
* @param obj 输出对象
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public void finishJson(final JsonConvert convert, final HttpResult result) {
|
||||
if (output == null) {
|
||||
finish("");
|
||||
return;
|
||||
}
|
||||
if (result.getContentType() != null) setContentType(result.getContentType());
|
||||
addHeader(result.getHeaders()).addCookie(result.getCookies()).setStatus(result.getStatus() < 1 ? 200 : result.getStatus());
|
||||
if (result.getResult() instanceof File) {
|
||||
public void finish(final Object obj) {
|
||||
finish(request.getJsonConvert(), (Type) null, obj);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将结果对象输出
|
||||
*
|
||||
* @param convert 指定的Convert
|
||||
* @param obj 输出对象
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public void finish(final Convert convert, final Object obj) {
|
||||
finish(convert, (Type) null, obj);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将结果对象输出
|
||||
*
|
||||
* @param convert 指定的Convert
|
||||
* @param type 指定的类型
|
||||
* @param obj 输出对象
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public void finish(final Convert convert, final Type type, final Object obj) {
|
||||
if (obj == null) {
|
||||
finish("null");
|
||||
} else if (obj instanceof CompletableFuture) {
|
||||
((CompletableFuture) obj).whenComplete((v, e) -> {
|
||||
if (e != null) {
|
||||
context.getLogger().log(Level.WARNING, "Servlet occur, forece to close channel. request = " + request + ", result is CompletableFuture", (Throwable) e);
|
||||
finish(500, null);
|
||||
return;
|
||||
}
|
||||
finish(convert, type, v);
|
||||
});
|
||||
} else if (obj instanceof CharSequence) {
|
||||
finish((String) obj.toString());
|
||||
} else if (obj instanceof byte[]) {
|
||||
finish((byte[]) obj);
|
||||
} else if (obj instanceof ByteBuffer) {
|
||||
finish((ByteBuffer) obj);
|
||||
} else if (obj instanceof ByteBuffer[]) {
|
||||
finish((ByteBuffer[]) obj);
|
||||
} else if (obj instanceof File) {
|
||||
try {
|
||||
finish((File) result.getResult());
|
||||
finish((File) obj);
|
||||
} catch (IOException e) {
|
||||
getContext().getLogger().log(Level.WARNING, "HttpServlet finishJson HttpResult File occur, forece to close channel. request = " + getRequest(), e);
|
||||
getContext().getLogger().log(Level.WARNING, "HttpServlet finish File occur, forece to close channel. request = " + getRequest(), e);
|
||||
finish(500, null);
|
||||
}
|
||||
} else if (result.getResult() instanceof String) {
|
||||
finish((String) result.getResult());
|
||||
} else if (result.getResult() == null) {
|
||||
finish(result.getMessage());
|
||||
} else if (obj instanceof HttpResult) {
|
||||
HttpResult result = (HttpResult) obj;
|
||||
if (result.getContentType() != null) setContentType(result.getContentType());
|
||||
addHeader(result.getHeaders()).addCookie(result.getCookies()).setStatus(result.getStatus() < 1 ? 200 : result.getStatus());
|
||||
if (result.getResult() == null) {
|
||||
finish("");
|
||||
} else {
|
||||
finish(convert, result.getResult());
|
||||
}
|
||||
} else {
|
||||
finishJson(result.getResult());
|
||||
if (convert instanceof TextConvert) this.contentType = "text/plain; charset=utf-8";
|
||||
if (this.recycleListener != null) this.output = obj;
|
||||
if (obj instanceof org.redkale.service.RetResult) {
|
||||
org.redkale.service.RetResult ret = (org.redkale.service.RetResult) obj;
|
||||
if (!ret.isSuccess()) {
|
||||
this.header.addValue("retcode", String.valueOf(ret.getRetcode())).addValue("retinfo", ret.getRetinfo());
|
||||
}
|
||||
}
|
||||
ByteBuffer[] buffers = type == null ? convert.convertTo(context.getBufferSupplier(), obj)
|
||||
: convert.convertTo(context.getBufferSupplier(), type, obj);
|
||||
finish(buffers);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -642,7 +654,7 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
|
||||
* @param attachment 异步回调参数
|
||||
* @param handler 异步回调函数
|
||||
*/
|
||||
public <A> void sendBody(ByteBuffer buffer, A attachment, AsyncHandler<Integer, A> handler) {
|
||||
public <A> void sendBody(ByteBuffer buffer, A attachment, CompletionHandler<Integer, A> handler) {
|
||||
if (!this.headsended) {
|
||||
if (this.contentLength < 0) this.contentLength = buffer == null ? 0 : buffer.remaining();
|
||||
ByteBuffer headbuf = createHeader();
|
||||
@@ -665,7 +677,7 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
|
||||
* @param attachment 异步回调参数
|
||||
* @param handler 异步回调函数
|
||||
*/
|
||||
public <A> void sendBody(ByteBuffer[] buffers, A attachment, AsyncHandler<Integer, A> handler) {
|
||||
public <A> void sendBody(ByteBuffer[] buffers, A attachment, CompletionHandler<Integer, A> handler) {
|
||||
if (!this.headsended) {
|
||||
if (this.contentLength < 0) {
|
||||
int len = 0;
|
||||
@@ -1016,7 +1028,7 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
|
||||
this.bufferHandler = bufferHandler;
|
||||
}
|
||||
|
||||
protected final class TransferFileHandler implements AsyncHandler<Integer, ByteBuffer> {
|
||||
protected final class TransferFileHandler implements CompletionHandler<Integer, ByteBuffer> {
|
||||
|
||||
private final File file;
|
||||
|
||||
|
||||
@@ -76,6 +76,7 @@ public class MimeType {
|
||||
contentTypes.put("m3u", "audio/x-mpegurl");
|
||||
contentTypes.put("mac", "image/x-macpaint");
|
||||
contentTypes.put("man", "application/x-troff-man");
|
||||
contentTypes.put("manifest", "text/cache-manifest");
|
||||
contentTypes.put("mathml", "application/mathml+xml");
|
||||
contentTypes.put("me", "application/x-troff-me");
|
||||
contentTypes.put("mid", "audio/x-midi");
|
||||
|
||||
@@ -10,6 +10,7 @@ import java.lang.annotation.*;
|
||||
import static java.lang.annotation.ElementType.*;
|
||||
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||
import java.lang.reflect.*;
|
||||
import java.nio.channels.CompletionHandler;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import javax.annotation.Resource;
|
||||
@@ -17,6 +18,7 @@ import jdk.internal.org.objectweb.asm.*;
|
||||
import static jdk.internal.org.objectweb.asm.ClassWriter.COMPUTE_FRAMES;
|
||||
import static jdk.internal.org.objectweb.asm.Opcodes.*;
|
||||
import jdk.internal.org.objectweb.asm.Type;
|
||||
import org.redkale.convert.Convert;
|
||||
import org.redkale.convert.json.*;
|
||||
import org.redkale.service.*;
|
||||
import org.redkale.util.*;
|
||||
@@ -127,22 +129,34 @@ public final class Rest {
|
||||
List<Class> types = new ArrayList<>();
|
||||
for (RestConvert rc : converts) {
|
||||
if (types.contains(rc.type())) throw new RuntimeException("@RestConvert type(" + rc.type() + ") repeat");
|
||||
childFactory.register(rc.type(), false, rc.convertColumns());
|
||||
childFactory.register(rc.type(), true, rc.ignoreColumns());
|
||||
childFactory.reloadCoder(rc.type());
|
||||
if (rc.skipIgnore()) {
|
||||
childFactory.registerSkipIgnore(rc.type());
|
||||
childFactory.reloadCoder(rc.type());
|
||||
} else {
|
||||
childFactory.register(rc.type(), false, rc.convertColumns());
|
||||
childFactory.register(rc.type(), true, rc.ignoreColumns());
|
||||
childFactory.reloadCoder(rc.type());
|
||||
}
|
||||
types.add(rc.type());
|
||||
childFactory.tiny(rc.tiny());
|
||||
}
|
||||
return childFactory.getConvert();
|
||||
}
|
||||
|
||||
static String getWebModuleName(Class<? extends Service> serviceType) {
|
||||
static String getWebModuleNameLowerCase(Class<? extends Service> serviceType) {
|
||||
final RestService controller = serviceType.getAnnotation(RestService.class);
|
||||
if (controller == null) return serviceType.getSimpleName().replaceAll("Service.*$", "").toLowerCase();
|
||||
if (controller.ignore()) return null;
|
||||
return (!controller.name().isEmpty()) ? controller.name() : serviceType.getSimpleName().replaceAll("Service.*$", "").toLowerCase();
|
||||
}
|
||||
|
||||
static String getWebModuleName(Class<? extends Service> serviceType) {
|
||||
final RestService controller = serviceType.getAnnotation(RestService.class);
|
||||
if (controller == null) return serviceType.getSimpleName().replaceAll("Service.*$", "");
|
||||
if (controller.ignore()) return null;
|
||||
return (!controller.name().isEmpty()) ? controller.name() : serviceType.getSimpleName().replaceAll("Service.*$", "");
|
||||
}
|
||||
|
||||
static boolean isRestDyn(HttpServlet servlet) {
|
||||
return servlet.getClass().getAnnotation(RestDyn.class) != null;
|
||||
}
|
||||
@@ -183,9 +197,9 @@ public final class Rest {
|
||||
}
|
||||
}
|
||||
if (!valid) throw new RuntimeException("Rest WebSocket Class(" + webSocketType + ") must have public or protected Constructor on createRestWebSocketServlet");
|
||||
|
||||
final String rwsname = ResourceFactory.formatResourceName(rws.name());
|
||||
if (!checkName(rws.catalog())) throw new RuntimeException(webSocketType.getName() + " have illeal " + RestWebSocket.class.getSimpleName() + ".catalog, only 0-9 a-z A-Z _ cannot begin 0-9");
|
||||
if (!checkName(rws.name())) throw new RuntimeException(webSocketType.getName() + " have illeal " + RestWebSocket.class.getSimpleName() + ".name, only 0-9 a-z A-Z _ cannot begin 0-9");
|
||||
if (!checkName(rwsname)) throw new RuntimeException(webSocketType.getName() + " have illeal " + RestWebSocket.class.getSimpleName() + ".name, only 0-9 a-z A-Z _ cannot begin 0-9");
|
||||
|
||||
//----------------------------------------------------------------------------------------
|
||||
final Set<Field> resourcesFieldSet = new LinkedHashSet<>();
|
||||
@@ -260,7 +274,7 @@ public final class Rest {
|
||||
av0.visitEnd();
|
||||
}
|
||||
{ //注入 @WebServlet 注解
|
||||
String urlpath = (rws.catalog().isEmpty() ? "/" : ("/" + rws.catalog() + "/")) + rws.name();
|
||||
String urlpath = (rws.catalog().isEmpty() ? "/" : ("/" + rws.catalog() + "/")) + rwsname;
|
||||
av0 = cw.visitAnnotation(webServletDesc, true);
|
||||
{
|
||||
AnnotationVisitor av1 = av0.visitArray("value");
|
||||
@@ -299,25 +313,36 @@ public final class Rest {
|
||||
fv.visitEnd();
|
||||
}
|
||||
}
|
||||
{ //构造函数
|
||||
{ //_DynWebSocketServlet构造函数
|
||||
mv = new AsmMethodVisitor(cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null));
|
||||
mv.visitVarInsn(ALOAD, 0);
|
||||
mv.visitMethodInsn(INVOKESPECIAL, supDynName, "<init>", "()V", false);
|
||||
mv.visitVarInsn(ALOAD, 0);
|
||||
mv.visitLdcInsn(Type.getObjectType(newDynName + "$" + newDynWebSokcetSimpleName + "Message"));
|
||||
mv.visitFieldInsn(PUTFIELD, newDynName, "messageTextType", "Ljava/lang/reflect/Type;");
|
||||
|
||||
mv.visitVarInsn(ALOAD, 0);
|
||||
if (rws.liveinterval() < 6) {
|
||||
mv.visitInsn(ICONST_0 + rws.liveinterval());
|
||||
} else {
|
||||
mv.visitIntInsn(BIPUSH, rws.liveinterval());
|
||||
}
|
||||
pushInt(mv, rws.liveinterval());
|
||||
mv.visitFieldInsn(PUTFIELD, newDynName, "liveinterval", "I");
|
||||
|
||||
mv.visitVarInsn(ALOAD, 0);
|
||||
pushInt(mv, rws.wsmaxconns());
|
||||
mv.visitFieldInsn(PUTFIELD, newDynName, "wsmaxconns", "I");
|
||||
|
||||
mv.visitVarInsn(ALOAD, 0);
|
||||
pushInt(mv, rws.wsmaxbody());
|
||||
mv.visitFieldInsn(PUTFIELD, newDynName, "wsmaxbody", "I");
|
||||
|
||||
mv.visitVarInsn(ALOAD, 0);
|
||||
mv.visitInsn(rws.single() ? ICONST_1 : ICONST_0);
|
||||
mv.visitFieldInsn(PUTFIELD, newDynName, "single", "Z");
|
||||
|
||||
mv.visitVarInsn(ALOAD, 0);
|
||||
mv.visitInsn(rws.anyuser() ? ICONST_1 : ICONST_0);
|
||||
mv.visitFieldInsn(PUTFIELD, newDynName, "anyuser", "Z");
|
||||
|
||||
mv.visitInsn(RETURN);
|
||||
mv.visitMaxs(2, 1);
|
||||
mv.visitMaxs(3, 1);
|
||||
mv.visitEnd();
|
||||
}
|
||||
{ //createWebSocket 方法
|
||||
@@ -344,7 +369,7 @@ public final class Rest {
|
||||
}
|
||||
{ //resourceName
|
||||
mv = new AsmMethodVisitor(cw.visitMethod(ACC_PUBLIC, "resourceName", "()Ljava/lang/String;", null, null));
|
||||
mv.visitLdcInsn(rws.name());
|
||||
mv.visitLdcInsn(rwsname);
|
||||
mv.visitInsn(ARETURN);
|
||||
mv.visitMaxs(1, 1);
|
||||
mv.visitEnd();
|
||||
@@ -562,7 +587,9 @@ public final class Rest {
|
||||
final String webServletDesc = Type.getDescriptor(WebServlet.class);
|
||||
final String reqDesc = Type.getDescriptor(HttpRequest.class);
|
||||
final String respDesc = Type.getDescriptor(HttpResponse.class);
|
||||
final String convertDesc = Type.getDescriptor(JsonConvert.class);
|
||||
final String convertDesc = Type.getDescriptor(Convert.class);
|
||||
final String typeDesc = Type.getDescriptor(java.lang.reflect.Type.class);
|
||||
final String jsonConvertDesc = Type.getDescriptor(JsonConvert.class);
|
||||
final String retDesc = Type.getDescriptor(RetResult.class);
|
||||
final String futureDesc = Type.getDescriptor(CompletableFuture.class);
|
||||
final String flipperDesc = Type.getDescriptor(Flipper.class);
|
||||
@@ -589,7 +616,8 @@ public final class Rest {
|
||||
String newDynName = serviceTypeInternalName.substring(0, serviceTypeInternalName.lastIndexOf('/') + 1) + "_Dyn" + serviceType.getSimpleName().replaceAll("Service.*$", "") + "RestServlet";
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
final String defmodulename = getWebModuleName(serviceType);
|
||||
final String defmodulename = getWebModuleNameLowerCase(serviceType);
|
||||
final String bigmodulename = getWebModuleName(serviceType);
|
||||
final String catalog = controller == null ? "" : controller.catalog();
|
||||
if (!checkName(catalog)) throw new RuntimeException(serviceType.getName() + " have illeal " + RestService.class.getSimpleName() + ".catalog, only 0-9 a-z A-Z _ cannot begin 0-9");
|
||||
if (!checkName(defmodulename)) throw new RuntimeException(serviceType.getName() + " have illeal " + RestService.class.getSimpleName() + ".value, only 0-9 a-z A-Z _ cannot begin 0-9");
|
||||
@@ -692,7 +720,7 @@ public final class Rest {
|
||||
if (ignore) continue;
|
||||
paramtypes.add(method.getGenericParameterTypes());
|
||||
if (mappings.length == 0) { //没有Mapping,设置一个默认值
|
||||
MappingEntry entry = new MappingEntry(methodidex, null, defmodulename, method);
|
||||
MappingEntry entry = new MappingEntry(methodidex, null, bigmodulename, method);
|
||||
if (entrys.contains(entry)) throw new RuntimeException(serviceType.getName() + " on " + method.getName() + " 's mapping(" + entry.name + ") is repeat");
|
||||
entrys.add(entry);
|
||||
} else {
|
||||
@@ -705,7 +733,6 @@ public final class Rest {
|
||||
methodidex++;
|
||||
}
|
||||
if (entrys.isEmpty()) return null; //没有可HttpMapping的方法
|
||||
|
||||
//将每个Service可转换的方法生成HttpServlet对应的HttpMapping方法
|
||||
final Map<String, List<String>> asmParamMap = MethodParamClassVisitor.getMethodParamNames(serviceType);
|
||||
final Map<String, java.lang.reflect.Type> bodyTypes = new HashMap<>();
|
||||
@@ -852,7 +879,7 @@ public final class Rest {
|
||||
if (ptype.isPrimitive() || ptype == String.class) n = "#";
|
||||
}
|
||||
if (annhead == null && anncookie == null && annsid == null && annaddr == null && annbody == null && annfile == null
|
||||
&& !ptype.isPrimitive() && ptype != String.class && ptype != Flipper.class && !AsyncHandler.class.isAssignableFrom(ptype)
|
||||
&& !ptype.isPrimitive() && ptype != String.class && ptype != Flipper.class && !CompletionHandler.class.isAssignableFrom(ptype)
|
||||
&& !ptype.getName().startsWith("java") && n.charAt(0) != '#' && !"&".equals(n)) { //判断Json对象是否包含@RestUploadFile
|
||||
Class loop = ptype;
|
||||
do {
|
||||
@@ -989,17 +1016,17 @@ public final class Rest {
|
||||
|
||||
paramMap.put("name", pname);
|
||||
paramMap.put("type", ptype.getName());
|
||||
if (AsyncHandler.class.isAssignableFrom(ptype)) { //HttpResponse.createAsyncHandler() or HttpResponse.createAsyncHandler(Class)
|
||||
if (ptype == AsyncHandler.class) {
|
||||
if (CompletionHandler.class.isAssignableFrom(ptype)) { //HttpResponse.createAsyncHandler() or HttpResponse.createAsyncHandler(Class)
|
||||
if (ptype == CompletionHandler.class) {
|
||||
mv.visitVarInsn(ALOAD, 2);
|
||||
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "createAsyncHandler", "()Lorg/redkale/util/AsyncHandler;", false);
|
||||
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "createAsyncHandler", "()Ljava/nio/channels/CompletionHandler;", false);
|
||||
mv.visitVarInsn(ASTORE, maxLocals);
|
||||
varInsns.add(new int[]{ALOAD, maxLocals});
|
||||
} else {
|
||||
mv.visitVarInsn(ALOAD, 3);
|
||||
mv.visitVarInsn(ALOAD, 2);
|
||||
mv.visitLdcInsn(Type.getType(Type.getDescriptor(ptype)));
|
||||
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "createAsyncHandler", "(Ljava/lang/Class;)Lorg/redkale/util/AsyncHandler;", false);
|
||||
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "createAsyncHandler", "(Ljava/lang/Class;)Ljava/nio/channels/CompletionHandler;", false);
|
||||
mv.visitTypeInsn(CHECKCAST, ptype.getName().replace('.', '/'));
|
||||
mv.visitVarInsn(ASTORE, maxLocals);
|
||||
varInsns.add(new int[]{ALOAD, maxLocals});
|
||||
@@ -1283,11 +1310,7 @@ public final class Rest {
|
||||
} else {
|
||||
mv.visitVarInsn(ALOAD, 0);
|
||||
mv.visitFieldInsn(GETFIELD, newDynName, REST_PARAMTYPES_FIELD_NAME, "[[Ljava/lang/reflect/Type;");
|
||||
if (entry.methodidx <= 5) { //方法下标
|
||||
mv.visitInsn(ICONST_0 + entry.methodidx);
|
||||
} else {
|
||||
mv.visitIntInsn(BIPUSH, entry.methodidx);
|
||||
}
|
||||
pushInt(mv, entry.methodidx);//方法下标
|
||||
mv.visitInsn(AALOAD);
|
||||
int paramidx = 0;
|
||||
for (int i = 0; i < params.length; i++) {
|
||||
@@ -1296,11 +1319,7 @@ public final class Rest {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (paramidx <= 5) { //参数下标
|
||||
mv.visitInsn(ICONST_0 + paramidx);
|
||||
} else {
|
||||
mv.visitIntInsn(BIPUSH, paramidx);
|
||||
}
|
||||
pushInt(mv, paramidx); //参数下标
|
||||
mv.visitInsn(AALOAD);
|
||||
}
|
||||
mv.visitLdcInsn(pname);
|
||||
@@ -1528,34 +1547,6 @@ public final class Rest {
|
||||
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finish", "(Ljava/io/File;)V", false);
|
||||
mv.visitInsn(RETURN);
|
||||
maxLocals++;
|
||||
} else if (RetResult.class.isAssignableFrom(returnType)) {
|
||||
mv.visitVarInsn(ASTORE, maxLocals);
|
||||
mv.visitVarInsn(ALOAD, 2); //response
|
||||
if (rcs != null && rcs.length > 0) {
|
||||
mv.visitVarInsn(ALOAD, 0);
|
||||
mv.visitFieldInsn(GETFIELD, newDynName, REST_JSONCONVERT_FIELD_PREFIX + restConverts.size(), convertDesc);
|
||||
mv.visitVarInsn(ALOAD, maxLocals);
|
||||
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finishJson", "(" + convertDesc + retDesc + ")V", false);
|
||||
} else {
|
||||
mv.visitVarInsn(ALOAD, maxLocals);
|
||||
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finishJson", "(" + retDesc + ")V", false);
|
||||
}
|
||||
mv.visitInsn(RETURN);
|
||||
maxLocals++;
|
||||
} else if (HttpResult.class.isAssignableFrom(returnType)) {
|
||||
mv.visitVarInsn(ASTORE, maxLocals);
|
||||
mv.visitVarInsn(ALOAD, 2); //response
|
||||
if (rcs != null && rcs.length > 0) {
|
||||
mv.visitVarInsn(ALOAD, 0);
|
||||
mv.visitFieldInsn(GETFIELD, newDynName, REST_JSONCONVERT_FIELD_PREFIX + restConverts.size(), convertDesc);
|
||||
mv.visitVarInsn(ALOAD, maxLocals);
|
||||
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finishJson", "(" + convertDesc + httprsDesc + ")V", false);
|
||||
} else {
|
||||
mv.visitVarInsn(ALOAD, maxLocals);
|
||||
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finishJson", "(" + httprsDesc + ")V", false);
|
||||
}
|
||||
mv.visitInsn(RETURN);
|
||||
maxLocals++;
|
||||
} else if (Number.class.isAssignableFrom(returnType) || CharSequence.class.isAssignableFrom(returnType)) { //returnType == String.class 必须放在前面
|
||||
mv.visitVarInsn(ASTORE, maxLocals);
|
||||
mv.visitVarInsn(ALOAD, 2); //response
|
||||
@@ -1564,31 +1555,17 @@ public final class Rest {
|
||||
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finish", "(Ljava/lang/String;)V", false);
|
||||
mv.visitInsn(RETURN);
|
||||
maxLocals++;
|
||||
} else if (CompletableFuture.class.isAssignableFrom(returnType)) {
|
||||
mv.visitVarInsn(ASTORE, maxLocals);
|
||||
mv.visitVarInsn(ALOAD, 2);//response
|
||||
if (rcs != null && rcs.length > 0) {
|
||||
mv.visitVarInsn(ALOAD, 0);
|
||||
mv.visitFieldInsn(GETFIELD, newDynName, REST_JSONCONVERT_FIELD_PREFIX + restConverts.size(), convertDesc);
|
||||
mv.visitVarInsn(ALOAD, maxLocals);
|
||||
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finishJson", "(" + convertDesc + futureDesc + ")V", false);
|
||||
} else {
|
||||
mv.visitVarInsn(ALOAD, maxLocals);
|
||||
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finishJson", "(" + futureDesc + ")V", false);
|
||||
}
|
||||
mv.visitInsn(RETURN);
|
||||
maxLocals++;
|
||||
} else {
|
||||
mv.visitVarInsn(ASTORE, maxLocals);
|
||||
mv.visitVarInsn(ALOAD, 2); //response
|
||||
if (rcs != null && rcs.length > 0) {
|
||||
mv.visitVarInsn(ALOAD, 0);
|
||||
mv.visitFieldInsn(GETFIELD, newDynName, REST_JSONCONVERT_FIELD_PREFIX + restConverts.size(), convertDesc);
|
||||
mv.visitFieldInsn(GETFIELD, newDynName, REST_JSONCONVERT_FIELD_PREFIX + restConverts.size(), jsonConvertDesc);
|
||||
mv.visitVarInsn(ALOAD, maxLocals);
|
||||
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finishJson", "(" + convertDesc + "Ljava/lang/Object;)V", false);
|
||||
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finish", "(" + convertDesc + "Ljava/lang/Object;)V", false);
|
||||
} else {
|
||||
mv.visitVarInsn(ALOAD, maxLocals);
|
||||
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finishJson", "(Ljava/lang/Object;)V", false);
|
||||
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finish", "(Ljava/lang/Object;)V", false);
|
||||
}
|
||||
mv.visitInsn(RETURN);
|
||||
maxLocals++;
|
||||
@@ -1609,7 +1586,7 @@ public final class Rest {
|
||||
}
|
||||
|
||||
for (int i = 1; i <= restConverts.size(); i++) {
|
||||
fv = cw.visitField(ACC_PRIVATE, REST_JSONCONVERT_FIELD_PREFIX + i, convertDesc, null, null);
|
||||
fv = cw.visitField(ACC_PRIVATE, REST_JSONCONVERT_FIELD_PREFIX + i, jsonConvertDesc, null, null);
|
||||
fv.visitEnd();
|
||||
}
|
||||
|
||||
@@ -1665,6 +1642,30 @@ public final class Rest {
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void pushInt(AsmMethodVisitor mv, int num) {
|
||||
if (num < 6) {
|
||||
mv.visitInsn(ICONST_0 + num);
|
||||
} else if (num <= Byte.MAX_VALUE) {
|
||||
mv.visitIntInsn(BIPUSH, num);
|
||||
} else if (num <= Short.MAX_VALUE) {
|
||||
mv.visitIntInsn(SIPUSH, num);
|
||||
} else {
|
||||
mv.visitLdcInsn(num);
|
||||
}
|
||||
}
|
||||
|
||||
private static void pushInt(MethodVisitor mv, int num) {
|
||||
if (num < 6) {
|
||||
mv.visitInsn(ICONST_0 + num);
|
||||
} else if (num <= Byte.MAX_VALUE) {
|
||||
mv.visitIntInsn(BIPUSH, num);
|
||||
} else if (num <= Short.MAX_VALUE) {
|
||||
mv.visitIntInsn(SIPUSH, num);
|
||||
} else {
|
||||
mv.visitLdcInsn(num);
|
||||
}
|
||||
}
|
||||
|
||||
private static class RestClassLoader extends ClassLoader {
|
||||
|
||||
public RestClassLoader(ClassLoader parent) {
|
||||
|
||||
@@ -26,6 +26,8 @@ public @interface RestConvert {
|
||||
|
||||
boolean tiny() default true;
|
||||
|
||||
boolean skipIgnore() default false;
|
||||
|
||||
Class type();
|
||||
|
||||
String[] ignoreColumns() default {};
|
||||
|
||||
@@ -12,6 +12,7 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||
/**
|
||||
* 只能依附在WebSocket类上,name默认为Service的类名小写并去掉Service字样及后面的字符串 (如HelloWebSocket/HelloWebSocketImpl,的默认路径为 hello)。 <br>
|
||||
* <b>注意: </b> 被标记@RestWebSocket的WebSocket不能被修饰为abstract或final,且其内部标记为@Resource的字段只能是protected或public,且必须要有一个protected或public的空参数构造函数。 <br>
|
||||
* name值支持含{system.property.xxx}模式
|
||||
* <p>
|
||||
* 详情见: https://redkale.org
|
||||
*
|
||||
@@ -51,6 +52,13 @@ public @interface RestWebSocket {
|
||||
*/
|
||||
boolean single() default true;
|
||||
|
||||
/**
|
||||
* WebSocket.createUserid返回的值是否不能表示户登录态
|
||||
*
|
||||
* @return 默认false
|
||||
*/
|
||||
boolean anyuser() default false;
|
||||
|
||||
/**
|
||||
* WebScoket服务器给客户端进行ping操作的间隔时间, 单位: 秒, 默认值:15秒
|
||||
*
|
||||
@@ -58,6 +66,20 @@ public @interface RestWebSocket {
|
||||
*/
|
||||
int liveinterval() default WebSocketServlet.DEFAILT_LIVEINTERVAL;
|
||||
|
||||
/**
|
||||
* 最大连接数, 小于1表示无限制
|
||||
*
|
||||
* @return 最大连接数
|
||||
*/
|
||||
int wsmaxconns() default 0;
|
||||
|
||||
/**
|
||||
* 最大消息体长度, 小于1表示无限制
|
||||
*
|
||||
* @return 最大消息体长度
|
||||
*/
|
||||
int wsmaxbody() default 16 * 1024;
|
||||
|
||||
/**
|
||||
* 是否屏蔽该类的转换
|
||||
*
|
||||
|
||||
@@ -64,6 +64,9 @@ public abstract class WebSocket<G extends Serializable, T> {
|
||||
@Comment("WebSocket已离线")
|
||||
public static final int RETCODE_WSOFFLINE = 1 << 8; //256
|
||||
|
||||
@Comment("WebSocket将延迟发送")
|
||||
public static final int RETCODE_DEAYSEND = 1 << 9; //512
|
||||
|
||||
WebSocketRunner _runner; //不可能为空
|
||||
|
||||
WebSocketEngine _engine; //不可能为空
|
||||
@@ -90,6 +93,8 @@ public abstract class WebSocket<G extends Serializable, T> {
|
||||
|
||||
private Map<String, Object> attributes = new HashMap<>(); //非线程安全
|
||||
|
||||
List<WebSocketPacket> delayPackets;
|
||||
|
||||
protected WebSocket() {
|
||||
}
|
||||
|
||||
@@ -225,8 +230,15 @@ public abstract class WebSocket<G extends Serializable, T> {
|
||||
* @return 0表示成功, 非0表示错误码
|
||||
*/
|
||||
CompletableFuture<Integer> sendPacket(WebSocketPacket packet) {
|
||||
if (this._runner == null) {
|
||||
if (delayPackets == null) delayPackets = new ArrayList<>();
|
||||
delayPackets.add(packet);
|
||||
return CompletableFuture.completedFuture(RETCODE_DEAYSEND);
|
||||
}
|
||||
CompletableFuture<Integer> rs = this._runner.sendMessage(packet);
|
||||
if (_engine.finest) _engine.logger.finest("userid:" + getUserid() + " send websocket message(" + packet + ")" + " on " + this);
|
||||
if (_engine.logger.isLoggable(Level.FINEST) && packet != WebSocketPacket.DEFAULT_PING_PACKET) {
|
||||
_engine.logger.finest("userid:" + getUserid() + " send websocket message(" + packet + ")" + " on " + this);
|
||||
}
|
||||
return rs == null ? CompletableFuture.completedFuture(RETCODE_WSOCKET_CLOSED) : rs;
|
||||
}
|
||||
|
||||
@@ -342,7 +354,7 @@ public abstract class WebSocket<G extends Serializable, T> {
|
||||
return ((CompletableFuture) message).thenCompose((json) -> _engine.node.sendMessage(convert, json, last, userids));
|
||||
}
|
||||
CompletableFuture<Integer> rs = _engine.node.sendMessage(convert, message, last, userids);
|
||||
if (_engine.finest) _engine.logger.finest("userids:" + Arrays.toString(userids) + " send websocket message(" + message + ")");
|
||||
if (_engine.logger.isLoggable(Level.FINEST)) _engine.logger.finest("userids:" + Arrays.toString(userids) + " send websocket message(" + message + ")");
|
||||
return rs;
|
||||
}
|
||||
|
||||
@@ -396,7 +408,7 @@ public abstract class WebSocket<G extends Serializable, T> {
|
||||
return ((CompletableFuture) message).thenCompose((json) -> _engine.node.broadcastMessage(convert, json, last));
|
||||
}
|
||||
CompletableFuture<Integer> rs = _engine.node.broadcastMessage(convert, message, last);
|
||||
if (_engine.finest) _engine.logger.finest("broadcast send websocket message(" + message + ")");
|
||||
if (_engine.logger.isLoggable(Level.FINEST)) _engine.logger.finest("broadcast send websocket message(" + message + ")");
|
||||
return rs;
|
||||
}
|
||||
|
||||
@@ -427,6 +439,18 @@ public abstract class WebSocket<G extends Serializable, T> {
|
||||
return _engine.node.getRpcNodeWebSocketAddresses(userid);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更改本WebSocket的userid
|
||||
*
|
||||
* @param newuserid 新用户ID,不能为null
|
||||
*
|
||||
* @return CompletableFuture
|
||||
*/
|
||||
public CompletableFuture<Void> changeUserid(final G newuserid) {
|
||||
if (newuserid == null) throw new NullPointerException("newuserid is null");
|
||||
return _engine.changeUserid(this, newuserid);
|
||||
}
|
||||
|
||||
/**
|
||||
* 强制关闭用户的所有WebSocket
|
||||
*
|
||||
|
||||
@@ -10,11 +10,12 @@ import java.io.*;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.concurrent.atomic.*;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.function.*;
|
||||
import java.util.logging.*;
|
||||
import java.util.stream.*;
|
||||
import org.redkale.convert.Convert;
|
||||
import static org.redkale.net.http.WebSocket.RETCODE_GROUP_EMPTY;
|
||||
import static org.redkale.net.http.WebSocketServlet.*;
|
||||
import org.redkale.util.*;
|
||||
|
||||
/**
|
||||
@@ -53,46 +54,58 @@ public class WebSocketEngine {
|
||||
@Comment("在线用户ID对应的WebSocket组,用于单用户多连接模式")
|
||||
private final Map<Serializable, List<WebSocket>> websockets2 = new ConcurrentHashMap<>();
|
||||
|
||||
@Comment("当前连接数")
|
||||
protected final AtomicInteger currconns = new AtomicInteger();
|
||||
|
||||
@Comment("用于PING的定时器")
|
||||
private ScheduledThreadPoolExecutor scheduler;
|
||||
|
||||
@Comment("日志")
|
||||
protected final Logger logger;
|
||||
|
||||
@Comment("日志级别")
|
||||
protected final boolean finest;
|
||||
|
||||
@Comment("PING的间隔秒数")
|
||||
private int liveinterval;
|
||||
protected int liveinterval;
|
||||
|
||||
protected WebSocketEngine(String engineid, boolean single, HttpContext context, int liveinterval, WebSocketNode node, Convert sendConvert, Logger logger) {
|
||||
@Comment("最大连接数, 为0表示无限制")
|
||||
protected int wsmaxconns;
|
||||
|
||||
@Comment("最大消息体长度, 小于1表示无限制")
|
||||
protected int wsmaxbody;
|
||||
|
||||
protected WebSocketEngine(String engineid, boolean single, HttpContext context, int liveinterval,
|
||||
int wsmaxconns, int wsmaxbody, WebSocketNode node, Convert sendConvert, Logger logger) {
|
||||
this.engineid = engineid;
|
||||
this.single = single;
|
||||
this.context = context;
|
||||
this.sendConvert = sendConvert;
|
||||
this.node = node;
|
||||
this.liveinterval = liveinterval;
|
||||
this.wsmaxconns = wsmaxconns;
|
||||
this.wsmaxbody = wsmaxbody;
|
||||
this.logger = logger;
|
||||
this.finest = logger.isLoggable(Level.FINEST);
|
||||
this.index = sequence.getAndIncrement();
|
||||
}
|
||||
|
||||
void init(AnyValue conf) {
|
||||
final int interval = conf == null ? (liveinterval < 0 ? DEFAILT_LIVEINTERVAL : liveinterval) : conf.getIntValue("liveinterval", (liveinterval < 0 ? DEFAILT_LIVEINTERVAL : liveinterval));
|
||||
if (interval <= 0) return;
|
||||
AnyValue props = conf;
|
||||
if (conf != null && conf.getAnyValue("properties") != null) props = conf.getAnyValue("properties");
|
||||
this.liveinterval = props == null ? (liveinterval < 0 ? DEFAILT_LIVEINTERVAL : liveinterval) : props.getIntValue(WEBPARAM__LIVEINTERVAL, (liveinterval < 0 ? DEFAILT_LIVEINTERVAL : liveinterval));
|
||||
if (liveinterval <= 0) return;
|
||||
if (props != null) this.wsmaxconns = props.getIntValue(WEBPARAM__WSMAXCONNS, this.wsmaxconns);
|
||||
if (props != null) this.wsmaxbody = props.getIntValue(WEBPARAM__WSMAXBODY, this.wsmaxbody);
|
||||
if (scheduler != null) return;
|
||||
this.scheduler = new ScheduledThreadPoolExecutor(1, (Runnable r) -> {
|
||||
final Thread t = new Thread(r, engineid + "-WebSocket-LiveInterval-Thread");
|
||||
t.setDaemon(true);
|
||||
return t;
|
||||
});
|
||||
long delay = (interval - System.currentTimeMillis() / 1000 % interval) + index * 5;
|
||||
final int intervalms = interval * 1000;
|
||||
long delay = (liveinterval - System.currentTimeMillis() / 1000 % liveinterval) + index * 5;
|
||||
final int intervalms = liveinterval * 1000;
|
||||
scheduler.scheduleWithFixedDelay(() -> {
|
||||
long now = System.currentTimeMillis();
|
||||
getLocalWebSockets().stream().filter(x -> (now - x.getLastSendTime()) > intervalms).forEach(x -> x.sendPing());
|
||||
}, delay, interval, TimeUnit.SECONDS);
|
||||
if (finest) logger.finest(this.getClass().getSimpleName() + "(" + engineid + ")" + " start keeplive(delay:" + delay + ", interval:" + interval + "s) scheduler executor");
|
||||
}, delay, liveinterval, TimeUnit.SECONDS);
|
||||
if (logger.isLoggable(Level.FINEST)) logger.finest(this.getClass().getSimpleName() + "(" + engineid + ")" + " start keeplive(delay:" + delay + ", wsmaxconns:" + wsmaxconns + ", interval:" + liveinterval + "s) scheduler executor");
|
||||
}
|
||||
|
||||
void destroy(AnyValue conf) {
|
||||
@@ -102,6 +115,7 @@ public class WebSocketEngine {
|
||||
@Comment("添加WebSocket")
|
||||
void add(WebSocket socket) {
|
||||
if (single) {
|
||||
currconns.incrementAndGet();
|
||||
websockets.put(socket._userid, socket);
|
||||
} else { //非线程安全, 在常规场景中无需锁
|
||||
List<WebSocket> list = websockets2.get(socket._userid);
|
||||
@@ -109,6 +123,7 @@ public class WebSocketEngine {
|
||||
list = new CopyOnWriteArrayList<>();
|
||||
websockets2.put(socket._userid, list);
|
||||
}
|
||||
currconns.incrementAndGet();
|
||||
list.add(socket);
|
||||
}
|
||||
if (node != null) node.connect(socket._userid);
|
||||
@@ -118,11 +133,13 @@ public class WebSocketEngine {
|
||||
void remove(WebSocket socket) {
|
||||
Serializable userid = socket._userid;
|
||||
if (single) {
|
||||
currconns.decrementAndGet();
|
||||
websockets.remove(userid);
|
||||
if (node != null) node.disconnect(userid);
|
||||
} else { //非线程安全, 在常规场景中无需锁
|
||||
List<WebSocket> list = websockets2.get(userid);
|
||||
if (list != null) {
|
||||
currconns.decrementAndGet();
|
||||
list.remove(socket);
|
||||
if (list.isEmpty()) {
|
||||
websockets2.remove(userid);
|
||||
@@ -132,6 +149,31 @@ public class WebSocketEngine {
|
||||
}
|
||||
}
|
||||
|
||||
@Comment("更改WebSocket的userid")
|
||||
CompletableFuture<Void> changeUserid(WebSocket socket, final Serializable newuserid) {
|
||||
if (newuserid == null) throw new NullPointerException("newuserid is null");
|
||||
final Serializable olduserid = socket._userid;
|
||||
socket._userid = newuserid;
|
||||
if (single) {
|
||||
websockets.remove(olduserid);
|
||||
websockets.put(newuserid, socket);
|
||||
} else { //非线程安全, 在常规场景中无需锁
|
||||
List<WebSocket> oldlist = websockets2.get(olduserid);
|
||||
if (oldlist != null) {
|
||||
oldlist.remove(socket);
|
||||
if (oldlist.isEmpty()) websockets2.remove(olduserid);
|
||||
}
|
||||
List<WebSocket> newlist = websockets2.get(newuserid);
|
||||
if (newlist == null) {
|
||||
newlist = new CopyOnWriteArrayList<>();
|
||||
websockets2.put(newuserid, newlist);
|
||||
}
|
||||
newlist.add(socket);
|
||||
}
|
||||
if (node != null) return node.changeUserid(olduserid, newuserid);
|
||||
return CompletableFuture.completedFuture(null);
|
||||
}
|
||||
|
||||
@Comment("强制关闭本地用户的WebSocket")
|
||||
public int forceCloseLocalWebSocket(Serializable userid) {
|
||||
if (single) {
|
||||
@@ -262,6 +304,17 @@ public class WebSocketEngine {
|
||||
}
|
||||
}
|
||||
|
||||
@Comment("获取最大连接数")
|
||||
public int getLocalWsmaxconns() {
|
||||
return this.wsmaxconns;
|
||||
}
|
||||
|
||||
@Comment("连接数是否达到上限")
|
||||
public boolean isLocalConnLimited() {
|
||||
if (this.wsmaxconns < 1) return false;
|
||||
return currconns.get() >= this.wsmaxconns;
|
||||
}
|
||||
|
||||
@Comment("获取所有连接")
|
||||
public Collection<WebSocket> getLocalWebSockets() {
|
||||
if (single) return websockets.values();
|
||||
@@ -270,6 +323,16 @@ public class WebSocketEngine {
|
||||
return list;
|
||||
}
|
||||
|
||||
@Comment("获取所有连接")
|
||||
public void forEachLocalWebSocket(Consumer<WebSocket> consumer) {
|
||||
if (consumer == null) return;
|
||||
if (single) {
|
||||
websockets.values().stream().forEach(consumer);
|
||||
} else {
|
||||
websockets2.values().forEach(x -> x.stream().forEach(consumer));
|
||||
}
|
||||
}
|
||||
|
||||
@Comment("获取当前连接总数")
|
||||
public int getLocalWebSocketSize() {
|
||||
if (single) return websockets.size();
|
||||
|
||||
@@ -15,6 +15,7 @@ import java.util.stream.Stream;
|
||||
import javax.annotation.*;
|
||||
import org.redkale.boot.*;
|
||||
import org.redkale.convert.*;
|
||||
import org.redkale.convert.json.JsonConvert;
|
||||
import org.redkale.service.*;
|
||||
import org.redkale.source.*;
|
||||
import org.redkale.util.*;
|
||||
@@ -28,18 +29,17 @@ import org.redkale.util.*;
|
||||
*/
|
||||
public abstract class WebSocketNode {
|
||||
|
||||
@Comment("存储当前SNCP节点列表的key")
|
||||
public static final String SOURCE_SNCP_NODES_KEY = "redkale_sncpnodes";
|
||||
@Comment("存储用户ID的key前缀")
|
||||
public static final String SOURCE_SNCP_USERID_PREFIX = "sncpws_uid:";
|
||||
|
||||
@Comment("存储当前用户数量的key")
|
||||
public static final String SOURCE_USER_COUNT_KEY = "redkale_usercount";
|
||||
@Comment("存储用户数的key")
|
||||
public static final String SOURCE_SNCP_USERCOUNT_KEY = "sncpws_usercount";
|
||||
|
||||
@Comment("存储当前SNCP节点列表的key")
|
||||
public static final String SOURCE_SNCP_ADDRS_KEY = "sncpws_addrs";
|
||||
|
||||
protected final Logger logger = Logger.getLogger(this.getClass().getSimpleName());
|
||||
|
||||
protected final boolean finest = logger.isLoggable(Level.FINEST);
|
||||
|
||||
protected final boolean finer = logger.isLoggable(Level.FINER);
|
||||
|
||||
//"SNCP_ADDR" 如果不是分布式(没有SNCP) 值为null
|
||||
@Resource(name = Application.RESNAME_SNCP_ADDR)
|
||||
protected InetSocketAddress localSncpAddress; //为SncpServer的服务address
|
||||
@@ -48,16 +48,20 @@ public abstract class WebSocketNode {
|
||||
@RpcRemote
|
||||
protected WebSocketNode remoteNode;
|
||||
|
||||
@Resource(name = "$_textconvert")
|
||||
protected Convert textConvert;
|
||||
|
||||
//存放所有用户分布在节点上的队列信息,Set<InetSocketAddress> 为 sncpnode 的集合, key: groupid
|
||||
//集合包含 localSncpAddress
|
||||
//如果不是分布式(没有SNCP),sncpNodeAddresses 将不会被用到
|
||||
@Resource(name = "$_nodes")
|
||||
protected CacheSource<Serializable, InetSocketAddress> sncpNodeAddresses;
|
||||
@Resource(name = "$")
|
||||
protected CacheSource<InetSocketAddress> sncpNodeAddresses;
|
||||
|
||||
//当前节点的本地WebSocketEngine
|
||||
protected WebSocketEngine localEngine;
|
||||
|
||||
public void init(AnyValue conf) {
|
||||
if (sncpNodeAddresses != null) sncpNodeAddresses.initValueType(InetSocketAddress.class);
|
||||
}
|
||||
|
||||
public void destroy(AnyValue conf) {
|
||||
@@ -68,7 +72,7 @@ public abstract class WebSocketNode {
|
||||
//关掉所有本地本地WebSocket
|
||||
this.localEngine.getLocalWebSockets().forEach(g -> disconnect(g.getUserid()));
|
||||
if (sncpNodeAddresses != null && localSncpAddress != null) {
|
||||
sncpNodeAddresses.removeSetItem(SOURCE_SNCP_NODES_KEY, localSncpAddress);
|
||||
sncpNodeAddresses.removeSetItem(SOURCE_SNCP_ADDRS_KEY, localSncpAddress);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,19 +86,26 @@ public abstract class WebSocketNode {
|
||||
|
||||
protected abstract CompletableFuture<Void> disconnect(Serializable userid, InetSocketAddress addr);
|
||||
|
||||
protected abstract CompletableFuture<Void> changeUserid(Serializable fromuserid, Serializable touserid, InetSocketAddress addr);
|
||||
|
||||
protected abstract CompletableFuture<Integer> forceCloseWebSocket(Serializable userid, InetSocketAddress addr);
|
||||
|
||||
//--------------------------------------------------------------------------------
|
||||
final CompletableFuture<Void> connect(final Serializable userid) {
|
||||
if (finest) logger.finest(localSncpAddress + " receive websocket connect event (" + userid + " on " + this.localEngine.getEngineid() + ").");
|
||||
if (logger.isLoggable(Level.FINEST)) logger.finest(localSncpAddress + " receive websocket connect event (" + userid + " on " + (this.localEngine == null ? null : this.localEngine.getEngineid()) + ").");
|
||||
return connect(userid, localSncpAddress);
|
||||
}
|
||||
|
||||
final CompletableFuture<Void> disconnect(final Serializable userid) {
|
||||
if (finest) logger.finest(localSncpAddress + " receive websocket disconnect event (" + userid + " on " + this.localEngine.getEngineid() + ").");
|
||||
if (logger.isLoggable(Level.FINEST)) logger.finest(localSncpAddress + " receive websocket disconnect event (" + userid + " on " + (this.localEngine == null ? null : this.localEngine.getEngineid()) + ").");
|
||||
return disconnect(userid, localSncpAddress);
|
||||
}
|
||||
|
||||
final CompletableFuture<Void> changeUserid(Serializable olduserid, final Serializable newuserid) {
|
||||
if (logger.isLoggable(Level.FINEST)) logger.finest(localSncpAddress + " receive websocket changeUserid event (from " + olduserid + " to " + newuserid + " on " + (this.localEngine == null ? null : this.localEngine.getEngineid()) + ").");
|
||||
return changeUserid(olduserid, newuserid, localSncpAddress);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------
|
||||
/**
|
||||
* 获取目标地址 <br>
|
||||
@@ -124,7 +135,7 @@ public abstract class WebSocketNode {
|
||||
* @return 地址列表
|
||||
*/
|
||||
public CompletableFuture<Collection<InetSocketAddress>> getRpcNodeAddresses(final Serializable userid) {
|
||||
if (this.sncpNodeAddresses != null) return this.sncpNodeAddresses.getCollectionAsync(userid);
|
||||
if (this.sncpNodeAddresses != null) return this.sncpNodeAddresses.getCollectionAsync(SOURCE_SNCP_USERID_PREFIX + userid);
|
||||
List<InetSocketAddress> rs = new ArrayList<>();
|
||||
rs.add(this.localSncpAddress);
|
||||
return CompletableFuture.completedFuture(rs);
|
||||
@@ -142,7 +153,7 @@ public abstract class WebSocketNode {
|
||||
public CompletableFuture<Map<InetSocketAddress, List<String>>> getRpcNodeWebSocketAddresses(final Serializable userid) {
|
||||
CompletableFuture<Collection<InetSocketAddress>> sncpFuture = getRpcNodeAddresses(userid);
|
||||
return sncpFuture.thenCompose((Collection<InetSocketAddress> addrs) -> {
|
||||
if (finest) logger.finest("websocket found userid:" + userid + " on " + addrs);
|
||||
if (logger.isLoggable(Level.FINEST)) logger.finest("websocket found userid:" + userid + " on " + addrs);
|
||||
if (addrs == null || addrs.isEmpty()) return CompletableFuture.completedFuture(new HashMap<>());
|
||||
CompletableFuture<Map<InetSocketAddress, List<String>>> future = null;
|
||||
for (final InetSocketAddress nodeAddress : addrs) {
|
||||
@@ -165,7 +176,7 @@ public abstract class WebSocketNode {
|
||||
if (this.localEngine != null && this.sncpNodeAddresses == null) {
|
||||
return CompletableFuture.completedFuture(this.localEngine.existsLocalWebSocket(userid));
|
||||
}
|
||||
return this.sncpNodeAddresses.existsAsync(userid);
|
||||
return this.sncpNodeAddresses.existsAsync(SOURCE_SNCP_USERID_PREFIX + userid);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -178,9 +189,7 @@ public abstract class WebSocketNode {
|
||||
if (this.localEngine != null && this.sncpNodeAddresses == null) {
|
||||
return CompletableFuture.completedFuture(this.localEngine.getLocalUserSize());
|
||||
}
|
||||
return this.sncpNodeAddresses.getKeySizeAsync().thenCompose(count -> {
|
||||
return sncpNodeAddresses.existsAsync(SOURCE_SNCP_NODES_KEY).thenApply(exists -> exists ? (count - 1) : count);
|
||||
});
|
||||
return this.sncpNodeAddresses.getLongAsync(SOURCE_SNCP_USERCOUNT_KEY, 0L).thenApply(v -> v.intValue());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -194,14 +203,14 @@ public abstract class WebSocketNode {
|
||||
CompletableFuture<Integer> localFuture = null;
|
||||
if (this.localEngine != null) localFuture = CompletableFuture.completedFuture(localEngine.forceCloseLocalWebSocket(userid));
|
||||
if (this.sncpNodeAddresses == null || this.remoteNode == null) {
|
||||
if (finest) logger.finest("websocket remote node is null");
|
||||
if (logger.isLoggable(Level.FINEST)) logger.finest("websocket remote node is null");
|
||||
//没有CacheSource就不会有分布式节点
|
||||
return localFuture;
|
||||
}
|
||||
//远程节点关闭
|
||||
CompletableFuture<Collection<InetSocketAddress>> addrsFuture = sncpNodeAddresses.getCollectionAsync(userid);
|
||||
CompletableFuture<Collection<InetSocketAddress>> addrsFuture = sncpNodeAddresses.getCollectionAsync(SOURCE_SNCP_USERID_PREFIX + userid);
|
||||
CompletableFuture<Integer> remoteFuture = addrsFuture.thenCompose((Collection<InetSocketAddress> addrs) -> {
|
||||
if (finest) logger.finest("websocket found userid:" + userid + " on " + addrs);
|
||||
if (logger.isLoggable(Level.FINEST)) logger.finest("websocket found userid:" + userid + " on " + addrs);
|
||||
if (addrs == null || addrs.isEmpty()) return CompletableFuture.completedFuture(0);
|
||||
CompletableFuture<Integer> future = null;
|
||||
for (InetSocketAddress addr : addrs) {
|
||||
@@ -402,15 +411,16 @@ public abstract class WebSocketNode {
|
||||
return this.localEngine.broadcastMessage(message, last);
|
||||
}
|
||||
CompletableFuture<Integer> localFuture = this.localEngine == null ? null : this.localEngine.broadcastMessage(message, last);
|
||||
CompletableFuture<Collection<InetSocketAddress>> addrsFuture = sncpNodeAddresses.getCollectionAsync("redkale_sncpnodes");
|
||||
CompletableFuture<Collection<InetSocketAddress>> addrsFuture = sncpNodeAddresses.getCollectionAsync(SOURCE_SNCP_ADDRS_KEY);
|
||||
CompletableFuture<Integer> remoteFuture = addrsFuture.thenCompose((Collection<InetSocketAddress> addrs) -> {
|
||||
if (finest) logger.finest("websocket broadcast message on " + addrs);
|
||||
if (logger.isLoggable(Level.FINEST)) logger.finest("websocket broadcast message on " + addrs);
|
||||
if (addrs == null || addrs.isEmpty()) return CompletableFuture.completedFuture(0);
|
||||
CompletableFuture<Integer> future = null;
|
||||
Object remoteMessage = formatRemoteMessage(message);
|
||||
for (InetSocketAddress addr : addrs) {
|
||||
if (addr == null || addr.equals(localSncpAddress)) continue;
|
||||
future = future == null ? remoteNode.broadcastMessage(addr, message, last)
|
||||
: future.thenCombine(remoteNode.broadcastMessage(addr, message, last), (a, b) -> a | b);
|
||||
future = future == null ? remoteNode.broadcastMessage(addr, remoteMessage, last)
|
||||
: future.thenCombine(remoteNode.broadcastMessage(addr, remoteMessage, last), (a, b) -> a | b);
|
||||
}
|
||||
return future == null ? CompletableFuture.completedFuture(0) : future;
|
||||
});
|
||||
@@ -418,30 +428,40 @@ public abstract class WebSocketNode {
|
||||
}
|
||||
|
||||
private CompletableFuture<Integer> sendOneMessage(final Object message, final boolean last, final Serializable userid) {
|
||||
if (finest) logger.finest("websocket want send message {userid:" + userid + ", content:'" + message + "'} from locale node to locale engine");
|
||||
if (message instanceof CompletableFuture) return ((CompletableFuture) message).thenApply(msg -> sendOneMessage(msg, last, userid));
|
||||
if (logger.isLoggable(Level.FINEST)) logger.finest("websocket want send message {userid:" + userid + ", content:'" + JsonConvert.root().convertTo(message) + "'} from locale node to " + ((this.localEngine != null) ? "locale" : "remote") + " engine");
|
||||
CompletableFuture<Integer> localFuture = null;
|
||||
if (this.localEngine != null) localFuture = localEngine.sendMessage(message, last, userid);
|
||||
if (this.sncpNodeAddresses == null || this.remoteNode == null) {
|
||||
if (finest) logger.finest("websocket remote node is null");
|
||||
if (logger.isLoggable(Level.FINEST)) logger.finest("websocket remote node is null");
|
||||
//没有CacheSource就不会有分布式节点
|
||||
return localFuture == null ? CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY) : localFuture;
|
||||
}
|
||||
//远程节点发送消息
|
||||
CompletableFuture<Collection<InetSocketAddress>> addrsFuture = sncpNodeAddresses.getCollectionAsync(userid);
|
||||
CompletableFuture<Collection<InetSocketAddress>> addrsFuture = sncpNodeAddresses.getCollectionAsync(SOURCE_SNCP_USERID_PREFIX + userid);
|
||||
CompletableFuture<Integer> remoteFuture = addrsFuture.thenCompose((Collection<InetSocketAddress> addrs) -> {
|
||||
if (addrs == null || addrs.isEmpty()) {
|
||||
if (finer) logger.finer("websocket not found userid:" + userid + " on any node ");
|
||||
return CompletableFuture.completedFuture(0);
|
||||
if (logger.isLoggable(Level.FINER)) logger.finer("websocket not found userid:" + userid + " on any node ");
|
||||
return CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY);
|
||||
}
|
||||
if (finest) logger.finest("websocket found userid:" + userid + " on " + addrs);
|
||||
if (logger.isLoggable(Level.FINEST)) logger.finest("websocket(localaddr=" + localSncpAddress + ") found userid:" + userid + " on " + addrs);
|
||||
CompletableFuture<Integer> future = null;
|
||||
Object remoteMessage = formatRemoteMessage(message);
|
||||
for (InetSocketAddress addr : addrs) {
|
||||
if (addr == null || addr.equals(localSncpAddress)) continue;
|
||||
future = future == null ? remoteNode.sendMessage(addr, message, last, userid)
|
||||
: future.thenCombine(remoteNode.sendMessage(addr, message, last, userid), (a, b) -> a | b);
|
||||
future = future == null ? remoteNode.sendMessage(addr, remoteMessage, last, userid)
|
||||
: future.thenCombine(remoteNode.sendMessage(addr, remoteMessage, last, userid), (a, b) -> a | b);
|
||||
}
|
||||
return future == null ? CompletableFuture.completedFuture(0) : future;
|
||||
return future == null ? CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY) : future;
|
||||
});
|
||||
return localFuture == null ? remoteFuture : localFuture.thenCombine(remoteFuture, (a, b) -> a | b);
|
||||
}
|
||||
|
||||
private Object formatRemoteMessage(Object message) {
|
||||
if (message instanceof WebSocketPacket) return message;
|
||||
if (message instanceof byte[]) return message;
|
||||
if (message instanceof CharSequence) return message;
|
||||
if (textConvert != null) return ((TextConvert) textConvert).convertTo(message);
|
||||
return JsonConvert.root().convertTo(message);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,8 @@ package org.redkale.net.http;
|
||||
import org.redkale.util.Utility;
|
||||
import java.io.*;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.AbstractMap;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.logging.*;
|
||||
import org.redkale.convert.*;
|
||||
@@ -21,8 +23,16 @@ import org.redkale.convert.*;
|
||||
*/
|
||||
public final class WebSocketPacket {
|
||||
|
||||
private static final Charset UTF_8 = Charset.forName("UTF-8");
|
||||
|
||||
static final WebSocketPacket NONE = new WebSocketPacket();
|
||||
|
||||
public static final WebSocketPacket DEFAULT_PING_PACKET = new WebSocketPacket(FrameType.PING, new byte[0]);
|
||||
|
||||
public static enum MessageType {
|
||||
STRING, BYTES, OBJECT;
|
||||
}
|
||||
|
||||
public static enum FrameType {
|
||||
|
||||
TEXT(0x01), BINARY(0x02), CLOSE(0x08), PING(0x09), PONG(0x0A);
|
||||
@@ -57,14 +67,24 @@ public final class WebSocketPacket {
|
||||
|
||||
protected boolean last = true;
|
||||
|
||||
//---------------发送------------------------
|
||||
Object sendJson;
|
||||
|
||||
Convert sendConvert;
|
||||
|
||||
boolean mapconvable;
|
||||
boolean sendMapconvable;
|
||||
|
||||
ByteBuffer[] sendBuffers;
|
||||
|
||||
//---------------接收------------------------
|
||||
MessageType receiveType;
|
||||
|
||||
int receiveCount;
|
||||
|
||||
int receiveLength;
|
||||
|
||||
Object receiveMessage;
|
||||
|
||||
ConvertMask receiveMasker;
|
||||
|
||||
ByteBuffer[] receiveBuffers;
|
||||
@@ -119,7 +139,7 @@ public final class WebSocketPacket {
|
||||
WebSocketPacket(Convert convert, boolean mapconvable, Object json, boolean fin) {
|
||||
this.type = (convert == null || !convert.isBinary()) ? FrameType.TEXT : FrameType.BINARY;
|
||||
this.sendConvert = convert;
|
||||
this.mapconvable = mapconvable;
|
||||
this.sendMapconvable = mapconvable;
|
||||
this.sendJson = json;
|
||||
this.last = fin;
|
||||
if (mapconvable && !(json instanceof Object[])) throw new IllegalArgumentException();
|
||||
@@ -183,7 +203,7 @@ public final class WebSocketPacket {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.getClass().getSimpleName() + "[type=" + type + ", last=" + last + (payload != null ? (", payload=" + payload) : "") + (bytes != null ? (", bytes=[" + bytes.length + ']') : "") + (sendJson != null ? (", json=" + (mapconvable ? Utility.ofMap((Object[]) sendJson) : sendJson)) : "") + "]";
|
||||
return this.getClass().getSimpleName() + "[type=" + type + ", last=" + last + (payload != null ? (", payload=" + payload) : "") + (bytes != null ? (", bytes=[" + bytes.length + ']') : (receiveLength > 0 ? (", receivemsg=" + receiveMessage) : "")) + (sendJson != null ? (", json=" + (sendMapconvable ? Utility.ofMap((Object[]) sendJson) : sendJson)) : "") + "]";
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -211,7 +231,7 @@ public final class WebSocketPacket {
|
||||
return supplier.get();
|
||||
}
|
||||
};
|
||||
ByteBuffer[] buffers = this.mapconvable ? this.sendConvert.convertMapTo(newsupplier, (Object[]) sendJson) : this.sendConvert.convertTo(newsupplier, sendJson);
|
||||
ByteBuffer[] buffers = this.sendMapconvable ? this.sendConvert.convertMapTo(newsupplier, (Object[]) sendJson) : this.sendConvert.convertTo(newsupplier, sendJson);
|
||||
int len = 0;
|
||||
for (ByteBuffer buf : buffers) {
|
||||
len += buf.remaining();
|
||||
@@ -290,6 +310,21 @@ public final class WebSocketPacket {
|
||||
// String rs = JsonConvert.root().convertFrom(String.class, masker, buffer);
|
||||
// System.out.println(rs);
|
||||
// }
|
||||
/**
|
||||
*
|
||||
* @param webSocket WebSocket
|
||||
* @param readBuffer ByteBuffer
|
||||
*
|
||||
* @return boolean 已接收完返回true, 需要继续接收body返回false;
|
||||
*/
|
||||
boolean receiveBody(WebSocket webSocket, ByteBuffer readBuffer) {
|
||||
int need = receiveLength - receiveCount;
|
||||
boolean over = readBuffer.remaining() >= need;
|
||||
this.receiveBuffers = Utility.append(this.receiveBuffers, readBuffer);
|
||||
if (over) parseReceiveMessage(webSocket, this.receiveBuffers);
|
||||
return over;
|
||||
}
|
||||
|
||||
/**
|
||||
* 消息解码 <br>
|
||||
*
|
||||
@@ -315,26 +350,25 @@ public final class WebSocketPacket {
|
||||
* @param buffer
|
||||
* @param exbuffers
|
||||
*
|
||||
* @return
|
||||
* @return 返回NONE表示Buffer内容不够; 返回this表示解析完成或部分解析完成;返回null表示解析异常;
|
||||
*/
|
||||
WebSocketPacket decode(final Logger logger, final ByteBuffer buffer, ByteBuffer... exbuffers) {
|
||||
WebSocketPacket decode(final Logger logger, final WebSocket webSocket, final int wsmaxbody,
|
||||
final AbstractMap.SimpleEntry<String, byte[]> halfBytes, final ByteBuffer buffer) {
|
||||
//开始
|
||||
final boolean debug = false; //调试开关
|
||||
if (debug) {
|
||||
int remain = buffer.remaining();
|
||||
if (exbuffers != null) {
|
||||
for (ByteBuffer b : exbuffers) {
|
||||
remain += b == null ? 0 : b.remaining();
|
||||
}
|
||||
}
|
||||
logger.log(Level.FINEST, "read websocket message's length = " + remain);
|
||||
if (debug) logger.log(Level.FINEST, "read websocket message's length = " + buffer.remaining());
|
||||
if (!buffer.hasRemaining()) return NONE;
|
||||
if (buffer.remaining() < 2) {
|
||||
byte[] bs = new byte[buffer.remaining()];
|
||||
buffer.get(bs);
|
||||
halfBytes.setValue(bs);
|
||||
return NONE;
|
||||
}
|
||||
if (buffer.remaining() < 2) return null;
|
||||
byte opcode = buffer.get();
|
||||
final byte opcode = buffer.get(); //第一个字节
|
||||
this.last = (opcode & 0b1000_0000) != 0;
|
||||
this.type = FrameType.valueOf(opcode & 0xF);
|
||||
if (type == FrameType.CLOSE) {
|
||||
if (debug) logger.log(Level.FINEST, " receive close command from websocket client");
|
||||
return this;
|
||||
}
|
||||
final boolean checkrsv = false;//暂时不校验
|
||||
if (checkrsv && (opcode & 0b0111_0000) != 0) {
|
||||
@@ -350,9 +384,23 @@ public final class WebSocketPacket {
|
||||
//0xA 表示一个pong
|
||||
//0x0B-0F 为以后的控制帧保留
|
||||
final boolean control = (opcode & 0b0000_1000) != 0; //是否控制帧
|
||||
byte lengthCode = buffer.get();
|
||||
final byte crcode = buffer.get(); //第二个字节
|
||||
|
||||
byte lengthCode = crcode;
|
||||
final boolean masked = (lengthCode & 0x80) == 0x80;
|
||||
if (masked) lengthCode ^= 0x80; //mask
|
||||
|
||||
//判断Buffer剩余内容够不够基本信息的创建
|
||||
int minBufferLength = ((lengthCode <= 0x7D) ? 0 : (lengthCode == 0x7E ? 2 : 4)) + (masked ? 4 : 0);
|
||||
if (buffer.remaining() < minBufferLength) {
|
||||
byte[] bs = new byte[2 + buffer.remaining()];
|
||||
bs[0] = opcode;
|
||||
bs[1] = crcode;
|
||||
buffer.get(bs, 2, buffer.remaining());
|
||||
halfBytes.setValue(bs);
|
||||
return NONE;
|
||||
}
|
||||
|
||||
int length;
|
||||
if (lengthCode <= 0x7D) { //125
|
||||
length = lengthCode;
|
||||
@@ -367,6 +415,11 @@ public final class WebSocketPacket {
|
||||
length = buffer.getInt();
|
||||
}
|
||||
}
|
||||
if (length > wsmaxbody && wsmaxbody > 0) {
|
||||
if (debug) logger.log(Level.FINE, "message length (" + length + ") too big, must less " + wsmaxbody + "");
|
||||
return null;
|
||||
}
|
||||
this.receiveLength = length;
|
||||
if (masked) {
|
||||
final byte[] masks = new byte[4];
|
||||
buffer.get(masks);
|
||||
@@ -380,25 +433,65 @@ public final class WebSocketPacket {
|
||||
}
|
||||
};
|
||||
}
|
||||
this.receiveBuffers = Utility.append(new ByteBuffer[]{buffer}, exbuffers);
|
||||
if (buffer.remaining() >= this.receiveLength) { //内容足够, 可以解析
|
||||
this.parseReceiveMessage(webSocket, buffer);
|
||||
this.receiveCount = this.receiveLength;
|
||||
} else {
|
||||
this.receiveCount = buffer.remaining();
|
||||
this.receiveBuffers = buffer.hasRemaining() ? new ByteBuffer[]{buffer} : null;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
byte[] getReceiveBytes() {
|
||||
if (this.receiveBuffers.length == 0) return new byte[0];
|
||||
if (this.receiveBuffers.length == 1 && this.receiveBuffers[0].remaining() == 0) return new byte[0];
|
||||
|
||||
int count = 0;
|
||||
for (ByteBuffer buf : this.receiveBuffers) {
|
||||
count += buf.remaining();
|
||||
void parseReceiveMessage(WebSocket webSocket, ByteBuffer... buffers) {
|
||||
if (this.type == FrameType.TEXT) {
|
||||
Convert textConvert = webSocket.getTextConvert();
|
||||
if (textConvert == null) {
|
||||
this.receiveMessage = new String(this.getReceiveBytes(buffers), UTF_8);
|
||||
this.receiveType = MessageType.STRING;
|
||||
} else {
|
||||
this.receiveMessage = textConvert.convertFrom(webSocket._messageTextType, this.receiveMasker, buffers);
|
||||
this.receiveCount = this.receiveLength;
|
||||
this.receiveType = MessageType.OBJECT;
|
||||
}
|
||||
} else if (this.type == FrameType.BINARY) {
|
||||
Convert binaryConvert = webSocket.getBinaryConvert();
|
||||
if (binaryConvert == null) {
|
||||
this.receiveMessage = this.getReceiveBytes(buffers);
|
||||
this.receiveType = MessageType.BYTES;
|
||||
} else {
|
||||
this.receiveMessage = binaryConvert.convertFrom(webSocket._messageTextType, this.receiveMasker, buffers);
|
||||
this.receiveCount = this.receiveLength;
|
||||
this.receiveType = MessageType.OBJECT;
|
||||
}
|
||||
} else if (this.type == FrameType.PING) {
|
||||
this.receiveMessage = this.getReceiveBytes(buffers);
|
||||
this.receiveType = MessageType.BYTES;
|
||||
} else if (this.type == FrameType.PONG) {
|
||||
this.receiveMessage = this.getReceiveBytes(buffers);
|
||||
this.receiveType = MessageType.BYTES;
|
||||
} else if (this.type == FrameType.CLOSE) {
|
||||
this.receiveMessage = this.getReceiveBytes(buffers);
|
||||
this.receiveType = MessageType.BYTES;
|
||||
}
|
||||
byte[] bs = new byte[count];
|
||||
}
|
||||
|
||||
boolean isReceiveFinished() {
|
||||
return this.receiveLength <= this.receiveCount;
|
||||
}
|
||||
|
||||
byte[] getReceiveBytes(ByteBuffer... buffers) {
|
||||
final int length = this.receiveLength;
|
||||
if (length == 0) return new byte[0];
|
||||
byte[] bs = new byte[length];
|
||||
int index = 0;
|
||||
for (ByteBuffer buf : this.receiveBuffers) {
|
||||
int r = buf.remaining();
|
||||
for (ByteBuffer buf : buffers) {
|
||||
int r = Math.min(buf.remaining(), length - index);
|
||||
buf.get(bs, index, r);
|
||||
index += r;
|
||||
if (index >= length) break;
|
||||
}
|
||||
this.receiveCount = index;
|
||||
ConvertMask mask = this.receiveMasker;
|
||||
if (mask != null) {
|
||||
for (int i = 0; i < bs.length; i++) {
|
||||
@@ -407,4 +500,5 @@ public final class WebSocketPacket {
|
||||
}
|
||||
return bs;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -12,12 +12,11 @@ import org.redkale.net.http.WebSocketPacket.FrameType;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.*;
|
||||
import java.util.*;
|
||||
import java.util.AbstractMap.SimpleEntry;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.logging.*;
|
||||
import org.redkale.convert.Convert;
|
||||
import org.redkale.util.Utility;
|
||||
|
||||
/**
|
||||
* WebSocket的消息接收发送器, 一个WebSocket对应一个WebSocketRunner
|
||||
@@ -65,175 +64,270 @@ class WebSocketRunner implements Runnable {
|
||||
webSocket.onConnected();
|
||||
channel.setReadTimeoutSecond(300); //读取超时5分钟
|
||||
if (channel.isOpen()) {
|
||||
final int wsmaxbody = webSocket._engine.wsmaxbody;
|
||||
channel.read(readBuffer, null, new CompletionHandler<Integer, Void>() {
|
||||
|
||||
private ByteBuffer recentExBuffer;
|
||||
//尚未解析完的数据包
|
||||
private WebSocketPacket unfinishPacket;
|
||||
|
||||
//当接收的数据流长度大于ByteBuffer长度时, 则需要额外的ByteBuffer 辅助;
|
||||
private final List<ByteBuffer> readBuffers = new ArrayList<>();
|
||||
private final List<ByteBuffer> exBuffers = new ArrayList<>();
|
||||
|
||||
private final SimpleEntry<String, byte[]> halfBytes = new SimpleEntry("", null);
|
||||
|
||||
@Override
|
||||
public void completed(Integer count, Void attachment1) {
|
||||
if (count < 1 && readBuffers.isEmpty()) {
|
||||
if (count < 1) {
|
||||
closeRunner(0);
|
||||
if (debug) context.getLogger().log(Level.FINEST, "WebSocketRunner abort on read buffer count, force to close channel, live " + (System.currentTimeMillis() - webSocket.getCreatetime()) / 1000 + " seconds");
|
||||
return;
|
||||
}
|
||||
if (readBuffer == null) return;
|
||||
if (!readBuffer.hasRemaining() && (recentExBuffer == null || !recentExBuffer.hasRemaining())) {
|
||||
final ByteBuffer buffer = context.pollBuffer();
|
||||
recentExBuffer = buffer;
|
||||
readBuffers.add(buffer);
|
||||
channel.read(buffer, null, this);
|
||||
return;
|
||||
}
|
||||
readBuffer.flip();
|
||||
|
||||
ByteBuffer[] exBuffers = null;
|
||||
if (!readBuffers.isEmpty()) {
|
||||
exBuffers = readBuffers.toArray(new ByteBuffer[readBuffers.size()]);
|
||||
readBuffers.clear();
|
||||
recentExBuffer = null;
|
||||
for (ByteBuffer b : exBuffers) {
|
||||
b.flip();
|
||||
WebSocketPacket onePacket = null;
|
||||
if (unfinishPacket != null) {
|
||||
if (unfinishPacket.receiveBody(webSocket, readBuffer)) { //已经接收完毕
|
||||
onePacket = unfinishPacket;
|
||||
unfinishPacket = null;
|
||||
for (ByteBuffer b : exBuffers) {
|
||||
context.offerBuffer(b);
|
||||
}
|
||||
exBuffers.clear();
|
||||
} else { //需要继续接收
|
||||
readBuffer = context.pollBuffer();
|
||||
channel.read(readBuffer, null, this);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
final List<WebSocketPacket> packets = new ArrayList<>();
|
||||
if (onePacket != null) packets.add(onePacket);
|
||||
try {
|
||||
WebSocketPacket packet;
|
||||
try {
|
||||
packet = new WebSocketPacket().decode(context.getLogger(), readBuffer, exBuffers);
|
||||
} catch (Exception e) { //接收的消息体解析失败
|
||||
webSocket.onOccurException(e, Utility.append(new ByteBuffer[]{readBuffer}, exBuffers == null ? new ByteBuffer[0] : exBuffers));
|
||||
if (readBuffer != null) {
|
||||
readBuffer.clear();
|
||||
channel.read(readBuffer, null, this);
|
||||
while (true) {
|
||||
WebSocketPacket packet = new WebSocketPacket().decode(context.getLogger(), webSocket, wsmaxbody, halfBytes, readBuffer);
|
||||
if (packet == WebSocketPacket.NONE) break; //解析完毕但是buffer有多余字节
|
||||
if (packet != null && !packet.isReceiveFinished()) {
|
||||
unfinishPacket = packet;
|
||||
if (readBuffer.hasRemaining()) {
|
||||
exBuffers.add(readBuffer);
|
||||
readBuffer = context.pollBuffer();
|
||||
}
|
||||
break;
|
||||
}
|
||||
return;
|
||||
packets.add(packet);
|
||||
if (packet == null || !readBuffer.hasRemaining()) break;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
context.getLogger().log(Level.SEVERE, "WebSocket parse message error", e);
|
||||
webSocket.onOccurException(e, null);
|
||||
}
|
||||
//继续监听消息
|
||||
readBuffer.clear();
|
||||
if (halfBytes.getValue() != null) {
|
||||
readBuffer.put(halfBytes.getValue());
|
||||
halfBytes.setValue(null);
|
||||
}
|
||||
channel.read(readBuffer, null, this);
|
||||
|
||||
//消息处理
|
||||
for (final WebSocketPacket packet : packets) {
|
||||
if (packet == null) {
|
||||
failed(null, attachment1);
|
||||
if (debug) context.getLogger().log(Level.FINEST, "WebSocketRunner abort on decode WebSocketPacket, force to close channel, live " + (System.currentTimeMillis() - webSocket.getCreatetime()) / 1000 + " seconds");
|
||||
return;
|
||||
}
|
||||
|
||||
if (packet.type == FrameType.TEXT) {
|
||||
Convert textConvert = webSocket.getTextConvert();
|
||||
if (textConvert == null) {
|
||||
byte[] message = packet.getReceiveBytes();
|
||||
if (readBuffer != null) {
|
||||
readBuffer.clear();
|
||||
channel.read(readBuffer, null, this);
|
||||
}
|
||||
try {
|
||||
webSocket.onMessage(new String(message, "UTF-8"), packet.last);
|
||||
} catch (Exception e) {
|
||||
context.getLogger().log(Level.SEVERE, "WebSocket onBinaryMessage error (" + packet + ")", e);
|
||||
}
|
||||
} else {
|
||||
Object message;
|
||||
try {
|
||||
message = textConvert.convertFrom(webSocket._messageTextType, packet.receiveMasker, packet.receiveBuffers);
|
||||
} catch (Exception e) { //接收的消息体解析失败
|
||||
webSocket.onOccurException(e, packet.receiveBuffers);
|
||||
if (readBuffer != null) {
|
||||
readBuffer.clear();
|
||||
channel.read(readBuffer, null, this);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (readBuffer != null) {
|
||||
readBuffer.clear();
|
||||
channel.read(readBuffer, null, this);
|
||||
}
|
||||
try {
|
||||
try {
|
||||
if (packet.receiveType == WebSocketPacket.MessageType.STRING) {
|
||||
webSocket.onMessage((String) packet.receiveMessage, packet.last);
|
||||
} else {
|
||||
if (restMessageConsumer != null) { //主要供RestWebSocket使用
|
||||
restMessageConsumer.accept(webSocket, message);
|
||||
restMessageConsumer.accept(webSocket, packet.receiveMessage);
|
||||
} else {
|
||||
webSocket.onMessage(message, packet.last);
|
||||
webSocket.onMessage(packet.receiveMessage, packet.last);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
context.getLogger().log(Level.SEVERE, "WebSocket onTextMessage error (" + packet + ")", e);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
context.getLogger().log(Level.SEVERE, "WebSocket onTextMessage error (" + packet + ")", e);
|
||||
}
|
||||
} else if (packet.type == FrameType.BINARY) {
|
||||
Convert binaryConvert = webSocket.getBinaryConvert();
|
||||
if (binaryConvert == null) {
|
||||
byte[] message = packet.getReceiveBytes();
|
||||
if (readBuffer != null) {
|
||||
readBuffer.clear();
|
||||
channel.read(readBuffer, null, this);
|
||||
}
|
||||
try {
|
||||
webSocket.onMessage(message, packet.last);
|
||||
} catch (Exception e) {
|
||||
context.getLogger().log(Level.SEVERE, "WebSocket onBinaryMessage error (" + packet + ")", e);
|
||||
}
|
||||
} else {
|
||||
Object message;
|
||||
try {
|
||||
message = binaryConvert.convertFrom(webSocket._messageTextType, packet.receiveMasker, packet.receiveBuffers);
|
||||
} catch (Exception e) { //接收的消息体解析失败
|
||||
webSocket.onOccurException(e, packet.receiveBuffers);
|
||||
if (readBuffer != null) {
|
||||
readBuffer.clear();
|
||||
channel.read(readBuffer, null, this);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (readBuffer != null) {
|
||||
readBuffer.clear();
|
||||
channel.read(readBuffer, null, this);
|
||||
}
|
||||
try {
|
||||
if (restMessageConsumer != null) { //主要供RestWebSocket使用
|
||||
restMessageConsumer.accept(webSocket, message);
|
||||
} else {
|
||||
webSocket.onMessage(message, packet.last);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
context.getLogger().log(Level.SEVERE, "WebSocket onTextMessage error (" + packet + ")", e);
|
||||
}
|
||||
}
|
||||
} else if (packet.type == FrameType.PONG) {
|
||||
byte[] message = packet.getReceiveBytes();
|
||||
if (readBuffer != null) {
|
||||
readBuffer.clear();
|
||||
channel.read(readBuffer, null, this);
|
||||
}
|
||||
try {
|
||||
webSocket.onPong(message);
|
||||
if (packet.receiveType == WebSocketPacket.MessageType.BYTES) {
|
||||
webSocket.onMessage((byte[]) packet.receiveMessage, packet.last);
|
||||
} else {
|
||||
if (restMessageConsumer != null) { //主要供RestWebSocket使用
|
||||
restMessageConsumer.accept(webSocket, packet.receiveMessage);
|
||||
} else {
|
||||
webSocket.onMessage(packet.receiveMessage, packet.last);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
context.getLogger().log(Level.SEVERE, "WebSocket onPong error (" + packet + ")", e);
|
||||
context.getLogger().log(Level.SEVERE, "WebSocket onBinaryMessage error (" + packet + ")", e);
|
||||
}
|
||||
} else if (packet.type == FrameType.PING) {
|
||||
byte[] message = packet.getReceiveBytes();
|
||||
if (readBuffer != null) {
|
||||
readBuffer.clear();
|
||||
channel.read(readBuffer, null, this);
|
||||
}
|
||||
try {
|
||||
webSocket.onPing(message);
|
||||
webSocket.onPing((byte[]) packet.receiveMessage);
|
||||
} catch (Exception e) {
|
||||
context.getLogger().log(Level.SEVERE, "WebSocket onPing error (" + packet + ")", e);
|
||||
}
|
||||
} else if (packet.type == FrameType.PONG) {
|
||||
try {
|
||||
webSocket.onPong((byte[]) packet.receiveMessage);
|
||||
} catch (Exception e) {
|
||||
context.getLogger().log(Level.SEVERE, "WebSocket onPong error (" + packet + ")", e);
|
||||
}
|
||||
} else if (packet.type == FrameType.CLOSE) {
|
||||
Logger logger = context.getLogger();
|
||||
if (logger.isLoggable(Level.FINEST)) logger.log(Level.FINEST, "WebSocketRunner onMessage by CLOSE FrameType : " + packet);
|
||||
closeRunner(0);
|
||||
return;
|
||||
} else {
|
||||
context.getLogger().log(Level.WARNING, "WebSocketRunner onMessage by unknown FrameType : " + packet);
|
||||
if (readBuffer != null) {
|
||||
readBuffer.clear();
|
||||
channel.read(readBuffer, null, this);
|
||||
}
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
closeRunner(0);
|
||||
if (debug) context.getLogger().log(Level.FINEST, "WebSocketRunner abort on read WebSocketPacket, force to close channel, live " + (System.currentTimeMillis() - webSocket.getCreatetime()) / 1000 + " seconds", t);
|
||||
} finally {
|
||||
if (exBuffers != null) {
|
||||
for (ByteBuffer b : exBuffers) {
|
||||
context.offerBuffer(b);
|
||||
}
|
||||
closeRunner(0);
|
||||
return;
|
||||
}
|
||||
}
|
||||
// if (true) return; //以下代码废弃
|
||||
// try {
|
||||
// WebSocketPacket packet;
|
||||
// try {
|
||||
// packet = new WebSocketPacket().decode(context.getLogger(), readBuffer, exBuffers);
|
||||
// } catch (Exception e) { //接收的消息体解析失败
|
||||
// webSocket.onOccurException(e, Utility.append(new ByteBuffer[]{readBuffer}, exBuffers == null ? new ByteBuffer[0] : exBuffers));
|
||||
// if (readBuffer != null) {
|
||||
// readBuffer.clear();
|
||||
// channel.read(readBuffer, null, this);
|
||||
// }
|
||||
// return;
|
||||
// }
|
||||
// if (packet == null) {
|
||||
// failed(null, attachment1);
|
||||
// if (debug) context.getLogger().log(Level.FINEST, "WebSocketRunner abort on decode WebSocketPacket, force to close channel, live " + (System.currentTimeMillis() - webSocket.getCreatetime()) / 1000 + " seconds");
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// if (packet.type == FrameType.TEXT) {
|
||||
// Convert textConvert = webSocket.getTextConvert();
|
||||
// if (textConvert == null) {
|
||||
// byte[] message = packet.getReceiveBytes();
|
||||
// if (readBuffer != null) {
|
||||
// readBuffer.clear();
|
||||
// channel.read(readBuffer, null, this);
|
||||
// }
|
||||
// try {
|
||||
// webSocket.onMessage(new String(message, "UTF-8"), packet.last);
|
||||
// } catch (Exception e) {
|
||||
// context.getLogger().log(Level.SEVERE, "WebSocket onBinaryMessage error (" + packet + ")", e);
|
||||
// }
|
||||
// } else {
|
||||
// Object message;
|
||||
// try {
|
||||
// message = textConvert.convertFrom(webSocket._messageTextType, packet.receiveMasker, packet.receiveBuffers);
|
||||
// } catch (Exception e) { //接收的消息体解析失败
|
||||
// webSocket.onOccurException(e, packet.receiveBuffers);
|
||||
// if (readBuffer != null) {
|
||||
// readBuffer.clear();
|
||||
// channel.read(readBuffer, null, this);
|
||||
// }
|
||||
// return;
|
||||
// }
|
||||
// if (readBuffer != null) {
|
||||
// readBuffer.clear();
|
||||
// channel.read(readBuffer, null, this);
|
||||
// }
|
||||
// try {
|
||||
// if (restMessageConsumer != null) { //主要供RestWebSocket使用
|
||||
// restMessageConsumer.accept(webSocket, message);
|
||||
// } else {
|
||||
// webSocket.onMessage(message, packet.last);
|
||||
// }
|
||||
// } catch (Exception e) {
|
||||
// context.getLogger().log(Level.SEVERE, "WebSocket onTextMessage error (" + packet + ")", e);
|
||||
// }
|
||||
// }
|
||||
// } else if (packet.type == FrameType.BINARY) {
|
||||
// Convert binaryConvert = webSocket.getBinaryConvert();
|
||||
// if (binaryConvert == null) {
|
||||
// byte[] message = packet.getReceiveBytes();
|
||||
// if (readBuffer != null) {
|
||||
// readBuffer.clear();
|
||||
// channel.read(readBuffer, null, this);
|
||||
// }
|
||||
// try {
|
||||
// webSocket.onMessage(message, packet.last);
|
||||
// } catch (Exception e) {
|
||||
// context.getLogger().log(Level.SEVERE, "WebSocket onBinaryMessage error (" + packet + ")", e);
|
||||
// }
|
||||
// } else {
|
||||
// Object message;
|
||||
// try {
|
||||
// message = binaryConvert.convertFrom(webSocket._messageTextType, packet.receiveMasker, packet.receiveBuffers);
|
||||
// } catch (Exception e) { //接收的消息体解析失败
|
||||
// webSocket.onOccurException(e, packet.receiveBuffers);
|
||||
// if (readBuffer != null) {
|
||||
// readBuffer.clear();
|
||||
// channel.read(readBuffer, null, this);
|
||||
// }
|
||||
// return;
|
||||
// }
|
||||
// if (readBuffer != null) {
|
||||
// readBuffer.clear();
|
||||
// channel.read(readBuffer, null, this);
|
||||
// }
|
||||
// try {
|
||||
// if (restMessageConsumer != null) { //主要供RestWebSocket使用
|
||||
// restMessageConsumer.accept(webSocket, message);
|
||||
// } else {
|
||||
// webSocket.onMessage(message, packet.last);
|
||||
// }
|
||||
// } catch (Exception e) {
|
||||
// context.getLogger().log(Level.SEVERE, "WebSocket onTextMessage error (" + packet + ")", e);
|
||||
// }
|
||||
// }
|
||||
// } else if (packet.type == FrameType.PONG) {
|
||||
// byte[] message = packet.getReceiveBytes();
|
||||
// if (readBuffer != null) {
|
||||
// readBuffer.clear();
|
||||
// channel.read(readBuffer, null, this);
|
||||
// }
|
||||
// try {
|
||||
// webSocket.onPong(message);
|
||||
// } catch (Exception e) {
|
||||
// context.getLogger().log(Level.SEVERE, "WebSocket onPong error (" + packet + ")", e);
|
||||
// }
|
||||
// } else if (packet.type == FrameType.PING) {
|
||||
// byte[] message = packet.getReceiveBytes();
|
||||
// if (readBuffer != null) {
|
||||
// readBuffer.clear();
|
||||
// channel.read(readBuffer, null, this);
|
||||
// }
|
||||
// try {
|
||||
// webSocket.onPing(message);
|
||||
// } catch (Exception e) {
|
||||
// context.getLogger().log(Level.SEVERE, "WebSocket onPing error (" + packet + ")", e);
|
||||
// }
|
||||
// } else if (packet.type == FrameType.CLOSE) {
|
||||
// Logger logger = context.getLogger();
|
||||
// if (logger.isLoggable(Level.FINEST)) logger.log(Level.FINEST, "WebSocketRunner onMessage by CLOSE FrameType : " + packet);
|
||||
// closeRunner(0);
|
||||
// } else {
|
||||
// context.getLogger().log(Level.WARNING, "WebSocketRunner onMessage by unknown FrameType : " + packet);
|
||||
// if (readBuffer != null) {
|
||||
// readBuffer.clear();
|
||||
// channel.read(readBuffer, null, this);
|
||||
// }
|
||||
// }
|
||||
// } catch (Throwable t) {
|
||||
// closeRunner(0);
|
||||
// if (debug) context.getLogger().log(Level.FINEST, "WebSocketRunner abort on read WebSocketPacket, force to close channel, live " + (System.currentTimeMillis() - webSocket.getCreatetime()) / 1000 + " seconds", t);
|
||||
// } finally {
|
||||
// if (exBuffers != null) {
|
||||
// for (ByteBuffer b : exBuffers) {
|
||||
// context.offerBuffer(b);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -339,6 +433,10 @@ class WebSocketRunner implements Runnable {
|
||||
return futureResult;
|
||||
}
|
||||
|
||||
public boolean isClosed() {
|
||||
return closed;
|
||||
}
|
||||
|
||||
public void closeRunner(int code) {
|
||||
if (closed) return;
|
||||
synchronized (this) {
|
||||
|
||||
@@ -9,6 +9,7 @@ import java.io.*;
|
||||
import java.lang.reflect.*;
|
||||
import java.net.*;
|
||||
import java.nio.*;
|
||||
import java.nio.channels.CompletionHandler;
|
||||
import java.security.*;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
@@ -45,6 +46,12 @@ public abstract class WebSocketServlet extends HttpServlet implements Resourcabl
|
||||
@Comment("WebScoket服务器给客户端进行ping操作的间隔时间, 单位: 秒")
|
||||
public static final String WEBPARAM__LIVEINTERVAL = "liveinterval";
|
||||
|
||||
@Comment("WebScoket服务器最大连接数,为0表示无限制")
|
||||
public static final String WEBPARAM__WSMAXCONNS = "wsmaxconns";
|
||||
|
||||
@Comment("最大消息体长度, 小于1表示无限制")
|
||||
public static final String WEBPARAM__WSMAXBODY = "wsmaxbody";
|
||||
|
||||
@Comment("WebScoket服务器给客户端进行ping操作的默认间隔时间, 单位: 秒")
|
||||
public static final int DEFAILT_LIVEINTERVAL = 15;
|
||||
|
||||
@@ -56,10 +63,21 @@ public abstract class WebSocketServlet extends HttpServlet implements Resourcabl
|
||||
|
||||
protected Type messageTextType; //RestWebSocket时会被修改
|
||||
|
||||
//同RestWebSocket.single
|
||||
protected boolean single = true; //是否单用户单连接
|
||||
|
||||
//同RestWebSocket.liveinterval
|
||||
protected int liveinterval = DEFAILT_LIVEINTERVAL;
|
||||
|
||||
//同RestWebSocket.wsmaxconns
|
||||
protected int wsmaxconns = 0;
|
||||
|
||||
//同RestWebSocket.wsmaxbody
|
||||
protected int wsmaxbody = 16 * 1024;
|
||||
|
||||
//同RestWebSocket.anyuser
|
||||
protected boolean anyuser = false;
|
||||
|
||||
@Resource(name = "jsonconvert")
|
||||
protected Convert jsonConvert;
|
||||
|
||||
@@ -105,8 +123,10 @@ public abstract class WebSocketServlet extends HttpServlet implements Resourcabl
|
||||
this.node = new WebSocketNodeService();
|
||||
if (logger.isLoggable(Level.WARNING)) logger.warning("Not found WebSocketNode, create a default value for " + getClass().getName());
|
||||
}
|
||||
|
||||
//存在WebSocketServlet,则此WebSocketNode必须是本地模式Service
|
||||
this.node.localEngine = new WebSocketEngine("WebSocketEngine-" + addr.getHostString() + ":" + addr.getPort() + "-[" + resourceName() + "]", this.single, context, liveinterval, this.node, this.sendConvert, logger);
|
||||
this.node.localEngine = new WebSocketEngine("WebSocketEngine-" + addr.getHostString() + ":" + addr.getPort() + "-[" + resourceName() + "]",
|
||||
this.single, context, liveinterval, wsmaxconns, wsmaxbody, this.node, this.sendConvert, logger);
|
||||
this.node.init(conf);
|
||||
this.node.localEngine.init(conf);
|
||||
}
|
||||
@@ -137,6 +157,11 @@ public abstract class WebSocketServlet extends HttpServlet implements Resourcabl
|
||||
response.finish(true);
|
||||
return;
|
||||
}
|
||||
if (this.node.localEngine.isLocalConnLimited()) {
|
||||
if (debug) logger.finest("WebSocket connections limit, wsmaxconns=" + this.node.localEngine.getLocalWsmaxconns());
|
||||
response.finish(true);
|
||||
return;
|
||||
}
|
||||
final WebSocket webSocket = this.createWebSocket();
|
||||
webSocket._engine = this.node.localEngine;
|
||||
webSocket._messageTextType = this.messageTextType;
|
||||
@@ -153,7 +178,7 @@ public abstract class WebSocketServlet extends HttpServlet implements Resourcabl
|
||||
return;
|
||||
}
|
||||
sessionFuture.whenComplete((sessionid, ex) -> {
|
||||
if (sessionid == null || ex != null) {
|
||||
if ((sessionid == null && webSocket.delayPackets == null) || ex != null) {
|
||||
if (debug || ex != null) logger.log(ex == null ? Level.FINEST : Level.FINE, "WebSocket connect abort, Not found sessionid or occur error. request=" + request, ex);
|
||||
response.finish(true);
|
||||
return;
|
||||
@@ -168,41 +193,95 @@ public abstract class WebSocketServlet extends HttpServlet implements Resourcabl
|
||||
response.setHeader("Connection", "Upgrade");
|
||||
response.addHeader("Upgrade", "websocket");
|
||||
response.addHeader("Sec-WebSocket-Accept", Base64.getEncoder().encodeToString(bytes));
|
||||
response.sendBody((ByteBuffer) null, null, new AsyncHandler<Integer, Void>() {
|
||||
response.sendBody((ByteBuffer) null, null, new CompletionHandler<Integer, Void>() {
|
||||
|
||||
WebSocketRunner temprunner = null;
|
||||
|
||||
@Override
|
||||
public void completed(Integer result, Void attachment) {
|
||||
HttpContext context = response.getContext();
|
||||
CompletableFuture<Serializable> userFuture = webSocket.createUserid();
|
||||
if (userFuture == null) {
|
||||
if (debug) logger.finest("WebSocket connect abort, Create userid abort. request = " + request);
|
||||
response.finish(true);
|
||||
return;
|
||||
}
|
||||
userFuture.whenComplete((userid, ex2) -> {
|
||||
if (userid == null || ex2 != null) {
|
||||
if (debug || ex2 != null) logger.log(ex2 == null ? Level.FINEST : Level.FINE, "WebSocket connect abort, Create userid abort. request = " + request, ex2);
|
||||
|
||||
Runnable createUseridHandler = () -> {
|
||||
CompletableFuture<Serializable> userFuture = webSocket.createUserid();
|
||||
if (userFuture == null) {
|
||||
if (debug) logger.finest("WebSocket connect abort, Create userid abort. request = " + request);
|
||||
response.finish(true);
|
||||
return;
|
||||
}
|
||||
webSocket._userid = userid;
|
||||
if (single) {
|
||||
WebSocketServlet.this.node.existsWebSocket(userid).whenComplete((rs, ex) -> {
|
||||
if (rs) webSocket.onSingleRepeatConnect();
|
||||
WebSocketServlet.this.node.localEngine.add(webSocket);
|
||||
WebSocketRunner runner = new WebSocketRunner(context, webSocket, restMessageConsumer, response.removeChannel());
|
||||
webSocket._runner = runner;
|
||||
context.runAsync(runner);
|
||||
userFuture.whenComplete((userid, ex2) -> {
|
||||
if ((userid == null && webSocket.delayPackets == null) || ex2 != null) {
|
||||
if (debug || ex2 != null) logger.log(ex2 == null ? Level.FINEST : Level.FINE, "WebSocket connect abort, Create userid abort. request = " + request, ex2);
|
||||
response.finish(true);
|
||||
});
|
||||
} else {
|
||||
WebSocketServlet.this.node.localEngine.add(webSocket);
|
||||
WebSocketRunner runner = new WebSocketRunner(context, webSocket, restMessageConsumer, response.removeChannel());
|
||||
webSocket._runner = runner;
|
||||
context.runAsync(runner);
|
||||
response.finish(true);
|
||||
return;
|
||||
}
|
||||
Runnable runHandler = () -> {
|
||||
temprunner = null;
|
||||
webSocket._userid = userid;
|
||||
if (single && !anyuser) {
|
||||
WebSocketServlet.this.node.existsWebSocket(userid).whenComplete((rs, ex) -> {
|
||||
if (rs) webSocket.onSingleRepeatConnect();
|
||||
WebSocketServlet.this.node.localEngine.add(webSocket);
|
||||
WebSocketRunner runner = new WebSocketRunner(context, webSocket, restMessageConsumer, response.removeChannel());
|
||||
webSocket._runner = runner;
|
||||
context.runAsync(runner);
|
||||
response.finish(true);
|
||||
});
|
||||
} else {
|
||||
WebSocketServlet.this.node.localEngine.add(webSocket);
|
||||
WebSocketRunner runner = new WebSocketRunner(context, webSocket, restMessageConsumer, response.removeChannel());
|
||||
webSocket._runner = runner;
|
||||
context.runAsync(runner);
|
||||
response.finish(true);
|
||||
}
|
||||
};
|
||||
if (webSocket.delayPackets != null) { //存在待发送的消息
|
||||
if (temprunner == null) temprunner = new WebSocketRunner(context, webSocket, restMessageConsumer, response.getChannel());
|
||||
List<WebSocketPacket> delayPackets = webSocket.delayPackets;
|
||||
webSocket.delayPackets = null;
|
||||
CompletableFuture<Integer> cf = null;
|
||||
for (WebSocketPacket packet : delayPackets) {
|
||||
if (cf == null) {
|
||||
cf = temprunner.sendMessage(packet);
|
||||
} else {
|
||||
cf = cf.thenCombine(temprunner.sendMessage(packet), (a, b) -> a | b);
|
||||
}
|
||||
}
|
||||
cf.whenComplete((Integer v, Throwable t) -> {
|
||||
if (userid == null || t != null || (temprunner != null && temprunner.isClosed())) {
|
||||
if (t != null) logger.log(Level.FINEST, "WebSocket connect abort, Response send delayPackets abort. request = " + request, t);
|
||||
response.finish(true);
|
||||
} else {
|
||||
runHandler.run();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
runHandler.run();
|
||||
}
|
||||
});
|
||||
};
|
||||
if (webSocket.delayPackets != null) { //存在待发送的消息
|
||||
if (temprunner == null) temprunner = new WebSocketRunner(context, webSocket, restMessageConsumer, response.getChannel());
|
||||
List<WebSocketPacket> delayPackets = webSocket.delayPackets;
|
||||
webSocket.delayPackets = null;
|
||||
CompletableFuture<Integer> cf = null;
|
||||
for (WebSocketPacket packet : delayPackets) {
|
||||
if (cf == null) {
|
||||
cf = temprunner.sendMessage(packet);
|
||||
} else {
|
||||
cf = cf.thenCombine(temprunner.sendMessage(packet), (a, b) -> a | b);
|
||||
}
|
||||
}
|
||||
});
|
||||
cf.whenComplete((Integer v, Throwable t) -> {
|
||||
if (sessionid == null || t != null || (temprunner != null && temprunner.isClosed())) {
|
||||
if (t != null) logger.log(Level.FINEST, "WebSocket connect abort, Response send delayPackets abort. request = " + request, t);
|
||||
response.finish(true);
|
||||
} else {
|
||||
createUseridHandler.run();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
createUseridHandler.run();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -8,6 +8,8 @@ package org.redkale.net.sncp;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.*;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.CompletionHandler;
|
||||
import java.security.*;
|
||||
import java.util.*;
|
||||
import javax.annotation.Resource;
|
||||
@@ -31,6 +33,10 @@ import org.redkale.util.*;
|
||||
*/
|
||||
public abstract class Sncp {
|
||||
|
||||
public static final ByteBuffer PING_BUFFER = ByteBuffer.wrap("PING".getBytes()).asReadOnlyBuffer();
|
||||
|
||||
public static final ByteBuffer PONG_BUFFER = ByteBuffer.wrap("PONG".getBytes()).asReadOnlyBuffer();
|
||||
|
||||
static final String FIELDPREFIX = "_redkale";
|
||||
|
||||
static final String LOCALPREFIX = "_DynLocal";
|
||||
@@ -133,12 +139,12 @@ public abstract class Sncp {
|
||||
}
|
||||
|
||||
static void checkAsyncModifier(Class param, Method method) {
|
||||
if (param == AsyncHandler.class) return;
|
||||
if (param == CompletionHandler.class) return;
|
||||
if (Modifier.isFinal(param.getModifiers())) {
|
||||
throw new RuntimeException("AsyncHandler Type Parameter on {" + method + "} cannot final modifier");
|
||||
throw new RuntimeException("CompletionHandler Type Parameter on {" + method + "} cannot final modifier");
|
||||
}
|
||||
if (!Modifier.isPublic(param.getModifiers())) {
|
||||
throw new RuntimeException("AsyncHandler Type Parameter on {" + method + "} must be public modifier");
|
||||
throw new RuntimeException("CompletionHandler Type Parameter on {" + method + "} must be public modifier");
|
||||
}
|
||||
if (param.isInterface()) return;
|
||||
boolean constructorflag = false;
|
||||
@@ -383,8 +389,8 @@ public abstract class Sncp {
|
||||
int varindex = 0;
|
||||
boolean handlerFuncFlag = false;
|
||||
for (Class pt : paramtypes) {
|
||||
if (AsyncHandler.class.isAssignableFrom(pt)) {
|
||||
if (handlerFuncFlag) throw new RuntimeException(method + " have more than one AsyncHandler type parameter");
|
||||
if (CompletionHandler.class.isAssignableFrom(pt)) {
|
||||
if (handlerFuncFlag) throw new RuntimeException(method + " have more than one CompletionHandler type parameter");
|
||||
checkAsyncModifier(pt, method);
|
||||
handlerFuncFlag = true;
|
||||
}
|
||||
@@ -479,12 +485,12 @@ public abstract class Sncp {
|
||||
} else if (returnType.isPrimitive()) {
|
||||
if (returnType == long.class) {
|
||||
mv.visitVarInsn(LSTORE, ++varindex);
|
||||
++varindex; //多加1
|
||||
//++varindex; //多加1
|
||||
} else if (returnType == float.class) {
|
||||
mv.visitVarInsn(FSTORE, ++varindex);
|
||||
} else if (returnType == double.class) {
|
||||
mv.visitVarInsn(DSTORE, ++varindex);
|
||||
++varindex; //多加1
|
||||
//++varindex; //多加1
|
||||
} else {
|
||||
mv.visitVarInsn(ISTORE, ++varindex);
|
||||
}
|
||||
@@ -528,16 +534,8 @@ public abstract class Sncp {
|
||||
mv.visitFieldInsn(GETFIELD, newDynName, FIELDPREFIX + "_client", clientDesc);
|
||||
final int preparams = 3; //调用selfrunnable之前的参数个数; _client
|
||||
|
||||
if (index <= 5) { //第几个 SncpAction
|
||||
mv.visitInsn(ICONST_0 + index);
|
||||
} else {
|
||||
mv.visitIntInsn(BIPUSH, index);
|
||||
}
|
||||
if (paramtypes.length + preparams <= 5) { //参数总数量
|
||||
mv.visitInsn(ICONST_0 + paramtypes.length + preparams);
|
||||
} else {
|
||||
mv.visitIntInsn(BIPUSH, paramtypes.length + preparams);
|
||||
}
|
||||
pushInt(mv, index); //第几个 SncpAction
|
||||
pushInt(mv, paramtypes.length + preparams); //参数总数量
|
||||
|
||||
mv.visitTypeInsn(ANEWARRAY, "java/lang/Object");
|
||||
|
||||
@@ -564,11 +562,7 @@ public abstract class Sncp {
|
||||
final Class pt = paramtypes[j];
|
||||
mv.visitInsn(DUP);
|
||||
insn++;
|
||||
if (j <= 2) {
|
||||
mv.visitInsn(ICONST_0 + j + 3);
|
||||
} else {
|
||||
mv.visitIntInsn(BIPUSH, j + 3);
|
||||
}
|
||||
pushInt(mv, j + 3);
|
||||
if (pt.isPrimitive()) {
|
||||
if (pt == long.class) {
|
||||
mv.visitVarInsn(LLOAD, insn++);
|
||||
@@ -596,16 +590,8 @@ public abstract class Sncp {
|
||||
mv.visitVarInsn(ALOAD, 0);
|
||||
mv.visitFieldInsn(GETFIELD, newDynName, FIELDPREFIX + "_client", clientDesc);
|
||||
|
||||
if (index <= 5) { //第几个 SncpAction
|
||||
mv.visitInsn(ICONST_0 + index);
|
||||
} else {
|
||||
mv.visitIntInsn(BIPUSH, index);
|
||||
}
|
||||
if (paramtypes.length + preparams <= 5) { //参数总数量
|
||||
mv.visitInsn(ICONST_0 + paramtypes.length + preparams);
|
||||
} else {
|
||||
mv.visitIntInsn(BIPUSH, paramtypes.length + preparams);
|
||||
}
|
||||
pushInt(mv, index); //第几个 SncpAction
|
||||
pushInt(mv, paramtypes.length + preparams); //参数总数量
|
||||
|
||||
mv.visitTypeInsn(ANEWARRAY, "java/lang/Object");
|
||||
|
||||
@@ -632,11 +618,7 @@ public abstract class Sncp {
|
||||
final Class pt = paramtypes[j];
|
||||
mv.visitInsn(DUP);
|
||||
insn++;
|
||||
if (j <= 2) {
|
||||
mv.visitInsn(ICONST_0 + j + 3);
|
||||
} else {
|
||||
mv.visitIntInsn(BIPUSH, j + 3);
|
||||
}
|
||||
pushInt(mv, j + 3);
|
||||
if (pt.isPrimitive()) {
|
||||
if (pt == long.class) {
|
||||
mv.visitVarInsn(LLOAD, insn++);
|
||||
@@ -874,7 +856,7 @@ public abstract class Sncp {
|
||||
* @param serviceTypeOrImplClass Service类
|
||||
* @param transportFactory TransportFactory
|
||||
* @param clientAddress 本地IP地址
|
||||
* @param groups 所有的组节点,包含自身
|
||||
* @param groups0 所有的组节点,包含自身
|
||||
* @param conf 启动配置项
|
||||
*
|
||||
* @return Service的远程模式实例
|
||||
@@ -887,10 +869,11 @@ public abstract class Sncp {
|
||||
final Class<T> serviceTypeOrImplClass,
|
||||
final TransportFactory transportFactory,
|
||||
final InetSocketAddress clientAddress,
|
||||
final Set<String> groups,
|
||||
final Set<String> groups0,
|
||||
final AnyValue conf) {
|
||||
if (serviceTypeOrImplClass == null) return null;
|
||||
if (!Service.class.isAssignableFrom(serviceTypeOrImplClass)) return null;
|
||||
Set<String> groups = groups0 == null ? new HashSet<>() : groups0;
|
||||
ResourceFactory.checkResourceName(name);
|
||||
int mod = serviceTypeOrImplClass.getModifiers();
|
||||
boolean realed = !(java.lang.reflect.Modifier.isAbstract(mod) || serviceTypeOrImplClass.isInterface());
|
||||
@@ -1011,19 +994,11 @@ public abstract class Sncp {
|
||||
mv.visitVarInsn(ALOAD, 0);
|
||||
mv.visitFieldInsn(GETFIELD, newDynName, FIELDPREFIX + "_client", clientDesc);
|
||||
|
||||
if (index <= 5) {
|
||||
mv.visitInsn(ICONST_0 + index);
|
||||
} else {
|
||||
mv.visitIntInsn(BIPUSH, index);
|
||||
}
|
||||
pushInt(mv, index);
|
||||
|
||||
{ //传参数
|
||||
int paramlen = entry.paramTypes.length;
|
||||
if (paramlen <= 5) {
|
||||
mv.visitInsn(ICONST_0 + paramlen);
|
||||
} else {
|
||||
mv.visitIntInsn(BIPUSH, paramlen);
|
||||
}
|
||||
pushInt(mv, paramlen);
|
||||
mv.visitTypeInsn(ANEWARRAY, "java/lang/Object");
|
||||
java.lang.reflect.Type[] paramtypes = entry.paramTypes;
|
||||
int insn = 0;
|
||||
@@ -1031,11 +1006,7 @@ public abstract class Sncp {
|
||||
final java.lang.reflect.Type pt = paramtypes[j];
|
||||
mv.visitInsn(DUP);
|
||||
insn++;
|
||||
if (j <= 5) {
|
||||
mv.visitInsn(ICONST_0 + j);
|
||||
} else {
|
||||
mv.visitIntInsn(BIPUSH, j);
|
||||
}
|
||||
pushInt(mv, j);
|
||||
if (pt instanceof Class && ((Class) pt).isPrimitive()) {
|
||||
if (pt == long.class) {
|
||||
mv.visitVarInsn(LLOAD, insn++);
|
||||
@@ -1118,4 +1089,28 @@ public abstract class Sncp {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static void pushInt(AsmMethodVisitor mv, int num) {
|
||||
if (num < 6) {
|
||||
mv.visitInsn(ICONST_0 + num);
|
||||
} else if (num <= Byte.MAX_VALUE) {
|
||||
mv.visitIntInsn(BIPUSH, num);
|
||||
} else if (num <= Short.MAX_VALUE) {
|
||||
mv.visitIntInsn(SIPUSH, num);
|
||||
} else {
|
||||
mv.visitLdcInsn(num);
|
||||
}
|
||||
}
|
||||
|
||||
private static void pushInt(MethodVisitor mv, int num) {
|
||||
if (num < 6) {
|
||||
mv.visitInsn(ICONST_0 + num);
|
||||
} else if (num <= Byte.MAX_VALUE) {
|
||||
mv.visitIntInsn(BIPUSH, num);
|
||||
} else if (num <= Short.MAX_VALUE) {
|
||||
mv.visitIntInsn(SIPUSH, num);
|
||||
} else {
|
||||
mv.visitLdcInsn(num);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
*/
|
||||
package org.redkale.net.sncp;
|
||||
|
||||
import java.nio.channels.CompletionHandler;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.logging.Level;
|
||||
import jdk.internal.org.objectweb.asm.*;
|
||||
@@ -26,7 +27,7 @@ import org.redkale.util.*;
|
||||
* @param <V> 结果对象的泛型
|
||||
* @param <A> 附件对象的泛型
|
||||
*/
|
||||
public interface SncpAsyncHandler<V, A> extends AsyncHandler<V, A> {
|
||||
public interface SncpAsyncHandler<V, A> extends CompletionHandler<V, A> {
|
||||
|
||||
public Object[] sncp_getParams();
|
||||
|
||||
@@ -42,9 +43,9 @@ public interface SncpAsyncHandler<V, A> extends AsyncHandler<V, A> {
|
||||
* <blockquote><pre>
|
||||
*
|
||||
* 考虑点:
|
||||
* 1、AsyncHandler子类是接口,且还有其他多个方法
|
||||
* 2、AsyncHandler子类是类, 需要继承,且必须有空参数构造函数
|
||||
* 3、AsyncHandler子类无论是接口还是类,都可能存在其他泛型
|
||||
* 1、CompletionHandler子类是接口,且还有其他多个方法
|
||||
* 2、CompletionHandler子类是类, 需要继承,且必须有空参数构造函数
|
||||
* 3、CompletionHandler子类无论是接口还是类,都可能存在其他泛型
|
||||
*
|
||||
* public class XXXAsyncHandler_DyncSncpAsyncHandler_4323 extends XXXAsyncHandler implements SncpAsyncHandler {
|
||||
*
|
||||
@@ -91,11 +92,11 @@ public interface SncpAsyncHandler<V, A> extends AsyncHandler<V, A> {
|
||||
*
|
||||
* </pre></blockquote>
|
||||
*
|
||||
* @param handlerClass AsyncHandler类型或子类
|
||||
* @param handlerClass CompletionHandler类型或子类
|
||||
*
|
||||
* @return Creator
|
||||
*/
|
||||
public static Creator<SncpAsyncHandler> createCreator(Class<? extends AsyncHandler> handlerClass) {
|
||||
public static Creator<SncpAsyncHandler> createCreator(Class<? extends CompletionHandler> handlerClass) {
|
||||
//-------------------------------------------------------------
|
||||
final boolean handlerinterface = handlerClass.isInterface();
|
||||
final String handlerClassName = handlerClass.getName().replace('.', '/');
|
||||
|
||||
@@ -12,6 +12,7 @@ import java.nio.*;
|
||||
import java.nio.channels.*;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.logging.*;
|
||||
import javax.annotation.Resource;
|
||||
import org.redkale.convert.bson.*;
|
||||
@@ -33,8 +34,6 @@ public final class SncpClient {
|
||||
|
||||
protected static final Logger logger = Logger.getLogger(SncpClient.class.getSimpleName());
|
||||
|
||||
protected final boolean finest = logger.isLoggable(Level.FINEST);
|
||||
|
||||
protected final JsonConvert convert = JsonFactory.root().getConvert();
|
||||
|
||||
protected final String name;
|
||||
@@ -57,6 +56,8 @@ public final class SncpClient {
|
||||
|
||||
protected final ExecutorService executor;
|
||||
|
||||
protected final Supplier<ByteBuffer> bufferSupplier;
|
||||
|
||||
@Resource
|
||||
protected JsonConvert jsonConvert;
|
||||
|
||||
@@ -85,6 +86,7 @@ public final class SncpClient {
|
||||
final boolean remote, final Class serviceClass, final InetSocketAddress clientAddress) {
|
||||
this.remote = remote;
|
||||
this.executor = factory.getExecutor();
|
||||
this.bufferSupplier = factory.getBufferSupplier();
|
||||
this.serviceClass = serviceClass;
|
||||
this.serviceversion = 0;
|
||||
this.clientAddress = clientAddress;
|
||||
@@ -101,6 +103,7 @@ public final class SncpClient {
|
||||
this.actions = methodens.toArray(new SncpAction[methodens.size()]);
|
||||
this.addrBytes = clientAddress == null ? new byte[4] : clientAddress.getAddress().getAddress();
|
||||
this.addrPort = clientAddress == null ? 0 : clientAddress.getPort();
|
||||
if (this.addrBytes.length != 4) throw new RuntimeException("SNCP clientAddress only support IPv4");
|
||||
}
|
||||
|
||||
static List<SncpAction> getSncpActions(final Class serviceClass) {
|
||||
@@ -290,7 +293,7 @@ public final class SncpClient {
|
||||
//只给远程模式调用的
|
||||
public <T> T remote(final int index, final Object... params) {
|
||||
final SncpAction action = actions[index];
|
||||
final AsyncHandler handlerFunc = action.handlerFuncParamIndex >= 0 ? (AsyncHandler) params[action.handlerFuncParamIndex] : null;
|
||||
final CompletionHandler handlerFunc = action.handlerFuncParamIndex >= 0 ? (CompletionHandler) params[action.handlerFuncParamIndex] : null;
|
||||
if (action.handlerFuncParamIndex >= 0) params[action.handlerFuncParamIndex] = null;
|
||||
final BsonReader reader = bsonConvert.pollBsonReader();
|
||||
CompletableFuture<byte[]> future = remote0(handlerFunc, remoteGroupTransport, null, action, params);
|
||||
@@ -336,159 +339,156 @@ public final class SncpClient {
|
||||
}
|
||||
}
|
||||
|
||||
private CompletableFuture<byte[]> remote0(final AsyncHandler handler, final Transport transport, final SocketAddress addr0, final SncpAction action, final Object... params) {
|
||||
private CompletableFuture<byte[]> remote0(final CompletionHandler handler, final Transport transport, final SocketAddress addr0, final SncpAction action, final Object... params) {
|
||||
final Type[] myparamtypes = action.paramTypes;
|
||||
final Class[] myparamclass = action.paramClass;
|
||||
if (action.addressSourceParamIndex >= 0) params[action.addressSourceParamIndex] = this.clientAddress;
|
||||
if (bsonConvert == null) bsonConvert = BsonConvert.root();
|
||||
final BsonWriter writer = bsonConvert.pollBsonWriter(transport.getBufferSupplier()); // 将head写入
|
||||
writer.writeTo(DEFAULT_HEADER);
|
||||
for (int i = 0; i < params.length; i++) { //params 可能包含: 3 个 boolean
|
||||
bsonConvert.convertTo(writer, AsyncHandler.class.isAssignableFrom(myparamclass[i]) ? AsyncHandler.class : myparamtypes[i], params[i]);
|
||||
bsonConvert.convertTo(writer, CompletionHandler.class.isAssignableFrom(myparamclass[i]) ? CompletionHandler.class : myparamtypes[i], params[i]);
|
||||
}
|
||||
final int reqBodyLength = writer.count() - HEADER_SIZE; //body总长度
|
||||
final long seqid = System.nanoTime();
|
||||
final DLong actionid = action.actionid;
|
||||
final SocketAddress addr = addr0 == null ? (action.addressTargetParamIndex >= 0 ? (SocketAddress) params[action.addressTargetParamIndex] : null) : addr0;
|
||||
final CompletableFuture<byte[]> future = new CompletableFuture();
|
||||
AsyncConnection conn0;
|
||||
try {
|
||||
conn0 = transport.pollConnection(addr);
|
||||
} catch (Exception e) {
|
||||
future.completeExceptionally(e);
|
||||
return future;
|
||||
}
|
||||
if (conn0 == null || !conn0.isOpen()) {
|
||||
future.completeExceptionally(new RuntimeException("sncp " + (conn0 == null ? addr : conn0.getRemoteAddress()) + " cannot connect"));
|
||||
return future;
|
||||
}
|
||||
final AsyncConnection conn = conn0;
|
||||
final ByteBuffer[] sendBuffers = writer.toBuffers();
|
||||
fillHeader(sendBuffers[0], seqid, actionid, reqBodyLength);
|
||||
CompletableFuture<AsyncConnection> connFuture = transport.pollConnection(addr);
|
||||
return connFuture.thenCompose(conn0 -> {
|
||||
final CompletableFuture<byte[]> future = new CompletableFuture();
|
||||
if (conn0 == null || !conn0.isOpen()) {
|
||||
future.completeExceptionally(new RuntimeException("sncp " + (conn0 == null ? addr : conn0.getRemoteAddress()) + " cannot connect"));
|
||||
return future;
|
||||
}
|
||||
final AsyncConnection conn = conn0;
|
||||
final ByteBuffer[] sendBuffers = writer.toBuffers();
|
||||
fillHeader(sendBuffers[0], seqid, actionid, reqBodyLength);
|
||||
|
||||
final ByteBuffer buffer = transport.pollBuffer();
|
||||
conn.write(sendBuffers, sendBuffers, new CompletionHandler<Integer, ByteBuffer[]>() {
|
||||
final ByteBuffer buffer = transport.pollBuffer();
|
||||
conn.write(sendBuffers, sendBuffers, new CompletionHandler<Integer, ByteBuffer[]>() {
|
||||
|
||||
@Override
|
||||
public void completed(Integer result, ByteBuffer[] attachments) {
|
||||
int index = -1;
|
||||
for (int i = 0; i < attachments.length; i++) {
|
||||
if (attachments[i].hasRemaining()) {
|
||||
index = i;
|
||||
break;
|
||||
} else {
|
||||
transport.offerBuffer(attachments[i]);
|
||||
@Override
|
||||
public void completed(Integer result, ByteBuffer[] attachments) {
|
||||
int index = -1;
|
||||
for (int i = 0; i < attachments.length; i++) {
|
||||
if (attachments[i].hasRemaining()) {
|
||||
index = i;
|
||||
break;
|
||||
} else {
|
||||
transport.offerBuffer(attachments[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (index == 0) {
|
||||
conn.write(attachments, attachments, this);
|
||||
return;
|
||||
} else if (index > 0) {
|
||||
ByteBuffer[] newattachs = new ByteBuffer[attachments.length - index];
|
||||
System.arraycopy(attachments, index, newattachs, 0, newattachs.length);
|
||||
conn.write(newattachs, newattachs, this);
|
||||
return;
|
||||
}
|
||||
//----------------------- 读取返回结果 -------------------------------------
|
||||
buffer.clear();
|
||||
conn.read(buffer, null, new CompletionHandler<Integer, Void>() {
|
||||
if (index == 0) {
|
||||
conn.write(attachments, attachments, this);
|
||||
return;
|
||||
} else if (index > 0) {
|
||||
ByteBuffer[] newattachs = new ByteBuffer[attachments.length - index];
|
||||
System.arraycopy(attachments, index, newattachs, 0, newattachs.length);
|
||||
conn.write(newattachs, newattachs, this);
|
||||
return;
|
||||
}
|
||||
//----------------------- 读取返回结果 -------------------------------------
|
||||
buffer.clear();
|
||||
conn.read(buffer, null, new CompletionHandler<Integer, Void>() {
|
||||
|
||||
private byte[] body;
|
||||
private byte[] body;
|
||||
|
||||
private int received;
|
||||
private int received;
|
||||
|
||||
@Override
|
||||
public void completed(Integer count, Void attachment2) {
|
||||
if (count < 1 && buffer.remaining() == buffer.limit()) { //没有数据可读
|
||||
future.completeExceptionally(new RuntimeException(action.method + " sncp[" + conn.getRemoteAddress() + "] remote no response data"));
|
||||
transport.offerBuffer(buffer);
|
||||
transport.offerConnection(true, conn);
|
||||
return;
|
||||
}
|
||||
if (received < 1 && buffer.limit() < buffer.remaining() + HEADER_SIZE) { //header都没读全
|
||||
conn.read(buffer, attachment2, this);
|
||||
return;
|
||||
}
|
||||
buffer.flip();
|
||||
if (received > 0) {
|
||||
int offset = this.received;
|
||||
this.received += buffer.remaining();
|
||||
buffer.get(body, offset, Math.min(buffer.remaining(), this.body.length - offset));
|
||||
if (this.received < this.body.length) {// 数据仍然不全,需要继续读取
|
||||
@Override
|
||||
public void completed(Integer count, Void attachment2) {
|
||||
if (count < 1 && buffer.remaining() == buffer.limit()) { //没有数据可读
|
||||
future.completeExceptionally(new RuntimeException(action.method + " sncp[" + conn.getRemoteAddress() + "] remote no response data"));
|
||||
transport.offerBuffer(buffer);
|
||||
transport.offerConnection(true, conn);
|
||||
return;
|
||||
}
|
||||
if (received < 1 && buffer.limit() < buffer.remaining() + HEADER_SIZE) { //header都没读全
|
||||
conn.read(buffer, attachment2, this);
|
||||
return;
|
||||
}
|
||||
buffer.flip();
|
||||
if (received > 0) {
|
||||
int offset = this.received;
|
||||
this.received += buffer.remaining();
|
||||
buffer.get(body, offset, Math.min(buffer.remaining(), this.body.length - offset));
|
||||
if (this.received < this.body.length) {// 数据仍然不全,需要继续读取
|
||||
buffer.clear();
|
||||
conn.read(buffer, attachment2, this);
|
||||
} else {
|
||||
success();
|
||||
}
|
||||
return;
|
||||
}
|
||||
checkResult(seqid, action, buffer);
|
||||
|
||||
final int respBodyLength = buffer.getInt();
|
||||
final int retcode = buffer.getInt();
|
||||
if (retcode != 0) {
|
||||
logger.log(Level.SEVERE, action.method + " sncp (params: " + convert.convertTo(params) + ") deal error (retcode=" + retcode + ", retinfo=" + SncpResponse.getRetCodeInfo(retcode) + ")");
|
||||
throw new RuntimeException("remote service(" + action.method + ") deal error (retcode=" + retcode + ", retinfo=" + SncpResponse.getRetCodeInfo(retcode) + ")");
|
||||
}
|
||||
|
||||
if (respBodyLength > buffer.remaining()) { // 数据不全,需要继续读取
|
||||
this.body = new byte[respBodyLength];
|
||||
this.received = buffer.remaining();
|
||||
buffer.get(body, 0, this.received);
|
||||
buffer.clear();
|
||||
conn.read(buffer, attachment2, this);
|
||||
} else {
|
||||
this.body = new byte[respBodyLength];
|
||||
buffer.get(body, 0, respBodyLength);
|
||||
success();
|
||||
}
|
||||
return;
|
||||
}
|
||||
checkResult(seqid, action, buffer);
|
||||
|
||||
final int respBodyLength = buffer.getInt();
|
||||
final int retcode = buffer.getInt();
|
||||
if (retcode != 0) {
|
||||
logger.log(Level.SEVERE, action.method + " sncp (params: " + convert.convertTo(params) + ") deal error (retcode=" + retcode + ", retinfo=" + SncpResponse.getRetCodeInfo(retcode) + ")");
|
||||
throw new RuntimeException("remote service(" + action.method + ") deal error (retcode=" + retcode + ", retinfo=" + SncpResponse.getRetCodeInfo(retcode) + ")");
|
||||
}
|
||||
|
||||
if (respBodyLength > buffer.remaining()) { // 数据不全,需要继续读取
|
||||
this.body = new byte[respBodyLength];
|
||||
this.received = buffer.remaining();
|
||||
buffer.get(body, 0, this.received);
|
||||
buffer.clear();
|
||||
conn.read(buffer, attachment2, this);
|
||||
} else {
|
||||
this.body = new byte[respBodyLength];
|
||||
buffer.get(body, 0, respBodyLength);
|
||||
success();
|
||||
}
|
||||
}
|
||||
|
||||
public void success() {
|
||||
future.complete(this.body);
|
||||
transport.offerBuffer(buffer);
|
||||
transport.offerConnection(false, conn);
|
||||
if (handler != null) {
|
||||
final Object handlerAttach = action.handlerAttachParamIndex >= 0 ? params[action.handlerAttachParamIndex] : null;
|
||||
final BsonReader reader = bsonConvert.pollBsonReader();
|
||||
try {
|
||||
reader.setBytes(this.body);
|
||||
int i;
|
||||
while ((i = (reader.readByte() & 0xff)) != 0) {
|
||||
final Attribute attr = action.paramAttrs[i];
|
||||
attr.set(params[i - 1], bsonConvert.convertFrom(attr.type(), reader));
|
||||
public void success() {
|
||||
future.complete(this.body);
|
||||
transport.offerBuffer(buffer);
|
||||
transport.offerConnection(false, conn);
|
||||
if (handler != null) {
|
||||
final Object handlerAttach = action.handlerAttachParamIndex >= 0 ? params[action.handlerAttachParamIndex] : null;
|
||||
final BsonReader reader = bsonConvert.pollBsonReader();
|
||||
try {
|
||||
reader.setBytes(this.body);
|
||||
int i;
|
||||
while ((i = (reader.readByte() & 0xff)) != 0) {
|
||||
final Attribute attr = action.paramAttrs[i];
|
||||
attr.set(params[i - 1], bsonConvert.convertFrom(attr.type(), reader));
|
||||
}
|
||||
Object rs = bsonConvert.convertFrom(action.handlerFuncParamIndex >= 0 ? Object.class : action.resultTypes, reader);
|
||||
handler.completed(rs, handlerAttach);
|
||||
} catch (Exception e) {
|
||||
handler.failed(e, handlerAttach);
|
||||
} finally {
|
||||
bsonConvert.offerBsonReader(reader);
|
||||
}
|
||||
Object rs = bsonConvert.convertFrom(action.handlerFuncParamIndex >= 0 ? Object.class : action.resultTypes, reader);
|
||||
handler.completed(rs, handlerAttach);
|
||||
} catch (Exception e) {
|
||||
handler.failed(e, handlerAttach);
|
||||
} finally {
|
||||
bsonConvert.offerBsonReader(reader);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void failed(Throwable exc, Void attachment2) {
|
||||
logger.log(Level.SEVERE, action.method + " sncp (params: " + convert.convertTo(params) + ") remote read exec failed", exc);
|
||||
future.completeExceptionally(new RuntimeException(action.method + " sncp remote exec failed"));
|
||||
transport.offerBuffer(buffer);
|
||||
transport.offerConnection(true, conn);
|
||||
if (handler != null) {
|
||||
final Object handlerAttach = action.handlerAttachParamIndex >= 0 ? params[action.handlerAttachParamIndex] : null;
|
||||
handler.failed(exc, handlerAttach);
|
||||
@Override
|
||||
public void failed(Throwable exc, Void attachment2) {
|
||||
logger.log(Level.SEVERE, action.method + " sncp (params: " + convert.convertTo(params) + ") remote read exec failed", exc);
|
||||
future.completeExceptionally(new RuntimeException(action.method + " sncp remote exec failed"));
|
||||
transport.offerBuffer(buffer);
|
||||
transport.offerConnection(true, conn);
|
||||
if (handler != null) {
|
||||
final Object handlerAttach = action.handlerAttachParamIndex >= 0 ? params[action.handlerAttachParamIndex] : null;
|
||||
handler.failed(exc, handlerAttach);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void failed(Throwable exc, ByteBuffer[] attachment) {
|
||||
logger.log(Level.SEVERE, action.method + " sncp (params: " + convert.convertTo(params) + ") remote write exec failed", exc);
|
||||
transport.offerBuffer(buffer);
|
||||
transport.offerConnection(true, conn);
|
||||
}
|
||||
@Override
|
||||
public void failed(Throwable exc, ByteBuffer[] attachment) {
|
||||
logger.log(Level.SEVERE, action.method + " sncp (params: " + convert.convertTo(params) + ") remote write exec failed", exc);
|
||||
transport.offerBuffer(buffer);
|
||||
transport.offerConnection(true, conn);
|
||||
}
|
||||
});
|
||||
return future;
|
||||
});
|
||||
return future;
|
||||
}
|
||||
|
||||
private void checkResult(long seqid, final SncpAction action, ByteBuffer buffer) {
|
||||
@@ -571,12 +571,12 @@ public final class SncpClient {
|
||||
if (anns.length > 0) {
|
||||
Class<?>[] params = method.getParameterTypes();
|
||||
for (int i = 0; i < params.length; i++) {
|
||||
if (AsyncHandler.class.isAssignableFrom(params[i])) {
|
||||
if (CompletionHandler.class.isAssignableFrom(params[i])) {
|
||||
if (boolReturnTypeFuture) {
|
||||
throw new RuntimeException(method + " have both AsyncHandler and CompletableFuture");
|
||||
throw new RuntimeException(method + " have both CompletionHandler and CompletableFuture");
|
||||
}
|
||||
if (handlerFuncIndex >= 0) {
|
||||
throw new RuntimeException(method + " have more than one AsyncHandler type parameter");
|
||||
throw new RuntimeException(method + " have more than one CompletionHandler type parameter");
|
||||
}
|
||||
Sncp.checkAsyncModifier(params[i], method);
|
||||
handlerFuncIndex = i;
|
||||
@@ -617,7 +617,7 @@ public final class SncpClient {
|
||||
this.handlerAttachParamIndex = handlerAttachIndex;
|
||||
this.paramAttrs = hasattr ? atts : null;
|
||||
if (this.handlerFuncParamIndex >= 0 && method.getReturnType() != void.class) {
|
||||
throw new RuntimeException(method + " have AsyncHandler type parameter but return type is not void");
|
||||
throw new RuntimeException(method + " have CompletionHandler type parameter but return type is not void");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ import java.io.*;
|
||||
import java.lang.annotation.*;
|
||||
import java.lang.reflect.*;
|
||||
import java.nio.*;
|
||||
import java.nio.channels.CompletionHandler;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.function.*;
|
||||
@@ -40,8 +41,6 @@ public final class SncpDynServlet extends SncpServlet {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(SncpDynServlet.class.getSimpleName());
|
||||
|
||||
private final boolean finest = logger.isLoggable(Level.FINEST);
|
||||
|
||||
private final DLong serviceid;
|
||||
|
||||
private final HashMap<DLong, SncpServletAction> actions = new HashMap<>();
|
||||
@@ -110,7 +109,7 @@ public final class SncpDynServlet extends SncpServlet {
|
||||
bufferSupplier = request.getContext().getBufferSupplier();
|
||||
}
|
||||
final SncpServletAction action = actions.get(request.getActionid());
|
||||
//if (finest) logger.log(Level.FINEST, "sncpdyn.execute: " + request + ", " + (action == null ? "null" : action.method));
|
||||
//logger.log(Level.FINEST, "sncpdyn.execute: " + request + ", " + (action == null ? "null" : action.method));
|
||||
if (action == null) {
|
||||
response.finish(SncpResponse.RETCODE_ILLACTIONID, null); //无效actionid
|
||||
} else {
|
||||
@@ -120,7 +119,7 @@ public final class SncpDynServlet extends SncpServlet {
|
||||
SncpAsyncHandler handler = null;
|
||||
try {
|
||||
if (action.handlerFuncParamIndex >= 0) {
|
||||
if (action.handlerFuncParamClass == AsyncHandler.class) {
|
||||
if (action.handlerFuncParamClass == CompletionHandler.class) {
|
||||
handler = new DefaultSncpAsyncHandler(action, in, out, request, response);
|
||||
} else {
|
||||
Creator<SncpAsyncHandler> creator = action.handlerCreator;
|
||||
@@ -180,15 +179,15 @@ public final class SncpDynServlet extends SncpServlet {
|
||||
|
||||
protected java.lang.reflect.Type[] paramTypes; //index=0表示返回参数的type, void的返回参数类型为null
|
||||
|
||||
protected int handlerFuncParamIndex = -1; //handlerFuncParamIndex>=0表示存在AsyncHandler参数
|
||||
protected int handlerFuncParamIndex = -1; //handlerFuncParamIndex>=0表示存在CompletionHandler参数
|
||||
|
||||
protected boolean boolReturnTypeFuture = false; // 返回结果类型是否为 CompletableFuture
|
||||
|
||||
protected Class handlerFuncParamClass; //AsyncHandler参数的类型
|
||||
protected Class handlerFuncParamClass; //CompletionHandler参数的类型
|
||||
|
||||
public abstract void action(final BsonReader in, final BsonWriter out, final SncpAsyncHandler handler) throws Throwable;
|
||||
|
||||
//只有同步方法才调用 (没有AsyncHandler、CompletableFuture)
|
||||
//只有同步方法才调用 (没有CompletionHandler、CompletableFuture)
|
||||
public final void _callParameter(final BsonWriter out, final Object... params) {
|
||||
if (paramAttrs != null) {
|
||||
for (int i = 1; i < paramAttrs.length; i++) {
|
||||
@@ -209,10 +208,10 @@ public final class SncpDynServlet extends SncpServlet {
|
||||
* return false;
|
||||
* }
|
||||
*
|
||||
* public void insert(AsyncHandler<Boolean, TestBean> handler, TestBean bean, String name, int id) {
|
||||
* public void insert(CompletionHandler<Boolean, TestBean> handler, TestBean bean, String name, int id) {
|
||||
* }
|
||||
*
|
||||
* public void update(long show, short v2, AsyncHandler<Boolean, TestBean> handler, TestBean bean, String name, int id) {
|
||||
* public void update(long show, short v2, CompletionHandler<Boolean, TestBean> handler, TestBean bean, String name, int id) {
|
||||
* }
|
||||
*
|
||||
* public CompletableFuture<String> changeName(TestBean bean, String name, int id) {
|
||||
@@ -243,7 +242,7 @@ public final class SncpDynServlet extends SncpServlet {
|
||||
* @Override
|
||||
* public void action(BsonReader in, BsonWriter out, SncpAsyncHandler handler) throws Throwable {
|
||||
* SncpAsyncHandler arg0 = handler;
|
||||
* convert.convertFrom(AsyncHandler.class, in);
|
||||
* convert.convertFrom(CompletionHandler.class, in);
|
||||
* TestBean arg1 = convert.convertFrom(paramTypes[2], in);
|
||||
* String arg2 = convert.convertFrom(paramTypes[3], in);
|
||||
* int arg3 = convert.convertFrom(paramTypes[4], in);
|
||||
@@ -261,7 +260,7 @@ public final class SncpDynServlet extends SncpServlet {
|
||||
* long a1 = convert.convertFrom(paramTypes[1], in);
|
||||
* short a2 = convert.convertFrom(paramTypes[2], in);
|
||||
* SncpAsyncHandler a3 = handler;
|
||||
* convert.convertFrom(AsyncHandler.class, in);
|
||||
* convert.convertFrom(CompletionHandler.class, in);
|
||||
* TestBean arg1 = convert.convertFrom(paramTypes[4], in);
|
||||
* String arg2 = convert.convertFrom(paramTypes[5], in);
|
||||
* int arg3 = convert.convertFrom(paramTypes[6], in);
|
||||
@@ -355,12 +354,12 @@ public final class SncpDynServlet extends SncpServlet {
|
||||
final Class[] paramClasses = method.getParameterTypes();
|
||||
int[][] codes = new int[paramClasses.length][2];
|
||||
for (int i = 0; i < paramClasses.length; i++) { //反序列化方法的每个参数
|
||||
if (AsyncHandler.class.isAssignableFrom(paramClasses[i])) {
|
||||
if (CompletionHandler.class.isAssignableFrom(paramClasses[i])) {
|
||||
if (boolReturnTypeFuture) {
|
||||
throw new RuntimeException(method + " have both AsyncHandler and CompletableFuture");
|
||||
throw new RuntimeException(method + " have both CompletionHandler and CompletableFuture");
|
||||
}
|
||||
if (handlerFuncIndex >= 0) {
|
||||
throw new RuntimeException(method + " have more than one AsyncHandler type parameter");
|
||||
throw new RuntimeException(method + " have more than one CompletionHandler type parameter");
|
||||
}
|
||||
Sncp.checkAsyncModifier(paramClasses[i], method);
|
||||
handlerFuncIndex = i;
|
||||
@@ -374,7 +373,7 @@ public final class SncpDynServlet extends SncpServlet {
|
||||
intconst++;
|
||||
mv.visitVarInsn(ALOAD, 0);
|
||||
mv.visitFieldInsn(GETFIELD, newDynName, "convert", Type.getDescriptor(BsonConvert.class));
|
||||
mv.visitLdcInsn(Type.getType(Type.getDescriptor(AsyncHandler.class)));
|
||||
mv.visitLdcInsn(Type.getType(Type.getDescriptor(CompletionHandler.class)));
|
||||
mv.visitVarInsn(ALOAD, 1);
|
||||
mv.visitMethodInsn(INVOKEVIRTUAL, convertName, "convertFrom", convertFromDesc, false);
|
||||
mv.visitInsn(POP);
|
||||
@@ -384,10 +383,15 @@ public final class SncpDynServlet extends SncpServlet {
|
||||
mv.visitFieldInsn(GETFIELD, newDynName, "convert", Type.getDescriptor(BsonConvert.class));
|
||||
mv.visitVarInsn(ALOAD, 0);
|
||||
mv.visitFieldInsn(GETFIELD, newDynName, "paramTypes", "[Ljava/lang/reflect/Type;");
|
||||
if (iconst > ICONST_5) {
|
||||
|
||||
if (intconst < 6) {
|
||||
mv.visitInsn(ICONST_0 + intconst);
|
||||
} else if (iconst <= Byte.MAX_VALUE) {
|
||||
mv.visitIntInsn(BIPUSH, intconst);
|
||||
} else if (iconst <= Short.MAX_VALUE) {
|
||||
mv.visitIntInsn(SIPUSH, intconst);
|
||||
} else {
|
||||
mv.visitInsn(iconst); //
|
||||
mv.visitLdcInsn(intconst);
|
||||
}
|
||||
mv.visitInsn(AALOAD);
|
||||
mv.visitVarInsn(ALOAD, 1);
|
||||
@@ -433,21 +437,30 @@ public final class SncpDynServlet extends SncpServlet {
|
||||
}
|
||||
if (boolReturnTypeFuture || handlerFuncIndex >= 0) { //调用SncpAsyncHandler.setParams(Object... params)
|
||||
mv.visitVarInsn(ALOAD, 3);
|
||||
if (paramClasses.length > 5) {
|
||||
if (paramClasses.length < 6) {
|
||||
mv.visitInsn(ICONST_0 + paramClasses.length);
|
||||
} else if (paramClasses.length <= Byte.MAX_VALUE) {
|
||||
mv.visitIntInsn(BIPUSH, paramClasses.length);
|
||||
} else if (paramClasses.length <= Short.MAX_VALUE) {
|
||||
mv.visitIntInsn(SIPUSH, paramClasses.length);
|
||||
} else {
|
||||
mv.visitInsn(paramClasses.length + ICONST_0);
|
||||
mv.visitLdcInsn(paramClasses.length);
|
||||
}
|
||||
|
||||
mv.visitTypeInsn(ANEWARRAY, "java/lang/Object");
|
||||
int insn = 3; //action的参数个数
|
||||
for (int j = 0; j < paramClasses.length; j++) {
|
||||
final Class pt = paramClasses[j];
|
||||
mv.visitInsn(DUP);
|
||||
insn++;
|
||||
if (j <= 5) {
|
||||
if (j < 6) {
|
||||
mv.visitInsn(ICONST_0 + j);
|
||||
} else {
|
||||
} else if (j <= Byte.MAX_VALUE) {
|
||||
mv.visitIntInsn(BIPUSH, j);
|
||||
} else if (j <= Short.MAX_VALUE) {
|
||||
mv.visitIntInsn(SIPUSH, j);
|
||||
} else {
|
||||
mv.visitLdcInsn(j);
|
||||
}
|
||||
if (pt.isPrimitive()) {
|
||||
if (pt == long.class) {
|
||||
@@ -499,10 +512,14 @@ public final class SncpDynServlet extends SncpServlet {
|
||||
//------------------------- _callParameter 方法 --------------------------------
|
||||
mv.visitVarInsn(ALOAD, 0);
|
||||
mv.visitVarInsn(ALOAD, 2);
|
||||
if (paramClasses.length <= 5) { //参数总数量
|
||||
if (paramClasses.length < 6) { //参数总数量
|
||||
mv.visitInsn(ICONST_0 + paramClasses.length);
|
||||
} else {
|
||||
} else if (paramClasses.length <= Byte.MAX_VALUE) {
|
||||
mv.visitIntInsn(BIPUSH, paramClasses.length);
|
||||
} else if (paramClasses.length <= Short.MAX_VALUE) {
|
||||
mv.visitIntInsn(SIPUSH, paramClasses.length);
|
||||
} else {
|
||||
mv.visitLdcInsn(paramClasses.length);
|
||||
}
|
||||
mv.visitTypeInsn(ANEWARRAY, "java/lang/Object");
|
||||
int insn = 3;//action的参数个数
|
||||
@@ -510,10 +527,14 @@ public final class SncpDynServlet extends SncpServlet {
|
||||
final Class pt = paramClasses[j];
|
||||
mv.visitInsn(DUP);
|
||||
insn++;
|
||||
if (j <= 5) {
|
||||
if (j < 6) {
|
||||
mv.visitInsn(ICONST_0 + j);
|
||||
} else {
|
||||
} else if (j <= Byte.MAX_VALUE) {
|
||||
mv.visitIntInsn(BIPUSH, j);
|
||||
} else if (j <= Short.MAX_VALUE) {
|
||||
mv.visitIntInsn(SIPUSH, j);
|
||||
} else {
|
||||
mv.visitLdcInsn(j);
|
||||
}
|
||||
if (pt.isPrimitive()) {
|
||||
if (pt == long.class) {
|
||||
|
||||
@@ -8,7 +8,6 @@ package org.redkale.net.sncp;
|
||||
import org.redkale.net.PrepareServlet;
|
||||
import org.redkale.util.AnyValue;
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import org.redkale.service.Service;
|
||||
import org.redkale.util.*;
|
||||
|
||||
@@ -23,7 +22,6 @@ public class SncpPrepareServlet extends PrepareServlet<DLong, SncpContext, SncpR
|
||||
|
||||
private final Object sncplock = new Object();
|
||||
|
||||
private static final ByteBuffer pongBuffer = ByteBuffer.wrap("PONG".getBytes()).asReadOnlyBuffer();
|
||||
|
||||
@Override
|
||||
public void addServlet(SncpServlet servlet, Object attachment, AnyValue conf, DLong... mappings) {
|
||||
@@ -69,7 +67,7 @@ public class SncpPrepareServlet extends PrepareServlet<DLong, SncpContext, SncpR
|
||||
@Override
|
||||
public void execute(SncpRequest request, SncpResponse response) throws IOException {
|
||||
if (request.isPing()) {
|
||||
response.finish(pongBuffer.duplicate());
|
||||
response.finish(Sncp.PONG_BUFFER.duplicate());
|
||||
return;
|
||||
}
|
||||
SncpServlet servlet = (SncpServlet) mappingServlet(request.getServiceid());
|
||||
|
||||
@@ -7,6 +7,7 @@ package org.redkale.net.sncp;
|
||||
|
||||
import java.net.*;
|
||||
import java.nio.*;
|
||||
import java.util.logging.Level;
|
||||
import org.redkale.convert.bson.*;
|
||||
import org.redkale.net.*;
|
||||
import org.redkale.util.*;
|
||||
@@ -58,7 +59,7 @@ public final class SncpRequest extends Request<SncpContext> {
|
||||
//---------------------head----------------------------------
|
||||
this.seqid = buffer.getLong();
|
||||
if (buffer.getChar() != HEADER_SIZE) {
|
||||
context.getLogger().finest("sncp buffer header.length not " + HEADER_SIZE);
|
||||
if (context.getLogger().isLoggable(Level.FINEST)) context.getLogger().finest("sncp buffer header.length not " + HEADER_SIZE);
|
||||
return -1;
|
||||
}
|
||||
this.serviceid = DLong.read(buffer);
|
||||
@@ -68,7 +69,7 @@ public final class SncpRequest extends Request<SncpContext> {
|
||||
this.bodylength = buffer.getInt();
|
||||
|
||||
if (buffer.getInt() != 0) {
|
||||
context.getLogger().finest("sncp buffer header.retcode not 0");
|
||||
if (context.getLogger().isLoggable(Level.FINEST)) context.getLogger().finest("sncp buffer header.retcode not 0");
|
||||
return -1;
|
||||
}
|
||||
//---------------------body----------------------------------
|
||||
|
||||
@@ -49,6 +49,7 @@ public final class SncpResponse extends Response<SncpContext, SncpRequest> {
|
||||
super(context, request);
|
||||
this.addrBytes = context.getServerAddress().getAddress().getAddress();
|
||||
this.addrPort = context.getServerAddress().getPort();
|
||||
if (this.addrBytes.length != 4) throw new RuntimeException("SNCP serverAddress only support IPv4");
|
||||
}
|
||||
|
||||
public void finish(final int retcode, final BsonWriter out) {
|
||||
|
||||
@@ -124,7 +124,8 @@ public class RetResult<T> {
|
||||
*/
|
||||
public RetResult<T> attach(String key, Object value) {
|
||||
if (this.attach == null) this.attach = new HashMap<>();
|
||||
this.attach.put(key, value == null ? null : String.valueOf(value));
|
||||
boolean canstr = value != null && (value instanceof CharSequence || value.getClass().isPrimitive());
|
||||
this.attach.put(key, value == null ? null : (canstr ? String.valueOf(value) : JsonConvert.root().convertTo(value)));
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
@@ -22,10 +22,10 @@ import org.redkale.util.*;
|
||||
* <blockquote><pre>
|
||||
* 异步方法:
|
||||
* Service编写异步方法:
|
||||
* 1、异步方法有且仅有一个类型为AsyncHandler的参数, 返回类型必须是void。若参数类型为AsyncHandler子类,必须保证其子类可被继承且completed、failed可被重载且包含空参数的构造函数。
|
||||
* 1、异步方法有且仅有一个类型为CompletionHandler的参数, 返回类型必须是void。若参数类型为CompletionHandler子类,必须保证其子类可被继承且completed、failed可被重载且包含空参数的构造函数。
|
||||
* 2、异步方法返回类型是CompletableFuture。
|
||||
* 例如:
|
||||
* public void insertRecord(AsyncHandler<Integer, Record> handler, String name, @RpcAttachment Record record);
|
||||
* public void insertRecord(CompletionHandler<Integer, Record> handler, String name, @RpcAttachment Record record);
|
||||
*
|
||||
* </pre></blockquote>
|
||||
*
|
||||
|
||||
@@ -10,6 +10,7 @@ import java.io.*;
|
||||
import java.net.*;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.logging.Level;
|
||||
import org.redkale.net.http.*;
|
||||
import org.redkale.util.*;
|
||||
|
||||
@@ -60,38 +61,57 @@ public class WebSocketNodeService extends WebSocketNode implements Service {
|
||||
/**
|
||||
* 当用户连接到节点,需要更新到CacheSource
|
||||
*
|
||||
* @param userid String
|
||||
* @param userid Serializable
|
||||
* @param sncpAddr InetSocketAddress
|
||||
*
|
||||
* @return 无返回值
|
||||
*/
|
||||
@Override
|
||||
public CompletableFuture<Void> connect(Serializable userid, InetSocketAddress sncpAddr) {
|
||||
CompletableFuture<Void> future = sncpNodeAddresses.appendSetItemAsync(userid, sncpAddr);
|
||||
future = future.thenAccept((a) -> sncpNodeAddresses.appendSetItemAsync(SOURCE_SNCP_NODES_KEY, sncpAddr));
|
||||
if (finest) logger.finest(WebSocketNodeService.class.getSimpleName() + ".event: " + userid + " connect from " + sncpAddr);
|
||||
CompletableFuture<Void> future = sncpNodeAddresses.appendSetItemAsync(SOURCE_SNCP_USERID_PREFIX + userid, sncpAddr);
|
||||
future = future.thenAccept((a) -> sncpNodeAddresses.incr(SOURCE_SNCP_USERCOUNT_KEY));
|
||||
future = future.thenAccept((a) -> sncpNodeAddresses.appendSetItemAsync(SOURCE_SNCP_ADDRS_KEY, sncpAddr));
|
||||
if (logger.isLoggable(Level.FINEST)) logger.finest(WebSocketNodeService.class.getSimpleName() + ".event: " + userid + " connect from " + sncpAddr);
|
||||
return future;
|
||||
}
|
||||
|
||||
/**
|
||||
* 当用户从一个节点断掉了所有的连接,需要从CacheSource中删除
|
||||
*
|
||||
* @param userid String
|
||||
* @param userid Serializable
|
||||
* @param sncpAddr InetSocketAddress
|
||||
*
|
||||
* @return 无返回值
|
||||
*/
|
||||
@Override
|
||||
public CompletableFuture<Void> disconnect(Serializable userid, InetSocketAddress sncpAddr) {
|
||||
CompletableFuture<Void> future = sncpNodeAddresses.removeSetItemAsync(userid, sncpAddr);
|
||||
if (finest) logger.finest(WebSocketNodeService.class.getSimpleName() + ".event: " + userid + " disconnect from " + sncpAddr);
|
||||
CompletableFuture<Void> future = sncpNodeAddresses.removeSetItemAsync(SOURCE_SNCP_USERID_PREFIX + userid, sncpAddr);
|
||||
future = future.thenAccept((a) -> sncpNodeAddresses.decr(SOURCE_SNCP_USERCOUNT_KEY));
|
||||
if (logger.isLoggable(Level.FINEST)) logger.finest(WebSocketNodeService.class.getSimpleName() + ".event: " + userid + " disconnect from " + sncpAddr);
|
||||
return future;
|
||||
}
|
||||
|
||||
/**
|
||||
* 更改用户ID,需要更新到CacheSource
|
||||
*
|
||||
* @param olduserid Serializable
|
||||
* @param newuserid Serializable
|
||||
* @param sncpAddr InetSocketAddress
|
||||
*
|
||||
* @return 无返回值
|
||||
*/
|
||||
@Override
|
||||
public CompletableFuture<Void> changeUserid(Serializable olduserid, Serializable newuserid, InetSocketAddress sncpAddr) {
|
||||
CompletableFuture<Void> future = sncpNodeAddresses.appendSetItemAsync(SOURCE_SNCP_USERID_PREFIX + newuserid, sncpAddr);
|
||||
future = future.thenAccept((a) -> sncpNodeAddresses.removeSetItemAsync(SOURCE_SNCP_USERID_PREFIX + olduserid, sncpAddr));
|
||||
if (logger.isLoggable(Level.FINEST)) logger.finest(WebSocketNodeService.class.getSimpleName() + ".event: " + olduserid + " changeUserid to " + newuserid + " from " + sncpAddr);
|
||||
return future;
|
||||
}
|
||||
|
||||
/**
|
||||
* 强制关闭用户的WebSocket
|
||||
*
|
||||
* @param userid String
|
||||
* @param userid Serializable
|
||||
* @param sncpAddr InetSocketAddress
|
||||
*
|
||||
* @return 无返回值
|
||||
@@ -99,7 +119,7 @@ public class WebSocketNodeService extends WebSocketNode implements Service {
|
||||
@Override
|
||||
public CompletableFuture<Integer> forceCloseWebSocket(Serializable userid, InetSocketAddress sncpAddr) {
|
||||
//不能从sncpNodeAddresses中移除,因为engine.forceCloseWebSocket 会调用到disconnect
|
||||
if (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 " + sncpAddr);
|
||||
if (localEngine == null) return CompletableFuture.completedFuture(0);
|
||||
return CompletableFuture.completedFuture(localEngine.forceCloseLocalWebSocket(userid));
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import java.io.*;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.logging.*;
|
||||
import javax.annotation.Resource;
|
||||
@@ -20,7 +21,6 @@ import org.redkale.util.*;
|
||||
/**
|
||||
* CacheSource的默认实现--内存缓存
|
||||
*
|
||||
* @param <K> key类型
|
||||
* @param <V> value类型
|
||||
* <p>
|
||||
* 详情见: https://redkale.org
|
||||
@@ -30,66 +30,76 @@ import org.redkale.util.*;
|
||||
@Local
|
||||
@AutoLoad(false)
|
||||
@ResourceType(CacheSource.class)
|
||||
public class CacheMemorySource<K extends Serializable, V extends Object> extends AbstractService implements CacheSource<K, V>, Service, AutoCloseable, Resourcable {
|
||||
public class CacheMemorySource<V extends Object> extends AbstractService implements CacheSource<V>, Service, AutoCloseable, Resourcable {
|
||||
|
||||
private static final Type STRING_ENTRY_TYPE = new TypeToken<CacheEntry<String>>() {
|
||||
}.getType();
|
||||
|
||||
private static final Type LONG_ENTRY_TYPE = new TypeToken<CacheEntry<Long>>() {
|
||||
}.getType();
|
||||
|
||||
private static final Type ATOMIC_ENTRY_TYPE = new TypeToken<CacheEntry<AtomicLong>>() {
|
||||
}.getType();
|
||||
|
||||
@Resource(name = "APP_HOME")
|
||||
private File home;
|
||||
|
||||
@Resource
|
||||
private JsonConvert defaultConvert;
|
||||
|
||||
@Resource(name = "$_convert")
|
||||
private JsonConvert convert;
|
||||
|
||||
private boolean needStore;
|
||||
|
||||
private Class keyType;
|
||||
|
||||
private Type objValueType;
|
||||
|
||||
private Type setValueType;
|
||||
|
||||
private Type listValueType;
|
||||
|
||||
private ScheduledThreadPoolExecutor scheduler;
|
||||
|
||||
private Consumer<CacheEntry> expireHandler;
|
||||
|
||||
private final Logger logger = Logger.getLogger(this.getClass().getSimpleName());
|
||||
|
||||
protected final ConcurrentHashMap<K, CacheEntry<K, Object>> container = new ConcurrentHashMap<>();
|
||||
protected final ConcurrentHashMap<String, CacheEntry<Object>> container = new ConcurrentHashMap<>();
|
||||
|
||||
@RpcRemote
|
||||
protected CacheSource<K, V> remoteSource;
|
||||
protected CacheSource<V> remoteSource;
|
||||
|
||||
public CacheMemorySource() {
|
||||
}
|
||||
|
||||
public final CacheMemorySource setStoreType(Class keyType, Class valueType) {
|
||||
this.keyType = keyType;
|
||||
@Override
|
||||
public final void initValueType(Type valueType) {
|
||||
this.objValueType = valueType;
|
||||
this.setValueType = TypeToken.createParameterizedType(null, CopyOnWriteArraySet.class, valueType);
|
||||
this.listValueType = TypeToken.createParameterizedType(null, ConcurrentLinkedQueue.class, valueType);
|
||||
this.setNeedStore(this.keyType != null && this.keyType != Serializable.class && this.objValueType != null);
|
||||
return this;
|
||||
this.initTransient(this.objValueType == null);
|
||||
}
|
||||
|
||||
public final void setNeedStore(boolean needStore) {
|
||||
this.needStore = needStore;
|
||||
@Override
|
||||
public final void initTransient(boolean flag) {
|
||||
this.needStore = !flag;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final String getType() {
|
||||
return "memory";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(AnyValue conf) {
|
||||
if (this.convert == null) this.convert = this.defaultConvert;
|
||||
if (this.convert == null) this.convert = JsonConvert.root();
|
||||
final CacheMemorySource self = this;
|
||||
AnyValue prop = conf == null ? null : conf.getAnyValue("property");
|
||||
if (keyType == null && prop != null) {
|
||||
String storeKeyStr = prop.getValue("key-type");
|
||||
AnyValue prop = conf == null ? null : conf.getAnyValue("properties");
|
||||
if (prop != null) {
|
||||
String storeValueStr = prop.getValue("value-type");
|
||||
if (storeKeyStr != null && storeValueStr != null) {
|
||||
if (storeValueStr != null) {
|
||||
try {
|
||||
this.setStoreType(Thread.currentThread().getContextClassLoader().loadClass(storeKeyStr), Thread.currentThread().getContextClassLoader().loadClass(storeValueStr));
|
||||
this.initValueType(Thread.currentThread().getContextClassLoader().loadClass(storeValueStr));
|
||||
} catch (Throwable e) {
|
||||
logger.log(Level.SEVERE, self.getClass().getSimpleName() + " load key & value store class (" + storeKeyStr + ", " + storeValueStr + ") error", e);
|
||||
logger.log(Level.SEVERE, self.getClass().getSimpleName() + " load key & value store class (" + storeValueStr + ") error", e);
|
||||
}
|
||||
}
|
||||
if (prop.getBoolValue("store-ignore", false)) setNeedStore(false);
|
||||
this.initTransient(prop.getBoolValue("store-ignore", false));
|
||||
}
|
||||
String expireHandlerClass = prop == null ? null : prop.getValue("expirehandler");
|
||||
if (expireHandlerClass != null) {
|
||||
@@ -105,7 +115,7 @@ public class CacheMemorySource<K extends Serializable, V extends Object> extends
|
||||
t.setDaemon(true);
|
||||
return t;
|
||||
});
|
||||
final List<K> keys = new ArrayList<>();
|
||||
final List<String> keys = new ArrayList<>();
|
||||
scheduler.scheduleWithFixedDelay(() -> {
|
||||
keys.clear();
|
||||
int now = (int) (System.currentTimeMillis() / 1000);
|
||||
@@ -114,12 +124,12 @@ public class CacheMemorySource<K extends Serializable, V extends Object> extends
|
||||
keys.add(x.key);
|
||||
}
|
||||
});
|
||||
for (K key : keys) {
|
||||
for (String key : keys) {
|
||||
CacheEntry entry = container.remove(key);
|
||||
if (expireHandler != null && entry != null) expireHandler.accept(entry);
|
||||
}
|
||||
}, 10, 10, TimeUnit.SECONDS);
|
||||
logger.finest(self.getClass().getSimpleName() + ":" + self.resourceName() + " start schedule expire executor");
|
||||
if (logger.isLoggable(Level.FINEST)) logger.finest(self.getClass().getSimpleName() + ":" + self.resourceName() + " start schedule expire executor");
|
||||
}
|
||||
if (Sncp.isRemote(self)) return;
|
||||
|
||||
@@ -128,22 +138,24 @@ public class CacheMemorySource<K extends Serializable, V extends Object> extends
|
||||
// TODO
|
||||
if (this.needStore) {
|
||||
try {
|
||||
File store = new File(home, "cache/" + resourceName());
|
||||
File store = home == null ? new File("cache/" + resourceName()) : new File(home, "cache/" + resourceName());
|
||||
if (!store.isFile() || !store.canRead()) return;
|
||||
LineNumberReader reader = new LineNumberReader(new FileReader(store));
|
||||
if (this.keyType == null) this.keyType = Serializable.class;
|
||||
if (this.objValueType == null) {
|
||||
this.objValueType = Object.class;
|
||||
this.setValueType = TypeToken.createParameterizedType(null, CopyOnWriteArraySet.class, this.objValueType);
|
||||
this.listValueType = TypeToken.createParameterizedType(null, ConcurrentLinkedQueue.class, this.objValueType);
|
||||
}
|
||||
final Type storeObjType = TypeToken.createParameterizedType(null, CacheEntry.class, keyType, objValueType);
|
||||
final Type storeSetType = TypeToken.createParameterizedType(null, CacheEntry.class, keyType, setValueType);
|
||||
final Type storeListType = TypeToken.createParameterizedType(null, CacheEntry.class, keyType, listValueType);
|
||||
if (this.objValueType == null) this.objValueType = Object.class;
|
||||
final Type storeObjType = TypeToken.createParameterizedType(null, CacheEntry.class, objValueType);
|
||||
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
if (line.isEmpty()) continue;
|
||||
CacheEntry<K, Object> entry = convert.convertFrom(line.startsWith(CacheEntry.JSON_SET_KEY) ? storeSetType : (line.startsWith(CacheEntry.JSON_LIST_KEY) ? storeListType : storeObjType), line);
|
||||
Type convertType = storeObjType;
|
||||
if (line.startsWith("{\"cacheType\":\"" + CacheEntryType.LONG)) {
|
||||
convertType = LONG_ENTRY_TYPE;
|
||||
} else if (line.startsWith("{\"cacheType\":\"" + CacheEntryType.STRING)) {
|
||||
convertType = STRING_ENTRY_TYPE;
|
||||
} else if (line.startsWith("{\"cacheType\":\"" + CacheEntryType.ATOMIC)) {
|
||||
convertType = ATOMIC_ENTRY_TYPE;
|
||||
}
|
||||
CacheEntry<Object> entry = convert.convertFrom(convertType, line);
|
||||
if (entry.isExpired()) continue;
|
||||
if (datasync && container.containsKey(entry.key)) continue; //已经同步了
|
||||
container.put(entry.key, entry);
|
||||
@@ -156,27 +168,84 @@ public class CacheMemorySource<K extends Serializable, V extends Object> extends
|
||||
}
|
||||
if (remoteSource != null && !Sncp.isRemote(this)) {
|
||||
SncpClient client = Sncp.getSncpClient((Service) remoteSource);
|
||||
if (client != null && client.getRemoteGroupTransport() != null) {
|
||||
if (client != null && client.getRemoteGroupTransport() != null
|
||||
&& client.getRemoteGroupTransport().getRemoteAddresses().length > 0) {
|
||||
super.runAsync(() -> {
|
||||
try {
|
||||
CompletableFuture<List<CacheEntry<K, Object>>> listFuture = remoteSource.queryListAsync();
|
||||
CompletableFuture<List<CacheEntry<Object>>> listFuture = remoteSource.queryListAsync();
|
||||
listFuture.whenComplete((list, exp) -> {
|
||||
if (exp != null) {
|
||||
logger.log(Level.FINEST, CacheSource.class.getSimpleName() + "(" + resourceName() + ") queryListAsync error", exp);
|
||||
if (logger.isLoggable(Level.FINEST)) logger.log(Level.FINEST, CacheSource.class.getSimpleName() + "(" + resourceName() + ") queryListAsync error", exp);
|
||||
} else {
|
||||
for (CacheEntry<K, Object> entry : list) {
|
||||
for (CacheEntry<Object> entry : list) {
|
||||
container.put(entry.key, entry);
|
||||
}
|
||||
}
|
||||
});
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.FINEST, CacheSource.class.getSimpleName() + "(" + resourceName() + ") queryListAsync error, maybe remote node connot connect ", e);
|
||||
if (logger.isLoggable(Level.FINEST)) logger.log(Level.FINEST, CacheSource.class.getSimpleName() + "(" + resourceName() + ") queryListAsync error, maybe remote node connot connect ", e);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* public static void main(String[] args) throws Exception {
|
||||
* AnyValue.DefaultAnyValue conf = new AnyValue.DefaultAnyValue();
|
||||
* conf.addValue("node", new AnyValue.DefaultAnyValue().addValue("addr", "127.0.0.1").addValue("port", "6379"));
|
||||
*
|
||||
* CacheMemorySource source = new CacheMemorySource();
|
||||
* source.defaultConvert = JsonFactory.root().getConvert();
|
||||
* source.initValueType(String.class); //value用String类型
|
||||
* source.initTransient(false);
|
||||
* source.init(conf);
|
||||
*
|
||||
* System.out.println("------------------------------------");
|
||||
* source.remove("key1");
|
||||
* source.remove("key2");
|
||||
* source.remove("300");
|
||||
* source.set("key1", "value1");
|
||||
* source.setString("keystr1", "strvalue1");
|
||||
* source.setLong("keylong1", 333L);
|
||||
* source.set("300", "4000");
|
||||
* source.getAndRefresh("key1", 3500);
|
||||
* System.out.println("[有值] 300 GET : " + source.get("300"));
|
||||
* System.out.println("[有值] key1 GET : " + source.get("key1"));
|
||||
* System.out.println("[无值] key2 GET : " + source.get("key2"));
|
||||
* System.out.println("[有值] keylong1 GET : " + source.getLong("keylong1", 0L));
|
||||
* System.out.println("[有值] key1 EXISTS : " + source.exists("key1"));
|
||||
* System.out.println("[无值] key2 EXISTS : " + source.exists("key2"));
|
||||
*
|
||||
* source.remove("keys3");
|
||||
* source.appendListItem("keys3", "vals1");
|
||||
* source.appendListItem("keys3", "vals2");
|
||||
* System.out.println("-------- keys3 追加了两个值 --------");
|
||||
* System.out.println("[两值] keys3 VALUES : " + source.getCollection("keys3"));
|
||||
* System.out.println("[有值] keys3 EXISTS : " + source.exists("keys3"));
|
||||
* source.removeListItem("keys3", "vals1");
|
||||
* System.out.println("[一值] keys3 VALUES : " + source.getCollection("keys3"));
|
||||
* source.getCollectionAndRefresh("keys3", 3000);
|
||||
*
|
||||
* source.remove("sets3");
|
||||
* source.appendSetItem("sets3", "setvals1");
|
||||
* source.appendSetItem("sets3", "setvals2");
|
||||
* source.appendSetItem("sets3", "setvals1");
|
||||
* System.out.println("[两值] sets3 VALUES : " + source.getCollection("sets3"));
|
||||
* System.out.println("[有值] sets3 EXISTS : " + source.exists("sets3"));
|
||||
* source.removeSetItem("sets3", "setvals1");
|
||||
* System.out.println("[一值] sets3 VALUES : " + source.getCollection("sets3"));
|
||||
* System.out.println("sets3 大小 : " + source.getCollectionSize("sets3"));
|
||||
* System.out.println("all keys: " + source.queryKeys());
|
||||
* System.out.println("newnum 值 : " + source.incr("newnum"));
|
||||
* System.out.println("newnum 值 : " + source.decr("newnum"));
|
||||
* System.out.println("------------------------------------");
|
||||
* source.destroy(null);
|
||||
* source.init(null);
|
||||
* System.out.println("all keys: " + source.queryKeys());
|
||||
* System.out.println("[有值] keylong1 GET : " + source.getLong("keylong1", 0L));
|
||||
* }
|
||||
*/
|
||||
@Override
|
||||
public void close() throws Exception { //给Application 关闭时调用
|
||||
destroy(null);
|
||||
@@ -185,7 +254,7 @@ public class CacheMemorySource<K extends Serializable, V extends Object> extends
|
||||
@Override
|
||||
public String resourceName() {
|
||||
Resource res = this.getClass().getAnnotation(Resource.class);
|
||||
return res == null ? null : res.name();
|
||||
return res == null ? "cachememory" : res.name();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -196,12 +265,39 @@ public class CacheMemorySource<K extends Serializable, V extends Object> extends
|
||||
File store = new File(home, "cache/" + resourceName());
|
||||
store.getParentFile().mkdirs();
|
||||
PrintStream stream = new PrintStream(store, "UTF-8");
|
||||
final Type storeObjType = TypeToken.createParameterizedType(null, CacheEntry.class, keyType, objValueType);
|
||||
final Type storeSetType = TypeToken.createParameterizedType(null, CacheEntry.class, keyType, setValueType);
|
||||
final Type storeListType = TypeToken.createParameterizedType(null, CacheEntry.class, keyType, listValueType);
|
||||
Collection<CacheEntry<K, Object>> entrys = container.values();
|
||||
final Type storeObjType = TypeToken.createParameterizedType(null, CacheEntry.class, objValueType);
|
||||
final Type storeSetType = TypeToken.createParameterizedType(null, CacheEntry.class, objValueType);
|
||||
final Type storeListType = TypeToken.createParameterizedType(null, CacheEntry.class, objValueType);
|
||||
Collection<CacheEntry<Object>> entrys = container.values();
|
||||
for (CacheEntry entry : entrys) {
|
||||
stream.println(convert.convertTo(entry.isSetCacheType() ? storeSetType : (entry.isListCacheType() ? storeListType : storeObjType), entry));
|
||||
Type convertType = storeObjType;
|
||||
if (entry.cacheType == CacheEntryType.LONG) {
|
||||
convertType = LONG_ENTRY_TYPE;
|
||||
} else if (entry.cacheType == CacheEntryType.STRING) {
|
||||
convertType = STRING_ENTRY_TYPE;
|
||||
} else if (entry.cacheType == CacheEntryType.ATOMIC) {
|
||||
convertType = ATOMIC_ENTRY_TYPE;
|
||||
} else if (entry.cacheType == CacheEntryType.OBJECT) {
|
||||
convertType = storeObjType;
|
||||
} else if (entry.cacheType == CacheEntryType.LONG_LIST) {
|
||||
convertType = LONG_ENTRY_TYPE;
|
||||
} else if (entry.cacheType == CacheEntryType.LONG_SET) {
|
||||
convertType = LONG_ENTRY_TYPE;
|
||||
} else if (entry.cacheType == CacheEntryType.STRING_LIST) {
|
||||
convertType = STRING_ENTRY_TYPE;
|
||||
} else if (entry.cacheType == CacheEntryType.STRING_SET) {
|
||||
convertType = STRING_ENTRY_TYPE;
|
||||
} else if (entry.cacheType == CacheEntryType.OBJECT_LIST) {
|
||||
convertType = storeListType;
|
||||
} else if (entry.cacheType == CacheEntryType.OBJECT_SET) {
|
||||
convertType = storeSetType;
|
||||
}
|
||||
try {
|
||||
stream.println(convert.convertTo(convertType, entry));
|
||||
} catch (Exception ee) {
|
||||
System.err.println(storeSetType + "-----" + entry);
|
||||
throw ee;
|
||||
}
|
||||
}
|
||||
container.clear();
|
||||
stream.close();
|
||||
@@ -211,7 +307,7 @@ public class CacheMemorySource<K extends Serializable, V extends Object> extends
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean exists(K key) {
|
||||
public boolean exists(String key) {
|
||||
if (key == null) return false;
|
||||
CacheEntry entry = container.get(key);
|
||||
if (entry == null) return false;
|
||||
@@ -219,46 +315,108 @@ public class CacheMemorySource<K extends Serializable, V extends Object> extends
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Boolean> existsAsync(final K key) {
|
||||
public CompletableFuture<Boolean> existsAsync(final String key) {
|
||||
return CompletableFuture.supplyAsync(() -> exists(key), getExecutor());
|
||||
}
|
||||
|
||||
@Override
|
||||
public V get(K key) {
|
||||
public V get(String key) {
|
||||
if (key == null) return null;
|
||||
CacheEntry entry = container.get(key);
|
||||
if (entry == null || entry.isExpired()) return null;
|
||||
if (entry.isListCacheType()) return (V) (entry.listValue == null ? null : new ArrayList(entry.listValue));
|
||||
if (entry.isSetCacheType()) return (V) (entry.setValue == null ? null : new HashSet(entry.setValue));
|
||||
if (entry.isSetCacheType()) return (V) (entry.csetValue == null ? null : new HashSet(entry.csetValue));
|
||||
return (V) entry.objectValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<V> getAsync(final K key) {
|
||||
public String getString(String key) {
|
||||
if (key == null) return null;
|
||||
CacheEntry entry = container.get(key);
|
||||
if (entry == null || entry.isExpired()) return null;
|
||||
return (String) entry.objectValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getLong(String key, long defValue) {
|
||||
if (key == null) return defValue;
|
||||
CacheEntry entry = container.get(key);
|
||||
if (entry == null || entry.isExpired()) return defValue;
|
||||
return entry.objectValue == null ? defValue : (entry.objectValue instanceof AtomicLong ? ((AtomicLong) entry.objectValue).get() : (Long) entry.objectValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<V> getAsync(final String key) {
|
||||
return CompletableFuture.supplyAsync(() -> get(key), getExecutor());
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<String> getStringAsync(final String key) {
|
||||
return CompletableFuture.supplyAsync(() -> getString(key), getExecutor());
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Long> getLongAsync(final String key, long defValue) {
|
||||
return CompletableFuture.supplyAsync(() -> getLong(key, defValue), getExecutor());
|
||||
}
|
||||
|
||||
@Override
|
||||
@RpcMultiRun
|
||||
public V getAndRefresh(K key, final int expireSeconds) {
|
||||
public V getAndRefresh(String key, final int expireSeconds) {
|
||||
if (key == null) return null;
|
||||
CacheEntry entry = container.get(key);
|
||||
if (entry == null || entry.isExpired()) return null;
|
||||
entry.lastAccessed = (int) (System.currentTimeMillis() / 1000);
|
||||
entry.expireSeconds = expireSeconds;
|
||||
if (entry.isListCacheType()) return (V) (entry.listValue == null ? null : new ArrayList(entry.listValue));
|
||||
if (entry.isSetCacheType()) return (V) (entry.setValue == null ? null : new HashSet(entry.setValue));
|
||||
if (entry.isSetCacheType()) return (V) (entry.csetValue == null ? null : new HashSet(entry.csetValue));
|
||||
return (V) entry.objectValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<V> getAndRefreshAsync(final K key, final int expireSeconds) {
|
||||
@RpcMultiRun
|
||||
public String getStringAndRefresh(String key, final int expireSeconds) {
|
||||
if (key == null) return null;
|
||||
CacheEntry entry = container.get(key);
|
||||
if (entry == null || entry.isExpired()) return null;
|
||||
entry.lastAccessed = (int) (System.currentTimeMillis() / 1000);
|
||||
entry.expireSeconds = expireSeconds;
|
||||
return (String) entry.objectValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
@RpcMultiRun
|
||||
public long getLongAndRefresh(String key, final int expireSeconds, long defValue) {
|
||||
if (key == null) return defValue;
|
||||
CacheEntry entry = container.get(key);
|
||||
if (entry == null || entry.isExpired()) return defValue;
|
||||
entry.lastAccessed = (int) (System.currentTimeMillis() / 1000);
|
||||
entry.expireSeconds = expireSeconds;
|
||||
return entry.objectValue == null ? defValue : (entry.objectValue instanceof AtomicLong ? ((AtomicLong) entry.objectValue).get() : (Long) entry.objectValue);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
@RpcMultiRun
|
||||
public CompletableFuture<V> getAndRefreshAsync(final String key, final int expireSeconds) {
|
||||
return CompletableFuture.supplyAsync(() -> getAndRefresh(key, expireSeconds), getExecutor());
|
||||
}
|
||||
|
||||
@Override
|
||||
@RpcMultiRun
|
||||
public void refresh(K key, final int expireSeconds) {
|
||||
public CompletableFuture<String> getStringAndRefreshAsync(final String key, final int expireSeconds) {
|
||||
return CompletableFuture.supplyAsync(() -> getStringAndRefresh(key, expireSeconds), getExecutor());
|
||||
}
|
||||
|
||||
@Override
|
||||
@RpcMultiRun
|
||||
public CompletableFuture<Long> getLongAndRefreshAsync(final String key, final int expireSeconds, long defValue) {
|
||||
return CompletableFuture.supplyAsync(() -> getLongAndRefresh(key, expireSeconds, defValue), getExecutor());
|
||||
}
|
||||
|
||||
@Override
|
||||
@RpcMultiRun
|
||||
public void refresh(String key, final int expireSeconds) {
|
||||
if (key == null) return;
|
||||
CacheEntry entry = container.get(key);
|
||||
if (entry == null) return;
|
||||
@@ -267,17 +425,16 @@ public class CacheMemorySource<K extends Serializable, V extends Object> extends
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> refreshAsync(final K key, final int expireSeconds) {
|
||||
@RpcMultiRun
|
||||
public CompletableFuture<Void> refreshAsync(final String key, final int expireSeconds) {
|
||||
return CompletableFuture.runAsync(() -> refresh(key, expireSeconds), getExecutor());
|
||||
}
|
||||
|
||||
@Override
|
||||
@RpcMultiRun
|
||||
public void set(K key, V value) {
|
||||
protected void set(CacheEntryType cacheType, String key, Object value) {
|
||||
if (key == null) return;
|
||||
CacheEntry entry = container.get(key);
|
||||
if (entry == null) {
|
||||
entry = new CacheEntry(CacheEntryType.OBJECT, key, value, null, null);
|
||||
entry = new CacheEntry(cacheType, key, value, null, null);
|
||||
container.putIfAbsent(key, entry);
|
||||
} else {
|
||||
entry.expireSeconds = 0;
|
||||
@@ -287,17 +444,46 @@ public class CacheMemorySource<K extends Serializable, V extends Object> extends
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> setAsync(K key, V value) {
|
||||
@RpcMultiRun
|
||||
public void set(String key, V value) {
|
||||
set(CacheEntryType.OBJECT, key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
@RpcMultiRun
|
||||
public void setString(String key, String value) {
|
||||
set(CacheEntryType.STRING, key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
@RpcMultiRun
|
||||
public void setLong(String key, long value) {
|
||||
set(CacheEntryType.LONG, key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
@RpcMultiRun
|
||||
public CompletableFuture<Void> setAsync(String key, V value) {
|
||||
return CompletableFuture.runAsync(() -> set(key, value), getExecutor());
|
||||
}
|
||||
|
||||
@Override
|
||||
@RpcMultiRun
|
||||
public void set(int expireSeconds, K key, V value) {
|
||||
public CompletableFuture<Void> setStringAsync(String key, String value) {
|
||||
return CompletableFuture.runAsync(() -> setString(key, value), getExecutor());
|
||||
}
|
||||
|
||||
@Override
|
||||
@RpcMultiRun
|
||||
public CompletableFuture<Void> setLongAsync(String key, long value) {
|
||||
return CompletableFuture.runAsync(() -> setLong(key, value), getExecutor());
|
||||
}
|
||||
|
||||
protected void set(CacheEntryType cacheType, int expireSeconds, String key, Object value) {
|
||||
if (key == null) return;
|
||||
CacheEntry entry = container.get(key);
|
||||
if (entry == null) {
|
||||
entry = new CacheEntry(CacheEntryType.OBJECT, expireSeconds, key, value, null, null);
|
||||
entry = new CacheEntry(cacheType, expireSeconds, key, value, null, null);
|
||||
container.putIfAbsent(key, entry);
|
||||
} else {
|
||||
if (expireSeconds > 0) entry.expireSeconds = expireSeconds;
|
||||
@@ -307,13 +493,44 @@ public class CacheMemorySource<K extends Serializable, V extends Object> extends
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> setAsync(int expireSeconds, K key, V value) {
|
||||
@RpcMultiRun
|
||||
public void set(int expireSeconds, String key, V value) {
|
||||
set(CacheEntryType.OBJECT, expireSeconds, key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
@RpcMultiRun
|
||||
public void setString(int expireSeconds, String key, String value) {
|
||||
set(CacheEntryType.STRING, expireSeconds, key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
@RpcMultiRun
|
||||
public void setLong(int expireSeconds, String key, long value) {
|
||||
set(CacheEntryType.LONG, expireSeconds, key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
@RpcMultiRun
|
||||
public CompletableFuture<Void> setAsync(int expireSeconds, String key, V value) {
|
||||
return CompletableFuture.runAsync(() -> set(expireSeconds, key, value), getExecutor());
|
||||
}
|
||||
|
||||
@Override
|
||||
@RpcMultiRun
|
||||
public void setExpireSeconds(K key, int expireSeconds) {
|
||||
public CompletableFuture<Void> setStringAsync(int expireSeconds, String key, String value) {
|
||||
return CompletableFuture.runAsync(() -> setString(expireSeconds, key, value), getExecutor());
|
||||
}
|
||||
|
||||
@Override
|
||||
@RpcMultiRun
|
||||
public CompletableFuture<Void> setLongAsync(int expireSeconds, String key, long value) {
|
||||
return CompletableFuture.runAsync(() -> setLong(expireSeconds, key, value), getExecutor());
|
||||
}
|
||||
|
||||
@Override
|
||||
@RpcMultiRun
|
||||
public void setExpireSeconds(String key, int expireSeconds) {
|
||||
if (key == null) return;
|
||||
CacheEntry entry = container.get(key);
|
||||
if (entry == null) return;
|
||||
@@ -321,61 +538,165 @@ public class CacheMemorySource<K extends Serializable, V extends Object> extends
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> setExpireSecondsAsync(final K key, final int expireSeconds) {
|
||||
@RpcMultiRun
|
||||
public CompletableFuture<Void> setExpireSecondsAsync(final String key, final int expireSeconds) {
|
||||
return CompletableFuture.runAsync(() -> setExpireSeconds(key, expireSeconds), getExecutor());
|
||||
}
|
||||
|
||||
@Override
|
||||
@RpcMultiRun
|
||||
public void remove(K key) {
|
||||
public void remove(String key) {
|
||||
if (key == null) return;
|
||||
container.remove(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> removeAsync(final K key) {
|
||||
@RpcMultiRun
|
||||
public long incr(final String key) {
|
||||
return incr(key, 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
@RpcMultiRun
|
||||
public CompletableFuture<Long> incrAsync(final String key) {
|
||||
return CompletableFuture.supplyAsync(() -> incr(key), getExecutor());
|
||||
}
|
||||
|
||||
@Override
|
||||
@RpcMultiRun
|
||||
public long incr(final String key, long num) {
|
||||
CacheEntry entry = container.get(key);
|
||||
if (entry == null) {
|
||||
synchronized (container) {
|
||||
entry = container.get(key);
|
||||
if (entry == null) {
|
||||
entry = new CacheEntry(CacheEntryType.ATOMIC, key, new AtomicLong(), null, null);
|
||||
container.put(key, entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
return ((AtomicLong) entry.objectValue).addAndGet(num);
|
||||
}
|
||||
|
||||
@Override
|
||||
@RpcMultiRun
|
||||
public CompletableFuture<Long> incrAsync(final String key, long num) {
|
||||
return CompletableFuture.supplyAsync(() -> incr(key, num), getExecutor());
|
||||
}
|
||||
|
||||
@Override
|
||||
@RpcMultiRun
|
||||
public long decr(final String key) {
|
||||
return incr(key, -1);
|
||||
}
|
||||
|
||||
@Override
|
||||
@RpcMultiRun
|
||||
public CompletableFuture<Long> decrAsync(final String key) {
|
||||
return CompletableFuture.supplyAsync(() -> decr(key), getExecutor());
|
||||
}
|
||||
|
||||
@Override
|
||||
@RpcMultiRun
|
||||
public long decr(final String key, long num) {
|
||||
return incr(key, -num);
|
||||
}
|
||||
|
||||
@Override
|
||||
@RpcMultiRun
|
||||
public CompletableFuture<Long> decrAsync(final String key, long num) {
|
||||
return CompletableFuture.supplyAsync(() -> decr(key, num), getExecutor());
|
||||
}
|
||||
|
||||
@Override
|
||||
@RpcMultiRun
|
||||
public CompletableFuture<Void> removeAsync(final String key) {
|
||||
return CompletableFuture.runAsync(() -> remove(key), getExecutor());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<V> getCollection(final K key) {
|
||||
public Collection<V> getCollection(final String key) {
|
||||
return (Collection<V>) get(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Collection<V>> getCollectionAsync(final K key) {
|
||||
public Collection<String> getStringCollection(final String key) {
|
||||
return (Collection<String>) get(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Long> getLongCollection(final String key) {
|
||||
return (Collection<Long>) get(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Collection<V>> getCollectionAsync(final String key) {
|
||||
return CompletableFuture.supplyAsync(() -> getCollection(key), getExecutor());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCollectionSize(final K key) {
|
||||
public CompletableFuture<Collection<String>> getStringCollectionAsync(final String key) {
|
||||
return CompletableFuture.supplyAsync(() -> getStringCollection(key), getExecutor());
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Collection<Long>> getLongCollectionAsync(final String key) {
|
||||
return CompletableFuture.supplyAsync(() -> getLongCollection(key), getExecutor());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCollectionSize(final String key) {
|
||||
Collection<V> collection = (Collection<V>) get(key);
|
||||
return collection == null ? 0 : collection.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Integer> getCollectionSizeAsync(final K key) {
|
||||
public CompletableFuture<Integer> getCollectionSizeAsync(final String key) {
|
||||
return CompletableFuture.supplyAsync(() -> getCollectionSize(key), getExecutor());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<V> getCollectionAndRefresh(final K key, final int expireSeconds) {
|
||||
@RpcMultiRun
|
||||
public Collection<V> getCollectionAndRefresh(final String key, final int expireSeconds) {
|
||||
return (Collection<V>) getAndRefresh(key, expireSeconds);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Collection<V>> getCollectionAndRefreshAsync(final K key, final int expireSeconds) {
|
||||
@RpcMultiRun
|
||||
public Collection<String> getStringCollectionAndRefresh(final String key, final int expireSeconds) {
|
||||
return (Collection<String>) getAndRefresh(key, expireSeconds);
|
||||
}
|
||||
|
||||
@Override
|
||||
@RpcMultiRun
|
||||
public Collection<Long> getLongCollectionAndRefresh(final String key, final int expireSeconds) {
|
||||
return (Collection<Long>) getAndRefresh(key, expireSeconds);
|
||||
}
|
||||
|
||||
@Override
|
||||
@RpcMultiRun
|
||||
public CompletableFuture<Collection<V>> getCollectionAndRefreshAsync(final String key, final int expireSeconds) {
|
||||
return CompletableFuture.supplyAsync(() -> getCollectionAndRefresh(key, expireSeconds), getExecutor());
|
||||
}
|
||||
|
||||
@Override
|
||||
@RpcMultiRun
|
||||
public void appendListItem(K key, V value) {
|
||||
public CompletableFuture<Collection<String>> getStringCollectionAndRefreshAsync(final String key, final int expireSeconds) {
|
||||
return CompletableFuture.supplyAsync(() -> getStringCollectionAndRefresh(key, expireSeconds), getExecutor());
|
||||
}
|
||||
|
||||
@Override
|
||||
@RpcMultiRun
|
||||
public CompletableFuture<Collection<Long>> getLongCollectionAndRefreshAsync(final String key, final int expireSeconds) {
|
||||
return CompletableFuture.supplyAsync(() -> getLongCollectionAndRefresh(key, expireSeconds), getExecutor());
|
||||
}
|
||||
|
||||
protected void appendListItem(CacheEntryType cacheType, String key, Object value) {
|
||||
if (key == null) return;
|
||||
CacheEntry entry = container.get(key);
|
||||
if (entry == null || !entry.isListCacheType() || entry.listValue == null) {
|
||||
ConcurrentLinkedQueue<V> list = new ConcurrentLinkedQueue();
|
||||
entry = new CacheEntry(CacheEntryType.LIST, key, null, null, list);
|
||||
ConcurrentLinkedQueue list = new ConcurrentLinkedQueue();
|
||||
entry = new CacheEntry(cacheType, key, null, null, list);
|
||||
CacheEntry old = container.putIfAbsent(key, entry);
|
||||
if (old != null) list = old.listValue;
|
||||
if (list != null) list.add(value);
|
||||
@@ -385,13 +706,44 @@ public class CacheMemorySource<K extends Serializable, V extends Object> extends
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> appendListItemAsync(final K key, final V value) {
|
||||
@RpcMultiRun
|
||||
public void appendListItem(String key, V value) {
|
||||
appendListItem(CacheEntryType.OBJECT_LIST, key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
@RpcMultiRun
|
||||
public void appendStringListItem(String key, String value) {
|
||||
appendListItem(CacheEntryType.STRING_LIST, key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
@RpcMultiRun
|
||||
public void appendLongListItem(String key, long value) {
|
||||
appendListItem(CacheEntryType.LONG_LIST, key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
@RpcMultiRun
|
||||
public CompletableFuture<Void> appendListItemAsync(final String key, final V value) {
|
||||
return CompletableFuture.runAsync(() -> appendListItem(key, value), getExecutor());
|
||||
}
|
||||
|
||||
@Override
|
||||
@RpcMultiRun
|
||||
public void removeListItem(K key, V value) {
|
||||
public CompletableFuture<Void> appendStringListItemAsync(final String key, final String value) {
|
||||
return CompletableFuture.runAsync(() -> appendStringListItem(key, value), getExecutor());
|
||||
}
|
||||
|
||||
@Override
|
||||
@RpcMultiRun
|
||||
public CompletableFuture<Void> appendLongListItemAsync(final String key, final long value) {
|
||||
return CompletableFuture.runAsync(() -> appendLongListItem(key, value), getExecutor());
|
||||
}
|
||||
|
||||
@Override
|
||||
@RpcMultiRun
|
||||
public void removeListItem(String key, V value) {
|
||||
if (key == null) return;
|
||||
CacheEntry entry = container.get(key);
|
||||
if (entry == null || entry.listValue == null) return;
|
||||
@@ -399,47 +751,138 @@ public class CacheMemorySource<K extends Serializable, V extends Object> extends
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> removeListItemAsync(final K key, final V value) {
|
||||
@RpcMultiRun
|
||||
public void removeStringListItem(String key, String value) {
|
||||
if (key == null) return;
|
||||
CacheEntry entry = container.get(key);
|
||||
if (entry == null || entry.listValue == null) return;
|
||||
entry.listValue.remove(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
@RpcMultiRun
|
||||
public void removeLongListItem(String key, long value) {
|
||||
if (key == null) return;
|
||||
CacheEntry entry = container.get(key);
|
||||
if (entry == null || entry.listValue == null) return;
|
||||
entry.listValue.remove(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
@RpcMultiRun
|
||||
public CompletableFuture<Void> removeListItemAsync(final String key, final V value) {
|
||||
return CompletableFuture.runAsync(() -> removeListItem(key, value), getExecutor());
|
||||
}
|
||||
|
||||
@Override
|
||||
@RpcMultiRun
|
||||
public void appendSetItem(K key, V value) {
|
||||
public CompletableFuture<Void> removeStringListItemAsync(final String key, final String value) {
|
||||
return CompletableFuture.runAsync(() -> removeStringListItem(key, value), getExecutor());
|
||||
}
|
||||
|
||||
@Override
|
||||
@RpcMultiRun
|
||||
public CompletableFuture<Void> removeLongListItemAsync(final String key, final long value) {
|
||||
return CompletableFuture.runAsync(() -> removeLongListItem(key, value), getExecutor());
|
||||
}
|
||||
|
||||
protected void appendSetItem(CacheEntryType cacheType, String key, Object value) {
|
||||
if (key == null) return;
|
||||
CacheEntry entry = container.get(key);
|
||||
if (entry == null || !entry.isSetCacheType() || entry.setValue == null) {
|
||||
CopyOnWriteArraySet<V> set = new CopyOnWriteArraySet();
|
||||
entry = new CacheEntry(CacheEntryType.SET, key, null, set, null);
|
||||
if (entry == null || !entry.isSetCacheType() || entry.csetValue == null) {
|
||||
CopyOnWriteArraySet set = new CopyOnWriteArraySet();
|
||||
entry = new CacheEntry(cacheType, key, null, set, null);
|
||||
CacheEntry old = container.putIfAbsent(key, entry);
|
||||
if (old != null) set = old.setValue;
|
||||
if (old != null) set = old.csetValue;
|
||||
if (set != null) set.add(value);
|
||||
} else {
|
||||
entry.setValue.add(value);
|
||||
entry.csetValue.add(value);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> appendSetItemAsync(final K key, final V value) {
|
||||
@RpcMultiRun
|
||||
public void appendSetItem(String key, V value) {
|
||||
appendSetItem(CacheEntryType.OBJECT_SET, key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
@RpcMultiRun
|
||||
public void appendStringSetItem(String key, String value) {
|
||||
appendSetItem(CacheEntryType.OBJECT_SET, key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
@RpcMultiRun
|
||||
public void appendLongSetItem(String key, long value) {
|
||||
appendSetItem(CacheEntryType.OBJECT_SET, key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
@RpcMultiRun
|
||||
public CompletableFuture<Void> appendSetItemAsync(final String key, final V value) {
|
||||
return CompletableFuture.runAsync(() -> appendSetItem(key, value), getExecutor());
|
||||
}
|
||||
|
||||
@Override
|
||||
@RpcMultiRun
|
||||
public void removeSetItem(K key, V value) {
|
||||
if (key == null) return;
|
||||
CacheEntry entry = container.get(key);
|
||||
if (entry == null || entry.setValue == null) return;
|
||||
entry.setValue.remove(value);
|
||||
public CompletableFuture<Void> appendStringSetItemAsync(final String key, final String value) {
|
||||
return CompletableFuture.runAsync(() -> appendStringSetItem(key, value), getExecutor());
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> removeSetItemAsync(final K key, final V value) {
|
||||
@RpcMultiRun
|
||||
public CompletableFuture<Void> appendLongSetItemAsync(final String key, final long value) {
|
||||
return CompletableFuture.runAsync(() -> appendLongSetItem(key, value), getExecutor());
|
||||
}
|
||||
|
||||
@Override
|
||||
@RpcMultiRun
|
||||
public void removeSetItem(String key, V value) {
|
||||
if (key == null) return;
|
||||
CacheEntry entry = container.get(key);
|
||||
if (entry == null || entry.csetValue == null) return;
|
||||
entry.csetValue.remove(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
@RpcMultiRun
|
||||
public void removeStringSetItem(String key, String value) {
|
||||
if (key == null) return;
|
||||
CacheEntry entry = container.get(key);
|
||||
if (entry == null || entry.csetValue == null) return;
|
||||
entry.csetValue.remove(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
@RpcMultiRun
|
||||
public void removeLongSetItem(String key, long value) {
|
||||
if (key == null) return;
|
||||
CacheEntry entry = container.get(key);
|
||||
if (entry == null || entry.csetValue == null) return;
|
||||
entry.csetValue.remove(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
@RpcMultiRun
|
||||
public CompletableFuture<Void> removeSetItemAsync(final String key, final V value) {
|
||||
return CompletableFuture.runAsync(() -> removeSetItem(key, value), getExecutor());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<K> queryKeys() {
|
||||
@RpcMultiRun
|
||||
public CompletableFuture<Void> removeStringSetItemAsync(final String key, final String value) {
|
||||
return CompletableFuture.runAsync(() -> removeStringSetItem(key, value), getExecutor());
|
||||
}
|
||||
|
||||
@Override
|
||||
@RpcMultiRun
|
||||
public CompletableFuture<Void> removeLongSetItemAsync(final String key, final long value) {
|
||||
return CompletableFuture.runAsync(() -> removeLongSetItem(key, value), getExecutor());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> queryKeys() {
|
||||
return new ArrayList<>(container.keySet());
|
||||
}
|
||||
|
||||
@@ -449,17 +892,17 @@ public class CacheMemorySource<K extends Serializable, V extends Object> extends
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<List<CacheEntry<K, Object>>> queryListAsync() {
|
||||
public CompletableFuture<List<CacheEntry<Object>>> queryListAsync() {
|
||||
return CompletableFuture.completedFuture(new ArrayList<>(container.values()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CacheEntry<K, Object>> queryList() {
|
||||
public List<CacheEntry< Object>> queryList() {
|
||||
return new ArrayList<>(container.values());
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<List<K>> queryKeysAsync() {
|
||||
public CompletableFuture<List<String>> queryKeysAsync() {
|
||||
return CompletableFuture.completedFuture(new ArrayList<>(container.keySet()));
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
package org.redkale.source;
|
||||
|
||||
import java.beans.ConstructorProperties;
|
||||
import java.io.*;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.*;
|
||||
import org.redkale.convert.ConvertColumn;
|
||||
@@ -14,109 +14,208 @@ import org.redkale.convert.json.JsonFactory;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param <K> key的类型
|
||||
* @param <V> value的类型
|
||||
* <p>
|
||||
* 详情见: https://redkale.org
|
||||
*
|
||||
* @author zhangjx
|
||||
*/
|
||||
public interface CacheSource<K extends Serializable, V extends Object> {
|
||||
public interface CacheSource<V extends Object> {
|
||||
|
||||
public String getType();
|
||||
|
||||
public void initValueType(Type valueType);
|
||||
|
||||
public void initTransient(boolean flag);
|
||||
|
||||
default boolean isOpen() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean exists(final K key);
|
||||
public boolean exists(final String key);
|
||||
|
||||
public V get(final K key);
|
||||
public V get(final String key);
|
||||
|
||||
public V getAndRefresh(final K key, final int expireSeconds);
|
||||
public V getAndRefresh(final String key, final int expireSeconds);
|
||||
|
||||
public void refresh(final K key, final int expireSeconds);
|
||||
public void refresh(final String key, final int expireSeconds);
|
||||
|
||||
public void set(final K key, final V value);
|
||||
public void set(final String key, final V value);
|
||||
|
||||
public void set(final int expireSeconds, final K key, final V value);
|
||||
public void set(final int expireSeconds, final String key, final V value);
|
||||
|
||||
public void setExpireSeconds(final K key, final int expireSeconds);
|
||||
public void setExpireSeconds(final String key, final int expireSeconds);
|
||||
|
||||
public void remove(final K key);
|
||||
public void remove(final String key);
|
||||
|
||||
public Collection<V> getCollection(final K key);
|
||||
public long incr(final String key);
|
||||
|
||||
public int getCollectionSize(final K key);
|
||||
public long incr(final String key, long num);
|
||||
|
||||
public Collection<V> getCollectionAndRefresh(final K key, final int expireSeconds);
|
||||
public long decr(final String key);
|
||||
|
||||
public void appendListItem(final K key, final V value);
|
||||
public long decr(final String key, long num);
|
||||
|
||||
public void removeListItem(final K key, final V value);
|
||||
public Collection<V> getCollection(final String key);
|
||||
|
||||
public void appendSetItem(final K key, final V value);
|
||||
public int getCollectionSize(final String key);
|
||||
|
||||
public void removeSetItem(final K key, final V value);
|
||||
public Collection<V> getCollectionAndRefresh(final String key, final int expireSeconds);
|
||||
|
||||
public List<K> queryKeys();
|
||||
public void appendListItem(final String key, final V value);
|
||||
|
||||
public void removeListItem(final String key, final V value);
|
||||
|
||||
public void appendSetItem(final String key, final V value);
|
||||
|
||||
public void removeSetItem(final String key, final V value);
|
||||
|
||||
public List<String> queryKeys();
|
||||
|
||||
public int getKeySize();
|
||||
|
||||
public List<CacheEntry<K, Object>> queryList();
|
||||
public List<CacheEntry<Object>> queryList();
|
||||
|
||||
public String getString(final String key);
|
||||
|
||||
public String getStringAndRefresh(final String key, final int expireSeconds);
|
||||
|
||||
public void setString(final String key, final String value);
|
||||
|
||||
public void setString(final int expireSeconds, final String key, final String value);
|
||||
|
||||
public Collection<String> getStringCollection(final String key);
|
||||
|
||||
public Collection<String> getStringCollectionAndRefresh(final String key, final int expireSeconds);
|
||||
|
||||
public void appendStringListItem(final String key, final String value);
|
||||
|
||||
public void removeStringListItem(final String key, final String value);
|
||||
|
||||
public void appendStringSetItem(final String key, final String value);
|
||||
|
||||
public void removeStringSetItem(final String key, final String value);
|
||||
|
||||
public long getLong(final String key, long defValue);
|
||||
|
||||
public long getLongAndRefresh(final String key, final int expireSeconds, long defValue);
|
||||
|
||||
public void setLong(final String key, final long value);
|
||||
|
||||
public void setLong(final int expireSeconds, final String key, final long value);
|
||||
|
||||
public Collection<Long> getLongCollection(final String key);
|
||||
|
||||
public Collection<Long> getLongCollectionAndRefresh(final String key, final int expireSeconds);
|
||||
|
||||
public void appendLongListItem(final String key, final long value);
|
||||
|
||||
public void removeLongListItem(final String key, final long value);
|
||||
|
||||
public void appendLongSetItem(final String key, final long value);
|
||||
|
||||
public void removeLongSetItem(final String key, final long value);
|
||||
|
||||
//---------------------- CompletableFuture 异步版 ---------------------------------
|
||||
public CompletableFuture<Boolean> existsAsync(final K key);
|
||||
public CompletableFuture<Boolean> existsAsync(final String key);
|
||||
|
||||
public CompletableFuture<V> getAsync(final K key);
|
||||
public CompletableFuture<V> getAsync(final String key);
|
||||
|
||||
public CompletableFuture<V> getAndRefreshAsync(final K key, final int expireSeconds);
|
||||
public CompletableFuture<V> getAndRefreshAsync(final String key, final int expireSeconds);
|
||||
|
||||
public CompletableFuture<Void> refreshAsync(final K key, final int expireSeconds);
|
||||
public CompletableFuture<Void> refreshAsync(final String key, final int expireSeconds);
|
||||
|
||||
public CompletableFuture<Void> setAsync(final K key, final V value);
|
||||
public CompletableFuture<Void> setAsync(final String key, final V value);
|
||||
|
||||
public CompletableFuture<Void> setAsync(final int expireSeconds, final K key, final V value);
|
||||
public CompletableFuture<Void> setAsync(final int expireSeconds, final String key, final V value);
|
||||
|
||||
public CompletableFuture<Void> setExpireSecondsAsync(final K key, final int expireSeconds);
|
||||
public CompletableFuture<Void> setExpireSecondsAsync(final String key, final int expireSeconds);
|
||||
|
||||
public CompletableFuture<Void> removeAsync(final K key);
|
||||
public CompletableFuture<Void> removeAsync(final String key);
|
||||
|
||||
public CompletableFuture<Collection<V>> getCollectionAsync(final K key);
|
||||
public CompletableFuture<Long> incrAsync(final String key);
|
||||
|
||||
public CompletableFuture<Integer> getCollectionSizeAsync(final K key);
|
||||
public CompletableFuture<Long> incrAsync(final String key, long num);
|
||||
|
||||
public CompletableFuture<Collection<V>> getCollectionAndRefreshAsync(final K key, final int expireSeconds);
|
||||
public CompletableFuture<Long> decrAsync(final String key);
|
||||
|
||||
public CompletableFuture<Void> appendListItemAsync(final K key, final V value);
|
||||
public CompletableFuture<Long> decrAsync(final String key, long num);
|
||||
|
||||
public CompletableFuture<Void> removeListItemAsync(final K key, final V value);
|
||||
public CompletableFuture<Collection<V>> getCollectionAsync(final String key);
|
||||
|
||||
public CompletableFuture<Void> appendSetItemAsync(final K key, final V value);
|
||||
public CompletableFuture<Integer> getCollectionSizeAsync(final String key);
|
||||
|
||||
public CompletableFuture<Void> removeSetItemAsync(final K key, final V value);
|
||||
public CompletableFuture<Collection<V>> getCollectionAndRefreshAsync(final String key, final int expireSeconds);
|
||||
|
||||
public CompletableFuture<List<K>> queryKeysAsync();
|
||||
public CompletableFuture<Void> appendListItemAsync(final String key, final V value);
|
||||
|
||||
public CompletableFuture<Void> removeListItemAsync(final String key, final V value);
|
||||
|
||||
public CompletableFuture<Void> appendSetItemAsync(final String key, final V value);
|
||||
|
||||
public CompletableFuture<Void> removeSetItemAsync(final String key, final V value);
|
||||
|
||||
public CompletableFuture<List<String>> queryKeysAsync();
|
||||
|
||||
public CompletableFuture<Integer> getKeySizeAsync();
|
||||
|
||||
public CompletableFuture<List<CacheEntry<K, Object>>> queryListAsync();
|
||||
public CompletableFuture<List<CacheEntry< Object>>> queryListAsync();
|
||||
|
||||
public CompletableFuture<String> getStringAsync(final String key);
|
||||
|
||||
public CompletableFuture<String> getStringAndRefreshAsync(final String key, final int expireSeconds);
|
||||
|
||||
public CompletableFuture<Void> setStringAsync(final String key, final String value);
|
||||
|
||||
public CompletableFuture<Void> setStringAsync(final int expireSeconds, final String key, final String value);
|
||||
|
||||
public CompletableFuture<Collection<String>> getStringCollectionAsync(final String key);
|
||||
|
||||
public CompletableFuture<Collection<String>> getStringCollectionAndRefreshAsync(final String key, final int expireSeconds);
|
||||
|
||||
public CompletableFuture<Void> appendStringListItemAsync(final String key, final String value);
|
||||
|
||||
public CompletableFuture<Void> removeStringListItemAsync(final String key, final String value);
|
||||
|
||||
public CompletableFuture<Void> appendStringSetItemAsync(final String key, final String value);
|
||||
|
||||
public CompletableFuture<Void> removeStringSetItemAsync(final String key, final String value);
|
||||
|
||||
public CompletableFuture<Long> getLongAsync(final String key, long defValue);
|
||||
|
||||
public CompletableFuture<Long> getLongAndRefreshAsync(final String key, final int expireSeconds, long defValue);
|
||||
|
||||
public CompletableFuture<Void> setLongAsync(final String key, long value);
|
||||
|
||||
public CompletableFuture<Void> setLongAsync(final int expireSeconds, final String key, final long value);
|
||||
|
||||
public CompletableFuture<Collection<Long>> getLongCollectionAsync(final String key);
|
||||
|
||||
public CompletableFuture<Collection<Long>> getLongCollectionAndRefreshAsync(final String key, final int expireSeconds);
|
||||
|
||||
public CompletableFuture<Void> appendLongListItemAsync(final String key, final long value);
|
||||
|
||||
public CompletableFuture<Void> removeLongListItemAsync(final String key, final long value);
|
||||
|
||||
public CompletableFuture<Void> appendLongSetItemAsync(final String key, final long value);
|
||||
|
||||
public CompletableFuture<Void> removeLongSetItemAsync(final String key, final long value);
|
||||
|
||||
default CompletableFuture<Boolean> isOpenAsync() {
|
||||
return CompletableFuture.completedFuture(true);
|
||||
return CompletableFuture.completedFuture(isOpen());
|
||||
}
|
||||
|
||||
public static enum CacheEntryType {
|
||||
OBJECT, SET, LIST;
|
||||
LONG, STRING, OBJECT, ATOMIC,
|
||||
LONG_SET, STRING_SET, OBJECT_SET,
|
||||
LONG_LIST, STRING_LIST, OBJECT_LIST;
|
||||
}
|
||||
|
||||
public static final class CacheEntry<K extends Serializable, T> {
|
||||
|
||||
static final String JSON_SET_KEY = "{\"cacheType\":\"" + CacheEntryType.SET + "\"";
|
||||
|
||||
static final String JSON_LIST_KEY = "{\"cacheType\":\"" + CacheEntryType.LIST + "\"";
|
||||
public static final class CacheEntry<T> {
|
||||
|
||||
final CacheEntryType cacheType;
|
||||
|
||||
final K key;
|
||||
final String key;
|
||||
|
||||
//<=0表示永久保存
|
||||
int expireSeconds;
|
||||
@@ -125,26 +224,26 @@ public interface CacheSource<K extends Serializable, V extends Object> {
|
||||
|
||||
T objectValue;
|
||||
|
||||
CopyOnWriteArraySet<T> setValue;
|
||||
CopyOnWriteArraySet<T> csetValue;
|
||||
|
||||
ConcurrentLinkedQueue<T> listValue;
|
||||
|
||||
public CacheEntry(CacheEntryType cacheType, K key, T objectValue, CopyOnWriteArraySet<T> setValue, ConcurrentLinkedQueue<T> listValue) {
|
||||
this(cacheType, 0, key, objectValue, setValue, listValue);
|
||||
public CacheEntry(CacheEntryType cacheType, String key, T objectValue, CopyOnWriteArraySet<T> csetValue, ConcurrentLinkedQueue<T> listValue) {
|
||||
this(cacheType, 0, key, objectValue, csetValue, listValue);
|
||||
}
|
||||
|
||||
public CacheEntry(CacheEntryType cacheType, int expireSeconds, K key, T objectValue, CopyOnWriteArraySet<T> setValue, ConcurrentLinkedQueue<T> listValue) {
|
||||
this(cacheType, expireSeconds, (int) (System.currentTimeMillis() / 1000), key, objectValue, setValue, listValue);
|
||||
public CacheEntry(CacheEntryType cacheType, int expireSeconds, String key, T objectValue, CopyOnWriteArraySet<T> csetValue, ConcurrentLinkedQueue<T> listValue) {
|
||||
this(cacheType, expireSeconds, (int) (System.currentTimeMillis() / 1000), key, objectValue, csetValue, listValue);
|
||||
}
|
||||
|
||||
@ConstructorProperties({"cacheType", "expireSeconds", "lastAccessed", "key", "objectValue", "setValue", "listValue"})
|
||||
public CacheEntry(CacheEntryType cacheType, int expireSeconds, int lastAccessed, K key, T objectValue, CopyOnWriteArraySet<T> setValue, ConcurrentLinkedQueue<T> listValue) {
|
||||
@ConstructorProperties({"cacheType", "expireSeconds", "lastAccessed", "key", "objectValue", "csetValue", "listValue"})
|
||||
public CacheEntry(CacheEntryType cacheType, int expireSeconds, int lastAccessed, String key, T objectValue, CopyOnWriteArraySet<T> csetValue, ConcurrentLinkedQueue<T> listValue) {
|
||||
this.cacheType = cacheType;
|
||||
this.expireSeconds = expireSeconds;
|
||||
this.lastAccessed = lastAccessed;
|
||||
this.key = key;
|
||||
this.objectValue = objectValue;
|
||||
this.setValue = setValue;
|
||||
this.csetValue = csetValue;
|
||||
this.listValue = listValue;
|
||||
}
|
||||
|
||||
@@ -155,12 +254,12 @@ public interface CacheSource<K extends Serializable, V extends Object> {
|
||||
|
||||
@ConvertColumn(ignore = true)
|
||||
public boolean isListCacheType() {
|
||||
return cacheType == CacheEntryType.LIST;
|
||||
return cacheType == CacheEntryType.LONG_LIST || cacheType == CacheEntryType.STRING_LIST || cacheType == CacheEntryType.OBJECT_LIST;
|
||||
}
|
||||
|
||||
@ConvertColumn(ignore = true)
|
||||
public boolean isSetCacheType() {
|
||||
return cacheType == CacheEntryType.SET;
|
||||
return cacheType == CacheEntryType.LONG_SET || cacheType == CacheEntryType.STRING_SET || cacheType == CacheEntryType.OBJECT_SET;
|
||||
}
|
||||
|
||||
@ConvertColumn(ignore = true)
|
||||
@@ -180,7 +279,7 @@ public interface CacheSource<K extends Serializable, V extends Object> {
|
||||
return lastAccessed;
|
||||
}
|
||||
|
||||
public K getKey() {
|
||||
public String getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
@@ -188,8 +287,8 @@ public interface CacheSource<K extends Serializable, V extends Object> {
|
||||
return objectValue;
|
||||
}
|
||||
|
||||
public CopyOnWriteArraySet<T> getSetValue() {
|
||||
return setValue;
|
||||
public CopyOnWriteArraySet<T> getCsetValue() {
|
||||
return csetValue;
|
||||
}
|
||||
|
||||
public ConcurrentLinkedQueue<T> getListValue() {
|
||||
|
||||
@@ -10,7 +10,6 @@ import java.net.URL;
|
||||
import java.sql.*;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.function.*;
|
||||
import java.util.logging.*;
|
||||
import java.util.stream.Stream;
|
||||
@@ -37,17 +36,15 @@ public class DataJdbcSource extends AbstractService implements DataSource, DataC
|
||||
|
||||
protected final Logger logger = Logger.getLogger(DataJdbcSource.class.getSimpleName());
|
||||
|
||||
protected final AtomicBoolean debug = new AtomicBoolean(logger.isLoggable(Level.FINEST));
|
||||
protected String name;
|
||||
|
||||
protected final String name;
|
||||
protected URL conf;
|
||||
|
||||
protected final URL conf;
|
||||
protected boolean cacheForbidden;
|
||||
|
||||
protected final boolean cacheForbidden;
|
||||
protected PoolJdbcSource readPool;
|
||||
|
||||
protected final PoolJdbcSource readPool;
|
||||
|
||||
protected final PoolJdbcSource writePool;
|
||||
protected PoolJdbcSource writePool;
|
||||
|
||||
@Resource(name = "$")
|
||||
protected DataCacheListener cacheListener;
|
||||
@@ -55,6 +52,44 @@ public class DataJdbcSource extends AbstractService implements DataSource, DataC
|
||||
protected final BiFunction<DataSource, Class, List> fullloader = (s, t) -> querySheet(false, false, t, null, null, (FilterNode) null).list(true);
|
||||
|
||||
public DataJdbcSource(String unitName, Properties readprop, Properties writeprop) {
|
||||
this.preConstruct(unitName, readprop, writeprop);
|
||||
this.initByProperties(unitName, readprop, writeprop);
|
||||
}
|
||||
|
||||
public DataJdbcSource() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(AnyValue config) { //通过空构造函数创建的对象需要调用init方法进行初始化
|
||||
String unitName = config.getValue("name");
|
||||
Properties readprop = new Properties();
|
||||
Properties writeprop = new Properties();
|
||||
|
||||
for (AnyValue confs : config.getAnyValues("properties")) {
|
||||
boolean write = confs.getValue("name", "").contains("write");
|
||||
for (AnyValue conf : confs.getAnyValues("property")) {
|
||||
String pn = conf.getValue("name");
|
||||
String pv = conf.getValue("value");
|
||||
if (pn == null || pv == null) continue;
|
||||
(write ? writeprop : readprop).put(pn, pv);
|
||||
}
|
||||
}
|
||||
|
||||
for (AnyValue conf : config.getAnyValues("property")) {
|
||||
String pn = conf.getValue("name");
|
||||
String pv = conf.getValue("value");
|
||||
if (pn == null || pv == null) continue;
|
||||
readprop.put(pn, pv);
|
||||
}
|
||||
if (writeprop.isEmpty()) writeprop = readprop;
|
||||
this.initByProperties(unitName, readprop, writeprop);
|
||||
}
|
||||
|
||||
//构造前调用
|
||||
protected void preConstruct(String unitName, Properties readprop, Properties writeprop) {
|
||||
}
|
||||
|
||||
protected void initByProperties(String unitName, Properties readprop, Properties writeprop) {
|
||||
this.name = unitName;
|
||||
this.conf = null;
|
||||
this.readPool = new PoolJdbcSource(this, "read", readprop);
|
||||
@@ -62,6 +97,12 @@ public class DataJdbcSource extends AbstractService implements DataSource, DataC
|
||||
this.cacheForbidden = "NONE".equalsIgnoreCase(readprop.getProperty(JDBC_CACHE_MODE));
|
||||
}
|
||||
|
||||
@Local
|
||||
@Override
|
||||
public String getType() {
|
||||
return "jdbc";
|
||||
}
|
||||
|
||||
@Override
|
||||
public final String resourceName() {
|
||||
return name;
|
||||
@@ -73,15 +114,23 @@ public class DataJdbcSource extends AbstractService implements DataSource, DataC
|
||||
writePool.close();
|
||||
}
|
||||
|
||||
protected Connection createReadSQLConnection() {
|
||||
public PoolJdbcSource getReadPoolJdbcSource() {
|
||||
return readPool;
|
||||
}
|
||||
|
||||
public PoolJdbcSource getWritePoolJdbcSource() {
|
||||
return writePool;
|
||||
}
|
||||
|
||||
public Connection createReadSQLConnection() {
|
||||
return readPool.poll();
|
||||
}
|
||||
|
||||
protected <T> Connection createWriteSQLConnection() {
|
||||
public <T> Connection createWriteSQLConnection() {
|
||||
return writePool.poll();
|
||||
}
|
||||
|
||||
protected void closeSQLConnection(final Connection sqlconn) {
|
||||
public void closeSQLConnection(final Connection sqlconn) {
|
||||
if (sqlconn == null) return;
|
||||
try {
|
||||
sqlconn.close();
|
||||
@@ -219,7 +268,7 @@ public class DataJdbcSource extends AbstractService implements DataSource, DataC
|
||||
}
|
||||
prestmt.close();
|
||||
//------------------------------------------------------------
|
||||
if (debug.get() && info.isLoggable(Level.FINEST)) { //打印调试信息
|
||||
if (info.isLoggable(logger, Level.FINEST)) { //打印调试信息
|
||||
char[] sqlchars = sql.toCharArray();
|
||||
for (final T value : values) {
|
||||
//-----------------------------
|
||||
@@ -370,7 +419,7 @@ public class DataJdbcSource extends AbstractService implements DataSource, DataC
|
||||
final Statement stmt = conn.createStatement();
|
||||
for (Serializable key : keys) {
|
||||
String sql = "DELETE FROM " + info.getTable(key) + " WHERE " + info.getPrimarySQLColumn() + " = " + FilterNode.formatToString(key);
|
||||
if (debug.get() && info.isLoggable(Level.FINEST)) logger.finest(info.getType().getSimpleName() + " delete sql=" + sql);
|
||||
if (info.isLoggable(logger, Level.FINEST)) logger.finest(info.getType().getSimpleName() + " delete sql=" + sql);
|
||||
stmt.addBatch(sql);
|
||||
}
|
||||
int[] pc = stmt.executeBatch();
|
||||
@@ -451,7 +500,7 @@ public class DataJdbcSource extends AbstractService implements DataSource, DataC
|
||||
+ ((where == null || where.length() == 0) ? (join2 == null ? "" : (" WHERE " + join2))
|
||||
: (" WHERE " + where + (join2 == null ? "" : (" AND " + join2)))) + info.createSQLOrderby(flipper)
|
||||
+ ((flipper == null || flipper.getLimit() < 1) ? "" : (" LIMIT " + flipper.getLimit()));
|
||||
if (debug.get() && info.isLoggable(Level.FINEST)) logger.finest(info.getType().getSimpleName() + " delete sql=" + sql);
|
||||
if (info.isLoggable(logger, Level.FINEST)) logger.finest(info.getType().getSimpleName() + " delete sql=" + sql);
|
||||
conn.setReadOnly(false);
|
||||
final Statement stmt = conn.createStatement();
|
||||
c = stmt.executeUpdate(sql);
|
||||
@@ -533,7 +582,7 @@ public class DataJdbcSource extends AbstractService implements DataSource, DataC
|
||||
conn.setReadOnly(false);
|
||||
final PreparedStatement prestmt = conn.prepareStatement(updateSQL);
|
||||
Attribute<T, Serializable>[] attrs = info.updateAttributes;
|
||||
final boolean debugfinest = debug.get() && info.isLoggable(Level.FINEST);
|
||||
final boolean debugfinest = info.isLoggable(logger, Level.FINEST);
|
||||
char[] sqlchars = debugfinest ? updateSQL.toCharArray() : null;
|
||||
for (final T value : values) {
|
||||
int k = 0;
|
||||
@@ -625,7 +674,7 @@ public class DataJdbcSource extends AbstractService implements DataSource, DataC
|
||||
if (!info.isVirtualEntity()) {
|
||||
if (value instanceof byte[]) {
|
||||
String sql = "UPDATE " + info.getTable(id) + " SET " + info.getSQLColumn(null, column) + " = ? WHERE " + info.getPrimarySQLColumn() + " = " + FilterNode.formatToString(id);
|
||||
if (debug.get() && info.isLoggable(Level.FINEST)) logger.finest(info.getType().getSimpleName() + " update sql=" + sql);
|
||||
if (info.isLoggable(logger, Level.FINEST)) logger.finest(info.getType().getSimpleName() + " update sql=" + sql);
|
||||
conn.setReadOnly(false);
|
||||
final PreparedStatement stmt = conn.prepareStatement(sql);
|
||||
Blob blob = conn.createBlob();
|
||||
@@ -636,7 +685,7 @@ public class DataJdbcSource extends AbstractService implements DataSource, DataC
|
||||
} else {
|
||||
String sql = "UPDATE " + info.getTable(id) + " SET " + info.getSQLColumn(null, column) + " = "
|
||||
+ info.formatToString(value) + " WHERE " + info.getPrimarySQLColumn() + " = " + FilterNode.formatToString(id);
|
||||
if (debug.get() && info.isLoggable(Level.FINEST)) logger.finest(info.getType().getSimpleName() + " update sql=" + sql);
|
||||
if (info.isLoggable(logger, Level.FINEST)) logger.finest(info.getType().getSimpleName() + " update sql=" + sql);
|
||||
conn.setReadOnly(false);
|
||||
final Statement stmt = conn.createStatement();
|
||||
c = stmt.executeUpdate(sql);
|
||||
@@ -706,7 +755,7 @@ public class DataJdbcSource extends AbstractService implements DataSource, DataC
|
||||
+ " SET " + info.getSQLColumn("a", column) + " = ?"
|
||||
+ ((where == null || where.length() == 0) ? (join2 == null ? "" : (" WHERE " + join2))
|
||||
: (" WHERE " + where + (join2 == null ? "" : (" AND " + join2))));
|
||||
if (debug.get() && info.isLoggable(Level.FINEST)) logger.finest(info.getType().getSimpleName() + " update sql=" + sql);
|
||||
if (info.isLoggable(logger, Level.FINEST)) logger.finest(info.getType().getSimpleName() + " update sql=" + sql);
|
||||
conn.setReadOnly(false);
|
||||
Blob blob = conn.createBlob();
|
||||
blob.setBytes(1, (byte[]) value);
|
||||
@@ -719,7 +768,7 @@ public class DataJdbcSource extends AbstractService implements DataSource, DataC
|
||||
+ " SET " + info.getSQLColumn("a", column) + " = " + info.formatToString(value)
|
||||
+ ((where == null || where.length() == 0) ? (join2 == null ? "" : (" WHERE " + join2))
|
||||
: (" WHERE " + where + (join2 == null ? "" : (" AND " + join2))));
|
||||
if (debug.get() && info.isLoggable(Level.FINEST)) logger.finest(info.getType().getSimpleName() + " update sql=" + sql);
|
||||
if (info.isLoggable(logger, Level.FINEST)) logger.finest(info.getType().getSimpleName() + " update sql=" + sql);
|
||||
conn.setReadOnly(false);
|
||||
final Statement stmt = conn.createStatement();
|
||||
c = stmt.executeUpdate(sql);
|
||||
@@ -796,7 +845,7 @@ public class DataJdbcSource extends AbstractService implements DataSource, DataC
|
||||
int c = -1;
|
||||
if (!virtual) {
|
||||
String sql = "UPDATE " + info.getTable(id) + " SET " + setsql + " WHERE " + info.getPrimarySQLColumn() + " = " + FilterNode.formatToString(id);
|
||||
if (debug.get() && info.isLoggable(Level.FINEST)) logger.finest(info.getType().getSimpleName() + ": " + sql);
|
||||
if (info.isLoggable(logger, Level.FINEST)) logger.finest(info.getType().getSimpleName() + ": " + sql);
|
||||
conn.setReadOnly(false);
|
||||
if (blobs != null) {
|
||||
final PreparedStatement stmt = conn.prepareStatement(sql);
|
||||
@@ -926,7 +975,7 @@ public class DataJdbcSource extends AbstractService implements DataSource, DataC
|
||||
: (" WHERE " + where + (join2 == null ? "" : (" AND " + join2))));
|
||||
//注:LIMIT 仅支持MySQL 且在多表关联式会异常, 该BUG尚未解决
|
||||
sql += info.createSQLOrderby(flipper) + ((flipper == null || flipper.getLimit() < 1) ? "" : (" LIMIT " + flipper.getLimit()));
|
||||
if (debug.get() && info.isLoggable(Level.FINEST)) logger.finest(info.getType().getSimpleName() + " update sql=" + sql);
|
||||
if (info.isLoggable(logger, Level.FINEST)) logger.finest(info.getType().getSimpleName() + " update sql=" + sql);
|
||||
conn.setReadOnly(false);
|
||||
if (blobs != null) {
|
||||
final PreparedStatement stmt = conn.prepareStatement(sql);
|
||||
@@ -1022,7 +1071,7 @@ public class DataJdbcSource extends AbstractService implements DataSource, DataC
|
||||
int c = -1;
|
||||
if (!virtual) {
|
||||
String sql = "UPDATE " + info.getTable(id) + " SET " + setsql + " WHERE " + info.getPrimarySQLColumn() + " = " + FilterNode.formatToString(id);
|
||||
if (debug.get() && info.isLoggable(Level.FINEST)) logger.finest(bean.getClass().getSimpleName() + ": " + sql);
|
||||
if (info.isLoggable(logger, Level.FINEST)) logger.finest(bean.getClass().getSimpleName() + ": " + sql);
|
||||
conn.setReadOnly(false);
|
||||
if (blobs != null) {
|
||||
final PreparedStatement stmt = conn.prepareStatement(sql);
|
||||
@@ -1110,7 +1159,7 @@ public class DataJdbcSource extends AbstractService implements DataSource, DataC
|
||||
String sql = "UPDATE " + info.getTable(node) + " a " + (join1 == null ? "" : (", " + join1)) + " SET " + setsql
|
||||
+ ((where == null || where.length() == 0) ? (join2 == null ? "" : (" WHERE " + join2))
|
||||
: (" WHERE " + where + (join2 == null ? "" : (" AND " + join2))));
|
||||
if (debug.get() && info.isLoggable(Level.FINEST)) logger.finest(info.getType().getSimpleName() + " update sql=" + sql);
|
||||
if (info.isLoggable(logger, Level.FINEST)) logger.finest(info.getType().getSimpleName() + " update sql=" + sql);
|
||||
conn.setReadOnly(false);
|
||||
if (blobs != null) {
|
||||
final PreparedStatement stmt = conn.prepareStatement(sql);
|
||||
@@ -1273,7 +1322,7 @@ public class DataJdbcSource extends AbstractService implements DataSource, DataC
|
||||
}
|
||||
final String sql = "SELECT " + sb + " FROM " + info.getTable(node) + " a"
|
||||
+ (join == null ? "" : join) + ((where == null || where.length() == 0) ? "" : (" WHERE " + where));
|
||||
if (debug.get() && info.isLoggable(Level.FINEST)) logger.finest(entityClass.getSimpleName() + " single sql=" + sql);
|
||||
if (info.isLoggable(logger, Level.FINEST)) logger.finest(entityClass.getSimpleName() + " single sql=" + sql);
|
||||
conn.setReadOnly(true);
|
||||
final PreparedStatement prestmt = conn.prepareStatement(sql);
|
||||
|
||||
@@ -1329,7 +1378,7 @@ public class DataJdbcSource extends AbstractService implements DataSource, DataC
|
||||
final CharSequence where = node == null ? null : node.createSQLExpress(info, joinTabalis);
|
||||
final String sql = "SELECT " + func.getColumn((column == null || column.isEmpty() ? "*" : info.getSQLColumn("a", column))) + " FROM " + info.getTable(node) + " a"
|
||||
+ (join == null ? "" : join) + ((where == null || where.length() == 0) ? "" : (" WHERE " + where));
|
||||
if (debug.get() && info.isLoggable(Level.FINEST)) logger.finest(entityClass.getSimpleName() + " single sql=" + sql);
|
||||
if (info.isLoggable(logger, Level.FINEST)) logger.finest(entityClass.getSimpleName() + " single sql=" + sql);
|
||||
conn.setReadOnly(true);
|
||||
final PreparedStatement prestmt = conn.prepareStatement(sql);
|
||||
Number rs = defVal;
|
||||
@@ -1388,7 +1437,7 @@ public class DataJdbcSource extends AbstractService implements DataSource, DataC
|
||||
final CharSequence where = node == null ? null : node.createSQLExpress(info, joinTabalis);
|
||||
final String sql = "SELECT a." + sqlkey + ", " + func.getColumn((funcColumn == null || funcColumn.isEmpty() ? "*" : info.getSQLColumn("a", funcColumn)))
|
||||
+ " FROM " + info.getTable(node) + " a" + (join == null ? "" : join) + ((where == null || where.length() == 0) ? "" : (" WHERE " + where)) + " GROUP BY a." + sqlkey;
|
||||
if (debug.get() && info.isLoggable(Level.FINEST)) logger.finest(entityClass.getSimpleName() + " single sql=" + sql);
|
||||
if (info.isLoggable(logger, Level.FINEST)) logger.finest(entityClass.getSimpleName() + " single sql=" + sql);
|
||||
conn.setReadOnly(true);
|
||||
final PreparedStatement prestmt = conn.prepareStatement(sql);
|
||||
Map<K, N> rs = new LinkedHashMap<>();
|
||||
@@ -1446,8 +1495,8 @@ public class DataJdbcSource extends AbstractService implements DataSource, DataC
|
||||
final Connection conn = createReadSQLConnection();
|
||||
try {
|
||||
final SelectColumn sels = selects;
|
||||
final String sql = "SELECT * FROM " + info.getTable(pk) + " WHERE " + info.getPrimarySQLColumn() + " = " + FilterNode.formatToString(pk);
|
||||
if (debug.get() && info.isLoggable(Level.FINEST)) logger.finest(clazz.getSimpleName() + " find sql=" + sql);
|
||||
final String sql = "SELECT " + info.getQueryColumns(null, selects) + " FROM " + info.getTable(pk) + " WHERE " + info.getPrimarySQLColumn() + " = " + FilterNode.formatToString(pk);
|
||||
if (info.isLoggable(logger, Level.FINEST)) logger.finest(clazz.getSimpleName() + " find sql=" + sql);
|
||||
conn.setReadOnly(true);
|
||||
final PreparedStatement ps = conn.prepareStatement(sql, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
|
||||
ps.setFetchSize(1);
|
||||
@@ -1523,8 +1572,8 @@ public class DataJdbcSource extends AbstractService implements DataSource, DataC
|
||||
final Map<Class, String> joinTabalis = node == null ? null : node.getJoinTabalis();
|
||||
final CharSequence join = node == null ? null : node.createSQLJoin(this, false, joinTabalis, new HashSet<>(), info);
|
||||
final CharSequence where = node == null ? null : node.createSQLExpress(info, joinTabalis);
|
||||
final String sql = "SELECT a.* FROM " + info.getTable(node) + " a" + (join == null ? "" : join) + ((where == null || where.length() == 0) ? "" : (" WHERE " + where));
|
||||
if (debug.get() && info.isLoggable(Level.FINEST)) logger.finest(clazz.getSimpleName() + " find sql=" + sql);
|
||||
final String sql = "SELECT " + info.getQueryColumns("a", selects) + " FROM " + info.getTable(node) + " a" + (join == null ? "" : join) + ((where == null || where.length() == 0) ? "" : (" WHERE " + where));
|
||||
if (info.isLoggable(logger, Level.FINEST)) logger.finest(clazz.getSimpleName() + " find sql=" + sql);
|
||||
conn.setReadOnly(true);
|
||||
final PreparedStatement ps = conn.prepareStatement(sql, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
|
||||
ps.setFetchSize(1);
|
||||
@@ -1591,7 +1640,7 @@ public class DataJdbcSource extends AbstractService implements DataSource, DataC
|
||||
try {
|
||||
final Attribute<T, Serializable> attr = info.getAttribute(column);
|
||||
final String sql = "SELECT " + info.getSQLColumn(null, column) + " FROM " + info.getTable(pk) + " WHERE " + info.getPrimarySQLColumn() + " = " + FilterNode.formatToString(pk);
|
||||
if (debug.get() && info.isLoggable(Level.FINEST)) logger.finest(clazz.getSimpleName() + " find sql=" + sql);
|
||||
if (info.isLoggable(logger, Level.FINEST)) logger.finest(clazz.getSimpleName() + " find sql=" + sql);
|
||||
conn.setReadOnly(true);
|
||||
final PreparedStatement ps = conn.prepareStatement(sql, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
|
||||
ps.setFetchSize(1);
|
||||
@@ -1646,7 +1695,7 @@ public class DataJdbcSource extends AbstractService implements DataSource, DataC
|
||||
final CharSequence join = node == null ? null : node.createSQLJoin(this, false, joinTabalis, new HashSet<>(), info);
|
||||
final CharSequence where = node == null ? null : node.createSQLExpress(info, joinTabalis);
|
||||
final String sql = "SELECT " + info.getSQLColumn("a", column) + " FROM " + info.getTable(node) + " a" + (join == null ? "" : join) + ((where == null || where.length() == 0) ? "" : (" WHERE " + where));
|
||||
if (debug.get() && info.isLoggable(Level.FINEST)) logger.finest(clazz.getSimpleName() + " find sql=" + sql);
|
||||
if (info.isLoggable(logger, Level.FINEST)) logger.finest(clazz.getSimpleName() + " find sql=" + sql);
|
||||
conn.setReadOnly(true);
|
||||
final PreparedStatement ps = conn.prepareStatement(sql, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
|
||||
ps.setFetchSize(1);
|
||||
@@ -1688,7 +1737,7 @@ public class DataJdbcSource extends AbstractService implements DataSource, DataC
|
||||
}
|
||||
|
||||
final Connection conn = createReadSQLConnection();
|
||||
final boolean log = debug.get() && info.isLoggable(Level.FINEST);
|
||||
final boolean log = info.isLoggable(logger, Level.FINEST);
|
||||
String logstr = null;
|
||||
try {
|
||||
final String sql = "SELECT COUNT(*) FROM " + info.getTable(pk) + " WHERE " + info.getPrimarySQLColumn() + " = " + FilterNode.formatToString(pk);
|
||||
@@ -1734,7 +1783,7 @@ public class DataJdbcSource extends AbstractService implements DataSource, DataC
|
||||
if (cache != null && cache.isFullLoaded() && (node == null || node.isCacheUseable(this))) return cache.exists(node);
|
||||
|
||||
final Connection conn = createReadSQLConnection();
|
||||
final boolean log = debug.get() && info.isLoggable(Level.FINEST);
|
||||
final boolean log = info.isLoggable(logger, Level.FINEST);
|
||||
String logstr = null;
|
||||
try {
|
||||
final Map<Class, String> joinTabalis = node == null ? null : node.getJoinTabalis();
|
||||
@@ -2238,7 +2287,7 @@ public class DataJdbcSource extends AbstractService implements DataSource, DataC
|
||||
final EntityCache<T> cache = info.getCache();
|
||||
if (readcache && cache != null && cache.isFullLoaded()) {
|
||||
if (node == null || node.isCacheUseable(this)) {
|
||||
if (debug.get() && info.isLoggable(Level.FINEST)) logger.finest(clazz.getSimpleName() + " cache query predicate = " + (node == null ? null : node.createPredicate(cache)));
|
||||
if (info.isLoggable(logger, Level.FINEST)) logger.finest(clazz.getSimpleName() + " cache query predicate = " + (node == null ? null : node.createPredicate(cache)));
|
||||
return cache.querySheet(needtotal, selects, flipper, node);
|
||||
}
|
||||
}
|
||||
@@ -2249,9 +2298,9 @@ public class DataJdbcSource extends AbstractService implements DataSource, DataC
|
||||
final Map<Class, String> joinTabalis = node == null ? null : node.getJoinTabalis();
|
||||
final CharSequence join = node == null ? null : node.createSQLJoin(this, false, joinTabalis, new HashSet<>(), info);
|
||||
final CharSequence where = node == null ? null : node.createSQLExpress(info, joinTabalis);
|
||||
final String sql = "SELECT a.* FROM " + info.getTable(node) + " a" + (join == null ? "" : join)
|
||||
final String sql = "SELECT " + info.getQueryColumns("a", selects) + " FROM " + info.getTable(node) + " a" + (join == null ? "" : join)
|
||||
+ ((where == null || where.length() == 0) ? "" : (" WHERE " + where)) + info.createSQLOrderby(flipper);
|
||||
if (debug.get() && info.isLoggable(Level.FINEST)) {
|
||||
if (info.isLoggable(logger, Level.FINEST)) {
|
||||
logger.finest(clazz.getSimpleName() + " query sql=" + sql + (flipper == null || flipper.getLimit() < 1 ? "" : (" LIMIT " + flipper.getOffset() + "," + flipper.getLimit())));
|
||||
}
|
||||
conn.setReadOnly(true);
|
||||
@@ -2339,13 +2388,14 @@ public class DataJdbcSource extends AbstractService implements DataSource, DataC
|
||||
public final void directQuery(String sql, Consumer<ResultSet> consumer) {
|
||||
final Connection conn = createReadSQLConnection();
|
||||
try {
|
||||
if (debug.get()) logger.finest("direct query sql=" + sql);
|
||||
if (logger.isLoggable(Level.FINEST)) logger.finest("direct query sql=" + sql);
|
||||
conn.setReadOnly(true);
|
||||
final PreparedStatement ps = conn.prepareStatement(sql, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
|
||||
final ResultSet set = ps.executeQuery();
|
||||
final Statement statement = conn.createStatement();
|
||||
//final PreparedStatement statement = conn.prepareStatement(sql, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
|
||||
final ResultSet set = statement.executeQuery(sql);// ps.executeQuery();
|
||||
consumer.accept(set);
|
||||
set.close();
|
||||
ps.close();
|
||||
statement.close();
|
||||
} catch (Exception ex) {
|
||||
throw new RuntimeException(ex);
|
||||
} finally {
|
||||
|
||||
@@ -24,6 +24,13 @@ import org.redkale.util.*;
|
||||
@SuppressWarnings("unchecked")
|
||||
public interface DataSource {
|
||||
|
||||
/**
|
||||
* 获取数据源类型
|
||||
*
|
||||
* @return String
|
||||
*/
|
||||
public String getType();
|
||||
|
||||
//----------------------insertAsync-----------------------------
|
||||
/**
|
||||
* 新增记录, 多对象必须是同一个Entity类 <br>
|
||||
|
||||
@@ -46,6 +46,14 @@ public final class DataSources {
|
||||
private DataSources() {
|
||||
}
|
||||
|
||||
public static DataSource createDataSource(final String unitName, Properties prop) throws IOException {
|
||||
return new DataJdbcSource(unitName, prop, prop);
|
||||
}
|
||||
|
||||
public static DataSource createDataSource(final String unitName, Properties readprop, Properties writeprop) throws IOException {
|
||||
return new DataJdbcSource(unitName, readprop, writeprop);
|
||||
}
|
||||
|
||||
public static DataSource createDataSource(final String unitName) throws IOException {
|
||||
return createDataSource(unitName, System.getProperty(DATASOURCE_CONFPATH) == null
|
||||
? DataJdbcSource.class.getResource("/META-INF/persistence.xml")
|
||||
@@ -84,6 +92,7 @@ public final class DataSources {
|
||||
}
|
||||
}
|
||||
if (readprop == null) throw new IOException("Cannot find (resource.name = '" + unitName + "') DataSource");
|
||||
if (writeprop == null) writeprop = readprop;
|
||||
String impl = readprop.getProperty(JDBC_DATASOURCE_CLASS, DataJdbcSource.class.getName());
|
||||
if (DataJdbcSource.class.getName().equals(impl)) return new DataJdbcSource(unitName, readprop, writeprop);
|
||||
try {
|
||||
|
||||
@@ -286,7 +286,7 @@ public final class EntityCache<T> {
|
||||
rs = rs2;
|
||||
} else if (func == DISTINCTCOUNT) {
|
||||
Map rs2 = new LinkedHashMap();
|
||||
rs.forEach((x, y) -> rs2.put(x, ((Set) y).size()));
|
||||
rs.forEach((x, y) -> rs2.put(x, ((Set) y).size() + 0L));
|
||||
rs = rs2;
|
||||
}
|
||||
return rs;
|
||||
|
||||
@@ -434,6 +434,26 @@ public final class EntityInfo<T> {
|
||||
return deleteSQL.replace("${newtable}", getTable(bean));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取查询字段列表
|
||||
*
|
||||
* @param tabalis 表别名
|
||||
* @param selects 过滤字段
|
||||
*
|
||||
* @return String
|
||||
*/
|
||||
public CharSequence getQueryColumns(String tabalis, SelectColumn selects) {
|
||||
if (selects == null) return tabalis == null ? "*" : (tabalis + ".*");
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (Attribute attr : this.attributes) {
|
||||
if (!selects.test(attr.field())) continue;
|
||||
if (sb.length() > 0) sb.append(',');
|
||||
sb.append(getSQLColumn(tabalis, attr.field()));
|
||||
}
|
||||
if (sb.length() == 0) sb.append('*');
|
||||
return sb;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据主键值获取Entity的表名
|
||||
*
|
||||
@@ -629,12 +649,13 @@ public final class EntityInfo<T> {
|
||||
/**
|
||||
* 判断日志级别
|
||||
*
|
||||
* @param l Level
|
||||
* @param logger Logger
|
||||
* @param l Level
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public boolean isLoggable(Level l) {
|
||||
return l.intValue() >= this.logLevel;
|
||||
public boolean isLoggable(Logger logger, Level l) {
|
||||
return logger.isLoggable(l) && l.intValue() >= this.logLevel;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -63,7 +63,7 @@ public final class FilterNodeBean<T extends FilterBean> implements Comparable<Fi
|
||||
this.nodeBeans = bean == null ? null : bean.nodeBeans;
|
||||
}
|
||||
|
||||
private FilterNodeBean(final FilterJoinColumn joinCol, final FilterColumn filterCol, final Attribute<T, Serializable> attr) {
|
||||
private FilterNodeBean(final FilterJoinColumn joinCol, final FilterColumn filterCol, final Attribute<T, Serializable> attr, final Type genericType) {
|
||||
this.beanAttr = attr;
|
||||
this.joinClass = joinCol == null ? null : joinCol.table();
|
||||
this.joinColumns = joinCol == null ? null : joinCol.columns();
|
||||
@@ -72,8 +72,13 @@ public final class FilterNodeBean<T extends FilterBean> implements Comparable<Fi
|
||||
this.column = (filterCol != null && !filterCol.name().isEmpty()) ? filterCol.name() : attr.field();
|
||||
|
||||
FilterExpress exp = filterCol == null ? null : filterCol.express();
|
||||
Class compType = type.getComponentType();
|
||||
if (Collection.class.isAssignableFrom(type) && genericType instanceof ParameterizedType) {
|
||||
Type pt = ((ParameterizedType) genericType).getActualTypeArguments()[0];
|
||||
if (pt instanceof Class) compType = (Class) pt;
|
||||
}
|
||||
if ((exp == null || exp == EQUAL) && (type.isArray() || Collection.class.isAssignableFrom(type))) {
|
||||
if (Range.class.isAssignableFrom(type.getComponentType())) {
|
||||
if (compType != null && Range.class.isAssignableFrom(compType)) {
|
||||
if (AND != exp) exp = OR;
|
||||
} else if (NOTIN != exp) {
|
||||
exp = IN;
|
||||
@@ -189,7 +194,7 @@ public final class FilterNodeBean<T extends FilterBean> implements Comparable<Fi
|
||||
fields.add(field.getName());
|
||||
|
||||
final Attribute<T, Serializable> beanAttr = pubmod ? Attribute.create(field) : Attribute.create(getter, null);
|
||||
FilterNodeBean<T> nodeBean = new FilterNodeBean(field.getAnnotation(FilterJoinColumn.class), field.getAnnotation(FilterColumn.class), beanAttr);
|
||||
FilterNodeBean<T> nodeBean = new FilterNodeBean(field.getAnnotation(FilterJoinColumn.class), field.getAnnotation(FilterColumn.class), beanAttr, field.getGenericType());
|
||||
|
||||
//------------------------------------
|
||||
{
|
||||
|
||||
@@ -7,7 +7,7 @@ package org.redkale.util;
|
||||
|
||||
import java.lang.reflect.Array;
|
||||
import java.util.*;
|
||||
import java.util.function.BiPredicate;
|
||||
import java.util.function.*;
|
||||
|
||||
/**
|
||||
* 该类提供类似JSONObject的数据结构,主要用于读取xml配置文件和http-header存储
|
||||
@@ -190,6 +190,25 @@ public abstract class AnyValue {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void forEach(BiConsumer<String, String> stringConsumer) {
|
||||
forEach(stringConsumer, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void forEach(BiConsumer<String, String> stringConsumer, BiConsumer<String, AnyValue> anyConsumer) {
|
||||
if (stringConsumer != null) {
|
||||
for (Entry<String> en : stringEntrys) {
|
||||
stringConsumer.accept(en.name, en.value);
|
||||
}
|
||||
}
|
||||
if (anyConsumer != null) {
|
||||
for (Entry<AnyValue> en : (Entry[]) anyEntrys) {
|
||||
anyConsumer.accept(en.name, en.value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Entry<String>[] getStringEntrys() {
|
||||
return stringEntrys;
|
||||
@@ -430,6 +449,10 @@ public abstract class AnyValue {
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public abstract void forEach(BiConsumer<String, String> stringConsumer);
|
||||
|
||||
public abstract void forEach(BiConsumer<String, String> stringConsumer, BiConsumer<String, AnyValue> anyConsumer);
|
||||
|
||||
public abstract Entry<String>[] getStringEntrys();
|
||||
|
||||
public abstract Entry<AnyValue>[] getAnyEntrys();
|
||||
|
||||
@@ -16,10 +16,12 @@ import java.util.function.*;
|
||||
* <p>
|
||||
* 详情见: https://redkale.org
|
||||
*
|
||||
* @deprecated 使用 java.nio.channels.CompletionHandler 代替
|
||||
* @author zhangjx
|
||||
* @param <V> 结果对象的泛型
|
||||
* @param <A> 附件对象的泛型
|
||||
*/
|
||||
@Deprecated
|
||||
public interface AsyncHandler<V, A> extends CompletionHandler<V, A> {
|
||||
|
||||
/**
|
||||
|
||||
@@ -351,8 +351,12 @@ public interface Creator<T> {
|
||||
mv.visitVarInsn(ALOAD, 1);
|
||||
if (i < 6) {
|
||||
mv.visitInsn(iconsts[i]);
|
||||
} else {
|
||||
} else if (i <= Byte.MAX_VALUE) {
|
||||
mv.visitIntInsn(BIPUSH, i);
|
||||
} else if (i <= Short.MAX_VALUE) {
|
||||
mv.visitIntInsn(SIPUSH, i);
|
||||
} else {
|
||||
mv.visitLdcInsn(i);
|
||||
}
|
||||
mv.visitInsn(AALOAD);
|
||||
Label lab = new Label();
|
||||
@@ -360,8 +364,12 @@ public interface Creator<T> {
|
||||
mv.visitVarInsn(ALOAD, 1);
|
||||
if (i < 6) {
|
||||
mv.visitInsn(iconsts[i]);
|
||||
} else {
|
||||
} else if (i <= Byte.MAX_VALUE) {
|
||||
mv.visitIntInsn(BIPUSH, i);
|
||||
} else if (i <= Short.MAX_VALUE) {
|
||||
mv.visitIntInsn(SIPUSH, i);
|
||||
} else {
|
||||
mv.visitLdcInsn(i);
|
||||
}
|
||||
if (pt == int.class) {
|
||||
mv.visitInsn(ICONST_0);
|
||||
@@ -399,8 +407,12 @@ public interface Creator<T> {
|
||||
mv.visitVarInsn(ALOAD, 1);
|
||||
if (i < 6) {
|
||||
mv.visitInsn(iconsts[i]);
|
||||
} else {
|
||||
} else if (i <= Byte.MAX_VALUE) {
|
||||
mv.visitIntInsn(BIPUSH, i);
|
||||
} else if (i <= Short.MAX_VALUE) {
|
||||
mv.visitIntInsn(SIPUSH, i);
|
||||
} else {
|
||||
mv.visitLdcInsn(i);
|
||||
}
|
||||
mv.visitInsn(AALOAD);
|
||||
final Class ct = constructorParameters[i].getValue();
|
||||
|
||||
@@ -19,7 +19,7 @@ import java.util.logging.*;
|
||||
* @author zhangjx
|
||||
* @param <T> 对象池元素的数据类型
|
||||
*/
|
||||
public final class ObjectPool<T> implements Supplier<T> {
|
||||
public final class ObjectPool<T> implements Supplier<T>, Consumer<T> {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(ObjectPool.class.getSimpleName());
|
||||
|
||||
@@ -78,21 +78,27 @@ public final class ObjectPool<T> implements Supplier<T> {
|
||||
return result;
|
||||
}
|
||||
|
||||
public void offer(final T e) {
|
||||
@Override
|
||||
public void accept(final T e) {
|
||||
if (e != null && recycler.test(e)) {
|
||||
if (cycleCounter != null) cycleCounter.incrementAndGet();
|
||||
if (debug) {
|
||||
for (T t : queue) {
|
||||
if (t == e) {
|
||||
logger.log(Level.WARNING, "[" + Thread.currentThread().getName() + "] repeat offer the same object(" + e + ")", new Exception());
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
// if (debug) {
|
||||
// for (T t : queue) {
|
||||
// if (t == e) {
|
||||
// logger.log(Level.WARNING, "[" + Thread.currentThread().getName() + "] repeat offer the same object(" + e + ")", new Exception());
|
||||
// return;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
queue.offer(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public void offer(final T e) {
|
||||
accept(e);
|
||||
}
|
||||
|
||||
public long getCreatCount() {
|
||||
return creatCounter.longValue();
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ public final class Redkale {
|
||||
}
|
||||
|
||||
public static String getDotedVersion() {
|
||||
return "1.8.5";
|
||||
return "1.8.8";
|
||||
}
|
||||
|
||||
public static int getMajorVersion() {
|
||||
|
||||
@@ -532,6 +532,19 @@ public final class ResourceFactory {
|
||||
return inject(src, attachment, consumer, new ArrayList());
|
||||
}
|
||||
|
||||
public static String formatResourceName(String name) {
|
||||
if (name == null) return null;
|
||||
int pos = name.indexOf("{system.property.");
|
||||
if (pos < 0) return name;
|
||||
String prefix = name.substring(0, pos);
|
||||
String subname = name.substring(pos + "{system.property.".length());
|
||||
pos = subname.indexOf('}');
|
||||
if (pos < 0) return name;
|
||||
String postfix = subname.substring(pos + 1);
|
||||
String property = subname.substring(0, pos);
|
||||
return formatResourceName(prefix + System.getProperty(property, "") + postfix);
|
||||
}
|
||||
|
||||
private <T> boolean inject(final Object src, final T attachment, final BiConsumer<Object, Field> consumer, final List<Object> list) {
|
||||
if (src == null) return false;
|
||||
try {
|
||||
@@ -575,25 +588,12 @@ public final class ResourceFactory {
|
||||
|
||||
}
|
||||
boolean autoregnull = true;
|
||||
final String rcname = tname;
|
||||
ResourceEntry re = findEntry(rcname, genctype);
|
||||
if (re == null) {
|
||||
if (rcname.startsWith("property.")) {
|
||||
re = findEntry(rcname, String.class);
|
||||
} else {
|
||||
re = findEntry(rcname, classtype);
|
||||
}
|
||||
}
|
||||
if (re == null) {
|
||||
ResourceLoader it = findLoader(genctype, field);
|
||||
if (it != null) {
|
||||
it.load(this, src, rcname, field, attachment);
|
||||
autoregnull = it.autoNone();
|
||||
re = findEntry(rcname, genctype);
|
||||
}
|
||||
}
|
||||
if (re == null && genctype != classtype) {
|
||||
re = findEntry(rcname, classtype);
|
||||
final String rcname = formatResourceName(tname);
|
||||
Object rs;
|
||||
if (rcname.startsWith("system.property.")) {
|
||||
rs = System.getProperty(rcname.substring("system.property.".length()));
|
||||
} else {
|
||||
ResourceEntry re = findEntry(rcname, genctype);
|
||||
if (re == null) {
|
||||
if (rcname.startsWith("property.")) {
|
||||
re = findEntry(rcname, String.class);
|
||||
@@ -602,22 +602,39 @@ public final class ResourceFactory {
|
||||
}
|
||||
}
|
||||
if (re == null) {
|
||||
ResourceLoader it = findLoader(classtype, field);
|
||||
ResourceLoader it = findLoader(genctype, field);
|
||||
if (it != null) {
|
||||
it.load(this, src, rcname, field, attachment);
|
||||
autoregnull = it.autoNone();
|
||||
re = findEntry(rcname, classtype);
|
||||
re = findEntry(rcname, genctype);
|
||||
}
|
||||
}
|
||||
if (re == null && genctype != classtype) {
|
||||
re = findEntry(rcname, classtype);
|
||||
if (re == null) {
|
||||
if (rcname.startsWith("property.")) {
|
||||
re = findEntry(rcname, String.class);
|
||||
} else {
|
||||
re = findEntry(rcname, classtype);
|
||||
}
|
||||
}
|
||||
if (re == null) {
|
||||
ResourceLoader it = findLoader(classtype, field);
|
||||
if (it != null) {
|
||||
it.load(this, src, rcname, field, attachment);
|
||||
autoregnull = it.autoNone();
|
||||
re = findEntry(rcname, classtype);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (re == null && autoregnull) {
|
||||
register(rcname, genctype, null); //自动注入null的值
|
||||
re = findEntry(rcname, genctype);
|
||||
}
|
||||
if (re == null) continue;
|
||||
re.elements.add(new ResourceElement<>(src, field));
|
||||
rs = re.value;
|
||||
}
|
||||
if (re == null && autoregnull) {
|
||||
register(rcname, genctype, null); //自动注入null的值
|
||||
re = findEntry(rcname, genctype);
|
||||
}
|
||||
if (re == null) continue;
|
||||
re.elements.add(new ResourceElement<>(src, field));
|
||||
|
||||
Object rs = re.value;
|
||||
if (rs != null && !rs.getClass().isPrimitive() && classtype.isPrimitive()) {
|
||||
if (classtype == int.class) {
|
||||
rs = Integer.decode(rs.toString());
|
||||
@@ -760,25 +777,27 @@ public final class ResourceFactory {
|
||||
this.listener = tn.startsWith("java.") || tn.startsWith("javax.") ? null : findListener(t, field.getType());
|
||||
}
|
||||
|
||||
private static synchronized Method findListener(Class clazz, Class fieldType) {
|
||||
Class loop = clazz;
|
||||
Method m = listenerMethods.get(clazz.getName() + "-" + fieldType.getName());
|
||||
if (m != null) return m;
|
||||
do {
|
||||
for (Method method : loop.getDeclaredMethods()) {
|
||||
if (method.getAnnotation(ResourceListener.class) != null
|
||||
&& method.getParameterCount() == 3
|
||||
&& String.class.isAssignableFrom(method.getParameterTypes()[0])
|
||||
&& method.getParameterTypes()[1] == method.getParameterTypes()[2]
|
||||
&& method.getParameterTypes()[1].isAssignableFrom(fieldType)) {
|
||||
m = method;
|
||||
m.setAccessible(true);
|
||||
break;
|
||||
private static Method findListener(Class clazz, Class fieldType) {
|
||||
synchronized (listenerMethods) {
|
||||
Class loop = clazz;
|
||||
Method m = listenerMethods.get(clazz.getName() + "-" + fieldType.getName());
|
||||
if (m != null) return m;
|
||||
do {
|
||||
for (Method method : loop.getDeclaredMethods()) {
|
||||
if (method.getAnnotation(ResourceListener.class) != null
|
||||
&& method.getParameterCount() == 3
|
||||
&& String.class.isAssignableFrom(method.getParameterTypes()[0])
|
||||
&& method.getParameterTypes()[1] == method.getParameterTypes()[2]
|
||||
&& method.getParameterTypes()[1].isAssignableFrom(fieldType)) {
|
||||
m = method;
|
||||
m.setAccessible(true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} while ((loop = loop.getSuperclass()) != Object.class);
|
||||
listenerMethods.put(clazz.getName() + "-" + fieldType.getName(), m);
|
||||
return m;
|
||||
} while ((loop = loop.getSuperclass()) != Object.class);
|
||||
listenerMethods.put(clazz.getName() + "-" + fieldType.getName(), m);
|
||||
return m;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,10 +8,11 @@ import java.io.*;
|
||||
import java.lang.reflect.*;
|
||||
import java.net.*;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.CompletionHandler;
|
||||
import java.nio.charset.*;
|
||||
import java.time.*;
|
||||
import java.util.*;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.function.*;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
import javax.net.ssl.*;
|
||||
|
||||
@@ -229,6 +230,191 @@ public final class Utility {
|
||||
return news;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取int数组之和
|
||||
*
|
||||
* @param array 数组
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public static int sum(final int... array) {
|
||||
if (array == null || array.length == 0) throw new NullPointerException("array is null or empty");
|
||||
int sum = 0;
|
||||
for (int i : array) {
|
||||
sum += i;
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取long数组之和
|
||||
*
|
||||
* @param array 数组
|
||||
*
|
||||
* @return long
|
||||
*/
|
||||
public static long sum(final long... array) {
|
||||
if (array == null || array.length == 0) throw new NullPointerException("array is null or empty");
|
||||
long sum = 0L;
|
||||
for (long i : array) {
|
||||
sum += i;
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取int数组最大值
|
||||
*
|
||||
* @param array 数组
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public static int max(final int... array) {
|
||||
if (array == null || array.length == 0) throw new NullPointerException("array is null or empty");
|
||||
int max = array[0];
|
||||
for (int i : array) {
|
||||
if (i > max) i = max;
|
||||
}
|
||||
return max;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取long数组最大值
|
||||
*
|
||||
* @param array 数组
|
||||
*
|
||||
* @return long
|
||||
*/
|
||||
public static long max(final long... array) {
|
||||
if (array == null || array.length == 0) throw new NullPointerException("array is null or empty");
|
||||
long max = array[0];
|
||||
for (long i : array) {
|
||||
if (i > max) i = max;
|
||||
}
|
||||
return max;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取int数组最小值
|
||||
*
|
||||
* @param array 数组
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public static long min(final int... array) {
|
||||
if (array == null || array.length == 0) throw new NullPointerException("array is null or empty");
|
||||
int min = array[0];
|
||||
for (int i : array) {
|
||||
if (i < min) i = min;
|
||||
}
|
||||
return min;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取long数组最小值
|
||||
*
|
||||
* @param array 数组
|
||||
*
|
||||
* @return long
|
||||
*/
|
||||
public static long min(final long... array) {
|
||||
if (array == null || array.length == 0) throw new NullPointerException("array is null or empty");
|
||||
long min = array[0];
|
||||
for (long i : array) {
|
||||
if (i < min) i = min;
|
||||
}
|
||||
return min;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将int数组用分隔符拼接成字符串
|
||||
*
|
||||
* @param array 数组
|
||||
* @param delimiter 分隔符
|
||||
*
|
||||
* @return String
|
||||
*/
|
||||
public static String joining(final int[] array, final String delimiter) {
|
||||
if (array == null || array.length == 0) return "";
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (int i : array) {
|
||||
if (sb.length() > 0) sb.append(delimiter);
|
||||
sb.append(i);
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 将long数组用分隔符拼接成字符串
|
||||
*
|
||||
* @param array 数组
|
||||
* @param delimiter 分隔符
|
||||
*
|
||||
* @return String
|
||||
*/
|
||||
public static String joining(final long[] array, final String delimiter) {
|
||||
if (array == null || array.length == 0) return "";
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (long i : array) {
|
||||
if (sb.length() > 0) sb.append(delimiter);
|
||||
sb.append(i);
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 将对象数组用分隔符拼接成字符串
|
||||
*
|
||||
* @param <T> 泛型
|
||||
* @param array 数组
|
||||
* @param delimiter 分隔符
|
||||
*
|
||||
* @return String
|
||||
*/
|
||||
public static <T> String joining(final T[] array, final String delimiter) {
|
||||
if (array == null || array.length == 0) return "";
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (T i : array) {
|
||||
if (sb.length() > 0) sb.append(delimiter);
|
||||
sb.append(i);
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 将一个或多个int新元素添加到int数组结尾
|
||||
*
|
||||
* @param array 原数组
|
||||
* @param objs 待追加数据
|
||||
*
|
||||
* @return 新数组
|
||||
*/
|
||||
public static int[] append(final int[] array, final int... objs) {
|
||||
if (array == null || array.length == 0) return objs;
|
||||
if (objs == null || objs.length == 0) return array;
|
||||
final int[] news = new int[array.length + objs.length];
|
||||
System.arraycopy(array, 0, news, 0, array.length);
|
||||
System.arraycopy(objs, 0, news, array.length, objs.length);
|
||||
return news;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将一个或多个long新元素添加到long数组结尾
|
||||
*
|
||||
* @param array 原数组
|
||||
* @param objs 待追加数据
|
||||
*
|
||||
* @return 新数组
|
||||
*/
|
||||
public static long[] append(final long[] array, final long... objs) {
|
||||
if (array == null || array.length == 0) return objs;
|
||||
if (objs == null || objs.length == 0) return array;
|
||||
final long[] news = new long[array.length + objs.length];
|
||||
System.arraycopy(array, 0, news, 0, array.length);
|
||||
System.arraycopy(objs, 0, news, array.length, objs.length);
|
||||
return news;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将一个或多个新元素添加到数组结尾
|
||||
*
|
||||
@@ -458,6 +644,76 @@ public final class Utility {
|
||||
return back;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建 CompletionHandler 对象
|
||||
*
|
||||
* @param <V> 结果对象的泛型
|
||||
* @param <A> 附件对象的泛型
|
||||
* @param success 成功的回调函数
|
||||
* @param fail 失败的回调函数
|
||||
*
|
||||
* @return CompletionHandler
|
||||
*/
|
||||
public static <V, A> CompletionHandler<V, A> createAsyncHandler(final BiConsumer<V, A> success, final BiConsumer<Throwable, A> fail) {
|
||||
return new CompletionHandler<V, A>() {
|
||||
@Override
|
||||
public void completed(V result, A attachment) {
|
||||
if (success != null) success.accept(result, attachment);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void failed(Throwable exc, A attachment) {
|
||||
if (fail != null) fail.accept(exc, attachment);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建没有返回结果的 CompletionHandler 对象
|
||||
*
|
||||
* @param <A> 附件对象的泛型
|
||||
* @param success 成功的回调函数
|
||||
* @param fail 失败的回调函数
|
||||
*
|
||||
* @return CompletionHandler
|
||||
*/
|
||||
public static <A> CompletionHandler<Void, A> createAsyncHandler(final Consumer<A> success, final BiConsumer<Throwable, A> fail) {
|
||||
return new CompletionHandler<Void, A>() {
|
||||
@Override
|
||||
public void completed(Void result, A attachment) {
|
||||
if (success != null) success.accept(attachment);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void failed(Throwable exc, A attachment) {
|
||||
if (fail != null) fail.accept(exc, attachment);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建没有附件对象的 CompletionHandler 对象
|
||||
*
|
||||
* @param <V> 结果对象的泛型
|
||||
* @param success 成功的回调函数
|
||||
* @param fail 失败的回调函数
|
||||
*
|
||||
* @return CompletionHandler
|
||||
*/
|
||||
public static <V> CompletionHandler<V, Void> createAsyncHandler(final Consumer<V> success, final Consumer<Throwable> fail) {
|
||||
return new CompletionHandler<V, Void>() {
|
||||
@Override
|
||||
public void completed(V result, Void attachment) {
|
||||
if (success != null) success.accept(result);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void failed(Throwable exc, Void attachment) {
|
||||
if (fail != null) fail.accept(exc);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取格式为yyyy-MM-dd HH:mm:ss的当前时间
|
||||
*
|
||||
|
||||
35
test/org/redkale/test/asm/AsmCreator.java
Normal file
35
test/org/redkale/test/asm/AsmCreator.java
Normal file
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* To change this license header, choose License Headers in Project Properties.
|
||||
* To change this template file, choose Tools | Templates
|
||||
* and open the template in the editor.
|
||||
*/
|
||||
package org.redkale.test.asm;
|
||||
|
||||
import java.io.*;
|
||||
import org.redkale.util.Utility;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author zhangjx
|
||||
*/
|
||||
public class AsmCreator {
|
||||
|
||||
public static void main(String[] args) throws Throwable {
|
||||
boolean realasm = true; //从http://forge.ow2.org/projects/asm/ 下载最新asm的src放在 srcasmroot 目录下
|
||||
File srcasmroot = new File("D:/JAVA/JDK源码/JDK9源码/java.base/jdk/internal/org/objectweb/asm");
|
||||
if(realasm) srcasmroot = new File("D:/JAVA/JDK源码/org/objectweb/asm");
|
||||
File destasmroot = new File("D:/Java-Projects/RedkaleProject/src/org/redkale/asm");
|
||||
String line = null;
|
||||
LineNumberReader txtin = new LineNumberReader(new FileReader(new File(destasmroot, "asm.txt")));
|
||||
while ((line = txtin.readLine()) != null) {
|
||||
line = line.trim();
|
||||
if (!line.endsWith(".java")) continue;
|
||||
File srcfile = new File(srcasmroot, line);
|
||||
File destfile = new File(destasmroot, line);
|
||||
String content = Utility.readThenClose(new FileInputStream(srcfile));
|
||||
FileOutputStream out = new FileOutputStream(destfile);
|
||||
out.write(content.replace("jdk.internal.org.objectweb", "org.redkale").replace("org.objectweb", "org.redkale").getBytes());
|
||||
out.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -11,8 +11,9 @@ import org.redkale.convert.bson.BsonFactory;
|
||||
import org.redkale.util.Utility;
|
||||
import org.redkale.convert.bson.BsonConvert;
|
||||
import java.nio.*;
|
||||
import java.util.Arrays;
|
||||
import java.util.*;
|
||||
import org.redkale.convert.json.*;
|
||||
import org.redkale.util.*;
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -24,12 +25,14 @@ public class BsonTestMain {
|
||||
Serializable[] sers = new Serializable[]{"aaa", 4};
|
||||
final BsonConvert convert = BsonFactory.root().getConvert();
|
||||
byte[] bytes = convert.convertTo(sers);
|
||||
Utility.println("---", bytes);
|
||||
Utility.println("---", bytes);
|
||||
Serializable[] a = convert.convertFrom(Serializable[].class, bytes);
|
||||
System.out.println(Arrays.toString(a));
|
||||
main2(args);
|
||||
main3(args);
|
||||
main4(args);
|
||||
main5(args);
|
||||
main6(args);
|
||||
}
|
||||
|
||||
public static void main2(String[] args) throws Exception {
|
||||
@@ -86,4 +89,31 @@ public class BsonTestMain {
|
||||
System.out.println(rs.toString());
|
||||
|
||||
}
|
||||
|
||||
public static void main5(String[] args) throws Exception {
|
||||
final BsonConvert convert = BsonFactory.root().getConvert();
|
||||
|
||||
LinkedHashMap map = new LinkedHashMap();
|
||||
map.put("1", 1);
|
||||
map.put("2", "a2");
|
||||
byte[] bs = convert.convertTo(Object.class, map);
|
||||
Object mapobj = convert.convertFrom(Object.class, bs);
|
||||
System.out.println(mapobj);
|
||||
}
|
||||
|
||||
public static void main6(String[] args) throws Exception {
|
||||
final BsonConvert convert = BsonFactory.root().getConvert();
|
||||
|
||||
Optional<String> val = Optional.ofNullable("haha");
|
||||
byte[] bs = convert.convertTo(val);
|
||||
Object obj = convert.convertFrom(Optional.class, bs);
|
||||
System.out.println(obj);
|
||||
bs = convert.convertTo(Object.class, val);
|
||||
obj = convert.convertFrom(Object.class, bs);
|
||||
System.out.println(obj);
|
||||
bs = convert.convertTo(new TypeToken<Optional<String>>(){}.getType(), val);
|
||||
obj = convert.convertFrom(new TypeToken<Optional<String>>(){}.getType(), bs);
|
||||
System.out.println(obj);
|
||||
System.out.println(JsonConvert.root().convertTo(val));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ public class JsonTestMain {
|
||||
public static void main(String[] args) throws Exception {
|
||||
JsonFactory factory = JsonFactory.root().tiny(true);
|
||||
final JsonConvert convert = JsonConvert.root();
|
||||
String json = "{\"access_token\":\"vVX2bIjN5P9TMOphDkStM96eNWapAehTuWAlVDO74aFaYxLwj2b-9-T9p_W2mfr9\",\"expires_in\":7200, \"aa\":\"\"}";
|
||||
String json = "{\"access_token\":\"null\",\"priv\":null, vvv:nulla,\"priv2\":\"nulla\",\"expires_in\":7200, \"aa\":\"\"}";
|
||||
Map<String, String> map = convert.convertFrom(JsonConvert.TYPE_MAP_STRING_STRING, json);
|
||||
System.out.println(map);
|
||||
System.out.println(convert.convertTo(map));
|
||||
|
||||
@@ -9,12 +9,12 @@ import java.io.*;
|
||||
import java.lang.reflect.*;
|
||||
import java.net.*;
|
||||
import java.nio.*;
|
||||
import java.nio.channels.CompletionHandler;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.function.BiConsumer;
|
||||
import org.redkale.convert.json.*;
|
||||
import org.redkale.net.http.*;
|
||||
import org.redkale.util.AsyncHandler;
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -28,11 +28,11 @@ public interface HttpResponseDesc {
|
||||
//增加Cookie值
|
||||
public HttpResponse addCookie(Collection<HttpCookie> cookies);
|
||||
|
||||
//创建AsyncHandler实例,将非字符串对象以JSON格式输出,字符串以文本输出
|
||||
public AsyncHandler createAsyncHandler();
|
||||
//创建CompletionHandler实例,将非字符串对象以JSON格式输出,字符串以文本输出
|
||||
public CompletionHandler createAsyncHandler();
|
||||
|
||||
//传入的AsyncHandler子类必须是public,且保证其子类可被继承且completed、failed可被重载且包含空参数的构造函数
|
||||
public <H extends AsyncHandler> H createAsyncHandler(Class<H> handlerClass);
|
||||
//传入的CompletionHandler子类必须是public,且保证其子类可被继承且completed、failed可被重载且包含空参数的构造函数
|
||||
public <H extends CompletionHandler> H createAsyncHandler(Class<H> handlerClass);
|
||||
|
||||
//设置状态码
|
||||
public void setStatus(int status);
|
||||
@@ -66,10 +66,10 @@ public interface HttpResponseDesc {
|
||||
public HttpResponse skipHeader();
|
||||
|
||||
//异步输出指定内容
|
||||
public <A> void sendBody(ByteBuffer buffer, A attachment, AsyncHandler<Integer, A> handler);
|
||||
public <A> void sendBody(ByteBuffer buffer, A attachment, CompletionHandler<Integer, A> handler);
|
||||
|
||||
//异步输出指定内容
|
||||
public <A> void sendBody(ByteBuffer[] buffers, A attachment, AsyncHandler<Integer, A> handler);
|
||||
public <A> void sendBody(ByteBuffer[] buffers, A attachment, CompletionHandler<Integer, A> handler);
|
||||
|
||||
//关闭HTTP连接,如果是keep-alive则不强制关闭
|
||||
public void finish();
|
||||
|
||||
@@ -5,13 +5,13 @@
|
||||
*/
|
||||
package org.redkale.test.rest;
|
||||
|
||||
import org.redkale.util.AsyncHandler;
|
||||
import java.nio.channels.CompletionHandler;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author zhangjx
|
||||
*/
|
||||
public class HelloAsyncHandler implements AsyncHandler {
|
||||
public class HelloAsyncHandler implements CompletionHandler {
|
||||
|
||||
@Override
|
||||
public void completed(Object result, Object attachment) {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package org.redkale.test.rest;
|
||||
|
||||
import java.nio.channels.CompletionHandler;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import javax.annotation.Resource;
|
||||
@@ -42,6 +43,11 @@ public class HelloService implements Service {
|
||||
return new RetResult<>(entity);
|
||||
}
|
||||
|
||||
//
|
||||
public HttpResult showHello(int id) {
|
||||
return new HttpResult("a");
|
||||
}
|
||||
|
||||
//删除记录
|
||||
public void deleteHello(int id) { //通过 /pipes/hello/delete/1234 删除对象
|
||||
source.delete(HelloEntity.class, id);
|
||||
@@ -68,6 +74,7 @@ public class HelloService implements Service {
|
||||
|
||||
//查询List列表
|
||||
@RestMapping(name = "list")
|
||||
@RestConvert(type = HelloEntity.class, ignoreColumns = {"createtime"})
|
||||
public List<HelloEntity> queryHello(HelloBean bean) { //通过 /pipes/hello/list?bean={...} 查询List列表
|
||||
return source.queryList(HelloEntity.class, bean);
|
||||
}
|
||||
@@ -96,7 +103,7 @@ public class HelloService implements Service {
|
||||
|
||||
//异步查询单个
|
||||
@RestMapping(name = "asyncfind2")
|
||||
public void asyncFindHello(AsyncHandler hander, @RestParam(name = "#") int id) { //通过 /pipes/hello/find/1234、/pipes/hello/jsfind/1234 查询对象
|
||||
public void asyncFindHello(CompletionHandler hander, @RestParam(name = "#") int id) { //通过 /pipes/hello/find/1234、/pipes/hello/jsfind/1234 查询对象
|
||||
if (source != null) source.findAsync(HelloEntity.class, id);
|
||||
System.out.println("-----------进入asyncfind2--------" + hander);
|
||||
hander.completed(new HelloEntity(id), id);
|
||||
|
||||
@@ -8,7 +8,7 @@ package org.redkale.test.service;
|
||||
import java.io.IOException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.AsynchronousChannelGroup;
|
||||
import java.nio.channels.*;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.concurrent.atomic.*;
|
||||
import java.util.logging.*;
|
||||
@@ -37,7 +37,7 @@ public class ABMainService implements Service {
|
||||
final int abport = 8888;
|
||||
ResourceFactory resFactory = ResourceFactory.root();
|
||||
ExecutorService executor = Executors.newSingleThreadExecutor();
|
||||
final TransportFactory transFactory = new TransportFactory(executor, newBufferPool(), newChannelGroup());
|
||||
final TransportFactory transFactory = TransportFactory.create(executor, newBufferPool(), newChannelGroup());
|
||||
transFactory.addGroupInfo("g77", new InetSocketAddress("127.0.0.1", 5577));
|
||||
transFactory.addGroupInfo("g88", new InetSocketAddress("127.0.0.1", 5588));
|
||||
transFactory.addGroupInfo("g99", new InetSocketAddress("127.0.0.1", 5599));
|
||||
@@ -165,8 +165,8 @@ public class ABMainService implements Service {
|
||||
}
|
||||
|
||||
@RestMapping(name = "asyncabtime")
|
||||
public void abCurrentTime(final AsyncHandler<String, Void> handler, @RestParam(name = "#") final String name) {
|
||||
bcService.bcCurrentTime(AsyncHandler.create((v, a) -> {
|
||||
public void abCurrentTime(final CompletionHandler<String, Void> handler, @RestParam(name = "#") final String name) {
|
||||
bcService.bcCurrentTime(Utility.createAsyncHandler((v, a) -> {
|
||||
System.out.println("执行了 ABMainService.abCurrentTime----异步方法");
|
||||
String rs = "异步abCurrentTime: " + v;
|
||||
if (handler != null) handler.completed(rs, a);
|
||||
|
||||
@@ -5,9 +5,10 @@
|
||||
*/
|
||||
package org.redkale.test.service;
|
||||
|
||||
import java.nio.channels.CompletionHandler;
|
||||
import javax.annotation.Resource;
|
||||
import org.redkale.service.*;
|
||||
import org.redkale.util.AsyncHandler;
|
||||
import org.redkale.util.*;
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -24,8 +25,8 @@ public class BCService implements Service {
|
||||
return rs;
|
||||
}
|
||||
|
||||
public void bcCurrentTime(final AsyncHandler<String, Void> handler, final String name) {
|
||||
cService.ccCurrentTime(AsyncHandler.create((v, a) -> {
|
||||
public void bcCurrentTime(final CompletionHandler<String, Void> handler, final String name) {
|
||||
cService.ccCurrentTime(Utility.createAsyncHandler((v, a) -> {
|
||||
System.out.println("执行了 BCService.bcCurrentTime----异步方法");
|
||||
String rs = "异步bcCurrentTime: " + (v == null ? null : v.getResult());
|
||||
if (handler != null) handler.completed(rs, null);
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
*/
|
||||
package org.redkale.test.service;
|
||||
|
||||
import java.nio.channels.CompletionHandler;
|
||||
import org.redkale.service.*;
|
||||
import org.redkale.util.*;
|
||||
|
||||
@@ -20,7 +21,7 @@ public class CService implements Service {
|
||||
return new RetResult(rs);
|
||||
}
|
||||
|
||||
public void ccCurrentTime(final AsyncHandler<RetResult<String>, Void> handler, final String name) {
|
||||
public void ccCurrentTime(final CompletionHandler<RetResult<String>, Void> handler, final String name) {
|
||||
String rs = "异步ccCurrentTime: " + name + ": " + Utility.formatTime(System.currentTimeMillis());
|
||||
System.out.println("执行了 CService.ccCurrentTime----异步方法");
|
||||
if (handler != null) handler.completed(new RetResult(rs), null);
|
||||
|
||||
@@ -5,13 +5,13 @@
|
||||
*/
|
||||
package org.redkale.test.service;
|
||||
|
||||
import org.redkale.util.AsyncHandler;
|
||||
import java.nio.channels.CompletionHandler;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author zhangjx
|
||||
*/
|
||||
public abstract class MyAsyncInnerHandler<V, A> implements AsyncHandler<V, A> {
|
||||
public abstract class MyAsyncInnerHandler<V, A> implements CompletionHandler<V, A> {
|
||||
|
||||
protected abstract int id2();
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
*/
|
||||
package org.redkale.test.service;
|
||||
|
||||
import java.nio.channels.CompletionHandler;
|
||||
import org.redkale.net.sncp.*;
|
||||
import org.redkale.service.Service;
|
||||
import org.redkale.util.*;
|
||||
@@ -19,7 +20,7 @@ public class TestService implements Service {
|
||||
// return false;
|
||||
// }
|
||||
|
||||
public void change(AsyncHandler<Boolean, TestBean> handler, TestBean bean, String name, int id) {
|
||||
public void change(CompletionHandler<Boolean, TestBean> handler, TestBean bean, String name, int id) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -79,7 +79,7 @@ public class SncpTest {
|
||||
Set<InetSocketAddress> set = new LinkedHashSet<>();
|
||||
set.add(addr);
|
||||
if (port2 > 0) set.add(new InetSocketAddress(myhost, port2));
|
||||
final TransportFactory transFactory = new TransportFactory(Executors.newSingleThreadExecutor(), newBufferPool(), newChannelGroup());
|
||||
final TransportFactory transFactory = TransportFactory.create(Executors.newSingleThreadExecutor(), newBufferPool(), newChannelGroup());
|
||||
transFactory.addGroupInfo("client", set);
|
||||
final SncpTestIService service = Sncp.createSimpleRemoteService(SncpTestIService.class, transFactory, addr, "client");
|
||||
ResourceFactory.root().inject(service);
|
||||
@@ -94,6 +94,7 @@ public class SncpTest {
|
||||
SncpTestBean callbean = new SncpTestBean();
|
||||
callbean.setId(1);
|
||||
callbean.setContent("数据X");
|
||||
service.queryLongResult("f", 3,33L);
|
||||
|
||||
service.insert(callbean);
|
||||
System.out.println("bean.id应该会被修改(id不会是1): " + callbean);
|
||||
@@ -156,7 +157,7 @@ public class SncpTest {
|
||||
SncpServer server = new SncpServer();
|
||||
Set<InetSocketAddress> set = new LinkedHashSet<>();
|
||||
if (port2 > 0) set.add(new InetSocketAddress(myhost, port2));
|
||||
final TransportFactory transFactory = new TransportFactory(Executors.newSingleThreadExecutor(), newBufferPool(), newChannelGroup());
|
||||
final TransportFactory transFactory = TransportFactory.create(Executors.newSingleThreadExecutor(), newBufferPool(), newChannelGroup());
|
||||
transFactory.addGroupInfo("server", set);
|
||||
SncpTestIService service = Sncp.createSimpleLocalService(SncpTestServiceImpl.class, transFactory, addr, "server");
|
||||
ResourceFactory.root().inject(service);
|
||||
@@ -191,7 +192,7 @@ public class SncpTest {
|
||||
Set<InetSocketAddress> set = new LinkedHashSet<>();
|
||||
set.add(new InetSocketAddress(myhost, port));
|
||||
|
||||
final TransportFactory transFactory = new TransportFactory(Executors.newSingleThreadExecutor(), newBufferPool(), newChannelGroup());
|
||||
final TransportFactory transFactory = TransportFactory.create(Executors.newSingleThreadExecutor(), newBufferPool(), newChannelGroup());
|
||||
transFactory.addGroupInfo("server", set);
|
||||
Service service = Sncp.createSimpleLocalService(SncpTestServiceImpl.class, transFactory, addr, "server");
|
||||
server.addSncpServlet(service);
|
||||
|
||||
@@ -17,8 +17,12 @@ public interface SncpTestIService extends Service {
|
||||
|
||||
public String queryResult(SncpTestBean bean);
|
||||
|
||||
public CompletableFuture<String> queryResultAsync(SncpTestBean bean);
|
||||
public double queryDoubleResult(String a, int b, double value);
|
||||
|
||||
public long queryLongResult(String a, int b, long value);
|
||||
|
||||
public CompletableFuture<String> queryResultAsync(SncpTestBean bean);
|
||||
|
||||
public void insert(@RpcCall(DataCallArrayAttribute.class) SncpTestBean... beans);
|
||||
|
||||
public String updateBean(@RpcCall(SncpTestServiceImpl.CallAttribute.class) SncpTestBean bean);
|
||||
|
||||
@@ -7,6 +7,7 @@ package org.redkale.test.sncp;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.nio.channels.CompletionHandler;
|
||||
import java.util.concurrent.*;
|
||||
import org.redkale.net.TransportFactory;
|
||||
import org.redkale.net.sncp.*;
|
||||
@@ -41,6 +42,18 @@ public class SncpTestServiceImpl implements SncpTestIService {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
@RpcMultiRun
|
||||
public long queryLongResult(String a, int b, long value) {
|
||||
return value + 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
@RpcMultiRun
|
||||
public double queryDoubleResult(String a, int b, double value) {
|
||||
return value + 1;
|
||||
}
|
||||
|
||||
public static class CallAttribute implements Attribute<SncpTestBean, Long> {
|
||||
|
||||
@Override
|
||||
@@ -85,7 +98,7 @@ public class SncpTestServiceImpl implements SncpTestIService {
|
||||
return "result: " + bean;
|
||||
}
|
||||
|
||||
public void queryResult(AsyncHandler<String, SncpTestBean> handler, @RpcAttachment SncpTestBean bean) {
|
||||
public void queryResult(CompletionHandler<String, SncpTestBean> handler, @RpcAttachment SncpTestBean bean) {
|
||||
System.out.println(Thread.currentThread().getName() + " handler 运行了queryResult方法");
|
||||
if (handler != null) handler.completed("result: " + bean, bean);
|
||||
}
|
||||
@@ -100,7 +113,7 @@ public class SncpTestServiceImpl implements SncpTestIService {
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
|
||||
final TransportFactory transFactory = new TransportFactory(Executors.newSingleThreadExecutor(), newBufferPool(), newChannelGroup());
|
||||
final TransportFactory transFactory = TransportFactory.create(Executors.newSingleThreadExecutor(), newBufferPool(), newChannelGroup());
|
||||
|
||||
transFactory.addGroupInfo("g70", new InetSocketAddress("127.0.0.1", 7070));
|
||||
Service service = Sncp.createSimpleLocalService(SncpTestServiceImpl.class, transFactory, new InetSocketAddress("127.0.0.1", 7070), "g70");
|
||||
|
||||
28
test/org/redkale/test/sncp/_DynLocalSncpTestService.java
Normal file
28
test/org/redkale/test/sncp/_DynLocalSncpTestService.java
Normal file
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* 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.test.sncp;
|
||||
|
||||
import org.redkale.net.sncp.*;
|
||||
import org.redkale.util.ResourceType;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author zhangjx
|
||||
*/
|
||||
@ResourceType(SncpTestIService.class)
|
||||
public class _DynLocalSncpTestService extends SncpTestServiceImpl {
|
||||
|
||||
private SncpClient _redkale_client;
|
||||
|
||||
@SncpDyn(remote = false, index = 1)
|
||||
public long _redkale_queryLongResult(boolean selfrunnable, boolean samerunnable, boolean diffrunnable, String a, int b, long value) {
|
||||
long rs = super.queryLongResult(a, b, value);
|
||||
if (_redkale_client == null) return rs;
|
||||
if (samerunnable) _redkale_client.remoteSameGroup(1, true, false, false, a, b, value);
|
||||
if (diffrunnable) _redkale_client.remoteDiffGroup(1, true, true, false, a, b, value);
|
||||
return rs;
|
||||
}
|
||||
}
|
||||
@@ -15,7 +15,7 @@ import org.redkale.net.http.*;
|
||||
*
|
||||
* @author zhangjx
|
||||
*/
|
||||
@RestWebSocket(name = "chat", catalog = "ws", comment = "文字聊天")
|
||||
@RestWebSocket(name = "chat", catalog = "ws", comment = "文字聊天", anyuser = true)
|
||||
public class ChatWebSocket extends WebSocket<Integer, Object> {
|
||||
|
||||
protected static final AtomicInteger idcreator = new AtomicInteger(10000);
|
||||
|
||||
Reference in New Issue
Block a user