This commit is contained in:
@@ -1,8 +1,24 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
${APP_HOME} 指当前程序的总目录APP_HOME
|
||||
node: 进程节点的名称, 默认为空
|
||||
required: 被声明required的属性值不能为空
|
||||
文件说明:
|
||||
${APP_HOME} 指当前程序的总目录APP_HOME
|
||||
required: 被声明required的属性值不能为空
|
||||
|
||||
group
|
||||
/ / \ \
|
||||
/ / \ \
|
||||
/ / \ \
|
||||
node1 node2 node3 node4
|
||||
/ \
|
||||
/ \
|
||||
/ \
|
||||
/ \
|
||||
serviceid1 serviceid2
|
||||
/ \ / \
|
||||
serviceid1_name1 serviceid1_name2 serviceid2_name1 serviceid2_name2
|
||||
-->
|
||||
<!--
|
||||
address: 本地的IP地址, 默认值为默认网卡的ip,当不使用默认值需要指定值,如127.0.0.1
|
||||
port: required 程序的管理Server的端口,用于关闭或者与监管系统进行数据交互
|
||||
host: 程序的管理Server的地址; 默认为127.0.0.1。
|
||||
lib: 加上额外的lib路径,多个路径用分号;隔开; 默认为空。 例如: ${APP_HOME}/lib/a.jar;${APP_HOME}/lib2/b.jar;
|
||||
@@ -11,6 +27,28 @@
|
||||
|
||||
<!-- 所有服务所需的资源 -->
|
||||
<resources>
|
||||
|
||||
<!-- 设置系统的 DataCacheListener 的Service实现,值[none]表示不需要启动DataCacheListener同步, 默认为系统自带实现类 -->
|
||||
<datacachelistener service="none"/>
|
||||
|
||||
<!-- 设置系统的 WebSocketNode 的Service实现, 值[none]表示不需要启动WebSocketNode同步,默认为系统自带实现类 -->
|
||||
<websocketnode service="xxxx"/>
|
||||
|
||||
<!--
|
||||
一个组包含多个NODE, 同一Service服务可以由多个进程提供,这些进程称为一个GROUP,且同一GROUP内的进程必须在同一机房或局域网内
|
||||
name: 服务组ID,长度不能超过11个字节. 默认为空字符串。
|
||||
protocol:值只能是UDP TCP, 默认UDP
|
||||
-->
|
||||
<group name="" protocol="UDP">
|
||||
<!--
|
||||
需要将本地node的addr与port列在此处。
|
||||
addr: required IP地址
|
||||
port: required 端口
|
||||
clients: 连接池数, 默认: CPU核数*4
|
||||
buffers: ByteBuffer对象池的大小, 默认: CPU核数*8
|
||||
-->
|
||||
<node addr="127.0.0.1" port="7070"/>
|
||||
</group>
|
||||
<!--
|
||||
远程client地址组资源. 注意: remote的name值不能为LOCAL(不区分大小写)
|
||||
protocol: 值只能是UDP TCP, 默认UDP
|
||||
@@ -48,13 +86,14 @@
|
||||
protocol: required server所启动的协议,有HTTP、SNCP, 目前只支持HTTP、SNCP。SNCP也分TCP、UDP实现,默认使用UDP实现,TCP实现则使用SNCP.TCP值;
|
||||
host: 服务所占address , 默认: 0.0.0.0
|
||||
port: 服务所占端口 ,默认: 80
|
||||
group: 所属组的节点,多个节点值用;隔开,如果配置文件中存在多个SNCP协议的Server节点,需要显式指定group属性
|
||||
root: 如果是web类型服务,则包含页面 默认:{APP_HOME}/root
|
||||
lib: server额外的class目录, 默认为空
|
||||
charset: 文本编码, 默认: UTF-8
|
||||
backlog: 默认10K
|
||||
threads: 线程总数, 默认: CPU核数*16
|
||||
maxbody: request.body最大值, 默认: 64K
|
||||
capacity: ByteBuffer的初始化大小, 默认: 8K
|
||||
capacity: ByteBuffer的初始化大小, 默认: 8K; 如果是HTTP协议则默认: 16K + 8B (HTTP 2.0)
|
||||
bufferPoolSize: ByteBuffer池的大小,默认: CPU核数*512
|
||||
responsePoolSize: Response池的大小,默认: CPU核数*256
|
||||
readTimeoutSecond: 读操作超时秒数, 默认0, 表示永久不超时
|
||||
@@ -72,16 +111,13 @@
|
||||
autoload="false" 需要显著的指定Service类
|
||||
includes: 当autoload="true", 拉取类名与includes中的正则表达式匹配的类, 多个正则表达式用分号;隔开
|
||||
excludes: 当autoload="true", 排除类名与includes中的正则表达式匹配的类, 多个正则表达式用分号;隔开
|
||||
remote: 远程地址组的名称, 默认为空, 即本地模式。
|
||||
当<services>指定remote值为非空非LOCAL(即远程模式)时, 所有service的remote值均默认是<services>指定remote值。
|
||||
当<services>指定remote值为空(即本地模式)时, 所有service的remote值均默认是LOCAL值。
|
||||
-->
|
||||
<services autoload="true" includes="" excludes="" remote="myremote">
|
||||
<services autoload="true" includes="" excludes="">
|
||||
<!--
|
||||
大部分的情况下, 存在多个节点环境中很多service节点配置都一致,为此提供group节点来方便配置。
|
||||
remotenames: 远程模式Service的name名称集合, 多个用分号;隔开, 名称必须是在resources节点中定义的remote节点。
|
||||
以下group节点例子等价于:
|
||||
<service value="com.xxx.XXX0Service" name="LC001" remote="LOCAL"/>
|
||||
<service value="com.xxx.XXX0Service" name="" remote="LOCAL"/>
|
||||
<service value="com.xxx.XXX0Service" name="RT001" remote="RT001"/>
|
||||
<service value="com.xxx.XXX0Service" name="RT002" remote="RT002"/>
|
||||
<service value="com.xxx.XXX0Service" name="RT003" remote="RT003"/>
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
*/
|
||||
package com.wentch.redkale.boot;
|
||||
|
||||
import static com.wentch.redkale.boot.NodeServer.LINE_SEPARATOR;
|
||||
import com.wentch.redkale.convert.bson.*;
|
||||
import com.wentch.redkale.convert.json.*;
|
||||
import com.wentch.redkale.net.*;
|
||||
@@ -17,23 +16,23 @@ import com.wentch.redkale.util.*;
|
||||
import com.wentch.redkale.util.AnyValue.DefaultAnyValue;
|
||||
import com.wentch.redkale.watch.*;
|
||||
import java.io.*;
|
||||
import java.lang.reflect.*;
|
||||
import java.net.*;
|
||||
import java.nio.*;
|
||||
import java.nio.channels.*;
|
||||
import java.nio.file.*;
|
||||
import java.util.*;
|
||||
import java.util.AbstractMap.SimpleEntry;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.logging.*;
|
||||
import javax.annotation.*;
|
||||
import javax.xml.parsers.*;
|
||||
import org.w3c.dom.*;
|
||||
|
||||
/**
|
||||
* 编译时需要加入: -XDignore.symbol.file=true
|
||||
* <p>
|
||||
* 进程启动类,程序启动后读取application.xml,进行classpath扫描动态加载Service与Servlet, 再进行Service、Servlet与其他资源之间的依赖注入。
|
||||
* 进程启动类,程序启动后读取application.xml,进行classpath扫描动态加载Service与Servlet
|
||||
* 优先加载所有SNCP协议的服务, 再加载其他协议服务,
|
||||
* 最后进行Service、Servlet与其他资源之间的依赖注入。
|
||||
*
|
||||
*
|
||||
* @author zhangjx
|
||||
*/
|
||||
@@ -45,18 +44,6 @@ public final class Application {
|
||||
//当前进程的根目录, 类型:String
|
||||
public static final String RESNAME_HOME = "APP_HOME";
|
||||
|
||||
//当前进程节点的名称, 类型:String
|
||||
public static final String RESNAME_NODE = "APP_NODE";
|
||||
|
||||
//当前进程节点的所属组, 类型:String、Map<String, Set<String>>、Map<String, List<SimpleEntry<String, InetSocketAddress[]>>>
|
||||
public static final String RESNAME_GROUP = "APP_GROUP";
|
||||
|
||||
//当前进程节点的所属组所有节点名, 类型:Set<String> 、List<SimpleEntry<String, InetSocketAddress[]>>包含自身节点名
|
||||
public static final String RESNAME_INGROUP = "APP_INGROUP";
|
||||
|
||||
//除当前进程节点的所属组外其他所有组的所有节点名, 类型:Map<String, Set<String>>、Map<String, List<SimpleEntry<String, InetSocketAddress[]>>>
|
||||
public static final String RESNAME_OUTGROUP = "APP_OUTGROUP";
|
||||
|
||||
//当前进程节点的IP地址, 类型:InetAddress、String
|
||||
public static final String RESNAME_ADDR = "APP_ADDR";
|
||||
|
||||
@@ -67,17 +54,19 @@ public final class Application {
|
||||
|
||||
protected final WatchFactory watch = WatchFactory.root();
|
||||
|
||||
protected final HashMap<Class, ServiceEntry> localServices = new HashMap<>();
|
||||
protected final Map<InetSocketAddress, String> addrGroups = new HashMap<>();
|
||||
|
||||
protected final ArrayList<ServiceEntry> remoteServices = new ArrayList<>();
|
||||
protected final List<Transport> transports = new ArrayList<>();
|
||||
|
||||
protected boolean serviceInited = false;
|
||||
protected final InetAddress localAddress;
|
||||
|
||||
protected final InetAddress localAddress = Utility.localInetAddress();
|
||||
protected Class<? extends DataCacheListener> dataCacheListenerClass = DataCacheListenerService.class;
|
||||
|
||||
protected String nodeGroup = "";
|
||||
protected Class<? extends WebSocketNode> webSocketNodeClass = WebSocketNodeService.class;
|
||||
|
||||
protected String nodeName = "";
|
||||
protected final List<DataSource> sources = new CopyOnWriteArrayList<>();
|
||||
|
||||
protected final List<NodeServer> servers = new CopyOnWriteArrayList<>();
|
||||
|
||||
//--------------------------------------------------------------------------------------------
|
||||
private File home;
|
||||
@@ -86,10 +75,6 @@ public final class Application {
|
||||
|
||||
private final AnyValue config;
|
||||
|
||||
private final List<NodeServer> servers = new CopyOnWriteArrayList<>();
|
||||
|
||||
private final List<DataSource> sources = new CopyOnWriteArrayList<>();
|
||||
|
||||
private final long startTime = System.currentTimeMillis();
|
||||
|
||||
private CountDownLatch serverscdl;
|
||||
@@ -106,16 +91,12 @@ public final class Application {
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
String localaddr = config.getValue("address", "").trim();
|
||||
this.localAddress = localaddr.isEmpty() ? Utility.localInetAddress() : new InetSocketAddress(localaddr, 0).getAddress();
|
||||
Application.this.factory.register(RESNAME_ADDR, Application.this.localAddress.getHostAddress());
|
||||
Application.this.factory.register(RESNAME_ADDR, InetAddress.class, Application.this.localAddress);
|
||||
//以下是初始化日志配置
|
||||
final File logconf = new File(root, "conf/logging.properties");
|
||||
this.nodeName = config.getValue("node", "");
|
||||
this.nodeGroup = config.getValue("group", "");
|
||||
this.factory.register(RESNAME_NODE, this.nodeName);
|
||||
this.factory.register(RESNAME_GROUP, this.nodeGroup);
|
||||
System.setProperty(RESNAME_NODE, this.nodeName);
|
||||
System.setProperty(RESNAME_GROUP, this.nodeGroup);
|
||||
|
||||
this.factory.register(RESNAME_ADDR, this.localAddress.getHostAddress());
|
||||
this.factory.register(RESNAME_ADDR, InetAddress.class, this.localAddress);
|
||||
if (logconf.isFile() && logconf.canRead()) {
|
||||
try {
|
||||
final String rootpath = root.getCanonicalPath().replace('\\', '/');
|
||||
@@ -178,17 +159,16 @@ public final class Application {
|
||||
System.setProperty("convert.bson.writer.buffer.defsize", "4096");
|
||||
System.setProperty("convert.json.writer.buffer.defsize", "4096");
|
||||
|
||||
final File root = new File(System.getProperty(RESNAME_HOME));
|
||||
File persist = new File(root, "conf/persistence.xml");
|
||||
File persist = new File(this.home, "conf/persistence.xml");
|
||||
final String homepath = this.home.getCanonicalPath();
|
||||
if (persist.isFile()) System.setProperty(DataDefaultSource.DATASOURCE_CONFPATH, persist.getCanonicalPath());
|
||||
logger.log(Level.INFO, RESNAME_HOME + "=" + root.getCanonicalPath() + "\r\n" + RESNAME_ADDR + "=" + this.localAddress.getHostAddress());
|
||||
String lib = config.getValue("lib", "").trim().replace("${APP_HOME}", root.getCanonicalPath());
|
||||
lib = lib.isEmpty() ? (root.getCanonicalPath() + "/conf") : (lib + ";" + root.getCanonicalPath() + "/conf");
|
||||
logger.log(Level.INFO, RESNAME_HOME + "=" + homepath + "\r\n" + RESNAME_ADDR + "=" + this.localAddress.getHostAddress());
|
||||
String lib = config.getValue("lib", "").trim().replace("${APP_HOME}", homepath);
|
||||
lib = lib.isEmpty() ? (homepath + "/conf") : (lib + ";" + homepath + "/conf");
|
||||
Server.loadLib(logger, lib);
|
||||
initLogging();
|
||||
InetAddress addr = Utility.localInetAddress();
|
||||
if (addr != null) {
|
||||
byte[] bs = addr.getAddress();
|
||||
if (this.localAddress != null) {
|
||||
byte[] bs = this.localAddress.getAddress();
|
||||
int v = (0xff & bs[bs.length - 2]) % 10 * 100 + (0xff & bs[bs.length - 1]);
|
||||
this.factory.register("property.datasource.nodeid", "" + v);
|
||||
}
|
||||
@@ -232,14 +212,25 @@ public final class Application {
|
||||
initResources();
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public static void singleton(Service service) throws Exception {
|
||||
final Application application = Application.create();
|
||||
application.init();
|
||||
application.factory.register(service);
|
||||
new NodeHttpServer(application, new CountDownLatch(1), null).prepare(application.config);
|
||||
new NodeHttpServer(application, null, new CountDownLatch(1), null).prepare(application.config);
|
||||
application.factory.inject(service);
|
||||
}
|
||||
|
||||
public static <T extends Service> T singleton(Class<T> serviceClass) throws Exception {
|
||||
final Application application = Application.create();
|
||||
T service = Sncp.createLocalService("", serviceClass, null, null, null);
|
||||
application.init();
|
||||
application.factory.register(service);
|
||||
new NodeSncpServer(application, null, new CountDownLatch(1), null).init(application.config);
|
||||
application.factory.inject(service);
|
||||
return service;
|
||||
}
|
||||
|
||||
private static Application create() throws IOException {
|
||||
final String home = new File(System.getProperty(RESNAME_HOME, "")).getCanonicalPath();
|
||||
System.setProperty(RESNAME_HOME, home);
|
||||
@@ -257,7 +248,12 @@ public final class Application {
|
||||
}
|
||||
application.init();
|
||||
application.startSelfServer();
|
||||
application.start();
|
||||
try {
|
||||
application.start();
|
||||
} catch (Exception e) {
|
||||
application.logger.log(Level.SEVERE, "Application start error", e);
|
||||
System.exit(0);
|
||||
}
|
||||
System.exit(0);
|
||||
}
|
||||
|
||||
@@ -336,20 +332,38 @@ public final class Application {
|
||||
final AnyValue[] entrys = config.getAnyValues("server");
|
||||
this.serverscdl = new CountDownLatch(entrys.length + 1);
|
||||
CountDownLatch timecd = new CountDownLatch(entrys.length);
|
||||
runServers(timecd, entrys);
|
||||
final List<AnyValue> sncps = new ArrayList<>();
|
||||
final List<AnyValue> others = new ArrayList<>();
|
||||
for (final AnyValue entry : entrys) {
|
||||
if (entry.getValue("protocol", "").toUpperCase().startsWith("SNCP")) {
|
||||
sncps.add(entry);
|
||||
} else {
|
||||
others.add(entry);
|
||||
}
|
||||
}
|
||||
for (AnyValue sncpconf : sncps) {
|
||||
String host = sncpconf.getValue("host", "0.0.0.0").replace("0.0.0.0", "");
|
||||
InetSocketAddress addr = new InetSocketAddress(host.isEmpty() ? this.localAddress.getHostAddress() : host, sncpconf.getIntValue("port", 80));
|
||||
String oldgroup = addrGroups.get(addr);
|
||||
if (oldgroup != null && !((sncpconf.getValue("group", "") + ";").contains(oldgroup + ";"))) throw new RuntimeException(addr + " has one more group " + (addrGroups.get(addr)));
|
||||
if (oldgroup == null) addrGroups.put(addr, "");
|
||||
}
|
||||
runServers(timecd, sncps); //确保sncp都启动后再启动其他协议
|
||||
runServers(timecd, others);
|
||||
timecd.await();
|
||||
logger.info(this.getClass().getSimpleName() + " started in " + (System.currentTimeMillis() - startTime) + " ms");
|
||||
this.serverscdl.await();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private void runServers(CountDownLatch timecd, final AnyValue[] entrys) throws Exception {
|
||||
CountDownLatch servicecdl = new CountDownLatch(entrys.length);
|
||||
for (final AnyValue entry : entrys) {
|
||||
new Thread() {
|
||||
private void runServers(CountDownLatch timecd, final List<AnyValue> serconfs) throws Exception {
|
||||
CountDownLatch servicecdl = new CountDownLatch(serconfs.size());
|
||||
CountDownLatch sercdl = new CountDownLatch(serconfs.size());
|
||||
for (final AnyValue serconf : serconfs) {
|
||||
Thread thread = new Thread() {
|
||||
{
|
||||
String host = entry.getValue("host", "").replace("0.0.0.0", "");
|
||||
setName(entry.getValue("protocol", "Server").toUpperCase() + "-" + host + ":" + entry.getIntValue("port", 80) + "-Thread");
|
||||
String host = serconf.getValue("host", "").replace("0.0.0.0", "[0]");
|
||||
setName(serconf.getValue("protocol", "Server").toUpperCase() + "-" + host + ":" + serconf.getIntValue("port", 80) + "-Thread");
|
||||
this.setDaemon(true);
|
||||
}
|
||||
|
||||
@@ -358,167 +372,96 @@ public final class Application {
|
||||
try {
|
||||
//Thread ctd = Thread.currentThread();
|
||||
//ctd.setContextClassLoader(new URLClassLoader(new URL[0], ctd.getContextClassLoader()));
|
||||
String protocol = entry.getValue("protocol", "");
|
||||
String subprotocol = "UDP";
|
||||
String protocol = serconf.getValue("protocol", "");
|
||||
String subprotocol = Sncp.DEFAULT_PROTOCOL;
|
||||
int pos = protocol.indexOf('.');
|
||||
if (pos > 0) {
|
||||
subprotocol = protocol.substring(pos + 1);
|
||||
protocol = protocol.substring(0, pos);
|
||||
}
|
||||
NodeServer server = null;
|
||||
if ("HTTP".equalsIgnoreCase(protocol)) {
|
||||
server = new NodeHttpServer(Application.this, servicecdl, new HttpServer(startTime, watch));
|
||||
} else if ("SNCP".equalsIgnoreCase(protocol)) {
|
||||
server = new NodeSncpServer(Application.this, servicecdl, new SncpServer(startTime, subprotocol, watch));
|
||||
String host = serconf.getValue("host", "0.0.0.0").replace("0.0.0.0", "");
|
||||
InetSocketAddress addr = new InetSocketAddress(host.isEmpty() ? localAddress.getHostAddress() : host, serconf.getIntValue("port", 80));
|
||||
if ("SNCP".equalsIgnoreCase(protocol)) {
|
||||
server = new NodeSncpServer(Application.this, addr, servicecdl, new SncpServer(startTime, subprotocol, addr, watch));
|
||||
} else if ("HTTP".equalsIgnoreCase(protocol)) {
|
||||
server = new NodeHttpServer(Application.this, addr, servicecdl, new HttpServer(startTime, watch));
|
||||
}
|
||||
if (server == null) {
|
||||
logger.log(Level.SEVERE, "Not found Server Class for protocol({0})", entry.getValue("protocol"));
|
||||
logger.log(Level.SEVERE, "Not found Server Class for protocol({0})", serconf.getValue("protocol"));
|
||||
System.exit(0);
|
||||
}
|
||||
servers.add(server);
|
||||
|
||||
server.prepare(entry); //必须在init之前
|
||||
server.init(entry);
|
||||
server.init(serconf);
|
||||
server.start();
|
||||
timecd.countDown();
|
||||
sercdl.countDown();
|
||||
} catch (Exception ex) {
|
||||
logger.log(Level.WARNING, entry + " runServers error", ex);
|
||||
logger.log(Level.WARNING, serconf + " runServers error", ex);
|
||||
serverscdl.countDown();
|
||||
}
|
||||
}
|
||||
}.start();
|
||||
};
|
||||
thread.start();
|
||||
}
|
||||
sercdl.await();
|
||||
}
|
||||
|
||||
private void initResources() throws Exception {
|
||||
|
||||
this.factory.add(DataSource.class, (ResourceFactory rf, final Object src, Field field) -> {
|
||||
try {
|
||||
Resource rs = field.getAnnotation(Resource.class);
|
||||
if (rs == null) return;
|
||||
if (src.getClass().getAnnotation(RemoteOn.class) != null) return;
|
||||
DataSource source = DataSourceFactory.create(rs.name());
|
||||
sources.add(source);
|
||||
rf.register(rs.name(), DataSource.class, source);
|
||||
field.set(src, source);
|
||||
rf.inject(source); // 给 "datasource.nodeid" 赋值
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.SEVERE, "DataSource inject error", e);
|
||||
}
|
||||
});
|
||||
|
||||
this.factory.add(DataSQLListener.class, (ResourceFactory rf, Object src, Field field) -> {
|
||||
|
||||
try {
|
||||
Resource rs = field.getAnnotation(Resource.class);
|
||||
if (rs == null) return;
|
||||
if (src.getClass().getAnnotation(RemoteOn.class) != null) return;
|
||||
DataSQLListener service = rf.findChild("", DataSQLListener.class);
|
||||
if (service != null) {
|
||||
field.set(src, service);
|
||||
rf.inject(service);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.SEVERE, DataSQLListener.class.getSimpleName() + " inject error", e);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
this.factory.add(DataCacheListener.class, (ResourceFactory rf, Object src, Field field) -> {
|
||||
|
||||
try {
|
||||
Resource rs = field.getAnnotation(Resource.class);
|
||||
if (rs == null) return;
|
||||
if (src.getClass().getAnnotation(RemoteOn.class) != null) return;
|
||||
DataCacheListener service = rf.findChild("", DataCacheListener.class);
|
||||
if (service != null) {
|
||||
field.set(src, service);
|
||||
rf.inject(service);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.SEVERE, DataCacheListener.class.getSimpleName() + " inject error", e);
|
||||
}
|
||||
}
|
||||
);
|
||||
//-------------------------------------------------------------------------
|
||||
final AnyValue resources = config.getAnyValue("resources");
|
||||
if (resources != null) {
|
||||
//------------------------------------------------------------------------
|
||||
final String host = this.localAddress.getHostAddress();
|
||||
final Map<String, Set<String>> groups = new HashMap<>();
|
||||
final Map<String, List<SimpleEntry<String, InetSocketAddress[]>>> groups2 = new HashMap<>();
|
||||
|
||||
for (AnyValue conf : resources.getAnyValues("remote")) {
|
||||
final String name = conf.getValue("name");
|
||||
final String group = conf.getValue("group", "");
|
||||
if (name == null) throw new RuntimeException("remote name is null");
|
||||
String protocol = conf.getValue("protocol", "UDP").toUpperCase();
|
||||
if (!"TCP".equalsIgnoreCase(protocol) && !"UDP".equalsIgnoreCase(protocol)) {
|
||||
throw new RuntimeException("Not supported Transport Protocol " + conf.getValue("protocol"));
|
||||
}
|
||||
{
|
||||
Set<String> set = groups.get(group);
|
||||
if (set == null) {
|
||||
set = new HashSet<>();
|
||||
groups.put(group, set);
|
||||
AnyValue datacachelistenerConf = resources.getAnyValue("datacachelistener");
|
||||
if (datacachelistenerConf != null) {
|
||||
String val = datacachelistenerConf.getValue("service", "");
|
||||
if (!val.isEmpty()) {
|
||||
if ("none".equalsIgnoreCase(val)) {
|
||||
this.dataCacheListenerClass = null;
|
||||
} else {
|
||||
Class clazz = Class.forName(val);
|
||||
if (!DataCacheListener.class.isAssignableFrom(clazz) || !Service.class.isAssignableFrom(clazz)) {
|
||||
throw new RuntimeException("datacachelistener service (" + val + ") is illegal");
|
||||
}
|
||||
this.dataCacheListenerClass = clazz;
|
||||
}
|
||||
set.add(name);
|
||||
}
|
||||
AnyValue[] addrs = conf.getAnyValues("address");
|
||||
InetSocketAddress[] addresses = new InetSocketAddress[addrs.length];
|
||||
int i = -1;
|
||||
for (AnyValue addr : addrs) {
|
||||
addresses[++i] = new InetSocketAddress(addr.getValue("addr"), addr.getIntValue("port"));
|
||||
}
|
||||
if (addresses.length < 1) throw new RuntimeException("Transport(" + name + ") have no address ");
|
||||
{
|
||||
List<SimpleEntry<String, InetSocketAddress[]>> list = groups2.get(group);
|
||||
if (list == null) {
|
||||
list = new ArrayList<>();
|
||||
groups2.put(group, list);
|
||||
}
|
||||
list.add(new SimpleEntry<>(name, addresses));
|
||||
}
|
||||
Transport transport = new Transport(name, protocol, conf.getIntValue("clients", Runtime.getRuntime().availableProcessors() * 8),
|
||||
conf.getIntValue("buffers:", Runtime.getRuntime().availableProcessors() * 16), watch, addresses);
|
||||
factory.register(name, Transport.class, transport);
|
||||
if (this.nodeName.isEmpty() && host.equals(addrs[0].getValue("addr"))) {
|
||||
this.nodeName = name;
|
||||
this.nodeGroup = group;
|
||||
this.factory.register(RESNAME_NODE, this.nodeName);
|
||||
this.factory.register(RESNAME_GROUP, this.nodeGroup);
|
||||
System.setProperty(RESNAME_NODE, this.nodeName);
|
||||
System.setProperty(RESNAME_GROUP, this.nodeGroup);
|
||||
}
|
||||
}
|
||||
//------------------------------------------------------------------------
|
||||
AnyValue websocketnodeConf = resources.getAnyValue("websocketnode");
|
||||
if (websocketnodeConf != null) {
|
||||
String val = websocketnodeConf.getValue("service", "");
|
||||
if (!val.isEmpty()) {
|
||||
if ("none".equalsIgnoreCase(val)) {
|
||||
this.webSocketNodeClass = null;
|
||||
} else {
|
||||
Class clazz = Class.forName(val);
|
||||
if (!WebSocketNode.class.isAssignableFrom(clazz) || !Service.class.isAssignableFrom(clazz)) {
|
||||
throw new RuntimeException("websocketnode service (" + val + ") is illegal");
|
||||
}
|
||||
this.webSocketNodeClass = clazz;
|
||||
}
|
||||
}
|
||||
}
|
||||
//------------------------------------------------------------------------
|
||||
final Map<String, Set<String>> groups = new HashMap<>();
|
||||
|
||||
this.factory.register(RESNAME_GROUP, new TypeToken<Map<String, Set<String>>>() {
|
||||
}.getType(), groups);
|
||||
this.factory.register(RESNAME_GROUP, new TypeToken<Map<String, List<SimpleEntry<String, InetSocketAddress[]>>>>() {
|
||||
}.getType(), groups2);
|
||||
|
||||
final Map<String, List<SimpleEntry<String, InetSocketAddress[]>>> outgroups2 = new HashMap<>();
|
||||
final Map<String, Set<String>> outgroups = new HashMap<>();
|
||||
groups.entrySet().stream().filter(x -> !x.getKey().equals(nodeName)).forEach(x -> outgroups.put(x.getKey(), x.getValue()));
|
||||
groups2.entrySet().stream().filter(x -> !x.getKey().equals(nodeName)).forEach(x -> outgroups2.put(x.getKey(), x.getValue()));
|
||||
|
||||
this.factory.register(RESNAME_OUTGROUP, new TypeToken<Map<String, Set<String>>>() {
|
||||
}.getType(), outgroups);
|
||||
this.factory.register(RESNAME_OUTGROUP, new TypeToken<Map<String, List<SimpleEntry<String, InetSocketAddress[]>>>>() {
|
||||
}.getType(), outgroups2);
|
||||
|
||||
Set<String> ingroup = groups.get(this.nodeGroup);
|
||||
if (ingroup != null) this.factory.register(RESNAME_INGROUP, new TypeToken<Set<String>>() {
|
||||
}.getType(), ingroup);
|
||||
List<SimpleEntry<String, InetSocketAddress[]>> inengroup = groups2.get(this.nodeGroup);
|
||||
if (inengroup != null) this.factory.register(RESNAME_INGROUP, new TypeToken<List<SimpleEntry<String, InetSocketAddress[]>>>() {
|
||||
}.getType(), inengroup);
|
||||
for (AnyValue conf : resources.getAnyValues("group")) {
|
||||
final String group = conf.getValue("name", "");
|
||||
String protocol = conf.getValue("protocol", Sncp.DEFAULT_PROTOCOL).toUpperCase();
|
||||
if (!"TCP".equalsIgnoreCase(protocol) && !Sncp.DEFAULT_PROTOCOL.equalsIgnoreCase(protocol)) {
|
||||
throw new RuntimeException("Not supported Transport Protocol " + conf.getValue("protocol"));
|
||||
}
|
||||
List<InetSocketAddress> addrs = new ArrayList<>();
|
||||
for (AnyValue node : conf.getAnyValues("node")) {
|
||||
InetSocketAddress addr = new InetSocketAddress(node.getValue("addr"), node.getIntValue("port"));
|
||||
if (addrGroups.containsKey(addr)) throw new RuntimeException(addr + " had one more group " + (addrGroups.get(addr)));
|
||||
addrGroups.put(addr, group);
|
||||
addrs.add(addr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
logger.info(RESNAME_NODE + "=" + this.nodeName + "; " + RESNAME_GROUP + "=" + this.nodeGroup);
|
||||
logger.info("datasource.nodeid=" + this.factory.find("property.datasource.nodeid", String.class));
|
||||
|
||||
}
|
||||
|
||||
private void shutdown() throws Exception {
|
||||
@@ -531,18 +474,6 @@ public final class Application {
|
||||
serverscdl.countDown();
|
||||
}
|
||||
});
|
||||
final StringBuilder sb = logger.isLoggable(Level.INFO) ? new StringBuilder() : null;
|
||||
localServices.entrySet().stream().forEach(k -> {
|
||||
Class x = k.getKey();
|
||||
ServiceEntry y = k.getValue();
|
||||
long s = System.currentTimeMillis();
|
||||
y.getService().destroy(y.getServiceConf());
|
||||
long e = System.currentTimeMillis() - s;
|
||||
if (e > 2 && sb != null) {
|
||||
sb.append("LocalService(").append(y.getNames()).append("|").append(y.getServiceClass()).append(") destroy ").append(e).append("ms").append(LINE_SEPARATOR);
|
||||
}
|
||||
});
|
||||
if (sb != null && sb.length() > 0) logger.log(Level.INFO, sb.toString());
|
||||
for (DataSource source : sources) {
|
||||
try {
|
||||
source.getClass().getMethod("close").invoke(source);
|
||||
|
||||
@@ -37,11 +37,27 @@ public final class ClassFilter<T> {
|
||||
|
||||
private Pattern[] excludePatterns;
|
||||
|
||||
private List<ClassFilter> ors;
|
||||
|
||||
private List<ClassFilter> ands;
|
||||
|
||||
public ClassFilter(Class<? extends Annotation> annotationClass, Class superClass) {
|
||||
this.annotationClass = annotationClass;
|
||||
this.superClass = superClass;
|
||||
}
|
||||
|
||||
public ClassFilter<T> or(ClassFilter<T> filter) {
|
||||
if (ors == null) ors = new ArrayList<>();
|
||||
ors.add(filter);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ClassFilter<T> and(ClassFilter<T> filter) {
|
||||
if (ands == null) ands = new ArrayList<>();
|
||||
ands.add(filter);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取符合条件的class集合
|
||||
* <p>
|
||||
@@ -74,8 +90,7 @@ public final class ClassFilter<T> {
|
||||
try {
|
||||
Class clazz = Class.forName(clazzname);
|
||||
if (accept(property, clazz, autoscan)) {
|
||||
FilterEntry en = new FilterEntry(clazz, property);
|
||||
if (!entrys.contains(en)) entrys.add(en);
|
||||
entrys.add(new FilterEntry(clazz, property));
|
||||
}
|
||||
} catch (Throwable cfe) {
|
||||
}
|
||||
@@ -104,6 +119,21 @@ public final class ClassFilter<T> {
|
||||
* @return
|
||||
*/
|
||||
public boolean accept(AnyValue property, String classname) {
|
||||
boolean r = accept0(property, classname);
|
||||
if (r && ands != null) {
|
||||
for (ClassFilter filter : ands) {
|
||||
if (!filter.accept(property, classname)) return false;
|
||||
}
|
||||
}
|
||||
if (!r && ors != null) {
|
||||
for (ClassFilter filter : ors) {
|
||||
if (filter.accept(property, classname)) return true;
|
||||
}
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
private boolean accept0(AnyValue property, String classname) {
|
||||
if (this.refused) return false;
|
||||
if (classname.startsWith("java.") || classname.startsWith("javax.")) return false;
|
||||
if (excludePatterns != null) {
|
||||
@@ -186,16 +216,17 @@ public final class ClassFilter<T> {
|
||||
*/
|
||||
public static final class FilterEntry<T> {
|
||||
|
||||
private String group;
|
||||
|
||||
private final String name;
|
||||
|
||||
private final Class<T> type;
|
||||
|
||||
private final AnyValue property;
|
||||
|
||||
protected Object attachment;
|
||||
|
||||
public FilterEntry(Class<T> type, AnyValue property) {
|
||||
this.type = type;
|
||||
this.group = property == null ? "" : property.getValue("group", "");
|
||||
this.property = property;
|
||||
this.name = property == null ? "" : property.getValue("name", "");
|
||||
}
|
||||
@@ -203,8 +234,7 @@ public final class ClassFilter<T> {
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.getClass().getSimpleName() + "[thread=" + Thread.currentThread().getName()
|
||||
+ ", type=" + this.type.getSimpleName() + ", name=" + name
|
||||
+ ", remote=" + (property == null ? "null" : property.getValue("remote")) + "]";
|
||||
+ ", type=" + this.type.getSimpleName() + ", name=" + name + ", group=" + group + "]";
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -216,7 +246,7 @@ public final class ClassFilter<T> {
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == null) return false;
|
||||
if (getClass() != obj.getClass()) return false;
|
||||
return (this.type == ((FilterEntry<?>) obj).type && this.name.equals(((FilterEntry<?>) obj).name));
|
||||
return (this.type == ((FilterEntry<?>) obj).type && this.group.equals(((FilterEntry<?>) obj).group) && this.name.equals(((FilterEntry<?>) obj).name));
|
||||
}
|
||||
|
||||
public Class<T> getType() {
|
||||
@@ -231,14 +261,13 @@ public final class ClassFilter<T> {
|
||||
return property;
|
||||
}
|
||||
|
||||
public Object getAttachment() {
|
||||
return attachment;
|
||||
public String getGroup() {
|
||||
return group;
|
||||
}
|
||||
|
||||
public void setAttachment(Object attachment) {
|
||||
this.attachment = attachment;
|
||||
public void setGroup(String group) {
|
||||
this.group = group;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -10,14 +10,21 @@ import com.wentch.redkale.net.http.HttpServer;
|
||||
import com.wentch.redkale.net.http.HttpServlet;
|
||||
import com.wentch.redkale.util.AnyValue;
|
||||
import com.wentch.redkale.boot.ClassFilter.FilterEntry;
|
||||
import com.wentch.redkale.service.Service;
|
||||
import java.lang.reflect.Modifier;
|
||||
import com.wentch.redkale.net.*;
|
||||
import com.wentch.redkale.net.http.*;
|
||||
import com.wentch.redkale.net.sncp.*;
|
||||
import com.wentch.redkale.service.*;
|
||||
import com.wentch.redkale.util.*;
|
||||
import java.io.*;
|
||||
import java.lang.reflect.*;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.logging.*;
|
||||
import javax.annotation.*;
|
||||
|
||||
/**
|
||||
* HTTP Server节点的配置Server
|
||||
*
|
||||
* @author zhangjx
|
||||
*/
|
||||
@@ -25,15 +32,12 @@ public final class NodeHttpServer extends NodeServer {
|
||||
|
||||
private final HttpServer server;
|
||||
|
||||
public NodeHttpServer(Application application, CountDownLatch servicecdl, HttpServer server) {
|
||||
super(application, servicecdl, server);
|
||||
this.server = server;
|
||||
}
|
||||
private final File home;
|
||||
|
||||
@Override
|
||||
public void init(AnyValue config) throws Exception {
|
||||
server.init(config);
|
||||
super.init(config);
|
||||
public NodeHttpServer(Application application, InetSocketAddress addr, CountDownLatch servicecdl, HttpServer server) {
|
||||
super(application, application.factory.createChild(), servicecdl, server);
|
||||
this.server = server;
|
||||
this.home = application.getHome();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -43,22 +47,81 @@ public final class NodeHttpServer extends NodeServer {
|
||||
|
||||
@Override
|
||||
public void prepare(AnyValue config) throws Exception {
|
||||
super.prepare(config);
|
||||
ClassFilter<HttpServlet> httpFilter = createHttpServletClassFilter(application.nodeName, config);
|
||||
ClassFilter<Service> serviceFilter = createServiceClassFilter(application.nodeName, config);
|
||||
ClassFilter<HttpServlet> httpFilter = createClassFilter(null, config, WebServlet.class, HttpServlet.class, null, "servlets", "servlet");
|
||||
ClassFilter<Service> serviceFilter = createServiceClassFilter(config);
|
||||
long s = System.currentTimeMillis();
|
||||
ClassFilter.Loader.load(application.getHome(), serviceFilter, httpFilter);
|
||||
ClassFilter.Loader.load(home, serviceFilter, httpFilter);
|
||||
long e = System.currentTimeMillis() - s;
|
||||
logger.info(this.getClass().getSimpleName() + " load filter class in " + e + " ms");
|
||||
loadService(config.getAnyValue("services"), serviceFilter); //必须在servlet之前
|
||||
if (server != null) initHttpServlet(config.getAnyValue("servlets"), httpFilter);
|
||||
initWebSocketService();
|
||||
if (server != null) loadHttpServlet(config.getAnyValue("servlets"), httpFilter);
|
||||
}
|
||||
|
||||
protected static ClassFilter<HttpServlet> createHttpServletClassFilter(final String node, final AnyValue config) {
|
||||
return createClassFilter(node, config, WebServlet.class, HttpServlet.class, null, "servlets", "servlet");
|
||||
private void initWebSocketService() {
|
||||
String defgroup = servconf.getValue("group", ""); //Server节点获取group信息
|
||||
NodeSncpServer firstnss = null;
|
||||
NodeSncpServer sncpServer = null;
|
||||
for (NodeServer ns : application.servers) {
|
||||
if (!(ns instanceof NodeSncpServer)) continue;
|
||||
final NodeSncpServer nss = (NodeSncpServer) ns;
|
||||
if (firstnss == null) firstnss = nss;
|
||||
if (defgroup.equals(nss.nodeGroup)) {
|
||||
sncpServer = nss;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (sncpServer == null) sncpServer = firstnss;
|
||||
if (defgroup.isEmpty() && sncpServer != null) {
|
||||
defgroup = sncpServer.servconf.getValue("group", "");
|
||||
}
|
||||
final String localGroup = sncpServer == null ? this.nodeGroup : sncpServer.nodeGroup;
|
||||
final InetSocketAddress localAddr = sncpServer == null ? this.servaddr : sncpServer.servaddr;
|
||||
final List<Transport>[] transportses = parseTransport(defgroup, localGroup, localAddr);
|
||||
final List<Transport> sameGroupTransports = transportses[0];
|
||||
final List<Transport> diffGroupTransports = transportses[1];
|
||||
//---------------------------------------------------------------------------------------------
|
||||
final NodeSncpServer onesncpServer = sncpServer;
|
||||
final ResourceFactory regFactory = application.factory;
|
||||
final String subprotocol = sncpServer == null ? Sncp.DEFAULT_PROTOCOL : sncpServer.getSncpServer().getProtocol();
|
||||
factory.add(WebSocketNode.class, (ResourceFactory rf, final Object src, Field field) -> {
|
||||
try {
|
||||
Resource rs = field.getAnnotation(Resource.class);
|
||||
if (rs == null) return;
|
||||
if (!(src instanceof WebSocketServlet)) return;
|
||||
String rcname = rs.name();
|
||||
if (rcname.equals(ResourceFactory.RESOURCE_PARENT_NAME)) rcname = ((WebSocketServlet) src).name();
|
||||
synchronized (regFactory) {
|
||||
Service nodeService = (Service) rf.find(rcname, WebSocketNode.class);
|
||||
if (nodeService == null) {
|
||||
Class<? extends Service> sc = (Class<? extends Service>) application.webSocketNodeClass;
|
||||
nodeService = Sncp.createLocalService(rcname, (Class<? extends Service>) (sc == null ? WebSocketNodeService.class : sc),
|
||||
localAddr, (sc == null ? null : sameGroupTransports), (sc == null ? null : diffGroupTransports));
|
||||
regFactory.register(rcname, WebSocketNode.class, nodeService);
|
||||
WebSocketNode wsn = (WebSocketNode) nodeService;
|
||||
wsn.setLocalSncpAddress(localAddr);
|
||||
final Set<InetSocketAddress> alladdrs = new HashSet<>();
|
||||
application.addrGroups.forEach((k, v) -> alladdrs.add(k));
|
||||
alladdrs.remove(localAddr);
|
||||
WebSocketNode remoteNode = (WebSocketNode) Sncp.createRemoteService(rcname, (Class<? extends Service>) (sc == null ? WebSocketNodeService.class : sc),
|
||||
localAddr, (sc == null ? null : loadTransport(localGroup, subprotocol, alladdrs)));
|
||||
wsn.setRemoteWebSocketNode(remoteNode);
|
||||
factory.inject(nodeService);
|
||||
factory.inject(remoteNode);
|
||||
if (onesncpServer != null) {
|
||||
ServiceWrapper wrapper = new ServiceWrapper((Class<? extends Service>) (sc == null ? WebSocketNodeService.class : sc), nodeService, localGroup, rcname, null);
|
||||
onesncpServer.getSncpServer().addService(wrapper);
|
||||
}
|
||||
}
|
||||
field.set(src, nodeService);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.SEVERE, "WebSocketNode inject error", e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected void initHttpServlet(final AnyValue conf, final ClassFilter<HttpServlet> filter) throws Exception {
|
||||
protected void loadHttpServlet(final AnyValue conf, final ClassFilter<HttpServlet> filter) throws Exception {
|
||||
final StringBuilder sb = logger.isLoggable(Level.FINE) ? new StringBuilder() : null;
|
||||
final String prefix = conf == null ? "" : conf.getValue("prefix", "");
|
||||
final String threadName = "[" + Thread.currentThread().getName() + "] ";
|
||||
@@ -68,7 +131,7 @@ public final class NodeHttpServer extends NodeServer {
|
||||
WebServlet ws = clazz.getAnnotation(WebServlet.class);
|
||||
if (ws == null || ws.value().length == 0) continue;
|
||||
final HttpServlet servlet = clazz.newInstance();
|
||||
application.factory.inject(servlet);
|
||||
factory.inject(servlet);
|
||||
String[] mappings = ws.value();
|
||||
if (ws.fillurl() && !prefix.isEmpty()) {
|
||||
for (int i = 0; i < mappings.length; i++) {
|
||||
@@ -80,4 +143,9 @@ public final class NodeHttpServer extends NodeServer {
|
||||
}
|
||||
if (sb != null && sb.length() > 0) logger.log(Level.FINE, sb.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSNCP() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,15 +5,16 @@
|
||||
*/
|
||||
package com.wentch.redkale.boot;
|
||||
|
||||
import com.wentch.redkale.net.sncp.ServiceEntry;
|
||||
import com.wentch.redkale.net.sncp.ServiceWrapper;
|
||||
import com.wentch.redkale.net.Server;
|
||||
import com.wentch.redkale.net.sncp.Sncp;
|
||||
import com.wentch.redkale.service.Service;
|
||||
import com.wentch.redkale.service.MultiService;
|
||||
import com.wentch.redkale.util.AnyValue;
|
||||
import com.wentch.redkale.util.Ignore;
|
||||
import com.wentch.redkale.boot.ClassFilter.FilterEntry;
|
||||
import com.wentch.redkale.util.AnyValue.DefaultAnyValue;
|
||||
import com.wentch.redkale.net.*;
|
||||
import com.wentch.redkale.source.*;
|
||||
import com.wentch.redkale.util.*;
|
||||
import java.io.*;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.*;
|
||||
@@ -22,6 +23,7 @@ import java.util.*;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.logging.*;
|
||||
import javax.annotation.*;
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -35,23 +37,36 @@ public abstract class NodeServer {
|
||||
|
||||
protected final Application application;
|
||||
|
||||
protected final ResourceFactory factory;
|
||||
|
||||
private final CountDownLatch servicecdl;
|
||||
|
||||
private final Server server;
|
||||
|
||||
protected Consumer<ServiceEntry> consumer;
|
||||
protected AnyValue servconf;
|
||||
|
||||
public NodeServer(Application application, CountDownLatch servicecdl, Server server) {
|
||||
protected InetSocketAddress servaddr;
|
||||
|
||||
protected String nodeGroup = "";
|
||||
|
||||
protected Consumer<ServiceWrapper> consumer;
|
||||
|
||||
protected final Set<ServiceWrapper> localServices = new LinkedHashSet<>();
|
||||
|
||||
protected final Set<ServiceWrapper> remoteServices = new LinkedHashSet<>();
|
||||
|
||||
public NodeServer(Application application, ResourceFactory factory, CountDownLatch servicecdl, Server server) {
|
||||
this.application = application;
|
||||
this.factory = factory;
|
||||
this.servicecdl = servicecdl;
|
||||
this.server = server;
|
||||
this.logger = Logger.getLogger(this.getClass().getSimpleName());
|
||||
}
|
||||
|
||||
public void prepare(final AnyValue config) throws Exception {
|
||||
}
|
||||
protected abstract void prepare(final AnyValue config) throws Exception;
|
||||
|
||||
public void init(AnyValue config) throws Exception {
|
||||
this.servconf = config == null ? new AnyValue.DefaultAnyValue() : config;
|
||||
//设置root文件夹
|
||||
String webroot = config.getValue("root", "root");
|
||||
File myroot = new File(webroot);
|
||||
@@ -60,64 +75,114 @@ public abstract class NodeServer {
|
||||
}
|
||||
final String homepath = myroot.getCanonicalPath();
|
||||
Server.loadLib(logger, config.getValue("lib", "") + ";" + homepath + "/lib/*;" + homepath + "/classes");
|
||||
if (server != null) server.init(config);
|
||||
initResource();
|
||||
prepare(config);
|
||||
}
|
||||
|
||||
private void initResource() {
|
||||
if (!isSNCP()) return;
|
||||
final String defgroup = servconf.getValue("group", ""); //Server节点获取group信息
|
||||
final List<Transport>[] transportses = parseTransport(defgroup, this.nodeGroup, this.servaddr);
|
||||
final List<Transport> sameGroupTransports = transportses[0];
|
||||
final List<Transport> diffGroupTransports = transportses[1];
|
||||
//---------------------------------------------------------------------------------------------
|
||||
final ResourceFactory regFactory = application.factory;
|
||||
regFactory.add(DataSource.class, (ResourceFactory rf, final Object src, Field field) -> {
|
||||
try {
|
||||
Resource rs = field.getAnnotation(Resource.class);
|
||||
if (rs == null) return;
|
||||
if ((src instanceof Service) && Sncp.isRemote((Service) src)) return;
|
||||
DataSource source = DataSourceFactory.create(rs.name());
|
||||
application.sources.add(source);
|
||||
regFactory.register(rs.name(), DataSource.class, source);
|
||||
Class<? extends Service> sc = (Class<? extends Service>) application.dataCacheListenerClass;
|
||||
if (sc != null) {
|
||||
Service cacheListenerService = Sncp.createLocalService(rs.name(), sc, this.servaddr, sameGroupTransports, diffGroupTransports);
|
||||
regFactory.register(rs.name(), DataCacheListener.class, cacheListenerService);
|
||||
ServiceWrapper wrapper = new ServiceWrapper(sc, cacheListenerService, nodeGroup, rs.name(), null);
|
||||
localServices.add(wrapper);
|
||||
if (consumer != null) consumer.accept(wrapper);
|
||||
rf.inject(cacheListenerService);
|
||||
}
|
||||
field.set(src, source);
|
||||
rf.inject(source); // 给 "datasource.nodeid" 赋值
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.SEVERE, "DataSource inject error", e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected List<Transport>[] parseTransport(final String group, final String localGroup, final InetSocketAddress addr) {
|
||||
final Set<InetSocketAddress> sameGroupAddrs = new LinkedHashSet<>();
|
||||
final Map<String, Set<InetSocketAddress>> diffGroupAddrs = new HashMap<>();
|
||||
for (String str : group.split(";")) {
|
||||
application.addrGroups.forEach((k, v) -> {
|
||||
if (v.equals(str)) {
|
||||
if (v.equals(localGroup)) {
|
||||
sameGroupAddrs.add(k);
|
||||
} else {
|
||||
Set<InetSocketAddress> set = diffGroupAddrs.get(v);
|
||||
if (set == null) {
|
||||
set = new LinkedHashSet<>();
|
||||
diffGroupAddrs.put(v, set);
|
||||
}
|
||||
set.add(k);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
sameGroupAddrs.remove(addr);
|
||||
final List<Transport> sameGroupTransports = new ArrayList<>();
|
||||
for (InetSocketAddress iaddr : sameGroupAddrs) {
|
||||
Set<InetSocketAddress> tset = new HashSet<>();
|
||||
tset.add(iaddr);
|
||||
sameGroupTransports.add(loadTransport(localGroup, server.getProtocol(), tset));
|
||||
}
|
||||
final List<Transport> diffGroupTransports = new ArrayList<>();
|
||||
diffGroupAddrs.forEach((k, v) -> diffGroupTransports.add(loadTransport(k, server.getProtocol(), v)));
|
||||
return new List[]{sameGroupTransports, diffGroupTransports};
|
||||
}
|
||||
|
||||
public abstract InetSocketAddress getSocketAddress();
|
||||
|
||||
public abstract boolean isSNCP();
|
||||
|
||||
public void start() throws IOException {
|
||||
server.start();
|
||||
}
|
||||
|
||||
public void shutdown() throws IOException {
|
||||
final StringBuilder sb = logger.isLoggable(Level.INFO) ? new StringBuilder() : null;
|
||||
localServices.forEach(y -> {
|
||||
long s = System.currentTimeMillis();
|
||||
y.getService().destroy(y.getConf());
|
||||
long e = System.currentTimeMillis() - s;
|
||||
if (e > 2 && sb != null) {
|
||||
sb.append("LocalServices(").append(y.getType()).append(':').append(y.getName()).append(") destroy ").append(e).append("ms").append(LINE_SEPARATOR);
|
||||
}
|
||||
});
|
||||
if (sb != null && sb.length() > 0) logger.log(Level.INFO, sb.toString());
|
||||
server.shutdown();
|
||||
}
|
||||
|
||||
protected void loadLocalService(final Set<FilterEntry<Service>> entrys) throws Exception {
|
||||
final HashMap<Class, ServiceEntry> localServices = application.localServices;
|
||||
for (final FilterEntry<Service> entry : entrys) {
|
||||
Class<? extends Service> serviceClass = entry.getType();
|
||||
if (serviceClass.getAnnotation(Ignore.class) != null) continue;
|
||||
final boolean multi = MultiService.class.isAssignableFrom(serviceClass);
|
||||
if (!multi) { //单例模式
|
||||
synchronized (application.localServices) {
|
||||
ServiceEntry old = localServices.get(serviceClass);
|
||||
if (old == null) {
|
||||
old = new ServiceEntry(serviceClass, (Service) serviceClass.newInstance(), entry.getProperty(), "");
|
||||
localServices.put(serviceClass, old);
|
||||
protected Transport loadTransport(String group, String protocol, Set<InetSocketAddress> addrs) {
|
||||
Transport transport = null;
|
||||
if (!addrs.isEmpty()) {
|
||||
synchronized (application.transports) {
|
||||
for (Transport tran : application.transports) {
|
||||
if (tran.match(addrs)) {
|
||||
transport = tran;
|
||||
break;
|
||||
}
|
||||
if (consumer != null) consumer.accept(old);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
String name = multi ? entry.getName() : "";
|
||||
synchronized (application.localServices) {
|
||||
ServiceEntry old = localServices.get(serviceClass);
|
||||
if (old != null && old.containsName(name)) {
|
||||
if (consumer != null) consumer.accept(old);
|
||||
continue;
|
||||
if (transport == null) {
|
||||
transport = new Transport(group + "_" + application.transports.size(), protocol, application.watch, 32, addrs);
|
||||
application.transports.add(transport);
|
||||
}
|
||||
final Service service = (Service) serviceClass.newInstance();
|
||||
if (old == null) {
|
||||
old = new ServiceEntry(serviceClass, service, entry.getProperty(), name);
|
||||
localServices.put(serviceClass, old);
|
||||
} else {
|
||||
old.addName(name);
|
||||
}
|
||||
if (consumer != null) consumer.accept(old);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void loadRemoteService(final Set<FilterEntry<Service>> entrys) throws Exception {
|
||||
for (FilterEntry<Service> entry : entrys) {
|
||||
Class<Service> serviceClass = entry.getType();
|
||||
if (serviceClass.getAnnotation(Ignore.class) != null) continue;
|
||||
String remote = entry.getAttachment().toString();
|
||||
Service service = Sncp.createRemoteService(entry.getName(), serviceClass, remote);
|
||||
synchronized (application.remoteServices) {
|
||||
application.remoteServices.add(new ServiceEntry(serviceClass, service, entry.getProperty(), entry.getName()));
|
||||
}
|
||||
}
|
||||
return transport;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@@ -125,126 +190,129 @@ public abstract class NodeServer {
|
||||
if (serviceFilter == null) return;
|
||||
final String threadName = "[" + Thread.currentThread().getName() + "] ";
|
||||
final Set<FilterEntry<Service>> entrys = serviceFilter.getFilterEntrys();
|
||||
final Set<FilterEntry<Service>> localentrys = new HashSet<>(entrys.size());
|
||||
final Set<FilterEntry<Service>> remotentrys = new HashSet<>(entrys.size());
|
||||
final String defremote = getRemoteName(servicesConf, "LOCAL");
|
||||
final String defgroup = servconf == null ? "" : servconf.getValue("group", ""); //Server节点获取group信息
|
||||
ResourceFactory regFactory = isSNCP() ? application.factory : factory;
|
||||
for (FilterEntry<Service> entry : entrys) { //service实现类
|
||||
final Class type = entry.getType();
|
||||
final Class<? extends Service> type = entry.getType();
|
||||
if (type.isInterface()) continue;
|
||||
if (Modifier.isFinal(type.getModifiers())) continue;
|
||||
if (!Modifier.isPublic(type.getModifiers())) continue;
|
||||
if (Modifier.isAbstract(type.getModifiers())) continue;
|
||||
if (type.getAnnotation(Ignore.class) != null) continue;
|
||||
final String remote = getRemoteName(entry.getProperty(), defremote);
|
||||
if ("LOCAL".equals(remote)) { //本地模式
|
||||
localentrys.add(entry);
|
||||
} else { //远程模式
|
||||
entry.setAttachment(remote);
|
||||
remotentrys.add(entry);
|
||||
if (!isSNCP() && factory.find(entry.getName(), type) != null) continue;
|
||||
String group = entry.getGroup();
|
||||
if (group == null || group.isEmpty()) group = defgroup;
|
||||
final Set<InetSocketAddress> sameGroupAddrs = new LinkedHashSet<>();
|
||||
final Map<String, Set<InetSocketAddress>> diffGroupAddrs = new HashMap<>();
|
||||
for (String str : group.split(";")) {
|
||||
application.addrGroups.forEach((k, v) -> {
|
||||
if (v.equals(str)) {
|
||||
if (v.equals(this.nodeGroup)) {
|
||||
sameGroupAddrs.add(k);
|
||||
} else {
|
||||
Set<InetSocketAddress> set = diffGroupAddrs.get(v);
|
||||
if (set == null) {
|
||||
set = new LinkedHashSet<>();
|
||||
diffGroupAddrs.put(v, set);
|
||||
}
|
||||
set.add(k);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
final boolean localable = sameGroupAddrs.contains(this.servaddr);
|
||||
Service service;
|
||||
|
||||
List<Transport> diffGroupTransports = new ArrayList<>();
|
||||
diffGroupAddrs.forEach((k, v) -> diffGroupTransports.add(loadTransport(k, server.getProtocol(), v)));
|
||||
|
||||
if (localable || (sameGroupAddrs.isEmpty() && diffGroupTransports.isEmpty())) {
|
||||
sameGroupAddrs.remove(this.servaddr);
|
||||
List<Transport> sameGroupTransports = new ArrayList<>();
|
||||
for (InetSocketAddress iaddr : sameGroupAddrs) {
|
||||
Set<InetSocketAddress> tset = new HashSet<>();
|
||||
tset.add(iaddr);
|
||||
sameGroupTransports.add(loadTransport(this.nodeGroup, server.getProtocol(), tset));
|
||||
}
|
||||
service = Sncp.createLocalService(entry.getName(), type, this.servaddr, sameGroupTransports, diffGroupTransports);
|
||||
} else {
|
||||
StringBuilder g = new StringBuilder(this.nodeGroup);
|
||||
diffGroupAddrs.forEach((k, v) -> {
|
||||
if (g.length() > 0) g.append(';');
|
||||
g.append(k);
|
||||
sameGroupAddrs.addAll(v);
|
||||
});
|
||||
if (sameGroupAddrs.isEmpty()) throw new RuntimeException(type + ":" + group);
|
||||
service = Sncp.createRemoteService(entry.getName(), type, this.servaddr, loadTransport(g.toString(), server.getProtocol(), sameGroupAddrs));
|
||||
}
|
||||
ServiceWrapper wrapper = new ServiceWrapper(type, service, entry);
|
||||
if (factory.find(wrapper.getName(), wrapper.getType()) == null) {
|
||||
regFactory.register(wrapper.getName(), wrapper.getType(), wrapper.getService());
|
||||
if (wrapper.isRemote()) {
|
||||
remoteServices.add(wrapper);
|
||||
} else {
|
||||
localServices.add(wrapper);
|
||||
if (consumer != null) consumer.accept(wrapper);
|
||||
}
|
||||
} else if (isSNCP()) {
|
||||
throw new RuntimeException(ServiceWrapper.class.getSimpleName() + "(class:" + type.getName() + ", name:" + entry.getName() + ", group:" + group + ") is repeat.");
|
||||
}
|
||||
}
|
||||
loadLocalService(localentrys);
|
||||
loadRemoteService(remotentrys);
|
||||
servicecdl.countDown();
|
||||
servicecdl.await();
|
||||
|
||||
final StringBuilder sb = logger.isLoggable(Level.INFO) ? new StringBuilder() : null;
|
||||
synchronized (application) {
|
||||
if (!application.serviceInited) {
|
||||
application.serviceInited = true;
|
||||
//--------------- register ---------------
|
||||
application.localServices.forEach((x, y) -> {
|
||||
y.getNames().forEach(n -> application.factory.register(n, y.getServiceClass(), y.getService()));
|
||||
});
|
||||
application.remoteServices.forEach(y -> {
|
||||
y.getNames().forEach(n -> application.factory.register(n, y.getServiceClass(), y.getService()));
|
||||
});
|
||||
//---------------- inject ----------------
|
||||
application.localServices.forEach((x, y) -> {
|
||||
application.factory.inject(y.getService());
|
||||
});
|
||||
application.remoteServices.forEach(y -> {
|
||||
application.factory.inject(y.getService());
|
||||
});
|
||||
//----------------- init -----------------
|
||||
application.localServices.entrySet().parallelStream().forEach(k -> {
|
||||
Class x = k.getKey();
|
||||
ServiceEntry y = k.getValue();
|
||||
long s = System.currentTimeMillis();
|
||||
y.getService().init(y.getServiceConf());
|
||||
long e = System.currentTimeMillis() - s;
|
||||
if (e > 2 && sb != null) {
|
||||
sb.append(threadName).append("LocalService(").append(y.getNames()).append("|").append(y.getServiceClass()).append(") init ").append(e).append("ms").append(LINE_SEPARATOR);
|
||||
}
|
||||
});
|
||||
if (sb != null && sb.length() > 0) logger.log(Level.INFO, sb.toString());
|
||||
//---------------- inject ----------------
|
||||
new HashSet<>(localServices).forEach(y -> {
|
||||
factory.inject(y.getService());
|
||||
});
|
||||
remoteServices.forEach(y -> {
|
||||
factory.inject(y.getService());
|
||||
});
|
||||
//----------------- init -----------------
|
||||
localServices.parallelStream().forEach(y -> {
|
||||
long s = System.currentTimeMillis();
|
||||
y.getService().init(y.getConf());
|
||||
long e = System.currentTimeMillis() - s;
|
||||
if (e > 2 && sb != null) {
|
||||
sb.append(threadName).append("LocalService(").append(y.getType()).append(':').append(y.getName()).append(") init ").append(e).append("ms").append(LINE_SEPARATOR);
|
||||
}
|
||||
}
|
||||
});
|
||||
if (sb != null && sb.length() > 0) logger.log(Level.INFO, sb.toString());
|
||||
}
|
||||
|
||||
private String getRemoteName(AnyValue av, String remote) {
|
||||
remote = (remote == null || remote.trim().isEmpty()) ? "LOCAL" : remote.trim();
|
||||
if (av == null) return remote;
|
||||
String r = av.getValue("remote");
|
||||
if ("LOCAL".equalsIgnoreCase(r)) return "LOCAL";
|
||||
if (r != null && !r.trim().isEmpty()) return r.trim();
|
||||
return remote;
|
||||
protected ClassFilter<Service> createServiceClassFilter(final AnyValue config) {
|
||||
return createClassFilter(this.nodeGroup, config, null, Service.class, Annotation.class, "services", "service");
|
||||
}
|
||||
|
||||
protected static ClassFilter<Service> createServiceClassFilter(final String localNode, final AnyValue config) {
|
||||
return createClassFilter(localNode, config, null, Service.class, Annotation.class, "services", "service");
|
||||
}
|
||||
|
||||
protected static ClassFilter createClassFilter(final String localNode, final AnyValue config, Class<? extends Annotation> ref,
|
||||
protected static ClassFilter createClassFilter(final String localGroup, final AnyValue config, Class<? extends Annotation> ref,
|
||||
Class inter, Class<? extends Annotation> ref2, String properties, String property) {
|
||||
ClassFilter filter = new ClassFilter(ref, inter);
|
||||
if (properties == null && properties == null) return filter;
|
||||
AnyValue list = config == null ? null : config.getAnyValue(properties);
|
||||
if (list == null) return filter;
|
||||
if ("services".equals(properties)) {
|
||||
for (AnyValue group : list.getAnyValues("group")) {
|
||||
String remotenames = group.getValue("remotenames");
|
||||
for (AnyValue propnode : group.getAnyValues(property)) {
|
||||
boolean hasremote = false;
|
||||
if (remotenames != null) {
|
||||
for (String rn : remotenames.split(";")) {
|
||||
rn = rn.trim();
|
||||
if (rn.isEmpty() || localNode.equals(rn)) continue;
|
||||
DefaultAnyValue s = new DefaultAnyValue();
|
||||
s.addValue("value", propnode.getValue("value"));
|
||||
s.addValue("name", rn);
|
||||
s.addValue("remote", rn);
|
||||
((DefaultAnyValue) list).addValue(property, s);
|
||||
hasremote = true;
|
||||
}
|
||||
}
|
||||
if (hasremote) {
|
||||
((DefaultAnyValue) propnode).setValue("name", localNode);
|
||||
((DefaultAnyValue) propnode).setValue("remote", "");
|
||||
((DefaultAnyValue) list).addValue(property, propnode);
|
||||
}
|
||||
ClassFilter cf = new ClassFilter(ref, inter);
|
||||
if (properties == null && properties == null) return cf;
|
||||
if (config == null) return cf;
|
||||
AnyValue[] proplist = config.getAnyValues(properties);
|
||||
if (proplist == null || proplist.length < 1) return cf;
|
||||
cf = null;
|
||||
for (AnyValue list : proplist) {
|
||||
ClassFilter filter = new ClassFilter(ref, inter);
|
||||
for (AnyValue av : list.getAnyValues(property)) {
|
||||
filter.filter(av, av.getValue("value"), false);
|
||||
}
|
||||
if (list.getBoolValue("autoload", true)) {
|
||||
String includes = list.getValue("includes", "");
|
||||
String excludes = list.getValue("excludes", "");
|
||||
filter.setIncludePatterns(includes.split(";"));
|
||||
filter.setExcludePatterns(excludes.split(";"));
|
||||
} else {
|
||||
if (ref2 == null || ref2 == Annotation.class) { //service如果是autoload=false则不需要加载
|
||||
filter.setRefused(true);
|
||||
} else if (ref2 != Annotation.class) {
|
||||
filter.setAnnotationClass(ref2);
|
||||
}
|
||||
}
|
||||
cf = (cf == null) ? filter : cf.or(filter);
|
||||
}
|
||||
for (AnyValue av : list.getAnyValues(property)) {
|
||||
for (AnyValue prop : av.getAnyValues("property")) {
|
||||
((DefaultAnyValue) av).addValue(prop.getValue("name"), prop.getValue("value"));
|
||||
}
|
||||
filter.filter(av, av.getValue("value"), false);
|
||||
}
|
||||
if (list.getBoolValue("autoload", true)) {
|
||||
String includes = list.getValue("includes", "");
|
||||
String excludes = list.getValue("excludes", "");
|
||||
filter.setIncludePatterns(includes.split(";"));
|
||||
filter.setExcludePatterns(excludes.split(";"));
|
||||
} else {
|
||||
if (ref2 == null || ref2 == Annotation.class) {
|
||||
filter.setRefused(true);
|
||||
} else if (ref2 != Annotation.class) {
|
||||
filter.setAnnotationClass(ref2);
|
||||
}
|
||||
}
|
||||
return filter;
|
||||
return cf;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -5,11 +5,13 @@
|
||||
*/
|
||||
package com.wentch.redkale.boot;
|
||||
|
||||
import com.wentch.redkale.net.sncp.SncpServer;
|
||||
import com.wentch.redkale.net.sncp.*;
|
||||
import com.wentch.redkale.util.AnyValue;
|
||||
import com.wentch.redkale.service.Service;
|
||||
import java.io.*;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.logging.*;
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -19,16 +21,15 @@ public final class NodeSncpServer extends NodeServer {
|
||||
|
||||
private final SncpServer server;
|
||||
|
||||
public NodeSncpServer(Application application, CountDownLatch regcdl, SncpServer server) {
|
||||
super(application, regcdl, server);
|
||||
this.server = server;
|
||||
this.consumer = x -> server.addService(x);
|
||||
}
|
||||
private final File home;
|
||||
|
||||
@Override
|
||||
public void init(AnyValue config) throws Exception {
|
||||
server.init(config);
|
||||
super.init(config);
|
||||
public NodeSncpServer(Application application, InetSocketAddress addr, CountDownLatch regcdl, SncpServer server) {
|
||||
super(application, application.factory.createChild(), regcdl, server);
|
||||
this.server = server;
|
||||
this.home = application.getHome();
|
||||
this.servaddr = addr;
|
||||
this.nodeGroup = application.addrGroups.getOrDefault(addr, "");
|
||||
this.consumer = server == null ? null : x -> server.addService(x);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -38,13 +39,28 @@ public final class NodeSncpServer extends NodeServer {
|
||||
|
||||
@Override
|
||||
public void prepare(AnyValue config) throws Exception {
|
||||
super.prepare(config);
|
||||
ClassFilter<Service> serviceFilter = createServiceClassFilter(application.nodeName, config);
|
||||
ClassFilter<Service> serviceFilter = createServiceClassFilter(config);
|
||||
long s = System.currentTimeMillis();
|
||||
ClassFilter.Loader.load(application.getHome(), serviceFilter);
|
||||
ClassFilter.Loader.load(home, serviceFilter);
|
||||
long e = System.currentTimeMillis() - s;
|
||||
logger.info(this.getClass().getSimpleName() + " load filter class in " + e + " ms");
|
||||
loadService(config.getAnyValue("services"), serviceFilter); //必须在servlet之前
|
||||
//-------------------------------------------------------------------
|
||||
if(server == null) return; //调试时server才可能为null
|
||||
final StringBuilder sb = logger.isLoggable(Level.FINE) ? new StringBuilder() : null;
|
||||
final String threadName = "[" + Thread.currentThread().getName() + "] ";
|
||||
for (SncpServlet en : server.getSncpServlets()) {
|
||||
if (sb != null) sb.append(threadName).append(" Loaded ").append(en).append(LINE_SEPARATOR);
|
||||
}
|
||||
if (sb != null && sb.length() > 0) logger.log(Level.FINE, sb.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSNCP() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public SncpServer getSncpServer() {
|
||||
return server;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,28 +1,28 @@
|
||||
/*
|
||||
* To change this template, choose Tools | Templates
|
||||
* and open the template in the editor.
|
||||
*/
|
||||
package com.wentch.redkale.convert;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author zhangjx
|
||||
*/
|
||||
public class ConvertException extends RuntimeException {
|
||||
|
||||
public ConvertException() {
|
||||
super();
|
||||
}
|
||||
|
||||
public ConvertException(String s) {
|
||||
super(s);
|
||||
}
|
||||
|
||||
public ConvertException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public ConvertException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
}
|
||||
/*
|
||||
* To change this template, choose Tools | Templates
|
||||
* and open the template in the editor.
|
||||
*/
|
||||
package com.wentch.redkale.convert;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author zhangjx
|
||||
*/
|
||||
public class ConvertException extends RuntimeException {
|
||||
|
||||
public ConvertException() {
|
||||
super();
|
||||
}
|
||||
|
||||
public ConvertException(String s) {
|
||||
super(s);
|
||||
}
|
||||
|
||||
public ConvertException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public ConvertException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,7 +87,6 @@ public abstract class Factory<R extends Reader, W extends Writer> {
|
||||
this.register(DLong.class, DLongSimpledCoder.instance);
|
||||
this.register(Class.class, TypeSimpledCoder.instance);
|
||||
this.register(InetSocketAddress.class, InetSocketAddressSimpledCoder.instance);
|
||||
this.register(InetSocketAddress.class, InetSocketAddressSimpledCoder.instance);
|
||||
this.register(Pattern.class, PatternSimpledCoder.instance);
|
||||
//---------------------------------------------------------
|
||||
this.register(boolean[].class, BoolArraySimpledCoder.instance);
|
||||
|
||||
@@ -1,68 +1,68 @@
|
||||
/*
|
||||
* To change this template, choose Tools | Templates
|
||||
* and open the template in the editor.
|
||||
*/
|
||||
package com.wentch.redkale.convert;
|
||||
|
||||
/**
|
||||
* 只增不减的伪Map类
|
||||
*
|
||||
* @author zhangjx
|
||||
* @param <K>
|
||||
* @param <V>
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public final class HashedMap<K, V> {
|
||||
|
||||
protected final transient Entry<K, V>[] table;
|
||||
|
||||
public HashedMap() {
|
||||
this(128);
|
||||
}
|
||||
|
||||
public HashedMap(int initCapacity) {
|
||||
this.table = new Entry[Math.max(initCapacity, 16)];
|
||||
}
|
||||
|
||||
public final V get(final K key) {
|
||||
final K k = key;
|
||||
final Entry<K, V>[] data = this.table;
|
||||
Entry<K, V> entry = data[k.hashCode() & (data.length - 1)];
|
||||
while (entry != null) {
|
||||
if (k == entry.key) return entry.value;
|
||||
entry = entry.next;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public final V put(K key, V value) {
|
||||
final K k = key;
|
||||
final Entry<K, V>[] data = this.table;
|
||||
final int index = k.hashCode() & (data.length - 1);
|
||||
Entry<K, V> entry = data[index];
|
||||
while (entry != null) {
|
||||
if (k == entry.key) {
|
||||
entry.value = value;
|
||||
return entry.value;
|
||||
}
|
||||
entry = entry.next;
|
||||
}
|
||||
data[index] = new Entry<>(key, value, data[index]);
|
||||
return null;
|
||||
}
|
||||
|
||||
protected static final class Entry<K, V> {
|
||||
|
||||
protected V value;
|
||||
|
||||
protected final K key;
|
||||
|
||||
protected final Entry<K, V> next;
|
||||
|
||||
protected Entry(K key, V value, Entry<K, V> next) {
|
||||
this.key = key;
|
||||
this.value = value;
|
||||
this.next = next;
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
* To change this template, choose Tools | Templates
|
||||
* and open the template in the editor.
|
||||
*/
|
||||
package com.wentch.redkale.convert;
|
||||
|
||||
/**
|
||||
* 只增不减的伪Map类
|
||||
*
|
||||
* @author zhangjx
|
||||
* @param <K>
|
||||
* @param <V>
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public final class HashedMap<K, V> {
|
||||
|
||||
protected final transient Entry<K, V>[] table;
|
||||
|
||||
public HashedMap() {
|
||||
this(128);
|
||||
}
|
||||
|
||||
public HashedMap(int initCapacity) {
|
||||
this.table = new Entry[Math.max(initCapacity, 16)];
|
||||
}
|
||||
|
||||
public final V get(final K key) {
|
||||
final K k = key;
|
||||
final Entry<K, V>[] data = this.table;
|
||||
Entry<K, V> entry = data[k.hashCode() & (data.length - 1)];
|
||||
while (entry != null) {
|
||||
if (k == entry.key) return entry.value;
|
||||
entry = entry.next;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public final V put(K key, V value) {
|
||||
final K k = key;
|
||||
final Entry<K, V>[] data = this.table;
|
||||
final int index = k.hashCode() & (data.length - 1);
|
||||
Entry<K, V> entry = data[index];
|
||||
while (entry != null) {
|
||||
if (k == entry.key) {
|
||||
entry.value = value;
|
||||
return entry.value;
|
||||
}
|
||||
entry = entry.next;
|
||||
}
|
||||
data[index] = new Entry<>(key, value, data[index]);
|
||||
return null;
|
||||
}
|
||||
|
||||
protected static final class Entry<K, V> {
|
||||
|
||||
protected V value;
|
||||
|
||||
protected final K key;
|
||||
|
||||
protected final Entry<K, V> next;
|
||||
|
||||
protected Entry(K key, V value, Entry<K, V> next) {
|
||||
this.key = key;
|
||||
this.value = value;
|
||||
this.next = next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ package com.wentch.redkale.convert.bson;
|
||||
import com.wentch.redkale.convert.*;
|
||||
import com.wentch.redkale.util.*;
|
||||
import java.lang.reflect.*;
|
||||
import java.nio.*;
|
||||
|
||||
/**
|
||||
* BSON协议格式:
|
||||
@@ -78,4 +79,26 @@ public final class BsonConvert extends Convert<BsonReader, BsonWriter> {
|
||||
}
|
||||
return convertTo(value.getClass(), value);
|
||||
}
|
||||
|
||||
public ByteBuffer convertToBuffer(final Type type, Object value) {
|
||||
if (type == null) return null;
|
||||
final BsonWriter out = writerPool.poll();
|
||||
out.setTiny(tiny);
|
||||
factory.loadEncoder(type).convertTo(out, value);
|
||||
ByteBuffer result = out.toBuffer();
|
||||
writerPool.offer(out);
|
||||
return result;
|
||||
}
|
||||
|
||||
public ByteBuffer convertToBuffer(Object value) {
|
||||
if (value == null) {
|
||||
final BsonWriter out = writerPool.poll();
|
||||
out.setTiny(tiny);
|
||||
out.writeNull();
|
||||
ByteBuffer result = out.toBuffer();
|
||||
writerPool.offer(out);
|
||||
return result;
|
||||
}
|
||||
return convertToBuffer(value.getClass(), value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ package com.wentch.redkale.convert.bson;
|
||||
|
||||
import com.wentch.redkale.convert.*;
|
||||
import com.wentch.redkale.util.*;
|
||||
import java.nio.*;
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -33,6 +34,10 @@ public final class BsonWriter implements Writer {
|
||||
return newdata;
|
||||
}
|
||||
|
||||
public ByteBuffer toBuffer() {
|
||||
return ByteBuffer.wrap(content, 0, count);
|
||||
}
|
||||
|
||||
public BsonWriter() {
|
||||
this(defaultSize);
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ public final class InetAddressSimpledCoder<R extends Reader, W extends Writer> e
|
||||
}
|
||||
}
|
||||
|
||||
public static class InetSocketAddressSimpledCoder<R extends Reader, W extends Writer> extends SimpledCoder<R, W, InetSocketAddress> {
|
||||
public final static class InetSocketAddressSimpledCoder<R extends Reader, W extends Writer> extends SimpledCoder<R, W, InetSocketAddress> {
|
||||
|
||||
public static final InetSocketAddressSimpledCoder instance = new InetSocketAddressSimpledCoder();
|
||||
|
||||
|
||||
@@ -30,4 +30,4 @@ public final class LongSimpledCoder<R extends Reader, W extends Writer> extends
|
||||
return in.readLong();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* 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 com.wentch.redkale.convert.json;
|
||||
|
||||
import com.wentch.redkale.convert.*;
|
||||
import com.wentch.redkale.convert.ext.*;
|
||||
import java.net.*;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author zhangjx
|
||||
* @param <R>
|
||||
* @param <W>
|
||||
*/
|
||||
public final class InetAddressJsonSimpledCoder<R extends Reader, W extends Writer> extends SimpledCoder<JsonReader, JsonWriter, InetAddress> {
|
||||
|
||||
public static final InetAddressJsonSimpledCoder instance = new InetAddressJsonSimpledCoder();
|
||||
|
||||
@Override
|
||||
public void convertTo(JsonWriter out, InetAddress value) {
|
||||
if (value == null) {
|
||||
out.writeNull();
|
||||
return;
|
||||
}
|
||||
StringSimpledCoder.instance.convertTo(out, value.getHostAddress());
|
||||
}
|
||||
|
||||
@Override
|
||||
public InetAddress convertFrom(JsonReader in) {
|
||||
String str = StringSimpledCoder.instance.convertFrom(in);
|
||||
if (str == null) return null;
|
||||
try {
|
||||
return InetAddress.getByName(str);
|
||||
} catch (Exception ex) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public final static class InetSocketAddressJsonSimpledCoder<R extends Reader, W extends Writer> extends SimpledCoder<JsonReader, JsonWriter, InetSocketAddress> {
|
||||
|
||||
public static final InetSocketAddressJsonSimpledCoder instance = new InetSocketAddressJsonSimpledCoder();
|
||||
|
||||
@Override
|
||||
public void convertTo(JsonWriter out, InetSocketAddress value) {
|
||||
if (value == null) {
|
||||
out.writeNull();
|
||||
return;
|
||||
}
|
||||
StringSimpledCoder.instance.convertTo(out, value.getHostString() + ":" + value.getPort());
|
||||
}
|
||||
|
||||
@Override
|
||||
public InetSocketAddress convertFrom(JsonReader in) {
|
||||
String str = StringSimpledCoder.instance.convertFrom(in);
|
||||
if (str == null) return null;
|
||||
try {
|
||||
int pos = str.indexOf(':');
|
||||
return new InetSocketAddress(str.substring(0, pos), Integer.parseInt(str.substring(pos + 1)));
|
||||
} catch (Exception ex) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,7 @@ package com.wentch.redkale.convert.json;
|
||||
import com.wentch.redkale.convert.ConvertType;
|
||||
import com.wentch.redkale.convert.Factory;
|
||||
import java.io.Serializable;
|
||||
import java.net.*;
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -18,6 +19,8 @@ public final class JsonFactory extends Factory<JsonReader, JsonWriter> {
|
||||
private static final JsonFactory instance = new JsonFactory(null, Boolean.getBoolean("convert.json.tiny"));
|
||||
|
||||
static {
|
||||
instance.register(InetAddress.class, InetAddressJsonSimpledCoder.instance);
|
||||
instance.register(InetSocketAddress.class, InetAddressJsonSimpledCoder.InetSocketAddressJsonSimpledCoder.instance);
|
||||
instance.register(Serializable.class, instance.loadEncoder(Object.class));
|
||||
}
|
||||
|
||||
|
||||
@@ -34,6 +34,8 @@ public abstract class Server {
|
||||
|
||||
protected final String protocol;
|
||||
|
||||
protected final PrepareServlet prepare;
|
||||
|
||||
protected AnyValue config;
|
||||
|
||||
protected Charset charset;
|
||||
@@ -64,9 +66,10 @@ public abstract class Server {
|
||||
|
||||
private ScheduledThreadPoolExecutor scheduler;
|
||||
|
||||
protected Server(long serverStartTime, String protocol, final WatchFactory watch) {
|
||||
protected Server(long serverStartTime, String protocol, PrepareServlet servlet, final WatchFactory watch) {
|
||||
this.serverStartTime = serverStartTime;
|
||||
this.protocol = protocol;
|
||||
this.prepare = servlet;
|
||||
this.watch = watch;
|
||||
}
|
||||
|
||||
@@ -88,12 +91,13 @@ public abstract class Server {
|
||||
final Format f = createFormat();
|
||||
this.executor = Executors.newFixedThreadPool(threads, (Runnable r) -> {
|
||||
Thread t = new WorkThread(executor, r);
|
||||
t.setName("Servlet-HTTP-" + port + "-Thread-" + f.format(counter.incrementAndGet()));
|
||||
t.setName("Servlet-" + protocol + "-" + port + "-Thread-" + f.format(counter.incrementAndGet()));
|
||||
return t;
|
||||
});
|
||||
}
|
||||
|
||||
public void destroy(final AnyValue config) throws Exception {
|
||||
this.prepare.destroy(context, config);
|
||||
if (scheduler != null) scheduler.shutdownNow();
|
||||
}
|
||||
|
||||
@@ -101,23 +105,28 @@ public abstract class Server {
|
||||
return address;
|
||||
}
|
||||
|
||||
public String getProtocol() {
|
||||
return protocol;
|
||||
}
|
||||
|
||||
public Logger getLogger() {
|
||||
return this.logger;
|
||||
}
|
||||
|
||||
public void start() throws IOException {
|
||||
this.context = this.createContext();
|
||||
this.context.prepare.init(this.context, config);
|
||||
if (this.watch != null) this.watch.inject(this.context.prepare);
|
||||
this.prepare.init(this.context, config);
|
||||
if (this.watch != null) this.watch.inject(this.prepare);
|
||||
this.transport = ProtocolServer.create(this.protocol, context);
|
||||
this.transport.open();
|
||||
transport.setOption(StandardSocketOptions.SO_REUSEADDR, true);
|
||||
transport.setOption(StandardSocketOptions.SO_RCVBUF, 16 * 1024 + 8);
|
||||
transport.setOption(StandardSocketOptions.SO_RCVBUF, 8 * 1024);
|
||||
transport.bind(address, backlog);
|
||||
logger.info(this.getClass().getSimpleName() + " listen: " + address);
|
||||
logger.info(this.getClass().getSimpleName() + " threads: " + threads + ", bufferPoolSize: " + bufferPoolSize + ", responsePoolSize: " + responsePoolSize);
|
||||
transport.accept();
|
||||
logger.info(this.getClass().getSimpleName() + " started in " + (System.currentTimeMillis() - context.getServerStartTime()) + " ms");
|
||||
final String threadName = "[" + Thread.currentThread().getName() + "] ";
|
||||
logger.info(threadName + this.getClass().getSimpleName() + " listen: " + address
|
||||
+ ", threads: " + threads + ", bufferPoolSize: " + bufferPoolSize + ", responsePoolSize: " + responsePoolSize
|
||||
+ ", started in " + (System.currentTimeMillis() - context.getServerStartTime()) + " ms");
|
||||
}
|
||||
|
||||
protected abstract Context createContext();
|
||||
@@ -130,7 +139,7 @@ public abstract class Server {
|
||||
} catch (Exception e) {
|
||||
}
|
||||
logger.info(this.getClass().getSimpleName() + "-" + this.protocol + " shutdow prepare servlet");
|
||||
this.context.prepare.destroy(this.context, config);
|
||||
this.prepare.destroy(this.context, config);
|
||||
long e = System.currentTimeMillis() - s;
|
||||
logger.info(this.getClass().getSimpleName() + " shutdown in " + e + " ms");
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import java.io.*;
|
||||
import java.net.*;
|
||||
import java.nio.*;
|
||||
import java.nio.channels.*;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.concurrent.atomic.*;
|
||||
|
||||
@@ -21,25 +22,21 @@ import java.util.concurrent.atomic.*;
|
||||
*/
|
||||
public final class Transport {
|
||||
|
||||
protected SocketAddress[] remoteAddres;
|
||||
|
||||
protected final ObjectPool<ByteBuffer> bufferPool;
|
||||
|
||||
protected final String name;
|
||||
|
||||
protected final String protocol;
|
||||
|
||||
private final boolean udp;
|
||||
|
||||
protected final AsynchronousChannelGroup group;
|
||||
|
||||
protected BlockingQueue<AsyncConnection> queue;
|
||||
protected final InetSocketAddress[] remoteAddres;
|
||||
|
||||
public Transport(String name, String protocol, int clients, int bufferPoolSize, WatchFactory watch, SocketAddress... addresses) {
|
||||
protected final ObjectPool<ByteBuffer> bufferPool;
|
||||
|
||||
protected final AtomicInteger index = new AtomicInteger();
|
||||
|
||||
public Transport(String name, String protocol, WatchFactory watch, int bufferPoolSize, Collection<InetSocketAddress> addresses) {
|
||||
this.name = name;
|
||||
this.protocol = protocol;
|
||||
this.udp = "UDP".equalsIgnoreCase(protocol);
|
||||
this.queue = this.udp ? null : new ArrayBlockingQueue<>(clients);
|
||||
AsynchronousChannelGroup g = null;
|
||||
try {
|
||||
final AtomicInteger counter = new AtomicInteger();
|
||||
@@ -63,7 +60,21 @@ public final class Transport {
|
||||
e.clear();
|
||||
return true;
|
||||
});
|
||||
this.remoteAddres = addresses;
|
||||
this.remoteAddres = addresses.toArray(new InetSocketAddress[addresses.size()]);
|
||||
}
|
||||
|
||||
public boolean match(Collection<InetSocketAddress> addrs) {
|
||||
if (addrs == null) return false;
|
||||
if (addrs.size() != this.remoteAddres.length) return false;
|
||||
for (InetSocketAddress addr : this.remoteAddres) {
|
||||
if (!addrs.contains(addr)) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return Transport.class.getSimpleName() + "{name=" + name + ",protocol=" + protocol + ",remoteAddres=" + Arrays.toString(remoteAddres) + "}";
|
||||
}
|
||||
|
||||
public ByteBuffer pollBuffer() {
|
||||
@@ -78,27 +89,18 @@ public final class Transport {
|
||||
for (ByteBuffer buffer : buffers) offerBuffer(buffer);
|
||||
}
|
||||
|
||||
public boolean isUDP() {
|
||||
return udp;
|
||||
}
|
||||
|
||||
public AsyncConnection pollConnection() {
|
||||
if (udp) return createConnection();
|
||||
AsyncConnection conn = queue.poll();
|
||||
return (conn != null && conn.isOpen()) ? conn : createConnection();
|
||||
}
|
||||
|
||||
private AsyncConnection createConnection() {
|
||||
SocketAddress addr = remoteAddres[0];
|
||||
int i = index.get();
|
||||
SocketAddress addr = remoteAddres[i];
|
||||
try {
|
||||
if (udp) {
|
||||
AsyncDatagramChannel channel = AsyncDatagramChannel.open(group);
|
||||
channel.connect(addr);
|
||||
return AsyncConnection.create(channel, addr, true, 0, 0);
|
||||
} else {
|
||||
if ("TCP".equalsIgnoreCase(protocol)) {
|
||||
AsynchronousSocketChannel channel = AsynchronousSocketChannel.open(group);
|
||||
channel.connect(addr).get(2, TimeUnit.SECONDS);
|
||||
return AsyncConnection.create(channel, 0, 0);
|
||||
} else {
|
||||
AsyncDatagramChannel channel = AsyncDatagramChannel.open(group);
|
||||
channel.connect(addr);
|
||||
return AsyncConnection.create(channel, addr, true, 0, 0);
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
throw new RuntimeException("transport address = " + addr, ex);
|
||||
@@ -106,18 +108,9 @@ public final class Transport {
|
||||
}
|
||||
|
||||
public void offerConnection(AsyncConnection conn) {
|
||||
if (udp) {
|
||||
try {
|
||||
conn.close();
|
||||
} catch (IOException io) {
|
||||
}
|
||||
} else if (conn.isOpen()) {
|
||||
if (!queue.offer(conn)) {
|
||||
try {
|
||||
conn.close();
|
||||
} catch (IOException io) {
|
||||
}
|
||||
}
|
||||
try {
|
||||
conn.close();
|
||||
} catch (IOException io) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -29,8 +29,8 @@ public final class HttpContext extends Context {
|
||||
protected final SecureRandom random = new SecureRandom();
|
||||
|
||||
public HttpContext(long serverStartTime, Logger logger, ExecutorService executor, ObjectPool<ByteBuffer> bufferPool,
|
||||
ObjectPool<Response> responsePool, int maxbody, Charset charset, InetSocketAddress address,
|
||||
PrepareServlet prepare, WatchFactory watch, int readTimeoutSecond, int writeTimeoutSecond, String contextPath) {
|
||||
ObjectPool<Response> responsePool, int maxbody, Charset charset, InetSocketAddress address, PrepareServlet prepare,
|
||||
WatchFactory watch, int readTimeoutSecond, int writeTimeoutSecond, String contextPath) {
|
||||
super(serverStartTime, logger, executor, bufferPool, responsePool, maxbody, charset,
|
||||
address, prepare, watch, readTimeoutSecond, writeTimeoutSecond);
|
||||
this.contextPath = contextPath;
|
||||
|
||||
@@ -25,7 +25,7 @@ public final class HttpPrepareServlet extends PrepareServlet<HttpRequest, HttpRe
|
||||
|
||||
private ByteBuffer flashPolicyBuffer;
|
||||
|
||||
private final List<SimpleEntry<HttpServlet, AnyValue>> servlets = new ArrayList<>();
|
||||
private final List<HttpServlet> servlets = new ArrayList<>();
|
||||
|
||||
private final Map<String, HttpServlet> strmaps = new HashMap<>();
|
||||
|
||||
@@ -43,13 +43,13 @@ public final class HttpPrepareServlet extends PrepareServlet<HttpRequest, HttpRe
|
||||
|
||||
@Override
|
||||
public void init(Context context, AnyValue config) {
|
||||
this.servlets.stream().forEach((en) -> {
|
||||
en.getKey().init(context, en.getValue());
|
||||
this.servlets.stream().forEach(s -> {
|
||||
s.init(context, s.conf);
|
||||
});
|
||||
final WatchFactory watch = ((HttpContext) context).getWatchFactory();
|
||||
if (watch != null) {
|
||||
this.servlets.stream().forEach((en) -> {
|
||||
watch.inject(en.getKey());
|
||||
this.servlets.stream().forEach(s -> {
|
||||
watch.inject(s);
|
||||
});
|
||||
}
|
||||
if (config != null) {
|
||||
@@ -131,7 +131,8 @@ public final class HttpPrepareServlet extends PrepareServlet<HttpRequest, HttpRe
|
||||
strmaps.put(mapping, servlet);
|
||||
}
|
||||
}
|
||||
this.servlets.add(new SimpleEntry<>(servlet, conf));
|
||||
servlet.conf = conf;
|
||||
this.servlets.add(servlet);
|
||||
}
|
||||
|
||||
private static boolean contains(String string, char... values) {
|
||||
@@ -153,8 +154,8 @@ public final class HttpPrepareServlet extends PrepareServlet<HttpRequest, HttpRe
|
||||
@Override
|
||||
public void destroy(Context context, AnyValue config) {
|
||||
this.resourceHttpServlet.destroy(context, config);
|
||||
this.servlets.stream().forEach((en) -> {
|
||||
en.getKey().destroy(context, en.getValue());
|
||||
this.servlets.stream().forEach(s -> {
|
||||
s.destroy(context, s.conf);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,6 @@ import com.wentch.redkale.watch.*;
|
||||
import java.net.*;
|
||||
import java.nio.*;
|
||||
import java.util.*;
|
||||
import java.util.AbstractMap.SimpleEntry;
|
||||
import java.util.concurrent.atomic.*;
|
||||
|
||||
/**
|
||||
@@ -20,8 +19,6 @@ import java.util.concurrent.atomic.*;
|
||||
*/
|
||||
public final class HttpServer extends Server {
|
||||
|
||||
private final Map<SimpleEntry<HttpServlet, AnyValue>, String[]> servlets = new HashMap<>();
|
||||
|
||||
private String contextPath;
|
||||
|
||||
public HttpServer() {
|
||||
@@ -29,7 +26,7 @@ public final class HttpServer extends Server {
|
||||
}
|
||||
|
||||
public HttpServer(long serverStartTime, final WatchFactory watch) {
|
||||
super(serverStartTime, "TCP", watch);
|
||||
super(serverStartTime, "TCP", new HttpPrepareServlet(), watch);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -40,7 +37,7 @@ public final class HttpServer extends Server {
|
||||
}
|
||||
|
||||
public void addHttpServlet(HttpServlet servlet, AnyValue conf, String... mappings) {
|
||||
this.servlets.put(new SimpleEntry<>(servlet, conf), mappings);
|
||||
((HttpPrepareServlet) this.prepare).addHttpServlet(servlet, conf, mappings);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -56,13 +53,8 @@ public final class HttpServer extends Server {
|
||||
e.clear();
|
||||
return true;
|
||||
});
|
||||
HttpPrepareServlet prepare = new HttpPrepareServlet();
|
||||
this.servlets.entrySet().stream().forEach((en) -> {
|
||||
prepare.addHttpServlet(en.getKey().getKey(), en.getKey().getValue(), en.getValue());
|
||||
});
|
||||
this.servlets.clear();
|
||||
String[][] defaultAddHeaders = null;
|
||||
String[][] defaultSetHeaders = null;
|
||||
final List<String[]> defaultAddHeaders = new ArrayList<>();
|
||||
final List<String[]> defaultSetHeaders = new ArrayList<>();
|
||||
HttpCookie defaultCookie = null;
|
||||
String remoteAddrHeader = null;
|
||||
if (config != null) {
|
||||
@@ -83,29 +75,27 @@ public final class HttpServer extends Server {
|
||||
if (resps != null) {
|
||||
AnyValue[] addHeaders = resps.getAnyValues("addheader");
|
||||
if (addHeaders.length > 0) {
|
||||
defaultAddHeaders = new String[addHeaders.length][];
|
||||
for (int i = 0; i < addHeaders.length; i++) {
|
||||
String val = addHeaders[i].getValue("value");
|
||||
if (val == null) continue;
|
||||
if (val.startsWith("request.headers.")) {
|
||||
defaultAddHeaders[i] = new String[]{addHeaders[i].getValue("name"), val, val.substring("request.headers.".length())};
|
||||
defaultAddHeaders.add(new String[]{addHeaders[i].getValue("name"), val, val.substring("request.headers.".length())});
|
||||
} else if (val.startsWith("system.property.")) {
|
||||
String v = System.getProperty(val.substring("system.property.".length()));
|
||||
if (v != null) defaultAddHeaders[i] = new String[]{addHeaders[i].getValue("name"), v};
|
||||
if (v != null) defaultAddHeaders.add(new String[]{addHeaders[i].getValue("name"), v});
|
||||
} else {
|
||||
defaultAddHeaders[i] = new String[]{addHeaders[i].getValue("name"), val};
|
||||
defaultAddHeaders.add(new String[]{addHeaders[i].getValue("name"), val});
|
||||
}
|
||||
}
|
||||
}
|
||||
AnyValue[] setHeaders = resps.getAnyValues("setheader");
|
||||
if (setHeaders.length > 0) {
|
||||
defaultSetHeaders = new String[setHeaders.length][];
|
||||
for (int i = 0; i < setHeaders.length; i++) {
|
||||
String val = setHeaders[i].getValue("value");
|
||||
if (val != null && val.startsWith("request.headers.")) {
|
||||
defaultSetHeaders[i] = new String[]{setHeaders[i].getValue("name"), val, val.substring("request.headers.".length())};
|
||||
defaultSetHeaders.add(new String[]{setHeaders[i].getValue("name"), val, val.substring("request.headers.".length())});
|
||||
} else {
|
||||
defaultSetHeaders[i] = new String[]{setHeaders[i].getValue("name"), val};
|
||||
defaultSetHeaders.add(new String[]{setHeaders[i].getValue("name"), val});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -121,15 +111,15 @@ public final class HttpServer extends Server {
|
||||
}
|
||||
}
|
||||
}
|
||||
final String[][] addHeaders = defaultAddHeaders;
|
||||
final String[][] setHeaders = defaultSetHeaders;
|
||||
final String[][] addHeaders = defaultAddHeaders.isEmpty() ? null : defaultAddHeaders.toArray(new String[defaultAddHeaders.size()][]);
|
||||
final String[][] setHeaders = defaultSetHeaders.isEmpty() ? null : defaultSetHeaders.toArray(new String[defaultSetHeaders.size()][]);
|
||||
final HttpCookie defCookie = defaultCookie;
|
||||
final String addrHeader = remoteAddrHeader;
|
||||
AtomicLong createResponseCounter = watch == null ? new AtomicLong() : watch.createWatchNumber("HTTP_" + port + ".Response.creatCounter");
|
||||
AtomicLong cycleResponseCounter = watch == null ? new AtomicLong() : watch.createWatchNumber("HTTP_" + port + ".Response.cycleCounter");
|
||||
ObjectPool<Response> responsePool = HttpResponse.createPool(createResponseCounter, cycleResponseCounter, this.responsePoolSize, null);
|
||||
HttpContext httpcontext = new HttpContext(this.serverStartTime, this.logger, executor, bufferPool, responsePool,
|
||||
this.maxbody, this.charset, this.address, prepare, this.watch, this.readTimeoutSecond, this.writeTimeoutSecond, contextPath);
|
||||
this.maxbody, this.charset, this.address, this.prepare, this.watch, this.readTimeoutSecond, this.writeTimeoutSecond, contextPath);
|
||||
responsePool.setCreator((Object... params)
|
||||
-> new HttpResponse(httpcontext, new HttpRequest(httpcontext, httpcontext.jsonFactory, addrHeader), addHeaders, setHeaders, defCookie));
|
||||
return httpcontext;
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
package com.wentch.redkale.net.http;
|
||||
|
||||
import com.wentch.redkale.net.Servlet;
|
||||
import com.wentch.redkale.util.*;
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -13,6 +14,8 @@ import com.wentch.redkale.net.Servlet;
|
||||
*/
|
||||
public abstract class HttpServlet implements Servlet<HttpRequest, HttpResponse> {
|
||||
|
||||
AnyValue conf; //当前HttpServlet的配置
|
||||
|
||||
@Override
|
||||
public final boolean equals(Object obj) {
|
||||
return obj != null && obj.getClass() == this.getClass();
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
package com.wentch.redkale.net.http;
|
||||
|
||||
import com.wentch.redkale.net.*;
|
||||
import com.wentch.redkale.service.*;
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
@@ -41,7 +40,7 @@ public abstract class WebSocket {
|
||||
|
||||
WebSocketGroup group;
|
||||
|
||||
WebSocketNodeService nodeService;
|
||||
WebSocketNode node;
|
||||
|
||||
Serializable sessionid;
|
||||
|
||||
@@ -107,6 +106,16 @@ public abstract class WebSocket {
|
||||
send(new WebSocketPacket(data, last));
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送消息, 消息类型是String或byte[]
|
||||
* <p>
|
||||
* @param message 不可为空, 只能是String或者byte[]
|
||||
* @param last 是否最后一条
|
||||
*/
|
||||
public final void send(Serializable message, boolean last) {
|
||||
send(new WebSocketPacket(message, last));
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------
|
||||
/**
|
||||
* 给指定groupid的WebSocketGroup下所有WebSocket节点发送文本消息
|
||||
@@ -201,21 +210,13 @@ public abstract class WebSocket {
|
||||
}
|
||||
|
||||
private int sendMessage(Serializable groupid, boolean recent, String text, boolean last) {
|
||||
if (nodeService == null) return WebSocketNodeService.RETCODE_NODESERVICE_NULL;
|
||||
if (groupid == this.groupid) {
|
||||
return nodeService.onSend(this.engine.getEngineid(), groupid, recent, text, last);
|
||||
} else {
|
||||
return nodeService.send(this.engine.getEngineid(), groupid, recent, text, last);
|
||||
}
|
||||
if (node == null) return WebSocketNode.RETCODE_NODESERVICE_NULL;
|
||||
return node.sendMessage(groupid, recent, text, last);
|
||||
}
|
||||
|
||||
private int sendMessage(Serializable groupid, boolean recent, byte[] data, boolean last) {
|
||||
if (nodeService == null) return WebSocketNodeService.RETCODE_NODESERVICE_NULL;
|
||||
if (groupid == this.groupid) {
|
||||
return nodeService.onSend(this.engine.getEngineid(), groupid, recent, data, last);
|
||||
} else {
|
||||
return nodeService.send(this.engine.getEngineid(), groupid, recent, data, last);
|
||||
}
|
||||
if (node == null) return WebSocketNode.RETCODE_NODESERVICE_NULL;
|
||||
return node.sendMessage(groupid, recent, data, last);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -309,9 +310,7 @@ public abstract class WebSocket {
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public Serializable createGroupid() {
|
||||
return null;
|
||||
}
|
||||
protected abstract Serializable createGroupid();
|
||||
|
||||
/**
|
||||
*
|
||||
|
||||
@@ -15,21 +15,11 @@ import java.util.concurrent.*;
|
||||
*/
|
||||
public final class WebSocketEngine {
|
||||
|
||||
private static final Map<String, WebSocketEngine> globals = new ConcurrentHashMap<>();
|
||||
|
||||
private final String engineid;
|
||||
|
||||
private final Map<Serializable, WebSocketGroup> containers = new ConcurrentHashMap<>();
|
||||
|
||||
static WebSocketEngine create(String engineid) {
|
||||
WebSocketEngine engine = globals.get(engineid);
|
||||
if (engine != null) return engine;
|
||||
engine = new WebSocketEngine(engineid);
|
||||
globals.put(engineid, engine);
|
||||
return engine;
|
||||
}
|
||||
|
||||
private WebSocketEngine(String engineid) {
|
||||
protected WebSocketEngine(String engineid) {
|
||||
this.engineid = engineid;
|
||||
}
|
||||
|
||||
@@ -38,7 +28,6 @@ public final class WebSocketEngine {
|
||||
if (group == null) {
|
||||
group = new WebSocketGroup(socket.groupid);
|
||||
containers.put(socket.groupid, group);
|
||||
group.recentWebSocket = socket;
|
||||
}
|
||||
group.add(socket);
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ public final class WebSocketGroup {
|
||||
|
||||
private final Serializable groupid;
|
||||
|
||||
WebSocket recentWebSocket;
|
||||
private WebSocket recentWebSocket;
|
||||
|
||||
private final List<WebSocket> list = new CopyOnWriteArrayList<>();
|
||||
|
||||
@@ -42,9 +42,14 @@ public final class WebSocketGroup {
|
||||
|
||||
void add(WebSocket socket) {
|
||||
socket.group = this;
|
||||
this.recentWebSocket = socket;
|
||||
list.add(socket);
|
||||
}
|
||||
|
||||
void setRecentWebSocket(WebSocket socket) {
|
||||
this.recentWebSocket = socket;
|
||||
}
|
||||
|
||||
public final boolean isEmpty() {
|
||||
return list.isEmpty();
|
||||
}
|
||||
@@ -71,6 +76,22 @@ public final class WebSocketGroup {
|
||||
attributes.put(name, value);
|
||||
}
|
||||
|
||||
public final void send(boolean recent, Serializable message, boolean last) {
|
||||
if (recent) {
|
||||
recentWebSocket.send(message, last);
|
||||
} else {
|
||||
list.forEach(x -> x.send(message, last));
|
||||
}
|
||||
}
|
||||
|
||||
public final void sendEach(Serializable message, boolean last) {
|
||||
list.forEach(x -> x.send(message, last));
|
||||
}
|
||||
|
||||
public final void sendRecent(Serializable message, boolean last) {
|
||||
recentWebSocket.send(message, last);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "{groupid: " + groupid + ", list.size: " + (list == null ? -1 : list.size()) + "}";
|
||||
|
||||
143
src/com/wentch/redkale/net/http/WebSocketNode.java
Normal file
143
src/com/wentch/redkale/net/http/WebSocketNode.java
Normal file
@@ -0,0 +1,143 @@
|
||||
/*
|
||||
* 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 com.wentch.redkale.net.http;
|
||||
|
||||
import com.wentch.redkale.util.*;
|
||||
import java.io.*;
|
||||
import java.net.*;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.logging.*;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author zhangjx
|
||||
*/
|
||||
public abstract class WebSocketNode {
|
||||
|
||||
public static final int RETCODE_ENGINE_NULL = 5001;
|
||||
|
||||
public static final int RETCODE_NODESERVICE_NULL = 5002;
|
||||
|
||||
public static final int RETCODE_GROUP_EMPTY = 5005;
|
||||
|
||||
public static final int RETCODE_WSOFFLINE = 5011;
|
||||
|
||||
protected final Logger logger = Logger.getLogger(this.getClass().getSimpleName());
|
||||
|
||||
protected final boolean finest = logger.isLoggable(Level.FINEST);
|
||||
|
||||
protected InetSocketAddress localSncpAddress; //为SncpServer的服务address
|
||||
|
||||
protected WebSocketNode remoteNode;
|
||||
|
||||
//存放所有用户分布在节点上的队列信息,Set<InetSocketAddress> 为 sncpnode 的集合
|
||||
protected final ConcurrentHashMap<Serializable, Set<InetSocketAddress>> dataNodes = new ConcurrentHashMap();
|
||||
|
||||
//存放所有用户分布在节点上的队列信息,Set<String> 为 engineid 的集合
|
||||
protected final ConcurrentHashMap<Serializable, Set<String>> localNodes = new ConcurrentHashMap();
|
||||
|
||||
protected final ConcurrentHashMap<String, WebSocketEngine> engines = new ConcurrentHashMap();
|
||||
|
||||
public void init(AnyValue conf) {
|
||||
if (remoteNode != null) {
|
||||
try {
|
||||
Map<Serializable, Set<InetSocketAddress>> map = remoteNode.getDataNodes();
|
||||
if (map != null) dataNodes.putAll(map);
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.INFO, WebSocketNode.class.getSimpleName() + "(" + this.localSncpAddress + ") not load data nodes ", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void destroy(AnyValue conf) {
|
||||
HashMap<Serializable, Set<String>> nodes = new HashMap<>(localNodes);
|
||||
nodes.forEach((k, v) -> {
|
||||
new HashSet<>(v).forEach(e -> {
|
||||
if (engines.containsKey(e)) disconnect(k, e);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public Map<Serializable, Set<InetSocketAddress>> getDataNodes() {
|
||||
return dataNodes;
|
||||
}
|
||||
|
||||
protected abstract int sendMessage(Serializable groupid, boolean recent, Serializable message, boolean last);
|
||||
|
||||
protected abstract void connect(Serializable groupid, InetSocketAddress addr);
|
||||
|
||||
protected abstract void disconnect(Serializable groupid, InetSocketAddress addr);
|
||||
|
||||
//--------------------------------------------------------------------------------
|
||||
public final void connect(Serializable groupid, String engineid) {
|
||||
if (finest) logger.finest(localSncpAddress +" receive websocket connect event (" + groupid + " on " + engineid + ").");
|
||||
Set<String> engineids = localNodes.get(groupid);
|
||||
if (engineids == null) {
|
||||
engineids = new CopyOnWriteArraySet<>();
|
||||
localNodes.put(groupid, engineids);
|
||||
}
|
||||
if (localSncpAddress != null && engineids.isEmpty()) connect(groupid, localSncpAddress);
|
||||
engineids.add(engineid);
|
||||
}
|
||||
|
||||
public final void disconnect(Serializable groupid, String engineid) {
|
||||
if (finest) logger.finest(localSncpAddress +" receive websocket disconnect event (" + groupid + " on " + engineid + ").");
|
||||
Set<String> engineids = localNodes.get(groupid);
|
||||
if (engineids == null || engineids.isEmpty()) return;
|
||||
engineids.remove(engineid);
|
||||
if (engineids.isEmpty()) {
|
||||
localNodes.remove(groupid);
|
||||
if (localSncpAddress != null) disconnect(groupid, localSncpAddress);
|
||||
}
|
||||
}
|
||||
|
||||
public final void setLocalSncpAddress(InetSocketAddress localSncpAddress) {
|
||||
this.localSncpAddress = localSncpAddress;
|
||||
}
|
||||
|
||||
public final void setRemoteWebSocketNode(WebSocketNode node) {
|
||||
this.remoteNode = node;
|
||||
}
|
||||
|
||||
public final void addWebSocketEngine(WebSocketEngine engine) {
|
||||
engines.put(engine.getEngineid(), engine);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------
|
||||
public final int sendMessage(Serializable groupid, String text) {
|
||||
return sendMessage(groupid, false, text);
|
||||
}
|
||||
|
||||
public final int sendMessage(Serializable groupid, String text, boolean last) {
|
||||
return sendMessage(groupid, false, text, last);
|
||||
}
|
||||
|
||||
public final int sendMessage(Serializable groupid, boolean recent, String text) {
|
||||
return sendMessage(groupid, recent, text, true);
|
||||
}
|
||||
|
||||
public final int sendMessage(Serializable groupid, boolean recent, String text, boolean last) {
|
||||
return sendMessage(groupid, recent, (Serializable) text, last);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------
|
||||
public final int sendMessage(Serializable groupid, byte[] data) {
|
||||
return sendMessage(groupid, false, data);
|
||||
}
|
||||
|
||||
public final int sendMessage(Serializable groupid, byte[] data, boolean last) {
|
||||
return sendMessage(groupid, false, data, last);
|
||||
}
|
||||
|
||||
public final int sendMessage(Serializable groupid, boolean recent, byte[] data) {
|
||||
return sendMessage(groupid, recent, data, true);
|
||||
}
|
||||
|
||||
public final int sendMessage(Serializable groupid, boolean recent, byte[] data, boolean last) {
|
||||
return sendMessage(groupid, recent, (Serializable) data, last);
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@
|
||||
package com.wentch.redkale.net.http;
|
||||
|
||||
import com.wentch.redkale.util.Utility;
|
||||
import java.io.*;
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -54,6 +55,18 @@ public final class WebSocketPacket {
|
||||
this(payload, true);
|
||||
}
|
||||
|
||||
public WebSocketPacket(Serializable message, boolean fin) {
|
||||
boolean bin = message != null && message.getClass() == byte[].class;
|
||||
if (bin) {
|
||||
this.type = PacketType.BINARY;
|
||||
this.bytes = (byte[]) message;
|
||||
} else {
|
||||
this.type = PacketType.TEXT;
|
||||
this.payload = String.valueOf(message);
|
||||
}
|
||||
this.last = fin;
|
||||
}
|
||||
|
||||
public WebSocketPacket(String payload, boolean fin) {
|
||||
this.type = PacketType.TEXT;
|
||||
this.payload = payload;
|
||||
|
||||
@@ -61,7 +61,7 @@ public class WebSocketRunner implements Runnable {
|
||||
public void run() {
|
||||
final boolean debug = this.coder.debugable;
|
||||
try {
|
||||
if (webSocket.nodeService != null) webSocket.nodeService.connectSelf(webSocket.groupid);
|
||||
if (webSocket.node != null) webSocket.node.connect(webSocket.groupid, webSocket.engine.getEngineid());
|
||||
webSocket.onConnected();
|
||||
channel.setReadTimeoutSecond(300); //读取超时5分钟
|
||||
if (channel.isOpen()) {
|
||||
@@ -117,7 +117,7 @@ public class WebSocketRunner implements Runnable {
|
||||
readBuffer.clear();
|
||||
channel.read(readBuffer, null, this);
|
||||
}
|
||||
webSocket.group.recentWebSocket = webSocket;
|
||||
webSocket.group.setRecentWebSocket(webSocket);
|
||||
if (packet.type == PacketType.TEXT) {
|
||||
webSocket.onMessage(packet.getPayload());
|
||||
} else if (packet.type == PacketType.BINARY) {
|
||||
@@ -229,9 +229,9 @@ public class WebSocketRunner implements Runnable {
|
||||
readBuffer = null;
|
||||
writeBuffer = null;
|
||||
engine.remove(webSocket);
|
||||
if (webSocket.nodeService != null) {
|
||||
if (webSocket.node != null) {
|
||||
WebSocketGroup group = webSocket.getWebSocketGroup();
|
||||
if (group == null || group.isEmpty()) webSocket.nodeService.disconnectSelf(webSocket.groupid);
|
||||
if (group == null || group.isEmpty()) webSocket.node.disconnect(webSocket.groupid, webSocket.engine.getEngineid());
|
||||
}
|
||||
webSocket.onClose(0, null);
|
||||
}
|
||||
|
||||
@@ -6,9 +6,9 @@
|
||||
package com.wentch.redkale.net.http;
|
||||
|
||||
import com.wentch.redkale.net.*;
|
||||
import com.wentch.redkale.service.*;
|
||||
import com.wentch.redkale.util.*;
|
||||
import java.io.*;
|
||||
import java.net.*;
|
||||
import java.nio.*;
|
||||
import java.nio.channels.*;
|
||||
import java.security.*;
|
||||
@@ -33,7 +33,7 @@ import javax.annotation.*;
|
||||
*
|
||||
* @author zhangjx
|
||||
*/
|
||||
public abstract class WebSocketServlet extends HttpServlet {
|
||||
public abstract class WebSocketServlet extends HttpServlet implements Nameable {
|
||||
|
||||
protected final Logger logger = Logger.getLogger(this.getClass().getSimpleName());
|
||||
|
||||
@@ -50,21 +50,22 @@ public abstract class WebSocketServlet extends HttpServlet {
|
||||
//是否用于二进制流传输
|
||||
protected final boolean wsbinary = getClass().getAnnotation(WebSocketBinary.class) != null;
|
||||
|
||||
@Resource
|
||||
protected WebSocketNodeService nodeService;
|
||||
@Resource(name = "$")
|
||||
protected WebSocketNode node;
|
||||
|
||||
protected final WebSocketEngine engine = WebSocketEngine.create(this.getClass().getName() + "-" + Arrays.toString(this.getClass().getAnnotation(WebServlet.class).value()));
|
||||
protected WebSocketEngine engine;
|
||||
|
||||
@Override
|
||||
public void init(Context context, AnyValue conf) {
|
||||
if (nodeService != null) {
|
||||
nodeService.addWebSocketEngine(engine);
|
||||
nodeService.initUserNodes();
|
||||
}
|
||||
InetSocketAddress addr = context.getServerAddress();
|
||||
this.engine = new WebSocketEngine(addr.getHostString() + ":" + addr.getPort() + "-" + name());
|
||||
this.node.addWebSocketEngine(engine);
|
||||
this.node.init(conf);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy(Context context, AnyValue conf) {
|
||||
this.node.destroy(conf);
|
||||
super.destroy(context, conf);
|
||||
engine.close();
|
||||
}
|
||||
@@ -87,7 +88,7 @@ public abstract class WebSocketServlet extends HttpServlet {
|
||||
}
|
||||
final WebSocket webSocket = this.createWebSocket();
|
||||
webSocket.engine = engine;
|
||||
webSocket.nodeService = nodeService;
|
||||
webSocket.node = node;
|
||||
Serializable sessionid = webSocket.onOpen(request);
|
||||
if (sessionid == null) {
|
||||
if (debug) logger.finer("WebSocket connect abort, Not found sessionid. request=" + request);
|
||||
@@ -130,5 +131,8 @@ public abstract class WebSocketServlet extends HttpServlet {
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public abstract String name();
|
||||
|
||||
protected abstract WebSocket createWebSocket();
|
||||
}
|
||||
|
||||
@@ -1,83 +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 com.wentch.redkale.net.sncp;
|
||||
|
||||
import com.wentch.redkale.service.Service;
|
||||
import com.wentch.redkale.util.AnyValue;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author zhangjx
|
||||
*/
|
||||
public final class ServiceEntry {
|
||||
|
||||
private final Class<? extends Service> serviceClass;
|
||||
|
||||
private final Service service;
|
||||
|
||||
private final AnyValue conf;
|
||||
|
||||
private final List<String> names = new ArrayList<>();
|
||||
|
||||
public ServiceEntry(Class<? extends Service> serviceClass, Service service, AnyValue conf, String name) {
|
||||
this.serviceClass = serviceClass == null ? service.getClass() : serviceClass;
|
||||
this.service = service;
|
||||
this.conf = conf;
|
||||
this.names.add(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == this) return true;
|
||||
if (obj == null) return false;
|
||||
if (!(obj instanceof ServiceEntry)) return false;
|
||||
ServiceEntry other = (ServiceEntry) obj;
|
||||
if (this.serviceClass != other.serviceClass) return false;
|
||||
return (this.service == other.service);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int hash = 5;
|
||||
hash = 97 * hash + Objects.hashCode(this.serviceClass);
|
||||
hash = 97 * hash + Objects.hashCode(this.service);
|
||||
return hash;
|
||||
}
|
||||
|
||||
public Class<? extends Service> getServiceClass() {
|
||||
return serviceClass;
|
||||
}
|
||||
|
||||
public Service getService() {
|
||||
return service;
|
||||
}
|
||||
|
||||
public AnyValue getServiceConf() {
|
||||
return conf;
|
||||
}
|
||||
|
||||
public void initService() {
|
||||
service.init(conf);
|
||||
}
|
||||
|
||||
public void destroyService() {
|
||||
service.destroy(conf);
|
||||
}
|
||||
|
||||
public List<String> getNames() {
|
||||
return names;
|
||||
}
|
||||
|
||||
public void addName(String name) {
|
||||
this.names.add(name);
|
||||
}
|
||||
|
||||
public boolean containsName(String name) {
|
||||
return names.contains(name);
|
||||
}
|
||||
|
||||
}
|
||||
84
src/com/wentch/redkale/net/sncp/ServiceWrapper.java
Normal file
84
src/com/wentch/redkale/net/sncp/ServiceWrapper.java
Normal file
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* 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 com.wentch.redkale.net.sncp;
|
||||
|
||||
import com.wentch.redkale.boot.*;
|
||||
import com.wentch.redkale.service.Service;
|
||||
import com.wentch.redkale.util.AnyValue;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author zhangjx
|
||||
* @param <T>
|
||||
*/
|
||||
public final class ServiceWrapper<T extends Service> {
|
||||
|
||||
private final Class<T> type;
|
||||
|
||||
private final T service;
|
||||
|
||||
private final AnyValue conf;
|
||||
|
||||
private final String group;
|
||||
|
||||
private final String name;
|
||||
|
||||
private final boolean remote;
|
||||
|
||||
public ServiceWrapper(Class<T> type, T service, ClassFilter.FilterEntry<Service> entry) {
|
||||
this(type, service, entry.getGroup(), entry.getName(), entry.getProperty());
|
||||
}
|
||||
|
||||
public ServiceWrapper(Class<T> type, T service, String group, String name, AnyValue conf) {
|
||||
this.type = type == null ? (Class<T>) service.getClass() : type;
|
||||
this.service = service;
|
||||
this.conf = conf;
|
||||
this.group = group;
|
||||
this.name = name;
|
||||
this.remote = Sncp.isRemote(service);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == this) return true;
|
||||
if (obj == null) return false;
|
||||
if (!(obj instanceof ServiceWrapper)) return false;
|
||||
ServiceWrapper other = (ServiceWrapper) obj;
|
||||
return (this.type.equals(other.type) && this.remote == other.remote && this.name.equals(other.name) && this.group.equals(other.group));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int hash = 3;
|
||||
hash = 67 * hash + Objects.hashCode(this.type);
|
||||
hash = 67 * hash + Objects.hashCode(this.group);
|
||||
hash = 67 * hash + Objects.hashCode(this.name);
|
||||
hash = 67 * hash + (this.remote ? 1 : 0);
|
||||
return hash;
|
||||
}
|
||||
|
||||
public Class<? extends Service> getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public Service getService() {
|
||||
return service;
|
||||
}
|
||||
|
||||
public AnyValue getConf() {
|
||||
return conf;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return service.name();
|
||||
}
|
||||
|
||||
public boolean isRemote() {
|
||||
return remote;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -10,8 +10,12 @@ import com.wentch.redkale.net.*;
|
||||
import com.wentch.redkale.net.sncp.SncpClient.SncpAction;
|
||||
import com.wentch.redkale.service.*;
|
||||
import com.wentch.redkale.util.*;
|
||||
import java.lang.reflect.*;
|
||||
import java.net.*;
|
||||
import java.util.*;
|
||||
import jdk.internal.org.objectweb.asm.*;
|
||||
import static jdk.internal.org.objectweb.asm.Opcodes.*;
|
||||
import jdk.internal.org.objectweb.asm.Type;
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -19,6 +23,12 @@ import static jdk.internal.org.objectweb.asm.Opcodes.*;
|
||||
*/
|
||||
public abstract class Sncp {
|
||||
|
||||
public static final String DEFAULT_PROTOCOL = "UDP";
|
||||
|
||||
static final String LOCALPREFIX = "_DynLocal";
|
||||
|
||||
static final String REMOTEPREFIX = "_DynRemote";
|
||||
|
||||
private static final byte[] hashes = new byte[255];
|
||||
|
||||
static {
|
||||
@@ -40,12 +50,23 @@ public abstract class Sncp {
|
||||
private Sncp() {
|
||||
}
|
||||
|
||||
public static long nodeid(InetSocketAddress ip) {
|
||||
byte[] bytes = ip.getAddress().getAddress();
|
||||
return ((0L + ip.getPort()) << 32) | ((0xffffffff & bytes[0]) << 24) | ((0xffffff & bytes[1]) << 16) | ((0xffff & bytes[2]) << 8) | (0xff & bytes[3]);
|
||||
}
|
||||
|
||||
public static long hash(final Class clazz) {
|
||||
if (clazz == null) return Long.MIN_VALUE;
|
||||
long rs = hash(clazz.getSimpleName());
|
||||
return (rs < Integer.MAX_VALUE) ? rs | 0xF00000000L : rs;
|
||||
}
|
||||
|
||||
public static long hashClass(final String clazzName) {
|
||||
if (clazzName == null || clazzName.isEmpty()) return Long.MIN_VALUE;
|
||||
long rs = hash(clazzName.substring(clazzName.lastIndexOf('.') + 1));
|
||||
return (rs < Integer.MAX_VALUE) ? rs | 0xF00000000L : rs;
|
||||
}
|
||||
|
||||
public static DLong hash(final java.lang.reflect.Method method) {
|
||||
if (method == null) return new DLong(-1L, -1L);
|
||||
long rs1 = hash(method.getName());
|
||||
@@ -95,61 +116,103 @@ public abstract class Sncp {
|
||||
return Math.abs(rs);
|
||||
}
|
||||
|
||||
public static boolean isRemote(Service service) {
|
||||
return service.getClass().getName().startsWith(REMOTEPREFIX);
|
||||
}
|
||||
|
||||
/*
|
||||
* public final class DynRemoteTestService extends TestService{
|
||||
*
|
||||
* @Resource
|
||||
* private BsonConvert convert;
|
||||
*
|
||||
* @Resource(name="xxxx")
|
||||
* private Transport transport;
|
||||
*
|
||||
* public SncpClient client;
|
||||
*
|
||||
* @Override
|
||||
* public boolean testChange(TestBean bean) {
|
||||
* return client.remote(convert, transport, 0, bean);
|
||||
* }
|
||||
*
|
||||
* @Override
|
||||
* public TestBean findTestBean(long id) {
|
||||
* return client.remote(convert, transport, 1, id);
|
||||
* }
|
||||
*
|
||||
* @Override
|
||||
* public void runTestBean(long id, TestBean bean) {
|
||||
* client.remote(convert, transport, 2, id, bean);
|
||||
* }
|
||||
*/
|
||||
/**
|
||||
* public class TestService implements Service{
|
||||
*
|
||||
* public String queryNode(){
|
||||
* return "hello";
|
||||
* }
|
||||
*
|
||||
* @MultiRun
|
||||
* public String updateSomeThing(String id){
|
||||
* return "hello" + id;
|
||||
* }
|
||||
*
|
||||
* @MultiRun
|
||||
* public void createSomeThing(TestBean bean){
|
||||
* "xxxxx" + bean;
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* public final class _DynLocalTestService extends TestService{
|
||||
*
|
||||
* @Resource
|
||||
* private BsonConvert _convert;
|
||||
*
|
||||
* private Transport[] _sameGroupTransports;
|
||||
*
|
||||
* private Transport[] _diffGroupTransports;
|
||||
*
|
||||
* private SncpClient _client;
|
||||
*
|
||||
* @Override
|
||||
* public final String name() {
|
||||
* return "";
|
||||
* }
|
||||
*
|
||||
* @Override
|
||||
* public String updateSomeThing(String id){
|
||||
* return _updateSomeThing(true, true, id);
|
||||
* }
|
||||
*
|
||||
* public String _updateSomeThing(boolean cansamerun, boolean candiffrun, String id){
|
||||
* String rs = super.updateSomeThing(id);
|
||||
* _client.remote(_convert, _sameGroupTransports, cansamerun, 0, false, false, id);
|
||||
* _client.remote(_convert, _diffGroupTransports, candiffrun, 0, true, false, id);
|
||||
* return rs;
|
||||
* }
|
||||
*
|
||||
* @Override
|
||||
* public void createSomeThing(TestBean bean){
|
||||
* _createSomeThing(true, true, bean);
|
||||
* }
|
||||
*
|
||||
* public void _createSomeThing(boolean cansamerun, boolean candiffrun, TestBean bean){
|
||||
* super.createSomeThing(bean);
|
||||
* _client.remote(_convert, _sameGroupTransports, cansamerun, 1, false, false, bean);
|
||||
* _client.remote(_convert, _diffGroupTransports, candiffrun, 1, true, false, bean);
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* 创建Service的本地模式Class
|
||||
* @param <T>
|
||||
* @param serviceName
|
||||
* @param remote
|
||||
* @param name
|
||||
* @param serviceClass
|
||||
* @return
|
||||
* @return
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T extends Service> T createRemoteService(final String serviceName, final Class<T> serviceClass, final String remote) {
|
||||
public static <T extends Service> Class<? extends T> createLocalServiceClass(final String name, final Class<T> serviceClass) {
|
||||
if (serviceClass == null) return null;
|
||||
if (!Service.class.isAssignableFrom(serviceClass)) return null;
|
||||
if (!Service.class.isAssignableFrom(serviceClass)) return serviceClass;
|
||||
int mod = serviceClass.getModifiers();
|
||||
if (!java.lang.reflect.Modifier.isPublic(mod)) return null;
|
||||
if (java.lang.reflect.Modifier.isAbstract(mod)) return null;
|
||||
if (!java.lang.reflect.Modifier.isPublic(mod)) return serviceClass;
|
||||
if (java.lang.reflect.Modifier.isAbstract(mod)) return serviceClass;
|
||||
final List<Method> methods = SncpClient.parseMethod(serviceClass, false);
|
||||
boolean hasMultiRun0 = false;
|
||||
for (Method method : methods) {
|
||||
if (method.getAnnotation(MultiRun.class) != null) {
|
||||
hasMultiRun0 = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
final boolean hasMultiRun = hasMultiRun0;
|
||||
final String supDynName = serviceClass.getName().replace('.', '/');
|
||||
final String clientName = SncpClient.class.getName().replace('.', '/');
|
||||
final String clientDesc = Type.getDescriptor(SncpClient.class);
|
||||
final String convertDesc = Type.getDescriptor(BsonConvert.class);
|
||||
final String transportDesc = Type.getDescriptor(Transport.class);
|
||||
final String anyValueDesc = Type.getDescriptor(AnyValue.class);
|
||||
final String sncpDynDesc = Type.getDescriptor(SncpDyn.class);
|
||||
final String transportsDesc = Type.getDescriptor(Transport[].class);
|
||||
ClassLoader loader = Sncp.class.getClassLoader();
|
||||
String newDynName = supDynName.substring(0, supDynName.lastIndexOf('/') + 1) + "DynRemote" + serviceClass.getSimpleName();
|
||||
String newDynName = supDynName.substring(0, supDynName.lastIndexOf('/') + 1) + LOCALPREFIX + serviceClass.getSimpleName();
|
||||
try {
|
||||
return (T) Class.forName(newDynName.replace('/', '.')).newInstance();
|
||||
return (Class<T>) Class.forName(newDynName.replace('/', '.'));
|
||||
} catch (Exception ex) {
|
||||
}
|
||||
final SncpClient client = new SncpClient(serviceName, serviceClass);
|
||||
//------------------------------------------------------------------------------
|
||||
ClassWriter cw = new ClassWriter(0);
|
||||
FieldVisitor fv;
|
||||
@@ -158,24 +221,436 @@ public abstract class Sncp {
|
||||
|
||||
cw.visit(V1_8, ACC_PUBLIC + ACC_FINAL + ACC_SUPER, newDynName, null, supDynName, null);
|
||||
{
|
||||
av0 = cw.visitAnnotation(Type.getDescriptor(RemoteOn.class), true);
|
||||
av0 = cw.visitAnnotation(sncpDynDesc, true);
|
||||
av0.visitEnd();
|
||||
}
|
||||
if (hasMultiRun) {
|
||||
{
|
||||
fv = cw.visitField(ACC_PRIVATE, "_convert", convertDesc, null, null);
|
||||
av0 = fv.visitAnnotation("Ljavax/annotation/Resource;", true);
|
||||
av0.visitEnd();
|
||||
fv.visitEnd();
|
||||
}
|
||||
{
|
||||
fv = cw.visitField(ACC_PRIVATE, "_sameGroupTransports", transportsDesc, null, null);
|
||||
fv.visitEnd();
|
||||
}
|
||||
{
|
||||
fv = cw.visitField(ACC_PRIVATE, "_diffGroupTransports", transportsDesc, null, null);
|
||||
fv.visitEnd();
|
||||
}
|
||||
{
|
||||
fv = cw.visitField(ACC_PRIVATE, "_client", clientDesc, null, null);
|
||||
fv.visitEnd();
|
||||
}
|
||||
}
|
||||
{ //构造函数
|
||||
mv = new DebugMethodVisitor(cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null));
|
||||
//mv.setDebug(true);
|
||||
mv.visitVarInsn(ALOAD, 0);
|
||||
mv.visitMethodInsn(INVOKESPECIAL, supDynName, "<init>", "()V", false);
|
||||
mv.visitInsn(RETURN);
|
||||
mv.visitMaxs(1, 1);
|
||||
mv.visitEnd();
|
||||
}
|
||||
{ // name()
|
||||
mv = new DebugMethodVisitor(cw.visitMethod(ACC_PUBLIC + ACC_FINAL, "name", "()Ljava/lang/String;", null, null));
|
||||
mv.visitLdcInsn(name);
|
||||
mv.visitInsn(ARETURN);
|
||||
mv.visitMaxs(1, 1);
|
||||
mv.visitEnd();
|
||||
}
|
||||
int i = - 1;
|
||||
for (final Method method : methods) {
|
||||
final MultiRun mrun = method.getAnnotation(MultiRun.class);
|
||||
if (mrun == null) continue;
|
||||
final Class returnType = method.getReturnType();
|
||||
final String methodDesc = Type.getMethodDescriptor(method);
|
||||
final Class[] paramtypes = method.getParameterTypes();
|
||||
final int index = ++i;
|
||||
{ //原始方法
|
||||
mv = new DebugMethodVisitor(cw.visitMethod(ACC_PUBLIC + (method.isVarArgs() ? ACC_VARARGS : 0), method.getName(), methodDesc, null, null));
|
||||
//mv.setDebug(true);
|
||||
mv.visitVarInsn(ALOAD, 0);
|
||||
mv.visitInsn(mrun.samerun() ? ICONST_1 : ICONST_0);
|
||||
mv.visitInsn(mrun.diffrun() ? ICONST_1 : ICONST_0);
|
||||
int varindex = 0;
|
||||
for (Class pt : paramtypes) {
|
||||
if (pt.isPrimitive()) {
|
||||
if (pt == long.class) {
|
||||
mv.visitVarInsn(LLOAD, ++varindex);
|
||||
++varindex;
|
||||
} else if (pt == double.class) {
|
||||
mv.visitVarInsn(DLOAD, ++varindex);
|
||||
++varindex;
|
||||
} else if (pt == float.class) {
|
||||
mv.visitVarInsn(FLOAD, ++varindex);
|
||||
} else {
|
||||
mv.visitVarInsn(ILOAD, ++varindex);
|
||||
}
|
||||
} else {
|
||||
mv.visitVarInsn(ALOAD, ++varindex);
|
||||
}
|
||||
}
|
||||
mv.visitMethodInsn(INVOKEVIRTUAL, newDynName, "_" + method.getName(), "(ZZ" + methodDesc.substring(1), false);
|
||||
if (returnType == void.class) {
|
||||
mv.visitInsn(RETURN);
|
||||
} else if (returnType.isPrimitive()) {
|
||||
if (returnType == long.class) {
|
||||
mv.visitInsn(LRETURN);
|
||||
} else if (returnType == float.class) {
|
||||
mv.visitInsn(FRETURN);
|
||||
} else if (returnType == double.class) {
|
||||
mv.visitInsn(DRETURN);
|
||||
} else {
|
||||
mv.visitInsn(IRETURN);
|
||||
}
|
||||
} else {
|
||||
mv.visitInsn(ARETURN);
|
||||
}
|
||||
mv.visitMaxs(varindex + 3, varindex + 1);
|
||||
mv.visitEnd();
|
||||
}
|
||||
{ // _方法
|
||||
mv = new DebugMethodVisitor(cw.visitMethod(ACC_PUBLIC + (method.isVarArgs() ? ACC_VARARGS : 0), "_" + method.getName(), "(ZZ" + methodDesc.substring(1), null, null));
|
||||
//mv.setDebug(true);
|
||||
av0 = mv.visitAnnotation(sncpDynDesc, true);
|
||||
av0.visit("index", index);
|
||||
av0.visitEnd();
|
||||
|
||||
mv.visitVarInsn(ALOAD, 0);
|
||||
int varindex = 2;
|
||||
for (Class pt : paramtypes) {
|
||||
if (pt.isPrimitive()) {
|
||||
if (pt == long.class) {
|
||||
mv.visitVarInsn(LLOAD, ++varindex);
|
||||
++varindex;
|
||||
} else if (pt == double.class) {
|
||||
mv.visitVarInsn(DLOAD, ++varindex);
|
||||
++varindex;
|
||||
} else if (pt == float.class) {
|
||||
mv.visitVarInsn(FLOAD, ++varindex);
|
||||
} else {
|
||||
mv.visitVarInsn(ILOAD, ++varindex);
|
||||
}
|
||||
} else {
|
||||
mv.visitVarInsn(ALOAD, ++varindex);
|
||||
}
|
||||
}
|
||||
mv.visitMethodInsn(INVOKESPECIAL, supDynName, method.getName(), methodDesc, false);
|
||||
if (returnType == void.class) {
|
||||
} else if (returnType.isPrimitive()) {
|
||||
if (returnType == long.class) {
|
||||
mv.visitVarInsn(LSTORE, ++varindex);
|
||||
++varindex; //多加1
|
||||
} else if (returnType == float.class) {
|
||||
mv.visitVarInsn(FSTORE, ++varindex);
|
||||
} else if (returnType == double.class) {
|
||||
mv.visitVarInsn(DSTORE, ++varindex);
|
||||
++varindex; //多加1
|
||||
} else {
|
||||
mv.visitVarInsn(ISTORE, ++varindex);
|
||||
}
|
||||
} else {
|
||||
mv.visitVarInsn(ASTORE, ++varindex);
|
||||
}
|
||||
final int rsindex = varindex; //
|
||||
|
||||
mv.visitVarInsn(ALOAD, 0);//调用 _client
|
||||
mv.visitFieldInsn(GETFIELD, newDynName, "_client", clientDesc);
|
||||
mv.visitVarInsn(ALOAD, 0); //传递 _convert
|
||||
mv.visitFieldInsn(GETFIELD, newDynName, "_convert", convertDesc);
|
||||
mv.visitVarInsn(ALOAD, 0); //传递 _sameGroupTransports
|
||||
mv.visitFieldInsn(GETFIELD, newDynName, "_sameGroupTransports", transportsDesc);
|
||||
|
||||
mv.visitVarInsn(ILOAD, 1); //传递 cansamerun
|
||||
|
||||
if (index <= 5) { //第几个 SncpAction
|
||||
mv.visitInsn(ICONST_0 + index);
|
||||
} else {
|
||||
mv.visitIntInsn(BIPUSH, index);
|
||||
}
|
||||
if (paramtypes.length + 2 <= 5) { //参数总数量
|
||||
mv.visitInsn(ICONST_0 + paramtypes.length + 2);
|
||||
} else {
|
||||
mv.visitIntInsn(BIPUSH, paramtypes.length + 2);
|
||||
}
|
||||
|
||||
mv.visitTypeInsn(ANEWARRAY, "java/lang/Object");
|
||||
|
||||
mv.visitInsn(DUP);
|
||||
mv.visitInsn(ICONST_0);
|
||||
mv.visitInsn(ICONST_0); //第一个参数 cansamerun
|
||||
mv.visitMethodInsn(INVOKESTATIC, "java/lang/Boolean", "valueOf", "(Z)Ljava/lang/Boolean;", false);
|
||||
mv.visitInsn(AASTORE);
|
||||
|
||||
mv.visitInsn(DUP);
|
||||
mv.visitInsn(ICONST_1);
|
||||
mv.visitInsn(ICONST_0); //第二个参数 candiffrun
|
||||
mv.visitMethodInsn(INVOKESTATIC, "java/lang/Boolean", "valueOf", "(Z)Ljava/lang/Boolean;", false);
|
||||
mv.visitInsn(AASTORE);
|
||||
|
||||
int insn = 2;
|
||||
for (int j = 0; j < paramtypes.length; j++) {
|
||||
final Class pt = paramtypes[j];
|
||||
mv.visitInsn(DUP);
|
||||
insn++;
|
||||
if (j <= 3) {
|
||||
mv.visitInsn(ICONST_0 + j + 2);
|
||||
} else {
|
||||
mv.visitIntInsn(BIPUSH, j + 2);
|
||||
}
|
||||
if (pt.isPrimitive()) {
|
||||
if (pt == long.class) {
|
||||
mv.visitVarInsn(LLOAD, insn++);
|
||||
} else if (pt == float.class) {
|
||||
mv.visitVarInsn(FLOAD, insn++);
|
||||
} else if (pt == double.class) {
|
||||
mv.visitVarInsn(DLOAD, insn++);
|
||||
} else {
|
||||
mv.visitVarInsn(ILOAD, insn);
|
||||
}
|
||||
Class bigclaz = java.lang.reflect.Array.get(java.lang.reflect.Array.newInstance(pt, 1), 0).getClass();
|
||||
mv.visitMethodInsn(INVOKESTATIC, bigclaz.getName().replace('.', '/'), "valueOf", "(" + Type.getDescriptor(pt) + ")" + Type.getDescriptor(bigclaz), false);
|
||||
} else {
|
||||
mv.visitVarInsn(ALOAD, insn);
|
||||
}
|
||||
mv.visitInsn(AASTORE);
|
||||
}
|
||||
mv.visitMethodInsn(INVOKEVIRTUAL, clientName, mrun.async() ? "asyncRemote" : "remote", "(" + convertDesc + transportsDesc + "ZI[Ljava/lang/Object;)V", false);
|
||||
|
||||
mv.visitVarInsn(ALOAD, 0);
|
||||
mv.visitFieldInsn(GETFIELD, newDynName, "_client", clientDesc);
|
||||
mv.visitVarInsn(ALOAD, 0);
|
||||
mv.visitFieldInsn(GETFIELD, newDynName, "_convert", convertDesc);
|
||||
mv.visitVarInsn(ALOAD, 0);
|
||||
mv.visitFieldInsn(GETFIELD, newDynName, "_diffGroupTransports", transportsDesc);
|
||||
|
||||
mv.visitVarInsn(ILOAD, 2); //传递 candiffrun
|
||||
if (index <= 5) { //第几个 SncpAction
|
||||
mv.visitInsn(ICONST_0 + index);
|
||||
} else {
|
||||
mv.visitIntInsn(BIPUSH, index);
|
||||
}
|
||||
if (paramtypes.length + 2 <= 5) { //参数总数量
|
||||
mv.visitInsn(ICONST_0 + paramtypes.length + 2);
|
||||
} else {
|
||||
mv.visitIntInsn(BIPUSH, paramtypes.length + 2);
|
||||
}
|
||||
|
||||
mv.visitTypeInsn(ANEWARRAY, "java/lang/Object");
|
||||
|
||||
mv.visitInsn(DUP);
|
||||
mv.visitInsn(ICONST_0);
|
||||
mv.visitInsn(ICONST_1); //第一个参数 cansamerun
|
||||
mv.visitMethodInsn(INVOKESTATIC, "java/lang/Boolean", "valueOf", "(Z)Ljava/lang/Boolean;", false);
|
||||
mv.visitInsn(AASTORE);
|
||||
|
||||
mv.visitInsn(DUP);
|
||||
mv.visitInsn(ICONST_1);
|
||||
mv.visitInsn(ICONST_0); //第二个参数 candiffrun
|
||||
mv.visitMethodInsn(INVOKESTATIC, "java/lang/Boolean", "valueOf", "(Z)Ljava/lang/Boolean;", false);
|
||||
mv.visitInsn(AASTORE);
|
||||
|
||||
insn = 2;
|
||||
for (int j = 0; j < paramtypes.length; j++) {
|
||||
final Class pt = paramtypes[j];
|
||||
mv.visitInsn(DUP);
|
||||
insn++;
|
||||
if (j <= 3) {
|
||||
mv.visitInsn(ICONST_0 + j + 2);
|
||||
} else {
|
||||
mv.visitIntInsn(BIPUSH, j + 2);
|
||||
}
|
||||
if (pt.isPrimitive()) {
|
||||
if (pt == long.class) {
|
||||
mv.visitVarInsn(LLOAD, insn++);
|
||||
} else if (pt == float.class) {
|
||||
mv.visitVarInsn(FLOAD, insn++);
|
||||
} else if (pt == double.class) {
|
||||
mv.visitVarInsn(DLOAD, insn++);
|
||||
} else {
|
||||
mv.visitVarInsn(ILOAD, insn);
|
||||
}
|
||||
Class bigclaz = java.lang.reflect.Array.get(java.lang.reflect.Array.newInstance(pt, 1), 0).getClass();
|
||||
mv.visitMethodInsn(INVOKESTATIC, bigclaz.getName().replace('.', '/'), "valueOf", "(" + Type.getDescriptor(pt) + ")" + Type.getDescriptor(bigclaz), false);
|
||||
} else {
|
||||
mv.visitVarInsn(ALOAD, insn);
|
||||
}
|
||||
mv.visitInsn(AASTORE);
|
||||
}
|
||||
mv.visitMethodInsn(INVOKEVIRTUAL, clientName, mrun.async() ? "asyncRemote" : "remote", "(" + convertDesc + transportsDesc + "ZI[Ljava/lang/Object;)V", false);
|
||||
|
||||
if (returnType == void.class) {
|
||||
mv.visitInsn(RETURN);
|
||||
} else if (returnType.isPrimitive()) {
|
||||
if (returnType == long.class) {
|
||||
mv.visitVarInsn(LLOAD, rsindex);
|
||||
mv.visitInsn(LRETURN);
|
||||
} else if (returnType == float.class) {
|
||||
mv.visitVarInsn(FLOAD, rsindex);
|
||||
mv.visitInsn(FRETURN);
|
||||
} else if (returnType == double.class) {
|
||||
mv.visitVarInsn(DLOAD, rsindex);
|
||||
mv.visitInsn(DRETURN);
|
||||
} else {
|
||||
mv.visitVarInsn(ILOAD, rsindex);
|
||||
mv.visitInsn(IRETURN);
|
||||
}
|
||||
} else {
|
||||
mv.visitVarInsn(ALOAD, rsindex);
|
||||
mv.visitInsn(ARETURN);
|
||||
}
|
||||
|
||||
mv.visitMaxs(Math.max(varindex, 10), varindex + 4);
|
||||
mv.visitEnd();
|
||||
}
|
||||
}
|
||||
cw.visitEnd();
|
||||
byte[] bytes = cw.toByteArray();
|
||||
Class<?> newClazz = new ClassLoader(loader) {
|
||||
public final Class<?> loadClass(String name, byte[] b) {
|
||||
return defineClass(name, b, 0, b.length);
|
||||
}
|
||||
}.loadClass(newDynName.replace('/', '.'), bytes);
|
||||
return (Class<T>) newClazz;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* 创建本地模式Service实例
|
||||
* @param <T>
|
||||
* @param name
|
||||
* @param serviceClass
|
||||
* @param clientAddress
|
||||
* @param sameGroupTransports
|
||||
* @param diffGroupTransports
|
||||
* @return
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T extends Service> T createLocalService(final String name, final Class<T> serviceClass,
|
||||
final InetSocketAddress clientAddress, Collection<Transport> sameGroupTransports, Collection<Transport> diffGroupTransports) {
|
||||
try {
|
||||
Class newClazz = createLocalServiceClass(name, serviceClass);
|
||||
T rs = (T) newClazz.newInstance();
|
||||
Field e = null;
|
||||
try {
|
||||
e = newClazz.getDeclaredField("_client");
|
||||
} catch (NoSuchFieldException ne) {
|
||||
return rs;
|
||||
}
|
||||
e.setAccessible(true);
|
||||
e.set(rs, new SncpClient(name, hash(serviceClass), false, newClazz, true, clientAddress));
|
||||
if (sameGroupTransports == null) sameGroupTransports = new ArrayList<>();
|
||||
Field c = newClazz.getDeclaredField("_sameGroupTransports");
|
||||
c.setAccessible(true);
|
||||
c.set(rs, sameGroupTransports.toArray(new Transport[sameGroupTransports.size()]));
|
||||
|
||||
if (diffGroupTransports == null) diffGroupTransports = new ArrayList<>();
|
||||
Field t = newClazz.getDeclaredField("_diffGroupTransports");
|
||||
t.setAccessible(true);
|
||||
t.set(rs, diffGroupTransports.toArray(new Transport[diffGroupTransports.size()]));
|
||||
return rs;
|
||||
} catch (RuntimeException rex) {
|
||||
throw rex;
|
||||
} catch (Exception ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* public final class _DynRemoteTestService extends TestService{
|
||||
*
|
||||
* @Resource
|
||||
* private BsonConvert _convert;
|
||||
*
|
||||
* private Transport _transport;
|
||||
*
|
||||
* private SncpClient _client;
|
||||
*
|
||||
* @Override
|
||||
* public final String name() {
|
||||
* return "";
|
||||
* }
|
||||
*
|
||||
* @Override
|
||||
* public boolean testChange(TestBean bean) {
|
||||
* return _client.remote(_convert, _transport, 0, bean);
|
||||
* }
|
||||
*
|
||||
* @Override
|
||||
* public TestBean findTestBean(long id) {
|
||||
* return _client.remote(_convert, _transport, 1, id);
|
||||
* }
|
||||
*
|
||||
* @Override
|
||||
* public void runTestBean(long id, TestBean bean) {
|
||||
* _client.remote(_convert, _transport, 2, id, bean);
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* 创建远程模式的Service实例
|
||||
* <p>
|
||||
* @param <T>
|
||||
* @param name
|
||||
* @param serviceClass
|
||||
* @param clientAddress
|
||||
* @param transport
|
||||
* @return
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T extends Service> T createRemoteService(final String name, final Class<T> serviceClass, final InetSocketAddress clientAddress, final Transport transport) {
|
||||
if (serviceClass == null) return null;
|
||||
if (!Service.class.isAssignableFrom(serviceClass)) return null;
|
||||
int mod = serviceClass.getModifiers();
|
||||
if (!java.lang.reflect.Modifier.isPublic(mod)) return null;
|
||||
if (java.lang.reflect.Modifier.isAbstract(mod)) return null;
|
||||
final String supDynName = serviceClass.getName().replace('.', '/');
|
||||
final String clientName = SncpClient.class.getName().replace('.', '/');
|
||||
final String clientDesc = Type.getDescriptor(SncpClient.class);
|
||||
final String sncpDynDesc = Type.getDescriptor(SncpDyn.class);
|
||||
final String convertDesc = Type.getDescriptor(BsonConvert.class);
|
||||
final String transportDesc = Type.getDescriptor(Transport.class);
|
||||
final String anyValueDesc = Type.getDescriptor(AnyValue.class);
|
||||
ClassLoader loader = Sncp.class.getClassLoader();
|
||||
String newDynName = supDynName.substring(0, supDynName.lastIndexOf('/') + 1) + REMOTEPREFIX + serviceClass.getSimpleName();
|
||||
final SncpClient client = new SncpClient(name, hash(serviceClass), true, createLocalServiceClass(name, serviceClass), false, clientAddress);
|
||||
try {
|
||||
Class newClazz = Class.forName(newDynName.replace('/', '.'));
|
||||
T rs = (T) newClazz.newInstance();
|
||||
Field c = newClazz.getDeclaredField("_client");
|
||||
c.setAccessible(true);
|
||||
c.set(rs, client);
|
||||
Field t = newClazz.getDeclaredField("_transport");
|
||||
t.setAccessible(true);
|
||||
t.set(rs, transport);
|
||||
return rs;
|
||||
} catch (Exception ex) {
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
ClassWriter cw = new ClassWriter(0);
|
||||
FieldVisitor fv;
|
||||
DebugMethodVisitor mv;
|
||||
AnnotationVisitor av0;
|
||||
|
||||
cw.visit(V1_8, ACC_PUBLIC + ACC_FINAL + ACC_SUPER, newDynName, null, supDynName, null);
|
||||
{
|
||||
av0 = cw.visitAnnotation(sncpDynDesc, true);
|
||||
av0.visitEnd();
|
||||
}
|
||||
{
|
||||
fv = cw.visitField(ACC_PRIVATE, "convert", convertDesc, null, null);
|
||||
fv = cw.visitField(ACC_PRIVATE, "_convert", convertDesc, null, null);
|
||||
av0 = fv.visitAnnotation("Ljavax/annotation/Resource;", true);
|
||||
av0.visitEnd();
|
||||
fv.visitEnd();
|
||||
}
|
||||
{
|
||||
fv = cw.visitField(ACC_PRIVATE, "transport", transportDesc, null, null);
|
||||
av0 = fv.visitAnnotation("Ljavax/annotation/Resource;", true);
|
||||
av0.visit("name", remote == null ? "" : remote);
|
||||
av0.visitEnd();
|
||||
fv = cw.visitField(ACC_PRIVATE, "_transport", transportDesc, null, null);
|
||||
fv.visitEnd();
|
||||
}
|
||||
{
|
||||
fv = cw.visitField(ACC_PUBLIC, "client", clientDesc, null, null);
|
||||
fv = cw.visitField(ACC_PRIVATE, "_client", clientDesc, null, null);
|
||||
fv.visitEnd();
|
||||
}
|
||||
{ //构造函数
|
||||
@@ -199,6 +674,13 @@ public abstract class Sncp {
|
||||
mv.visitMaxs(0, 2);
|
||||
mv.visitEnd();
|
||||
}
|
||||
{ // name()
|
||||
mv = new DebugMethodVisitor(cw.visitMethod(ACC_PUBLIC + ACC_FINAL, "name", "()Ljava/lang/String;", null, null));
|
||||
mv.visitLdcInsn(name);
|
||||
mv.visitInsn(ARETURN);
|
||||
mv.visitMaxs(1, 1);
|
||||
mv.visitEnd();
|
||||
}
|
||||
int i = -1;
|
||||
for (final SncpAction entry : client.actions) {
|
||||
final int index = ++i;
|
||||
@@ -207,11 +689,11 @@ public abstract class Sncp {
|
||||
mv = new DebugMethodVisitor(cw.visitMethod(ACC_PUBLIC, method.getName(), Type.getMethodDescriptor(method), null, null));
|
||||
//mv.setDebug(true);
|
||||
mv.visitVarInsn(ALOAD, 0);
|
||||
mv.visitFieldInsn(GETFIELD, newDynName, "client", clientDesc);
|
||||
mv.visitFieldInsn(GETFIELD, newDynName, "_client", clientDesc);
|
||||
mv.visitVarInsn(ALOAD, 0);
|
||||
mv.visitFieldInsn(GETFIELD, newDynName, "convert", convertDesc);
|
||||
mv.visitFieldInsn(GETFIELD, newDynName, "_convert", convertDesc);
|
||||
mv.visitVarInsn(ALOAD, 0);
|
||||
mv.visitFieldInsn(GETFIELD, newDynName, "transport", transportDesc);
|
||||
mv.visitFieldInsn(GETFIELD, newDynName, "_transport", transportDesc);
|
||||
if (index <= 5) {
|
||||
mv.visitInsn(ICONST_0 + index);
|
||||
} else {
|
||||
@@ -252,7 +734,6 @@ public abstract class Sncp {
|
||||
} else {
|
||||
mv.visitVarInsn(ALOAD, insn);
|
||||
}
|
||||
//mv.visitVarInsn(ALOAD, 1);
|
||||
mv.visitInsn(AASTORE);
|
||||
}
|
||||
}
|
||||
@@ -300,7 +781,12 @@ public abstract class Sncp {
|
||||
}.loadClass(newDynName.replace('/', '.'), bytes);
|
||||
try {
|
||||
T rs = (T) newClazz.newInstance();
|
||||
newClazz.getField("client").set(rs, client);
|
||||
Field c = newClazz.getDeclaredField("_client");
|
||||
c.setAccessible(true);
|
||||
c.set(rs, client);
|
||||
Field t = newClazz.getDeclaredField("_transport");
|
||||
t.setAccessible(true);
|
||||
t.set(rs, transport);
|
||||
return rs;
|
||||
} catch (Exception ex) {
|
||||
throw new RuntimeException(ex);
|
||||
|
||||
@@ -8,9 +8,9 @@ package com.wentch.redkale.net.sncp;
|
||||
import com.wentch.redkale.convert.bson.*;
|
||||
import com.wentch.redkale.net.*;
|
||||
import static com.wentch.redkale.net.sncp.SncpRequest.HEADER_SIZE;
|
||||
import com.wentch.redkale.service.*;
|
||||
import com.wentch.redkale.util.*;
|
||||
import java.lang.reflect.*;
|
||||
import java.net.*;
|
||||
import java.nio.*;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.*;
|
||||
@@ -57,92 +57,137 @@ public final class SncpClient {
|
||||
}
|
||||
}
|
||||
|
||||
protected final String name;
|
||||
|
||||
protected final boolean remote;
|
||||
|
||||
private final Class serviceClass;
|
||||
|
||||
private final byte[] addrBytes;
|
||||
|
||||
private final int addrPort;
|
||||
|
||||
protected final long nameid;
|
||||
|
||||
protected final long serviceid;
|
||||
|
||||
protected final SncpAction[] actions;
|
||||
|
||||
public SncpClient(final String serviceName, final Class serviceClass) {
|
||||
public SncpClient(final String serviceName, final long serviceid0, boolean remote, final Class serviceClass, boolean onlySncpDyn, final InetSocketAddress clientAddress) {
|
||||
if (serviceName.length() > 10) throw new RuntimeException(serviceClass + " @Resource name(" + serviceName + ") too long , must less 11");
|
||||
this.nameid = Sncp.hash(MultiService.class.isAssignableFrom(serviceClass) ? serviceName : "");
|
||||
this.serviceid = Sncp.hash(serviceClass);
|
||||
this.remote = remote;
|
||||
this.serviceClass = serviceClass;
|
||||
//if (subLocalClass != null && !serviceClass.isAssignableFrom(subLocalClass)) throw new RuntimeException(subLocalClass + " is not " + serviceClass + " sub class ");
|
||||
this.name = serviceName;
|
||||
this.nameid = Sncp.hash(serviceName);
|
||||
this.serviceid = serviceid0 > 0 ? serviceid0 : Sncp.hash(serviceClass);
|
||||
final List<SncpAction> methodens = new ArrayList<>();
|
||||
//------------------------------------------------------------------------------
|
||||
Set<DLong> actionids = new HashSet<>();
|
||||
for (java.lang.reflect.Method method : serviceClass.getDeclaredMethods()) {
|
||||
for (java.lang.reflect.Method method : parseMethod(serviceClass, onlySncpDyn)) {
|
||||
SncpAction en = new SncpAction(method, Sncp.hash(method));
|
||||
methodens.add(en);
|
||||
}
|
||||
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();
|
||||
logger.fine("[" + Thread.currentThread().getName() + "] Load " + this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
String service = serviceClass.getName();
|
||||
if (remote) service = service.replace(Sncp.LOCALPREFIX, Sncp.REMOTEPREFIX);
|
||||
return this.getClass().getSimpleName() + "(service = " + service + ", serviceid = " + serviceid + ", name = " + name + ", nameid = " + nameid + ", actions.size = " + actions.length + ")";
|
||||
}
|
||||
|
||||
public static List<Method> parseMethod(final Class serviceClass, boolean onlySncpDyn) {
|
||||
final List<Method> list = new ArrayList<>();
|
||||
final List<Method> multis = new ArrayList<>();
|
||||
final Map<DLong, Method> actionids = new HashMap<>();
|
||||
|
||||
for (final java.lang.reflect.Method method : serviceClass.getMethods()) {
|
||||
if (method.isSynthetic()) continue;
|
||||
final int mod = method.getModifiers();
|
||||
if (!Modifier.isPublic(mod) && !Modifier.isProtected(mod)) continue;
|
||||
if (Modifier.isStatic(mod)) continue;
|
||||
if (Modifier.isFinal(mod)) continue;
|
||||
if (method.getName().equals("getClass") || method.getName().equals("toString")) continue;
|
||||
if (method.getName().equals("equals") || method.getName().equals("hashCode")) continue;
|
||||
if (method.getName().equals("notify") || method.getName().equals("notifyAll") || method.getName().equals("wait")) continue;
|
||||
if (method.getName().equals("init") || method.getName().equals("destroy")) continue;
|
||||
Method onMethod = getOnMethod(serviceClass, method);
|
||||
SncpAction en = new SncpAction(method, onMethod == null ? Sncp.hash(method) : Sncp.hash(onMethod));
|
||||
if (actionids.contains(en.actionid)) {
|
||||
throw new RuntimeException(serviceClass.getName() + " have one more same action(Method=" + method + ", actionid=" + en.actionid + ")");
|
||||
if (method.getName().equals("init") || method.getName().equals("destroy") || method.getName().equals("name")) continue;
|
||||
if (onlySncpDyn && method.getAnnotation(SncpDyn.class) == null) continue;
|
||||
DLong actionid = Sncp.hash(method);
|
||||
Method old = actionids.get(actionid);
|
||||
if (old != null) {
|
||||
if (old.getDeclaringClass().equals(method.getDeclaringClass())) throw new RuntimeException(serviceClass.getName() + " have one more same action(Method=" + method + ", actionid=" + actionid + ")");
|
||||
continue;
|
||||
}
|
||||
methodens.add(en);
|
||||
actionids.add(en.actionid);
|
||||
}
|
||||
this.actions = methodens.toArray(new SncpAction[methodens.size()]);
|
||||
logger.fine("Load " + this.getClass().getSimpleName() + "(serviceClass = " + serviceClass.getName() + ", serviceid =" + serviceid + ", serviceName =" + serviceName + ", actions = " + methodens + ")");
|
||||
}
|
||||
|
||||
public static Method getOnMethod(final Class serviceClass, Method method) {
|
||||
Method onMethod = null;
|
||||
if (method.getAnnotation(RemoteOn.class) != null) {
|
||||
char[] ms = method.getName().toCharArray();
|
||||
ms[0] = Character.toUpperCase(ms[0]);
|
||||
try {
|
||||
onMethod = serviceClass.getMethod("on" + new String(ms), method.getParameterTypes());
|
||||
if (onMethod.getReturnType() != method.getReturnType()) {
|
||||
throw new RuntimeException(serviceClass.getName() + " (Method=" + method + ") and (Method=" + onMethod + ") has not same returnType");
|
||||
}
|
||||
if (!Modifier.isFinal(onMethod.getModifiers())) {
|
||||
throw new RuntimeException(serviceClass.getName() + " (Method=" + method + ") is not final");
|
||||
}
|
||||
} catch (NoSuchMethodException e) {
|
||||
throw new RuntimeException(serviceClass.getName() + " not found (Public Method=" + "on" + new String(ms) + "but " + method + " has @" + RemoteOn.class.getSimpleName());
|
||||
actionids.put(actionid, method);
|
||||
if (method.getAnnotation(SncpDyn.class) != null) {
|
||||
multis.add(method);
|
||||
} else {
|
||||
list.add(method);
|
||||
}
|
||||
}
|
||||
return onMethod;
|
||||
list.addAll(multis);
|
||||
if (onlySncpDyn && list.size() > 1) {
|
||||
list.sort((m1, m2) -> m1.getAnnotation(SncpDyn.class).index() - m2.getAnnotation(SncpDyn.class).index());
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
public <T> T remote(final BsonConvert convert, Transport transport, final int index, final Object... params) {
|
||||
return convert.convertFrom(actions[index].resultTypes, send(convert, transport, index, params));
|
||||
return convert.convertFrom(actions[index].resultTypes, send(convert, transport, actions[index], params));
|
||||
}
|
||||
|
||||
private void fillHeader(ByteBuffer buffer, long seqid, DLong actionid, int frameCount, int frameIndex, int bodyLength) {
|
||||
//---------------------head----------------------------------
|
||||
buffer.putLong(seqid); //序列号
|
||||
buffer.putChar((char) HEADER_SIZE); //header长度
|
||||
buffer.putLong(this.serviceid);
|
||||
buffer.putLong(this.nameid);
|
||||
buffer.putLong(actionid.getFirst());
|
||||
buffer.putLong(actionid.getSecond());
|
||||
buffer.put((byte) frameCount); //数据的帧数, 最小值为1
|
||||
buffer.put((byte) frameIndex); //数据的帧数序号, 从frame.count-1开始, 0表示最后一帧
|
||||
buffer.putInt(0); //结果码, 请求方固定传0
|
||||
buffer.putInt(bodyLength); //body长度
|
||||
public <T> void remote(final BsonConvert convert, Transport[] transports, boolean run, final int index, final Object... params) {
|
||||
if (!run) return;
|
||||
this.remote(false, convert, transports, run, index, params);
|
||||
}
|
||||
|
||||
private byte[] send(final BsonConvert convert, Transport transport, final int index, Object... params) {
|
||||
public <T> void asyncRemote(final BsonConvert convert, Transport[] transports, boolean run, final int index, final Object... params) {
|
||||
if (!run) return;
|
||||
this.remote(true, convert, transports, run, index, params);
|
||||
}
|
||||
|
||||
private <T> void remote(final boolean async, final BsonConvert convert, final Transport[] transports, final boolean run, final int index, final Object... params) {
|
||||
if (!run) return;
|
||||
if (async) {
|
||||
submit(() -> {
|
||||
for (Transport transport : transports) {
|
||||
convert.convertFrom(actions[index].resultTypes, send(convert, transport, actions[index], params));
|
||||
}
|
||||
});
|
||||
} else {
|
||||
for (Transport transport : transports) {
|
||||
convert.convertFrom(actions[index].resultTypes, send(convert, transport, actions[index], params));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void submit(Runnable runner) {
|
||||
Thread thread = Thread.currentThread();
|
||||
if (false && thread instanceof WorkThread) { //有待验证为什么WorkThread 不工作
|
||||
((WorkThread) thread).submit(runner);
|
||||
return;
|
||||
}
|
||||
Thread t = new Thread(runner);
|
||||
t.setPriority(Thread.MAX_PRIORITY);
|
||||
t.start();
|
||||
}
|
||||
|
||||
private byte[] send(final BsonConvert convert, Transport transport, final SncpAction action, Object... params) {
|
||||
int bodyLength = 2;
|
||||
Type[] myparamtypes = actions[index].paramTypes;
|
||||
Type[] myparamtypes = action.paramTypes;
|
||||
byte[][] bytesarray = new byte[params.length][];
|
||||
for (int i = 0; i < bytesarray.length; i++) {
|
||||
bytesarray[i] = convert.convertTo(myparamtypes[i], params[i]);
|
||||
bodyLength += 4 + bytesarray[i].length;
|
||||
}
|
||||
final SncpAction action = actions[index];
|
||||
final long seqid = System.nanoTime();
|
||||
final DLong actionid = action.actionid;
|
||||
final ByteBuffer buffer = transport.pollBuffer();
|
||||
final AsyncConnection conn = transport.pollConnection();
|
||||
if (conn == null || !conn.isOpen()) return null;
|
||||
final ByteBuffer buffer = transport.pollBuffer();
|
||||
final int readto = conn.getReadTimeoutSecond();
|
||||
final int writeto = conn.getWriteTimeoutSecond();
|
||||
try {
|
||||
@@ -200,12 +245,14 @@ public final class SncpClient {
|
||||
long ractionid1 = buffer.getLong();
|
||||
long ractionid2 = buffer.getLong();
|
||||
if (!actionid.compare(ractionid1, ractionid2)) throw new RuntimeException("sncp send actionid = " + actionid + ", but receive actionid =(" + ractionid1 + "_" + ractionid2 + ")");
|
||||
buffer.getInt();
|
||||
buffer.getInt();
|
||||
final int frameCount = buffer.get();
|
||||
if (frameCount < 1) throw new RuntimeException("sncp send nameid = " + nameid + ", but frame.count =" + frameCount);
|
||||
int frameIndex = buffer.get();
|
||||
if (frameIndex < 0 || frameIndex >= frameCount) throw new RuntimeException("sncp send nameid = " + nameid + ", but frame.count =" + frameCount + " & frame.index =" + frameIndex);
|
||||
final int retcode = buffer.getInt();
|
||||
if (retcode != 0) throw new RuntimeException("remote service deal error (receive retcode =" + retcode + ")");
|
||||
if (retcode != 0) throw new RuntimeException("remote service deal error (retcode=" + retcode + ", retinfo=" + SncpResponse.getRetCodeInfo(retcode) + ")");
|
||||
final int bodylen = buffer.getInt();
|
||||
final byte[] body = new byte[bodylen];
|
||||
if (frameCount == 1) {
|
||||
@@ -251,4 +298,19 @@ public final class SncpClient {
|
||||
}
|
||||
}
|
||||
|
||||
private void fillHeader(ByteBuffer buffer, long seqid, DLong actionid, int frameCount, int frameIndex, int bodyLength) {
|
||||
//---------------------head----------------------------------
|
||||
buffer.putLong(seqid); //序列号
|
||||
buffer.putChar((char) HEADER_SIZE); //header长度
|
||||
buffer.putLong(this.serviceid);
|
||||
buffer.putLong(this.nameid);
|
||||
buffer.putLong(actionid.getFirst());
|
||||
buffer.putLong(actionid.getSecond());
|
||||
buffer.put(addrBytes);
|
||||
buffer.putInt(this.addrPort);
|
||||
buffer.put((byte) frameCount); //数据的帧数, 最小值为1
|
||||
buffer.put((byte) frameIndex); //数据的帧数序号, 从frame.count-1开始, 0表示最后一帧
|
||||
buffer.putInt(0); //结果码, 请求方固定传0
|
||||
buffer.putInt(bodyLength); //body长度
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,8 +53,8 @@ public final class SncpContext extends Context {
|
||||
protected final BsonFactory bsonFactory;
|
||||
|
||||
public SncpContext(long serverStartTime, Logger logger, ExecutorService executor, ObjectPool<ByteBuffer> bufferPool,
|
||||
ObjectPool<Response> responsePool, int maxbody, Charset charset, InetSocketAddress address,
|
||||
PrepareServlet prepare, WatchFactory watch, int readTimeoutSecond, int writeTimeoutSecond) {
|
||||
ObjectPool<Response> responsePool, int maxbody, Charset charset, InetSocketAddress address, PrepareServlet prepare,
|
||||
WatchFactory watch, int readTimeoutSecond, int writeTimeoutSecond) {
|
||||
super(serverStartTime, logger, executor, bufferPool, responsePool, maxbody, charset,
|
||||
address, prepare, watch, readTimeoutSecond, writeTimeoutSecond);
|
||||
this.bsonFactory = BsonFactory.root();
|
||||
|
||||
24
src/com/wentch/redkale/net/sncp/SncpDyn.java
Normal file
24
src/com/wentch/redkale/net/sncp/SncpDyn.java
Normal file
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* 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 com.wentch.redkale.net.sncp;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
import static java.lang.annotation.ElementType.*;
|
||||
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||
|
||||
/**
|
||||
* 修饰由SNCP协议动态生成的class、和method
|
||||
*
|
||||
* @author zhangjx
|
||||
*/
|
||||
@Inherited
|
||||
@Documented
|
||||
@Target({METHOD, TYPE})
|
||||
@Retention(RUNTIME)
|
||||
public @interface SncpDyn {
|
||||
|
||||
int index() default 0; //排列顺序, 一般用于Method
|
||||
}
|
||||
@@ -6,7 +6,6 @@
|
||||
package com.wentch.redkale.net.sncp;
|
||||
|
||||
import com.wentch.redkale.convert.bson.*;
|
||||
import static com.wentch.redkale.net.sncp.SncpClient.getOnMethod;
|
||||
import com.wentch.redkale.service.*;
|
||||
import com.wentch.redkale.util.*;
|
||||
import java.io.*;
|
||||
@@ -22,10 +21,16 @@ import jdk.internal.org.objectweb.asm.Type;
|
||||
*
|
||||
* @author zhangjx
|
||||
*/
|
||||
public class SncpDynServlet extends SncpServlet {
|
||||
public final class SncpDynServlet extends SncpServlet {
|
||||
|
||||
private final Logger logger = Logger.getLogger(SncpDynServlet.class.getSimpleName());
|
||||
|
||||
private final boolean finest = logger.isLoggable(Level.FINEST);
|
||||
|
||||
private final Class<? extends Service> type;
|
||||
|
||||
private final String serviceName;
|
||||
|
||||
private final long nameid;
|
||||
|
||||
private final long serviceid;
|
||||
@@ -34,36 +39,33 @@ public class SncpDynServlet extends SncpServlet {
|
||||
|
||||
public SncpDynServlet(final BsonConvert convert, final String serviceName, final Service service, final AnyValue conf) {
|
||||
this.conf = conf;
|
||||
final Class serviceClass = service.getClass();
|
||||
this.serviceName = serviceName;
|
||||
this.type = (Class<? extends Service>) service.getClass().getSuperclass();
|
||||
this.nameid = Sncp.hash(serviceName);
|
||||
this.serviceid = Sncp.hash(serviceClass);
|
||||
this.serviceid = Sncp.hash(type);
|
||||
Set<DLong> actionids = new HashSet<>();
|
||||
for (java.lang.reflect.Method method : serviceClass.getMethods()) {
|
||||
for (java.lang.reflect.Method method : service.getClass().getMethods()) {
|
||||
if (method.isSynthetic()) continue;
|
||||
if (Modifier.isStatic(method.getModifiers())) continue;
|
||||
if (Modifier.isFinal(method.getModifiers())) continue;
|
||||
if (method.getName().equals("getClass") || method.getName().equals("toString")) continue;
|
||||
if (method.getName().equals("equals") || method.getName().equals("hashCode")) continue;
|
||||
if (method.getName().equals("notify") || method.getName().equals("notifyAll") || method.getName().equals("wait")) continue;
|
||||
if (method.getName().equals("init") || method.getName().equals("destroy")) continue;
|
||||
Method onMethod = getOnMethod(serviceClass, method);
|
||||
if (onMethod != null) method = onMethod;
|
||||
if (method.getName().equals("init") || method.getName().equals("destroy") || method.getName().equals("name")) continue;
|
||||
final DLong actionid = Sncp.hash(method);
|
||||
SncpServletAction action = SncpServletAction.create(service, actionid, method);
|
||||
action.convert = convert;
|
||||
if (actionids.contains(actionid)) {
|
||||
throw new RuntimeException(serviceClass.getName()
|
||||
+ " have action(Method=" + method + ", actionid=" + actionid + ") same to (" + actions.get(actionid).method + ")");
|
||||
throw new RuntimeException(type.getName() + " have action(Method=" + method + ", actionid=" + actionid + ") same to (" + actions.get(actionid).method + ")");
|
||||
}
|
||||
actions.put(actionid, action);
|
||||
actionids.add(actionid);
|
||||
}
|
||||
if (!logger.isLoggable(Level.FINE)) return;
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("{");
|
||||
actions.forEach((x, y) -> sb.append('{').append(x).append(',').append(y.method.getName()).append("},"));
|
||||
sb.append("}");
|
||||
logger.fine(this.getClass().getSimpleName() + "(serviceClass = " + serviceClass.getName() + ", serviceid =" + serviceid + ", serviceName =" + serviceName + ", actions = " + sb + ") loaded");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.getClass().getSimpleName() + "(type=" + type.getName() + ", serviceid=" + serviceid + ", name=" + serviceName + ", actions.size=" + actions.size() + ")";
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -79,6 +81,7 @@ public class SncpDynServlet extends SncpServlet {
|
||||
@Override
|
||||
public void execute(SncpRequest request, SncpResponse response) throws IOException {
|
||||
SncpServletAction action = actions.get(request.getActionid());
|
||||
//if (finest) logger.log(Level.FINEST, "sncpdyn.execute: " + request + ", " + (action == null ? "null" : action.method));
|
||||
if (action == null) {
|
||||
response.finish(SncpResponse.RETCODE_ILLACTIONID, null); //无效actionid
|
||||
} else {
|
||||
@@ -103,25 +106,25 @@ public class SncpDynServlet extends SncpServlet {
|
||||
public abstract byte[] action(byte[][] bytes) throws Throwable;
|
||||
|
||||
/*
|
||||
*
|
||||
*
|
||||
* public class TestService implements Service {
|
||||
* public boolean change(TestBean bean, String name, int id) {
|
||||
*
|
||||
* }
|
||||
* public boolean change(TestBean bean, String name, int id) {
|
||||
*
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* public class DynActionTestService_change extends SncpServletAction {
|
||||
*
|
||||
* public TestService service;
|
||||
* public TestService service;
|
||||
*
|
||||
* @Override
|
||||
* public byte[] action(byte[][] bytes) throws Throwable {
|
||||
* TestBean arg1 = convert.convertFrom(paramTypes[1], bytes[1]);
|
||||
* String arg2 = convert.convertFrom(paramTypes[2], bytes[2]);
|
||||
* int arg3 = convert.convertFrom(paramTypes[3], bytes[3]);
|
||||
* Object rs = service.change(arg1, arg2, arg3);
|
||||
* return convert.convertTo(paramTypes[0], rs);
|
||||
* }
|
||||
* @Override
|
||||
* public byte[] action(byte[][] bytes) throws Throwable {
|
||||
* TestBean arg1 = convert.convertFrom(paramTypes[1], bytes[1]);
|
||||
* String arg2 = convert.convertFrom(paramTypes[2], bytes[2]);
|
||||
* int arg3 = convert.convertFrom(paramTypes[3], bytes[3]);
|
||||
* Object rs = service.change(arg1, arg2, arg3);
|
||||
* return convert.convertTo(paramTypes[0], rs);
|
||||
* }
|
||||
* }
|
||||
*/
|
||||
/**
|
||||
|
||||
@@ -26,17 +26,27 @@ public class SncpPrepareServlet extends PrepareServlet<SncpRequest, SncpResponse
|
||||
|
||||
public void addSncpServlet(SncpServlet servlet) {
|
||||
if (servlet.getNameid() == 0) {
|
||||
singlemaps.put(servlet.getServiceid(), servlet);
|
||||
} else {
|
||||
Map<Long, SncpServlet> m = maps.get(servlet.getServiceid());
|
||||
if (m == null) {
|
||||
m = new HashMap<>();
|
||||
maps.put(servlet.getServiceid(), m);
|
||||
synchronized (singlemaps) {
|
||||
singlemaps.put(servlet.getServiceid(), servlet);
|
||||
}
|
||||
} else {
|
||||
synchronized (maps) {
|
||||
Map<Long, SncpServlet> m = maps.get(servlet.getServiceid());
|
||||
if (m == null) {
|
||||
m = new HashMap<>();
|
||||
maps.put(servlet.getServiceid(), m);
|
||||
}
|
||||
m.put(servlet.getNameid(), servlet);
|
||||
}
|
||||
m.put(servlet.getNameid(), servlet);
|
||||
}
|
||||
}
|
||||
|
||||
public List<SncpServlet> getSncpServlets() {
|
||||
ArrayList<SncpServlet> list = new ArrayList<>(singlemaps.values());
|
||||
maps.values().forEach(x -> list.addAll(x.values()));
|
||||
return list;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(Context context, AnyValue config) {
|
||||
Collection<Map<Long, SncpServlet>> values = this.maps.values();
|
||||
@@ -62,6 +72,10 @@ public class SncpPrepareServlet extends PrepareServlet<SncpRequest, SncpResponse
|
||||
SncpServlet servlet;
|
||||
if (request.getNameid() == 0) {
|
||||
servlet = singlemaps.get(request.getServiceid());
|
||||
if (servlet == null) {
|
||||
response.finish(SncpResponse.RETCODE_ILLSERVICEID, null); //无效serviceid
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
Map<Long, SncpServlet> m = maps.get(request.getServiceid());
|
||||
if (m == null) {
|
||||
|
||||
@@ -9,6 +9,7 @@ import com.wentch.redkale.convert.bson.*;
|
||||
import com.wentch.redkale.net.*;
|
||||
import com.wentch.redkale.net.sncp.SncpContext.RequestEntry;
|
||||
import com.wentch.redkale.util.*;
|
||||
import java.net.*;
|
||||
import java.nio.*;
|
||||
|
||||
/**
|
||||
@@ -16,36 +17,40 @@ import java.nio.*;
|
||||
* @author zhangjx
|
||||
*/
|
||||
public final class SncpRequest extends Request {
|
||||
|
||||
public static final int HEADER_SIZE = 52;
|
||||
|
||||
|
||||
public static final int HEADER_SIZE = 60;
|
||||
|
||||
protected final BsonConvert convert;
|
||||
|
||||
|
||||
private long seqid;
|
||||
|
||||
|
||||
private int framecount;
|
||||
|
||||
|
||||
private int frameindex;
|
||||
|
||||
|
||||
private long nameid;
|
||||
|
||||
|
||||
private long serviceid;
|
||||
|
||||
|
||||
private DLong actionid;
|
||||
|
||||
|
||||
private int bodylength;
|
||||
|
||||
|
||||
private byte[][] paramBytes;
|
||||
|
||||
|
||||
private boolean ping;
|
||||
|
||||
|
||||
private byte[] body;
|
||||
|
||||
|
||||
private byte[] bufferbytes = new byte[4];
|
||||
|
||||
private InetSocketAddress remoteAddress;
|
||||
|
||||
protected SncpRequest(SncpContext context, BsonFactory factory) {
|
||||
super(context);
|
||||
this.convert = factory.getConvert();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected int readHeader(ByteBuffer buffer) {
|
||||
if (buffer.remaining() < HEADER_SIZE) {
|
||||
@@ -61,6 +66,11 @@ public final class SncpRequest extends Request {
|
||||
this.serviceid = buffer.getLong();
|
||||
this.nameid = buffer.getLong();
|
||||
this.actionid = new DLong(buffer.getLong(), buffer.getLong());
|
||||
buffer.get(bufferbytes);
|
||||
int port = buffer.getInt();
|
||||
if (bufferbytes[0] > 0 && port > 0) {
|
||||
this.remoteAddress = new InetSocketAddress((0xff & bufferbytes[0]) + "." + (0xff & bufferbytes[1]) + "." + (0xff & bufferbytes[2]) + "." + (0xff & bufferbytes[3]), port);
|
||||
}
|
||||
this.framecount = buffer.get();
|
||||
this.frameindex = buffer.get();
|
||||
if (buffer.getInt() != 0) {
|
||||
@@ -85,21 +95,21 @@ public final class SncpRequest extends Request {
|
||||
RequestEntry entry = scontext.getRequestEntity(this.seqid);
|
||||
if (entry == null) entry = scontext.addRequestEntity(this.seqid, new byte[this.bodylength]);
|
||||
entry.add(buffer, (this.framecount - this.frameindex - 1) * (buffer.capacity() - HEADER_SIZE));
|
||||
|
||||
|
||||
if (entry.isCompleted()) { //数据读取完毕
|
||||
this.body = entry.body;
|
||||
scontext.removeRequestEntity(this.seqid);
|
||||
return 0;
|
||||
} else {
|
||||
scontext.expireRequestEntry(10 * 1000); //10秒过期
|
||||
}
|
||||
}
|
||||
return Integer.MIN_VALUE; //多帧数据返回 Integer.MIN_VALUE
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void readBody(ByteBuffer buffer) {
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void prepare() {
|
||||
if (this.body == null) return;
|
||||
@@ -116,14 +126,14 @@ public final class SncpRequest extends Request {
|
||||
}
|
||||
this.paramBytes = bbytes;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return SncpRequest.class.getSimpleName() + "{seqid=" + this.seqid
|
||||
+ ",serviceid=" + this.serviceid + ",actionid=" + this.actionid
|
||||
+ ",framecount=" + this.framecount + ",frameindex=" + this.frameindex + ",bodylength=" + this.bodylength + "}";
|
||||
+ ",framecount=" + this.framecount + ",frameindex=" + this.frameindex + ",bodylength=" + this.bodylength + ",remoteAddress=" + remoteAddress + "}";
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void recycle() {
|
||||
this.seqid = 0;
|
||||
@@ -135,31 +145,37 @@ public final class SncpRequest extends Request {
|
||||
this.body = null;
|
||||
this.paramBytes = null;
|
||||
this.ping = false;
|
||||
this.remoteAddress = null;
|
||||
this.bufferbytes[0] = 0;
|
||||
super.recycle();
|
||||
}
|
||||
|
||||
|
||||
protected boolean isPing() {
|
||||
return ping;
|
||||
}
|
||||
|
||||
|
||||
public byte[][] getParamBytes() {
|
||||
return paramBytes;
|
||||
}
|
||||
|
||||
|
||||
public long getSeqid() {
|
||||
return seqid;
|
||||
}
|
||||
|
||||
|
||||
public long getServiceid() {
|
||||
return serviceid;
|
||||
}
|
||||
|
||||
|
||||
public long getNameid() {
|
||||
return nameid;
|
||||
}
|
||||
|
||||
|
||||
public DLong getActionid() {
|
||||
return actionid;
|
||||
}
|
||||
|
||||
|
||||
public InetSocketAddress getRemoteAddress() {
|
||||
return remoteAddress;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -26,11 +26,25 @@ public final class SncpResponse extends Response<SncpRequest> {
|
||||
public static final int RETCODE_THROWEXCEPTION = 10011; //内部异常
|
||||
|
||||
public static ObjectPool<Response> createPool(AtomicLong creatCounter, AtomicLong cycleCounter, int max, Creator<Response> creator) {
|
||||
return new ObjectPool<>(creatCounter, cycleCounter, max, creator, (x)-> ((SncpResponse) x).prepare(), (x) -> ((SncpResponse) x).recycle());
|
||||
return new ObjectPool<>(creatCounter, cycleCounter, max, creator, (x) -> ((SncpResponse) x).prepare(), (x) -> ((SncpResponse) x).recycle());
|
||||
}
|
||||
|
||||
private final byte[] addrBytes;
|
||||
|
||||
private final int addrPort;
|
||||
|
||||
public static String getRetCodeInfo(int retcode) {
|
||||
if (retcode == RETCODE_ILLSERVICEID) return "serviceid is invalid";
|
||||
if (retcode == RETCODE_ILLNAMEID) return "nameid is invalid";
|
||||
if (retcode == RETCODE_ILLACTIONID) return "actionid is invalid";
|
||||
if (retcode == RETCODE_THROWEXCEPTION) return "Inner exception";
|
||||
return null;
|
||||
}
|
||||
|
||||
protected SncpResponse(Context context, SncpRequest request) {
|
||||
super(context, request);
|
||||
this.addrBytes = context.getServerAddress().getAddress().getAddress();
|
||||
this.addrPort = context.getServerAddress().getPort();
|
||||
}
|
||||
|
||||
public void finish(final int retcode, final byte[] bytes) {
|
||||
@@ -56,7 +70,7 @@ public final class SncpResponse extends Response<SncpRequest> {
|
||||
pos += len;
|
||||
buffer.flip();
|
||||
}
|
||||
finish(buffers);
|
||||
finish(buffers);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,6 +83,8 @@ public final class SncpResponse extends Response<SncpRequest> {
|
||||
DLong actionid = request.getActionid();
|
||||
buffer.putLong(actionid.getFirst());
|
||||
buffer.putLong(actionid.getSecond());
|
||||
buffer.put(addrBytes);
|
||||
buffer.putInt(this.addrPort);
|
||||
buffer.put((byte) frameCount); // frame count
|
||||
buffer.put((byte) frameIndex); //frame index
|
||||
buffer.putInt(retcode);
|
||||
|
||||
@@ -9,6 +9,7 @@ import com.wentch.redkale.convert.bson.*;
|
||||
import com.wentch.redkale.net.*;
|
||||
import com.wentch.redkale.util.*;
|
||||
import com.wentch.redkale.watch.*;
|
||||
import java.net.*;
|
||||
import java.nio.*;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.atomic.*;
|
||||
@@ -20,18 +21,45 @@ import java.util.concurrent.atomic.*;
|
||||
*/
|
||||
public final class SncpServer extends Server {
|
||||
|
||||
private final List<ServiceEntry> services = new ArrayList<>();
|
||||
protected InetSocketAddress nodeAddress;
|
||||
|
||||
public SncpServer(String protocol) {
|
||||
this(System.currentTimeMillis(), protocol, null);
|
||||
this(System.currentTimeMillis(), protocol, null, null);
|
||||
}
|
||||
|
||||
public SncpServer(long serverStartTime, String protocol, final WatchFactory watch) {
|
||||
super(serverStartTime, protocol, watch);
|
||||
public SncpServer(long serverStartTime, String protocol, InetSocketAddress nodeAddress, final WatchFactory watch) {
|
||||
super(serverStartTime, protocol, new SncpPrepareServlet(), watch);
|
||||
this.nodeAddress = nodeAddress;
|
||||
}
|
||||
|
||||
public void addService(ServiceEntry entry) {
|
||||
this.services.add(entry);
|
||||
@Override
|
||||
public void init(AnyValue config) throws Exception {
|
||||
super.init(config);
|
||||
if (this.nodeAddress == null) {
|
||||
if ("0.0.0.0".equals(this.address.getHostString())) {
|
||||
this.nodeAddress = new InetSocketAddress(Utility.localInetAddress().getHostAddress(), this.address.getPort());
|
||||
} else {
|
||||
this.nodeAddress = this.address;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void addService(ServiceWrapper entry) {
|
||||
((SncpPrepareServlet) this.prepare).addSncpServlet(new SncpDynServlet(BsonFactory.root().getConvert(), entry.getName(), entry.getService(), entry.getConf()));
|
||||
}
|
||||
|
||||
public List<SncpServlet> getSncpServlets() {
|
||||
return ((SncpPrepareServlet) this.prepare).getSncpServlets();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* 对外的IP地址
|
||||
*
|
||||
@return
|
||||
*/
|
||||
public InetSocketAddress getNodeAddress() {
|
||||
return nodeAddress;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -47,15 +75,11 @@ public final class SncpServer extends Server {
|
||||
e.clear();
|
||||
return true;
|
||||
});
|
||||
SncpPrepareServlet prepare = new SncpPrepareServlet();
|
||||
final BsonConvert convert = BsonFactory.root().getConvert();
|
||||
this.services.stream().forEach(x -> x.getNames().forEach(y -> prepare.addSncpServlet(new SncpDynServlet(convert, y, x.getService(), x.getServiceConf()))));
|
||||
this.services.clear();
|
||||
AtomicLong createResponseCounter = watch == null ? new AtomicLong() : watch.createWatchNumber("SNCP_" + port + ".Response.creatCounter");
|
||||
AtomicLong cycleResponseCounter = watch == null ? new AtomicLong() : watch.createWatchNumber("SNCP_" + port + ".Response.cycleCounter");
|
||||
ObjectPool<Response> responsePool = SncpResponse.createPool(createResponseCounter, cycleResponseCounter, this.responsePoolSize, null);
|
||||
SncpContext sncpcontext = new SncpContext(this.serverStartTime, this.logger, executor, bufferPool, responsePool,
|
||||
this.maxbody, this.charset, this.address, prepare, this.watch, this.readTimeoutSecond, this.writeTimeoutSecond);
|
||||
this.maxbody, this.charset, this.address, this.prepare, this.watch, this.readTimeoutSecond, this.writeTimeoutSecond);
|
||||
responsePool.setCreator((Object... params) -> new SncpResponse(sncpcontext, new SncpRequest(sncpcontext, sncpcontext.bsonFactory)));
|
||||
return sncpcontext;
|
||||
}
|
||||
|
||||
@@ -8,10 +8,6 @@ package com.wentch.redkale.service;
|
||||
import com.wentch.redkale.source.*;
|
||||
import com.wentch.redkale.util.*;
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
import java.util.AbstractMap.SimpleEntry;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.logging.*;
|
||||
import javax.annotation.*;
|
||||
|
||||
/**
|
||||
@@ -21,279 +17,25 @@ import javax.annotation.*;
|
||||
@AutoLoad(false)
|
||||
public class DataCacheListenerService implements DataCacheListener, Service {
|
||||
|
||||
protected final Logger logger = Logger.getLogger(this.getClass().getSimpleName());
|
||||
|
||||
private static final String format = "%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS.%tL";
|
||||
|
||||
private final ConcurrentHashMap<String, BlockingQueue<Map.Entry<Class, Object[]>>> insertQueues = new ConcurrentHashMap<>();
|
||||
|
||||
private final ConcurrentHashMap<String, BlockingQueue<Map.Entry<Class, Object[]>>> updateQueues = new ConcurrentHashMap<>();
|
||||
|
||||
private final ConcurrentHashMap<String, BlockingQueue<Map.Entry<Class, Serializable[]>>> deleteQueues = new ConcurrentHashMap<>();
|
||||
|
||||
private final boolean finest = logger.isLoggable(Level.FINEST);
|
||||
|
||||
@Resource(name = "APP_NODE")
|
||||
private String localNodeName = "";
|
||||
|
||||
@Resource(name = "APP_GROUP")
|
||||
private String localGroupName = "";
|
||||
|
||||
@Resource(name = "APP_GROUP")
|
||||
private Map<String, Set<String>> groups;
|
||||
|
||||
@Resource(name = ".*")
|
||||
private HashMap<String, DataSource> sourcesmap;
|
||||
|
||||
@Resource(name = ".*")
|
||||
private HashMap<String, DataCacheListenerService> nodesmap;
|
||||
@Resource(name = "$")
|
||||
private DataSource source;
|
||||
|
||||
@Override
|
||||
public void init(AnyValue config) {
|
||||
if (finest) {
|
||||
logger.finest(this.getClass().getSimpleName() + "-localgroup: " + localGroupName);
|
||||
logger.finest(this.getClass().getSimpleName() + "-groups: " + groups);
|
||||
logger.finest(this.getClass().getSimpleName() + "-sources: " + sourcesmap);
|
||||
}
|
||||
@MultiRun(async = true)
|
||||
public <T> void insertCache(Class<T> clazz, T... entitys) {
|
||||
((DataDefaultSource) source).insertCache(clazz, entitys);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> void insert(String sourceName, Class<T> clazz, T... entitys) {
|
||||
if (finest) logger.finest("(source:" + sourceName + ") insert " + clazz + " --> " + Arrays.toString(entitys));
|
||||
BlockingQueue<Map.Entry<Class, Object[]>> queue = this.insertQueues.get(sourceName);
|
||||
if (queue == null) {
|
||||
synchronized (this.insertQueues) {
|
||||
queue = this.insertQueues.get(sourceName);
|
||||
if (queue == null) {
|
||||
queue = new ArrayBlockingQueue<>(10240);
|
||||
this.insertQueues.put(sourceName, queue);
|
||||
final BlockingQueue<Map.Entry<Class, Object[]>> tq = queue;
|
||||
new Thread() {
|
||||
{
|
||||
setName(DataCacheListener.class.getSimpleName() + "-" + (sourceName.isEmpty() ? "<>" : sourceName) + "-Insert-Thread");
|
||||
setDaemon(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
while (true) {
|
||||
try {
|
||||
Map.Entry<Class, Object[]> entry = tq.take();
|
||||
sendInsert(localGroupName, false, sourceName, entry.getKey(), entry.getValue());
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.SEVERE, this.getName() + " sendInsert occur error", e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}.start();
|
||||
}
|
||||
}
|
||||
}
|
||||
try {
|
||||
queue.put(new SimpleEntry<>(clazz, entitys));
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.WARNING, this.getClass().getSimpleName() + " put insert queue error " + Arrays.toString(entitys), e);
|
||||
}
|
||||
}
|
||||
|
||||
@RemoteOn
|
||||
public <T> void sendInsert(String group, boolean ignoreRemote, String sourceName, Class<T> clazz, T... entitys) {
|
||||
if (nodesmap == null || groups == null) return;
|
||||
if (ignoreRemote && finest) logger.finest(DataSource.class.getSimpleName() + "(" + group + "--" + this.localNodeName + "," + sourceName + ") onGroupSendInsert " + Arrays.toString(entitys));
|
||||
for (Map.Entry<String, Set<String>> en : groups.entrySet()) {
|
||||
if (group != null && group.equals(en.getKey())) { //同机房
|
||||
for (String onode : en.getValue()) {
|
||||
if (onode.equals(localNodeName)) continue;
|
||||
DataCacheListenerService service = nodesmap.get(onode);
|
||||
if (service != null) {
|
||||
try {
|
||||
service.sendInsert(group, false, sourceName, clazz, entitys);
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.FINE, this.getClass().getSimpleName() + " send insert error (" + group + "--" + onode + ", " + sourceName + ", " + clazz + ", " + Arrays.toString(entitys) + ")", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ignoreRemote) break;
|
||||
} else if (!ignoreRemote) {
|
||||
for (String onode : en.getValue()) {
|
||||
DataCacheListenerService service = nodesmap.get(onode);
|
||||
if (service != null) {
|
||||
try {
|
||||
service.sendInsert(group, false, sourceName, clazz, entitys);
|
||||
break; //有一个成功就退出
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.FINE, this.getClass().getSimpleName() + " send insert error (" + group + "--" + onode + ", " + sourceName + ", " + clazz + ", " + Arrays.toString(entitys) + ")", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public final <T> void onSendInsert(String group, boolean ignoreRemote, String sourceName, Class<T> clazz, T... entitys) {
|
||||
if (finest) logger.finest(DataSource.class.getSimpleName() + "(" + this.localNodeName + "," + sourceName + ") onSendInsert " + Arrays.toString(entitys));
|
||||
((DataDefaultSource) sourcesmap.get(sourceName)).insertCache(entitys);
|
||||
if (!this.localGroupName.equals(group)) sendInsert(this.localGroupName, true, sourceName, clazz, entitys); //不是同一机房来的资源需要同步到其他同机房的节点上
|
||||
@MultiRun(async = true)
|
||||
public <T> void updateCache(Class<T> clazz, T... entitys) {
|
||||
((DataDefaultSource) source).updateCache(clazz, entitys);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> void update(String sourceName, Class<T> clazz, T... values) {
|
||||
if (finest) logger.finest("(source:" + sourceName + ") update " + clazz + " --> " + Arrays.toString(values));
|
||||
BlockingQueue<Map.Entry<Class, Object[]>> queue = this.updateQueues.get(sourceName);
|
||||
if (queue == null) {
|
||||
synchronized (this.updateQueues) {
|
||||
queue = this.updateQueues.get(sourceName);
|
||||
if (queue == null) {
|
||||
queue = new ArrayBlockingQueue<>(10240);
|
||||
this.updateQueues.put(sourceName, queue);
|
||||
final BlockingQueue<Map.Entry<Class, Object[]>> tq = queue;
|
||||
new Thread() {
|
||||
{
|
||||
setName(DataCacheListener.class.getSimpleName() + "-" + (sourceName.isEmpty() ? "<>" : sourceName) + "-Update-Thread");
|
||||
setDaemon(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
while (true) {
|
||||
try {
|
||||
Map.Entry<Class, Object[]> entry = tq.take();
|
||||
sendUpdate(localGroupName, false, sourceName, entry.getKey(), entry.getValue());
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.SEVERE, this.getName() + " sendUpdate occur error", e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}.start();
|
||||
}
|
||||
}
|
||||
}
|
||||
try {
|
||||
queue.put(new SimpleEntry<>(clazz, values));
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.WARNING, this.getClass().getSimpleName() + " put update queue error " + clazz + "," + Arrays.toString(values), e);
|
||||
}
|
||||
@MultiRun(async = true)
|
||||
public <T> void deleteCache(Class<T> clazz, Serializable... ids) {
|
||||
((DataDefaultSource) source).deleteCache(clazz, ids);
|
||||
}
|
||||
|
||||
@RemoteOn
|
||||
public <T> void sendUpdate(String group, boolean ignoreRemote, String sourceName, Class<T> clazz, T... entitys) {
|
||||
if (nodesmap == null || groups == null) return;
|
||||
if (ignoreRemote && finest) logger.finest(DataSource.class.getSimpleName() + "(" + group + "--" + this.localNodeName + "," + sourceName + ") onGroupSendUpdate " + Arrays.toString(entitys));
|
||||
for (Map.Entry<String, Set<String>> en : groups.entrySet()) {
|
||||
if (group != null && group.equals(en.getKey())) { //同机房
|
||||
for (String onode : en.getValue()) {
|
||||
if (onode.equals(localNodeName)) continue;
|
||||
DataCacheListenerService service = nodesmap.get(onode);
|
||||
if (service != null) {
|
||||
try {
|
||||
service.sendUpdate(group, false, sourceName, clazz, entitys);
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.FINE, this.getClass().getSimpleName() + " send update error (" + group + "--" + onode + ", " + sourceName + ", " + clazz + ", " + Arrays.toString(entitys) + ")", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ignoreRemote) break;
|
||||
} else if (!ignoreRemote) {
|
||||
for (String onode : en.getValue()) {
|
||||
DataCacheListenerService service = nodesmap.get(onode);
|
||||
if (service != null) {
|
||||
try {
|
||||
service.sendUpdate(group, false, sourceName, clazz, entitys);
|
||||
break; //有一个成功就退出
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.FINE, this.getClass().getSimpleName() + " send update error (" + group + "--" + onode + ", " + sourceName + ", " + clazz + ", " + Arrays.toString(entitys) + ")", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public final <T> void onSendUpdate(String group, boolean ignoreRemote, String sourceName, Class<T> clazz, T... entitys) {
|
||||
if (finest) logger.finest(DataSource.class.getSimpleName() + "(" + group + "--" + this.localNodeName + "," + sourceName + ") onSendUpdate " + Arrays.toString(entitys));
|
||||
((DataDefaultSource) sourcesmap.get(sourceName)).updateCache(clazz, entitys);
|
||||
if (!this.localGroupName.equals(group)) sendUpdate(this.localGroupName, true, sourceName, clazz, entitys); //不是同一机房来的资源需要同步到其他同机房的节点上
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> void delete(String sourceName, Class<T> clazz, Serializable... ids) {
|
||||
if (finest) logger.finest("(source:" + sourceName + ") delete " + clazz + " --> " + Arrays.toString(ids));
|
||||
BlockingQueue<Map.Entry<Class, Serializable[]>> queue = this.deleteQueues.get(sourceName);
|
||||
if (queue == null) {
|
||||
synchronized (this.deleteQueues) {
|
||||
queue = this.deleteQueues.get(sourceName);
|
||||
if (queue == null) {
|
||||
queue = new ArrayBlockingQueue<>(10240);
|
||||
this.deleteQueues.put(sourceName, queue);
|
||||
final BlockingQueue<Map.Entry<Class, Serializable[]>> tq = queue;
|
||||
new Thread() {
|
||||
{
|
||||
setName(DataCacheListener.class.getSimpleName() + "-" + (sourceName.isEmpty() ? "<>" : sourceName) + "-Delete-Thread");
|
||||
setDaemon(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
while (true) {
|
||||
try {
|
||||
Map.Entry<Class, Serializable[]> entry = tq.take();
|
||||
sendDelete(localGroupName, false, sourceName, entry.getKey(), entry.getValue());
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.SEVERE, this.getName() + " sendDelete occur error", e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}.start();
|
||||
}
|
||||
}
|
||||
}
|
||||
try {
|
||||
queue.put(new SimpleEntry<>(clazz, ids));
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.WARNING, this.getClass().getSimpleName() + " put delete queue error " + clazz + "," + Arrays.toString(ids), e);
|
||||
}
|
||||
}
|
||||
|
||||
@RemoteOn
|
||||
public <T> void sendDelete(String group, boolean ignoreRemote, String sourceName, Class<T> clazz, Serializable... ids) {
|
||||
if (nodesmap == null || groups == null) return;
|
||||
if (ignoreRemote && finest) logger.finest(DataSource.class.getSimpleName() + "(" + group + "--" + this.localNodeName + "," + sourceName + ") onGroupSendDelete " + Arrays.toString(ids));
|
||||
for (Map.Entry<String, Set<String>> en : groups.entrySet()) {
|
||||
if (group != null && group.equals(en.getKey())) { //同机房
|
||||
for (String onode : en.getValue()) {
|
||||
if (onode.equals(localNodeName)) continue;
|
||||
DataCacheListenerService service = nodesmap.get(onode);
|
||||
if (service != null) {
|
||||
try {
|
||||
service.sendDelete(group, false, sourceName, clazz, ids);
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.FINE, this.getClass().getSimpleName() + " send delete error (" + group + "--" + onode + ", " + sourceName + ", " + clazz + ", " + Arrays.toString(ids) + ")", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ignoreRemote) break;
|
||||
} else if (!ignoreRemote) {
|
||||
for (String onode : en.getValue()) {
|
||||
DataCacheListenerService service = nodesmap.get(onode);
|
||||
if (service != null) {
|
||||
try {
|
||||
service.sendDelete(group, false, sourceName, clazz, ids);
|
||||
break; //有一个成功就退出
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.FINE, this.getClass().getSimpleName() + " send delete error (" + group + "--" + onode + ", " + sourceName + ", " + clazz + ", " + Arrays.toString(ids) + ")", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public final <T> void onSendDelete(String group, boolean ignoreRemote, String sourceName, Class<T> clazz, Serializable... ids) {
|
||||
if (finest) logger.finest(DataSource.class.getSimpleName() + "(" + group + "--" + this.localNodeName + "," + sourceName + ") onSendDelete " + clazz.getName() + " " + Arrays.toString(ids));
|
||||
((DataDefaultSource) sourcesmap.get(sourceName)).deleteCache(clazz, ids);
|
||||
if (!this.localGroupName.equals(group)) sendDelete(this.localGroupName, true, sourceName, clazz, ids); //不是同一机房来的资源需要同步到其他同机房的节点上
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,162 +17,102 @@ import java.util.logging.*;
|
||||
import javax.annotation.Resource;
|
||||
|
||||
/**
|
||||
* 暂时不实现
|
||||
*
|
||||
* @author zhangjx
|
||||
*/
|
||||
@AutoLoad(false)
|
||||
public class DataSQLListenerService implements DataSQLListener, Service {
|
||||
|
||||
protected final Logger logger = Logger.getLogger(this.getClass().getSimpleName());
|
||||
|
||||
private static final String format = "%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS.%tL";
|
||||
|
||||
private boolean finest;
|
||||
protected final Logger logger = Logger.getLogger(this.getClass().getSimpleName());
|
||||
|
||||
@Resource(name = "APP_NODE")
|
||||
private String localNodeName = "";
|
||||
|
||||
private String localIDCName = "";
|
||||
private final boolean finest = logger.isLoggable(Level.FINEST);
|
||||
|
||||
@Resource(name = "APP_HOME")
|
||||
private File home;
|
||||
|
||||
private File root;
|
||||
@Resource(name = "$")
|
||||
private DataSource source;
|
||||
|
||||
@Resource(name = ".*")
|
||||
HashMap<String, DataSource> sourcemaps;
|
||||
private final BlockingQueue<String> queue = new ArrayBlockingQueue<>(1024 * 1024);
|
||||
|
||||
private ConcurrentHashMap<String, BlockingQueue<String>> queues = new ConcurrentHashMap<>();
|
||||
|
||||
@Resource
|
||||
private HashMap<String, DataSQLListenerService> nodemaps;
|
||||
|
||||
private final HashSet<String> allidcs = new HashSet<>();
|
||||
|
||||
private ConcurrentHashMap<String, PrintStream> syncfiles = new ConcurrentHashMap<>();
|
||||
private PrintStream syncfile;
|
||||
|
||||
@Override
|
||||
public void init(AnyValue config) {
|
||||
finest = logger.isLoggable(Level.FINEST);
|
||||
//nodename的前两位字符表示机房ID
|
||||
if (localNodeName.length() > 2) localIDCName = getIDC(localNodeName);
|
||||
if (finest) logger.fine("LocalNodeName: " + localNodeName + ", " + localIDCName + " " + this.nodemaps);
|
||||
if (this.nodemaps == null) return;
|
||||
this.nodemaps.forEach((x, y) -> allidcs.add(x.substring(0, 2)));
|
||||
new Thread() {
|
||||
{
|
||||
setName(DataSQLListener.class.getSimpleName() + "-Thread");
|
||||
setDaemon(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
while (true) {
|
||||
try {
|
||||
String sql = queue.take();
|
||||
send(sql);
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.SEVERE, this.getName() + " occur error");
|
||||
}
|
||||
}
|
||||
}
|
||||
}.start();
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy(AnyValue config) {
|
||||
this.syncfiles.forEach((x, y) -> {
|
||||
y.close();
|
||||
});
|
||||
if (syncfile != null) syncfile.close();
|
||||
}
|
||||
|
||||
private void write(String node, String sourceName, String... sqls) {
|
||||
if (sourceName == null || sourceName.isEmpty()) sourceName = "<>";
|
||||
String key = node + "-" + sourceName;
|
||||
PrintStream channel = syncfiles.get(key);
|
||||
private void write(String... sqls) {
|
||||
try {
|
||||
if (channel == null) {
|
||||
if (this.root == null) {
|
||||
this.root = new File(home, "dbsync");
|
||||
this.root.mkdirs();
|
||||
}
|
||||
channel = new PrintStream(new FileOutputStream(new File(this.root, key + ".sql"), true), false, "UTF-8");
|
||||
syncfiles.put(key, channel);
|
||||
if (syncfile == null) {
|
||||
File root = new File(home, "dbsync");
|
||||
root.mkdirs();
|
||||
syncfile = new PrintStream(new FileOutputStream(new File(root, "sql-" + name() + ".sql"), true), false, "UTF-8");
|
||||
}
|
||||
for (String sql : sqls) {
|
||||
channel.print(sql + ";\r\n");
|
||||
syncfile.print(sql + ";\r\n");
|
||||
}
|
||||
channel.flush();
|
||||
syncfile.flush();
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.WARNING, "write sql file error. (" + node + ", " + sourceName + ", " + Arrays.toString(sqls) + ")", e);
|
||||
logger.log(Level.WARNING, "write sql file error. (" + name() + ", " + Arrays.toString(sqls) + ")", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void insert(String sourceName, String... sqls) {
|
||||
put(sourceName, sqls);
|
||||
public void insert(String... sqls) {
|
||||
put(sqls);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(String sourceName, String... sqls) {
|
||||
put(sourceName, sqls);
|
||||
public void update(String... sqls) {
|
||||
put(sqls);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(String sourceName, String... sqls) {
|
||||
put(sourceName, sqls);
|
||||
public void delete(String... sqls) {
|
||||
put(sqls);
|
||||
}
|
||||
|
||||
private void put(final String sourceName, String... sqls) {
|
||||
private void put(String... sqls) {
|
||||
String date = String.format(format, System.currentTimeMillis());
|
||||
BlockingQueue<String> queue = this.queues.get(sourceName);
|
||||
if (queue == null) {
|
||||
synchronized (this) {
|
||||
queue = this.queues.get(sourceName);
|
||||
if (queue == null) {
|
||||
queue = new ArrayBlockingQueue<>(1024 * 1024);
|
||||
this.queues.put(sourceName, queue);
|
||||
final BlockingQueue<String> tq = queue;
|
||||
new Thread() {
|
||||
{
|
||||
setName(DataSQLListener.class.getSimpleName() + "-" + (sourceName.isEmpty() ? "<>" : sourceName) + "-Thread");
|
||||
setDaemon(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
while (true) {
|
||||
try {
|
||||
String sql = tq.take();
|
||||
send(sourceName, sql);
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.SEVERE, this.getName() + " occur error");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}.start();
|
||||
}
|
||||
}
|
||||
}
|
||||
try {
|
||||
for (String sql : sqls) {
|
||||
queue.put("/* " + date + " */ " + sql);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.WARNING, this.getClass().getSimpleName() + " put queue error" + Arrays.toString(sqls), e);
|
||||
}
|
||||
}
|
||||
|
||||
private String getIDC(String nodeName) {
|
||||
return nodeName.substring(0, 2);
|
||||
}
|
||||
|
||||
@RemoteOn
|
||||
public void send(String sourceName, String... sqls) {
|
||||
if (this.nodemaps == null) return;
|
||||
final Set<String> idcs = new HashSet<>();
|
||||
idcs.add(localIDCName);
|
||||
nodemaps.forEach((x, y) -> {
|
||||
for (String sql : sqls) {
|
||||
try {
|
||||
String idc = getIDC(x);
|
||||
if (!idcs.contains(idc)) {
|
||||
y.send(sourceName, sqls);
|
||||
idcs.add(idc);
|
||||
}
|
||||
queue.put("/* " + date + " */ " + sql);
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.FINE, this.getClass().getSimpleName() + " send error (" + x + ", " + sourceName + ", " + Arrays.toString(sqls) + ")", e);
|
||||
write(sql);
|
||||
}
|
||||
});
|
||||
allidcs.forEach(x -> {
|
||||
if (!idcs.contains(x)) write(x, sourceName, sqls);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public final void onSend(String sourceName, String... sqls) {
|
||||
((DataDefaultSource) sourcemaps.get(sourceName)).execute(sqls);
|
||||
@MultiRun
|
||||
public void send(String... sqls) {
|
||||
((DataDefaultSource) source).execute(sqls);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
27
src/com/wentch/redkale/service/MultiRun.java
Normal file
27
src/com/wentch/redkale/service/MultiRun.java
Normal file
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* 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 com.wentch.redkale.service;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
import static java.lang.annotation.ElementType.*;
|
||||
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author zhangjx
|
||||
*/
|
||||
@Inherited
|
||||
@Documented
|
||||
@Target({METHOD})
|
||||
@Retention(RUNTIME)
|
||||
public @interface MultiRun {
|
||||
|
||||
boolean samerun() default true; //是否同组节点也运营指定操作
|
||||
|
||||
boolean diffrun() default true; //是否不同组节点也运营指定操作
|
||||
|
||||
boolean async() default true; //分布式运行是否采用异步模式
|
||||
}
|
||||
@@ -1,42 +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 com.wentch.redkale.service;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
import static java.lang.annotation.ElementType.*;
|
||||
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||
|
||||
/**
|
||||
* * @author zhangjx
|
||||
*/
|
||||
/*
|
||||
* 只能标识在Service类的方法上, 且Service类被实例成RemoteService时才有效。
|
||||
* 被@RemoteOn 标记的xxx方法必须存在onXxx方法, 且参数和返回值必须一致, onXxx方法必须声明为public final。 且onXxx方法不会被RemoteService重载。
|
||||
* 例如:
|
||||
* public class XXXService implements Service {
|
||||
*
|
||||
* @Resource
|
||||
* private HashMap<String, XXXService> nodemaps;
|
||||
*
|
||||
* @RemoteOn
|
||||
* public void send(XXXBean bean){
|
||||
* nodemaps.forEach((x, y) -> {if(y != this) y.send(bean);});
|
||||
* }
|
||||
*
|
||||
* public final void onSend(XXXBean bean){
|
||||
* ...
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* 如果没有public final void onSend(XXXBean bean)方法,生成RemoteService会抛出异常。
|
||||
*/
|
||||
@Inherited
|
||||
@Documented
|
||||
@Target({METHOD, TYPE})
|
||||
@Retention(RUNTIME)
|
||||
public @interface RemoteOn {
|
||||
|
||||
}
|
||||
@@ -8,15 +8,15 @@ package com.wentch.redkale.service;
|
||||
import com.wentch.redkale.util.*;
|
||||
|
||||
/**
|
||||
* 所有Service的实现类不得声明为final, 允许远程模式的public方法不能声明为final。
|
||||
*
|
||||
* 所有Service的实现类不得声明为final, 允许远程模式的public方法和public String name()方法都不能声明为final。
|
||||
* <p>
|
||||
* @Resource(name = ".*")
|
||||
* private HashMap<String, XXXService> nodemap;
|
||||
* 被注入的多个XXXService实例 但不会包含自身的XXXService。
|
||||
*
|
||||
* @author zhangjx
|
||||
*/
|
||||
public interface Service {
|
||||
public interface Service extends Nameable {
|
||||
|
||||
/**
|
||||
* 该方法必须是可以重复调用, 当reload时需要重复调用init方法
|
||||
@@ -30,4 +30,13 @@ public interface Service {
|
||||
default void destroy(AnyValue config) {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Service的name, 一个Service在同一进程内可以包含多个实例, 使用name区分
|
||||
* <p>
|
||||
* @return
|
||||
*/
|
||||
default String name() {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,265 +8,66 @@ package com.wentch.redkale.service;
|
||||
import com.wentch.redkale.net.http.*;
|
||||
import com.wentch.redkale.util.*;
|
||||
import java.io.*;
|
||||
import java.net.*;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.logging.*;
|
||||
import javax.annotation.*;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author zhangjx
|
||||
*/
|
||||
@AutoLoad(false)
|
||||
public class WebSocketNodeService implements Service {
|
||||
public class WebSocketNodeService extends WebSocketNode implements Service {
|
||||
|
||||
public static final int RETCODE_ENGINE_NULL = 5001;
|
||||
|
||||
public static final int RETCODE_NODESERVICE_NULL = 5002;
|
||||
|
||||
public static final int RETCODE_GROUP_EMPTY = 5005;
|
||||
|
||||
public static final int RETCODE_WSOFFLINE = 5011;
|
||||
|
||||
protected final Logger logger = Logger.getLogger(this.getClass().getSimpleName());
|
||||
|
||||
protected final boolean finest = logger.isLoggable(Level.FINEST);
|
||||
|
||||
@Resource(name = "APP_NODE")
|
||||
protected String localNodeName = "";
|
||||
|
||||
@Resource
|
||||
protected HashMap<String, WebSocketNodeService> nodemaps;
|
||||
|
||||
//用户分布在节点上的队列信息,只保存远程节点的用户分布信息
|
||||
protected final ConcurrentHashMap<Serializable, Set<String>> usernodes = new ConcurrentHashMap();
|
||||
|
||||
protected final ConcurrentHashMap<String, WebSocketEngine> engines = new ConcurrentHashMap();
|
||||
|
||||
public void initUserNodes() {
|
||||
if (this.nodemaps == null || this.nodemaps.isEmpty()) return;
|
||||
new Thread() {
|
||||
{
|
||||
setDaemon(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
usernodes.putAll(queryNodes());
|
||||
}
|
||||
}.start();
|
||||
@Override
|
||||
public void init(AnyValue conf) {
|
||||
super.init(conf);
|
||||
}
|
||||
|
||||
public final void addWebSocketEngine(WebSocketEngine engine) {
|
||||
engines.put(engine.getEngineid(), engine);
|
||||
@Override
|
||||
public void destroy(AnyValue conf) {
|
||||
super.destroy(conf);
|
||||
}
|
||||
|
||||
@RemoteOn
|
||||
public Map<Serializable, Set<String>> queryNodes() {
|
||||
Map<Serializable, Set<String>> rs = new HashMap<>();
|
||||
this.nodemaps.forEach((x, y) -> {
|
||||
if (!rs.isEmpty()) return;
|
||||
try {
|
||||
rs.putAll(y.queryNodes());
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.WARNING, this.getClass().getSimpleName() + " query error (" + x + ")", e);
|
||||
}
|
||||
});
|
||||
return rs;
|
||||
}
|
||||
|
||||
public final Map<Serializable, Set<String>> onQueryNodes() {
|
||||
Map<Serializable, Set<String>> rs = new HashMap<>();
|
||||
rs.putAll(this.usernodes);
|
||||
return rs;
|
||||
}
|
||||
|
||||
public void connectSelf(Serializable userid) {
|
||||
connect(this.localNodeName, userid);
|
||||
}
|
||||
|
||||
public void disconnectSelf(Serializable userid) {
|
||||
disconnect(this.localNodeName, userid);
|
||||
}
|
||||
|
||||
@RemoteOn
|
||||
public void connect(String nodeid, Serializable userid) {
|
||||
onConnect(nodeid, userid);
|
||||
if (this.nodemaps == null) return;
|
||||
this.nodemaps.forEach((x, y) -> {
|
||||
try {
|
||||
if (finest) logger.finest("Node(" + localNodeName + "->" + x + ") send websocket connect event (" + userid + " on " + nodeid + ")");
|
||||
y.connect(nodeid, userid);
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.WARNING, "Node(" + localNodeName + "->" + x + ") send websocket connect event (" + userid + " on " + nodeid + ")", e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public final void onConnect(String nodeid, Serializable userid) {
|
||||
if (finest) logger.finest("Node (" + localNodeName + ") receive websocket connect event (" + userid + " on " + nodeid + ").");
|
||||
Set<String> userNodelist = usernodes.get(userid);
|
||||
if (userNodelist == null) {
|
||||
userNodelist = new CopyOnWriteArraySet<>();
|
||||
usernodes.put(userid, userNodelist);
|
||||
}
|
||||
userNodelist.add(nodeid);
|
||||
}
|
||||
|
||||
@RemoteOn
|
||||
public void disconnect(String nodeid, Serializable userid) {
|
||||
onDisconnect(nodeid, userid);
|
||||
if (this.nodemaps == null) return;
|
||||
this.nodemaps.forEach((x, y) -> {
|
||||
try {
|
||||
if (finest) logger.finest("Node(" + localNodeName + "->" + x + ") send websocket disconnect event (" + userid + " on " + nodeid + ")");
|
||||
y.disconnect(nodeid, userid);
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.WARNING, "Node(" + localNodeName + "->" + x + ") send websocket disconnect event (" + userid + " on " + nodeid + ")", e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public final void onDisconnect(String nodeid, Serializable userid) {
|
||||
if (finest) logger.finest("Node (" + localNodeName + ") receive websocket disconnect event (" + userid + " on " + nodeid + ").");
|
||||
Set<String> userNodelist = usernodes.get(userid);
|
||||
if (userNodelist == null) return;
|
||||
userNodelist.remove(nodeid);
|
||||
if (userNodelist.isEmpty()) usernodes.remove(userid);
|
||||
}
|
||||
|
||||
@RemoteOn
|
||||
public int send(String engineid, Serializable groupid, String text) {
|
||||
return send(engineid, groupid, text, true);
|
||||
}
|
||||
|
||||
public final int onSend(String engineid, Serializable groupid, String text) {
|
||||
return onSend(engineid, groupid, text, true);
|
||||
}
|
||||
|
||||
@RemoteOn
|
||||
public int send(String engineid, Serializable groupid, String text, boolean last) {
|
||||
return send0(engineid, groupid, false, text, last);
|
||||
}
|
||||
|
||||
public final int onSend(String engineid, Serializable groupid, String text, boolean last) {
|
||||
return onSend0(engineid, groupid, false, text, last);
|
||||
}
|
||||
|
||||
@RemoteOn
|
||||
public int send(String engineid, Serializable groupid, boolean recent, String text) {
|
||||
return send0(engineid, groupid, recent, text, true);
|
||||
}
|
||||
|
||||
public final int onSend(String engineid, Serializable groupid, boolean recent, String text) {
|
||||
return onSend0(engineid, groupid, recent, text, true);
|
||||
}
|
||||
|
||||
@RemoteOn
|
||||
public int send(String engineid, Serializable groupid, boolean recent, String text, boolean last) {
|
||||
return send0(engineid, groupid, recent, text, last);
|
||||
}
|
||||
|
||||
public final int onSend(String engineid, Serializable groupid, boolean recent, String text, boolean last) {
|
||||
return onSend0(engineid, groupid, recent, text, last);
|
||||
}
|
||||
|
||||
@RemoteOn
|
||||
public int send(String engineid, Serializable groupid, byte[] data) {
|
||||
return send(engineid, groupid, data, true);
|
||||
}
|
||||
|
||||
public final int onSend(String engineid, Serializable groupid, byte[] data) {
|
||||
return onSend(engineid, groupid, data, true);
|
||||
}
|
||||
|
||||
@RemoteOn
|
||||
public int send(String engineid, Serializable groupid, byte[] data, boolean last) {
|
||||
return send0(engineid, groupid, false, data, last);
|
||||
}
|
||||
|
||||
public final int onSend(String engineid, Serializable groupid, byte[] data, boolean last) {
|
||||
return onSend0(engineid, groupid, false, data, last);
|
||||
}
|
||||
|
||||
@RemoteOn
|
||||
public int send(String engineid, Serializable groupid, boolean recent, byte[] data) {
|
||||
return send0(engineid, groupid, recent, data, true);
|
||||
}
|
||||
|
||||
public final int onSend(String engineid, Serializable groupid, boolean recent, byte[] data) {
|
||||
return onSend0(engineid, groupid, recent, data, true);
|
||||
}
|
||||
|
||||
@RemoteOn
|
||||
public int send(String engineid, Serializable groupid, boolean recent, byte[] data, boolean last) {
|
||||
return send0(engineid, groupid, recent, data, last);
|
||||
}
|
||||
|
||||
public final int onSend(String engineid, Serializable groupid, boolean recent, byte[] data, boolean last) {
|
||||
return onSend0(engineid, groupid, recent, data, last);
|
||||
}
|
||||
|
||||
private int send0(String engineid, Serializable groupid, boolean recent, Serializable text, boolean last) {
|
||||
final Set<String> nodes = usernodes.get(groupid);
|
||||
if (nodes == null) return RETCODE_WSOFFLINE; //未登录
|
||||
int rs = 0;
|
||||
if (nodes.contains(this.localNodeName)) rs = onSend0(engineid, groupid, recent, text, last);
|
||||
if (nodemaps == null) return rs;
|
||||
this.nodemaps.forEach((x, y) -> {
|
||||
if (nodes.contains(x)) {
|
||||
int irs = -1;
|
||||
try {
|
||||
if (text != null && text.getClass() == byte[].class) {
|
||||
irs = y.send(engineid, groupid, (byte[]) text, last);
|
||||
} else {
|
||||
irs = y.send(engineid, groupid, (String) text, last);
|
||||
}
|
||||
if (finest) logger.finest("Node(" + localNodeName + "->" + x + ") send websocket message {engineid:'" + engineid + "', groupid:" + groupid + ", content:'" + text + "'} finish and result is " + irs);
|
||||
} catch (Exception e) {
|
||||
onDisconnect(x, groupid);
|
||||
logger.log(Level.WARNING, "Node(" + localNodeName + "->" + x + ") send websocket message {engineid:'" + engineid + "', groupid:" + groupid + ", content:'" + text + "'} failed and result is " + irs, e);
|
||||
@Override
|
||||
public int sendMessage(Serializable groupid, boolean recent, Serializable message, boolean last) {
|
||||
final Set<String> engineids = localNodes.get(groupid);
|
||||
if (engineids == null || engineids.isEmpty()) return RETCODE_GROUP_EMPTY;
|
||||
for (String engineid : engineids) {
|
||||
final WebSocketEngine engine = engines.get(engineid);
|
||||
if (engine != null) { //在本地
|
||||
final WebSocketGroup group = engine.getWebSocketGroup(groupid);
|
||||
if (group == null || group.isEmpty()) {
|
||||
if (finest) logger.finest("receive websocket message {engineid:'" + engineid + "', groupid:" + groupid + ", content:'" + message + "'} but result is " + RETCODE_GROUP_EMPTY);
|
||||
return RETCODE_GROUP_EMPTY;
|
||||
}
|
||||
}
|
||||
});
|
||||
return rs;
|
||||
}
|
||||
|
||||
/**
|
||||
* 消息接受者存在WebSocket并发送成功返回true, 否则返回false
|
||||
*
|
||||
* @param engineid
|
||||
* @param groupid 接收方
|
||||
* @param recent 是否只发送最近的WebSocket端
|
||||
* @param text
|
||||
* @return
|
||||
*/
|
||||
private int onSend0(String engineid, Serializable groupid, boolean recent, Serializable text, boolean last) {
|
||||
WebSocketEngine webSocketEngine = engines.get(engineid);
|
||||
if (webSocketEngine == null) {
|
||||
if (finest) logger.finest("Node(" + localNodeName + ") receive websocket message {engineid:'" + engineid + "', groupid:" + groupid + ", content:'" + text + "'} but result is " + RETCODE_ENGINE_NULL);
|
||||
return RETCODE_ENGINE_NULL;
|
||||
}
|
||||
WebSocketGroup group = webSocketEngine.getWebSocketGroup(groupid);
|
||||
if (group == null || group.isEmpty()) {
|
||||
if (finest) logger.finest("Node(" + localNodeName + ") receive websocket message {engineid:'" + engineid + "', groupid:" + groupid + ", content:'" + text + "'} but result is " + RETCODE_GROUP_EMPTY);
|
||||
return RETCODE_GROUP_EMPTY;
|
||||
}
|
||||
if (finest) logger.finest("Node (" + localNodeName + ") receive websocket message {engineid:'" + engineid + "', groupid:" + groupid + ", content:'" + text + "'}.");
|
||||
if (text != null && text.getClass() == byte[].class) {
|
||||
if (recent) {
|
||||
group.getRecentWebSocket().send((byte[]) text, last);
|
||||
} else {
|
||||
group.getWebSockets().forEach(x -> x.send((byte[]) text, last));
|
||||
}
|
||||
} else {
|
||||
if (recent) {
|
||||
group.getRecentWebSocket().send(text.toString(), last);
|
||||
} else {
|
||||
group.getWebSockets().forEach(x -> x.send(text.toString(), last));
|
||||
group.send(recent, message, last);
|
||||
} else { //对方连接在远程节点
|
||||
return RETCODE_WSOFFLINE;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
@MultiRun
|
||||
public void connect(Serializable groupid, InetSocketAddress addr) {
|
||||
Set<InetSocketAddress> addrs = dataNodes.get(groupid);
|
||||
if (addrs == null) {
|
||||
addrs = new CopyOnWriteArraySet<>();
|
||||
dataNodes.put(groupid, addrs);
|
||||
}
|
||||
addrs.add(addr);
|
||||
if(finest) logger.finest(WebSocketNodeService.class.getSimpleName() + ".event: " + groupid +" connect from " + addr);
|
||||
}
|
||||
|
||||
@Override
|
||||
@MultiRun
|
||||
public void disconnect(Serializable groupid, InetSocketAddress addr) {
|
||||
Set<InetSocketAddress> addrs = dataNodes.get(groupid);
|
||||
if (addrs == null) return;
|
||||
addrs.remove(addr);
|
||||
if (addrs.isEmpty()) dataNodes.remove(groupid);
|
||||
if(finest) logger.finest(WebSocketNodeService.class.getSimpleName() + ".event: " + groupid +" disconnect from " + addr);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,9 +13,9 @@ import java.io.Serializable;
|
||||
*/
|
||||
public interface DataCacheListener {
|
||||
|
||||
public <T> void insert(String sourceName, Class<T> clazz, T... entitys);
|
||||
public <T> void insertCache(Class<T> clazz, T... entitys);
|
||||
|
||||
public <T> void update(String sourceName, Class<T> clazz, T... entitys);
|
||||
public <T> void updateCache(Class<T> clazz, T... entitys);
|
||||
|
||||
public <T> void delete(String sourceName, Class<T> clazz, Serializable... ids);
|
||||
public <T> void deleteCache(Class<T> clazz, Serializable... ids);
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ import javax.xml.stream.*;
|
||||
* @author zhangjx
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public final class DataDefaultSource implements DataSource {
|
||||
public final class DataDefaultSource implements DataSource, Nameable {
|
||||
|
||||
public static final String DATASOURCE_CONFPATH = "DATASOURCE_CONFPATH";
|
||||
|
||||
@@ -50,6 +50,8 @@ public final class DataDefaultSource implements DataSource {
|
||||
|
||||
final URL conf;
|
||||
|
||||
final boolean cacheForbidden;
|
||||
|
||||
private final JDBCPoolSource readPool;
|
||||
|
||||
private final JDBCPoolSource writePool;
|
||||
@@ -57,10 +59,10 @@ public final class DataDefaultSource implements DataSource {
|
||||
@Resource(name = "property.datasource.nodeid")
|
||||
private int nodeid;
|
||||
|
||||
@Resource
|
||||
@Resource(name = "$")
|
||||
private DataSQLListener writeListener;
|
||||
|
||||
@Resource
|
||||
@Resource(name = "$")
|
||||
private DataCacheListener cacheListener;
|
||||
|
||||
private static class DataJDBCConnection extends DataConnection {
|
||||
@@ -153,7 +155,7 @@ public final class DataDefaultSource implements DataSource {
|
||||
this.conf = url;
|
||||
this.readPool = new JDBCPoolSource(this, "read", readprop);
|
||||
this.writePool = new JDBCPoolSource(this, "write", writeprop);
|
||||
EntityInfo.cacheForbidden = "NONE".equalsIgnoreCase(readprop.getProperty("shared-cache-mode"));
|
||||
this.cacheForbidden = "NONE".equalsIgnoreCase(readprop.getProperty("shared-cache-mode"));
|
||||
}
|
||||
|
||||
public DataDefaultSource(String unitName, Properties readprop, Properties writeprop) {
|
||||
@@ -161,7 +163,7 @@ public final class DataDefaultSource implements DataSource {
|
||||
this.conf = null;
|
||||
this.readPool = new JDBCPoolSource(this, "read", readprop);
|
||||
this.writePool = new JDBCPoolSource(this, "write", writeprop);
|
||||
EntityInfo.cacheForbidden = "NONE".equalsIgnoreCase(readprop.getProperty("shared-cache-mode"));
|
||||
this.cacheForbidden = "NONE".equalsIgnoreCase(readprop.getProperty("shared-cache-mode"));
|
||||
}
|
||||
|
||||
public static Map<String, DataDefaultSource> create(final InputStream in) {
|
||||
@@ -257,6 +259,11 @@ public final class DataDefaultSource implements DataSource {
|
||||
return (ConnectionPoolDataSource) pdsource;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final String name() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataConnection createReadConnection() {
|
||||
return new DataJDBCConnection(createReadSQLConnection());
|
||||
@@ -320,11 +327,11 @@ public final class DataDefaultSource implements DataSource {
|
||||
}
|
||||
|
||||
private <T> EntityInfo<T> loadEntityInfo(Class<T> clazz) {
|
||||
return EntityInfo.load(clazz, this.nodeid, fullloader);
|
||||
return EntityInfo.load(clazz, this.nodeid, this.cacheForbidden, fullloader);
|
||||
}
|
||||
|
||||
private <T extends FilterBean> FilterBeanNode loadFilterBeanNode(Class<T> clazz) {
|
||||
return FilterBeanNode.load(clazz, this.nodeid, fullloader);
|
||||
return FilterBeanNode.load(clazz, this.nodeid, this.cacheForbidden, fullloader);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -335,13 +342,13 @@ public final class DataDefaultSource implements DataSource {
|
||||
*/
|
||||
@Override
|
||||
public <T> void refreshCache(Class<T> clazz) {
|
||||
EntityInfo<T> info = EntityInfo.load(clazz, this.nodeid, fullloader);
|
||||
EntityInfo<T> info = loadEntityInfo(clazz);
|
||||
EntityCache<T> cache = info.getCache();
|
||||
if (cache == null) return;
|
||||
cache.fullLoad(queryList(clazz, (FilterNode) null));
|
||||
}
|
||||
|
||||
//----------------------insert-----------------------------
|
||||
//----------------------insertCache-----------------------------
|
||||
/**
|
||||
* 新增对象, 必须是Entity对象
|
||||
*
|
||||
@@ -467,7 +474,7 @@ public final class DataDefaultSource implements DataSource {
|
||||
}
|
||||
}
|
||||
prestmt.executeBatch();
|
||||
if (writeListener != null) writeListener.insert(name, sqls);
|
||||
if (writeListener != null) writeListener.insert(sqls);
|
||||
if (info.autoGenerated) {
|
||||
ResultSet set = prestmt.getGeneratedKeys();
|
||||
int i = -1;
|
||||
@@ -488,16 +495,16 @@ public final class DataDefaultSource implements DataSource {
|
||||
for (final T value : values) {
|
||||
cache.insert(value);
|
||||
}
|
||||
if (cacheListener != null) cacheListener.insert(name, info.getType(), values);
|
||||
if (cacheListener != null) cacheListener.insertCache(info.getType(), values);
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public <T> void insertCache(T... values) {
|
||||
public <T> void insertCache(Class<T> clazz, T... values) {
|
||||
if (values.length == 0) return;
|
||||
final EntityInfo<T> info = EntityInfo.load((Class<T>) values[0].getClass(), this.nodeid, fullloader);
|
||||
final EntityInfo<T> info = EntityInfo.load(clazz, this.nodeid, this.cacheForbidden, fullloader);
|
||||
final EntityCache<T> cache = info.getCache();
|
||||
if (cache == null) return;
|
||||
for (T value : values) {
|
||||
@@ -505,7 +512,7 @@ public final class DataDefaultSource implements DataSource {
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------delete--------------------------
|
||||
//-------------------------deleteCache--------------------------
|
||||
/**
|
||||
* 删除对象, 必须是Entity对象
|
||||
*
|
||||
@@ -576,7 +583,7 @@ public final class DataDefaultSource implements DataSource {
|
||||
final Statement stmt = conn.createStatement();
|
||||
stmt.execute(sql);
|
||||
stmt.close();
|
||||
if (writeListener != null) writeListener.delete(name, sql);
|
||||
if (writeListener != null) writeListener.delete(sql);
|
||||
}
|
||||
//------------------------------------
|
||||
final EntityCache<T> cache = info.getCache();
|
||||
@@ -584,7 +591,7 @@ public final class DataDefaultSource implements DataSource {
|
||||
final Attribute<T, Serializable> attr = info.getPrimary();
|
||||
final Serializable[] keys2 = keys;
|
||||
Serializable[] ids = cache.delete((T t) -> Arrays.binarySearch(keys2, attr.get(t)) >= 0);
|
||||
if (cacheListener != null) cacheListener.delete(name, info.getType(), ids);
|
||||
if (cacheListener != null) cacheListener.deleteCache(info.getType(), ids);
|
||||
} catch (SQLException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
@@ -618,13 +625,13 @@ public final class DataDefaultSource implements DataSource {
|
||||
final Statement stmt = conn.createStatement();
|
||||
stmt.execute(sql);
|
||||
stmt.close();
|
||||
if (writeListener != null) writeListener.delete(name, sql);
|
||||
if (writeListener != null) writeListener.delete(sql);
|
||||
}
|
||||
//------------------------------------
|
||||
final EntityCache<T> cache = info.getCache();
|
||||
if (cache == null) return;
|
||||
Serializable[] ids = cache.delete(node.createFilterPredicate(info, null));
|
||||
if (cacheListener != null) cacheListener.delete(name, info.getType(), ids);
|
||||
if (cacheListener != null) cacheListener.deleteCache(info.getType(), ids);
|
||||
} catch (SQLException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
@@ -717,7 +724,7 @@ public final class DataDefaultSource implements DataSource {
|
||||
}
|
||||
prestmt.executeBatch();
|
||||
prestmt.close();
|
||||
if (writeListener != null) writeListener.update(name, sqls);
|
||||
if (writeListener != null) writeListener.update(sqls);
|
||||
}
|
||||
//---------------------------------------------------
|
||||
final EntityCache<T> cache = info.getCache();
|
||||
@@ -725,7 +732,7 @@ public final class DataDefaultSource implements DataSource {
|
||||
for (final T value : values) {
|
||||
cache.update(value);
|
||||
}
|
||||
if (cacheListener != null) cacheListener.update(name, clazz, values);
|
||||
if (cacheListener != null) cacheListener.updateCache(clazz, values);
|
||||
} catch (SQLException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
@@ -769,13 +776,13 @@ public final class DataDefaultSource implements DataSource {
|
||||
final Statement stmt = conn.createStatement();
|
||||
stmt.execute(sql);
|
||||
stmt.close();
|
||||
if (writeListener != null) writeListener.update(name, sql);
|
||||
if (writeListener != null) writeListener.update(sql);
|
||||
}
|
||||
//---------------------------------------------------
|
||||
final EntityCache<T> cache = info.getCache();
|
||||
if (cache == null) return;
|
||||
T rs = cache.update(id, (Attribute<T, Serializable>) info.getAttribute(column), value);
|
||||
if (cacheListener != null) cacheListener.update(name, info.getType(), rs);
|
||||
if (cacheListener != null) cacheListener.updateCache(info.getType(), rs);
|
||||
} catch (SQLException e) {
|
||||
throw new RuntimeException(e);
|
||||
} finally {
|
||||
@@ -822,14 +829,14 @@ public final class DataDefaultSource implements DataSource {
|
||||
final Statement stmt = conn.createStatement();
|
||||
stmt.execute(sql);
|
||||
stmt.close();
|
||||
if (writeListener != null) writeListener.update(name, sql);
|
||||
if (writeListener != null) writeListener.update(sql);
|
||||
}
|
||||
//---------------------------------------------------
|
||||
final EntityCache<T> cache = info.getCache();
|
||||
if (cache == null) return;
|
||||
Attribute<T, Serializable> attr = info.getAttribute(column);
|
||||
T value = cache.updateColumnIncrement(id, attr, incvalue);
|
||||
if (value != null && cacheListener != null) cacheListener.update(name, info.getType(), value);
|
||||
if (value != null && cacheListener != null) cacheListener.updateCache(info.getType(), value);
|
||||
} catch (SQLException e) {
|
||||
throw new RuntimeException(e);
|
||||
} finally {
|
||||
@@ -888,13 +895,13 @@ public final class DataDefaultSource implements DataSource {
|
||||
final Statement stmt = conn.createStatement();
|
||||
stmt.execute(sql);
|
||||
stmt.close();
|
||||
if (writeListener != null) writeListener.update(name, sql);
|
||||
if (writeListener != null) writeListener.update(sql);
|
||||
}
|
||||
//---------------------------------------------------
|
||||
final EntityCache<T> cache = info.getCache();
|
||||
if (cache == null) return;
|
||||
cache.update(value, attrs);
|
||||
if (cacheListener != null) cacheListener.update(name, clazz, value);
|
||||
if (cacheListener != null) cacheListener.updateCache(clazz, value);
|
||||
} catch (SQLException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
@@ -12,9 +12,9 @@ package com.wentch.redkale.source;
|
||||
*/
|
||||
public interface DataSQLListener {
|
||||
|
||||
public void insert(String sourceName, String... sqls);
|
||||
public void insert(String... sqls);
|
||||
|
||||
public void update(String sourceName, String... sqls);
|
||||
public void update(String... sqls);
|
||||
|
||||
public void delete(String sourceName, String... sqls);
|
||||
public void delete(String... sqls);
|
||||
}
|
||||
|
||||
@@ -29,8 +29,6 @@ public final class EntityInfo<T> {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(EntityInfo.class);
|
||||
|
||||
static boolean cacheForbidden = false;
|
||||
|
||||
//Entity类的类名
|
||||
private final Class<T> type;
|
||||
|
||||
@@ -86,14 +84,14 @@ public final class EntityInfo<T> {
|
||||
final int allocationSize;
|
||||
//------------------------------------------------------------
|
||||
|
||||
public static <T> EntityInfo<T> load(Class<T> clazz, final int nodeid,
|
||||
public static <T> EntityInfo<T> load(Class<T> clazz, final int nodeid, final boolean cacheForbidden,
|
||||
Function<Class, List> fullloader) {
|
||||
EntityInfo rs = entityInfos.get(clazz);
|
||||
if (rs != null) return rs;
|
||||
synchronized (entityInfos) {
|
||||
rs = entityInfos.get(clazz);
|
||||
if (rs == null) {
|
||||
rs = new EntityInfo(clazz, nodeid, fullloader);
|
||||
rs = new EntityInfo(clazz, nodeid, cacheForbidden, fullloader);
|
||||
entityInfos.put(clazz, rs);
|
||||
AutoLoad auto = clazz.getAnnotation(AutoLoad.class);
|
||||
if (rs.cache != null && auto != null && auto.value() && fullloader != null) {
|
||||
@@ -104,7 +102,7 @@ public final class EntityInfo<T> {
|
||||
}
|
||||
}
|
||||
|
||||
private EntityInfo(Class<T> type, int nodeid, Function<Class<T>, List<T>> fullloader) {
|
||||
private EntityInfo(Class<T> type, int nodeid, final boolean cacheForbidden, Function<Class<T>, List<T>> fullloader) {
|
||||
this.type = type;
|
||||
//---------------------------------------------
|
||||
this.nodeid = nodeid;
|
||||
|
||||
@@ -26,21 +26,21 @@ final class FilterBeanNode extends FilterNode {
|
||||
|
||||
private static final ConcurrentHashMap<Class, FilterBeanNode> beanodes = new ConcurrentHashMap<>();
|
||||
|
||||
public static <T extends FilterBean> FilterBeanNode load(Class<T> clazz, final int nodeid,
|
||||
public static <T extends FilterBean> FilterBeanNode load(Class<T> clazz, final int nodeid, final boolean cacheForbidden,
|
||||
Function<Class, List> fullloader) {
|
||||
FilterBeanNode rs = beanodes.get(clazz);
|
||||
if (rs != null) return rs;
|
||||
synchronized (beanodes) {
|
||||
rs = beanodes.get(clazz);
|
||||
if (rs == null) {
|
||||
rs = createNode(clazz, nodeid, fullloader);
|
||||
rs = createNode(clazz, nodeid, cacheForbidden, fullloader);
|
||||
beanodes.put(clazz, rs);
|
||||
}
|
||||
return rs;
|
||||
}
|
||||
}
|
||||
|
||||
private static <T extends FilterBean> FilterBeanNode createNode(Class<T> clazz, final int nodeid,
|
||||
private static <T extends FilterBean> FilterBeanNode createNode(Class<T> clazz, final int nodeid, final boolean cacheForbidden,
|
||||
Function<Class, List> fullloader) {
|
||||
Class cltmp = clazz;
|
||||
Set<String> fields = new HashSet<>();
|
||||
@@ -78,7 +78,7 @@ final class FilterBeanNode extends FilterNode {
|
||||
joinTables.put(joinClass, String.valueOf((char) ('b' + joinTables.size())));
|
||||
}
|
||||
final String alias = joinTables.get(joinClass);
|
||||
final EntityInfo secinfo = EntityInfo.load(joinClass, nodeid, fullloader);
|
||||
final EntityInfo secinfo = EntityInfo.load(joinClass, nodeid, cacheForbidden, fullloader);
|
||||
if (secinfo.getCache() == null || !secinfo.getCache().isFullLoaded()) {
|
||||
joinallcached = false;
|
||||
}
|
||||
|
||||
@@ -1,494 +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 com.wentch.redkale.source;
|
||||
|
||||
import static com.wentch.redkale.source.FilterExpress.*;
|
||||
import com.wentch.redkale.util.Attribute;
|
||||
import com.wentch.redkale.util.Ignore;
|
||||
import java.lang.reflect.*;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.logging.Logger;
|
||||
import javax.persistence.*;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author zhangjx
|
||||
* @param <T>
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
final class FilterInfo<T extends FilterBean> {
|
||||
|
||||
private final Logger logger = Logger.getLogger(this.getClass().getSimpleName());
|
||||
|
||||
private static final ConcurrentHashMap<Class, FilterInfo> infos = new ConcurrentHashMap<>();
|
||||
|
||||
private final String joinsql;
|
||||
|
||||
private final boolean validCacheJoin;
|
||||
|
||||
private final Class<T> type;
|
||||
|
||||
private final FilterExpressNode<T> rootNode;
|
||||
|
||||
public static <T> FilterInfo load(Class<T> clazz, DataSource source) {
|
||||
FilterInfo rs = infos.get(clazz);
|
||||
if (rs != null) return rs;
|
||||
synchronized (infos) {
|
||||
rs = infos.get(clazz);
|
||||
if (rs == null) {
|
||||
rs = new FilterInfo(clazz, source);
|
||||
infos.put(clazz, rs);
|
||||
}
|
||||
return rs;
|
||||
}
|
||||
}
|
||||
|
||||
private FilterInfo(Class<T> type, DataSource source) {
|
||||
this.type = type;
|
||||
Class cltmp = type;
|
||||
Set<String> fields = new HashSet<>();
|
||||
StringBuilder joinsb = new StringBuilder();
|
||||
final Map<Class, String> joinTables = new HashMap<>();
|
||||
final Map<String, FilterItem> getters = new HashMap<>();
|
||||
final Map<String, List<FilterItem>> nodes = new HashMap<>();
|
||||
boolean cachejoin = true;
|
||||
int index = 0;
|
||||
do {
|
||||
for (Field field : cltmp.getDeclaredFields()) {
|
||||
if (field.getAnnotation(Ignore.class) != null) continue;
|
||||
if (field.getAnnotation(Transient.class) != null) continue;
|
||||
if (Modifier.isStatic(field.getModifiers())) continue;
|
||||
if (fields.contains(field.getName())) continue;
|
||||
char[] chars = field.getName().toCharArray();
|
||||
chars[0] = Character.toUpperCase(chars[0]);
|
||||
final Class t = field.getType();
|
||||
try {
|
||||
type.getMethod(((t == boolean.class || t == Boolean.class) ? "is" : "get") + new String(chars));
|
||||
} catch (Exception ex) {
|
||||
continue;
|
||||
}
|
||||
fields.add(field.getName());
|
||||
FilterItem item = new FilterItem(field, "a", null);
|
||||
FilterJoinColumn joinCol = field.getAnnotation(FilterJoinColumn.class);
|
||||
boolean again = true;
|
||||
if (joinCol != null) {
|
||||
if (!joinTables.containsKey(joinCol.table())) {
|
||||
again = false;
|
||||
joinTables.put(joinCol.table(), String.valueOf((char) ('a' + (++index))));
|
||||
}
|
||||
String alias = joinTables.get(joinCol.table());
|
||||
EntityInfo info = EntityInfo.load(joinCol.table(), 0, null);
|
||||
EntityCache cache = null;
|
||||
if (info.getCache() != null && info.getCache().isFullLoaded()) {
|
||||
cache = info.getCache();
|
||||
} else {
|
||||
cachejoin = false;
|
||||
}
|
||||
item = new FilterItem(field, alias, cache);
|
||||
EntityInfo secinfo = EntityInfo.load(joinCol.table(), 0, null);
|
||||
if (!again) {
|
||||
joinsb.append(" ").append(joinCol.type().name()).append(" JOIN ").append(secinfo.getTable())
|
||||
.append(" ").append(alias).append(" ON a.# = ").append(alias).append(".")
|
||||
.append(joinCol.column().isEmpty() ? secinfo.getPrimary().field() : joinCol.column());
|
||||
}
|
||||
}
|
||||
getters.put(field.getName(), item);
|
||||
FilterGroup[] refs = field.getAnnotationsByType(FilterGroup.class);
|
||||
String[] groups = new String[refs.length];
|
||||
for (int i = 0; i < refs.length; i++) {
|
||||
groups[i] = refs[i].value();
|
||||
}
|
||||
if (groups.length == 0) groups = new String[]{"[AND]"};
|
||||
for (String key : groups) {
|
||||
if (!key.startsWith("[AND]") && !key.startsWith("[OR]")) {
|
||||
throw new RuntimeException(field + "'s FilterGroup.value(" + key + ") illegal, must be [AND] or [OR] startsWith");
|
||||
}
|
||||
List<FilterItem> nd = nodes.get(key);
|
||||
if (nd == null) {
|
||||
nd = new ArrayList();
|
||||
nodes.put(key, nd);
|
||||
}
|
||||
nd.add(item);
|
||||
}
|
||||
}
|
||||
} while ((cltmp = cltmp.getSuperclass()) != Object.class);
|
||||
//---------------------------------------------------------------
|
||||
List<FilterExpressNode> expnodes = new ArrayList<>();
|
||||
for (Map.Entry<String, List<FilterItem>> en : nodes.entrySet()) {
|
||||
if (en.getValue().size() == 1) {
|
||||
expnodes.add(en.getValue().get(0));
|
||||
} else {
|
||||
List<FilterItem> sitems = en.getValue();
|
||||
expnodes.add(new FilterGroupNode(en.getKey(), sitems.toArray(new FilterExpressNode[sitems.size()])));
|
||||
}
|
||||
}
|
||||
rootNode = new FilterGroupNode("AND", expnodes.toArray(new FilterExpressNode[expnodes.size()]));
|
||||
//---------------------------------------------------------------
|
||||
this.validCacheJoin = cachejoin;
|
||||
if (!joinTables.isEmpty()) {
|
||||
this.joinsql = joinsb.toString();
|
||||
} else {
|
||||
this.joinsql = null;
|
||||
}
|
||||
}
|
||||
|
||||
public FilterItem[] getFilters() {
|
||||
return new FilterItem[0];
|
||||
}
|
||||
|
||||
public Class getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public boolean isJoin() {
|
||||
return joinsql != null;
|
||||
}
|
||||
|
||||
public boolean isValidCacheJoin() {
|
||||
return validCacheJoin;
|
||||
}
|
||||
|
||||
public <E> Predicate<E> getFilterPredicate(EntityInfo<E> info, T bean) {
|
||||
return rootNode.getFilterPredicate(info, bean);
|
||||
}
|
||||
|
||||
public StringBuilder createWhereSql(String primaryColumn, T obj) {
|
||||
StringBuilder sb = rootNode.getFilterExpress(obj);
|
||||
if (sb == null) return null;
|
||||
final StringBuilder all = new StringBuilder(128);
|
||||
if (this.isJoin()) {
|
||||
all.append(this.joinsql.replace("#", primaryColumn));
|
||||
}
|
||||
all.append(" WHERE ").append(sb);
|
||||
return all;
|
||||
}
|
||||
|
||||
public static String formatToString(Object rs) {
|
||||
if (rs == null) return null;
|
||||
Class clazz = rs.getClass();
|
||||
if (CharSequence.class.isAssignableFrom(clazz)) {
|
||||
return "'" + rs.toString().replace("'", "\\'") + "'";
|
||||
} else if (java.util.Date.class.isAssignableFrom(clazz)) {
|
||||
return "'" + String.format("%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS", (java.util.Date) rs) + "'";
|
||||
}
|
||||
return String.valueOf(rs);
|
||||
}
|
||||
|
||||
static final class FilterGroupNode<T> implements FilterExpressNode<T> {
|
||||
|
||||
private final String sign;
|
||||
|
||||
private final boolean or;
|
||||
|
||||
private final FilterExpressNode<T>[] nodes;
|
||||
|
||||
public FilterGroupNode(String sign, FilterExpressNode<T>[] nodes) {
|
||||
this.sign = sign.indexOf("[OR]") == 0 || sign.equalsIgnoreCase("OR") ? "OR" : "AND";
|
||||
this.or = "OR".equals(this.sign);
|
||||
this.nodes = nodes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StringBuilder getFilterExpress(T obj) {
|
||||
StringBuilder sb = null;
|
||||
int count = 0;
|
||||
for (FilterExpressNode node : nodes) {
|
||||
StringBuilder sub = node.getFilterExpress(obj);
|
||||
if (sub == null) continue;
|
||||
if (sb == null) {
|
||||
sb = new StringBuilder();
|
||||
sb.append(sub);
|
||||
count++;
|
||||
} else {
|
||||
sb.append(' ').append(sign).append(' ').append(sub);
|
||||
count++;
|
||||
}
|
||||
}
|
||||
if (sb == null) return null;
|
||||
if (count < 2) return sb;
|
||||
return new StringBuilder(sb.length() + 2).append('(').append(sb).append(')');
|
||||
}
|
||||
|
||||
@Override
|
||||
public <E> Predicate<E> getFilterPredicate(EntityInfo<E> info, T bean) {
|
||||
Predicate<E> predicate = null;
|
||||
for (FilterExpressNode node : nodes) {
|
||||
Predicate<E> p = node.getFilterPredicate(info, bean);
|
||||
if (p == null) continue;
|
||||
if (predicate == null) {
|
||||
predicate = p;
|
||||
} else {
|
||||
predicate = or ? predicate.or(p) : predicate.and(p);
|
||||
}
|
||||
}
|
||||
return predicate;
|
||||
}
|
||||
}
|
||||
|
||||
static final class FilterItem<T, F> implements FilterExpressNode<T> {
|
||||
|
||||
public final Attribute<T, F> attribute;
|
||||
|
||||
public final String aliasfield;
|
||||
|
||||
private final String field;
|
||||
|
||||
public final FilterExpress express;
|
||||
|
||||
public final boolean string;
|
||||
|
||||
public final boolean number;
|
||||
|
||||
public final boolean likefit;
|
||||
|
||||
public final boolean ignoreCase;
|
||||
|
||||
public final long least;
|
||||
|
||||
public final Class<F> type;
|
||||
|
||||
public final EntityCache joinCache; //待实现
|
||||
|
||||
public FilterItem(Field field, String alias, EntityCache joinCache) {
|
||||
this.joinCache = joinCache;
|
||||
FilterColumn column = field.getAnnotation(FilterColumn.class);
|
||||
String sqlfield = (column != null && !column.name().isEmpty() ? column.name() : field.getName());
|
||||
this.field = sqlfield;
|
||||
sqlfield = alias + "." + sqlfield;
|
||||
this.attribute = Attribute.create(sqlfield, field);
|
||||
this.aliasfield = this.attribute.field();
|
||||
this.type = (Class<F>) field.getType();
|
||||
FilterExpress exp = column == null ? FilterExpress.EQUAL : column.express();
|
||||
if (type.isArray() || Collection.class.isAssignableFrom(type)) {
|
||||
if (Range.class.isAssignableFrom(type.getComponentType())) {
|
||||
if (AND != exp) exp = FilterExpress.OR;
|
||||
} else {
|
||||
if (NOTIN != exp) exp = FilterExpress.IN;
|
||||
}
|
||||
} else if (Range.class.isAssignableFrom(type)) {
|
||||
if (NOTBETWEEN != exp) exp = FilterExpress.BETWEEN;
|
||||
}
|
||||
this.express = exp;
|
||||
this.least = column == null ? 1L : column.least();
|
||||
this.likefit = column == null ? true : column.likefit();
|
||||
this.ignoreCase = column == null ? true : column.ignoreCase();
|
||||
this.number = type.isPrimitive() || Number.class.isAssignableFrom(type);
|
||||
this.string = CharSequence.class.isAssignableFrom(type);
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回null表示无需过滤该字段
|
||||
* <p>
|
||||
* @param bean
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public StringBuilder getFilterExpress(final T bean) {
|
||||
final F rs = attribute.get(bean);
|
||||
if (rs == null) return null;
|
||||
if (string && ((CharSequence) rs).length() == 0) return null;
|
||||
if (number && ((Number) rs).longValue() < this.least) return null;
|
||||
if (Range.class.isAssignableFrom(type)) return getRangeExpress((Range) rs);
|
||||
if (type.isArray() || Collection.class.isAssignableFrom(type)) {
|
||||
Object[] os;
|
||||
if (Collection.class.isAssignableFrom(type)) {
|
||||
os = ((Collection) rs).toArray();
|
||||
} else {
|
||||
final int len = Array.getLength(rs);
|
||||
if (len < 1) return null;
|
||||
if (type.getComponentType().isPrimitive()) {
|
||||
os = new Object[len];
|
||||
for (int i = 0; i < len; i++) {
|
||||
os[i] = Array.get(rs, i);
|
||||
}
|
||||
} else {
|
||||
os = (Object[]) rs;
|
||||
}
|
||||
}
|
||||
if (Range.class.isAssignableFrom(os[0].getClass())) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append('(');
|
||||
boolean flag = false;
|
||||
for (Object o : os) {
|
||||
if (flag) sb.append(' ').append(express.value()).append(' ');
|
||||
sb.append(getRangeExpress((Range) o));
|
||||
flag = true;
|
||||
}
|
||||
return sb.append(')');
|
||||
} else {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(aliasfield).append(' ').append(express.value()).append(" (");
|
||||
boolean flag = false;
|
||||
for (Object o : os) {
|
||||
if (flag) sb.append(',');
|
||||
sb.append(formatValue(o));
|
||||
flag = true;
|
||||
}
|
||||
return sb.append(')');
|
||||
}
|
||||
} else if (express == OPAND || express == OPOR) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append('(').append(aliasfield).append(' ').append(express.value()).append(' ').append(formatValue(rs)).append(" > 0)");
|
||||
return sb;
|
||||
} else if (express == OPANDNO) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append('(').append(aliasfield).append(' ').append(express.value()).append(' ').append(formatValue(rs)).append(" = 0)");
|
||||
return sb;
|
||||
} else {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(aliasfield).append(' ').append(express.value()).append(' ').append(formatValue(rs));
|
||||
return sb;
|
||||
}
|
||||
}
|
||||
|
||||
private String formatValue(Object rs) {
|
||||
if ((LIKE == express || NOTLIKE == express) && this.likefit) return formatToString("%" + rs + "%");
|
||||
return formatToString(rs);
|
||||
}
|
||||
|
||||
private StringBuilder getRangeExpress(Range range) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
return sb.append("(").append(aliasfield).append((NOTBETWEEN == express ? " NOT BETWEEN " : " BETWEEN "))
|
||||
.append(formatToString(range.getMin())).append(" AND ").append(formatToString(range.getMax())).append(")");
|
||||
}
|
||||
|
||||
private <E> Predicate<E> getRangePredicate(final Attribute<E, ?> attr, Range range) {
|
||||
final Comparable min = range.getMin();
|
||||
final Comparable max = range.getMax();
|
||||
Predicate<E> p = (E t) -> {
|
||||
Comparable rs = (Comparable) attr.get(t);
|
||||
if (rs == null) return false;
|
||||
if (min != null && min.compareTo(rs) >= 0) return false;
|
||||
return !(max != null && max.compareTo(rs) <= 0);
|
||||
};
|
||||
return (express == NOTBETWEEN) ? p.negate() : p;
|
||||
}
|
||||
|
||||
private <E> Predicate<E> getArrayPredicate(final Attribute<E, ?> attr, Object beanValue) {
|
||||
if (beanValue == null) return null;
|
||||
Predicate<E> p;
|
||||
if (type.isArray()) {
|
||||
if (Array.getLength(beanValue) == 0) return null;
|
||||
final Class comp = type.getComponentType();
|
||||
if (comp == int.class) {
|
||||
p = (E t) -> {
|
||||
Object rs = attr.get(t);
|
||||
if (rs == null) return false;
|
||||
return Arrays.binarySearch((int[]) beanValue, (int) rs) >= 0;
|
||||
};
|
||||
} else if (comp == long.class) {
|
||||
p = (E t) -> {
|
||||
Object rs = attr.get(t);
|
||||
if (rs == null) return false;
|
||||
return Arrays.binarySearch((long[]) beanValue, (long) rs) >= 0;
|
||||
};
|
||||
} else if (comp == short.class) {
|
||||
p = (E t) -> {
|
||||
Object rs = attr.get(t);
|
||||
if (rs == null) return false;
|
||||
return Arrays.binarySearch((short[]) beanValue, (short) rs) >= 0;
|
||||
};
|
||||
} else if (comp == float.class) {
|
||||
p = (E t) -> {
|
||||
Object rs = attr.get(t);
|
||||
if (rs == null) return false;
|
||||
return Arrays.binarySearch((float[]) beanValue, (float) rs) >= 0;
|
||||
};
|
||||
} else if (comp == double.class) {
|
||||
p = (E t) -> {
|
||||
Object rs = attr.get(t);
|
||||
if (rs == null) return false;
|
||||
return Arrays.binarySearch((double[]) beanValue, (double) rs) >= 0;
|
||||
};
|
||||
} else if (comp == byte.class) {
|
||||
p = (E t) -> {
|
||||
Object rs = attr.get(t);
|
||||
if (rs == null) return false;
|
||||
return Arrays.binarySearch((byte[]) beanValue, (byte) rs) >= 0;
|
||||
};
|
||||
} else if (comp == char.class) {
|
||||
p = (E t) -> {
|
||||
Object rs = attr.get(t);
|
||||
if (rs == null) return false;
|
||||
return Arrays.binarySearch((char[]) beanValue, (char) rs) >= 0;
|
||||
};
|
||||
} else {
|
||||
p = (E t) -> {
|
||||
Object rs = attr.get(t);
|
||||
if (rs == null) return false;
|
||||
return Arrays.binarySearch((Object[]) beanValue, rs) >= 0;
|
||||
};
|
||||
}
|
||||
} else { // Collection
|
||||
Collection collection = (Collection) beanValue;
|
||||
if (collection.isEmpty()) return null;
|
||||
p = (E t) -> {
|
||||
Object rs = attr.get(t);
|
||||
if (rs == null) return false;
|
||||
return collection.contains(rs);
|
||||
};
|
||||
}
|
||||
return p == null ? null : (express == NOTIN) ? p.negate() : p;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <E> Predicate<E> getFilterPredicate(EntityInfo<E> info, T bean) {
|
||||
return getFilterPredicate(info.getAttribute(field), bean);
|
||||
}
|
||||
|
||||
private <E> Predicate<E> getFilterPredicate(final Attribute<E, ?> attr, T bean) {
|
||||
final F beanValue = attribute.get(bean);
|
||||
if (beanValue == null) return null;
|
||||
if (string && ((CharSequence) beanValue).length() == 0) return null;
|
||||
if (number && ((Number) beanValue).longValue() < this.least) return null;
|
||||
if (Range.class.isAssignableFrom(type)) return getRangePredicate(attr, (Range) beanValue);
|
||||
if (type.isArray() || Collection.class.isAssignableFrom(type)) return getArrayPredicate(attr, (Range) beanValue);
|
||||
final long beanLongValue = number ? ((Number) beanValue).longValue() : 0L;
|
||||
switch (express) {
|
||||
case EQUAL: return (E t) -> beanValue.equals(attr.get(t));
|
||||
case NOTEQUAL: return (E t) -> !beanValue.equals(attr.get(t));
|
||||
case GREATERTHAN: return (E t) -> ((Number) attr.get(t)).longValue() > beanLongValue;
|
||||
case LESSTHAN: return (E t) -> ((Number) attr.get(t)).longValue() < beanLongValue;
|
||||
case GREATERTHANOREQUALTO: return (E t) -> ((Number) attr.get(t)).longValue() >= beanLongValue;
|
||||
case LESSTHANOREQUALTO: return (E t) -> ((Number) attr.get(t)).longValue() <= beanLongValue;
|
||||
case LIKE: if (!ignoreCase) return (E t) -> {
|
||||
Object rs = attr.get(t);
|
||||
return rs != null && rs.toString().contains(beanValue.toString());
|
||||
};
|
||||
String v1 = beanValue.toString().toLowerCase();
|
||||
return (E t) -> {
|
||||
Object rs = attr.get(t);
|
||||
return rs != null && rs.toString().toLowerCase().contains(v1);
|
||||
};
|
||||
case NOTLIKE: if (!ignoreCase) return (E t) -> {
|
||||
Object rs = attr.get(t);
|
||||
return rs == null || !rs.toString().contains(beanValue.toString());
|
||||
};
|
||||
String v2 = beanValue.toString().toLowerCase();
|
||||
return (E t) -> {
|
||||
Object rs = attr.get(t);
|
||||
return rs == null || !rs.toString().toLowerCase().contains(v2);
|
||||
};
|
||||
case ISNULL: return (E t) -> attr.get(t) == null;
|
||||
case ISNOTNULL: return (E t) -> attr.get(t) != null;
|
||||
case OPAND: return (E t) -> (((Number) attr.get(t)).longValue() & beanLongValue) > 0;
|
||||
case OPOR: return (E t) -> (((Number) attr.get(t)).longValue() | beanLongValue) > 0;
|
||||
case OPANDNO: return (E t) -> (((Number) attr.get(t)).longValue() & beanLongValue) == 0;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static interface FilterExpressNode<T> {
|
||||
|
||||
public StringBuilder getFilterExpress(final T bean);
|
||||
|
||||
public <E> Predicate<E> getFilterPredicate(final EntityInfo<E> info, final T bean);
|
||||
}
|
||||
}
|
||||
@@ -1,207 +1,207 @@
|
||||
/*
|
||||
* To change this template, choose Tools | Templates
|
||||
* and open the template in the editor.
|
||||
*/
|
||||
package com.wentch.redkale.util;
|
||||
|
||||
import java.beans.ConstructorProperties;
|
||||
import java.lang.reflect.*;
|
||||
import java.util.*;
|
||||
import jdk.internal.org.objectweb.asm.*;
|
||||
import static jdk.internal.org.objectweb.asm.Opcodes.*;
|
||||
import jdk.internal.org.objectweb.asm.Type;
|
||||
|
||||
/**
|
||||
* 实现一个类的构造方法。 代替低效的反射实现方式。
|
||||
*
|
||||
* @author zhangjx
|
||||
* @param <T>
|
||||
*/
|
||||
public interface Creator<T> {
|
||||
//
|
||||
// static class PooledCreator<T> implements Creator<T> {
|
||||
//
|
||||
// private final T defValue;
|
||||
//
|
||||
// private final Reproduce<T, T> reproduce;
|
||||
//
|
||||
// private final ReferenceQueue<T> refQueue = new ReferenceQueue();
|
||||
//
|
||||
// private final Queue<T> queue;
|
||||
//
|
||||
// private final Creator<T> creator;
|
||||
//
|
||||
// public PooledCreator(int max, Class<T> clazz, Creator<T> creator) {
|
||||
// this.creator = creator;
|
||||
// this.defValue = creator.create();
|
||||
// this.reproduce = Reproduce.create(clazz, clazz);
|
||||
// this.queue = new ArrayBlockingQueue<>(Math.max(Runtime.getRuntime().availableProcessors() * 2, max));
|
||||
// new Thread() {
|
||||
// {
|
||||
// setDaemon(true);
|
||||
// setName(PooledCreator.class.getSimpleName() + " " + clazz.getSimpleName() + " Reference Handler");
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public void run() {
|
||||
// try {
|
||||
// for (;;) {
|
||||
// T r = refQueue.remove().get();
|
||||
// if (r == null) continue;
|
||||
// reproduce.copy(r, defValue);
|
||||
// queue.offer(r);
|
||||
// }
|
||||
// } catch (Exception e) {
|
||||
// //do nothind
|
||||
// }
|
||||
// }
|
||||
// }.start();
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public T create(Object... params) {
|
||||
// T rs = queue.poll();
|
||||
// if (rs == null) {
|
||||
// rs = creator.create(params);
|
||||
// }
|
||||
// return new WeakReference<>(rs, refQueue).get();
|
||||
// }
|
||||
//
|
||||
// }
|
||||
//
|
||||
// @SuppressWarnings("unchecked")
|
||||
// public static <T> Creator<T> create(int max, Class<T> clazz) {
|
||||
// return new PooledCreator<>(max, clazz, create(clazz));
|
||||
// }
|
||||
//
|
||||
// @SuppressWarnings("unchecked")
|
||||
// public static <T> Creator<T> create(int max, Class<T> clazz, Creator<T> creator) {
|
||||
// return new PooledCreator<>(max, clazz, creator);
|
||||
// }
|
||||
|
||||
public T create(Object... params);
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> Creator<T> create(Class<T> clazz) {
|
||||
if (clazz.isAssignableFrom(ArrayList.class)) {
|
||||
clazz = (Class<T>) ArrayList.class;
|
||||
} else if (clazz.isAssignableFrom(HashMap.class)) {
|
||||
clazz = (Class<T>) HashMap.class;
|
||||
} else if (clazz.isAssignableFrom(HashSet.class)) {
|
||||
clazz = (Class<T>) HashSet.class;
|
||||
}
|
||||
if (clazz.isInterface() || Modifier.isAbstract(clazz.getModifiers())) {
|
||||
throw new RuntimeException("[" + clazz + "] is a interface or abstract class, cannot create it's Creator.");
|
||||
}
|
||||
final String supDynName = Creator.class.getName().replace('.', '/');
|
||||
final String interName = clazz.getName().replace('.', '/');
|
||||
final String interDesc = Type.getDescriptor(clazz);
|
||||
ClassLoader loader = Creator.class.getClassLoader();
|
||||
String newDynName = supDynName + "_" + clazz.getSimpleName() + "_" + (System.currentTimeMillis() % 10000);
|
||||
if (String.class.getClassLoader() != clazz.getClassLoader()) {
|
||||
loader = clazz.getClassLoader();
|
||||
newDynName = interName + "_Dyn" + Creator.class.getSimpleName();
|
||||
}
|
||||
try {
|
||||
return (Creator) Class.forName(newDynName.replace('/', '.')).newInstance();
|
||||
} catch (Exception ex) {
|
||||
}
|
||||
Constructor<T> constructor = null;
|
||||
for (Constructor c : clazz.getConstructors()) {
|
||||
if (c.getParameterCount() == 0) {
|
||||
constructor = c;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (constructor == null) {
|
||||
for (Constructor c : clazz.getConstructors()) {
|
||||
if (c.getAnnotation(ConstructorProperties.class) != null) {
|
||||
constructor = c;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (constructor == null) throw new RuntimeException("[" + clazz + "] have no public or java.beans.ConstructorProperties-Annotation constructor.");
|
||||
//-------------------------------------------------------------
|
||||
ClassWriter cw = new ClassWriter(0);
|
||||
FieldVisitor fv;
|
||||
MethodVisitor mv;
|
||||
AnnotationVisitor av0;
|
||||
cw.visit(V1_8, ACC_PUBLIC + ACC_FINAL + ACC_SUPER, newDynName, "Ljava/lang/Object;L" + supDynName + "<" + interDesc + ">;", "java/lang/Object", new String[]{supDynName});
|
||||
|
||||
{//构造方法
|
||||
mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
|
||||
ConstructorProperties cps = constructor.getAnnotation(ConstructorProperties.class);
|
||||
if (cps != null) {
|
||||
av0 = mv.visitAnnotation("Ljava/beans/ConstructorProperties;", true);
|
||||
AnnotationVisitor av1 = av0.visitArray("value");
|
||||
for (String n : cps.value()) {
|
||||
av1.visit(null, n);
|
||||
}
|
||||
av1.visitEnd();
|
||||
av0.visitEnd();
|
||||
}
|
||||
mv.visitVarInsn(ALOAD, 0);
|
||||
mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
|
||||
mv.visitInsn(RETURN);
|
||||
mv.visitMaxs(1, 1);
|
||||
mv.visitEnd();
|
||||
}
|
||||
{//create 方法
|
||||
mv = cw.visitMethod(ACC_PUBLIC + ACC_VARARGS, "create", "([Ljava/lang/Object;)L" + interName + ";", null, null);
|
||||
mv.visitTypeInsn(NEW, interName);
|
||||
mv.visitInsn(DUP);
|
||||
//---------------------------------------
|
||||
{
|
||||
Parameter[] params = constructor.getParameters();
|
||||
final int[] iconsts = {ICONST_0, ICONST_1, ICONST_2, ICONST_3, ICONST_4, ICONST_5};
|
||||
for (int i = 0; i < params.length; i++) {
|
||||
mv.visitVarInsn(ALOAD, 1);
|
||||
if (i < 6) {
|
||||
mv.visitInsn(iconsts[i]);
|
||||
} else {
|
||||
mv.visitIntInsn(BIPUSH, i);
|
||||
}
|
||||
mv.visitInsn(AALOAD);
|
||||
Class ct = params[i].getType();
|
||||
mv.visitTypeInsn(CHECKCAST, Type.getInternalName(ct));
|
||||
if (ct.isPrimitive()) {
|
||||
Class fct = Array.get(Array.newInstance(ct, 1), 0).getClass();
|
||||
try {
|
||||
Method pm = ct.getMethod(ct.getSimpleName() + "Value");
|
||||
mv.visitMethodInsn(INVOKEVIRTUAL, fct.getName().replace('.', '/'), pm.getName(), Type.getMethodDescriptor(pm), false);
|
||||
} catch (Exception ex) {
|
||||
throw new RuntimeException(ex); //不可能会发生
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//---------------------------------------
|
||||
mv.visitMethodInsn(INVOKESPECIAL, interName, "<init>", Type.getConstructorDescriptor(constructor), false);
|
||||
mv.visitInsn(ARETURN);
|
||||
mv.visitMaxs((constructor.getParameterCount() > 0 ? (constructor.getParameterCount() + 3) : 2), 2);
|
||||
mv.visitEnd();
|
||||
}
|
||||
{ //虚拟 create 方法
|
||||
mv = cw.visitMethod(ACC_PUBLIC + ACC_BRIDGE + ACC_VARARGS + ACC_SYNTHETIC, "create", "([Ljava/lang/Object;)Ljava/lang/Object;", null, null);
|
||||
mv.visitVarInsn(ALOAD, 0);
|
||||
mv.visitVarInsn(ALOAD, 1);
|
||||
mv.visitMethodInsn(INVOKEVIRTUAL, newDynName, "create", "([Ljava/lang/Object;)" + interDesc, false);
|
||||
mv.visitInsn(ARETURN);
|
||||
mv.visitMaxs(2, 2);
|
||||
mv.visitEnd();
|
||||
}
|
||||
cw.visitEnd();
|
||||
byte[] bytes = cw.toByteArray();
|
||||
Class<?> creatorClazz = new ClassLoader(loader) {
|
||||
public final Class<?> loadClass(String name, byte[] b) {
|
||||
return defineClass(name, b, 0, b.length);
|
||||
}
|
||||
}.loadClass(newDynName.replace('/', '.'), bytes);
|
||||
try {
|
||||
return (Creator) creatorClazz.newInstance();
|
||||
} catch (Exception ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
* To change this template, choose Tools | Templates
|
||||
* and open the template in the editor.
|
||||
*/
|
||||
package com.wentch.redkale.util;
|
||||
|
||||
import java.beans.ConstructorProperties;
|
||||
import java.lang.reflect.*;
|
||||
import java.util.*;
|
||||
import jdk.internal.org.objectweb.asm.*;
|
||||
import static jdk.internal.org.objectweb.asm.Opcodes.*;
|
||||
import jdk.internal.org.objectweb.asm.Type;
|
||||
|
||||
/**
|
||||
* 实现一个类的构造方法。 代替低效的反射实现方式。
|
||||
*
|
||||
* @author zhangjx
|
||||
* @param <T>
|
||||
*/
|
||||
public interface Creator<T> {
|
||||
//
|
||||
// static class PooledCreator<T> implements Creator<T> {
|
||||
//
|
||||
// private final T defValue;
|
||||
//
|
||||
// private final Reproduce<T, T> reproduce;
|
||||
//
|
||||
// private final ReferenceQueue<T> refQueue = new ReferenceQueue();
|
||||
//
|
||||
// private final Queue<T> queue;
|
||||
//
|
||||
// private final Creator<T> creator;
|
||||
//
|
||||
// public PooledCreator(int max, Class<T> clazz, Creator<T> creator) {
|
||||
// this.creator = creator;
|
||||
// this.defValue = creator.create();
|
||||
// this.reproduce = Reproduce.create(clazz, clazz);
|
||||
// this.queue = new ArrayBlockingQueue<>(Math.max(Runtime.getRuntime().availableProcessors() * 2, max));
|
||||
// new Thread() {
|
||||
// {
|
||||
// setDaemon(true);
|
||||
// setName(PooledCreator.class.getSimpleName() + " " + clazz.getSimpleName() + " Reference Handler");
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public void run() {
|
||||
// try {
|
||||
// for (;;) {
|
||||
// T r = refQueue.remove().get();
|
||||
// if (r == null) continue;
|
||||
// reproduce.copy(r, defValue);
|
||||
// queue.offer(r);
|
||||
// }
|
||||
// } catch (Exception e) {
|
||||
// //do nothind
|
||||
// }
|
||||
// }
|
||||
// }.start();
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public T create(Object... params) {
|
||||
// T rs = queue.poll();
|
||||
// if (rs == null) {
|
||||
// rs = creator.create(params);
|
||||
// }
|
||||
// return new WeakReference<>(rs, refQueue).get();
|
||||
// }
|
||||
//
|
||||
// }
|
||||
//
|
||||
// @SuppressWarnings("unchecked")
|
||||
// public static <T> Creator<T> create(int max, Class<T> clazz) {
|
||||
// return new PooledCreator<>(max, clazz, create(clazz));
|
||||
// }
|
||||
//
|
||||
// @SuppressWarnings("unchecked")
|
||||
// public static <T> Creator<T> create(int max, Class<T> clazz, Creator<T> creator) {
|
||||
// return new PooledCreator<>(max, clazz, creator);
|
||||
// }
|
||||
|
||||
public T create(Object... params);
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> Creator<T> create(Class<T> clazz) {
|
||||
if (clazz.isAssignableFrom(ArrayList.class)) {
|
||||
clazz = (Class<T>) ArrayList.class;
|
||||
} else if (clazz.isAssignableFrom(HashMap.class)) {
|
||||
clazz = (Class<T>) HashMap.class;
|
||||
} else if (clazz.isAssignableFrom(HashSet.class)) {
|
||||
clazz = (Class<T>) HashSet.class;
|
||||
}
|
||||
if (clazz.isInterface() || Modifier.isAbstract(clazz.getModifiers())) {
|
||||
throw new RuntimeException("[" + clazz + "] is a interface or abstract class, cannot create it's Creator.");
|
||||
}
|
||||
final String supDynName = Creator.class.getName().replace('.', '/');
|
||||
final String interName = clazz.getName().replace('.', '/');
|
||||
final String interDesc = Type.getDescriptor(clazz);
|
||||
ClassLoader loader = Creator.class.getClassLoader();
|
||||
String newDynName = supDynName + "_" + clazz.getSimpleName() + "_" + (System.currentTimeMillis() % 10000);
|
||||
if (String.class.getClassLoader() != clazz.getClassLoader()) {
|
||||
loader = clazz.getClassLoader();
|
||||
newDynName = interName + "_Dyn" + Creator.class.getSimpleName();
|
||||
}
|
||||
try {
|
||||
return (Creator) Class.forName(newDynName.replace('/', '.')).newInstance();
|
||||
} catch (Exception ex) {
|
||||
}
|
||||
Constructor<T> constructor = null;
|
||||
for (Constructor c : clazz.getConstructors()) {
|
||||
if (c.getParameterCount() == 0) {
|
||||
constructor = c;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (constructor == null) {
|
||||
for (Constructor c : clazz.getConstructors()) {
|
||||
if (c.getAnnotation(ConstructorProperties.class) != null) {
|
||||
constructor = c;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (constructor == null) throw new RuntimeException("[" + clazz + "] have no public or java.beans.ConstructorProperties-Annotation constructor.");
|
||||
//-------------------------------------------------------------
|
||||
ClassWriter cw = new ClassWriter(0);
|
||||
FieldVisitor fv;
|
||||
MethodVisitor mv;
|
||||
AnnotationVisitor av0;
|
||||
cw.visit(V1_8, ACC_PUBLIC + ACC_FINAL + ACC_SUPER, newDynName, "Ljava/lang/Object;L" + supDynName + "<" + interDesc + ">;", "java/lang/Object", new String[]{supDynName});
|
||||
|
||||
{//构造方法
|
||||
mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
|
||||
ConstructorProperties cps = constructor.getAnnotation(ConstructorProperties.class);
|
||||
if (cps != null) {
|
||||
av0 = mv.visitAnnotation("Ljava/beans/ConstructorProperties;", true);
|
||||
AnnotationVisitor av1 = av0.visitArray("value");
|
||||
for (String n : cps.value()) {
|
||||
av1.visit(null, n);
|
||||
}
|
||||
av1.visitEnd();
|
||||
av0.visitEnd();
|
||||
}
|
||||
mv.visitVarInsn(ALOAD, 0);
|
||||
mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
|
||||
mv.visitInsn(RETURN);
|
||||
mv.visitMaxs(1, 1);
|
||||
mv.visitEnd();
|
||||
}
|
||||
{//create 方法
|
||||
mv = cw.visitMethod(ACC_PUBLIC + ACC_VARARGS, "create", "([Ljava/lang/Object;)L" + interName + ";", null, null);
|
||||
mv.visitTypeInsn(NEW, interName);
|
||||
mv.visitInsn(DUP);
|
||||
//---------------------------------------
|
||||
{
|
||||
Parameter[] params = constructor.getParameters();
|
||||
final int[] iconsts = {ICONST_0, ICONST_1, ICONST_2, ICONST_3, ICONST_4, ICONST_5};
|
||||
for (int i = 0; i < params.length; i++) {
|
||||
mv.visitVarInsn(ALOAD, 1);
|
||||
if (i < 6) {
|
||||
mv.visitInsn(iconsts[i]);
|
||||
} else {
|
||||
mv.visitIntInsn(BIPUSH, i);
|
||||
}
|
||||
mv.visitInsn(AALOAD);
|
||||
Class ct = params[i].getType();
|
||||
mv.visitTypeInsn(CHECKCAST, Type.getInternalName(ct));
|
||||
if (ct.isPrimitive()) {
|
||||
Class fct = Array.get(Array.newInstance(ct, 1), 0).getClass();
|
||||
try {
|
||||
Method pm = ct.getMethod(ct.getSimpleName() + "Value");
|
||||
mv.visitMethodInsn(INVOKEVIRTUAL, fct.getName().replace('.', '/'), pm.getName(), Type.getMethodDescriptor(pm), false);
|
||||
} catch (Exception ex) {
|
||||
throw new RuntimeException(ex); //不可能会发生
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//---------------------------------------
|
||||
mv.visitMethodInsn(INVOKESPECIAL, interName, "<init>", Type.getConstructorDescriptor(constructor), false);
|
||||
mv.visitInsn(ARETURN);
|
||||
mv.visitMaxs((constructor.getParameterCount() > 0 ? (constructor.getParameterCount() + 3) : 2), 2);
|
||||
mv.visitEnd();
|
||||
}
|
||||
{ //虚拟 create 方法
|
||||
mv = cw.visitMethod(ACC_PUBLIC + ACC_BRIDGE + ACC_VARARGS + ACC_SYNTHETIC, "create", "([Ljava/lang/Object;)Ljava/lang/Object;", null, null);
|
||||
mv.visitVarInsn(ALOAD, 0);
|
||||
mv.visitVarInsn(ALOAD, 1);
|
||||
mv.visitMethodInsn(INVOKEVIRTUAL, newDynName, "create", "([Ljava/lang/Object;)" + interDesc, false);
|
||||
mv.visitInsn(ARETURN);
|
||||
mv.visitMaxs(2, 2);
|
||||
mv.visitEnd();
|
||||
}
|
||||
cw.visitEnd();
|
||||
byte[] bytes = cw.toByteArray();
|
||||
Class<?> creatorClazz = new ClassLoader(loader) {
|
||||
public final Class<?> loadClass(String name, byte[] b) {
|
||||
return defineClass(name, b, 0, b.length);
|
||||
}
|
||||
}.loadClass(newDynName.replace('/', '.'), bytes);
|
||||
try {
|
||||
return (Creator) creatorClazz.newInstance();
|
||||
} catch (Exception ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,11 +32,6 @@ public final class DLong extends Number implements Comparable<DLong> {
|
||||
return this.first == one && this.second == two;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return intValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == null) return false;
|
||||
@@ -45,6 +40,14 @@ public final class DLong extends Number implements Comparable<DLong> {
|
||||
return (this.first == other.first && this.second == other.second);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int hash = 7;
|
||||
hash = 89 * hash + (int) (this.first ^ (this.first >>> 32));
|
||||
hash = 89 * hash + (int) (this.second ^ (this.second >>> 32));
|
||||
return hash;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.first + "_" + this.second;
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
*/
|
||||
package com.wentch.redkale.util;
|
||||
|
||||
import java.util.*;
|
||||
import jdk.internal.org.objectweb.asm.*;
|
||||
|
||||
/**
|
||||
@@ -21,6 +22,8 @@ public class DebugMethodVisitor {
|
||||
debug = d;
|
||||
}
|
||||
|
||||
private final Map<Label, Integer> labels = new LinkedHashMap<>();
|
||||
|
||||
private static final String[] opcodes = new String[200]; //0 -18
|
||||
|
||||
static {
|
||||
@@ -46,6 +49,12 @@ public class DebugMethodVisitor {
|
||||
this.visitor = visitor;
|
||||
}
|
||||
|
||||
public AnnotationVisitor visitAnnotation(String desc, boolean flag) {
|
||||
AnnotationVisitor av = visitor.visitAnnotation(desc, flag);
|
||||
if (debug) System.out.println("mv.visitAnnotation(\"" + desc + "\", " + flag + ");");
|
||||
return av;
|
||||
}
|
||||
|
||||
public void visitParameter(String name, int access) {
|
||||
visitor.visitParameter(name, access);
|
||||
if (debug) System.out.println("mv.visitParameter(" + name + ", " + access + ");");
|
||||
@@ -56,6 +65,37 @@ public class DebugMethodVisitor {
|
||||
if (debug) System.out.println("mv.visitVarInsn(" + opcodes[opcode] + ", " + var + ");");
|
||||
}
|
||||
|
||||
public void visitJumpInsn(int opcode, Label var) {
|
||||
visitor.visitJumpInsn(opcode, var);
|
||||
if (debug) {
|
||||
Integer index = labels.get(var);
|
||||
if (index == null) {
|
||||
index = labels.size();
|
||||
labels.put(var, index);
|
||||
System.out.println("Label l" + index + " = new Label();");
|
||||
}
|
||||
System.out.println("mv.visitJumpInsn(" + opcodes[opcode] + ", l" + index + ");");
|
||||
}
|
||||
}
|
||||
|
||||
public void visitCode() {
|
||||
visitor.visitCode();
|
||||
if (debug) System.out.println("mv.visitCode();");
|
||||
}
|
||||
|
||||
public void visitLabel(Label var) {
|
||||
visitor.visitLabel(var);
|
||||
if (debug) {
|
||||
Integer index = labels.get(var);
|
||||
if (index == null) {
|
||||
index = labels.size();
|
||||
labels.put(var, index);
|
||||
System.out.println("Label l" + index + " = new Label();");
|
||||
}
|
||||
System.out.println("mv.visitLabel(l" + index + ");");
|
||||
}
|
||||
}
|
||||
|
||||
public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) {
|
||||
visitor.visitMethodInsn(opcode, owner, name, desc, itf);
|
||||
if (debug) System.out.println("mv.visitMethodInsn(" + opcodes[opcode] + ", \"" + owner + "\", \"" + name + "\", \"" + desc + "\", " + itf + ");");
|
||||
@@ -81,6 +121,11 @@ public class DebugMethodVisitor {
|
||||
if (debug) System.out.println("mv.visitIntInsn(" + opcodes[opcode] + ", " + value + ");");
|
||||
}
|
||||
|
||||
public void visitIincInsn(int opcode, int value) {
|
||||
visitor.visitIincInsn(opcode, value);
|
||||
if (debug) System.out.println("mv.visitIincInsn(" + opcode + ", " + value + ");");
|
||||
}
|
||||
|
||||
public void visitLdcInsn(Object o) {
|
||||
visitor.visitLdcInsn(o);
|
||||
if (debug) System.out.println("mv.visitLdcInsn(" + o + ");");
|
||||
|
||||
@@ -3,12 +3,13 @@
|
||||
* To change this template file, choose Tools | Templates
|
||||
* and open the template in the editor.
|
||||
*/
|
||||
package com.wentch.redkale.service;
|
||||
package com.wentch.redkale.util;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author zhangjx
|
||||
*/
|
||||
public interface MultiService extends Service {
|
||||
public interface Nameable {
|
||||
|
||||
String name();
|
||||
}
|
||||
@@ -5,13 +5,6 @@ import java.util.function.Predicate;
|
||||
import static jdk.internal.org.objectweb.asm.Opcodes.*;
|
||||
import jdk.internal.org.objectweb.asm.*;
|
||||
|
||||
/**
|
||||
* 该类提供对象拷贝, 两对象存在相同的getter、setter的字段值会被拷贝
|
||||
*
|
||||
* @author zhangjx
|
||||
* @param <D>
|
||||
* @param <S>
|
||||
*/
|
||||
public interface Reproduce<D, S> {
|
||||
|
||||
public D copy(D dest, S src);
|
||||
|
||||
@@ -13,12 +13,15 @@ import java.util.regex.*;
|
||||
import javax.annotation.*;
|
||||
|
||||
/**
|
||||
* 如果Resource(name = "$") 表示资源name采用所属对象的name
|
||||
*
|
||||
* @author zhangjx
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public final class ResourceFactory {
|
||||
|
||||
public static final String RESOURCE_PARENT_NAME = "$";
|
||||
|
||||
private static final Logger logger = Logger.getLogger(ResourceFactory.class.getSimpleName());
|
||||
|
||||
private final ResourceFactory parent;
|
||||
@@ -170,15 +173,16 @@ public final class ResourceFactory {
|
||||
continue;
|
||||
}
|
||||
if (Modifier.isFinal(field.getModifiers())) continue;
|
||||
Object rs = genctype == classtype ? null : find(rc.name(), genctype);
|
||||
final String rcname = (rc.name().equals(RESOURCE_PARENT_NAME) && src instanceof Nameable) ? ((Nameable) src).name() : rc.name();
|
||||
Object rs = genctype == classtype ? null : find(rcname, genctype);
|
||||
if (rs == null) {
|
||||
if (Map.class.isAssignableFrom(classtype)) {
|
||||
rs = find(Pattern.compile(rc.name().isEmpty() ? ".+" : rc.name()), (Class) ((ParameterizedType) field.getGenericType()).getActualTypeArguments()[1], src);
|
||||
rs = find(Pattern.compile(rcname.isEmpty() ? ".*" : rcname), (Class) ((ParameterizedType) field.getGenericType()).getActualTypeArguments()[1], src);
|
||||
} else {
|
||||
if (rc.name().startsWith("property.")) {
|
||||
rs = find(rc.name(), String.class);
|
||||
if (rcname.startsWith("property.")) {
|
||||
rs = find(rcname, String.class);
|
||||
} else {
|
||||
rs = find(rc.name(), classtype);
|
||||
rs = find(rcname, classtype);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -324,6 +324,17 @@ public final class Utility {
|
||||
return size;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将两个数字组装成一个long
|
||||
* <p>
|
||||
* @param high
|
||||
* @param low
|
||||
* @return
|
||||
*/
|
||||
public static long merge(long high, long low) {
|
||||
return high << 32 | low;
|
||||
}
|
||||
|
||||
public static ByteBuffer encodeUTF8(final ByteBuffer buffer, final char[] text, final int start, final int len) {
|
||||
return encodeUTF8(buffer, encodeUTF8Length(text, start, len), text, start, len);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user