This commit is contained in:
地平线
2015-08-07 19:39:19 +08:00
parent 1da1422d2d
commit a50b904acf
58 changed files with 2411 additions and 2309 deletions

View File

@@ -1,8 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<!-- <!--
${APP_HOME} 指当前程序的总目录APP_HOME 文件说明:
node: 进程节点的名称, 默认为空 ${APP_HOME} 指当前程序的总目录APP_HOME
required 被声明required的属性值不能为空 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的端口用于关闭或者与监管系统进行数据交互 port: required 程序的管理Server的端口用于关闭或者与监管系统进行数据交互
host: 程序的管理Server的地址; 默认为127.0.0.1。 host: 程序的管理Server的地址; 默认为127.0.0.1。
lib: 加上额外的lib路径,多个路径用分号;隔开; 默认为空。 例如: ${APP_HOME}/lib/a.jar;${APP_HOME}/lib2/b.jar; lib: 加上额外的lib路径,多个路径用分号;隔开; 默认为空。 例如: ${APP_HOME}/lib/a.jar;${APP_HOME}/lib2/b.jar;
@@ -11,6 +27,28 @@
<!-- 所有服务所需的资源 --> <!-- 所有服务所需的资源 -->
<resources> <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不区分大小写 远程client地址组资源. 注意: remote的name值不能为LOCAL不区分大小写
protocol 值只能是UDP TCP 默认UDP protocol 值只能是UDP TCP 默认UDP
@@ -48,13 +86,14 @@
protocol: required server所启动的协议有HTTP、SNCP 目前只支持HTTP、SNCP。SNCP也分TCP、UDP实现默认使用UDP实现TCP实现则使用SNCP.TCP值; protocol: required server所启动的协议有HTTP、SNCP 目前只支持HTTP、SNCP。SNCP也分TCP、UDP实现默认使用UDP实现TCP实现则使用SNCP.TCP值;
host: 服务所占address 默认: 0.0.0.0 host: 服务所占address 默认: 0.0.0.0
port: 服务所占端口 ,默认: 80 port: 服务所占端口 ,默认: 80
group: 所属组的节点,多个节点值用;隔开如果配置文件中存在多个SNCP协议的Server节点需要显式指定group属性
root: 如果是web类型服务则包含页面 默认:{APP_HOME}/root root: 如果是web类型服务则包含页面 默认:{APP_HOME}/root
lib: server额外的class目录 默认为空 lib: server额外的class目录 默认为空
charset: 文本编码, 默认: UTF-8 charset: 文本编码, 默认: UTF-8
backlog: 默认10K backlog: 默认10K
threads 线程总数, 默认: CPU核数*16 threads 线程总数, 默认: CPU核数*16
maxbody: request.body最大值 默认: 64K maxbody: request.body最大值 默认: 64K
capacity: ByteBuffer的初始化大小 默认: 8K capacity: ByteBuffer的初始化大小 默认: 8K; 如果是HTTP协议则默认: 16K + 8B (HTTP 2.0)
bufferPoolSize ByteBuffer池的大小默认: CPU核数*512 bufferPoolSize ByteBuffer池的大小默认: CPU核数*512
responsePoolSize Response池的大小默认: CPU核数*256 responsePoolSize Response池的大小默认: CPU核数*256
readTimeoutSecond: 读操作超时秒数, 默认0 表示永久不超时 readTimeoutSecond: 读操作超时秒数, 默认0 表示永久不超时
@@ -72,16 +111,13 @@
autoload="false" 需要显著的指定Service类 autoload="false" 需要显著的指定Service类
includes 当autoload="true" 拉取类名与includes中的正则表达式匹配的类, 多个正则表达式用分号;隔开 includes 当autoload="true" 拉取类名与includes中的正则表达式匹配的类, 多个正则表达式用分号;隔开
excludes 当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节点来方便配置。 大部分的情况下, 存在多个节点环境中很多service节点配置都一致为此提供group节点来方便配置。
remotenames: 远程模式Service的name名称集合 多个用分号;隔开, 名称必须是在resources节点中定义的remote节点。 remotenames: 远程模式Service的name名称集合 多个用分号;隔开, 名称必须是在resources节点中定义的remote节点。
以下group节点例子等价于: 以下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="RT001" remote="RT001"/>
<service value="com.xxx.XXX0Service" name="RT002" remote="RT002"/> <service value="com.xxx.XXX0Service" name="RT002" remote="RT002"/>
<service value="com.xxx.XXX0Service" name="RT003" remote="RT003"/> <service value="com.xxx.XXX0Service" name="RT003" remote="RT003"/>

View File

@@ -5,7 +5,6 @@
*/ */
package com.wentch.redkale.boot; 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.bson.*;
import com.wentch.redkale.convert.json.*; import com.wentch.redkale.convert.json.*;
import com.wentch.redkale.net.*; 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.util.AnyValue.DefaultAnyValue;
import com.wentch.redkale.watch.*; import com.wentch.redkale.watch.*;
import java.io.*; import java.io.*;
import java.lang.reflect.*;
import java.net.*; import java.net.*;
import java.nio.*; import java.nio.*;
import java.nio.channels.*; import java.nio.channels.*;
import java.nio.file.*; import java.nio.file.*;
import java.util.*; import java.util.*;
import java.util.AbstractMap.SimpleEntry;
import java.util.concurrent.*; import java.util.concurrent.*;
import java.util.logging.*; import java.util.logging.*;
import javax.annotation.*;
import javax.xml.parsers.*; import javax.xml.parsers.*;
import org.w3c.dom.*; import org.w3c.dom.*;
/** /**
* 编译时需要加入: -XDignore.symbol.file=true * 编译时需要加入: -XDignore.symbol.file=true
* <p> * <p>
* 进程启动类程序启动后读取application.xml,进行classpath扫描动态加载Service与Servlet 再进行Service、Servlet与其他资源之间的依赖注入。 * 进程启动类程序启动后读取application.xml,进行classpath扫描动态加载Service与Servlet
* 优先加载所有SNCP协议的服务 再加载其他协议服务,
* 最后进行Service、Servlet与其他资源之间的依赖注入。
*
* *
* @author zhangjx * @author zhangjx
*/ */
@@ -45,18 +44,6 @@ public final class Application {
//当前进程的根目录, 类型String //当前进程的根目录, 类型String
public static final String RESNAME_HOME = "APP_HOME"; 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 //当前进程节点的IP地址 类型InetAddress、String
public static final String RESNAME_ADDR = "APP_ADDR"; public static final String RESNAME_ADDR = "APP_ADDR";
@@ -67,17 +54,19 @@ public final class Application {
protected final WatchFactory watch = WatchFactory.root(); 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; private File home;
@@ -86,10 +75,6 @@ public final class Application {
private final AnyValue config; 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 final long startTime = System.currentTimeMillis();
private CountDownLatch serverscdl; private CountDownLatch serverscdl;
@@ -106,16 +91,12 @@ public final class Application {
} catch (IOException e) { } catch (IOException e) {
throw new RuntimeException(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"); 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()) { if (logconf.isFile() && logconf.canRead()) {
try { try {
final String rootpath = root.getCanonicalPath().replace('\\', '/'); 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.bson.writer.buffer.defsize", "4096");
System.setProperty("convert.json.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(this.home, "conf/persistence.xml");
File persist = new File(root, "conf/persistence.xml"); final String homepath = this.home.getCanonicalPath();
if (persist.isFile()) System.setProperty(DataDefaultSource.DATASOURCE_CONFPATH, persist.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()); logger.log(Level.INFO, RESNAME_HOME + "=" + homepath + "\r\n" + RESNAME_ADDR + "=" + this.localAddress.getHostAddress());
String lib = config.getValue("lib", "").trim().replace("${APP_HOME}", root.getCanonicalPath()); String lib = config.getValue("lib", "").trim().replace("${APP_HOME}", homepath);
lib = lib.isEmpty() ? (root.getCanonicalPath() + "/conf") : (lib + ";" + root.getCanonicalPath() + "/conf"); lib = lib.isEmpty() ? (homepath + "/conf") : (lib + ";" + homepath + "/conf");
Server.loadLib(logger, lib); Server.loadLib(logger, lib);
initLogging(); initLogging();
InetAddress addr = Utility.localInetAddress(); if (this.localAddress != null) {
if (addr != null) { byte[] bs = this.localAddress.getAddress();
byte[] bs = addr.getAddress();
int v = (0xff & bs[bs.length - 2]) % 10 * 100 + (0xff & bs[bs.length - 1]); int v = (0xff & bs[bs.length - 2]) % 10 * 100 + (0xff & bs[bs.length - 1]);
this.factory.register("property.datasource.nodeid", "" + v); this.factory.register("property.datasource.nodeid", "" + v);
} }
@@ -232,14 +212,25 @@ public final class Application {
initResources(); initResources();
} }
@Deprecated
public static void singleton(Service service) throws Exception { public static void singleton(Service service) throws Exception {
final Application application = Application.create(); final Application application = Application.create();
application.init(); application.init();
application.factory.register(service); 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); 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 { private static Application create() throws IOException {
final String home = new File(System.getProperty(RESNAME_HOME, "")).getCanonicalPath(); final String home = new File(System.getProperty(RESNAME_HOME, "")).getCanonicalPath();
System.setProperty(RESNAME_HOME, home); System.setProperty(RESNAME_HOME, home);
@@ -257,7 +248,12 @@ public final class Application {
} }
application.init(); application.init();
application.startSelfServer(); 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); System.exit(0);
} }
@@ -336,20 +332,38 @@ public final class Application {
final AnyValue[] entrys = config.getAnyValues("server"); final AnyValue[] entrys = config.getAnyValues("server");
this.serverscdl = new CountDownLatch(entrys.length + 1); this.serverscdl = new CountDownLatch(entrys.length + 1);
CountDownLatch timecd = new CountDownLatch(entrys.length); 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(); timecd.await();
logger.info(this.getClass().getSimpleName() + " started in " + (System.currentTimeMillis() - startTime) + " ms"); logger.info(this.getClass().getSimpleName() + " started in " + (System.currentTimeMillis() - startTime) + " ms");
this.serverscdl.await(); this.serverscdl.await();
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
private void runServers(CountDownLatch timecd, final AnyValue[] entrys) throws Exception { private void runServers(CountDownLatch timecd, final List<AnyValue> serconfs) throws Exception {
CountDownLatch servicecdl = new CountDownLatch(entrys.length); CountDownLatch servicecdl = new CountDownLatch(serconfs.size());
for (final AnyValue entry : entrys) { CountDownLatch sercdl = new CountDownLatch(serconfs.size());
new Thread() { for (final AnyValue serconf : serconfs) {
Thread thread = new Thread() {
{ {
String host = entry.getValue("host", "").replace("0.0.0.0", ""); String host = serconf.getValue("host", "").replace("0.0.0.0", "[0]");
setName(entry.getValue("protocol", "Server").toUpperCase() + "-" + host + ":" + entry.getIntValue("port", 80) + "-Thread"); setName(serconf.getValue("protocol", "Server").toUpperCase() + "-" + host + ":" + serconf.getIntValue("port", 80) + "-Thread");
this.setDaemon(true); this.setDaemon(true);
} }
@@ -358,167 +372,96 @@ public final class Application {
try { try {
//Thread ctd = Thread.currentThread(); //Thread ctd = Thread.currentThread();
//ctd.setContextClassLoader(new URLClassLoader(new URL[0], ctd.getContextClassLoader())); //ctd.setContextClassLoader(new URLClassLoader(new URL[0], ctd.getContextClassLoader()));
String protocol = entry.getValue("protocol", ""); String protocol = serconf.getValue("protocol", "");
String subprotocol = "UDP"; String subprotocol = Sncp.DEFAULT_PROTOCOL;
int pos = protocol.indexOf('.'); int pos = protocol.indexOf('.');
if (pos > 0) { if (pos > 0) {
subprotocol = protocol.substring(pos + 1); subprotocol = protocol.substring(pos + 1);
protocol = protocol.substring(0, pos); protocol = protocol.substring(0, pos);
} }
NodeServer server = null; NodeServer server = null;
if ("HTTP".equalsIgnoreCase(protocol)) { String host = serconf.getValue("host", "0.0.0.0").replace("0.0.0.0", "");
server = new NodeHttpServer(Application.this, servicecdl, new HttpServer(startTime, watch)); InetSocketAddress addr = new InetSocketAddress(host.isEmpty() ? localAddress.getHostAddress() : host, serconf.getIntValue("port", 80));
} else if ("SNCP".equalsIgnoreCase(protocol)) { if ("SNCP".equalsIgnoreCase(protocol)) {
server = new NodeSncpServer(Application.this, servicecdl, new SncpServer(startTime, subprotocol, watch)); 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) { 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); System.exit(0);
} }
servers.add(server); servers.add(server);
server.init(serconf);
server.prepare(entry); //必须在init之前
server.init(entry);
server.start(); server.start();
timecd.countDown(); timecd.countDown();
sercdl.countDown();
} catch (Exception ex) { } catch (Exception ex) {
logger.log(Level.WARNING, entry + " runServers error", ex); logger.log(Level.WARNING, serconf + " runServers error", ex);
serverscdl.countDown(); serverscdl.countDown();
} }
} }
}.start(); };
thread.start();
} }
sercdl.await();
} }
private void initResources() throws Exception { 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"); final AnyValue resources = config.getAnyValue("resources");
if (resources != null) { if (resources != null) {
//------------------------------------------------------------------------ //------------------------------------------------------------------------
final String host = this.localAddress.getHostAddress(); AnyValue datacachelistenerConf = resources.getAnyValue("datacachelistener");
final Map<String, Set<String>> groups = new HashMap<>(); if (datacachelistenerConf != null) {
final Map<String, List<SimpleEntry<String, InetSocketAddress[]>>> groups2 = new HashMap<>(); String val = datacachelistenerConf.getValue("service", "");
if (!val.isEmpty()) {
for (AnyValue conf : resources.getAnyValues("remote")) { if ("none".equalsIgnoreCase(val)) {
final String name = conf.getValue("name"); this.dataCacheListenerClass = null;
final String group = conf.getValue("group", ""); } else {
if (name == null) throw new RuntimeException("remote name is null"); Class clazz = Class.forName(val);
String protocol = conf.getValue("protocol", "UDP").toUpperCase(); if (!DataCacheListener.class.isAssignableFrom(clazz) || !Service.class.isAssignableFrom(clazz)) {
if (!"TCP".equalsIgnoreCase(protocol) && !"UDP".equalsIgnoreCase(protocol)) { throw new RuntimeException("datacachelistener service (" + val + ") is illegal");
throw new RuntimeException("Not supported Transport Protocol " + conf.getValue("protocol")); }
} this.dataCacheListenerClass = clazz;
{
Set<String> set = groups.get(group);
if (set == null) {
set = new HashSet<>();
groups.put(group, set);
} }
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>>>() { for (AnyValue conf : resources.getAnyValues("group")) {
}.getType(), groups); final String group = conf.getValue("name", "");
this.factory.register(RESNAME_GROUP, new TypeToken<Map<String, List<SimpleEntry<String, InetSocketAddress[]>>>>() { String protocol = conf.getValue("protocol", Sncp.DEFAULT_PROTOCOL).toUpperCase();
}.getType(), groups2); if (!"TCP".equalsIgnoreCase(protocol) && !Sncp.DEFAULT_PROTOCOL.equalsIgnoreCase(protocol)) {
throw new RuntimeException("Not supported Transport Protocol " + conf.getValue("protocol"));
final Map<String, List<SimpleEntry<String, InetSocketAddress[]>>> outgroups2 = new HashMap<>(); }
final Map<String, Set<String>> outgroups = new HashMap<>(); List<InetSocketAddress> addrs = new ArrayList<>();
groups.entrySet().stream().filter(x -> !x.getKey().equals(nodeName)).forEach(x -> outgroups.put(x.getKey(), x.getValue())); for (AnyValue node : conf.getAnyValues("node")) {
groups2.entrySet().stream().filter(x -> !x.getKey().equals(nodeName)).forEach(x -> outgroups2.put(x.getKey(), x.getValue())); 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)));
this.factory.register(RESNAME_OUTGROUP, new TypeToken<Map<String, Set<String>>>() { addrGroups.put(addr, group);
}.getType(), outgroups); addrs.add(addr);
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);
} }
//------------------------------------------------------------------------ //------------------------------------------------------------------------
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 { private void shutdown() throws Exception {
@@ -531,18 +474,6 @@ public final class Application {
serverscdl.countDown(); 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) { for (DataSource source : sources) {
try { try {
source.getClass().getMethod("close").invoke(source); source.getClass().getMethod("close").invoke(source);

View File

@@ -37,11 +37,27 @@ public final class ClassFilter<T> {
private Pattern[] excludePatterns; private Pattern[] excludePatterns;
private List<ClassFilter> ors;
private List<ClassFilter> ands;
public ClassFilter(Class<? extends Annotation> annotationClass, Class superClass) { public ClassFilter(Class<? extends Annotation> annotationClass, Class superClass) {
this.annotationClass = annotationClass; this.annotationClass = annotationClass;
this.superClass = superClass; 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集合 * 获取符合条件的class集合
* <p> * <p>
@@ -74,8 +90,7 @@ public final class ClassFilter<T> {
try { try {
Class clazz = Class.forName(clazzname); Class clazz = Class.forName(clazzname);
if (accept(property, clazz, autoscan)) { if (accept(property, clazz, autoscan)) {
FilterEntry en = new FilterEntry(clazz, property); entrys.add(new FilterEntry(clazz, property));
if (!entrys.contains(en)) entrys.add(en);
} }
} catch (Throwable cfe) { } catch (Throwable cfe) {
} }
@@ -104,6 +119,21 @@ public final class ClassFilter<T> {
* @return * @return
*/ */
public boolean accept(AnyValue property, String classname) { 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 (this.refused) return false;
if (classname.startsWith("java.") || classname.startsWith("javax.")) return false; if (classname.startsWith("java.") || classname.startsWith("javax.")) return false;
if (excludePatterns != null) { if (excludePatterns != null) {
@@ -186,16 +216,17 @@ public final class ClassFilter<T> {
*/ */
public static final class FilterEntry<T> { public static final class FilterEntry<T> {
private String group;
private final String name; private final String name;
private final Class<T> type; private final Class<T> type;
private final AnyValue property; private final AnyValue property;
protected Object attachment;
public FilterEntry(Class<T> type, AnyValue property) { public FilterEntry(Class<T> type, AnyValue property) {
this.type = type; this.type = type;
this.group = property == null ? "" : property.getValue("group", "");
this.property = property; this.property = property;
this.name = property == null ? "" : property.getValue("name", ""); this.name = property == null ? "" : property.getValue("name", "");
} }
@@ -203,8 +234,7 @@ public final class ClassFilter<T> {
@Override @Override
public String toString() { public String toString() {
return this.getClass().getSimpleName() + "[thread=" + Thread.currentThread().getName() return this.getClass().getSimpleName() + "[thread=" + Thread.currentThread().getName()
+ ", type=" + this.type.getSimpleName() + ", name=" + name + ", type=" + this.type.getSimpleName() + ", name=" + name + ", group=" + group + "]";
+ ", remote=" + (property == null ? "null" : property.getValue("remote")) + "]";
} }
@Override @Override
@@ -216,7 +246,7 @@ public final class ClassFilter<T> {
public boolean equals(Object obj) { public boolean equals(Object obj) {
if (obj == null) return false; if (obj == null) return false;
if (getClass() != obj.getClass()) 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() { public Class<T> getType() {
@@ -231,14 +261,13 @@ public final class ClassFilter<T> {
return property; return property;
} }
public Object getAttachment() { public String getGroup() {
return attachment; return group;
} }
public void setAttachment(Object attachment) { public void setGroup(String group) {
this.attachment = attachment; this.group = group;
} }
} }
/** /**

View File

@@ -10,14 +10,21 @@ import com.wentch.redkale.net.http.HttpServer;
import com.wentch.redkale.net.http.HttpServlet; import com.wentch.redkale.net.http.HttpServlet;
import com.wentch.redkale.util.AnyValue; import com.wentch.redkale.util.AnyValue;
import com.wentch.redkale.boot.ClassFilter.FilterEntry; import com.wentch.redkale.boot.ClassFilter.FilterEntry;
import com.wentch.redkale.service.Service; import com.wentch.redkale.net.*;
import java.lang.reflect.Modifier; 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.net.InetSocketAddress;
import java.util.*; import java.util.*;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.CountDownLatch;
import java.util.logging.*; import java.util.logging.*;
import javax.annotation.*;
/** /**
* HTTP Server节点的配置Server
* *
* @author zhangjx * @author zhangjx
*/ */
@@ -25,15 +32,12 @@ public final class NodeHttpServer extends NodeServer {
private final HttpServer server; private final HttpServer server;
public NodeHttpServer(Application application, CountDownLatch servicecdl, HttpServer server) { private final File home;
super(application, servicecdl, server);
this.server = server;
}
@Override public NodeHttpServer(Application application, InetSocketAddress addr, CountDownLatch servicecdl, HttpServer server) {
public void init(AnyValue config) throws Exception { super(application, application.factory.createChild(), servicecdl, server);
server.init(config); this.server = server;
super.init(config); this.home = application.getHome();
} }
@Override @Override
@@ -43,22 +47,81 @@ public final class NodeHttpServer extends NodeServer {
@Override @Override
public void prepare(AnyValue config) throws Exception { public void prepare(AnyValue config) throws Exception {
super.prepare(config); ClassFilter<HttpServlet> httpFilter = createClassFilter(null, config, WebServlet.class, HttpServlet.class, null, "servlets", "servlet");
ClassFilter<HttpServlet> httpFilter = createHttpServletClassFilter(application.nodeName, config); ClassFilter<Service> serviceFilter = createServiceClassFilter(config);
ClassFilter<Service> serviceFilter = createServiceClassFilter(application.nodeName, config);
long s = System.currentTimeMillis(); long s = System.currentTimeMillis();
ClassFilter.Loader.load(application.getHome(), serviceFilter, httpFilter); ClassFilter.Loader.load(home, serviceFilter, httpFilter);
long e = System.currentTimeMillis() - s; long e = System.currentTimeMillis() - s;
logger.info(this.getClass().getSimpleName() + " load filter class in " + e + " ms"); logger.info(this.getClass().getSimpleName() + " load filter class in " + e + " ms");
loadService(config.getAnyValue("services"), serviceFilter); //必须在servlet之前 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) { private void initWebSocketService() {
return createClassFilter(node, config, WebServlet.class, HttpServlet.class, null, "servlets", "servlet"); 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 StringBuilder sb = logger.isLoggable(Level.FINE) ? new StringBuilder() : null;
final String prefix = conf == null ? "" : conf.getValue("prefix", ""); final String prefix = conf == null ? "" : conf.getValue("prefix", "");
final String threadName = "[" + Thread.currentThread().getName() + "] "; final String threadName = "[" + Thread.currentThread().getName() + "] ";
@@ -68,7 +131,7 @@ public final class NodeHttpServer extends NodeServer {
WebServlet ws = clazz.getAnnotation(WebServlet.class); WebServlet ws = clazz.getAnnotation(WebServlet.class);
if (ws == null || ws.value().length == 0) continue; if (ws == null || ws.value().length == 0) continue;
final HttpServlet servlet = clazz.newInstance(); final HttpServlet servlet = clazz.newInstance();
application.factory.inject(servlet); factory.inject(servlet);
String[] mappings = ws.value(); String[] mappings = ws.value();
if (ws.fillurl() && !prefix.isEmpty()) { if (ws.fillurl() && !prefix.isEmpty()) {
for (int i = 0; i < mappings.length; i++) { 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()); if (sb != null && sb.length() > 0) logger.log(Level.FINE, sb.toString());
} }
@Override
public boolean isSNCP() {
return false;
}
} }

View File

@@ -5,15 +5,16 @@
*/ */
package com.wentch.redkale.boot; 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.Server;
import com.wentch.redkale.net.sncp.Sncp; import com.wentch.redkale.net.sncp.Sncp;
import com.wentch.redkale.service.Service; import com.wentch.redkale.service.Service;
import com.wentch.redkale.service.MultiService;
import com.wentch.redkale.util.AnyValue; import com.wentch.redkale.util.AnyValue;
import com.wentch.redkale.util.Ignore; import com.wentch.redkale.util.Ignore;
import com.wentch.redkale.boot.ClassFilter.FilterEntry; 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.io.*;
import java.lang.annotation.Annotation; import java.lang.annotation.Annotation;
import java.lang.reflect.*; import java.lang.reflect.*;
@@ -22,6 +23,7 @@ import java.util.*;
import java.util.concurrent.*; import java.util.concurrent.*;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.logging.*; import java.util.logging.*;
import javax.annotation.*;
/** /**
* *
@@ -35,23 +37,36 @@ public abstract class NodeServer {
protected final Application application; protected final Application application;
protected final ResourceFactory factory;
private final CountDownLatch servicecdl; private final CountDownLatch servicecdl;
private final Server server; 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.application = application;
this.factory = factory;
this.servicecdl = servicecdl; this.servicecdl = servicecdl;
this.server = server; this.server = server;
this.logger = Logger.getLogger(this.getClass().getSimpleName()); 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 { public void init(AnyValue config) throws Exception {
this.servconf = config == null ? new AnyValue.DefaultAnyValue() : config;
//设置root文件夹 //设置root文件夹
String webroot = config.getValue("root", "root"); String webroot = config.getValue("root", "root");
File myroot = new File(webroot); File myroot = new File(webroot);
@@ -60,64 +75,114 @@ public abstract class NodeServer {
} }
final String homepath = myroot.getCanonicalPath(); final String homepath = myroot.getCanonicalPath();
Server.loadLib(logger, config.getValue("lib", "") + ";" + homepath + "/lib/*;" + homepath + "/classes"); 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 InetSocketAddress getSocketAddress();
public abstract boolean isSNCP();
public void start() throws IOException { public void start() throws IOException {
server.start(); server.start();
} }
public void shutdown() throws IOException { 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(); server.shutdown();
} }
protected void loadLocalService(final Set<FilterEntry<Service>> entrys) throws Exception { protected Transport loadTransport(String group, String protocol, Set<InetSocketAddress> addrs) {
final HashMap<Class, ServiceEntry> localServices = application.localServices; Transport transport = null;
for (final FilterEntry<Service> entry : entrys) { if (!addrs.isEmpty()) {
Class<? extends Service> serviceClass = entry.getType(); synchronized (application.transports) {
if (serviceClass.getAnnotation(Ignore.class) != null) continue; for (Transport tran : application.transports) {
final boolean multi = MultiService.class.isAssignableFrom(serviceClass); if (tran.match(addrs)) {
if (!multi) { //单例模式 transport = tran;
synchronized (application.localServices) { break;
ServiceEntry old = localServices.get(serviceClass);
if (old == null) {
old = new ServiceEntry(serviceClass, (Service) serviceClass.newInstance(), entry.getProperty(), "");
localServices.put(serviceClass, old);
} }
if (consumer != null) consumer.accept(old);
continue;
} }
} if (transport == null) {
String name = multi ? entry.getName() : ""; transport = new Transport(group + "_" + application.transports.size(), protocol, application.watch, 32, addrs);
synchronized (application.localServices) { application.transports.add(transport);
ServiceEntry old = localServices.get(serviceClass);
if (old != null && old.containsName(name)) {
if (consumer != null) consumer.accept(old);
continue;
} }
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") @SuppressWarnings("unchecked")
@@ -125,126 +190,129 @@ public abstract class NodeServer {
if (serviceFilter == null) return; if (serviceFilter == null) return;
final String threadName = "[" + Thread.currentThread().getName() + "] "; final String threadName = "[" + Thread.currentThread().getName() + "] ";
final Set<FilterEntry<Service>> entrys = serviceFilter.getFilterEntrys(); final Set<FilterEntry<Service>> entrys = serviceFilter.getFilterEntrys();
final Set<FilterEntry<Service>> localentrys = new HashSet<>(entrys.size()); final String defgroup = servconf == null ? "" : servconf.getValue("group", ""); //Server节点获取group信息
final Set<FilterEntry<Service>> remotentrys = new HashSet<>(entrys.size()); ResourceFactory regFactory = isSNCP() ? application.factory : factory;
final String defremote = getRemoteName(servicesConf, "LOCAL");
for (FilterEntry<Service> entry : entrys) { //service实现类 for (FilterEntry<Service> entry : entrys) { //service实现类
final Class type = entry.getType(); final Class<? extends Service> type = entry.getType();
if (type.isInterface()) continue; if (type.isInterface()) continue;
if (Modifier.isFinal(type.getModifiers())) continue; if (Modifier.isFinal(type.getModifiers())) continue;
if (!Modifier.isPublic(type.getModifiers())) continue; if (!Modifier.isPublic(type.getModifiers())) continue;
if (Modifier.isAbstract(type.getModifiers())) continue; if (Modifier.isAbstract(type.getModifiers())) continue;
if (type.getAnnotation(Ignore.class) != null) continue; if (type.getAnnotation(Ignore.class) != null) continue;
final String remote = getRemoteName(entry.getProperty(), defremote); if (!isSNCP() && factory.find(entry.getName(), type) != null) continue;
if ("LOCAL".equals(remote)) { //本地模式 String group = entry.getGroup();
localentrys.add(entry); if (group == null || group.isEmpty()) group = defgroup;
} else { //远程模式 final Set<InetSocketAddress> sameGroupAddrs = new LinkedHashSet<>();
entry.setAttachment(remote); final Map<String, Set<InetSocketAddress>> diffGroupAddrs = new HashMap<>();
remotentrys.add(entry); 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.countDown();
servicecdl.await(); servicecdl.await();
final StringBuilder sb = logger.isLoggable(Level.INFO) ? new StringBuilder() : null; final StringBuilder sb = logger.isLoggable(Level.INFO) ? new StringBuilder() : null;
synchronized (application) { //---------------- inject ----------------
if (!application.serviceInited) { new HashSet<>(localServices).forEach(y -> {
application.serviceInited = true; factory.inject(y.getService());
//--------------- register --------------- });
application.localServices.forEach((x, y) -> { remoteServices.forEach(y -> {
y.getNames().forEach(n -> application.factory.register(n, y.getServiceClass(), y.getService())); factory.inject(y.getService());
}); });
application.remoteServices.forEach(y -> { //----------------- init -----------------
y.getNames().forEach(n -> application.factory.register(n, y.getServiceClass(), y.getService())); localServices.parallelStream().forEach(y -> {
}); long s = System.currentTimeMillis();
//---------------- inject ---------------- y.getService().init(y.getConf());
application.localServices.forEach((x, y) -> { long e = System.currentTimeMillis() - s;
application.factory.inject(y.getService()); 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);
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());
} }
} });
if (sb != null && sb.length() > 0) logger.log(Level.INFO, sb.toString());
} }
private String getRemoteName(AnyValue av, String remote) { protected ClassFilter<Service> createServiceClassFilter(final AnyValue config) {
remote = (remote == null || remote.trim().isEmpty()) ? "LOCAL" : remote.trim(); return createClassFilter(this.nodeGroup, config, null, Service.class, Annotation.class, "services", "service");
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 static ClassFilter<Service> createServiceClassFilter(final String localNode, final AnyValue config) { protected static ClassFilter createClassFilter(final String localGroup, final AnyValue config, Class<? extends Annotation> ref,
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,
Class inter, Class<? extends Annotation> ref2, String properties, String property) { Class inter, Class<? extends Annotation> ref2, String properties, String property) {
ClassFilter filter = new ClassFilter(ref, inter); ClassFilter cf = new ClassFilter(ref, inter);
if (properties == null && properties == null) return filter; if (properties == null && properties == null) return cf;
AnyValue list = config == null ? null : config.getAnyValue(properties); if (config == null) return cf;
if (list == null) return filter; AnyValue[] proplist = config.getAnyValues(properties);
if ("services".equals(properties)) { if (proplist == null || proplist.length < 1) return cf;
for (AnyValue group : list.getAnyValues("group")) { cf = null;
String remotenames = group.getValue("remotenames"); for (AnyValue list : proplist) {
for (AnyValue propnode : group.getAnyValues(property)) { ClassFilter filter = new ClassFilter(ref, inter);
boolean hasremote = false; for (AnyValue av : list.getAnyValues(property)) {
if (remotenames != null) { filter.filter(av, av.getValue("value"), false);
for (String rn : remotenames.split(";")) { }
rn = rn.trim(); if (list.getBoolValue("autoload", true)) {
if (rn.isEmpty() || localNode.equals(rn)) continue; String includes = list.getValue("includes", "");
DefaultAnyValue s = new DefaultAnyValue(); String excludes = list.getValue("excludes", "");
s.addValue("value", propnode.getValue("value")); filter.setIncludePatterns(includes.split(";"));
s.addValue("name", rn); filter.setExcludePatterns(excludes.split(";"));
s.addValue("remote", rn); } else {
((DefaultAnyValue) list).addValue(property, s); if (ref2 == null || ref2 == Annotation.class) { //service如果是autoload=false则不需要加载
hasremote = true; filter.setRefused(true);
} } else if (ref2 != Annotation.class) {
} filter.setAnnotationClass(ref2);
if (hasremote) {
((DefaultAnyValue) propnode).setValue("name", localNode);
((DefaultAnyValue) propnode).setValue("remote", "");
((DefaultAnyValue) list).addValue(property, propnode);
}
} }
} }
cf = (cf == null) ? filter : cf.or(filter);
} }
for (AnyValue av : list.getAnyValues(property)) { return cf;
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;
} }
} }

View File

@@ -5,11 +5,13 @@
*/ */
package com.wentch.redkale.boot; 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.util.AnyValue;
import com.wentch.redkale.service.Service; import com.wentch.redkale.service.Service;
import java.io.*;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.CountDownLatch;
import java.util.logging.*;
/** /**
* *
@@ -19,16 +21,15 @@ public final class NodeSncpServer extends NodeServer {
private final SncpServer server; private final SncpServer server;
public NodeSncpServer(Application application, CountDownLatch regcdl, SncpServer server) { private final File home;
super(application, regcdl, server);
this.server = server;
this.consumer = x -> server.addService(x);
}
@Override public NodeSncpServer(Application application, InetSocketAddress addr, CountDownLatch regcdl, SncpServer server) {
public void init(AnyValue config) throws Exception { super(application, application.factory.createChild(), regcdl, server);
server.init(config); this.server = server;
super.init(config); this.home = application.getHome();
this.servaddr = addr;
this.nodeGroup = application.addrGroups.getOrDefault(addr, "");
this.consumer = server == null ? null : x -> server.addService(x);
} }
@Override @Override
@@ -38,13 +39,28 @@ public final class NodeSncpServer extends NodeServer {
@Override @Override
public void prepare(AnyValue config) throws Exception { public void prepare(AnyValue config) throws Exception {
super.prepare(config); ClassFilter<Service> serviceFilter = createServiceClassFilter(config);
ClassFilter<Service> serviceFilter = createServiceClassFilter(application.nodeName, config);
long s = System.currentTimeMillis(); long s = System.currentTimeMillis();
ClassFilter.Loader.load(application.getHome(), serviceFilter); ClassFilter.Loader.load(home, serviceFilter);
long e = System.currentTimeMillis() - s; long e = System.currentTimeMillis() - s;
logger.info(this.getClass().getSimpleName() + " load filter class in " + e + " ms"); logger.info(this.getClass().getSimpleName() + " load filter class in " + e + " ms");
loadService(config.getAnyValue("services"), serviceFilter); //必须在servlet之前 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;
}
} }

View File

@@ -1,28 +1,28 @@
/* /*
* To change this template, choose Tools | Templates * To change this template, choose Tools | Templates
* and open the template in the editor. * and open the template in the editor.
*/ */
package com.wentch.redkale.convert; package com.wentch.redkale.convert;
/** /**
* *
* @author zhangjx * @author zhangjx
*/ */
public class ConvertException extends RuntimeException { public class ConvertException extends RuntimeException {
public ConvertException() { public ConvertException() {
super(); super();
} }
public ConvertException(String s) { public ConvertException(String s) {
super(s); super(s);
} }
public ConvertException(String message, Throwable cause) { public ConvertException(String message, Throwable cause) {
super(message, cause); super(message, cause);
} }
public ConvertException(Throwable cause) { public ConvertException(Throwable cause) {
super(cause); super(cause);
} }
} }

View File

@@ -87,7 +87,6 @@ public abstract class Factory<R extends Reader, W extends Writer> {
this.register(DLong.class, DLongSimpledCoder.instance); this.register(DLong.class, DLongSimpledCoder.instance);
this.register(Class.class, TypeSimpledCoder.instance); this.register(Class.class, TypeSimpledCoder.instance);
this.register(InetSocketAddress.class, InetSocketAddressSimpledCoder.instance); this.register(InetSocketAddress.class, InetSocketAddressSimpledCoder.instance);
this.register(InetSocketAddress.class, InetSocketAddressSimpledCoder.instance);
this.register(Pattern.class, PatternSimpledCoder.instance); this.register(Pattern.class, PatternSimpledCoder.instance);
//--------------------------------------------------------- //---------------------------------------------------------
this.register(boolean[].class, BoolArraySimpledCoder.instance); this.register(boolean[].class, BoolArraySimpledCoder.instance);

View File

@@ -1,68 +1,68 @@
/* /*
* To change this template, choose Tools | Templates * To change this template, choose Tools | Templates
* and open the template in the editor. * and open the template in the editor.
*/ */
package com.wentch.redkale.convert; package com.wentch.redkale.convert;
/** /**
* 只增不减的伪Map类 * 只增不减的伪Map类
* *
* @author zhangjx * @author zhangjx
* @param <K> * @param <K>
* @param <V> * @param <V>
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public final class HashedMap<K, V> { public final class HashedMap<K, V> {
protected final transient Entry<K, V>[] table; protected final transient Entry<K, V>[] table;
public HashedMap() { public HashedMap() {
this(128); this(128);
} }
public HashedMap(int initCapacity) { public HashedMap(int initCapacity) {
this.table = new Entry[Math.max(initCapacity, 16)]; this.table = new Entry[Math.max(initCapacity, 16)];
} }
public final V get(final K key) { public final V get(final K key) {
final K k = key; final K k = key;
final Entry<K, V>[] data = this.table; final Entry<K, V>[] data = this.table;
Entry<K, V> entry = data[k.hashCode() & (data.length - 1)]; Entry<K, V> entry = data[k.hashCode() & (data.length - 1)];
while (entry != null) { while (entry != null) {
if (k == entry.key) return entry.value; if (k == entry.key) return entry.value;
entry = entry.next; entry = entry.next;
} }
return null; return null;
} }
public final V put(K key, V value) { public final V put(K key, V value) {
final K k = key; final K k = key;
final Entry<K, V>[] data = this.table; final Entry<K, V>[] data = this.table;
final int index = k.hashCode() & (data.length - 1); final int index = k.hashCode() & (data.length - 1);
Entry<K, V> entry = data[index]; Entry<K, V> entry = data[index];
while (entry != null) { while (entry != null) {
if (k == entry.key) { if (k == entry.key) {
entry.value = value; entry.value = value;
return entry.value; return entry.value;
} }
entry = entry.next; entry = entry.next;
} }
data[index] = new Entry<>(key, value, data[index]); data[index] = new Entry<>(key, value, data[index]);
return null; return null;
} }
protected static final class Entry<K, V> { protected static final class Entry<K, V> {
protected V value; protected V value;
protected final K key; protected final K key;
protected final Entry<K, V> next; protected final Entry<K, V> next;
protected Entry(K key, V value, Entry<K, V> next) { protected Entry(K key, V value, Entry<K, V> next) {
this.key = key; this.key = key;
this.value = value; this.value = value;
this.next = next; this.next = next;
} }
} }
} }

View File

@@ -8,6 +8,7 @@ package com.wentch.redkale.convert.bson;
import com.wentch.redkale.convert.*; import com.wentch.redkale.convert.*;
import com.wentch.redkale.util.*; import com.wentch.redkale.util.*;
import java.lang.reflect.*; import java.lang.reflect.*;
import java.nio.*;
/** /**
* BSON协议格式: * BSON协议格式:
@@ -78,4 +79,26 @@ public final class BsonConvert extends Convert<BsonReader, BsonWriter> {
} }
return convertTo(value.getClass(), value); 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);
}
} }

View File

@@ -7,6 +7,7 @@ package com.wentch.redkale.convert.bson;
import com.wentch.redkale.convert.*; import com.wentch.redkale.convert.*;
import com.wentch.redkale.util.*; import com.wentch.redkale.util.*;
import java.nio.*;
/** /**
* *
@@ -33,6 +34,10 @@ public final class BsonWriter implements Writer {
return newdata; return newdata;
} }
public ByteBuffer toBuffer() {
return ByteBuffer.wrap(content, 0, count);
}
public BsonWriter() { public BsonWriter() {
this(defaultSize); this(defaultSize);
} }

View File

@@ -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(); public static final InetSocketAddressSimpledCoder instance = new InetSocketAddressSimpledCoder();

View File

@@ -30,4 +30,4 @@ public final class LongSimpledCoder<R extends Reader, W extends Writer> extends
return in.readLong(); return in.readLong();
} }
} }

View File

@@ -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;
}
}
}
}

View File

@@ -8,6 +8,7 @@ package com.wentch.redkale.convert.json;
import com.wentch.redkale.convert.ConvertType; import com.wentch.redkale.convert.ConvertType;
import com.wentch.redkale.convert.Factory; import com.wentch.redkale.convert.Factory;
import java.io.Serializable; 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")); private static final JsonFactory instance = new JsonFactory(null, Boolean.getBoolean("convert.json.tiny"));
static { static {
instance.register(InetAddress.class, InetAddressJsonSimpledCoder.instance);
instance.register(InetSocketAddress.class, InetAddressJsonSimpledCoder.InetSocketAddressJsonSimpledCoder.instance);
instance.register(Serializable.class, instance.loadEncoder(Object.class)); instance.register(Serializable.class, instance.loadEncoder(Object.class));
} }

View File

@@ -34,6 +34,8 @@ public abstract class Server {
protected final String protocol; protected final String protocol;
protected final PrepareServlet prepare;
protected AnyValue config; protected AnyValue config;
protected Charset charset; protected Charset charset;
@@ -64,9 +66,10 @@ public abstract class Server {
private ScheduledThreadPoolExecutor scheduler; 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.serverStartTime = serverStartTime;
this.protocol = protocol; this.protocol = protocol;
this.prepare = servlet;
this.watch = watch; this.watch = watch;
} }
@@ -88,12 +91,13 @@ public abstract class Server {
final Format f = createFormat(); final Format f = createFormat();
this.executor = Executors.newFixedThreadPool(threads, (Runnable r) -> { this.executor = Executors.newFixedThreadPool(threads, (Runnable r) -> {
Thread t = new WorkThread(executor, 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; return t;
}); });
} }
public void destroy(final AnyValue config) throws Exception { public void destroy(final AnyValue config) throws Exception {
this.prepare.destroy(context, config);
if (scheduler != null) scheduler.shutdownNow(); if (scheduler != null) scheduler.shutdownNow();
} }
@@ -101,23 +105,28 @@ public abstract class Server {
return address; return address;
} }
public String getProtocol() {
return protocol;
}
public Logger getLogger() { public Logger getLogger() {
return this.logger; return this.logger;
} }
public void start() throws IOException { public void start() throws IOException {
this.context = this.createContext(); this.context = this.createContext();
this.context.prepare.init(this.context, config); this.prepare.init(this.context, config);
if (this.watch != null) this.watch.inject(this.context.prepare); if (this.watch != null) this.watch.inject(this.prepare);
this.transport = ProtocolServer.create(this.protocol, context); this.transport = ProtocolServer.create(this.protocol, context);
this.transport.open(); this.transport.open();
transport.setOption(StandardSocketOptions.SO_REUSEADDR, true); 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); transport.bind(address, backlog);
logger.info(this.getClass().getSimpleName() + " listen: " + address);
logger.info(this.getClass().getSimpleName() + " threads: " + threads + ", bufferPoolSize: " + bufferPoolSize + ", responsePoolSize: " + responsePoolSize);
transport.accept(); 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(); protected abstract Context createContext();
@@ -130,7 +139,7 @@ public abstract class Server {
} catch (Exception e) { } catch (Exception e) {
} }
logger.info(this.getClass().getSimpleName() + "-" + this.protocol + " shutdow prepare servlet"); 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; long e = System.currentTimeMillis() - s;
logger.info(this.getClass().getSimpleName() + " shutdown in " + e + " ms"); logger.info(this.getClass().getSimpleName() + " shutdown in " + e + " ms");
} }

View File

@@ -11,6 +11,7 @@ import java.io.*;
import java.net.*; import java.net.*;
import java.nio.*; import java.nio.*;
import java.nio.channels.*; import java.nio.channels.*;
import java.util.*;
import java.util.concurrent.*; import java.util.concurrent.*;
import java.util.concurrent.atomic.*; import java.util.concurrent.atomic.*;
@@ -21,25 +22,21 @@ import java.util.concurrent.atomic.*;
*/ */
public final class Transport { public final class Transport {
protected SocketAddress[] remoteAddres;
protected final ObjectPool<ByteBuffer> bufferPool;
protected final String name; protected final String name;
protected final String protocol; protected final String protocol;
private final boolean udp;
protected final AsynchronousChannelGroup group; 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.name = name;
this.protocol = protocol; this.protocol = protocol;
this.udp = "UDP".equalsIgnoreCase(protocol);
this.queue = this.udp ? null : new ArrayBlockingQueue<>(clients);
AsynchronousChannelGroup g = null; AsynchronousChannelGroup g = null;
try { try {
final AtomicInteger counter = new AtomicInteger(); final AtomicInteger counter = new AtomicInteger();
@@ -63,7 +60,21 @@ public final class Transport {
e.clear(); e.clear();
return true; 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() { public ByteBuffer pollBuffer() {
@@ -78,27 +89,18 @@ public final class Transport {
for (ByteBuffer buffer : buffers) offerBuffer(buffer); for (ByteBuffer buffer : buffers) offerBuffer(buffer);
} }
public boolean isUDP() {
return udp;
}
public AsyncConnection pollConnection() { public AsyncConnection pollConnection() {
if (udp) return createConnection(); int i = index.get();
AsyncConnection conn = queue.poll(); SocketAddress addr = remoteAddres[i];
return (conn != null && conn.isOpen()) ? conn : createConnection();
}
private AsyncConnection createConnection() {
SocketAddress addr = remoteAddres[0];
try { try {
if (udp) { if ("TCP".equalsIgnoreCase(protocol)) {
AsyncDatagramChannel channel = AsyncDatagramChannel.open(group);
channel.connect(addr);
return AsyncConnection.create(channel, addr, true, 0, 0);
} else {
AsynchronousSocketChannel channel = AsynchronousSocketChannel.open(group); AsynchronousSocketChannel channel = AsynchronousSocketChannel.open(group);
channel.connect(addr).get(2, TimeUnit.SECONDS); channel.connect(addr).get(2, TimeUnit.SECONDS);
return AsyncConnection.create(channel, 0, 0); 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) { } catch (Exception ex) {
throw new RuntimeException("transport address = " + addr, ex); throw new RuntimeException("transport address = " + addr, ex);
@@ -106,18 +108,9 @@ public final class Transport {
} }
public void offerConnection(AsyncConnection conn) { public void offerConnection(AsyncConnection conn) {
if (udp) { try {
try { conn.close();
conn.close(); } catch (IOException io) {
} catch (IOException io) {
}
} else if (conn.isOpen()) {
if (!queue.offer(conn)) {
try {
conn.close();
} catch (IOException io) {
}
}
} }
} }

View File

@@ -29,8 +29,8 @@ public final class HttpContext extends Context {
protected final SecureRandom random = new SecureRandom(); protected final SecureRandom random = new SecureRandom();
public HttpContext(long serverStartTime, Logger logger, ExecutorService executor, ObjectPool<ByteBuffer> bufferPool, public HttpContext(long serverStartTime, Logger logger, ExecutorService executor, ObjectPool<ByteBuffer> bufferPool,
ObjectPool<Response> responsePool, int maxbody, Charset charset, InetSocketAddress address, ObjectPool<Response> responsePool, int maxbody, Charset charset, InetSocketAddress address, PrepareServlet prepare,
PrepareServlet prepare, WatchFactory watch, int readTimeoutSecond, int writeTimeoutSecond, String contextPath) { WatchFactory watch, int readTimeoutSecond, int writeTimeoutSecond, String contextPath) {
super(serverStartTime, logger, executor, bufferPool, responsePool, maxbody, charset, super(serverStartTime, logger, executor, bufferPool, responsePool, maxbody, charset,
address, prepare, watch, readTimeoutSecond, writeTimeoutSecond); address, prepare, watch, readTimeoutSecond, writeTimeoutSecond);
this.contextPath = contextPath; this.contextPath = contextPath;

View File

@@ -25,7 +25,7 @@ public final class HttpPrepareServlet extends PrepareServlet<HttpRequest, HttpRe
private ByteBuffer flashPolicyBuffer; 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<>(); private final Map<String, HttpServlet> strmaps = new HashMap<>();
@@ -43,13 +43,13 @@ public final class HttpPrepareServlet extends PrepareServlet<HttpRequest, HttpRe
@Override @Override
public void init(Context context, AnyValue config) { public void init(Context context, AnyValue config) {
this.servlets.stream().forEach((en) -> { this.servlets.stream().forEach(s -> {
en.getKey().init(context, en.getValue()); s.init(context, s.conf);
}); });
final WatchFactory watch = ((HttpContext) context).getWatchFactory(); final WatchFactory watch = ((HttpContext) context).getWatchFactory();
if (watch != null) { if (watch != null) {
this.servlets.stream().forEach((en) -> { this.servlets.stream().forEach(s -> {
watch.inject(en.getKey()); watch.inject(s);
}); });
} }
if (config != null) { if (config != null) {
@@ -131,7 +131,8 @@ public final class HttpPrepareServlet extends PrepareServlet<HttpRequest, HttpRe
strmaps.put(mapping, servlet); 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) { private static boolean contains(String string, char... values) {
@@ -153,8 +154,8 @@ public final class HttpPrepareServlet extends PrepareServlet<HttpRequest, HttpRe
@Override @Override
public void destroy(Context context, AnyValue config) { public void destroy(Context context, AnyValue config) {
this.resourceHttpServlet.destroy(context, config); this.resourceHttpServlet.destroy(context, config);
this.servlets.stream().forEach((en) -> { this.servlets.stream().forEach(s -> {
en.getKey().destroy(context, en.getValue()); s.destroy(context, s.conf);
}); });
} }

View File

@@ -11,7 +11,6 @@ import com.wentch.redkale.watch.*;
import java.net.*; import java.net.*;
import java.nio.*; import java.nio.*;
import java.util.*; import java.util.*;
import java.util.AbstractMap.SimpleEntry;
import java.util.concurrent.atomic.*; import java.util.concurrent.atomic.*;
/** /**
@@ -20,8 +19,6 @@ import java.util.concurrent.atomic.*;
*/ */
public final class HttpServer extends Server { public final class HttpServer extends Server {
private final Map<SimpleEntry<HttpServlet, AnyValue>, String[]> servlets = new HashMap<>();
private String contextPath; private String contextPath;
public HttpServer() { public HttpServer() {
@@ -29,7 +26,7 @@ public final class HttpServer extends Server {
} }
public HttpServer(long serverStartTime, final WatchFactory watch) { public HttpServer(long serverStartTime, final WatchFactory watch) {
super(serverStartTime, "TCP", watch); super(serverStartTime, "TCP", new HttpPrepareServlet(), watch);
} }
@Override @Override
@@ -40,7 +37,7 @@ public final class HttpServer extends Server {
} }
public void addHttpServlet(HttpServlet servlet, AnyValue conf, String... mappings) { 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 @Override
@@ -56,13 +53,8 @@ public final class HttpServer extends Server {
e.clear(); e.clear();
return true; return true;
}); });
HttpPrepareServlet prepare = new HttpPrepareServlet(); final List<String[]> defaultAddHeaders = new ArrayList<>();
this.servlets.entrySet().stream().forEach((en) -> { final List<String[]> defaultSetHeaders = new ArrayList<>();
prepare.addHttpServlet(en.getKey().getKey(), en.getKey().getValue(), en.getValue());
});
this.servlets.clear();
String[][] defaultAddHeaders = null;
String[][] defaultSetHeaders = null;
HttpCookie defaultCookie = null; HttpCookie defaultCookie = null;
String remoteAddrHeader = null; String remoteAddrHeader = null;
if (config != null) { if (config != null) {
@@ -83,29 +75,27 @@ public final class HttpServer extends Server {
if (resps != null) { if (resps != null) {
AnyValue[] addHeaders = resps.getAnyValues("addheader"); AnyValue[] addHeaders = resps.getAnyValues("addheader");
if (addHeaders.length > 0) { if (addHeaders.length > 0) {
defaultAddHeaders = new String[addHeaders.length][];
for (int i = 0; i < addHeaders.length; i++) { for (int i = 0; i < addHeaders.length; i++) {
String val = addHeaders[i].getValue("value"); String val = addHeaders[i].getValue("value");
if (val == null) continue; if (val == null) continue;
if (val.startsWith("request.headers.")) { 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.")) { } else if (val.startsWith("system.property.")) {
String v = System.getProperty(val.substring("system.property.".length())); 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 { } else {
defaultAddHeaders[i] = new String[]{addHeaders[i].getValue("name"), val}; defaultAddHeaders.add(new String[]{addHeaders[i].getValue("name"), val});
} }
} }
} }
AnyValue[] setHeaders = resps.getAnyValues("setheader"); AnyValue[] setHeaders = resps.getAnyValues("setheader");
if (setHeaders.length > 0) { if (setHeaders.length > 0) {
defaultSetHeaders = new String[setHeaders.length][];
for (int i = 0; i < setHeaders.length; i++) { for (int i = 0; i < setHeaders.length; i++) {
String val = setHeaders[i].getValue("value"); String val = setHeaders[i].getValue("value");
if (val != null && val.startsWith("request.headers.")) { 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 { } 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[][] addHeaders = defaultAddHeaders.isEmpty() ? null : defaultAddHeaders.toArray(new String[defaultAddHeaders.size()][]);
final String[][] setHeaders = defaultSetHeaders; final String[][] setHeaders = defaultSetHeaders.isEmpty() ? null : defaultSetHeaders.toArray(new String[defaultSetHeaders.size()][]);
final HttpCookie defCookie = defaultCookie; final HttpCookie defCookie = defaultCookie;
final String addrHeader = remoteAddrHeader; final String addrHeader = remoteAddrHeader;
AtomicLong createResponseCounter = watch == null ? new AtomicLong() : watch.createWatchNumber("HTTP_" + port + ".Response.creatCounter"); AtomicLong createResponseCounter = watch == null ? new AtomicLong() : watch.createWatchNumber("HTTP_" + port + ".Response.creatCounter");
AtomicLong cycleResponseCounter = watch == null ? new AtomicLong() : watch.createWatchNumber("HTTP_" + port + ".Response.cycleCounter"); AtomicLong cycleResponseCounter = watch == null ? new AtomicLong() : watch.createWatchNumber("HTTP_" + port + ".Response.cycleCounter");
ObjectPool<Response> responsePool = HttpResponse.createPool(createResponseCounter, cycleResponseCounter, this.responsePoolSize, null); ObjectPool<Response> responsePool = HttpResponse.createPool(createResponseCounter, cycleResponseCounter, this.responsePoolSize, null);
HttpContext httpcontext = new HttpContext(this.serverStartTime, this.logger, executor, bufferPool, responsePool, 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) responsePool.setCreator((Object... params)
-> new HttpResponse(httpcontext, new HttpRequest(httpcontext, httpcontext.jsonFactory, addrHeader), addHeaders, setHeaders, defCookie)); -> new HttpResponse(httpcontext, new HttpRequest(httpcontext, httpcontext.jsonFactory, addrHeader), addHeaders, setHeaders, defCookie));
return httpcontext; return httpcontext;

View File

@@ -6,6 +6,7 @@
package com.wentch.redkale.net.http; package com.wentch.redkale.net.http;
import com.wentch.redkale.net.Servlet; 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> { public abstract class HttpServlet implements Servlet<HttpRequest, HttpResponse> {
AnyValue conf; //当前HttpServlet的配置
@Override @Override
public final boolean equals(Object obj) { public final boolean equals(Object obj) {
return obj != null && obj.getClass() == this.getClass(); return obj != null && obj.getClass() == this.getClass();

View File

@@ -6,7 +6,6 @@
package com.wentch.redkale.net.http; package com.wentch.redkale.net.http;
import com.wentch.redkale.net.*; import com.wentch.redkale.net.*;
import com.wentch.redkale.service.*;
import java.io.*; import java.io.*;
import java.util.*; import java.util.*;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
@@ -41,7 +40,7 @@ public abstract class WebSocket {
WebSocketGroup group; WebSocketGroup group;
WebSocketNodeService nodeService; WebSocketNode node;
Serializable sessionid; Serializable sessionid;
@@ -107,6 +106,16 @@ public abstract class WebSocket {
send(new WebSocketPacket(data, last)); 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节点发送文本消息 * 给指定groupid的WebSocketGroup下所有WebSocket节点发送文本消息
@@ -201,21 +210,13 @@ public abstract class WebSocket {
} }
private int sendMessage(Serializable groupid, boolean recent, String text, boolean last) { private int sendMessage(Serializable groupid, boolean recent, String text, boolean last) {
if (nodeService == null) return WebSocketNodeService.RETCODE_NODESERVICE_NULL; if (node == null) return WebSocketNode.RETCODE_NODESERVICE_NULL;
if (groupid == this.groupid) { return node.sendMessage(groupid, recent, text, last);
return nodeService.onSend(this.engine.getEngineid(), groupid, recent, text, last);
} else {
return nodeService.send(this.engine.getEngineid(), groupid, recent, text, last);
}
} }
private int sendMessage(Serializable groupid, boolean recent, byte[] data, boolean last) { private int sendMessage(Serializable groupid, boolean recent, byte[] data, boolean last) {
if (nodeService == null) return WebSocketNodeService.RETCODE_NODESERVICE_NULL; if (node == null) return WebSocketNode.RETCODE_NODESERVICE_NULL;
if (groupid == this.groupid) { return node.sendMessage(groupid, recent, data, last);
return nodeService.onSend(this.engine.getEngineid(), groupid, recent, data, last);
} else {
return nodeService.send(this.engine.getEngineid(), groupid, recent, data, last);
}
} }
/** /**
@@ -309,9 +310,7 @@ public abstract class WebSocket {
* *
* @return * @return
*/ */
public Serializable createGroupid() { protected abstract Serializable createGroupid();
return null;
}
/** /**
* *

View File

@@ -15,21 +15,11 @@ import java.util.concurrent.*;
*/ */
public final class WebSocketEngine { public final class WebSocketEngine {
private static final Map<String, WebSocketEngine> globals = new ConcurrentHashMap<>();
private final String engineid; private final String engineid;
private final Map<Serializable, WebSocketGroup> containers = new ConcurrentHashMap<>(); private final Map<Serializable, WebSocketGroup> containers = new ConcurrentHashMap<>();
static WebSocketEngine create(String engineid) { protected WebSocketEngine(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) {
this.engineid = engineid; this.engineid = engineid;
} }
@@ -38,7 +28,6 @@ public final class WebSocketEngine {
if (group == null) { if (group == null) {
group = new WebSocketGroup(socket.groupid); group = new WebSocketGroup(socket.groupid);
containers.put(socket.groupid, group); containers.put(socket.groupid, group);
group.recentWebSocket = socket;
} }
group.add(socket); group.add(socket);
} }

View File

@@ -18,7 +18,7 @@ public final class WebSocketGroup {
private final Serializable groupid; private final Serializable groupid;
WebSocket recentWebSocket; private WebSocket recentWebSocket;
private final List<WebSocket> list = new CopyOnWriteArrayList<>(); private final List<WebSocket> list = new CopyOnWriteArrayList<>();
@@ -42,9 +42,14 @@ public final class WebSocketGroup {
void add(WebSocket socket) { void add(WebSocket socket) {
socket.group = this; socket.group = this;
this.recentWebSocket = socket;
list.add(socket); list.add(socket);
} }
void setRecentWebSocket(WebSocket socket) {
this.recentWebSocket = socket;
}
public final boolean isEmpty() { public final boolean isEmpty() {
return list.isEmpty(); return list.isEmpty();
} }
@@ -71,6 +76,22 @@ public final class WebSocketGroup {
attributes.put(name, value); 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 @Override
public String toString() { public String toString() {
return "{groupid: " + groupid + ", list.size: " + (list == null ? -1 : list.size()) + "}"; return "{groupid: " + groupid + ", list.size: " + (list == null ? -1 : list.size()) + "}";

View 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);
}
}

View File

@@ -6,6 +6,7 @@
package com.wentch.redkale.net.http; package com.wentch.redkale.net.http;
import com.wentch.redkale.util.Utility; import com.wentch.redkale.util.Utility;
import java.io.*;
/** /**
* *
@@ -54,6 +55,18 @@ public final class WebSocketPacket {
this(payload, true); 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) { public WebSocketPacket(String payload, boolean fin) {
this.type = PacketType.TEXT; this.type = PacketType.TEXT;
this.payload = payload; this.payload = payload;

View File

@@ -61,7 +61,7 @@ public class WebSocketRunner implements Runnable {
public void run() { public void run() {
final boolean debug = this.coder.debugable; final boolean debug = this.coder.debugable;
try { try {
if (webSocket.nodeService != null) webSocket.nodeService.connectSelf(webSocket.groupid); if (webSocket.node != null) webSocket.node.connect(webSocket.groupid, webSocket.engine.getEngineid());
webSocket.onConnected(); webSocket.onConnected();
channel.setReadTimeoutSecond(300); //读取超时5分钟 channel.setReadTimeoutSecond(300); //读取超时5分钟
if (channel.isOpen()) { if (channel.isOpen()) {
@@ -117,7 +117,7 @@ public class WebSocketRunner implements Runnable {
readBuffer.clear(); readBuffer.clear();
channel.read(readBuffer, null, this); channel.read(readBuffer, null, this);
} }
webSocket.group.recentWebSocket = webSocket; webSocket.group.setRecentWebSocket(webSocket);
if (packet.type == PacketType.TEXT) { if (packet.type == PacketType.TEXT) {
webSocket.onMessage(packet.getPayload()); webSocket.onMessage(packet.getPayload());
} else if (packet.type == PacketType.BINARY) { } else if (packet.type == PacketType.BINARY) {
@@ -229,9 +229,9 @@ public class WebSocketRunner implements Runnable {
readBuffer = null; readBuffer = null;
writeBuffer = null; writeBuffer = null;
engine.remove(webSocket); engine.remove(webSocket);
if (webSocket.nodeService != null) { if (webSocket.node != null) {
WebSocketGroup group = webSocket.getWebSocketGroup(); 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); webSocket.onClose(0, null);
} }

View File

@@ -6,9 +6,9 @@
package com.wentch.redkale.net.http; package com.wentch.redkale.net.http;
import com.wentch.redkale.net.*; import com.wentch.redkale.net.*;
import com.wentch.redkale.service.*;
import com.wentch.redkale.util.*; import com.wentch.redkale.util.*;
import java.io.*; import java.io.*;
import java.net.*;
import java.nio.*; import java.nio.*;
import java.nio.channels.*; import java.nio.channels.*;
import java.security.*; import java.security.*;
@@ -33,7 +33,7 @@ import javax.annotation.*;
* *
* @author zhangjx * @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()); 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; protected final boolean wsbinary = getClass().getAnnotation(WebSocketBinary.class) != null;
@Resource @Resource(name = "$")
protected WebSocketNodeService nodeService; protected WebSocketNode node;
protected final WebSocketEngine engine = WebSocketEngine.create(this.getClass().getName() + "-" + Arrays.toString(this.getClass().getAnnotation(WebServlet.class).value())); protected WebSocketEngine engine;
@Override @Override
public void init(Context context, AnyValue conf) { public void init(Context context, AnyValue conf) {
if (nodeService != null) { InetSocketAddress addr = context.getServerAddress();
nodeService.addWebSocketEngine(engine); this.engine = new WebSocketEngine(addr.getHostString() + ":" + addr.getPort() + "-" + name());
nodeService.initUserNodes(); this.node.addWebSocketEngine(engine);
} this.node.init(conf);
} }
@Override @Override
public void destroy(Context context, AnyValue conf) { public void destroy(Context context, AnyValue conf) {
this.node.destroy(conf);
super.destroy(context, conf); super.destroy(context, conf);
engine.close(); engine.close();
} }
@@ -87,7 +88,7 @@ public abstract class WebSocketServlet extends HttpServlet {
} }
final WebSocket webSocket = this.createWebSocket(); final WebSocket webSocket = this.createWebSocket();
webSocket.engine = engine; webSocket.engine = engine;
webSocket.nodeService = nodeService; webSocket.node = node;
Serializable sessionid = webSocket.onOpen(request); Serializable sessionid = webSocket.onOpen(request);
if (sessionid == null) { if (sessionid == null) {
if (debug) logger.finer("WebSocket connect abort, Not found sessionid. request=" + request); 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(); protected abstract WebSocket createWebSocket();
} }

View File

@@ -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);
}
}

View 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;
}
}

View File

@@ -10,8 +10,12 @@ import com.wentch.redkale.net.*;
import com.wentch.redkale.net.sncp.SncpClient.SncpAction; import com.wentch.redkale.net.sncp.SncpClient.SncpAction;
import com.wentch.redkale.service.*; import com.wentch.redkale.service.*;
import com.wentch.redkale.util.*; import com.wentch.redkale.util.*;
import java.lang.reflect.*;
import java.net.*;
import java.util.*;
import jdk.internal.org.objectweb.asm.*; import jdk.internal.org.objectweb.asm.*;
import static jdk.internal.org.objectweb.asm.Opcodes.*; 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 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]; private static final byte[] hashes = new byte[255];
static { static {
@@ -40,12 +50,23 @@ public abstract class Sncp {
private 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) { public static long hash(final Class clazz) {
if (clazz == null) return Long.MIN_VALUE; if (clazz == null) return Long.MIN_VALUE;
long rs = hash(clazz.getSimpleName()); long rs = hash(clazz.getSimpleName());
return (rs < Integer.MAX_VALUE) ? rs | 0xF00000000L : rs; 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) { public static DLong hash(final java.lang.reflect.Method method) {
if (method == null) return new DLong(-1L, -1L); if (method == null) return new DLong(-1L, -1L);
long rs1 = hash(method.getName()); long rs1 = hash(method.getName());
@@ -95,61 +116,103 @@ public abstract class Sncp {
return Math.abs(rs); 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 <T>
* @param serviceName * @param name
* @param remote
* @param serviceClass * @param serviceClass
* @return * @return
*/ */
@SuppressWarnings("unchecked") @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 (serviceClass == null) return null;
if (!Service.class.isAssignableFrom(serviceClass)) return null; if (!Service.class.isAssignableFrom(serviceClass)) return serviceClass;
int mod = serviceClass.getModifiers(); int mod = serviceClass.getModifiers();
if (!java.lang.reflect.Modifier.isPublic(mod)) return null; if (!java.lang.reflect.Modifier.isPublic(mod)) return serviceClass;
if (java.lang.reflect.Modifier.isAbstract(mod)) return null; 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 supDynName = serviceClass.getName().replace('.', '/');
final String clientName = SncpClient.class.getName().replace('.', '/'); final String clientName = SncpClient.class.getName().replace('.', '/');
final String clientDesc = Type.getDescriptor(SncpClient.class); final String clientDesc = Type.getDescriptor(SncpClient.class);
final String convertDesc = Type.getDescriptor(BsonConvert.class); final String convertDesc = Type.getDescriptor(BsonConvert.class);
final String transportDesc = Type.getDescriptor(Transport.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(); 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 { try {
return (T) Class.forName(newDynName.replace('/', '.')).newInstance(); return (Class<T>) Class.forName(newDynName.replace('/', '.'));
} catch (Exception ex) { } catch (Exception ex) {
} }
final SncpClient client = new SncpClient(serviceName, serviceClass);
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
ClassWriter cw = new ClassWriter(0); ClassWriter cw = new ClassWriter(0);
FieldVisitor fv; FieldVisitor fv;
@@ -158,24 +221,436 @@ public abstract class Sncp {
cw.visit(V1_8, ACC_PUBLIC + ACC_FINAL + ACC_SUPER, newDynName, null, supDynName, null); 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(); 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 = fv.visitAnnotation("Ljavax/annotation/Resource;", true);
av0.visitEnd(); av0.visitEnd();
fv.visitEnd(); fv.visitEnd();
} }
{ {
fv = cw.visitField(ACC_PRIVATE, "transport", transportDesc, null, null); 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.visitEnd(); fv.visitEnd();
} }
{ {
fv = cw.visitField(ACC_PUBLIC, "client", clientDesc, null, null); fv = cw.visitField(ACC_PRIVATE, "_client", clientDesc, null, null);
fv.visitEnd(); fv.visitEnd();
} }
{ //构造函数 { //构造函数
@@ -199,6 +674,13 @@ public abstract class Sncp {
mv.visitMaxs(0, 2); mv.visitMaxs(0, 2);
mv.visitEnd(); 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; int i = -1;
for (final SncpAction entry : client.actions) { for (final SncpAction entry : client.actions) {
final int index = ++i; 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 = new DebugMethodVisitor(cw.visitMethod(ACC_PUBLIC, method.getName(), Type.getMethodDescriptor(method), null, null));
//mv.setDebug(true); //mv.setDebug(true);
mv.visitVarInsn(ALOAD, 0); mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, newDynName, "client", clientDesc); mv.visitFieldInsn(GETFIELD, newDynName, "_client", clientDesc);
mv.visitVarInsn(ALOAD, 0); mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, newDynName, "convert", convertDesc); mv.visitFieldInsn(GETFIELD, newDynName, "_convert", convertDesc);
mv.visitVarInsn(ALOAD, 0); mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, newDynName, "transport", transportDesc); mv.visitFieldInsn(GETFIELD, newDynName, "_transport", transportDesc);
if (index <= 5) { if (index <= 5) {
mv.visitInsn(ICONST_0 + index); mv.visitInsn(ICONST_0 + index);
} else { } else {
@@ -252,7 +734,6 @@ public abstract class Sncp {
} else { } else {
mv.visitVarInsn(ALOAD, insn); mv.visitVarInsn(ALOAD, insn);
} }
//mv.visitVarInsn(ALOAD, 1);
mv.visitInsn(AASTORE); mv.visitInsn(AASTORE);
} }
} }
@@ -300,7 +781,12 @@ public abstract class Sncp {
}.loadClass(newDynName.replace('/', '.'), bytes); }.loadClass(newDynName.replace('/', '.'), bytes);
try { try {
T rs = (T) newClazz.newInstance(); 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; return rs;
} catch (Exception ex) { } catch (Exception ex) {
throw new RuntimeException(ex); throw new RuntimeException(ex);

View File

@@ -8,9 +8,9 @@ package com.wentch.redkale.net.sncp;
import com.wentch.redkale.convert.bson.*; import com.wentch.redkale.convert.bson.*;
import com.wentch.redkale.net.*; import com.wentch.redkale.net.*;
import static com.wentch.redkale.net.sncp.SncpRequest.HEADER_SIZE; import static com.wentch.redkale.net.sncp.SncpRequest.HEADER_SIZE;
import com.wentch.redkale.service.*;
import com.wentch.redkale.util.*; import com.wentch.redkale.util.*;
import java.lang.reflect.*; import java.lang.reflect.*;
import java.net.*;
import java.nio.*; import java.nio.*;
import java.util.*; import java.util.*;
import java.util.concurrent.*; 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 nameid;
protected final long serviceid; protected final long serviceid;
protected final SncpAction[] actions; 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"); 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.remote = remote;
this.serviceid = Sncp.hash(serviceClass); 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<>(); final List<SncpAction> methodens = new ArrayList<>();
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
Set<DLong> actionids = new HashSet<>(); for (java.lang.reflect.Method method : parseMethod(serviceClass, onlySncpDyn)) {
for (java.lang.reflect.Method method : serviceClass.getDeclaredMethods()) { 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; if (method.isSynthetic()) continue;
final int mod = method.getModifiers(); final int mod = method.getModifiers();
if (!Modifier.isPublic(mod) && !Modifier.isProtected(mod)) continue;
if (Modifier.isStatic(mod)) continue; if (Modifier.isStatic(mod)) continue;
if (Modifier.isFinal(mod)) continue; if (Modifier.isFinal(mod)) continue;
if (method.getName().equals("getClass") || method.getName().equals("toString")) 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("equals") || method.getName().equals("hashCode")) continue;
if (method.getName().equals("notify") || method.getName().equals("notifyAll") || method.getName().equals("wait")) 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; if (method.getName().equals("init") || method.getName().equals("destroy") || method.getName().equals("name")) continue;
Method onMethod = getOnMethod(serviceClass, method); if (onlySncpDyn && method.getAnnotation(SncpDyn.class) == null) continue;
SncpAction en = new SncpAction(method, onMethod == null ? Sncp.hash(method) : Sncp.hash(onMethod)); DLong actionid = Sncp.hash(method);
if (actionids.contains(en.actionid)) { Method old = actionids.get(actionid);
throw new RuntimeException(serviceClass.getName() + " have one more same action(Method=" + method + ", actionid=" + en.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.put(actionid, method);
actionids.add(en.actionid); if (method.getAnnotation(SncpDyn.class) != null) {
} multis.add(method);
this.actions = methodens.toArray(new SncpAction[methodens.size()]); } else {
logger.fine("Load " + this.getClass().getSimpleName() + "(serviceClass = " + serviceClass.getName() + ", serviceid =" + serviceid + ", serviceName =" + serviceName + ", actions = " + methodens + ")"); list.add(method);
}
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());
} }
} }
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) { 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) { public <T> void remote(final BsonConvert convert, Transport[] transports, boolean run, final int index, final Object... params) {
//---------------------head---------------------------------- if (!run) return;
buffer.putLong(seqid); //序列号 this.remote(false, convert, transports, run, index, params);
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长度
} }
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; int bodyLength = 2;
Type[] myparamtypes = actions[index].paramTypes; Type[] myparamtypes = action.paramTypes;
byte[][] bytesarray = new byte[params.length][]; byte[][] bytesarray = new byte[params.length][];
for (int i = 0; i < bytesarray.length; i++) { for (int i = 0; i < bytesarray.length; i++) {
bytesarray[i] = convert.convertTo(myparamtypes[i], params[i]); bytesarray[i] = convert.convertTo(myparamtypes[i], params[i]);
bodyLength += 4 + bytesarray[i].length; bodyLength += 4 + bytesarray[i].length;
} }
final SncpAction action = actions[index];
final long seqid = System.nanoTime(); final long seqid = System.nanoTime();
final DLong actionid = action.actionid; final DLong actionid = action.actionid;
final ByteBuffer buffer = transport.pollBuffer();
final AsyncConnection conn = transport.pollConnection(); final AsyncConnection conn = transport.pollConnection();
if (conn == null || !conn.isOpen()) return null;
final ByteBuffer buffer = transport.pollBuffer();
final int readto = conn.getReadTimeoutSecond(); final int readto = conn.getReadTimeoutSecond();
final int writeto = conn.getWriteTimeoutSecond(); final int writeto = conn.getWriteTimeoutSecond();
try { try {
@@ -200,12 +245,14 @@ public final class SncpClient {
long ractionid1 = buffer.getLong(); long ractionid1 = buffer.getLong();
long ractionid2 = buffer.getLong(); long ractionid2 = buffer.getLong();
if (!actionid.compare(ractionid1, ractionid2)) throw new RuntimeException("sncp send actionid = " + actionid + ", but receive actionid =(" + ractionid1 + "_" + ractionid2 + ")"); 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(); final int frameCount = buffer.get();
if (frameCount < 1) throw new RuntimeException("sncp send nameid = " + nameid + ", but frame.count =" + frameCount); if (frameCount < 1) throw new RuntimeException("sncp send nameid = " + nameid + ", but frame.count =" + frameCount);
int frameIndex = buffer.get(); int frameIndex = buffer.get();
if (frameIndex < 0 || frameIndex >= frameCount) throw new RuntimeException("sncp send nameid = " + nameid + ", but frame.count =" + frameCount + " & frame.index =" + frameIndex); if (frameIndex < 0 || frameIndex >= frameCount) throw new RuntimeException("sncp send nameid = " + nameid + ", but frame.count =" + frameCount + " & frame.index =" + frameIndex);
final int retcode = buffer.getInt(); 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 int bodylen = buffer.getInt();
final byte[] body = new byte[bodylen]; final byte[] body = new byte[bodylen];
if (frameCount == 1) { 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长度
}
} }

View File

@@ -53,8 +53,8 @@ public final class SncpContext extends Context {
protected final BsonFactory bsonFactory; protected final BsonFactory bsonFactory;
public SncpContext(long serverStartTime, Logger logger, ExecutorService executor, ObjectPool<ByteBuffer> bufferPool, public SncpContext(long serverStartTime, Logger logger, ExecutorService executor, ObjectPool<ByteBuffer> bufferPool,
ObjectPool<Response> responsePool, int maxbody, Charset charset, InetSocketAddress address, ObjectPool<Response> responsePool, int maxbody, Charset charset, InetSocketAddress address, PrepareServlet prepare,
PrepareServlet prepare, WatchFactory watch, int readTimeoutSecond, int writeTimeoutSecond) { WatchFactory watch, int readTimeoutSecond, int writeTimeoutSecond) {
super(serverStartTime, logger, executor, bufferPool, responsePool, maxbody, charset, super(serverStartTime, logger, executor, bufferPool, responsePool, maxbody, charset,
address, prepare, watch, readTimeoutSecond, writeTimeoutSecond); address, prepare, watch, readTimeoutSecond, writeTimeoutSecond);
this.bsonFactory = BsonFactory.root(); this.bsonFactory = BsonFactory.root();

View 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
}

View File

@@ -6,7 +6,6 @@
package com.wentch.redkale.net.sncp; package com.wentch.redkale.net.sncp;
import com.wentch.redkale.convert.bson.*; import com.wentch.redkale.convert.bson.*;
import static com.wentch.redkale.net.sncp.SncpClient.getOnMethod;
import com.wentch.redkale.service.*; import com.wentch.redkale.service.*;
import com.wentch.redkale.util.*; import com.wentch.redkale.util.*;
import java.io.*; import java.io.*;
@@ -22,10 +21,16 @@ import jdk.internal.org.objectweb.asm.Type;
* *
* @author zhangjx * @author zhangjx
*/ */
public class SncpDynServlet extends SncpServlet { public final class SncpDynServlet extends SncpServlet {
private final Logger logger = Logger.getLogger(SncpDynServlet.class.getSimpleName()); 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 nameid;
private final long serviceid; 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) { public SncpDynServlet(final BsonConvert convert, final String serviceName, final Service service, final AnyValue conf) {
this.conf = 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.nameid = Sncp.hash(serviceName);
this.serviceid = Sncp.hash(serviceClass); this.serviceid = Sncp.hash(type);
Set<DLong> actionids = new HashSet<>(); 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 (method.isSynthetic()) continue;
if (Modifier.isStatic(method.getModifiers())) continue; if (Modifier.isStatic(method.getModifiers())) continue;
if (Modifier.isFinal(method.getModifiers())) continue; if (Modifier.isFinal(method.getModifiers())) continue;
if (method.getName().equals("getClass") || method.getName().equals("toString")) 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("equals") || method.getName().equals("hashCode")) continue;
if (method.getName().equals("notify") || method.getName().equals("notifyAll") || method.getName().equals("wait")) 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; if (method.getName().equals("init") || method.getName().equals("destroy") || method.getName().equals("name")) continue;
Method onMethod = getOnMethod(serviceClass, method);
if (onMethod != null) method = onMethod;
final DLong actionid = Sncp.hash(method); final DLong actionid = Sncp.hash(method);
SncpServletAction action = SncpServletAction.create(service, actionid, method); SncpServletAction action = SncpServletAction.create(service, actionid, method);
action.convert = convert; action.convert = convert;
if (actionids.contains(actionid)) { if (actionids.contains(actionid)) {
throw new RuntimeException(serviceClass.getName() throw new RuntimeException(type.getName() + " have action(Method=" + method + ", actionid=" + actionid + ") same to (" + actions.get(actionid).method + ")");
+ " have action(Method=" + method + ", actionid=" + actionid + ") same to (" + actions.get(actionid).method + ")");
} }
actions.put(actionid, action); actions.put(actionid, action);
actionids.add(actionid); actionids.add(actionid);
} }
if (!logger.isLoggable(Level.FINE)) return; }
StringBuilder sb = new StringBuilder();
sb.append("{"); @Override
actions.forEach((x, y) -> sb.append('{').append(x).append(',').append(y.method.getName()).append("},")); public String toString() {
sb.append("}"); return this.getClass().getSimpleName() + "(type=" + type.getName() + ", serviceid=" + serviceid + ", name=" + serviceName + ", actions.size=" + actions.size() + ")";
logger.fine(this.getClass().getSimpleName() + "(serviceClass = " + serviceClass.getName() + ", serviceid =" + serviceid + ", serviceName =" + serviceName + ", actions = " + sb + ") loaded");
} }
@Override @Override
@@ -79,6 +81,7 @@ public class SncpDynServlet extends SncpServlet {
@Override @Override
public void execute(SncpRequest request, SncpResponse response) throws IOException { public void execute(SncpRequest request, SncpResponse response) throws IOException {
SncpServletAction action = actions.get(request.getActionid()); SncpServletAction action = actions.get(request.getActionid());
//if (finest) logger.log(Level.FINEST, "sncpdyn.execute: " + request + ", " + (action == null ? "null" : action.method));
if (action == null) { if (action == null) {
response.finish(SncpResponse.RETCODE_ILLACTIONID, null); //无效actionid response.finish(SncpResponse.RETCODE_ILLACTIONID, null); //无效actionid
} else { } else {
@@ -103,25 +106,25 @@ public class SncpDynServlet extends SncpServlet {
public abstract byte[] action(byte[][] bytes) throws Throwable; public abstract byte[] action(byte[][] bytes) throws Throwable;
/* /*
* *
* public class TestService implements Service { * 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 class DynActionTestService_change extends SncpServletAction {
* *
* public TestService service; * public TestService service;
* *
* @Override * @Override
* public byte[] action(byte[][] bytes) throws Throwable { * public byte[] action(byte[][] bytes) throws Throwable {
* TestBean arg1 = convert.convertFrom(paramTypes[1], bytes[1]); * TestBean arg1 = convert.convertFrom(paramTypes[1], bytes[1]);
* String arg2 = convert.convertFrom(paramTypes[2], bytes[2]); * String arg2 = convert.convertFrom(paramTypes[2], bytes[2]);
* int arg3 = convert.convertFrom(paramTypes[3], bytes[3]); * int arg3 = convert.convertFrom(paramTypes[3], bytes[3]);
* Object rs = service.change(arg1, arg2, arg3); * Object rs = service.change(arg1, arg2, arg3);
* return convert.convertTo(paramTypes[0], rs); * return convert.convertTo(paramTypes[0], rs);
* } * }
* } * }
*/ */
/** /**

View File

@@ -26,17 +26,27 @@ public class SncpPrepareServlet extends PrepareServlet<SncpRequest, SncpResponse
public void addSncpServlet(SncpServlet servlet) { public void addSncpServlet(SncpServlet servlet) {
if (servlet.getNameid() == 0) { if (servlet.getNameid() == 0) {
singlemaps.put(servlet.getServiceid(), servlet); synchronized (singlemaps) {
} else { singlemaps.put(servlet.getServiceid(), servlet);
Map<Long, SncpServlet> m = maps.get(servlet.getServiceid()); }
if (m == null) { } else {
m = new HashMap<>(); synchronized (maps) {
maps.put(servlet.getServiceid(), m); 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 @Override
public void init(Context context, AnyValue config) { public void init(Context context, AnyValue config) {
Collection<Map<Long, SncpServlet>> values = this.maps.values(); Collection<Map<Long, SncpServlet>> values = this.maps.values();
@@ -62,6 +72,10 @@ public class SncpPrepareServlet extends PrepareServlet<SncpRequest, SncpResponse
SncpServlet servlet; SncpServlet servlet;
if (request.getNameid() == 0) { if (request.getNameid() == 0) {
servlet = singlemaps.get(request.getServiceid()); servlet = singlemaps.get(request.getServiceid());
if (servlet == null) {
response.finish(SncpResponse.RETCODE_ILLSERVICEID, null); //无效serviceid
return;
}
} else { } else {
Map<Long, SncpServlet> m = maps.get(request.getServiceid()); Map<Long, SncpServlet> m = maps.get(request.getServiceid());
if (m == null) { if (m == null) {

View File

@@ -9,6 +9,7 @@ import com.wentch.redkale.convert.bson.*;
import com.wentch.redkale.net.*; import com.wentch.redkale.net.*;
import com.wentch.redkale.net.sncp.SncpContext.RequestEntry; import com.wentch.redkale.net.sncp.SncpContext.RequestEntry;
import com.wentch.redkale.util.*; import com.wentch.redkale.util.*;
import java.net.*;
import java.nio.*; import java.nio.*;
/** /**
@@ -16,36 +17,40 @@ import java.nio.*;
* @author zhangjx * @author zhangjx
*/ */
public final class SncpRequest extends Request { public final class SncpRequest extends Request {
public static final int HEADER_SIZE = 52; public static final int HEADER_SIZE = 60;
protected final BsonConvert convert; protected final BsonConvert convert;
private long seqid; private long seqid;
private int framecount; private int framecount;
private int frameindex; private int frameindex;
private long nameid; private long nameid;
private long serviceid; private long serviceid;
private DLong actionid; private DLong actionid;
private int bodylength; private int bodylength;
private byte[][] paramBytes; private byte[][] paramBytes;
private boolean ping; private boolean ping;
private byte[] body; private byte[] body;
private byte[] bufferbytes = new byte[4];
private InetSocketAddress remoteAddress;
protected SncpRequest(SncpContext context, BsonFactory factory) { protected SncpRequest(SncpContext context, BsonFactory factory) {
super(context); super(context);
this.convert = factory.getConvert(); this.convert = factory.getConvert();
} }
@Override @Override
protected int readHeader(ByteBuffer buffer) { protected int readHeader(ByteBuffer buffer) {
if (buffer.remaining() < HEADER_SIZE) { if (buffer.remaining() < HEADER_SIZE) {
@@ -61,6 +66,11 @@ public final class SncpRequest extends Request {
this.serviceid = buffer.getLong(); this.serviceid = buffer.getLong();
this.nameid = buffer.getLong(); this.nameid = buffer.getLong();
this.actionid = new DLong(buffer.getLong(), 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.framecount = buffer.get();
this.frameindex = buffer.get(); this.frameindex = buffer.get();
if (buffer.getInt() != 0) { if (buffer.getInt() != 0) {
@@ -85,21 +95,21 @@ public final class SncpRequest extends Request {
RequestEntry entry = scontext.getRequestEntity(this.seqid); RequestEntry entry = scontext.getRequestEntity(this.seqid);
if (entry == null) entry = scontext.addRequestEntity(this.seqid, new byte[this.bodylength]); if (entry == null) entry = scontext.addRequestEntity(this.seqid, new byte[this.bodylength]);
entry.add(buffer, (this.framecount - this.frameindex - 1) * (buffer.capacity() - HEADER_SIZE)); entry.add(buffer, (this.framecount - this.frameindex - 1) * (buffer.capacity() - HEADER_SIZE));
if (entry.isCompleted()) { //数据读取完毕 if (entry.isCompleted()) { //数据读取完毕
this.body = entry.body; this.body = entry.body;
scontext.removeRequestEntity(this.seqid); scontext.removeRequestEntity(this.seqid);
return 0; return 0;
} else { } else {
scontext.expireRequestEntry(10 * 1000); //10秒过期 scontext.expireRequestEntry(10 * 1000); //10秒过期
} }
return Integer.MIN_VALUE; //多帧数据返回 Integer.MIN_VALUE return Integer.MIN_VALUE; //多帧数据返回 Integer.MIN_VALUE
} }
@Override @Override
protected void readBody(ByteBuffer buffer) { protected void readBody(ByteBuffer buffer) {
} }
@Override @Override
protected void prepare() { protected void prepare() {
if (this.body == null) return; if (this.body == null) return;
@@ -116,14 +126,14 @@ public final class SncpRequest extends Request {
} }
this.paramBytes = bbytes; this.paramBytes = bbytes;
} }
@Override @Override
public String toString() { public String toString() {
return SncpRequest.class.getSimpleName() + "{seqid=" + this.seqid return SncpRequest.class.getSimpleName() + "{seqid=" + this.seqid
+ ",serviceid=" + this.serviceid + ",actionid=" + this.actionid + ",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 @Override
protected void recycle() { protected void recycle() {
this.seqid = 0; this.seqid = 0;
@@ -135,31 +145,37 @@ public final class SncpRequest extends Request {
this.body = null; this.body = null;
this.paramBytes = null; this.paramBytes = null;
this.ping = false; this.ping = false;
this.remoteAddress = null;
this.bufferbytes[0] = 0;
super.recycle(); super.recycle();
} }
protected boolean isPing() { protected boolean isPing() {
return ping; return ping;
} }
public byte[][] getParamBytes() { public byte[][] getParamBytes() {
return paramBytes; return paramBytes;
} }
public long getSeqid() { public long getSeqid() {
return seqid; return seqid;
} }
public long getServiceid() { public long getServiceid() {
return serviceid; return serviceid;
} }
public long getNameid() { public long getNameid() {
return nameid; return nameid;
} }
public DLong getActionid() { public DLong getActionid() {
return actionid; return actionid;
} }
public InetSocketAddress getRemoteAddress() {
return remoteAddress;
}
} }

View File

@@ -26,11 +26,25 @@ public final class SncpResponse extends Response<SncpRequest> {
public static final int RETCODE_THROWEXCEPTION = 10011; //内部异常 public static final int RETCODE_THROWEXCEPTION = 10011; //内部异常
public static ObjectPool<Response> createPool(AtomicLong creatCounter, AtomicLong cycleCounter, int max, Creator<Response> creator) { 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) { protected SncpResponse(Context context, SncpRequest request) {
super(context, request); super(context, request);
this.addrBytes = context.getServerAddress().getAddress().getAddress();
this.addrPort = context.getServerAddress().getPort();
} }
public void finish(final int retcode, final byte[] bytes) { public void finish(final int retcode, final byte[] bytes) {
@@ -56,7 +70,7 @@ public final class SncpResponse extends Response<SncpRequest> {
pos += len; pos += len;
buffer.flip(); buffer.flip();
} }
finish(buffers); finish(buffers);
} }
} }
@@ -69,6 +83,8 @@ public final class SncpResponse extends Response<SncpRequest> {
DLong actionid = request.getActionid(); DLong actionid = request.getActionid();
buffer.putLong(actionid.getFirst()); buffer.putLong(actionid.getFirst());
buffer.putLong(actionid.getSecond()); buffer.putLong(actionid.getSecond());
buffer.put(addrBytes);
buffer.putInt(this.addrPort);
buffer.put((byte) frameCount); // frame count buffer.put((byte) frameCount); // frame count
buffer.put((byte) frameIndex); //frame index buffer.put((byte) frameIndex); //frame index
buffer.putInt(retcode); buffer.putInt(retcode);

View File

@@ -9,6 +9,7 @@ import com.wentch.redkale.convert.bson.*;
import com.wentch.redkale.net.*; import com.wentch.redkale.net.*;
import com.wentch.redkale.util.*; import com.wentch.redkale.util.*;
import com.wentch.redkale.watch.*; import com.wentch.redkale.watch.*;
import java.net.*;
import java.nio.*; import java.nio.*;
import java.util.*; import java.util.*;
import java.util.concurrent.atomic.*; import java.util.concurrent.atomic.*;
@@ -20,18 +21,45 @@ import java.util.concurrent.atomic.*;
*/ */
public final class SncpServer extends Server { public final class SncpServer extends Server {
private final List<ServiceEntry> services = new ArrayList<>(); protected InetSocketAddress nodeAddress;
public SncpServer(String protocol) { public SncpServer(String protocol) {
this(System.currentTimeMillis(), protocol, null); this(System.currentTimeMillis(), protocol, null, null);
} }
public SncpServer(long serverStartTime, String protocol, final WatchFactory watch) { public SncpServer(long serverStartTime, String protocol, InetSocketAddress nodeAddress, final WatchFactory watch) {
super(serverStartTime, protocol, watch); super(serverStartTime, protocol, new SncpPrepareServlet(), watch);
this.nodeAddress = nodeAddress;
} }
public void addService(ServiceEntry entry) { @Override
this.services.add(entry); 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 @Override
@@ -47,15 +75,11 @@ public final class SncpServer extends Server {
e.clear(); e.clear();
return true; 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 createResponseCounter = watch == null ? new AtomicLong() : watch.createWatchNumber("SNCP_" + port + ".Response.creatCounter");
AtomicLong cycleResponseCounter = watch == null ? new AtomicLong() : watch.createWatchNumber("SNCP_" + port + ".Response.cycleCounter"); AtomicLong cycleResponseCounter = watch == null ? new AtomicLong() : watch.createWatchNumber("SNCP_" + port + ".Response.cycleCounter");
ObjectPool<Response> responsePool = SncpResponse.createPool(createResponseCounter, cycleResponseCounter, this.responsePoolSize, null); ObjectPool<Response> responsePool = SncpResponse.createPool(createResponseCounter, cycleResponseCounter, this.responsePoolSize, null);
SncpContext sncpcontext = new SncpContext(this.serverStartTime, this.logger, executor, bufferPool, responsePool, 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))); responsePool.setCreator((Object... params) -> new SncpResponse(sncpcontext, new SncpRequest(sncpcontext, sncpcontext.bsonFactory)));
return sncpcontext; return sncpcontext;
} }

View File

@@ -8,10 +8,6 @@ package com.wentch.redkale.service;
import com.wentch.redkale.source.*; import com.wentch.redkale.source.*;
import com.wentch.redkale.util.*; import com.wentch.redkale.util.*;
import java.io.*; import java.io.*;
import java.util.*;
import java.util.AbstractMap.SimpleEntry;
import java.util.concurrent.*;
import java.util.logging.*;
import javax.annotation.*; import javax.annotation.*;
/** /**
@@ -21,279 +17,25 @@ import javax.annotation.*;
@AutoLoad(false) @AutoLoad(false)
public class DataCacheListenerService implements DataCacheListener, Service { public class DataCacheListenerService implements DataCacheListener, Service {
protected final Logger logger = Logger.getLogger(this.getClass().getSimpleName()); @Resource(name = "$")
private DataSource source;
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;
@Override @Override
public void init(AnyValue config) { @MultiRun(async = true)
if (finest) { public <T> void insertCache(Class<T> clazz, T... entitys) {
logger.finest(this.getClass().getSimpleName() + "-localgroup: " + localGroupName); ((DataDefaultSource) source).insertCache(clazz, entitys);
logger.finest(this.getClass().getSimpleName() + "-groups: " + groups);
logger.finest(this.getClass().getSimpleName() + "-sources: " + sourcesmap);
}
} }
@Override @Override
public <T> void insert(String sourceName, Class<T> clazz, T... entitys) { @MultiRun(async = true)
if (finest) logger.finest("(source:" + sourceName + ") insert " + clazz + " --> " + Arrays.toString(entitys)); public <T> void updateCache(Class<T> clazz, T... entitys) {
BlockingQueue<Map.Entry<Class, Object[]>> queue = this.insertQueues.get(sourceName); ((DataDefaultSource) source).updateCache(clazz, entitys);
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); //不是同一机房来的资源需要同步到其他同机房的节点上
} }
@Override @Override
public <T> void update(String sourceName, Class<T> clazz, T... values) { @MultiRun(async = true)
if (finest) logger.finest("(source:" + sourceName + ") update " + clazz + " --> " + Arrays.toString(values)); public <T> void deleteCache(Class<T> clazz, Serializable... ids) {
BlockingQueue<Map.Entry<Class, Object[]>> queue = this.updateQueues.get(sourceName); ((DataDefaultSource) source).deleteCache(clazz, ids);
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);
}
} }
@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); //不是同一机房来的资源需要同步到其他同机房的节点上
}
} }

View File

@@ -17,162 +17,102 @@ import java.util.logging.*;
import javax.annotation.Resource; import javax.annotation.Resource;
/** /**
* 暂时不实现
* *
* @author zhangjx * @author zhangjx
*/ */
@AutoLoad(false) @AutoLoad(false)
public class DataSQLListenerService implements DataSQLListener, Service { 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 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 final boolean finest = logger.isLoggable(Level.FINEST);
private String localNodeName = "";
private String localIDCName = "";
@Resource(name = "APP_HOME") @Resource(name = "APP_HOME")
private File home; private File home;
private File root; @Resource(name = "$")
private DataSource source;
@Resource(name = ".*") private final BlockingQueue<String> queue = new ArrayBlockingQueue<>(1024 * 1024);
HashMap<String, DataSource> sourcemaps;
private ConcurrentHashMap<String, BlockingQueue<String>> queues = new ConcurrentHashMap<>(); private PrintStream syncfile;
@Resource
private HashMap<String, DataSQLListenerService> nodemaps;
private final HashSet<String> allidcs = new HashSet<>();
private ConcurrentHashMap<String, PrintStream> syncfiles = new ConcurrentHashMap<>();
@Override @Override
public void init(AnyValue config) { public void init(AnyValue config) {
finest = logger.isLoggable(Level.FINEST); new Thread() {
//nodename的前两位字符表示机房ID {
if (localNodeName.length() > 2) localIDCName = getIDC(localNodeName); setName(DataSQLListener.class.getSimpleName() + "-Thread");
if (finest) logger.fine("LocalNodeName: " + localNodeName + ", " + localIDCName + " " + this.nodemaps); setDaemon(true);
if (this.nodemaps == null) return; }
this.nodemaps.forEach((x, y) -> allidcs.add(x.substring(0, 2)));
@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 @Override
public void destroy(AnyValue config) { public void destroy(AnyValue config) {
this.syncfiles.forEach((x, y) -> { if (syncfile != null) syncfile.close();
y.close();
});
} }
private void write(String node, String sourceName, String... sqls) { private void write(String... sqls) {
if (sourceName == null || sourceName.isEmpty()) sourceName = "<>";
String key = node + "-" + sourceName;
PrintStream channel = syncfiles.get(key);
try { try {
if (channel == null) { if (syncfile == null) {
if (this.root == null) { File root = new File(home, "dbsync");
this.root = new File(home, "dbsync"); root.mkdirs();
this.root.mkdirs(); syncfile = new PrintStream(new FileOutputStream(new File(root, "sql-" + name() + ".sql"), true), false, "UTF-8");
}
channel = new PrintStream(new FileOutputStream(new File(this.root, key + ".sql"), true), false, "UTF-8");
syncfiles.put(key, channel);
} }
for (String sql : sqls) { for (String sql : sqls) {
channel.print(sql + ";\r\n"); syncfile.print(sql + ";\r\n");
} }
channel.flush(); syncfile.flush();
} catch (Exception e) { } 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 @Override
public void insert(String sourceName, String... sqls) { public void insert(String... sqls) {
put(sourceName, sqls); put(sqls);
} }
@Override @Override
public void update(String sourceName, String... sqls) { public void update(String... sqls) {
put(sourceName, sqls); put(sqls);
} }
@Override @Override
public void delete(String sourceName, String... sqls) { public void delete(String... sqls) {
put(sourceName, sqls); put(sqls);
} }
private void put(final String sourceName, String... sqls) { private void put(String... sqls) {
String date = String.format(format, System.currentTimeMillis()); String date = String.format(format, System.currentTimeMillis());
BlockingQueue<String> queue = this.queues.get(sourceName); for (String sql : sqls) {
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) -> {
try { try {
String idc = getIDC(x); queue.put("/* " + date + " */ " + sql);
if (!idcs.contains(idc)) {
y.send(sourceName, sqls);
idcs.add(idc);
}
} catch (Exception e) { } 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) { @MultiRun
((DataDefaultSource) sourcemaps.get(sourceName)).execute(sqls); public void send(String... sqls) {
((DataDefaultSource) source).execute(sqls);
} }
} }

View 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; //分布式运行是否采用异步模式
}

View File

@@ -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 {
}

View File

@@ -8,15 +8,15 @@ package com.wentch.redkale.service;
import com.wentch.redkale.util.*; import com.wentch.redkale.util.*;
/** /**
* 所有Service的实现类不得声明为final 允许远程模式的public方法不能声明为final。 * 所有Service的实现类不得声明为final 允许远程模式的public方法和public String name()方法都不能声明为final。
* * <p>
* @Resource(name = ".*") * @Resource(name = ".*")
* private HashMap<String, XXXService> nodemap; * private HashMap<String, XXXService> nodemap;
* 被注入的多个XXXService实例 但不会包含自身的XXXService。 * 被注入的多个XXXService实例 但不会包含自身的XXXService。
* *
* @author zhangjx * @author zhangjx
*/ */
public interface Service { public interface Service extends Nameable {
/** /**
* 该方法必须是可以重复调用, 当reload时需要重复调用init方法 * 该方法必须是可以重复调用, 当reload时需要重复调用init方法
@@ -30,4 +30,13 @@ public interface Service {
default void destroy(AnyValue config) { default void destroy(AnyValue config) {
} }
/**
* Service的name 一个Service在同一进程内可以包含多个实例 使用name区分
* <p>
* @return
*/
default String name() {
return "";
}
} }

View File

@@ -8,265 +8,66 @@ package com.wentch.redkale.service;
import com.wentch.redkale.net.http.*; import com.wentch.redkale.net.http.*;
import com.wentch.redkale.util.*; import com.wentch.redkale.util.*;
import java.io.*; import java.io.*;
import java.net.*;
import java.util.*; import java.util.*;
import java.util.concurrent.*; import java.util.concurrent.*;
import java.util.logging.*;
import javax.annotation.*;
/** /**
* *
* @author zhangjx * @author zhangjx
*/ */
@AutoLoad(false) @AutoLoad(false)
public class WebSocketNodeService implements Service { public class WebSocketNodeService extends WebSocketNode implements Service {
public static final int RETCODE_ENGINE_NULL = 5001; @Override
public void init(AnyValue conf) {
public static final int RETCODE_NODESERVICE_NULL = 5002; super.init(conf);
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();
} }
public final void addWebSocketEngine(WebSocketEngine engine) { @Override
engines.put(engine.getEngineid(), engine); public void destroy(AnyValue conf) {
super.destroy(conf);
} }
@RemoteOn @Override
public Map<Serializable, Set<String>> queryNodes() { public int sendMessage(Serializable groupid, boolean recent, Serializable message, boolean last) {
Map<Serializable, Set<String>> rs = new HashMap<>(); final Set<String> engineids = localNodes.get(groupid);
this.nodemaps.forEach((x, y) -> { if (engineids == null || engineids.isEmpty()) return RETCODE_GROUP_EMPTY;
if (!rs.isEmpty()) return; for (String engineid : engineids) {
try { final WebSocketEngine engine = engines.get(engineid);
rs.putAll(y.queryNodes()); if (engine != null) { //在本地
} catch (Exception e) { final WebSocketGroup group = engine.getWebSocketGroup(groupid);
logger.log(Level.WARNING, this.getClass().getSimpleName() + " query error (" + x + ")", e); 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;
}
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);
} }
} group.send(recent, message, last);
}); } else { //对方连接在远程节点
return rs; return RETCODE_WSOFFLINE;
}
/**
* 消息接受者存在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));
} }
} }
return 0; 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);
}
} }

View File

@@ -13,9 +13,9 @@ import java.io.Serializable;
*/ */
public interface DataCacheListener { 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);
} }

View File

@@ -24,7 +24,7 @@ import javax.xml.stream.*;
* @author zhangjx * @author zhangjx
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public final class DataDefaultSource implements DataSource { public final class DataDefaultSource implements DataSource, Nameable {
public static final String DATASOURCE_CONFPATH = "DATASOURCE_CONFPATH"; public static final String DATASOURCE_CONFPATH = "DATASOURCE_CONFPATH";
@@ -50,6 +50,8 @@ public final class DataDefaultSource implements DataSource {
final URL conf; final URL conf;
final boolean cacheForbidden;
private final JDBCPoolSource readPool; private final JDBCPoolSource readPool;
private final JDBCPoolSource writePool; private final JDBCPoolSource writePool;
@@ -57,10 +59,10 @@ public final class DataDefaultSource implements DataSource {
@Resource(name = "property.datasource.nodeid") @Resource(name = "property.datasource.nodeid")
private int nodeid; private int nodeid;
@Resource @Resource(name = "$")
private DataSQLListener writeListener; private DataSQLListener writeListener;
@Resource @Resource(name = "$")
private DataCacheListener cacheListener; private DataCacheListener cacheListener;
private static class DataJDBCConnection extends DataConnection { private static class DataJDBCConnection extends DataConnection {
@@ -153,7 +155,7 @@ public final class DataDefaultSource implements DataSource {
this.conf = url; this.conf = url;
this.readPool = new JDBCPoolSource(this, "read", readprop); this.readPool = new JDBCPoolSource(this, "read", readprop);
this.writePool = new JDBCPoolSource(this, "write", writeprop); 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) { public DataDefaultSource(String unitName, Properties readprop, Properties writeprop) {
@@ -161,7 +163,7 @@ public final class DataDefaultSource implements DataSource {
this.conf = null; this.conf = null;
this.readPool = new JDBCPoolSource(this, "read", readprop); this.readPool = new JDBCPoolSource(this, "read", readprop);
this.writePool = new JDBCPoolSource(this, "write", writeprop); 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) { public static Map<String, DataDefaultSource> create(final InputStream in) {
@@ -257,6 +259,11 @@ public final class DataDefaultSource implements DataSource {
return (ConnectionPoolDataSource) pdsource; return (ConnectionPoolDataSource) pdsource;
} }
@Override
public final String name() {
return name;
}
@Override @Override
public DataConnection createReadConnection() { public DataConnection createReadConnection() {
return new DataJDBCConnection(createReadSQLConnection()); return new DataJDBCConnection(createReadSQLConnection());
@@ -320,11 +327,11 @@ public final class DataDefaultSource implements DataSource {
} }
private <T> EntityInfo<T> loadEntityInfo(Class<T> clazz) { 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) { 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 @Override
public <T> void refreshCache(Class<T> clazz) { 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(); EntityCache<T> cache = info.getCache();
if (cache == null) return; if (cache == null) return;
cache.fullLoad(queryList(clazz, (FilterNode) null)); cache.fullLoad(queryList(clazz, (FilterNode) null));
} }
//----------------------insert----------------------------- //----------------------insertCache-----------------------------
/** /**
* 新增对象, 必须是Entity对象 * 新增对象, 必须是Entity对象
* *
@@ -467,7 +474,7 @@ public final class DataDefaultSource implements DataSource {
} }
} }
prestmt.executeBatch(); prestmt.executeBatch();
if (writeListener != null) writeListener.insert(name, sqls); if (writeListener != null) writeListener.insert(sqls);
if (info.autoGenerated) { if (info.autoGenerated) {
ResultSet set = prestmt.getGeneratedKeys(); ResultSet set = prestmt.getGeneratedKeys();
int i = -1; int i = -1;
@@ -488,16 +495,16 @@ public final class DataDefaultSource implements DataSource {
for (final T value : values) { for (final T value : values) {
cache.insert(value); cache.insert(value);
} }
if (cacheListener != null) cacheListener.insert(name, info.getType(), values); if (cacheListener != null) cacheListener.insertCache(info.getType(), values);
} }
} catch (SQLException e) { } catch (SQLException e) {
throw new RuntimeException(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; 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(); final EntityCache<T> cache = info.getCache();
if (cache == null) return; if (cache == null) return;
for (T value : values) { for (T value : values) {
@@ -505,7 +512,7 @@ public final class DataDefaultSource implements DataSource {
} }
} }
//-------------------------delete-------------------------- //-------------------------deleteCache--------------------------
/** /**
* 删除对象, 必须是Entity对象 * 删除对象, 必须是Entity对象
* *
@@ -576,7 +583,7 @@ public final class DataDefaultSource implements DataSource {
final Statement stmt = conn.createStatement(); final Statement stmt = conn.createStatement();
stmt.execute(sql); stmt.execute(sql);
stmt.close(); stmt.close();
if (writeListener != null) writeListener.delete(name, sql); if (writeListener != null) writeListener.delete(sql);
} }
//------------------------------------ //------------------------------------
final EntityCache<T> cache = info.getCache(); final EntityCache<T> cache = info.getCache();
@@ -584,7 +591,7 @@ public final class DataDefaultSource implements DataSource {
final Attribute<T, Serializable> attr = info.getPrimary(); final Attribute<T, Serializable> attr = info.getPrimary();
final Serializable[] keys2 = keys; final Serializable[] keys2 = keys;
Serializable[] ids = cache.delete((T t) -> Arrays.binarySearch(keys2, attr.get(t)) >= 0); 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) { } catch (SQLException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
@@ -618,13 +625,13 @@ public final class DataDefaultSource implements DataSource {
final Statement stmt = conn.createStatement(); final Statement stmt = conn.createStatement();
stmt.execute(sql); stmt.execute(sql);
stmt.close(); stmt.close();
if (writeListener != null) writeListener.delete(name, sql); if (writeListener != null) writeListener.delete(sql);
} }
//------------------------------------ //------------------------------------
final EntityCache<T> cache = info.getCache(); final EntityCache<T> cache = info.getCache();
if (cache == null) return; if (cache == null) return;
Serializable[] ids = cache.delete(node.createFilterPredicate(info, null)); 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) { } catch (SQLException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
@@ -717,7 +724,7 @@ public final class DataDefaultSource implements DataSource {
} }
prestmt.executeBatch(); prestmt.executeBatch();
prestmt.close(); prestmt.close();
if (writeListener != null) writeListener.update(name, sqls); if (writeListener != null) writeListener.update(sqls);
} }
//--------------------------------------------------- //---------------------------------------------------
final EntityCache<T> cache = info.getCache(); final EntityCache<T> cache = info.getCache();
@@ -725,7 +732,7 @@ public final class DataDefaultSource implements DataSource {
for (final T value : values) { for (final T value : values) {
cache.update(value); cache.update(value);
} }
if (cacheListener != null) cacheListener.update(name, clazz, values); if (cacheListener != null) cacheListener.updateCache(clazz, values);
} catch (SQLException e) { } catch (SQLException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
@@ -769,13 +776,13 @@ public final class DataDefaultSource implements DataSource {
final Statement stmt = conn.createStatement(); final Statement stmt = conn.createStatement();
stmt.execute(sql); stmt.execute(sql);
stmt.close(); stmt.close();
if (writeListener != null) writeListener.update(name, sql); if (writeListener != null) writeListener.update(sql);
} }
//--------------------------------------------------- //---------------------------------------------------
final EntityCache<T> cache = info.getCache(); final EntityCache<T> cache = info.getCache();
if (cache == null) return; if (cache == null) return;
T rs = cache.update(id, (Attribute<T, Serializable>) info.getAttribute(column), value); 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) { } catch (SQLException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} finally { } finally {
@@ -822,14 +829,14 @@ public final class DataDefaultSource implements DataSource {
final Statement stmt = conn.createStatement(); final Statement stmt = conn.createStatement();
stmt.execute(sql); stmt.execute(sql);
stmt.close(); stmt.close();
if (writeListener != null) writeListener.update(name, sql); if (writeListener != null) writeListener.update(sql);
} }
//--------------------------------------------------- //---------------------------------------------------
final EntityCache<T> cache = info.getCache(); final EntityCache<T> cache = info.getCache();
if (cache == null) return; if (cache == null) return;
Attribute<T, Serializable> attr = info.getAttribute(column); Attribute<T, Serializable> attr = info.getAttribute(column);
T value = cache.updateColumnIncrement(id, attr, incvalue); 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) { } catch (SQLException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} finally { } finally {
@@ -888,13 +895,13 @@ public final class DataDefaultSource implements DataSource {
final Statement stmt = conn.createStatement(); final Statement stmt = conn.createStatement();
stmt.execute(sql); stmt.execute(sql);
stmt.close(); stmt.close();
if (writeListener != null) writeListener.update(name, sql); if (writeListener != null) writeListener.update(sql);
} }
//--------------------------------------------------- //---------------------------------------------------
final EntityCache<T> cache = info.getCache(); final EntityCache<T> cache = info.getCache();
if (cache == null) return; if (cache == null) return;
cache.update(value, attrs); cache.update(value, attrs);
if (cacheListener != null) cacheListener.update(name, clazz, value); if (cacheListener != null) cacheListener.updateCache(clazz, value);
} catch (SQLException e) { } catch (SQLException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }

View File

@@ -12,9 +12,9 @@ package com.wentch.redkale.source;
*/ */
public interface DataSQLListener { 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);
} }

View File

@@ -29,8 +29,6 @@ public final class EntityInfo<T> {
private static final Logger logger = Logger.getLogger(EntityInfo.class); private static final Logger logger = Logger.getLogger(EntityInfo.class);
static boolean cacheForbidden = false;
//Entity类的类名 //Entity类的类名
private final Class<T> type; private final Class<T> type;
@@ -86,14 +84,14 @@ public final class EntityInfo<T> {
final int allocationSize; 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) { Function<Class, List> fullloader) {
EntityInfo rs = entityInfos.get(clazz); EntityInfo rs = entityInfos.get(clazz);
if (rs != null) return rs; if (rs != null) return rs;
synchronized (entityInfos) { synchronized (entityInfos) {
rs = entityInfos.get(clazz); rs = entityInfos.get(clazz);
if (rs == null) { if (rs == null) {
rs = new EntityInfo(clazz, nodeid, fullloader); rs = new EntityInfo(clazz, nodeid, cacheForbidden, fullloader);
entityInfos.put(clazz, rs); entityInfos.put(clazz, rs);
AutoLoad auto = clazz.getAnnotation(AutoLoad.class); AutoLoad auto = clazz.getAnnotation(AutoLoad.class);
if (rs.cache != null && auto != null && auto.value() && fullloader != null) { 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.type = type;
//--------------------------------------------- //---------------------------------------------
this.nodeid = nodeid; this.nodeid = nodeid;

View File

@@ -26,21 +26,21 @@ final class FilterBeanNode extends FilterNode {
private static final ConcurrentHashMap<Class, FilterBeanNode> beanodes = new ConcurrentHashMap<>(); 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) { Function<Class, List> fullloader) {
FilterBeanNode rs = beanodes.get(clazz); FilterBeanNode rs = beanodes.get(clazz);
if (rs != null) return rs; if (rs != null) return rs;
synchronized (beanodes) { synchronized (beanodes) {
rs = beanodes.get(clazz); rs = beanodes.get(clazz);
if (rs == null) { if (rs == null) {
rs = createNode(clazz, nodeid, fullloader); rs = createNode(clazz, nodeid, cacheForbidden, fullloader);
beanodes.put(clazz, rs); beanodes.put(clazz, rs);
} }
return 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) { Function<Class, List> fullloader) {
Class cltmp = clazz; Class cltmp = clazz;
Set<String> fields = new HashSet<>(); Set<String> fields = new HashSet<>();
@@ -78,7 +78,7 @@ final class FilterBeanNode extends FilterNode {
joinTables.put(joinClass, String.valueOf((char) ('b' + joinTables.size()))); joinTables.put(joinClass, String.valueOf((char) ('b' + joinTables.size())));
} }
final String alias = joinTables.get(joinClass); 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()) { if (secinfo.getCache() == null || !secinfo.getCache().isFullLoaded()) {
joinallcached = false; joinallcached = false;
} }

View File

@@ -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);
}
}

View File

@@ -1,207 +1,207 @@
/* /*
* To change this template, choose Tools | Templates * To change this template, choose Tools | Templates
* and open the template in the editor. * and open the template in the editor.
*/ */
package com.wentch.redkale.util; package com.wentch.redkale.util;
import java.beans.ConstructorProperties; import java.beans.ConstructorProperties;
import java.lang.reflect.*; import java.lang.reflect.*;
import java.util.*; import java.util.*;
import jdk.internal.org.objectweb.asm.*; import jdk.internal.org.objectweb.asm.*;
import static jdk.internal.org.objectweb.asm.Opcodes.*; import static jdk.internal.org.objectweb.asm.Opcodes.*;
import jdk.internal.org.objectweb.asm.Type; import jdk.internal.org.objectweb.asm.Type;
/** /**
* 实现一个类的构造方法。 代替低效的反射实现方式。 * 实现一个类的构造方法。 代替低效的反射实现方式。
* *
* @author zhangjx * @author zhangjx
* @param <T> * @param <T>
*/ */
public interface Creator<T> { public interface Creator<T> {
// //
// static class PooledCreator<T> implements Creator<T> { // static class PooledCreator<T> implements Creator<T> {
// //
// private final T defValue; // private final T defValue;
// //
// private final Reproduce<T, T> reproduce; // private final Reproduce<T, T> reproduce;
// //
// private final ReferenceQueue<T> refQueue = new ReferenceQueue(); // private final ReferenceQueue<T> refQueue = new ReferenceQueue();
// //
// private final Queue<T> queue; // private final Queue<T> queue;
// //
// private final Creator<T> creator; // private final Creator<T> creator;
// //
// public PooledCreator(int max, Class<T> clazz, Creator<T> creator) { // public PooledCreator(int max, Class<T> clazz, Creator<T> creator) {
// this.creator = creator; // this.creator = creator;
// this.defValue = creator.create(); // this.defValue = creator.create();
// this.reproduce = Reproduce.create(clazz, clazz); // this.reproduce = Reproduce.create(clazz, clazz);
// this.queue = new ArrayBlockingQueue<>(Math.max(Runtime.getRuntime().availableProcessors() * 2, max)); // this.queue = new ArrayBlockingQueue<>(Math.max(Runtime.getRuntime().availableProcessors() * 2, max));
// new Thread() { // new Thread() {
// { // {
// setDaemon(true); // setDaemon(true);
// setName(PooledCreator.class.getSimpleName() + " " + clazz.getSimpleName() + " Reference Handler"); // setName(PooledCreator.class.getSimpleName() + " " + clazz.getSimpleName() + " Reference Handler");
// } // }
// //
// @Override // @Override
// public void run() { // public void run() {
// try { // try {
// for (;;) { // for (;;) {
// T r = refQueue.remove().get(); // T r = refQueue.remove().get();
// if (r == null) continue; // if (r == null) continue;
// reproduce.copy(r, defValue); // reproduce.copy(r, defValue);
// queue.offer(r); // queue.offer(r);
// } // }
// } catch (Exception e) { // } catch (Exception e) {
// //do nothind // //do nothind
// } // }
// } // }
// }.start(); // }.start();
// } // }
// //
// @Override // @Override
// public T create(Object... params) { // public T create(Object... params) {
// T rs = queue.poll(); // T rs = queue.poll();
// if (rs == null) { // if (rs == null) {
// rs = creator.create(params); // rs = creator.create(params);
// } // }
// return new WeakReference<>(rs, refQueue).get(); // return new WeakReference<>(rs, refQueue).get();
// } // }
// //
// } // }
// //
// @SuppressWarnings("unchecked") // @SuppressWarnings("unchecked")
// public static <T> Creator<T> create(int max, Class<T> clazz) { // public static <T> Creator<T> create(int max, Class<T> clazz) {
// return new PooledCreator<>(max, clazz, create(clazz)); // return new PooledCreator<>(max, clazz, create(clazz));
// } // }
// //
// @SuppressWarnings("unchecked") // @SuppressWarnings("unchecked")
// public static <T> Creator<T> create(int max, Class<T> clazz, Creator<T> creator) { // public static <T> Creator<T> create(int max, Class<T> clazz, Creator<T> creator) {
// return new PooledCreator<>(max, clazz, creator); // return new PooledCreator<>(max, clazz, creator);
// } // }
public T create(Object... params); public T create(Object... params);
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public static <T> Creator<T> create(Class<T> clazz) { public static <T> Creator<T> create(Class<T> clazz) {
if (clazz.isAssignableFrom(ArrayList.class)) { if (clazz.isAssignableFrom(ArrayList.class)) {
clazz = (Class<T>) ArrayList.class; clazz = (Class<T>) ArrayList.class;
} else if (clazz.isAssignableFrom(HashMap.class)) { } else if (clazz.isAssignableFrom(HashMap.class)) {
clazz = (Class<T>) HashMap.class; clazz = (Class<T>) HashMap.class;
} else if (clazz.isAssignableFrom(HashSet.class)) { } else if (clazz.isAssignableFrom(HashSet.class)) {
clazz = (Class<T>) HashSet.class; clazz = (Class<T>) HashSet.class;
} }
if (clazz.isInterface() || Modifier.isAbstract(clazz.getModifiers())) { if (clazz.isInterface() || Modifier.isAbstract(clazz.getModifiers())) {
throw new RuntimeException("[" + clazz + "] is a interface or abstract class, cannot create it's Creator."); throw new RuntimeException("[" + clazz + "] is a interface or abstract class, cannot create it's Creator.");
} }
final String supDynName = Creator.class.getName().replace('.', '/'); final String supDynName = Creator.class.getName().replace('.', '/');
final String interName = clazz.getName().replace('.', '/'); final String interName = clazz.getName().replace('.', '/');
final String interDesc = Type.getDescriptor(clazz); final String interDesc = Type.getDescriptor(clazz);
ClassLoader loader = Creator.class.getClassLoader(); ClassLoader loader = Creator.class.getClassLoader();
String newDynName = supDynName + "_" + clazz.getSimpleName() + "_" + (System.currentTimeMillis() % 10000); String newDynName = supDynName + "_" + clazz.getSimpleName() + "_" + (System.currentTimeMillis() % 10000);
if (String.class.getClassLoader() != clazz.getClassLoader()) { if (String.class.getClassLoader() != clazz.getClassLoader()) {
loader = clazz.getClassLoader(); loader = clazz.getClassLoader();
newDynName = interName + "_Dyn" + Creator.class.getSimpleName(); newDynName = interName + "_Dyn" + Creator.class.getSimpleName();
} }
try { try {
return (Creator) Class.forName(newDynName.replace('/', '.')).newInstance(); return (Creator) Class.forName(newDynName.replace('/', '.')).newInstance();
} catch (Exception ex) { } catch (Exception ex) {
} }
Constructor<T> constructor = null; Constructor<T> constructor = null;
for (Constructor c : clazz.getConstructors()) { for (Constructor c : clazz.getConstructors()) {
if (c.getParameterCount() == 0) { if (c.getParameterCount() == 0) {
constructor = c; constructor = c;
break; break;
} }
} }
if (constructor == null) { if (constructor == null) {
for (Constructor c : clazz.getConstructors()) { for (Constructor c : clazz.getConstructors()) {
if (c.getAnnotation(ConstructorProperties.class) != null) { if (c.getAnnotation(ConstructorProperties.class) != null) {
constructor = c; constructor = c;
break; break;
} }
} }
} }
if (constructor == null) throw new RuntimeException("[" + clazz + "] have no public or java.beans.ConstructorProperties-Annotation constructor."); if (constructor == null) throw new RuntimeException("[" + clazz + "] have no public or java.beans.ConstructorProperties-Annotation constructor.");
//------------------------------------------------------------- //-------------------------------------------------------------
ClassWriter cw = new ClassWriter(0); ClassWriter cw = new ClassWriter(0);
FieldVisitor fv; FieldVisitor fv;
MethodVisitor mv; MethodVisitor mv;
AnnotationVisitor av0; 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}); 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); mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
ConstructorProperties cps = constructor.getAnnotation(ConstructorProperties.class); ConstructorProperties cps = constructor.getAnnotation(ConstructorProperties.class);
if (cps != null) { if (cps != null) {
av0 = mv.visitAnnotation("Ljava/beans/ConstructorProperties;", true); av0 = mv.visitAnnotation("Ljava/beans/ConstructorProperties;", true);
AnnotationVisitor av1 = av0.visitArray("value"); AnnotationVisitor av1 = av0.visitArray("value");
for (String n : cps.value()) { for (String n : cps.value()) {
av1.visit(null, n); av1.visit(null, n);
} }
av1.visitEnd(); av1.visitEnd();
av0.visitEnd(); av0.visitEnd();
} }
mv.visitVarInsn(ALOAD, 0); mv.visitVarInsn(ALOAD, 0);
mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false); mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
mv.visitInsn(RETURN); mv.visitInsn(RETURN);
mv.visitMaxs(1, 1); mv.visitMaxs(1, 1);
mv.visitEnd(); mv.visitEnd();
} }
{//create 方法 {//create 方法
mv = cw.visitMethod(ACC_PUBLIC + ACC_VARARGS, "create", "([Ljava/lang/Object;)L" + interName + ";", null, null); mv = cw.visitMethod(ACC_PUBLIC + ACC_VARARGS, "create", "([Ljava/lang/Object;)L" + interName + ";", null, null);
mv.visitTypeInsn(NEW, interName); mv.visitTypeInsn(NEW, interName);
mv.visitInsn(DUP); mv.visitInsn(DUP);
//--------------------------------------- //---------------------------------------
{ {
Parameter[] params = constructor.getParameters(); Parameter[] params = constructor.getParameters();
final int[] iconsts = {ICONST_0, ICONST_1, ICONST_2, ICONST_3, ICONST_4, ICONST_5}; final int[] iconsts = {ICONST_0, ICONST_1, ICONST_2, ICONST_3, ICONST_4, ICONST_5};
for (int i = 0; i < params.length; i++) { for (int i = 0; i < params.length; i++) {
mv.visitVarInsn(ALOAD, 1); mv.visitVarInsn(ALOAD, 1);
if (i < 6) { if (i < 6) {
mv.visitInsn(iconsts[i]); mv.visitInsn(iconsts[i]);
} else { } else {
mv.visitIntInsn(BIPUSH, i); mv.visitIntInsn(BIPUSH, i);
} }
mv.visitInsn(AALOAD); mv.visitInsn(AALOAD);
Class ct = params[i].getType(); Class ct = params[i].getType();
mv.visitTypeInsn(CHECKCAST, Type.getInternalName(ct)); mv.visitTypeInsn(CHECKCAST, Type.getInternalName(ct));
if (ct.isPrimitive()) { if (ct.isPrimitive()) {
Class fct = Array.get(Array.newInstance(ct, 1), 0).getClass(); Class fct = Array.get(Array.newInstance(ct, 1), 0).getClass();
try { try {
Method pm = ct.getMethod(ct.getSimpleName() + "Value"); Method pm = ct.getMethod(ct.getSimpleName() + "Value");
mv.visitMethodInsn(INVOKEVIRTUAL, fct.getName().replace('.', '/'), pm.getName(), Type.getMethodDescriptor(pm), false); mv.visitMethodInsn(INVOKEVIRTUAL, fct.getName().replace('.', '/'), pm.getName(), Type.getMethodDescriptor(pm), false);
} catch (Exception ex) { } catch (Exception ex) {
throw new RuntimeException(ex); //不可能会发生 throw new RuntimeException(ex); //不可能会发生
} }
} }
} }
} }
//--------------------------------------- //---------------------------------------
mv.visitMethodInsn(INVOKESPECIAL, interName, "<init>", Type.getConstructorDescriptor(constructor), false); mv.visitMethodInsn(INVOKESPECIAL, interName, "<init>", Type.getConstructorDescriptor(constructor), false);
mv.visitInsn(ARETURN); mv.visitInsn(ARETURN);
mv.visitMaxs((constructor.getParameterCount() > 0 ? (constructor.getParameterCount() + 3) : 2), 2); mv.visitMaxs((constructor.getParameterCount() > 0 ? (constructor.getParameterCount() + 3) : 2), 2);
mv.visitEnd(); mv.visitEnd();
} }
{ //虚拟 create 方法 { //虚拟 create 方法
mv = cw.visitMethod(ACC_PUBLIC + ACC_BRIDGE + ACC_VARARGS + ACC_SYNTHETIC, "create", "([Ljava/lang/Object;)Ljava/lang/Object;", null, null); 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, 0);
mv.visitVarInsn(ALOAD, 1); mv.visitVarInsn(ALOAD, 1);
mv.visitMethodInsn(INVOKEVIRTUAL, newDynName, "create", "([Ljava/lang/Object;)" + interDesc, false); mv.visitMethodInsn(INVOKEVIRTUAL, newDynName, "create", "([Ljava/lang/Object;)" + interDesc, false);
mv.visitInsn(ARETURN); mv.visitInsn(ARETURN);
mv.visitMaxs(2, 2); mv.visitMaxs(2, 2);
mv.visitEnd(); mv.visitEnd();
} }
cw.visitEnd(); cw.visitEnd();
byte[] bytes = cw.toByteArray(); byte[] bytes = cw.toByteArray();
Class<?> creatorClazz = new ClassLoader(loader) { Class<?> creatorClazz = new ClassLoader(loader) {
public final Class<?> loadClass(String name, byte[] b) { public final Class<?> loadClass(String name, byte[] b) {
return defineClass(name, b, 0, b.length); return defineClass(name, b, 0, b.length);
} }
}.loadClass(newDynName.replace('/', '.'), bytes); }.loadClass(newDynName.replace('/', '.'), bytes);
try { try {
return (Creator) creatorClazz.newInstance(); return (Creator) creatorClazz.newInstance();
} catch (Exception ex) { } catch (Exception ex) {
throw new RuntimeException(ex); throw new RuntimeException(ex);
} }
} }
} }

View File

@@ -32,11 +32,6 @@ public final class DLong extends Number implements Comparable<DLong> {
return this.first == one && this.second == two; return this.first == one && this.second == two;
} }
@Override
public int hashCode() {
return intValue();
}
@Override @Override
public boolean equals(Object obj) { public boolean equals(Object obj) {
if (obj == null) return false; 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); 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 @Override
public String toString() { public String toString() {
return this.first + "_" + this.second; return this.first + "_" + this.second;

View File

@@ -5,6 +5,7 @@
*/ */
package com.wentch.redkale.util; package com.wentch.redkale.util;
import java.util.*;
import jdk.internal.org.objectweb.asm.*; import jdk.internal.org.objectweb.asm.*;
/** /**
@@ -21,6 +22,8 @@ public class DebugMethodVisitor {
debug = d; debug = d;
} }
private final Map<Label, Integer> labels = new LinkedHashMap<>();
private static final String[] opcodes = new String[200]; //0 -18 private static final String[] opcodes = new String[200]; //0 -18
static { static {
@@ -46,6 +49,12 @@ public class DebugMethodVisitor {
this.visitor = visitor; 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) { public void visitParameter(String name, int access) {
visitor.visitParameter(name, access); visitor.visitParameter(name, access);
if (debug) System.out.println("mv.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 + ");"); 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) { public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) {
visitor.visitMethodInsn(opcode, owner, name, desc, itf); visitor.visitMethodInsn(opcode, owner, name, desc, itf);
if (debug) System.out.println("mv.visitMethodInsn(" + opcodes[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 + ");"); 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) { public void visitLdcInsn(Object o) {
visitor.visitLdcInsn(o); visitor.visitLdcInsn(o);
if (debug) System.out.println("mv.visitLdcInsn(" + o + ");"); if (debug) System.out.println("mv.visitLdcInsn(" + o + ");");

View File

@@ -3,12 +3,13 @@
* To change this template file, choose Tools | Templates * To change this template file, choose Tools | Templates
* and open the template in the editor. * and open the template in the editor.
*/ */
package com.wentch.redkale.service; package com.wentch.redkale.util;
/** /**
* *
* @author zhangjx * @author zhangjx
*/ */
public interface MultiService extends Service { public interface Nameable {
String name();
} }

View File

@@ -5,13 +5,6 @@ import java.util.function.Predicate;
import static jdk.internal.org.objectweb.asm.Opcodes.*; import static jdk.internal.org.objectweb.asm.Opcodes.*;
import jdk.internal.org.objectweb.asm.*; import jdk.internal.org.objectweb.asm.*;
/**
* 该类提供对象拷贝, 两对象存在相同的getter、setter的字段值会被拷贝
*
* @author zhangjx
* @param <D>
* @param <S>
*/
public interface Reproduce<D, S> { public interface Reproduce<D, S> {
public D copy(D dest, S src); public D copy(D dest, S src);

View File

@@ -13,12 +13,15 @@ import java.util.regex.*;
import javax.annotation.*; import javax.annotation.*;
/** /**
* 如果Resource(name = "$") 表示资源name采用所属对象的name
* *
* @author zhangjx * @author zhangjx
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public final class ResourceFactory { public final class ResourceFactory {
public static final String RESOURCE_PARENT_NAME = "$";
private static final Logger logger = Logger.getLogger(ResourceFactory.class.getSimpleName()); private static final Logger logger = Logger.getLogger(ResourceFactory.class.getSimpleName());
private final ResourceFactory parent; private final ResourceFactory parent;
@@ -170,15 +173,16 @@ public final class ResourceFactory {
continue; continue;
} }
if (Modifier.isFinal(field.getModifiers())) 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 (rs == null) {
if (Map.class.isAssignableFrom(classtype)) { 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 { } else {
if (rc.name().startsWith("property.")) { if (rcname.startsWith("property.")) {
rs = find(rc.name(), String.class); rs = find(rcname, String.class);
} else { } else {
rs = find(rc.name(), classtype); rs = find(rcname, classtype);
} }
} }
} }

View File

@@ -324,6 +324,17 @@ public final class Utility {
return size; 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) { 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); return encodeUTF8(buffer, encodeUTF8Length(text, start, len), text, start, len);
} }