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"?>
<!--
${APP_HOME} 指当前程序的总目录APP_HOME
node: 进程节点的名称, 默认为空
required 被声明required的属性值不能为空
文件说明:
${APP_HOME} 指当前程序的总目录APP_HOME
required 被声明required的属性值不能为空
group
/ / \ \
/ / \ \
/ / \ \
node1 node2 node3 node4
/ \
/ \
/ \
/ \
serviceid1 serviceid2
/ \ / \
serviceid1_name1 serviceid1_name2 serviceid2_name1 serviceid2_name2
-->
<!--
address: 本地的IP地址 默认值为默认网卡的ip当不使用默认值需要指定值如127.0.0.1
port: required 程序的管理Server的端口用于关闭或者与监管系统进行数据交互
host: 程序的管理Server的地址; 默认为127.0.0.1。
lib: 加上额外的lib路径,多个路径用分号;隔开; 默认为空。 例如: ${APP_HOME}/lib/a.jar;${APP_HOME}/lib2/b.jar;
@@ -11,6 +27,28 @@
<!-- 所有服务所需的资源 -->
<resources>
<!-- 设置系统的 DataCacheListener 的Service实现值[none]表示不需要启动DataCacheListener同步 默认为系统自带实现类 -->
<datacachelistener service="none"/>
<!-- 设置系统的 WebSocketNode 的Service实现 值[none]表示不需要启动WebSocketNode同步默认为系统自带实现类 -->
<websocketnode service="xxxx"/>
<!--
一个组包含多个NODE 同一Service服务可以由多个进程提供这些进程称为一个GROUP且同一GROUP内的进程必须在同一机房或局域网内
name: 服务组ID长度不能超过11个字节. 默认为空字符串。
protocol值只能是UDP TCP 默认UDP
-->
<group name="" protocol="UDP">
<!--
需要将本地node的addr与port列在此处。
addr: required IP地址
port: required 端口
clients: 连接池数, 默认: CPU核数*4
buffers: ByteBuffer对象池的大小 默认: CPU核数*8
-->
<node addr="127.0.0.1" port="7070"/>
</group>
<!--
远程client地址组资源. 注意: remote的name值不能为LOCAL不区分大小写
protocol 值只能是UDP TCP 默认UDP
@@ -48,13 +86,14 @@
protocol: required server所启动的协议有HTTP、SNCP 目前只支持HTTP、SNCP。SNCP也分TCP、UDP实现默认使用UDP实现TCP实现则使用SNCP.TCP值;
host: 服务所占address 默认: 0.0.0.0
port: 服务所占端口 ,默认: 80
group: 所属组的节点,多个节点值用;隔开如果配置文件中存在多个SNCP协议的Server节点需要显式指定group属性
root: 如果是web类型服务则包含页面 默认:{APP_HOME}/root
lib: server额外的class目录 默认为空
charset: 文本编码, 默认: UTF-8
backlog: 默认10K
threads 线程总数, 默认: CPU核数*16
maxbody: request.body最大值 默认: 64K
capacity: ByteBuffer的初始化大小 默认: 8K
capacity: ByteBuffer的初始化大小 默认: 8K; 如果是HTTP协议则默认: 16K + 8B (HTTP 2.0)
bufferPoolSize ByteBuffer池的大小默认: CPU核数*512
responsePoolSize Response池的大小默认: CPU核数*256
readTimeoutSecond: 读操作超时秒数, 默认0 表示永久不超时
@@ -72,16 +111,13 @@
autoload="false" 需要显著的指定Service类
includes 当autoload="true" 拉取类名与includes中的正则表达式匹配的类, 多个正则表达式用分号;隔开
excludes 当autoload="true" 排除类名与includes中的正则表达式匹配的类, 多个正则表达式用分号;隔开
remote 远程地址组的名称, 默认为空, 即本地模式。
当<services>指定remote值为非空非LOCAL(即远程模式)时, 所有service的remote值均默认是<services>指定remote值。
当<services>指定remote值为空(即本地模式)时, 所有service的remote值均默认是LOCAL值。
-->
<services autoload="true" includes="" excludes="" remote="myremote">
<services autoload="true" includes="" excludes="">
<!--
大部分的情况下, 存在多个节点环境中很多service节点配置都一致为此提供group节点来方便配置。
remotenames: 远程模式Service的name名称集合 多个用分号;隔开, 名称必须是在resources节点中定义的remote节点。
以下group节点例子等价于:
<service value="com.xxx.XXX0Service" name="LC001" remote="LOCAL"/>
<service value="com.xxx.XXX0Service" name="" remote="LOCAL"/>
<service value="com.xxx.XXX0Service" name="RT001" remote="RT001"/>
<service value="com.xxx.XXX0Service" name="RT002" remote="RT002"/>
<service value="com.xxx.XXX0Service" name="RT003" remote="RT003"/>

View File

@@ -5,7 +5,6 @@
*/
package com.wentch.redkale.boot;
import static com.wentch.redkale.boot.NodeServer.LINE_SEPARATOR;
import com.wentch.redkale.convert.bson.*;
import com.wentch.redkale.convert.json.*;
import com.wentch.redkale.net.*;
@@ -17,23 +16,23 @@ import com.wentch.redkale.util.*;
import com.wentch.redkale.util.AnyValue.DefaultAnyValue;
import com.wentch.redkale.watch.*;
import java.io.*;
import java.lang.reflect.*;
import java.net.*;
import java.nio.*;
import java.nio.channels.*;
import java.nio.file.*;
import java.util.*;
import java.util.AbstractMap.SimpleEntry;
import java.util.concurrent.*;
import java.util.logging.*;
import javax.annotation.*;
import javax.xml.parsers.*;
import org.w3c.dom.*;
/**
* 编译时需要加入: -XDignore.symbol.file=true
* <p>
* 进程启动类程序启动后读取application.xml,进行classpath扫描动态加载Service与Servlet 再进行Service、Servlet与其他资源之间的依赖注入。
* 进程启动类程序启动后读取application.xml,进行classpath扫描动态加载Service与Servlet
* 优先加载所有SNCP协议的服务 再加载其他协议服务,
* 最后进行Service、Servlet与其他资源之间的依赖注入。
*
*
* @author zhangjx
*/
@@ -45,18 +44,6 @@ public final class Application {
//当前进程的根目录, 类型String
public static final String RESNAME_HOME = "APP_HOME";
//当前进程节点的名称, 类型String
public static final String RESNAME_NODE = "APP_NODE";
//当前进程节点的所属组, 类型String、Map<String, Set<String>>、Map<String, List<SimpleEntry<String, InetSocketAddress[]>>>
public static final String RESNAME_GROUP = "APP_GROUP";
//当前进程节点的所属组所有节点名, 类型Set<String> 、List<SimpleEntry<String, InetSocketAddress[]>>包含自身节点名
public static final String RESNAME_INGROUP = "APP_INGROUP";
//除当前进程节点的所属组外其他所有组的所有节点名, 类型Map<String, Set<String>>、Map<String, List<SimpleEntry<String, InetSocketAddress[]>>>
public static final String RESNAME_OUTGROUP = "APP_OUTGROUP";
//当前进程节点的IP地址 类型InetAddress、String
public static final String RESNAME_ADDR = "APP_ADDR";
@@ -67,17 +54,19 @@ public final class Application {
protected final WatchFactory watch = WatchFactory.root();
protected final HashMap<Class, ServiceEntry> localServices = new HashMap<>();
protected final Map<InetSocketAddress, String> addrGroups = new HashMap<>();
protected final ArrayList<ServiceEntry> remoteServices = new ArrayList<>();
protected final List<Transport> transports = new ArrayList<>();
protected boolean serviceInited = false;
protected final InetAddress localAddress;
protected final InetAddress localAddress = Utility.localInetAddress();
protected Class<? extends DataCacheListener> dataCacheListenerClass = DataCacheListenerService.class;
protected String nodeGroup = "";
protected Class<? extends WebSocketNode> webSocketNodeClass = WebSocketNodeService.class;
protected String nodeName = "";
protected final List<DataSource> sources = new CopyOnWriteArrayList<>();
protected final List<NodeServer> servers = new CopyOnWriteArrayList<>();
//--------------------------------------------------------------------------------------------
private File home;
@@ -86,10 +75,6 @@ public final class Application {
private final AnyValue config;
private final List<NodeServer> servers = new CopyOnWriteArrayList<>();
private final List<DataSource> sources = new CopyOnWriteArrayList<>();
private final long startTime = System.currentTimeMillis();
private CountDownLatch serverscdl;
@@ -106,16 +91,12 @@ public final class Application {
} catch (IOException e) {
throw new RuntimeException(e);
}
String localaddr = config.getValue("address", "").trim();
this.localAddress = localaddr.isEmpty() ? Utility.localInetAddress() : new InetSocketAddress(localaddr, 0).getAddress();
Application.this.factory.register(RESNAME_ADDR, Application.this.localAddress.getHostAddress());
Application.this.factory.register(RESNAME_ADDR, InetAddress.class, Application.this.localAddress);
//以下是初始化日志配置
final File logconf = new File(root, "conf/logging.properties");
this.nodeName = config.getValue("node", "");
this.nodeGroup = config.getValue("group", "");
this.factory.register(RESNAME_NODE, this.nodeName);
this.factory.register(RESNAME_GROUP, this.nodeGroup);
System.setProperty(RESNAME_NODE, this.nodeName);
System.setProperty(RESNAME_GROUP, this.nodeGroup);
this.factory.register(RESNAME_ADDR, this.localAddress.getHostAddress());
this.factory.register(RESNAME_ADDR, InetAddress.class, this.localAddress);
if (logconf.isFile() && logconf.canRead()) {
try {
final String rootpath = root.getCanonicalPath().replace('\\', '/');
@@ -178,17 +159,16 @@ public final class Application {
System.setProperty("convert.bson.writer.buffer.defsize", "4096");
System.setProperty("convert.json.writer.buffer.defsize", "4096");
final File root = new File(System.getProperty(RESNAME_HOME));
File persist = new File(root, "conf/persistence.xml");
File persist = new File(this.home, "conf/persistence.xml");
final String homepath = this.home.getCanonicalPath();
if (persist.isFile()) System.setProperty(DataDefaultSource.DATASOURCE_CONFPATH, persist.getCanonicalPath());
logger.log(Level.INFO, RESNAME_HOME + "=" + root.getCanonicalPath() + "\r\n" + RESNAME_ADDR + "=" + this.localAddress.getHostAddress());
String lib = config.getValue("lib", "").trim().replace("${APP_HOME}", root.getCanonicalPath());
lib = lib.isEmpty() ? (root.getCanonicalPath() + "/conf") : (lib + ";" + root.getCanonicalPath() + "/conf");
logger.log(Level.INFO, RESNAME_HOME + "=" + homepath + "\r\n" + RESNAME_ADDR + "=" + this.localAddress.getHostAddress());
String lib = config.getValue("lib", "").trim().replace("${APP_HOME}", homepath);
lib = lib.isEmpty() ? (homepath + "/conf") : (lib + ";" + homepath + "/conf");
Server.loadLib(logger, lib);
initLogging();
InetAddress addr = Utility.localInetAddress();
if (addr != null) {
byte[] bs = addr.getAddress();
if (this.localAddress != null) {
byte[] bs = this.localAddress.getAddress();
int v = (0xff & bs[bs.length - 2]) % 10 * 100 + (0xff & bs[bs.length - 1]);
this.factory.register("property.datasource.nodeid", "" + v);
}
@@ -232,14 +212,25 @@ public final class Application {
initResources();
}
@Deprecated
public static void singleton(Service service) throws Exception {
final Application application = Application.create();
application.init();
application.factory.register(service);
new NodeHttpServer(application, new CountDownLatch(1), null).prepare(application.config);
new NodeHttpServer(application, null, new CountDownLatch(1), null).prepare(application.config);
application.factory.inject(service);
}
public static <T extends Service> T singleton(Class<T> serviceClass) throws Exception {
final Application application = Application.create();
T service = Sncp.createLocalService("", serviceClass, null, null, null);
application.init();
application.factory.register(service);
new NodeSncpServer(application, null, new CountDownLatch(1), null).init(application.config);
application.factory.inject(service);
return service;
}
private static Application create() throws IOException {
final String home = new File(System.getProperty(RESNAME_HOME, "")).getCanonicalPath();
System.setProperty(RESNAME_HOME, home);
@@ -257,7 +248,12 @@ public final class Application {
}
application.init();
application.startSelfServer();
application.start();
try {
application.start();
} catch (Exception e) {
application.logger.log(Level.SEVERE, "Application start error", e);
System.exit(0);
}
System.exit(0);
}
@@ -336,20 +332,38 @@ public final class Application {
final AnyValue[] entrys = config.getAnyValues("server");
this.serverscdl = new CountDownLatch(entrys.length + 1);
CountDownLatch timecd = new CountDownLatch(entrys.length);
runServers(timecd, entrys);
final List<AnyValue> sncps = new ArrayList<>();
final List<AnyValue> others = new ArrayList<>();
for (final AnyValue entry : entrys) {
if (entry.getValue("protocol", "").toUpperCase().startsWith("SNCP")) {
sncps.add(entry);
} else {
others.add(entry);
}
}
for (AnyValue sncpconf : sncps) {
String host = sncpconf.getValue("host", "0.0.0.0").replace("0.0.0.0", "");
InetSocketAddress addr = new InetSocketAddress(host.isEmpty() ? this.localAddress.getHostAddress() : host, sncpconf.getIntValue("port", 80));
String oldgroup = addrGroups.get(addr);
if (oldgroup != null && !((sncpconf.getValue("group", "") + ";").contains(oldgroup + ";"))) throw new RuntimeException(addr + " has one more group " + (addrGroups.get(addr)));
if (oldgroup == null) addrGroups.put(addr, "");
}
runServers(timecd, sncps); //确保sncp都启动后再启动其他协议
runServers(timecd, others);
timecd.await();
logger.info(this.getClass().getSimpleName() + " started in " + (System.currentTimeMillis() - startTime) + " ms");
this.serverscdl.await();
}
@SuppressWarnings("unchecked")
private void runServers(CountDownLatch timecd, final AnyValue[] entrys) throws Exception {
CountDownLatch servicecdl = new CountDownLatch(entrys.length);
for (final AnyValue entry : entrys) {
new Thread() {
private void runServers(CountDownLatch timecd, final List<AnyValue> serconfs) throws Exception {
CountDownLatch servicecdl = new CountDownLatch(serconfs.size());
CountDownLatch sercdl = new CountDownLatch(serconfs.size());
for (final AnyValue serconf : serconfs) {
Thread thread = new Thread() {
{
String host = entry.getValue("host", "").replace("0.0.0.0", "");
setName(entry.getValue("protocol", "Server").toUpperCase() + "-" + host + ":" + entry.getIntValue("port", 80) + "-Thread");
String host = serconf.getValue("host", "").replace("0.0.0.0", "[0]");
setName(serconf.getValue("protocol", "Server").toUpperCase() + "-" + host + ":" + serconf.getIntValue("port", 80) + "-Thread");
this.setDaemon(true);
}
@@ -358,167 +372,96 @@ public final class Application {
try {
//Thread ctd = Thread.currentThread();
//ctd.setContextClassLoader(new URLClassLoader(new URL[0], ctd.getContextClassLoader()));
String protocol = entry.getValue("protocol", "");
String subprotocol = "UDP";
String protocol = serconf.getValue("protocol", "");
String subprotocol = Sncp.DEFAULT_PROTOCOL;
int pos = protocol.indexOf('.');
if (pos > 0) {
subprotocol = protocol.substring(pos + 1);
protocol = protocol.substring(0, pos);
}
NodeServer server = null;
if ("HTTP".equalsIgnoreCase(protocol)) {
server = new NodeHttpServer(Application.this, servicecdl, new HttpServer(startTime, watch));
} else if ("SNCP".equalsIgnoreCase(protocol)) {
server = new NodeSncpServer(Application.this, servicecdl, new SncpServer(startTime, subprotocol, watch));
String host = serconf.getValue("host", "0.0.0.0").replace("0.0.0.0", "");
InetSocketAddress addr = new InetSocketAddress(host.isEmpty() ? localAddress.getHostAddress() : host, serconf.getIntValue("port", 80));
if ("SNCP".equalsIgnoreCase(protocol)) {
server = new NodeSncpServer(Application.this, addr, servicecdl, new SncpServer(startTime, subprotocol, addr, watch));
} else if ("HTTP".equalsIgnoreCase(protocol)) {
server = new NodeHttpServer(Application.this, addr, servicecdl, new HttpServer(startTime, watch));
}
if (server == null) {
logger.log(Level.SEVERE, "Not found Server Class for protocol({0})", entry.getValue("protocol"));
logger.log(Level.SEVERE, "Not found Server Class for protocol({0})", serconf.getValue("protocol"));
System.exit(0);
}
servers.add(server);
server.prepare(entry); //必须在init之前
server.init(entry);
server.init(serconf);
server.start();
timecd.countDown();
sercdl.countDown();
} catch (Exception ex) {
logger.log(Level.WARNING, entry + " runServers error", ex);
logger.log(Level.WARNING, serconf + " runServers error", ex);
serverscdl.countDown();
}
}
}.start();
};
thread.start();
}
sercdl.await();
}
private void initResources() throws Exception {
this.factory.add(DataSource.class, (ResourceFactory rf, final Object src, Field field) -> {
try {
Resource rs = field.getAnnotation(Resource.class);
if (rs == null) return;
if (src.getClass().getAnnotation(RemoteOn.class) != null) return;
DataSource source = DataSourceFactory.create(rs.name());
sources.add(source);
rf.register(rs.name(), DataSource.class, source);
field.set(src, source);
rf.inject(source); // 给 "datasource.nodeid" 赋值
} catch (Exception e) {
logger.log(Level.SEVERE, "DataSource inject error", e);
}
});
this.factory.add(DataSQLListener.class, (ResourceFactory rf, Object src, Field field) -> {
try {
Resource rs = field.getAnnotation(Resource.class);
if (rs == null) return;
if (src.getClass().getAnnotation(RemoteOn.class) != null) return;
DataSQLListener service = rf.findChild("", DataSQLListener.class);
if (service != null) {
field.set(src, service);
rf.inject(service);
}
} catch (Exception e) {
logger.log(Level.SEVERE, DataSQLListener.class.getSimpleName() + " inject error", e);
}
}
);
this.factory.add(DataCacheListener.class, (ResourceFactory rf, Object src, Field field) -> {
try {
Resource rs = field.getAnnotation(Resource.class);
if (rs == null) return;
if (src.getClass().getAnnotation(RemoteOn.class) != null) return;
DataCacheListener service = rf.findChild("", DataCacheListener.class);
if (service != null) {
field.set(src, service);
rf.inject(service);
}
} catch (Exception e) {
logger.log(Level.SEVERE, DataCacheListener.class.getSimpleName() + " inject error", e);
}
}
);
//-------------------------------------------------------------------------
final AnyValue resources = config.getAnyValue("resources");
if (resources != null) {
//------------------------------------------------------------------------
final String host = this.localAddress.getHostAddress();
final Map<String, Set<String>> groups = new HashMap<>();
final Map<String, List<SimpleEntry<String, InetSocketAddress[]>>> groups2 = new HashMap<>();
for (AnyValue conf : resources.getAnyValues("remote")) {
final String name = conf.getValue("name");
final String group = conf.getValue("group", "");
if (name == null) throw new RuntimeException("remote name is null");
String protocol = conf.getValue("protocol", "UDP").toUpperCase();
if (!"TCP".equalsIgnoreCase(protocol) && !"UDP".equalsIgnoreCase(protocol)) {
throw new RuntimeException("Not supported Transport Protocol " + conf.getValue("protocol"));
}
{
Set<String> set = groups.get(group);
if (set == null) {
set = new HashSet<>();
groups.put(group, set);
AnyValue datacachelistenerConf = resources.getAnyValue("datacachelistener");
if (datacachelistenerConf != null) {
String val = datacachelistenerConf.getValue("service", "");
if (!val.isEmpty()) {
if ("none".equalsIgnoreCase(val)) {
this.dataCacheListenerClass = null;
} else {
Class clazz = Class.forName(val);
if (!DataCacheListener.class.isAssignableFrom(clazz) || !Service.class.isAssignableFrom(clazz)) {
throw new RuntimeException("datacachelistener service (" + val + ") is illegal");
}
this.dataCacheListenerClass = clazz;
}
set.add(name);
}
AnyValue[] addrs = conf.getAnyValues("address");
InetSocketAddress[] addresses = new InetSocketAddress[addrs.length];
int i = -1;
for (AnyValue addr : addrs) {
addresses[++i] = new InetSocketAddress(addr.getValue("addr"), addr.getIntValue("port"));
}
if (addresses.length < 1) throw new RuntimeException("Transport(" + name + ") have no address ");
{
List<SimpleEntry<String, InetSocketAddress[]>> list = groups2.get(group);
if (list == null) {
list = new ArrayList<>();
groups2.put(group, list);
}
list.add(new SimpleEntry<>(name, addresses));
}
Transport transport = new Transport(name, protocol, conf.getIntValue("clients", Runtime.getRuntime().availableProcessors() * 8),
conf.getIntValue("buffers:", Runtime.getRuntime().availableProcessors() * 16), watch, addresses);
factory.register(name, Transport.class, transport);
if (this.nodeName.isEmpty() && host.equals(addrs[0].getValue("addr"))) {
this.nodeName = name;
this.nodeGroup = group;
this.factory.register(RESNAME_NODE, this.nodeName);
this.factory.register(RESNAME_GROUP, this.nodeGroup);
System.setProperty(RESNAME_NODE, this.nodeName);
System.setProperty(RESNAME_GROUP, this.nodeGroup);
}
}
//------------------------------------------------------------------------
AnyValue websocketnodeConf = resources.getAnyValue("websocketnode");
if (websocketnodeConf != null) {
String val = websocketnodeConf.getValue("service", "");
if (!val.isEmpty()) {
if ("none".equalsIgnoreCase(val)) {
this.webSocketNodeClass = null;
} else {
Class clazz = Class.forName(val);
if (!WebSocketNode.class.isAssignableFrom(clazz) || !Service.class.isAssignableFrom(clazz)) {
throw new RuntimeException("websocketnode service (" + val + ") is illegal");
}
this.webSocketNodeClass = clazz;
}
}
}
//------------------------------------------------------------------------
final Map<String, Set<String>> groups = new HashMap<>();
this.factory.register(RESNAME_GROUP, new TypeToken<Map<String, Set<String>>>() {
}.getType(), groups);
this.factory.register(RESNAME_GROUP, new TypeToken<Map<String, List<SimpleEntry<String, InetSocketAddress[]>>>>() {
}.getType(), groups2);
final Map<String, List<SimpleEntry<String, InetSocketAddress[]>>> outgroups2 = new HashMap<>();
final Map<String, Set<String>> outgroups = new HashMap<>();
groups.entrySet().stream().filter(x -> !x.getKey().equals(nodeName)).forEach(x -> outgroups.put(x.getKey(), x.getValue()));
groups2.entrySet().stream().filter(x -> !x.getKey().equals(nodeName)).forEach(x -> outgroups2.put(x.getKey(), x.getValue()));
this.factory.register(RESNAME_OUTGROUP, new TypeToken<Map<String, Set<String>>>() {
}.getType(), outgroups);
this.factory.register(RESNAME_OUTGROUP, new TypeToken<Map<String, List<SimpleEntry<String, InetSocketAddress[]>>>>() {
}.getType(), outgroups2);
Set<String> ingroup = groups.get(this.nodeGroup);
if (ingroup != null) this.factory.register(RESNAME_INGROUP, new TypeToken<Set<String>>() {
}.getType(), ingroup);
List<SimpleEntry<String, InetSocketAddress[]>> inengroup = groups2.get(this.nodeGroup);
if (inengroup != null) this.factory.register(RESNAME_INGROUP, new TypeToken<List<SimpleEntry<String, InetSocketAddress[]>>>() {
}.getType(), inengroup);
for (AnyValue conf : resources.getAnyValues("group")) {
final String group = conf.getValue("name", "");
String protocol = conf.getValue("protocol", Sncp.DEFAULT_PROTOCOL).toUpperCase();
if (!"TCP".equalsIgnoreCase(protocol) && !Sncp.DEFAULT_PROTOCOL.equalsIgnoreCase(protocol)) {
throw new RuntimeException("Not supported Transport Protocol " + conf.getValue("protocol"));
}
List<InetSocketAddress> addrs = new ArrayList<>();
for (AnyValue node : conf.getAnyValues("node")) {
InetSocketAddress addr = new InetSocketAddress(node.getValue("addr"), node.getIntValue("port"));
if (addrGroups.containsKey(addr)) throw new RuntimeException(addr + " had one more group " + (addrGroups.get(addr)));
addrGroups.put(addr, group);
addrs.add(addr);
}
}
}
//------------------------------------------------------------------------
logger.info(RESNAME_NODE + "=" + this.nodeName + "; " + RESNAME_GROUP + "=" + this.nodeGroup);
logger.info("datasource.nodeid=" + this.factory.find("property.datasource.nodeid", String.class));
}
private void shutdown() throws Exception {
@@ -531,18 +474,6 @@ public final class Application {
serverscdl.countDown();
}
});
final StringBuilder sb = logger.isLoggable(Level.INFO) ? new StringBuilder() : null;
localServices.entrySet().stream().forEach(k -> {
Class x = k.getKey();
ServiceEntry y = k.getValue();
long s = System.currentTimeMillis();
y.getService().destroy(y.getServiceConf());
long e = System.currentTimeMillis() - s;
if (e > 2 && sb != null) {
sb.append("LocalService(").append(y.getNames()).append("|").append(y.getServiceClass()).append(") destroy ").append(e).append("ms").append(LINE_SEPARATOR);
}
});
if (sb != null && sb.length() > 0) logger.log(Level.INFO, sb.toString());
for (DataSource source : sources) {
try {
source.getClass().getMethod("close").invoke(source);

View File

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

View File

@@ -10,14 +10,21 @@ import com.wentch.redkale.net.http.HttpServer;
import com.wentch.redkale.net.http.HttpServlet;
import com.wentch.redkale.util.AnyValue;
import com.wentch.redkale.boot.ClassFilter.FilterEntry;
import com.wentch.redkale.service.Service;
import java.lang.reflect.Modifier;
import com.wentch.redkale.net.*;
import com.wentch.redkale.net.http.*;
import com.wentch.redkale.net.sncp.*;
import com.wentch.redkale.service.*;
import com.wentch.redkale.util.*;
import java.io.*;
import java.lang.reflect.*;
import java.net.InetSocketAddress;
import java.util.*;
import java.util.concurrent.CountDownLatch;
import java.util.logging.*;
import javax.annotation.*;
/**
* HTTP Server节点的配置Server
*
* @author zhangjx
*/
@@ -25,15 +32,12 @@ public final class NodeHttpServer extends NodeServer {
private final HttpServer server;
public NodeHttpServer(Application application, CountDownLatch servicecdl, HttpServer server) {
super(application, servicecdl, server);
this.server = server;
}
private final File home;
@Override
public void init(AnyValue config) throws Exception {
server.init(config);
super.init(config);
public NodeHttpServer(Application application, InetSocketAddress addr, CountDownLatch servicecdl, HttpServer server) {
super(application, application.factory.createChild(), servicecdl, server);
this.server = server;
this.home = application.getHome();
}
@Override
@@ -43,22 +47,81 @@ public final class NodeHttpServer extends NodeServer {
@Override
public void prepare(AnyValue config) throws Exception {
super.prepare(config);
ClassFilter<HttpServlet> httpFilter = createHttpServletClassFilter(application.nodeName, config);
ClassFilter<Service> serviceFilter = createServiceClassFilter(application.nodeName, config);
ClassFilter<HttpServlet> httpFilter = createClassFilter(null, config, WebServlet.class, HttpServlet.class, null, "servlets", "servlet");
ClassFilter<Service> serviceFilter = createServiceClassFilter(config);
long s = System.currentTimeMillis();
ClassFilter.Loader.load(application.getHome(), serviceFilter, httpFilter);
ClassFilter.Loader.load(home, serviceFilter, httpFilter);
long e = System.currentTimeMillis() - s;
logger.info(this.getClass().getSimpleName() + " load filter class in " + e + " ms");
loadService(config.getAnyValue("services"), serviceFilter); //必须在servlet之前
if (server != null) initHttpServlet(config.getAnyValue("servlets"), httpFilter);
initWebSocketService();
if (server != null) loadHttpServlet(config.getAnyValue("servlets"), httpFilter);
}
protected static ClassFilter<HttpServlet> createHttpServletClassFilter(final String node, final AnyValue config) {
return createClassFilter(node, config, WebServlet.class, HttpServlet.class, null, "servlets", "servlet");
private void initWebSocketService() {
String defgroup = servconf.getValue("group", ""); //Server节点获取group信息
NodeSncpServer firstnss = null;
NodeSncpServer sncpServer = null;
for (NodeServer ns : application.servers) {
if (!(ns instanceof NodeSncpServer)) continue;
final NodeSncpServer nss = (NodeSncpServer) ns;
if (firstnss == null) firstnss = nss;
if (defgroup.equals(nss.nodeGroup)) {
sncpServer = nss;
break;
}
}
if (sncpServer == null) sncpServer = firstnss;
if (defgroup.isEmpty() && sncpServer != null) {
defgroup = sncpServer.servconf.getValue("group", "");
}
final String localGroup = sncpServer == null ? this.nodeGroup : sncpServer.nodeGroup;
final InetSocketAddress localAddr = sncpServer == null ? this.servaddr : sncpServer.servaddr;
final List<Transport>[] transportses = parseTransport(defgroup, localGroup, localAddr);
final List<Transport> sameGroupTransports = transportses[0];
final List<Transport> diffGroupTransports = transportses[1];
//---------------------------------------------------------------------------------------------
final NodeSncpServer onesncpServer = sncpServer;
final ResourceFactory regFactory = application.factory;
final String subprotocol = sncpServer == null ? Sncp.DEFAULT_PROTOCOL : sncpServer.getSncpServer().getProtocol();
factory.add(WebSocketNode.class, (ResourceFactory rf, final Object src, Field field) -> {
try {
Resource rs = field.getAnnotation(Resource.class);
if (rs == null) return;
if (!(src instanceof WebSocketServlet)) return;
String rcname = rs.name();
if (rcname.equals(ResourceFactory.RESOURCE_PARENT_NAME)) rcname = ((WebSocketServlet) src).name();
synchronized (regFactory) {
Service nodeService = (Service) rf.find(rcname, WebSocketNode.class);
if (nodeService == null) {
Class<? extends Service> sc = (Class<? extends Service>) application.webSocketNodeClass;
nodeService = Sncp.createLocalService(rcname, (Class<? extends Service>) (sc == null ? WebSocketNodeService.class : sc),
localAddr, (sc == null ? null : sameGroupTransports), (sc == null ? null : diffGroupTransports));
regFactory.register(rcname, WebSocketNode.class, nodeService);
WebSocketNode wsn = (WebSocketNode) nodeService;
wsn.setLocalSncpAddress(localAddr);
final Set<InetSocketAddress> alladdrs = new HashSet<>();
application.addrGroups.forEach((k, v) -> alladdrs.add(k));
alladdrs.remove(localAddr);
WebSocketNode remoteNode = (WebSocketNode) Sncp.createRemoteService(rcname, (Class<? extends Service>) (sc == null ? WebSocketNodeService.class : sc),
localAddr, (sc == null ? null : loadTransport(localGroup, subprotocol, alladdrs)));
wsn.setRemoteWebSocketNode(remoteNode);
factory.inject(nodeService);
factory.inject(remoteNode);
if (onesncpServer != null) {
ServiceWrapper wrapper = new ServiceWrapper((Class<? extends Service>) (sc == null ? WebSocketNodeService.class : sc), nodeService, localGroup, rcname, null);
onesncpServer.getSncpServer().addService(wrapper);
}
}
field.set(src, nodeService);
}
} catch (Exception e) {
logger.log(Level.SEVERE, "WebSocketNode inject error", e);
}
});
}
protected void initHttpServlet(final AnyValue conf, final ClassFilter<HttpServlet> filter) throws Exception {
protected void loadHttpServlet(final AnyValue conf, final ClassFilter<HttpServlet> filter) throws Exception {
final StringBuilder sb = logger.isLoggable(Level.FINE) ? new StringBuilder() : null;
final String prefix = conf == null ? "" : conf.getValue("prefix", "");
final String threadName = "[" + Thread.currentThread().getName() + "] ";
@@ -68,7 +131,7 @@ public final class NodeHttpServer extends NodeServer {
WebServlet ws = clazz.getAnnotation(WebServlet.class);
if (ws == null || ws.value().length == 0) continue;
final HttpServlet servlet = clazz.newInstance();
application.factory.inject(servlet);
factory.inject(servlet);
String[] mappings = ws.value();
if (ws.fillurl() && !prefix.isEmpty()) {
for (int i = 0; i < mappings.length; i++) {
@@ -80,4 +143,9 @@ public final class NodeHttpServer extends NodeServer {
}
if (sb != null && sb.length() > 0) logger.log(Level.FINE, sb.toString());
}
@Override
public boolean isSNCP() {
return false;
}
}

View File

@@ -5,15 +5,16 @@
*/
package com.wentch.redkale.boot;
import com.wentch.redkale.net.sncp.ServiceEntry;
import com.wentch.redkale.net.sncp.ServiceWrapper;
import com.wentch.redkale.net.Server;
import com.wentch.redkale.net.sncp.Sncp;
import com.wentch.redkale.service.Service;
import com.wentch.redkale.service.MultiService;
import com.wentch.redkale.util.AnyValue;
import com.wentch.redkale.util.Ignore;
import com.wentch.redkale.boot.ClassFilter.FilterEntry;
import com.wentch.redkale.util.AnyValue.DefaultAnyValue;
import com.wentch.redkale.net.*;
import com.wentch.redkale.source.*;
import com.wentch.redkale.util.*;
import java.io.*;
import java.lang.annotation.Annotation;
import java.lang.reflect.*;
@@ -22,6 +23,7 @@ import java.util.*;
import java.util.concurrent.*;
import java.util.function.Consumer;
import java.util.logging.*;
import javax.annotation.*;
/**
*
@@ -35,23 +37,36 @@ public abstract class NodeServer {
protected final Application application;
protected final ResourceFactory factory;
private final CountDownLatch servicecdl;
private final Server server;
protected Consumer<ServiceEntry> consumer;
protected AnyValue servconf;
public NodeServer(Application application, CountDownLatch servicecdl, Server server) {
protected InetSocketAddress servaddr;
protected String nodeGroup = "";
protected Consumer<ServiceWrapper> consumer;
protected final Set<ServiceWrapper> localServices = new LinkedHashSet<>();
protected final Set<ServiceWrapper> remoteServices = new LinkedHashSet<>();
public NodeServer(Application application, ResourceFactory factory, CountDownLatch servicecdl, Server server) {
this.application = application;
this.factory = factory;
this.servicecdl = servicecdl;
this.server = server;
this.logger = Logger.getLogger(this.getClass().getSimpleName());
}
public void prepare(final AnyValue config) throws Exception {
}
protected abstract void prepare(final AnyValue config) throws Exception;
public void init(AnyValue config) throws Exception {
this.servconf = config == null ? new AnyValue.DefaultAnyValue() : config;
//设置root文件夹
String webroot = config.getValue("root", "root");
File myroot = new File(webroot);
@@ -60,64 +75,114 @@ public abstract class NodeServer {
}
final String homepath = myroot.getCanonicalPath();
Server.loadLib(logger, config.getValue("lib", "") + ";" + homepath + "/lib/*;" + homepath + "/classes");
if (server != null) server.init(config);
initResource();
prepare(config);
}
private void initResource() {
if (!isSNCP()) return;
final String defgroup = servconf.getValue("group", ""); //Server节点获取group信息
final List<Transport>[] transportses = parseTransport(defgroup, this.nodeGroup, this.servaddr);
final List<Transport> sameGroupTransports = transportses[0];
final List<Transport> diffGroupTransports = transportses[1];
//---------------------------------------------------------------------------------------------
final ResourceFactory regFactory = application.factory;
regFactory.add(DataSource.class, (ResourceFactory rf, final Object src, Field field) -> {
try {
Resource rs = field.getAnnotation(Resource.class);
if (rs == null) return;
if ((src instanceof Service) && Sncp.isRemote((Service) src)) return;
DataSource source = DataSourceFactory.create(rs.name());
application.sources.add(source);
regFactory.register(rs.name(), DataSource.class, source);
Class<? extends Service> sc = (Class<? extends Service>) application.dataCacheListenerClass;
if (sc != null) {
Service cacheListenerService = Sncp.createLocalService(rs.name(), sc, this.servaddr, sameGroupTransports, diffGroupTransports);
regFactory.register(rs.name(), DataCacheListener.class, cacheListenerService);
ServiceWrapper wrapper = new ServiceWrapper(sc, cacheListenerService, nodeGroup, rs.name(), null);
localServices.add(wrapper);
if (consumer != null) consumer.accept(wrapper);
rf.inject(cacheListenerService);
}
field.set(src, source);
rf.inject(source); // 给 "datasource.nodeid" 赋值
} catch (Exception e) {
logger.log(Level.SEVERE, "DataSource inject error", e);
}
});
}
protected List<Transport>[] parseTransport(final String group, final String localGroup, final InetSocketAddress addr) {
final Set<InetSocketAddress> sameGroupAddrs = new LinkedHashSet<>();
final Map<String, Set<InetSocketAddress>> diffGroupAddrs = new HashMap<>();
for (String str : group.split(";")) {
application.addrGroups.forEach((k, v) -> {
if (v.equals(str)) {
if (v.equals(localGroup)) {
sameGroupAddrs.add(k);
} else {
Set<InetSocketAddress> set = diffGroupAddrs.get(v);
if (set == null) {
set = new LinkedHashSet<>();
diffGroupAddrs.put(v, set);
}
set.add(k);
}
}
});
}
sameGroupAddrs.remove(addr);
final List<Transport> sameGroupTransports = new ArrayList<>();
for (InetSocketAddress iaddr : sameGroupAddrs) {
Set<InetSocketAddress> tset = new HashSet<>();
tset.add(iaddr);
sameGroupTransports.add(loadTransport(localGroup, server.getProtocol(), tset));
}
final List<Transport> diffGroupTransports = new ArrayList<>();
diffGroupAddrs.forEach((k, v) -> diffGroupTransports.add(loadTransport(k, server.getProtocol(), v)));
return new List[]{sameGroupTransports, diffGroupTransports};
}
public abstract InetSocketAddress getSocketAddress();
public abstract boolean isSNCP();
public void start() throws IOException {
server.start();
}
public void shutdown() throws IOException {
final StringBuilder sb = logger.isLoggable(Level.INFO) ? new StringBuilder() : null;
localServices.forEach(y -> {
long s = System.currentTimeMillis();
y.getService().destroy(y.getConf());
long e = System.currentTimeMillis() - s;
if (e > 2 && sb != null) {
sb.append("LocalServices(").append(y.getType()).append(':').append(y.getName()).append(") destroy ").append(e).append("ms").append(LINE_SEPARATOR);
}
});
if (sb != null && sb.length() > 0) logger.log(Level.INFO, sb.toString());
server.shutdown();
}
protected void loadLocalService(final Set<FilterEntry<Service>> entrys) throws Exception {
final HashMap<Class, ServiceEntry> localServices = application.localServices;
for (final FilterEntry<Service> entry : entrys) {
Class<? extends Service> serviceClass = entry.getType();
if (serviceClass.getAnnotation(Ignore.class) != null) continue;
final boolean multi = MultiService.class.isAssignableFrom(serviceClass);
if (!multi) { //单例模式
synchronized (application.localServices) {
ServiceEntry old = localServices.get(serviceClass);
if (old == null) {
old = new ServiceEntry(serviceClass, (Service) serviceClass.newInstance(), entry.getProperty(), "");
localServices.put(serviceClass, old);
protected Transport loadTransport(String group, String protocol, Set<InetSocketAddress> addrs) {
Transport transport = null;
if (!addrs.isEmpty()) {
synchronized (application.transports) {
for (Transport tran : application.transports) {
if (tran.match(addrs)) {
transport = tran;
break;
}
if (consumer != null) consumer.accept(old);
continue;
}
}
String name = multi ? entry.getName() : "";
synchronized (application.localServices) {
ServiceEntry old = localServices.get(serviceClass);
if (old != null && old.containsName(name)) {
if (consumer != null) consumer.accept(old);
continue;
if (transport == null) {
transport = new Transport(group + "_" + application.transports.size(), protocol, application.watch, 32, addrs);
application.transports.add(transport);
}
final Service service = (Service) serviceClass.newInstance();
if (old == null) {
old = new ServiceEntry(serviceClass, service, entry.getProperty(), name);
localServices.put(serviceClass, old);
} else {
old.addName(name);
}
if (consumer != null) consumer.accept(old);
}
}
}
protected void loadRemoteService(final Set<FilterEntry<Service>> entrys) throws Exception {
for (FilterEntry<Service> entry : entrys) {
Class<Service> serviceClass = entry.getType();
if (serviceClass.getAnnotation(Ignore.class) != null) continue;
String remote = entry.getAttachment().toString();
Service service = Sncp.createRemoteService(entry.getName(), serviceClass, remote);
synchronized (application.remoteServices) {
application.remoteServices.add(new ServiceEntry(serviceClass, service, entry.getProperty(), entry.getName()));
}
}
return transport;
}
@SuppressWarnings("unchecked")
@@ -125,126 +190,129 @@ public abstract class NodeServer {
if (serviceFilter == null) return;
final String threadName = "[" + Thread.currentThread().getName() + "] ";
final Set<FilterEntry<Service>> entrys = serviceFilter.getFilterEntrys();
final Set<FilterEntry<Service>> localentrys = new HashSet<>(entrys.size());
final Set<FilterEntry<Service>> remotentrys = new HashSet<>(entrys.size());
final String defremote = getRemoteName(servicesConf, "LOCAL");
final String defgroup = servconf == null ? "" : servconf.getValue("group", ""); //Server节点获取group信息
ResourceFactory regFactory = isSNCP() ? application.factory : factory;
for (FilterEntry<Service> entry : entrys) { //service实现类
final Class type = entry.getType();
final Class<? extends Service> type = entry.getType();
if (type.isInterface()) continue;
if (Modifier.isFinal(type.getModifiers())) continue;
if (!Modifier.isPublic(type.getModifiers())) continue;
if (Modifier.isAbstract(type.getModifiers())) continue;
if (type.getAnnotation(Ignore.class) != null) continue;
final String remote = getRemoteName(entry.getProperty(), defremote);
if ("LOCAL".equals(remote)) { //本地模式
localentrys.add(entry);
} else { //远程模式
entry.setAttachment(remote);
remotentrys.add(entry);
if (!isSNCP() && factory.find(entry.getName(), type) != null) continue;
String group = entry.getGroup();
if (group == null || group.isEmpty()) group = defgroup;
final Set<InetSocketAddress> sameGroupAddrs = new LinkedHashSet<>();
final Map<String, Set<InetSocketAddress>> diffGroupAddrs = new HashMap<>();
for (String str : group.split(";")) {
application.addrGroups.forEach((k, v) -> {
if (v.equals(str)) {
if (v.equals(this.nodeGroup)) {
sameGroupAddrs.add(k);
} else {
Set<InetSocketAddress> set = diffGroupAddrs.get(v);
if (set == null) {
set = new LinkedHashSet<>();
diffGroupAddrs.put(v, set);
}
set.add(k);
}
}
});
}
final boolean localable = sameGroupAddrs.contains(this.servaddr);
Service service;
List<Transport> diffGroupTransports = new ArrayList<>();
diffGroupAddrs.forEach((k, v) -> diffGroupTransports.add(loadTransport(k, server.getProtocol(), v)));
if (localable || (sameGroupAddrs.isEmpty() && diffGroupTransports.isEmpty())) {
sameGroupAddrs.remove(this.servaddr);
List<Transport> sameGroupTransports = new ArrayList<>();
for (InetSocketAddress iaddr : sameGroupAddrs) {
Set<InetSocketAddress> tset = new HashSet<>();
tset.add(iaddr);
sameGroupTransports.add(loadTransport(this.nodeGroup, server.getProtocol(), tset));
}
service = Sncp.createLocalService(entry.getName(), type, this.servaddr, sameGroupTransports, diffGroupTransports);
} else {
StringBuilder g = new StringBuilder(this.nodeGroup);
diffGroupAddrs.forEach((k, v) -> {
if (g.length() > 0) g.append(';');
g.append(k);
sameGroupAddrs.addAll(v);
});
if (sameGroupAddrs.isEmpty()) throw new RuntimeException(type + ":" + group);
service = Sncp.createRemoteService(entry.getName(), type, this.servaddr, loadTransport(g.toString(), server.getProtocol(), sameGroupAddrs));
}
ServiceWrapper wrapper = new ServiceWrapper(type, service, entry);
if (factory.find(wrapper.getName(), wrapper.getType()) == null) {
regFactory.register(wrapper.getName(), wrapper.getType(), wrapper.getService());
if (wrapper.isRemote()) {
remoteServices.add(wrapper);
} else {
localServices.add(wrapper);
if (consumer != null) consumer.accept(wrapper);
}
} else if (isSNCP()) {
throw new RuntimeException(ServiceWrapper.class.getSimpleName() + "(class:" + type.getName() + ", name:" + entry.getName() + ", group:" + group + ") is repeat.");
}
}
loadLocalService(localentrys);
loadRemoteService(remotentrys);
servicecdl.countDown();
servicecdl.await();
final StringBuilder sb = logger.isLoggable(Level.INFO) ? new StringBuilder() : null;
synchronized (application) {
if (!application.serviceInited) {
application.serviceInited = true;
//--------------- register ---------------
application.localServices.forEach((x, y) -> {
y.getNames().forEach(n -> application.factory.register(n, y.getServiceClass(), y.getService()));
});
application.remoteServices.forEach(y -> {
y.getNames().forEach(n -> application.factory.register(n, y.getServiceClass(), y.getService()));
});
//---------------- inject ----------------
application.localServices.forEach((x, y) -> {
application.factory.inject(y.getService());
});
application.remoteServices.forEach(y -> {
application.factory.inject(y.getService());
});
//----------------- init -----------------
application.localServices.entrySet().parallelStream().forEach(k -> {
Class x = k.getKey();
ServiceEntry y = k.getValue();
long s = System.currentTimeMillis();
y.getService().init(y.getServiceConf());
long e = System.currentTimeMillis() - s;
if (e > 2 && sb != null) {
sb.append(threadName).append("LocalService(").append(y.getNames()).append("|").append(y.getServiceClass()).append(") init ").append(e).append("ms").append(LINE_SEPARATOR);
}
});
if (sb != null && sb.length() > 0) logger.log(Level.INFO, sb.toString());
//---------------- inject ----------------
new HashSet<>(localServices).forEach(y -> {
factory.inject(y.getService());
});
remoteServices.forEach(y -> {
factory.inject(y.getService());
});
//----------------- init -----------------
localServices.parallelStream().forEach(y -> {
long s = System.currentTimeMillis();
y.getService().init(y.getConf());
long e = System.currentTimeMillis() - s;
if (e > 2 && sb != null) {
sb.append(threadName).append("LocalService(").append(y.getType()).append(':').append(y.getName()).append(") init ").append(e).append("ms").append(LINE_SEPARATOR);
}
}
});
if (sb != null && sb.length() > 0) logger.log(Level.INFO, sb.toString());
}
private String getRemoteName(AnyValue av, String remote) {
remote = (remote == null || remote.trim().isEmpty()) ? "LOCAL" : remote.trim();
if (av == null) return remote;
String r = av.getValue("remote");
if ("LOCAL".equalsIgnoreCase(r)) return "LOCAL";
if (r != null && !r.trim().isEmpty()) return r.trim();
return remote;
protected ClassFilter<Service> createServiceClassFilter(final AnyValue config) {
return createClassFilter(this.nodeGroup, config, null, Service.class, Annotation.class, "services", "service");
}
protected static ClassFilter<Service> createServiceClassFilter(final String localNode, final AnyValue config) {
return createClassFilter(localNode, config, null, Service.class, Annotation.class, "services", "service");
}
protected static ClassFilter createClassFilter(final String localNode, final AnyValue config, Class<? extends Annotation> ref,
protected static ClassFilter createClassFilter(final String localGroup, final AnyValue config, Class<? extends Annotation> ref,
Class inter, Class<? extends Annotation> ref2, String properties, String property) {
ClassFilter filter = new ClassFilter(ref, inter);
if (properties == null && properties == null) return filter;
AnyValue list = config == null ? null : config.getAnyValue(properties);
if (list == null) return filter;
if ("services".equals(properties)) {
for (AnyValue group : list.getAnyValues("group")) {
String remotenames = group.getValue("remotenames");
for (AnyValue propnode : group.getAnyValues(property)) {
boolean hasremote = false;
if (remotenames != null) {
for (String rn : remotenames.split(";")) {
rn = rn.trim();
if (rn.isEmpty() || localNode.equals(rn)) continue;
DefaultAnyValue s = new DefaultAnyValue();
s.addValue("value", propnode.getValue("value"));
s.addValue("name", rn);
s.addValue("remote", rn);
((DefaultAnyValue) list).addValue(property, s);
hasremote = true;
}
}
if (hasremote) {
((DefaultAnyValue) propnode).setValue("name", localNode);
((DefaultAnyValue) propnode).setValue("remote", "");
((DefaultAnyValue) list).addValue(property, propnode);
}
ClassFilter cf = new ClassFilter(ref, inter);
if (properties == null && properties == null) return cf;
if (config == null) return cf;
AnyValue[] proplist = config.getAnyValues(properties);
if (proplist == null || proplist.length < 1) return cf;
cf = null;
for (AnyValue list : proplist) {
ClassFilter filter = new ClassFilter(ref, inter);
for (AnyValue av : list.getAnyValues(property)) {
filter.filter(av, av.getValue("value"), false);
}
if (list.getBoolValue("autoload", true)) {
String includes = list.getValue("includes", "");
String excludes = list.getValue("excludes", "");
filter.setIncludePatterns(includes.split(";"));
filter.setExcludePatterns(excludes.split(";"));
} else {
if (ref2 == null || ref2 == Annotation.class) { //service如果是autoload=false则不需要加载
filter.setRefused(true);
} else if (ref2 != Annotation.class) {
filter.setAnnotationClass(ref2);
}
}
cf = (cf == null) ? filter : cf.or(filter);
}
for (AnyValue av : list.getAnyValues(property)) {
for (AnyValue prop : av.getAnyValues("property")) {
((DefaultAnyValue) av).addValue(prop.getValue("name"), prop.getValue("value"));
}
filter.filter(av, av.getValue("value"), false);
}
if (list.getBoolValue("autoload", true)) {
String includes = list.getValue("includes", "");
String excludes = list.getValue("excludes", "");
filter.setIncludePatterns(includes.split(";"));
filter.setExcludePatterns(excludes.split(";"));
} else {
if (ref2 == null || ref2 == Annotation.class) {
filter.setRefused(true);
} else if (ref2 != Annotation.class) {
filter.setAnnotationClass(ref2);
}
}
return filter;
return cf;
}
}

View File

@@ -5,11 +5,13 @@
*/
package com.wentch.redkale.boot;
import com.wentch.redkale.net.sncp.SncpServer;
import com.wentch.redkale.net.sncp.*;
import com.wentch.redkale.util.AnyValue;
import com.wentch.redkale.service.Service;
import java.io.*;
import java.net.InetSocketAddress;
import java.util.concurrent.CountDownLatch;
import java.util.logging.*;
/**
*
@@ -19,16 +21,15 @@ public final class NodeSncpServer extends NodeServer {
private final SncpServer server;
public NodeSncpServer(Application application, CountDownLatch regcdl, SncpServer server) {
super(application, regcdl, server);
this.server = server;
this.consumer = x -> server.addService(x);
}
private final File home;
@Override
public void init(AnyValue config) throws Exception {
server.init(config);
super.init(config);
public NodeSncpServer(Application application, InetSocketAddress addr, CountDownLatch regcdl, SncpServer server) {
super(application, application.factory.createChild(), regcdl, server);
this.server = server;
this.home = application.getHome();
this.servaddr = addr;
this.nodeGroup = application.addrGroups.getOrDefault(addr, "");
this.consumer = server == null ? null : x -> server.addService(x);
}
@Override
@@ -38,13 +39,28 @@ public final class NodeSncpServer extends NodeServer {
@Override
public void prepare(AnyValue config) throws Exception {
super.prepare(config);
ClassFilter<Service> serviceFilter = createServiceClassFilter(application.nodeName, config);
ClassFilter<Service> serviceFilter = createServiceClassFilter(config);
long s = System.currentTimeMillis();
ClassFilter.Loader.load(application.getHome(), serviceFilter);
ClassFilter.Loader.load(home, serviceFilter);
long e = System.currentTimeMillis() - s;
logger.info(this.getClass().getSimpleName() + " load filter class in " + e + " ms");
loadService(config.getAnyValue("services"), serviceFilter); //必须在servlet之前
//-------------------------------------------------------------------
if(server == null) return; //调试时server才可能为null
final StringBuilder sb = logger.isLoggable(Level.FINE) ? new StringBuilder() : null;
final String threadName = "[" + Thread.currentThread().getName() + "] ";
for (SncpServlet en : server.getSncpServlets()) {
if (sb != null) sb.append(threadName).append(" Loaded ").append(en).append(LINE_SEPARATOR);
}
if (sb != null && sb.length() > 0) logger.log(Level.FINE, sb.toString());
}
@Override
public boolean isSNCP() {
return true;
}
public SncpServer getSncpServer() {
return server;
}
}

View File

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

View File

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

View File

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

View File

@@ -8,6 +8,7 @@ package com.wentch.redkale.convert.bson;
import com.wentch.redkale.convert.*;
import com.wentch.redkale.util.*;
import java.lang.reflect.*;
import java.nio.*;
/**
* BSON协议格式:
@@ -78,4 +79,26 @@ public final class BsonConvert extends Convert<BsonReader, BsonWriter> {
}
return convertTo(value.getClass(), value);
}
public ByteBuffer convertToBuffer(final Type type, Object value) {
if (type == null) return null;
final BsonWriter out = writerPool.poll();
out.setTiny(tiny);
factory.loadEncoder(type).convertTo(out, value);
ByteBuffer result = out.toBuffer();
writerPool.offer(out);
return result;
}
public ByteBuffer convertToBuffer(Object value) {
if (value == null) {
final BsonWriter out = writerPool.poll();
out.setTiny(tiny);
out.writeNull();
ByteBuffer result = out.toBuffer();
writerPool.offer(out);
return result;
}
return convertToBuffer(value.getClass(), value);
}
}

View File

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

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();

View File

@@ -30,4 +30,4 @@ public final class LongSimpledCoder<R extends Reader, W extends Writer> extends
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.Factory;
import java.io.Serializable;
import java.net.*;
/**
*
@@ -18,6 +19,8 @@ public final class JsonFactory extends Factory<JsonReader, JsonWriter> {
private static final JsonFactory instance = new JsonFactory(null, Boolean.getBoolean("convert.json.tiny"));
static {
instance.register(InetAddress.class, InetAddressJsonSimpledCoder.instance);
instance.register(InetSocketAddress.class, InetAddressJsonSimpledCoder.InetSocketAddressJsonSimpledCoder.instance);
instance.register(Serializable.class, instance.loadEncoder(Object.class));
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -6,7 +6,6 @@
package com.wentch.redkale.net.http;
import com.wentch.redkale.net.*;
import com.wentch.redkale.service.*;
import java.io.*;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
@@ -41,7 +40,7 @@ public abstract class WebSocket {
WebSocketGroup group;
WebSocketNodeService nodeService;
WebSocketNode node;
Serializable sessionid;
@@ -107,6 +106,16 @@ public abstract class WebSocket {
send(new WebSocketPacket(data, last));
}
/**
* 发送消息, 消息类型是String或byte[]
* <p>
* @param message 不可为空, 只能是String或者byte[]
* @param last 是否最后一条
*/
public final void send(Serializable message, boolean last) {
send(new WebSocketPacket(message, last));
}
//----------------------------------------------------------------
/**
* 给指定groupid的WebSocketGroup下所有WebSocket节点发送文本消息
@@ -201,21 +210,13 @@ public abstract class WebSocket {
}
private int sendMessage(Serializable groupid, boolean recent, String text, boolean last) {
if (nodeService == null) return WebSocketNodeService.RETCODE_NODESERVICE_NULL;
if (groupid == this.groupid) {
return nodeService.onSend(this.engine.getEngineid(), groupid, recent, text, last);
} else {
return nodeService.send(this.engine.getEngineid(), groupid, recent, text, last);
}
if (node == null) return WebSocketNode.RETCODE_NODESERVICE_NULL;
return node.sendMessage(groupid, recent, text, last);
}
private int sendMessage(Serializable groupid, boolean recent, byte[] data, boolean last) {
if (nodeService == null) return WebSocketNodeService.RETCODE_NODESERVICE_NULL;
if (groupid == this.groupid) {
return nodeService.onSend(this.engine.getEngineid(), groupid, recent, data, last);
} else {
return nodeService.send(this.engine.getEngineid(), groupid, recent, data, last);
}
if (node == null) return WebSocketNode.RETCODE_NODESERVICE_NULL;
return node.sendMessage(groupid, recent, data, last);
}
/**
@@ -309,9 +310,7 @@ public abstract class WebSocket {
*
* @return
*/
public Serializable createGroupid() {
return null;
}
protected abstract Serializable createGroupid();
/**
*

View File

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

View File

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

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;
import com.wentch.redkale.util.Utility;
import java.io.*;
/**
*
@@ -54,6 +55,18 @@ public final class WebSocketPacket {
this(payload, true);
}
public WebSocketPacket(Serializable message, boolean fin) {
boolean bin = message != null && message.getClass() == byte[].class;
if (bin) {
this.type = PacketType.BINARY;
this.bytes = (byte[]) message;
} else {
this.type = PacketType.TEXT;
this.payload = String.valueOf(message);
}
this.last = fin;
}
public WebSocketPacket(String payload, boolean fin) {
this.type = PacketType.TEXT;
this.payload = payload;

View File

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

View File

@@ -6,9 +6,9 @@
package com.wentch.redkale.net.http;
import com.wentch.redkale.net.*;
import com.wentch.redkale.service.*;
import com.wentch.redkale.util.*;
import java.io.*;
import java.net.*;
import java.nio.*;
import java.nio.channels.*;
import java.security.*;
@@ -33,7 +33,7 @@ import javax.annotation.*;
*
* @author zhangjx
*/
public abstract class WebSocketServlet extends HttpServlet {
public abstract class WebSocketServlet extends HttpServlet implements Nameable {
protected final Logger logger = Logger.getLogger(this.getClass().getSimpleName());
@@ -50,21 +50,22 @@ public abstract class WebSocketServlet extends HttpServlet {
//是否用于二进制流传输
protected final boolean wsbinary = getClass().getAnnotation(WebSocketBinary.class) != null;
@Resource
protected WebSocketNodeService nodeService;
@Resource(name = "$")
protected WebSocketNode node;
protected final WebSocketEngine engine = WebSocketEngine.create(this.getClass().getName() + "-" + Arrays.toString(this.getClass().getAnnotation(WebServlet.class).value()));
protected WebSocketEngine engine;
@Override
public void init(Context context, AnyValue conf) {
if (nodeService != null) {
nodeService.addWebSocketEngine(engine);
nodeService.initUserNodes();
}
InetSocketAddress addr = context.getServerAddress();
this.engine = new WebSocketEngine(addr.getHostString() + ":" + addr.getPort() + "-" + name());
this.node.addWebSocketEngine(engine);
this.node.init(conf);
}
@Override
public void destroy(Context context, AnyValue conf) {
this.node.destroy(conf);
super.destroy(context, conf);
engine.close();
}
@@ -87,7 +88,7 @@ public abstract class WebSocketServlet extends HttpServlet {
}
final WebSocket webSocket = this.createWebSocket();
webSocket.engine = engine;
webSocket.nodeService = nodeService;
webSocket.node = node;
Serializable sessionid = webSocket.onOpen(request);
if (sessionid == null) {
if (debug) logger.finer("WebSocket connect abort, Not found sessionid. request=" + request);
@@ -130,5 +131,8 @@ public abstract class WebSocketServlet extends HttpServlet {
});
}
@Override
public abstract String name();
protected abstract WebSocket createWebSocket();
}

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.service.*;
import com.wentch.redkale.util.*;
import java.lang.reflect.*;
import java.net.*;
import java.util.*;
import jdk.internal.org.objectweb.asm.*;
import static jdk.internal.org.objectweb.asm.Opcodes.*;
import jdk.internal.org.objectweb.asm.Type;
/**
*
@@ -19,6 +23,12 @@ import static jdk.internal.org.objectweb.asm.Opcodes.*;
*/
public abstract class Sncp {
public static final String DEFAULT_PROTOCOL = "UDP";
static final String LOCALPREFIX = "_DynLocal";
static final String REMOTEPREFIX = "_DynRemote";
private static final byte[] hashes = new byte[255];
static {
@@ -40,12 +50,23 @@ public abstract class Sncp {
private Sncp() {
}
public static long nodeid(InetSocketAddress ip) {
byte[] bytes = ip.getAddress().getAddress();
return ((0L + ip.getPort()) << 32) | ((0xffffffff & bytes[0]) << 24) | ((0xffffff & bytes[1]) << 16) | ((0xffff & bytes[2]) << 8) | (0xff & bytes[3]);
}
public static long hash(final Class clazz) {
if (clazz == null) return Long.MIN_VALUE;
long rs = hash(clazz.getSimpleName());
return (rs < Integer.MAX_VALUE) ? rs | 0xF00000000L : rs;
}
public static long hashClass(final String clazzName) {
if (clazzName == null || clazzName.isEmpty()) return Long.MIN_VALUE;
long rs = hash(clazzName.substring(clazzName.lastIndexOf('.') + 1));
return (rs < Integer.MAX_VALUE) ? rs | 0xF00000000L : rs;
}
public static DLong hash(final java.lang.reflect.Method method) {
if (method == null) return new DLong(-1L, -1L);
long rs1 = hash(method.getName());
@@ -95,61 +116,103 @@ public abstract class Sncp {
return Math.abs(rs);
}
public static boolean isRemote(Service service) {
return service.getClass().getName().startsWith(REMOTEPREFIX);
}
/*
* public final class DynRemoteTestService extends TestService{
*
* @Resource
* private BsonConvert convert;
*
* @Resource(name="xxxx")
* private Transport transport;
*
* public SncpClient client;
*
* @Override
* public boolean testChange(TestBean bean) {
* return client.remote(convert, transport, 0, bean);
* }
*
* @Override
* public TestBean findTestBean(long id) {
* return client.remote(convert, transport, 1, id);
* }
*
* @Override
* public void runTestBean(long id, TestBean bean) {
* client.remote(convert, transport, 2, id, bean);
* }
*/
/**
* public class TestService implements Service{
*
* public String queryNode(){
* return "hello";
* }
*
* @MultiRun
* public String updateSomeThing(String id){
* return "hello" + id;
* }
*
* @MultiRun
* public void createSomeThing(TestBean bean){
* "xxxxx" + bean;
* }
* }
*
* public final class _DynLocalTestService extends TestService{
*
* @Resource
* private BsonConvert _convert;
*
* private Transport[] _sameGroupTransports;
*
* private Transport[] _diffGroupTransports;
*
* private SncpClient _client;
*
* @Override
* public final String name() {
* return "";
* }
*
* @Override
* public String updateSomeThing(String id){
* return _updateSomeThing(true, true, id);
* }
*
* public String _updateSomeThing(boolean cansamerun, boolean candiffrun, String id){
* String rs = super.updateSomeThing(id);
* _client.remote(_convert, _sameGroupTransports, cansamerun, 0, false, false, id);
* _client.remote(_convert, _diffGroupTransports, candiffrun, 0, true, false, id);
* return rs;
* }
*
* @Override
* public void createSomeThing(TestBean bean){
* _createSomeThing(true, true, bean);
* }
*
* public void _createSomeThing(boolean cansamerun, boolean candiffrun, TestBean bean){
* super.createSomeThing(bean);
* _client.remote(_convert, _sameGroupTransports, cansamerun, 1, false, false, bean);
* _client.remote(_convert, _diffGroupTransports, candiffrun, 1, true, false, bean);
* }
* }
*
* 创建Service的本地模式Class
* @param <T>
* @param serviceName
* @param remote
* @param name
* @param serviceClass
* @return
* @return
*/
@SuppressWarnings("unchecked")
public static <T extends Service> T createRemoteService(final String serviceName, final Class<T> serviceClass, final String remote) {
public static <T extends Service> Class<? extends T> createLocalServiceClass(final String name, final Class<T> serviceClass) {
if (serviceClass == null) return null;
if (!Service.class.isAssignableFrom(serviceClass)) return null;
if (!Service.class.isAssignableFrom(serviceClass)) return serviceClass;
int mod = serviceClass.getModifiers();
if (!java.lang.reflect.Modifier.isPublic(mod)) return null;
if (java.lang.reflect.Modifier.isAbstract(mod)) return null;
if (!java.lang.reflect.Modifier.isPublic(mod)) return serviceClass;
if (java.lang.reflect.Modifier.isAbstract(mod)) return serviceClass;
final List<Method> methods = SncpClient.parseMethod(serviceClass, false);
boolean hasMultiRun0 = false;
for (Method method : methods) {
if (method.getAnnotation(MultiRun.class) != null) {
hasMultiRun0 = true;
break;
}
}
final boolean hasMultiRun = hasMultiRun0;
final String supDynName = serviceClass.getName().replace('.', '/');
final String clientName = SncpClient.class.getName().replace('.', '/');
final String clientDesc = Type.getDescriptor(SncpClient.class);
final String convertDesc = Type.getDescriptor(BsonConvert.class);
final String transportDesc = Type.getDescriptor(Transport.class);
final String anyValueDesc = Type.getDescriptor(AnyValue.class);
final String sncpDynDesc = Type.getDescriptor(SncpDyn.class);
final String transportsDesc = Type.getDescriptor(Transport[].class);
ClassLoader loader = Sncp.class.getClassLoader();
String newDynName = supDynName.substring(0, supDynName.lastIndexOf('/') + 1) + "DynRemote" + serviceClass.getSimpleName();
String newDynName = supDynName.substring(0, supDynName.lastIndexOf('/') + 1) + LOCALPREFIX + serviceClass.getSimpleName();
try {
return (T) Class.forName(newDynName.replace('/', '.')).newInstance();
return (Class<T>) Class.forName(newDynName.replace('/', '.'));
} catch (Exception ex) {
}
final SncpClient client = new SncpClient(serviceName, serviceClass);
//------------------------------------------------------------------------------
ClassWriter cw = new ClassWriter(0);
FieldVisitor fv;
@@ -158,24 +221,436 @@ public abstract class Sncp {
cw.visit(V1_8, ACC_PUBLIC + ACC_FINAL + ACC_SUPER, newDynName, null, supDynName, null);
{
av0 = cw.visitAnnotation(Type.getDescriptor(RemoteOn.class), true);
av0 = cw.visitAnnotation(sncpDynDesc, true);
av0.visitEnd();
}
if (hasMultiRun) {
{
fv = cw.visitField(ACC_PRIVATE, "_convert", convertDesc, null, null);
av0 = fv.visitAnnotation("Ljavax/annotation/Resource;", true);
av0.visitEnd();
fv.visitEnd();
}
{
fv = cw.visitField(ACC_PRIVATE, "_sameGroupTransports", transportsDesc, null, null);
fv.visitEnd();
}
{
fv = cw.visitField(ACC_PRIVATE, "_diffGroupTransports", transportsDesc, null, null);
fv.visitEnd();
}
{
fv = cw.visitField(ACC_PRIVATE, "_client", clientDesc, null, null);
fv.visitEnd();
}
}
{ //构造函数
mv = new DebugMethodVisitor(cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null));
//mv.setDebug(true);
mv.visitVarInsn(ALOAD, 0);
mv.visitMethodInsn(INVOKESPECIAL, supDynName, "<init>", "()V", false);
mv.visitInsn(RETURN);
mv.visitMaxs(1, 1);
mv.visitEnd();
}
{ // name()
mv = new DebugMethodVisitor(cw.visitMethod(ACC_PUBLIC + ACC_FINAL, "name", "()Ljava/lang/String;", null, null));
mv.visitLdcInsn(name);
mv.visitInsn(ARETURN);
mv.visitMaxs(1, 1);
mv.visitEnd();
}
int i = - 1;
for (final Method method : methods) {
final MultiRun mrun = method.getAnnotation(MultiRun.class);
if (mrun == null) continue;
final Class returnType = method.getReturnType();
final String methodDesc = Type.getMethodDescriptor(method);
final Class[] paramtypes = method.getParameterTypes();
final int index = ++i;
{ //原始方法
mv = new DebugMethodVisitor(cw.visitMethod(ACC_PUBLIC + (method.isVarArgs() ? ACC_VARARGS : 0), method.getName(), methodDesc, null, null));
//mv.setDebug(true);
mv.visitVarInsn(ALOAD, 0);
mv.visitInsn(mrun.samerun() ? ICONST_1 : ICONST_0);
mv.visitInsn(mrun.diffrun() ? ICONST_1 : ICONST_0);
int varindex = 0;
for (Class pt : paramtypes) {
if (pt.isPrimitive()) {
if (pt == long.class) {
mv.visitVarInsn(LLOAD, ++varindex);
++varindex;
} else if (pt == double.class) {
mv.visitVarInsn(DLOAD, ++varindex);
++varindex;
} else if (pt == float.class) {
mv.visitVarInsn(FLOAD, ++varindex);
} else {
mv.visitVarInsn(ILOAD, ++varindex);
}
} else {
mv.visitVarInsn(ALOAD, ++varindex);
}
}
mv.visitMethodInsn(INVOKEVIRTUAL, newDynName, "_" + method.getName(), "(ZZ" + methodDesc.substring(1), false);
if (returnType == void.class) {
mv.visitInsn(RETURN);
} else if (returnType.isPrimitive()) {
if (returnType == long.class) {
mv.visitInsn(LRETURN);
} else if (returnType == float.class) {
mv.visitInsn(FRETURN);
} else if (returnType == double.class) {
mv.visitInsn(DRETURN);
} else {
mv.visitInsn(IRETURN);
}
} else {
mv.visitInsn(ARETURN);
}
mv.visitMaxs(varindex + 3, varindex + 1);
mv.visitEnd();
}
{ // _方法
mv = new DebugMethodVisitor(cw.visitMethod(ACC_PUBLIC + (method.isVarArgs() ? ACC_VARARGS : 0), "_" + method.getName(), "(ZZ" + methodDesc.substring(1), null, null));
//mv.setDebug(true);
av0 = mv.visitAnnotation(sncpDynDesc, true);
av0.visit("index", index);
av0.visitEnd();
mv.visitVarInsn(ALOAD, 0);
int varindex = 2;
for (Class pt : paramtypes) {
if (pt.isPrimitive()) {
if (pt == long.class) {
mv.visitVarInsn(LLOAD, ++varindex);
++varindex;
} else if (pt == double.class) {
mv.visitVarInsn(DLOAD, ++varindex);
++varindex;
} else if (pt == float.class) {
mv.visitVarInsn(FLOAD, ++varindex);
} else {
mv.visitVarInsn(ILOAD, ++varindex);
}
} else {
mv.visitVarInsn(ALOAD, ++varindex);
}
}
mv.visitMethodInsn(INVOKESPECIAL, supDynName, method.getName(), methodDesc, false);
if (returnType == void.class) {
} else if (returnType.isPrimitive()) {
if (returnType == long.class) {
mv.visitVarInsn(LSTORE, ++varindex);
++varindex; //多加1
} else if (returnType == float.class) {
mv.visitVarInsn(FSTORE, ++varindex);
} else if (returnType == double.class) {
mv.visitVarInsn(DSTORE, ++varindex);
++varindex; //多加1
} else {
mv.visitVarInsn(ISTORE, ++varindex);
}
} else {
mv.visitVarInsn(ASTORE, ++varindex);
}
final int rsindex = varindex; //
mv.visitVarInsn(ALOAD, 0);//调用 _client
mv.visitFieldInsn(GETFIELD, newDynName, "_client", clientDesc);
mv.visitVarInsn(ALOAD, 0); //传递 _convert
mv.visitFieldInsn(GETFIELD, newDynName, "_convert", convertDesc);
mv.visitVarInsn(ALOAD, 0); //传递 _sameGroupTransports
mv.visitFieldInsn(GETFIELD, newDynName, "_sameGroupTransports", transportsDesc);
mv.visitVarInsn(ILOAD, 1); //传递 cansamerun
if (index <= 5) { //第几个 SncpAction
mv.visitInsn(ICONST_0 + index);
} else {
mv.visitIntInsn(BIPUSH, index);
}
if (paramtypes.length + 2 <= 5) { //参数总数量
mv.visitInsn(ICONST_0 + paramtypes.length + 2);
} else {
mv.visitIntInsn(BIPUSH, paramtypes.length + 2);
}
mv.visitTypeInsn(ANEWARRAY, "java/lang/Object");
mv.visitInsn(DUP);
mv.visitInsn(ICONST_0);
mv.visitInsn(ICONST_0); //第一个参数 cansamerun
mv.visitMethodInsn(INVOKESTATIC, "java/lang/Boolean", "valueOf", "(Z)Ljava/lang/Boolean;", false);
mv.visitInsn(AASTORE);
mv.visitInsn(DUP);
mv.visitInsn(ICONST_1);
mv.visitInsn(ICONST_0); //第二个参数 candiffrun
mv.visitMethodInsn(INVOKESTATIC, "java/lang/Boolean", "valueOf", "(Z)Ljava/lang/Boolean;", false);
mv.visitInsn(AASTORE);
int insn = 2;
for (int j = 0; j < paramtypes.length; j++) {
final Class pt = paramtypes[j];
mv.visitInsn(DUP);
insn++;
if (j <= 3) {
mv.visitInsn(ICONST_0 + j + 2);
} else {
mv.visitIntInsn(BIPUSH, j + 2);
}
if (pt.isPrimitive()) {
if (pt == long.class) {
mv.visitVarInsn(LLOAD, insn++);
} else if (pt == float.class) {
mv.visitVarInsn(FLOAD, insn++);
} else if (pt == double.class) {
mv.visitVarInsn(DLOAD, insn++);
} else {
mv.visitVarInsn(ILOAD, insn);
}
Class bigclaz = java.lang.reflect.Array.get(java.lang.reflect.Array.newInstance(pt, 1), 0).getClass();
mv.visitMethodInsn(INVOKESTATIC, bigclaz.getName().replace('.', '/'), "valueOf", "(" + Type.getDescriptor(pt) + ")" + Type.getDescriptor(bigclaz), false);
} else {
mv.visitVarInsn(ALOAD, insn);
}
mv.visitInsn(AASTORE);
}
mv.visitMethodInsn(INVOKEVIRTUAL, clientName, mrun.async() ? "asyncRemote" : "remote", "(" + convertDesc + transportsDesc + "ZI[Ljava/lang/Object;)V", false);
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, newDynName, "_client", clientDesc);
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, newDynName, "_convert", convertDesc);
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, newDynName, "_diffGroupTransports", transportsDesc);
mv.visitVarInsn(ILOAD, 2); //传递 candiffrun
if (index <= 5) { //第几个 SncpAction
mv.visitInsn(ICONST_0 + index);
} else {
mv.visitIntInsn(BIPUSH, index);
}
if (paramtypes.length + 2 <= 5) { //参数总数量
mv.visitInsn(ICONST_0 + paramtypes.length + 2);
} else {
mv.visitIntInsn(BIPUSH, paramtypes.length + 2);
}
mv.visitTypeInsn(ANEWARRAY, "java/lang/Object");
mv.visitInsn(DUP);
mv.visitInsn(ICONST_0);
mv.visitInsn(ICONST_1); //第一个参数 cansamerun
mv.visitMethodInsn(INVOKESTATIC, "java/lang/Boolean", "valueOf", "(Z)Ljava/lang/Boolean;", false);
mv.visitInsn(AASTORE);
mv.visitInsn(DUP);
mv.visitInsn(ICONST_1);
mv.visitInsn(ICONST_0); //第二个参数 candiffrun
mv.visitMethodInsn(INVOKESTATIC, "java/lang/Boolean", "valueOf", "(Z)Ljava/lang/Boolean;", false);
mv.visitInsn(AASTORE);
insn = 2;
for (int j = 0; j < paramtypes.length; j++) {
final Class pt = paramtypes[j];
mv.visitInsn(DUP);
insn++;
if (j <= 3) {
mv.visitInsn(ICONST_0 + j + 2);
} else {
mv.visitIntInsn(BIPUSH, j + 2);
}
if (pt.isPrimitive()) {
if (pt == long.class) {
mv.visitVarInsn(LLOAD, insn++);
} else if (pt == float.class) {
mv.visitVarInsn(FLOAD, insn++);
} else if (pt == double.class) {
mv.visitVarInsn(DLOAD, insn++);
} else {
mv.visitVarInsn(ILOAD, insn);
}
Class bigclaz = java.lang.reflect.Array.get(java.lang.reflect.Array.newInstance(pt, 1), 0).getClass();
mv.visitMethodInsn(INVOKESTATIC, bigclaz.getName().replace('.', '/'), "valueOf", "(" + Type.getDescriptor(pt) + ")" + Type.getDescriptor(bigclaz), false);
} else {
mv.visitVarInsn(ALOAD, insn);
}
mv.visitInsn(AASTORE);
}
mv.visitMethodInsn(INVOKEVIRTUAL, clientName, mrun.async() ? "asyncRemote" : "remote", "(" + convertDesc + transportsDesc + "ZI[Ljava/lang/Object;)V", false);
if (returnType == void.class) {
mv.visitInsn(RETURN);
} else if (returnType.isPrimitive()) {
if (returnType == long.class) {
mv.visitVarInsn(LLOAD, rsindex);
mv.visitInsn(LRETURN);
} else if (returnType == float.class) {
mv.visitVarInsn(FLOAD, rsindex);
mv.visitInsn(FRETURN);
} else if (returnType == double.class) {
mv.visitVarInsn(DLOAD, rsindex);
mv.visitInsn(DRETURN);
} else {
mv.visitVarInsn(ILOAD, rsindex);
mv.visitInsn(IRETURN);
}
} else {
mv.visitVarInsn(ALOAD, rsindex);
mv.visitInsn(ARETURN);
}
mv.visitMaxs(Math.max(varindex, 10), varindex + 4);
mv.visitEnd();
}
}
cw.visitEnd();
byte[] bytes = cw.toByteArray();
Class<?> newClazz = new ClassLoader(loader) {
public final Class<?> loadClass(String name, byte[] b) {
return defineClass(name, b, 0, b.length);
}
}.loadClass(newDynName.replace('/', '.'), bytes);
return (Class<T>) newClazz;
}
/**
*
* 创建本地模式Service实例
* @param <T>
* @param name
* @param serviceClass
* @param clientAddress
* @param sameGroupTransports
* @param diffGroupTransports
* @return
*/
@SuppressWarnings("unchecked")
public static <T extends Service> T createLocalService(final String name, final Class<T> serviceClass,
final InetSocketAddress clientAddress, Collection<Transport> sameGroupTransports, Collection<Transport> diffGroupTransports) {
try {
Class newClazz = createLocalServiceClass(name, serviceClass);
T rs = (T) newClazz.newInstance();
Field e = null;
try {
e = newClazz.getDeclaredField("_client");
} catch (NoSuchFieldException ne) {
return rs;
}
e.setAccessible(true);
e.set(rs, new SncpClient(name, hash(serviceClass), false, newClazz, true, clientAddress));
if (sameGroupTransports == null) sameGroupTransports = new ArrayList<>();
Field c = newClazz.getDeclaredField("_sameGroupTransports");
c.setAccessible(true);
c.set(rs, sameGroupTransports.toArray(new Transport[sameGroupTransports.size()]));
if (diffGroupTransports == null) diffGroupTransports = new ArrayList<>();
Field t = newClazz.getDeclaredField("_diffGroupTransports");
t.setAccessible(true);
t.set(rs, diffGroupTransports.toArray(new Transport[diffGroupTransports.size()]));
return rs;
} catch (RuntimeException rex) {
throw rex;
} catch (Exception ex) {
throw new RuntimeException(ex);
}
}
/**
* public final class _DynRemoteTestService extends TestService{
*
* @Resource
* private BsonConvert _convert;
*
* private Transport _transport;
*
* private SncpClient _client;
*
* @Override
* public final String name() {
* return "";
* }
*
* @Override
* public boolean testChange(TestBean bean) {
* return _client.remote(_convert, _transport, 0, bean);
* }
*
* @Override
* public TestBean findTestBean(long id) {
* return _client.remote(_convert, _transport, 1, id);
* }
*
* @Override
* public void runTestBean(long id, TestBean bean) {
* _client.remote(_convert, _transport, 2, id, bean);
* }
* }
*
* 创建远程模式的Service实例
* <p>
* @param <T>
* @param name
* @param serviceClass
* @param clientAddress
* @param transport
* @return
*/
@SuppressWarnings("unchecked")
public static <T extends Service> T createRemoteService(final String name, final Class<T> serviceClass, final InetSocketAddress clientAddress, final Transport transport) {
if (serviceClass == null) return null;
if (!Service.class.isAssignableFrom(serviceClass)) return null;
int mod = serviceClass.getModifiers();
if (!java.lang.reflect.Modifier.isPublic(mod)) return null;
if (java.lang.reflect.Modifier.isAbstract(mod)) return null;
final String supDynName = serviceClass.getName().replace('.', '/');
final String clientName = SncpClient.class.getName().replace('.', '/');
final String clientDesc = Type.getDescriptor(SncpClient.class);
final String sncpDynDesc = Type.getDescriptor(SncpDyn.class);
final String convertDesc = Type.getDescriptor(BsonConvert.class);
final String transportDesc = Type.getDescriptor(Transport.class);
final String anyValueDesc = Type.getDescriptor(AnyValue.class);
ClassLoader loader = Sncp.class.getClassLoader();
String newDynName = supDynName.substring(0, supDynName.lastIndexOf('/') + 1) + REMOTEPREFIX + serviceClass.getSimpleName();
final SncpClient client = new SncpClient(name, hash(serviceClass), true, createLocalServiceClass(name, serviceClass), false, clientAddress);
try {
Class newClazz = Class.forName(newDynName.replace('/', '.'));
T rs = (T) newClazz.newInstance();
Field c = newClazz.getDeclaredField("_client");
c.setAccessible(true);
c.set(rs, client);
Field t = newClazz.getDeclaredField("_transport");
t.setAccessible(true);
t.set(rs, transport);
return rs;
} catch (Exception ex) {
}
//------------------------------------------------------------------------------
ClassWriter cw = new ClassWriter(0);
FieldVisitor fv;
DebugMethodVisitor mv;
AnnotationVisitor av0;
cw.visit(V1_8, ACC_PUBLIC + ACC_FINAL + ACC_SUPER, newDynName, null, supDynName, null);
{
av0 = cw.visitAnnotation(sncpDynDesc, true);
av0.visitEnd();
}
{
fv = cw.visitField(ACC_PRIVATE, "convert", convertDesc, null, null);
fv = cw.visitField(ACC_PRIVATE, "_convert", convertDesc, null, null);
av0 = fv.visitAnnotation("Ljavax/annotation/Resource;", true);
av0.visitEnd();
fv.visitEnd();
}
{
fv = cw.visitField(ACC_PRIVATE, "transport", transportDesc, null, null);
av0 = fv.visitAnnotation("Ljavax/annotation/Resource;", true);
av0.visit("name", remote == null ? "" : remote);
av0.visitEnd();
fv = cw.visitField(ACC_PRIVATE, "_transport", transportDesc, null, null);
fv.visitEnd();
}
{
fv = cw.visitField(ACC_PUBLIC, "client", clientDesc, null, null);
fv = cw.visitField(ACC_PRIVATE, "_client", clientDesc, null, null);
fv.visitEnd();
}
{ //构造函数
@@ -199,6 +674,13 @@ public abstract class Sncp {
mv.visitMaxs(0, 2);
mv.visitEnd();
}
{ // name()
mv = new DebugMethodVisitor(cw.visitMethod(ACC_PUBLIC + ACC_FINAL, "name", "()Ljava/lang/String;", null, null));
mv.visitLdcInsn(name);
mv.visitInsn(ARETURN);
mv.visitMaxs(1, 1);
mv.visitEnd();
}
int i = -1;
for (final SncpAction entry : client.actions) {
final int index = ++i;
@@ -207,11 +689,11 @@ public abstract class Sncp {
mv = new DebugMethodVisitor(cw.visitMethod(ACC_PUBLIC, method.getName(), Type.getMethodDescriptor(method), null, null));
//mv.setDebug(true);
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, newDynName, "client", clientDesc);
mv.visitFieldInsn(GETFIELD, newDynName, "_client", clientDesc);
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, newDynName, "convert", convertDesc);
mv.visitFieldInsn(GETFIELD, newDynName, "_convert", convertDesc);
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, newDynName, "transport", transportDesc);
mv.visitFieldInsn(GETFIELD, newDynName, "_transport", transportDesc);
if (index <= 5) {
mv.visitInsn(ICONST_0 + index);
} else {
@@ -252,7 +734,6 @@ public abstract class Sncp {
} else {
mv.visitVarInsn(ALOAD, insn);
}
//mv.visitVarInsn(ALOAD, 1);
mv.visitInsn(AASTORE);
}
}
@@ -300,7 +781,12 @@ public abstract class Sncp {
}.loadClass(newDynName.replace('/', '.'), bytes);
try {
T rs = (T) newClazz.newInstance();
newClazz.getField("client").set(rs, client);
Field c = newClazz.getDeclaredField("_client");
c.setAccessible(true);
c.set(rs, client);
Field t = newClazz.getDeclaredField("_transport");
t.setAccessible(true);
t.set(rs, transport);
return rs;
} catch (Exception ex) {
throw new RuntimeException(ex);

View File

@@ -8,9 +8,9 @@ package com.wentch.redkale.net.sncp;
import com.wentch.redkale.convert.bson.*;
import com.wentch.redkale.net.*;
import static com.wentch.redkale.net.sncp.SncpRequest.HEADER_SIZE;
import com.wentch.redkale.service.*;
import com.wentch.redkale.util.*;
import java.lang.reflect.*;
import java.net.*;
import java.nio.*;
import java.util.*;
import java.util.concurrent.*;
@@ -57,92 +57,137 @@ public final class SncpClient {
}
}
protected final String name;
protected final boolean remote;
private final Class serviceClass;
private final byte[] addrBytes;
private final int addrPort;
protected final long nameid;
protected final long serviceid;
protected final SncpAction[] actions;
public SncpClient(final String serviceName, final Class serviceClass) {
public SncpClient(final String serviceName, final long serviceid0, boolean remote, final Class serviceClass, boolean onlySncpDyn, final InetSocketAddress clientAddress) {
if (serviceName.length() > 10) throw new RuntimeException(serviceClass + " @Resource name(" + serviceName + ") too long , must less 11");
this.nameid = Sncp.hash(MultiService.class.isAssignableFrom(serviceClass) ? serviceName : "");
this.serviceid = Sncp.hash(serviceClass);
this.remote = remote;
this.serviceClass = serviceClass;
//if (subLocalClass != null && !serviceClass.isAssignableFrom(subLocalClass)) throw new RuntimeException(subLocalClass + " is not " + serviceClass + " sub class ");
this.name = serviceName;
this.nameid = Sncp.hash(serviceName);
this.serviceid = serviceid0 > 0 ? serviceid0 : Sncp.hash(serviceClass);
final List<SncpAction> methodens = new ArrayList<>();
//------------------------------------------------------------------------------
Set<DLong> actionids = new HashSet<>();
for (java.lang.reflect.Method method : serviceClass.getDeclaredMethods()) {
for (java.lang.reflect.Method method : parseMethod(serviceClass, onlySncpDyn)) {
SncpAction en = new SncpAction(method, Sncp.hash(method));
methodens.add(en);
}
this.actions = methodens.toArray(new SncpAction[methodens.size()]);
this.addrBytes = clientAddress == null ? new byte[4] : clientAddress.getAddress().getAddress();
this.addrPort = clientAddress == null ? 0 : clientAddress.getPort();
logger.fine("[" + Thread.currentThread().getName() + "] Load " + this);
}
@Override
public String toString() {
String service = serviceClass.getName();
if (remote) service = service.replace(Sncp.LOCALPREFIX, Sncp.REMOTEPREFIX);
return this.getClass().getSimpleName() + "(service = " + service + ", serviceid = " + serviceid + ", name = " + name + ", nameid = " + nameid + ", actions.size = " + actions.length + ")";
}
public static List<Method> parseMethod(final Class serviceClass, boolean onlySncpDyn) {
final List<Method> list = new ArrayList<>();
final List<Method> multis = new ArrayList<>();
final Map<DLong, Method> actionids = new HashMap<>();
for (final java.lang.reflect.Method method : serviceClass.getMethods()) {
if (method.isSynthetic()) continue;
final int mod = method.getModifiers();
if (!Modifier.isPublic(mod) && !Modifier.isProtected(mod)) continue;
if (Modifier.isStatic(mod)) continue;
if (Modifier.isFinal(mod)) continue;
if (method.getName().equals("getClass") || method.getName().equals("toString")) continue;
if (method.getName().equals("equals") || method.getName().equals("hashCode")) continue;
if (method.getName().equals("notify") || method.getName().equals("notifyAll") || method.getName().equals("wait")) continue;
if (method.getName().equals("init") || method.getName().equals("destroy")) continue;
Method onMethod = getOnMethod(serviceClass, method);
SncpAction en = new SncpAction(method, onMethod == null ? Sncp.hash(method) : Sncp.hash(onMethod));
if (actionids.contains(en.actionid)) {
throw new RuntimeException(serviceClass.getName() + " have one more same action(Method=" + method + ", actionid=" + en.actionid + ")");
if (method.getName().equals("init") || method.getName().equals("destroy") || method.getName().equals("name")) continue;
if (onlySncpDyn && method.getAnnotation(SncpDyn.class) == null) continue;
DLong actionid = Sncp.hash(method);
Method old = actionids.get(actionid);
if (old != null) {
if (old.getDeclaringClass().equals(method.getDeclaringClass())) throw new RuntimeException(serviceClass.getName() + " have one more same action(Method=" + method + ", actionid=" + actionid + ")");
continue;
}
methodens.add(en);
actionids.add(en.actionid);
}
this.actions = methodens.toArray(new SncpAction[methodens.size()]);
logger.fine("Load " + this.getClass().getSimpleName() + "(serviceClass = " + serviceClass.getName() + ", serviceid =" + serviceid + ", serviceName =" + serviceName + ", actions = " + methodens + ")");
}
public static Method getOnMethod(final Class serviceClass, Method method) {
Method onMethod = null;
if (method.getAnnotation(RemoteOn.class) != null) {
char[] ms = method.getName().toCharArray();
ms[0] = Character.toUpperCase(ms[0]);
try {
onMethod = serviceClass.getMethod("on" + new String(ms), method.getParameterTypes());
if (onMethod.getReturnType() != method.getReturnType()) {
throw new RuntimeException(serviceClass.getName() + " (Method=" + method + ") and (Method=" + onMethod + ") has not same returnType");
}
if (!Modifier.isFinal(onMethod.getModifiers())) {
throw new RuntimeException(serviceClass.getName() + " (Method=" + method + ") is not final");
}
} catch (NoSuchMethodException e) {
throw new RuntimeException(serviceClass.getName() + " not found (Public Method=" + "on" + new String(ms) + "but " + method + " has @" + RemoteOn.class.getSimpleName());
actionids.put(actionid, method);
if (method.getAnnotation(SncpDyn.class) != null) {
multis.add(method);
} else {
list.add(method);
}
}
return onMethod;
list.addAll(multis);
if (onlySncpDyn && list.size() > 1) {
list.sort((m1, m2) -> m1.getAnnotation(SncpDyn.class).index() - m2.getAnnotation(SncpDyn.class).index());
}
return list;
}
public <T> T remote(final BsonConvert convert, Transport transport, final int index, final Object... params) {
return convert.convertFrom(actions[index].resultTypes, send(convert, transport, index, params));
return convert.convertFrom(actions[index].resultTypes, send(convert, transport, actions[index], params));
}
private void fillHeader(ByteBuffer buffer, long seqid, DLong actionid, int frameCount, int frameIndex, int bodyLength) {
//---------------------head----------------------------------
buffer.putLong(seqid); //序列号
buffer.putChar((char) HEADER_SIZE); //header长度
buffer.putLong(this.serviceid);
buffer.putLong(this.nameid);
buffer.putLong(actionid.getFirst());
buffer.putLong(actionid.getSecond());
buffer.put((byte) frameCount); //数据的帧数, 最小值为1
buffer.put((byte) frameIndex); //数据的帧数序号, 从frame.count-1开始, 0表示最后一帧
buffer.putInt(0); //结果码, 请求方固定传0
buffer.putInt(bodyLength); //body长度
public <T> void remote(final BsonConvert convert, Transport[] transports, boolean run, final int index, final Object... params) {
if (!run) return;
this.remote(false, convert, transports, run, index, params);
}
private byte[] send(final BsonConvert convert, Transport transport, final int index, Object... params) {
public <T> void asyncRemote(final BsonConvert convert, Transport[] transports, boolean run, final int index, final Object... params) {
if (!run) return;
this.remote(true, convert, transports, run, index, params);
}
private <T> void remote(final boolean async, final BsonConvert convert, final Transport[] transports, final boolean run, final int index, final Object... params) {
if (!run) return;
if (async) {
submit(() -> {
for (Transport transport : transports) {
convert.convertFrom(actions[index].resultTypes, send(convert, transport, actions[index], params));
}
});
} else {
for (Transport transport : transports) {
convert.convertFrom(actions[index].resultTypes, send(convert, transport, actions[index], params));
}
}
}
private void submit(Runnable runner) {
Thread thread = Thread.currentThread();
if (false && thread instanceof WorkThread) { //有待验证为什么WorkThread 不工作
((WorkThread) thread).submit(runner);
return;
}
Thread t = new Thread(runner);
t.setPriority(Thread.MAX_PRIORITY);
t.start();
}
private byte[] send(final BsonConvert convert, Transport transport, final SncpAction action, Object... params) {
int bodyLength = 2;
Type[] myparamtypes = actions[index].paramTypes;
Type[] myparamtypes = action.paramTypes;
byte[][] bytesarray = new byte[params.length][];
for (int i = 0; i < bytesarray.length; i++) {
bytesarray[i] = convert.convertTo(myparamtypes[i], params[i]);
bodyLength += 4 + bytesarray[i].length;
}
final SncpAction action = actions[index];
final long seqid = System.nanoTime();
final DLong actionid = action.actionid;
final ByteBuffer buffer = transport.pollBuffer();
final AsyncConnection conn = transport.pollConnection();
if (conn == null || !conn.isOpen()) return null;
final ByteBuffer buffer = transport.pollBuffer();
final int readto = conn.getReadTimeoutSecond();
final int writeto = conn.getWriteTimeoutSecond();
try {
@@ -200,12 +245,14 @@ public final class SncpClient {
long ractionid1 = buffer.getLong();
long ractionid2 = buffer.getLong();
if (!actionid.compare(ractionid1, ractionid2)) throw new RuntimeException("sncp send actionid = " + actionid + ", but receive actionid =(" + ractionid1 + "_" + ractionid2 + ")");
buffer.getInt();
buffer.getInt();
final int frameCount = buffer.get();
if (frameCount < 1) throw new RuntimeException("sncp send nameid = " + nameid + ", but frame.count =" + frameCount);
int frameIndex = buffer.get();
if (frameIndex < 0 || frameIndex >= frameCount) throw new RuntimeException("sncp send nameid = " + nameid + ", but frame.count =" + frameCount + " & frame.index =" + frameIndex);
final int retcode = buffer.getInt();
if (retcode != 0) throw new RuntimeException("remote service deal error (receive retcode =" + retcode + ")");
if (retcode != 0) throw new RuntimeException("remote service deal error (retcode=" + retcode + ", retinfo=" + SncpResponse.getRetCodeInfo(retcode) + ")");
final int bodylen = buffer.getInt();
final byte[] body = new byte[bodylen];
if (frameCount == 1) {
@@ -251,4 +298,19 @@ public final class SncpClient {
}
}
private void fillHeader(ByteBuffer buffer, long seqid, DLong actionid, int frameCount, int frameIndex, int bodyLength) {
//---------------------head----------------------------------
buffer.putLong(seqid); //序列号
buffer.putChar((char) HEADER_SIZE); //header长度
buffer.putLong(this.serviceid);
buffer.putLong(this.nameid);
buffer.putLong(actionid.getFirst());
buffer.putLong(actionid.getSecond());
buffer.put(addrBytes);
buffer.putInt(this.addrPort);
buffer.put((byte) frameCount); //数据的帧数, 最小值为1
buffer.put((byte) frameIndex); //数据的帧数序号, 从frame.count-1开始, 0表示最后一帧
buffer.putInt(0); //结果码, 请求方固定传0
buffer.putInt(bodyLength); //body长度
}
}

View File

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

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

View File

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

View File

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

View File

@@ -26,11 +26,25 @@ public final class SncpResponse extends Response<SncpRequest> {
public static final int RETCODE_THROWEXCEPTION = 10011; //内部异常
public static ObjectPool<Response> createPool(AtomicLong creatCounter, AtomicLong cycleCounter, int max, Creator<Response> creator) {
return new ObjectPool<>(creatCounter, cycleCounter, max, creator, (x)-> ((SncpResponse) x).prepare(), (x) -> ((SncpResponse) x).recycle());
return new ObjectPool<>(creatCounter, cycleCounter, max, creator, (x) -> ((SncpResponse) x).prepare(), (x) -> ((SncpResponse) x).recycle());
}
private final byte[] addrBytes;
private final int addrPort;
public static String getRetCodeInfo(int retcode) {
if (retcode == RETCODE_ILLSERVICEID) return "serviceid is invalid";
if (retcode == RETCODE_ILLNAMEID) return "nameid is invalid";
if (retcode == RETCODE_ILLACTIONID) return "actionid is invalid";
if (retcode == RETCODE_THROWEXCEPTION) return "Inner exception";
return null;
}
protected SncpResponse(Context context, SncpRequest request) {
super(context, request);
this.addrBytes = context.getServerAddress().getAddress().getAddress();
this.addrPort = context.getServerAddress().getPort();
}
public void finish(final int retcode, final byte[] bytes) {
@@ -56,7 +70,7 @@ public final class SncpResponse extends Response<SncpRequest> {
pos += len;
buffer.flip();
}
finish(buffers);
finish(buffers);
}
}
@@ -69,6 +83,8 @@ public final class SncpResponse extends Response<SncpRequest> {
DLong actionid = request.getActionid();
buffer.putLong(actionid.getFirst());
buffer.putLong(actionid.getSecond());
buffer.put(addrBytes);
buffer.putInt(this.addrPort);
buffer.put((byte) frameCount); // frame count
buffer.put((byte) frameIndex); //frame index
buffer.putInt(retcode);

View File

@@ -9,6 +9,7 @@ import com.wentch.redkale.convert.bson.*;
import com.wentch.redkale.net.*;
import com.wentch.redkale.util.*;
import com.wentch.redkale.watch.*;
import java.net.*;
import java.nio.*;
import java.util.*;
import java.util.concurrent.atomic.*;
@@ -20,18 +21,45 @@ import java.util.concurrent.atomic.*;
*/
public final class SncpServer extends Server {
private final List<ServiceEntry> services = new ArrayList<>();
protected InetSocketAddress nodeAddress;
public SncpServer(String protocol) {
this(System.currentTimeMillis(), protocol, null);
this(System.currentTimeMillis(), protocol, null, null);
}
public SncpServer(long serverStartTime, String protocol, final WatchFactory watch) {
super(serverStartTime, protocol, watch);
public SncpServer(long serverStartTime, String protocol, InetSocketAddress nodeAddress, final WatchFactory watch) {
super(serverStartTime, protocol, new SncpPrepareServlet(), watch);
this.nodeAddress = nodeAddress;
}
public void addService(ServiceEntry entry) {
this.services.add(entry);
@Override
public void init(AnyValue config) throws Exception {
super.init(config);
if (this.nodeAddress == null) {
if ("0.0.0.0".equals(this.address.getHostString())) {
this.nodeAddress = new InetSocketAddress(Utility.localInetAddress().getHostAddress(), this.address.getPort());
} else {
this.nodeAddress = this.address;
}
}
}
public void addService(ServiceWrapper entry) {
((SncpPrepareServlet) this.prepare).addSncpServlet(new SncpDynServlet(BsonFactory.root().getConvert(), entry.getName(), entry.getService(), entry.getConf()));
}
public List<SncpServlet> getSncpServlets() {
return ((SncpPrepareServlet) this.prepare).getSncpServlets();
}
/**
*
* 对外的IP地址
*
@return
*/
public InetSocketAddress getNodeAddress() {
return nodeAddress;
}
@Override
@@ -47,15 +75,11 @@ public final class SncpServer extends Server {
e.clear();
return true;
});
SncpPrepareServlet prepare = new SncpPrepareServlet();
final BsonConvert convert = BsonFactory.root().getConvert();
this.services.stream().forEach(x -> x.getNames().forEach(y -> prepare.addSncpServlet(new SncpDynServlet(convert, y, x.getService(), x.getServiceConf()))));
this.services.clear();
AtomicLong createResponseCounter = watch == null ? new AtomicLong() : watch.createWatchNumber("SNCP_" + port + ".Response.creatCounter");
AtomicLong cycleResponseCounter = watch == null ? new AtomicLong() : watch.createWatchNumber("SNCP_" + port + ".Response.cycleCounter");
ObjectPool<Response> responsePool = SncpResponse.createPool(createResponseCounter, cycleResponseCounter, this.responsePoolSize, null);
SncpContext sncpcontext = new SncpContext(this.serverStartTime, this.logger, executor, bufferPool, responsePool,
this.maxbody, this.charset, this.address, prepare, this.watch, this.readTimeoutSecond, this.writeTimeoutSecond);
this.maxbody, this.charset, this.address, this.prepare, this.watch, this.readTimeoutSecond, this.writeTimeoutSecond);
responsePool.setCreator((Object... params) -> new SncpResponse(sncpcontext, new SncpRequest(sncpcontext, sncpcontext.bsonFactory)));
return sncpcontext;
}

View File

@@ -8,10 +8,6 @@ package com.wentch.redkale.service;
import com.wentch.redkale.source.*;
import com.wentch.redkale.util.*;
import java.io.*;
import java.util.*;
import java.util.AbstractMap.SimpleEntry;
import java.util.concurrent.*;
import java.util.logging.*;
import javax.annotation.*;
/**
@@ -21,279 +17,25 @@ import javax.annotation.*;
@AutoLoad(false)
public class DataCacheListenerService implements DataCacheListener, Service {
protected final Logger logger = Logger.getLogger(this.getClass().getSimpleName());
private static final String format = "%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS.%tL";
private final ConcurrentHashMap<String, BlockingQueue<Map.Entry<Class, Object[]>>> insertQueues = new ConcurrentHashMap<>();
private final ConcurrentHashMap<String, BlockingQueue<Map.Entry<Class, Object[]>>> updateQueues = new ConcurrentHashMap<>();
private final ConcurrentHashMap<String, BlockingQueue<Map.Entry<Class, Serializable[]>>> deleteQueues = new ConcurrentHashMap<>();
private final boolean finest = logger.isLoggable(Level.FINEST);
@Resource(name = "APP_NODE")
private String localNodeName = "";
@Resource(name = "APP_GROUP")
private String localGroupName = "";
@Resource(name = "APP_GROUP")
private Map<String, Set<String>> groups;
@Resource(name = ".*")
private HashMap<String, DataSource> sourcesmap;
@Resource(name = ".*")
private HashMap<String, DataCacheListenerService> nodesmap;
@Resource(name = "$")
private DataSource source;
@Override
public void init(AnyValue config) {
if (finest) {
logger.finest(this.getClass().getSimpleName() + "-localgroup: " + localGroupName);
logger.finest(this.getClass().getSimpleName() + "-groups: " + groups);
logger.finest(this.getClass().getSimpleName() + "-sources: " + sourcesmap);
}
@MultiRun(async = true)
public <T> void insertCache(Class<T> clazz, T... entitys) {
((DataDefaultSource) source).insertCache(clazz, entitys);
}
@Override
public <T> void insert(String sourceName, Class<T> clazz, T... entitys) {
if (finest) logger.finest("(source:" + sourceName + ") insert " + clazz + " --> " + Arrays.toString(entitys));
BlockingQueue<Map.Entry<Class, Object[]>> queue = this.insertQueues.get(sourceName);
if (queue == null) {
synchronized (this.insertQueues) {
queue = this.insertQueues.get(sourceName);
if (queue == null) {
queue = new ArrayBlockingQueue<>(10240);
this.insertQueues.put(sourceName, queue);
final BlockingQueue<Map.Entry<Class, Object[]>> tq = queue;
new Thread() {
{
setName(DataCacheListener.class.getSimpleName() + "-" + (sourceName.isEmpty() ? "<>" : sourceName) + "-Insert-Thread");
setDaemon(true);
}
@Override
public void run() {
while (true) {
try {
Map.Entry<Class, Object[]> entry = tq.take();
sendInsert(localGroupName, false, sourceName, entry.getKey(), entry.getValue());
} catch (Exception e) {
logger.log(Level.SEVERE, this.getName() + " sendInsert occur error", e);
}
}
}
}.start();
}
}
}
try {
queue.put(new SimpleEntry<>(clazz, entitys));
} catch (Exception e) {
logger.log(Level.WARNING, this.getClass().getSimpleName() + " put insert queue error " + Arrays.toString(entitys), e);
}
}
@RemoteOn
public <T> void sendInsert(String group, boolean ignoreRemote, String sourceName, Class<T> clazz, T... entitys) {
if (nodesmap == null || groups == null) return;
if (ignoreRemote && finest) logger.finest(DataSource.class.getSimpleName() + "(" + group + "--" + this.localNodeName + "," + sourceName + ") onGroupSendInsert " + Arrays.toString(entitys));
for (Map.Entry<String, Set<String>> en : groups.entrySet()) {
if (group != null && group.equals(en.getKey())) { //同机房
for (String onode : en.getValue()) {
if (onode.equals(localNodeName)) continue;
DataCacheListenerService service = nodesmap.get(onode);
if (service != null) {
try {
service.sendInsert(group, false, sourceName, clazz, entitys);
} catch (Exception e) {
logger.log(Level.FINE, this.getClass().getSimpleName() + " send insert error (" + group + "--" + onode + ", " + sourceName + ", " + clazz + ", " + Arrays.toString(entitys) + ")", e);
}
}
}
if (ignoreRemote) break;
} else if (!ignoreRemote) {
for (String onode : en.getValue()) {
DataCacheListenerService service = nodesmap.get(onode);
if (service != null) {
try {
service.sendInsert(group, false, sourceName, clazz, entitys);
break; //有一个成功就退出
} catch (Exception e) {
logger.log(Level.FINE, this.getClass().getSimpleName() + " send insert error (" + group + "--" + onode + ", " + sourceName + ", " + clazz + ", " + Arrays.toString(entitys) + ")", e);
}
}
}
}
}
}
public final <T> void onSendInsert(String group, boolean ignoreRemote, String sourceName, Class<T> clazz, T... entitys) {
if (finest) logger.finest(DataSource.class.getSimpleName() + "(" + this.localNodeName + "," + sourceName + ") onSendInsert " + Arrays.toString(entitys));
((DataDefaultSource) sourcesmap.get(sourceName)).insertCache(entitys);
if (!this.localGroupName.equals(group)) sendInsert(this.localGroupName, true, sourceName, clazz, entitys); //不是同一机房来的资源需要同步到其他同机房的节点上
@MultiRun(async = true)
public <T> void updateCache(Class<T> clazz, T... entitys) {
((DataDefaultSource) source).updateCache(clazz, entitys);
}
@Override
public <T> void update(String sourceName, Class<T> clazz, T... values) {
if (finest) logger.finest("(source:" + sourceName + ") update " + clazz + " --> " + Arrays.toString(values));
BlockingQueue<Map.Entry<Class, Object[]>> queue = this.updateQueues.get(sourceName);
if (queue == null) {
synchronized (this.updateQueues) {
queue = this.updateQueues.get(sourceName);
if (queue == null) {
queue = new ArrayBlockingQueue<>(10240);
this.updateQueues.put(sourceName, queue);
final BlockingQueue<Map.Entry<Class, Object[]>> tq = queue;
new Thread() {
{
setName(DataCacheListener.class.getSimpleName() + "-" + (sourceName.isEmpty() ? "<>" : sourceName) + "-Update-Thread");
setDaemon(true);
}
@Override
public void run() {
while (true) {
try {
Map.Entry<Class, Object[]> entry = tq.take();
sendUpdate(localGroupName, false, sourceName, entry.getKey(), entry.getValue());
} catch (Exception e) {
logger.log(Level.SEVERE, this.getName() + " sendUpdate occur error", e);
}
}
}
}.start();
}
}
}
try {
queue.put(new SimpleEntry<>(clazz, values));
} catch (Exception e) {
logger.log(Level.WARNING, this.getClass().getSimpleName() + " put update queue error " + clazz + "," + Arrays.toString(values), e);
}
@MultiRun(async = true)
public <T> void deleteCache(Class<T> clazz, Serializable... ids) {
((DataDefaultSource) source).deleteCache(clazz, ids);
}
@RemoteOn
public <T> void sendUpdate(String group, boolean ignoreRemote, String sourceName, Class<T> clazz, T... entitys) {
if (nodesmap == null || groups == null) return;
if (ignoreRemote && finest) logger.finest(DataSource.class.getSimpleName() + "(" + group + "--" + this.localNodeName + "," + sourceName + ") onGroupSendUpdate " + Arrays.toString(entitys));
for (Map.Entry<String, Set<String>> en : groups.entrySet()) {
if (group != null && group.equals(en.getKey())) { //同机房
for (String onode : en.getValue()) {
if (onode.equals(localNodeName)) continue;
DataCacheListenerService service = nodesmap.get(onode);
if (service != null) {
try {
service.sendUpdate(group, false, sourceName, clazz, entitys);
} catch (Exception e) {
logger.log(Level.FINE, this.getClass().getSimpleName() + " send update error (" + group + "--" + onode + ", " + sourceName + ", " + clazz + ", " + Arrays.toString(entitys) + ")", e);
}
}
}
if (ignoreRemote) break;
} else if (!ignoreRemote) {
for (String onode : en.getValue()) {
DataCacheListenerService service = nodesmap.get(onode);
if (service != null) {
try {
service.sendUpdate(group, false, sourceName, clazz, entitys);
break; //有一个成功就退出
} catch (Exception e) {
logger.log(Level.FINE, this.getClass().getSimpleName() + " send update error (" + group + "--" + onode + ", " + sourceName + ", " + clazz + ", " + Arrays.toString(entitys) + ")", e);
}
}
}
}
}
}
public final <T> void onSendUpdate(String group, boolean ignoreRemote, String sourceName, Class<T> clazz, T... entitys) {
if (finest) logger.finest(DataSource.class.getSimpleName() + "(" + group + "--" + this.localNodeName + "," + sourceName + ") onSendUpdate " + Arrays.toString(entitys));
((DataDefaultSource) sourcesmap.get(sourceName)).updateCache(clazz, entitys);
if (!this.localGroupName.equals(group)) sendUpdate(this.localGroupName, true, sourceName, clazz, entitys); //不是同一机房来的资源需要同步到其他同机房的节点上
}
@Override
public <T> void delete(String sourceName, Class<T> clazz, Serializable... ids) {
if (finest) logger.finest("(source:" + sourceName + ") delete " + clazz + " --> " + Arrays.toString(ids));
BlockingQueue<Map.Entry<Class, Serializable[]>> queue = this.deleteQueues.get(sourceName);
if (queue == null) {
synchronized (this.deleteQueues) {
queue = this.deleteQueues.get(sourceName);
if (queue == null) {
queue = new ArrayBlockingQueue<>(10240);
this.deleteQueues.put(sourceName, queue);
final BlockingQueue<Map.Entry<Class, Serializable[]>> tq = queue;
new Thread() {
{
setName(DataCacheListener.class.getSimpleName() + "-" + (sourceName.isEmpty() ? "<>" : sourceName) + "-Delete-Thread");
setDaemon(true);
}
@Override
public void run() {
while (true) {
try {
Map.Entry<Class, Serializable[]> entry = tq.take();
sendDelete(localGroupName, false, sourceName, entry.getKey(), entry.getValue());
} catch (Exception e) {
logger.log(Level.SEVERE, this.getName() + " sendDelete occur error", e);
}
}
}
}.start();
}
}
}
try {
queue.put(new SimpleEntry<>(clazz, ids));
} catch (Exception e) {
logger.log(Level.WARNING, this.getClass().getSimpleName() + " put delete queue error " + clazz + "," + Arrays.toString(ids), e);
}
}
@RemoteOn
public <T> void sendDelete(String group, boolean ignoreRemote, String sourceName, Class<T> clazz, Serializable... ids) {
if (nodesmap == null || groups == null) return;
if (ignoreRemote && finest) logger.finest(DataSource.class.getSimpleName() + "(" + group + "--" + this.localNodeName + "," + sourceName + ") onGroupSendDelete " + Arrays.toString(ids));
for (Map.Entry<String, Set<String>> en : groups.entrySet()) {
if (group != null && group.equals(en.getKey())) { //同机房
for (String onode : en.getValue()) {
if (onode.equals(localNodeName)) continue;
DataCacheListenerService service = nodesmap.get(onode);
if (service != null) {
try {
service.sendDelete(group, false, sourceName, clazz, ids);
} catch (Exception e) {
logger.log(Level.FINE, this.getClass().getSimpleName() + " send delete error (" + group + "--" + onode + ", " + sourceName + ", " + clazz + ", " + Arrays.toString(ids) + ")", e);
}
}
}
if (ignoreRemote) break;
} else if (!ignoreRemote) {
for (String onode : en.getValue()) {
DataCacheListenerService service = nodesmap.get(onode);
if (service != null) {
try {
service.sendDelete(group, false, sourceName, clazz, ids);
break; //有一个成功就退出
} catch (Exception e) {
logger.log(Level.FINE, this.getClass().getSimpleName() + " send delete error (" + group + "--" + onode + ", " + sourceName + ", " + clazz + ", " + Arrays.toString(ids) + ")", e);
}
}
}
}
}
}
public final <T> void onSendDelete(String group, boolean ignoreRemote, String sourceName, Class<T> clazz, Serializable... ids) {
if (finest) logger.finest(DataSource.class.getSimpleName() + "(" + group + "--" + this.localNodeName + "," + sourceName + ") onSendDelete " + clazz.getName() + " " + Arrays.toString(ids));
((DataDefaultSource) sourcesmap.get(sourceName)).deleteCache(clazz, ids);
if (!this.localGroupName.equals(group)) sendDelete(this.localGroupName, true, sourceName, clazz, ids); //不是同一机房来的资源需要同步到其他同机房的节点上
}
}

View File

@@ -17,162 +17,102 @@ import java.util.logging.*;
import javax.annotation.Resource;
/**
* 暂时不实现
*
* @author zhangjx
*/
@AutoLoad(false)
public class DataSQLListenerService implements DataSQLListener, Service {
protected final Logger logger = Logger.getLogger(this.getClass().getSimpleName());
private static final String format = "%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS.%tL";
private boolean finest;
protected final Logger logger = Logger.getLogger(this.getClass().getSimpleName());
@Resource(name = "APP_NODE")
private String localNodeName = "";
private String localIDCName = "";
private final boolean finest = logger.isLoggable(Level.FINEST);
@Resource(name = "APP_HOME")
private File home;
private File root;
@Resource(name = "$")
private DataSource source;
@Resource(name = ".*")
HashMap<String, DataSource> sourcemaps;
private final BlockingQueue<String> queue = new ArrayBlockingQueue<>(1024 * 1024);
private ConcurrentHashMap<String, BlockingQueue<String>> queues = new ConcurrentHashMap<>();
@Resource
private HashMap<String, DataSQLListenerService> nodemaps;
private final HashSet<String> allidcs = new HashSet<>();
private ConcurrentHashMap<String, PrintStream> syncfiles = new ConcurrentHashMap<>();
private PrintStream syncfile;
@Override
public void init(AnyValue config) {
finest = logger.isLoggable(Level.FINEST);
//nodename的前两位字符表示机房ID
if (localNodeName.length() > 2) localIDCName = getIDC(localNodeName);
if (finest) logger.fine("LocalNodeName: " + localNodeName + ", " + localIDCName + " " + this.nodemaps);
if (this.nodemaps == null) return;
this.nodemaps.forEach((x, y) -> allidcs.add(x.substring(0, 2)));
new Thread() {
{
setName(DataSQLListener.class.getSimpleName() + "-Thread");
setDaemon(true);
}
@Override
public void run() {
while (true) {
try {
String sql = queue.take();
send(sql);
} catch (Exception e) {
logger.log(Level.SEVERE, this.getName() + " occur error");
}
}
}
}.start();
}
@Override
public void destroy(AnyValue config) {
this.syncfiles.forEach((x, y) -> {
y.close();
});
if (syncfile != null) syncfile.close();
}
private void write(String node, String sourceName, String... sqls) {
if (sourceName == null || sourceName.isEmpty()) sourceName = "<>";
String key = node + "-" + sourceName;
PrintStream channel = syncfiles.get(key);
private void write(String... sqls) {
try {
if (channel == null) {
if (this.root == null) {
this.root = new File(home, "dbsync");
this.root.mkdirs();
}
channel = new PrintStream(new FileOutputStream(new File(this.root, key + ".sql"), true), false, "UTF-8");
syncfiles.put(key, channel);
if (syncfile == null) {
File root = new File(home, "dbsync");
root.mkdirs();
syncfile = new PrintStream(new FileOutputStream(new File(root, "sql-" + name() + ".sql"), true), false, "UTF-8");
}
for (String sql : sqls) {
channel.print(sql + ";\r\n");
syncfile.print(sql + ";\r\n");
}
channel.flush();
syncfile.flush();
} catch (Exception e) {
logger.log(Level.WARNING, "write sql file error. (" + node + ", " + sourceName + ", " + Arrays.toString(sqls) + ")", e);
logger.log(Level.WARNING, "write sql file error. (" + name() + ", " + Arrays.toString(sqls) + ")", e);
}
}
@Override
public void insert(String sourceName, String... sqls) {
put(sourceName, sqls);
public void insert(String... sqls) {
put(sqls);
}
@Override
public void update(String sourceName, String... sqls) {
put(sourceName, sqls);
public void update(String... sqls) {
put(sqls);
}
@Override
public void delete(String sourceName, String... sqls) {
put(sourceName, sqls);
public void delete(String... sqls) {
put(sqls);
}
private void put(final String sourceName, String... sqls) {
private void put(String... sqls) {
String date = String.format(format, System.currentTimeMillis());
BlockingQueue<String> queue = this.queues.get(sourceName);
if (queue == null) {
synchronized (this) {
queue = this.queues.get(sourceName);
if (queue == null) {
queue = new ArrayBlockingQueue<>(1024 * 1024);
this.queues.put(sourceName, queue);
final BlockingQueue<String> tq = queue;
new Thread() {
{
setName(DataSQLListener.class.getSimpleName() + "-" + (sourceName.isEmpty() ? "<>" : sourceName) + "-Thread");
setDaemon(true);
}
@Override
public void run() {
while (true) {
try {
String sql = tq.take();
send(sourceName, sql);
} catch (Exception e) {
logger.log(Level.SEVERE, this.getName() + " occur error");
}
}
}
}.start();
}
}
}
try {
for (String sql : sqls) {
queue.put("/* " + date + " */ " + sql);
}
} catch (Exception e) {
logger.log(Level.WARNING, this.getClass().getSimpleName() + " put queue error" + Arrays.toString(sqls), e);
}
}
private String getIDC(String nodeName) {
return nodeName.substring(0, 2);
}
@RemoteOn
public void send(String sourceName, String... sqls) {
if (this.nodemaps == null) return;
final Set<String> idcs = new HashSet<>();
idcs.add(localIDCName);
nodemaps.forEach((x, y) -> {
for (String sql : sqls) {
try {
String idc = getIDC(x);
if (!idcs.contains(idc)) {
y.send(sourceName, sqls);
idcs.add(idc);
}
queue.put("/* " + date + " */ " + sql);
} catch (Exception e) {
logger.log(Level.FINE, this.getClass().getSimpleName() + " send error (" + x + ", " + sourceName + ", " + Arrays.toString(sqls) + ")", e);
write(sql);
}
});
allidcs.forEach(x -> {
if (!idcs.contains(x)) write(x, sourceName, sqls);
});
}
}
public final void onSend(String sourceName, String... sqls) {
((DataDefaultSource) sourcemaps.get(sourceName)).execute(sqls);
@MultiRun
public void send(String... sqls) {
((DataDefaultSource) source).execute(sqls);
}
}

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

View File

@@ -8,265 +8,66 @@ package com.wentch.redkale.service;
import com.wentch.redkale.net.http.*;
import com.wentch.redkale.util.*;
import java.io.*;
import java.net.*;
import java.util.*;
import java.util.concurrent.*;
import java.util.logging.*;
import javax.annotation.*;
/**
*
* @author zhangjx
*/
@AutoLoad(false)
public class WebSocketNodeService implements Service {
public class WebSocketNodeService extends WebSocketNode implements Service {
public static final int RETCODE_ENGINE_NULL = 5001;
public static final int RETCODE_NODESERVICE_NULL = 5002;
public static final int RETCODE_GROUP_EMPTY = 5005;
public static final int RETCODE_WSOFFLINE = 5011;
protected final Logger logger = Logger.getLogger(this.getClass().getSimpleName());
protected final boolean finest = logger.isLoggable(Level.FINEST);
@Resource(name = "APP_NODE")
protected String localNodeName = "";
@Resource
protected HashMap<String, WebSocketNodeService> nodemaps;
//用户分布在节点上的队列信息,只保存远程节点的用户分布信息
protected final ConcurrentHashMap<Serializable, Set<String>> usernodes = new ConcurrentHashMap();
protected final ConcurrentHashMap<String, WebSocketEngine> engines = new ConcurrentHashMap();
public void initUserNodes() {
if (this.nodemaps == null || this.nodemaps.isEmpty()) return;
new Thread() {
{
setDaemon(true);
}
@Override
public void run() {
usernodes.putAll(queryNodes());
}
}.start();
@Override
public void init(AnyValue conf) {
super.init(conf);
}
public final void addWebSocketEngine(WebSocketEngine engine) {
engines.put(engine.getEngineid(), engine);
@Override
public void destroy(AnyValue conf) {
super.destroy(conf);
}
@RemoteOn
public Map<Serializable, Set<String>> queryNodes() {
Map<Serializable, Set<String>> rs = new HashMap<>();
this.nodemaps.forEach((x, y) -> {
if (!rs.isEmpty()) return;
try {
rs.putAll(y.queryNodes());
} catch (Exception e) {
logger.log(Level.WARNING, this.getClass().getSimpleName() + " query error (" + x + ")", e);
}
});
return rs;
}
public final Map<Serializable, Set<String>> onQueryNodes() {
Map<Serializable, Set<String>> rs = new HashMap<>();
rs.putAll(this.usernodes);
return rs;
}
public void connectSelf(Serializable userid) {
connect(this.localNodeName, userid);
}
public void disconnectSelf(Serializable userid) {
disconnect(this.localNodeName, userid);
}
@RemoteOn
public void connect(String nodeid, Serializable userid) {
onConnect(nodeid, userid);
if (this.nodemaps == null) return;
this.nodemaps.forEach((x, y) -> {
try {
if (finest) logger.finest("Node(" + localNodeName + "->" + x + ") send websocket connect event (" + userid + " on " + nodeid + ")");
y.connect(nodeid, userid);
} catch (Exception e) {
logger.log(Level.WARNING, "Node(" + localNodeName + "->" + x + ") send websocket connect event (" + userid + " on " + nodeid + ")", e);
}
});
}
public final void onConnect(String nodeid, Serializable userid) {
if (finest) logger.finest("Node (" + localNodeName + ") receive websocket connect event (" + userid + " on " + nodeid + ").");
Set<String> userNodelist = usernodes.get(userid);
if (userNodelist == null) {
userNodelist = new CopyOnWriteArraySet<>();
usernodes.put(userid, userNodelist);
}
userNodelist.add(nodeid);
}
@RemoteOn
public void disconnect(String nodeid, Serializable userid) {
onDisconnect(nodeid, userid);
if (this.nodemaps == null) return;
this.nodemaps.forEach((x, y) -> {
try {
if (finest) logger.finest("Node(" + localNodeName + "->" + x + ") send websocket disconnect event (" + userid + " on " + nodeid + ")");
y.disconnect(nodeid, userid);
} catch (Exception e) {
logger.log(Level.WARNING, "Node(" + localNodeName + "->" + x + ") send websocket disconnect event (" + userid + " on " + nodeid + ")", e);
}
});
}
public final void onDisconnect(String nodeid, Serializable userid) {
if (finest) logger.finest("Node (" + localNodeName + ") receive websocket disconnect event (" + userid + " on " + nodeid + ").");
Set<String> userNodelist = usernodes.get(userid);
if (userNodelist == null) return;
userNodelist.remove(nodeid);
if (userNodelist.isEmpty()) usernodes.remove(userid);
}
@RemoteOn
public int send(String engineid, Serializable groupid, String text) {
return send(engineid, groupid, text, true);
}
public final int onSend(String engineid, Serializable groupid, String text) {
return onSend(engineid, groupid, text, true);
}
@RemoteOn
public int send(String engineid, Serializable groupid, String text, boolean last) {
return send0(engineid, groupid, false, text, last);
}
public final int onSend(String engineid, Serializable groupid, String text, boolean last) {
return onSend0(engineid, groupid, false, text, last);
}
@RemoteOn
public int send(String engineid, Serializable groupid, boolean recent, String text) {
return send0(engineid, groupid, recent, text, true);
}
public final int onSend(String engineid, Serializable groupid, boolean recent, String text) {
return onSend0(engineid, groupid, recent, text, true);
}
@RemoteOn
public int send(String engineid, Serializable groupid, boolean recent, String text, boolean last) {
return send0(engineid, groupid, recent, text, last);
}
public final int onSend(String engineid, Serializable groupid, boolean recent, String text, boolean last) {
return onSend0(engineid, groupid, recent, text, last);
}
@RemoteOn
public int send(String engineid, Serializable groupid, byte[] data) {
return send(engineid, groupid, data, true);
}
public final int onSend(String engineid, Serializable groupid, byte[] data) {
return onSend(engineid, groupid, data, true);
}
@RemoteOn
public int send(String engineid, Serializable groupid, byte[] data, boolean last) {
return send0(engineid, groupid, false, data, last);
}
public final int onSend(String engineid, Serializable groupid, byte[] data, boolean last) {
return onSend0(engineid, groupid, false, data, last);
}
@RemoteOn
public int send(String engineid, Serializable groupid, boolean recent, byte[] data) {
return send0(engineid, groupid, recent, data, true);
}
public final int onSend(String engineid, Serializable groupid, boolean recent, byte[] data) {
return onSend0(engineid, groupid, recent, data, true);
}
@RemoteOn
public int send(String engineid, Serializable groupid, boolean recent, byte[] data, boolean last) {
return send0(engineid, groupid, recent, data, last);
}
public final int onSend(String engineid, Serializable groupid, boolean recent, byte[] data, boolean last) {
return onSend0(engineid, groupid, recent, data, last);
}
private int send0(String engineid, Serializable groupid, boolean recent, Serializable text, boolean last) {
final Set<String> nodes = usernodes.get(groupid);
if (nodes == null) return RETCODE_WSOFFLINE; //未登录
int rs = 0;
if (nodes.contains(this.localNodeName)) rs = onSend0(engineid, groupid, recent, text, last);
if (nodemaps == null) return rs;
this.nodemaps.forEach((x, y) -> {
if (nodes.contains(x)) {
int irs = -1;
try {
if (text != null && text.getClass() == byte[].class) {
irs = y.send(engineid, groupid, (byte[]) text, last);
} else {
irs = y.send(engineid, groupid, (String) text, last);
}
if (finest) logger.finest("Node(" + localNodeName + "->" + x + ") send websocket message {engineid:'" + engineid + "', groupid:" + groupid + ", content:'" + text + "'} finish and result is " + irs);
} catch (Exception e) {
onDisconnect(x, groupid);
logger.log(Level.WARNING, "Node(" + localNodeName + "->" + x + ") send websocket message {engineid:'" + engineid + "', groupid:" + groupid + ", content:'" + text + "'} failed and result is " + irs, e);
@Override
public int sendMessage(Serializable groupid, boolean recent, Serializable message, boolean last) {
final Set<String> engineids = localNodes.get(groupid);
if (engineids == null || engineids.isEmpty()) return RETCODE_GROUP_EMPTY;
for (String engineid : engineids) {
final WebSocketEngine engine = engines.get(engineid);
if (engine != null) { //在本地
final WebSocketGroup group = engine.getWebSocketGroup(groupid);
if (group == null || group.isEmpty()) {
if (finest) logger.finest("receive websocket message {engineid:'" + engineid + "', groupid:" + groupid + ", content:'" + message + "'} but result is " + RETCODE_GROUP_EMPTY);
return RETCODE_GROUP_EMPTY;
}
}
});
return rs;
}
/**
* 消息接受者存在WebSocket并发送成功返回true 否则返回false
*
* @param engineid
* @param groupid 接收方
* @param recent 是否只发送最近的WebSocket端
* @param text
* @return
*/
private int onSend0(String engineid, Serializable groupid, boolean recent, Serializable text, boolean last) {
WebSocketEngine webSocketEngine = engines.get(engineid);
if (webSocketEngine == null) {
if (finest) logger.finest("Node(" + localNodeName + ") receive websocket message {engineid:'" + engineid + "', groupid:" + groupid + ", content:'" + text + "'} but result is " + RETCODE_ENGINE_NULL);
return RETCODE_ENGINE_NULL;
}
WebSocketGroup group = webSocketEngine.getWebSocketGroup(groupid);
if (group == null || group.isEmpty()) {
if (finest) logger.finest("Node(" + localNodeName + ") receive websocket message {engineid:'" + engineid + "', groupid:" + groupid + ", content:'" + text + "'} but result is " + RETCODE_GROUP_EMPTY);
return RETCODE_GROUP_EMPTY;
}
if (finest) logger.finest("Node (" + localNodeName + ") receive websocket message {engineid:'" + engineid + "', groupid:" + groupid + ", content:'" + text + "'}.");
if (text != null && text.getClass() == byte[].class) {
if (recent) {
group.getRecentWebSocket().send((byte[]) text, last);
} else {
group.getWebSockets().forEach(x -> x.send((byte[]) text, last));
}
} else {
if (recent) {
group.getRecentWebSocket().send(text.toString(), last);
} else {
group.getWebSockets().forEach(x -> x.send(text.toString(), last));
group.send(recent, message, last);
} else { //对方连接在远程节点
return RETCODE_WSOFFLINE;
}
}
return 0;
}
@Override
@MultiRun
public void connect(Serializable groupid, InetSocketAddress addr) {
Set<InetSocketAddress> addrs = dataNodes.get(groupid);
if (addrs == null) {
addrs = new CopyOnWriteArraySet<>();
dataNodes.put(groupid, addrs);
}
addrs.add(addr);
if(finest) logger.finest(WebSocketNodeService.class.getSimpleName() + ".event: " + groupid +" connect from " + addr);
}
@Override
@MultiRun
public void disconnect(Serializable groupid, InetSocketAddress addr) {
Set<InetSocketAddress> addrs = dataNodes.get(groupid);
if (addrs == null) return;
addrs.remove(addr);
if (addrs.isEmpty()) dataNodes.remove(groupid);
if(finest) logger.finest(WebSocketNodeService.class.getSimpleName() + ".event: " + groupid +" disconnect from " + addr);
}
}

View File

@@ -13,9 +13,9 @@ import java.io.Serializable;
*/
public interface DataCacheListener {
public <T> void insert(String sourceName, Class<T> clazz, T... entitys);
public <T> void insertCache(Class<T> clazz, T... entitys);
public <T> void update(String sourceName, Class<T> clazz, T... entitys);
public <T> void updateCache(Class<T> clazz, T... entitys);
public <T> void delete(String sourceName, Class<T> clazz, Serializable... ids);
public <T> void deleteCache(Class<T> clazz, Serializable... ids);
}

View File

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

View File

@@ -12,9 +12,9 @@ package com.wentch.redkale.source;
*/
public interface DataSQLListener {
public void insert(String sourceName, String... sqls);
public void insert(String... sqls);
public void update(String sourceName, String... sqls);
public void update(String... sqls);
public void delete(String sourceName, String... sqls);
public void delete(String... sqls);
}

View File

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

View File

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

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

View File

@@ -32,11 +32,6 @@ public final class DLong extends Number implements Comparable<DLong> {
return this.first == one && this.second == two;
}
@Override
public int hashCode() {
return intValue();
}
@Override
public boolean equals(Object obj) {
if (obj == null) return false;
@@ -45,6 +40,14 @@ public final class DLong extends Number implements Comparable<DLong> {
return (this.first == other.first && this.second == other.second);
}
@Override
public int hashCode() {
int hash = 7;
hash = 89 * hash + (int) (this.first ^ (this.first >>> 32));
hash = 89 * hash + (int) (this.second ^ (this.second >>> 32));
return hash;
}
@Override
public String toString() {
return this.first + "_" + this.second;

View File

@@ -5,6 +5,7 @@
*/
package com.wentch.redkale.util;
import java.util.*;
import jdk.internal.org.objectweb.asm.*;
/**
@@ -21,6 +22,8 @@ public class DebugMethodVisitor {
debug = d;
}
private final Map<Label, Integer> labels = new LinkedHashMap<>();
private static final String[] opcodes = new String[200]; //0 -18
static {
@@ -46,6 +49,12 @@ public class DebugMethodVisitor {
this.visitor = visitor;
}
public AnnotationVisitor visitAnnotation(String desc, boolean flag) {
AnnotationVisitor av = visitor.visitAnnotation(desc, flag);
if (debug) System.out.println("mv.visitAnnotation(\"" + desc + "\", " + flag + ");");
return av;
}
public void visitParameter(String name, int access) {
visitor.visitParameter(name, access);
if (debug) System.out.println("mv.visitParameter(" + name + ", " + access + ");");
@@ -56,6 +65,37 @@ public class DebugMethodVisitor {
if (debug) System.out.println("mv.visitVarInsn(" + opcodes[opcode] + ", " + var + ");");
}
public void visitJumpInsn(int opcode, Label var) {
visitor.visitJumpInsn(opcode, var);
if (debug) {
Integer index = labels.get(var);
if (index == null) {
index = labels.size();
labels.put(var, index);
System.out.println("Label l" + index + " = new Label();");
}
System.out.println("mv.visitJumpInsn(" + opcodes[opcode] + ", l" + index + ");");
}
}
public void visitCode() {
visitor.visitCode();
if (debug) System.out.println("mv.visitCode();");
}
public void visitLabel(Label var) {
visitor.visitLabel(var);
if (debug) {
Integer index = labels.get(var);
if (index == null) {
index = labels.size();
labels.put(var, index);
System.out.println("Label l" + index + " = new Label();");
}
System.out.println("mv.visitLabel(l" + index + ");");
}
}
public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) {
visitor.visitMethodInsn(opcode, owner, name, desc, itf);
if (debug) System.out.println("mv.visitMethodInsn(" + opcodes[opcode] + ", \"" + owner + "\", \"" + name + "\", \"" + desc + "\", " + itf + ");");
@@ -81,6 +121,11 @@ public class DebugMethodVisitor {
if (debug) System.out.println("mv.visitIntInsn(" + opcodes[opcode] + ", " + value + ");");
}
public void visitIincInsn(int opcode, int value) {
visitor.visitIincInsn(opcode, value);
if (debug) System.out.println("mv.visitIincInsn(" + opcode + ", " + value + ");");
}
public void visitLdcInsn(Object o) {
visitor.visitLdcInsn(o);
if (debug) System.out.println("mv.visitLdcInsn(" + o + ");");

View File

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

View File

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

View File

@@ -13,12 +13,15 @@ import java.util.regex.*;
import javax.annotation.*;
/**
* 如果Resource(name = "$") 表示资源name采用所属对象的name
*
* @author zhangjx
*/
@SuppressWarnings("unchecked")
public final class ResourceFactory {
public static final String RESOURCE_PARENT_NAME = "$";
private static final Logger logger = Logger.getLogger(ResourceFactory.class.getSimpleName());
private final ResourceFactory parent;
@@ -170,15 +173,16 @@ public final class ResourceFactory {
continue;
}
if (Modifier.isFinal(field.getModifiers())) continue;
Object rs = genctype == classtype ? null : find(rc.name(), genctype);
final String rcname = (rc.name().equals(RESOURCE_PARENT_NAME) && src instanceof Nameable) ? ((Nameable) src).name() : rc.name();
Object rs = genctype == classtype ? null : find(rcname, genctype);
if (rs == null) {
if (Map.class.isAssignableFrom(classtype)) {
rs = find(Pattern.compile(rc.name().isEmpty() ? ".+" : rc.name()), (Class) ((ParameterizedType) field.getGenericType()).getActualTypeArguments()[1], src);
rs = find(Pattern.compile(rcname.isEmpty() ? ".*" : rcname), (Class) ((ParameterizedType) field.getGenericType()).getActualTypeArguments()[1], src);
} else {
if (rc.name().startsWith("property.")) {
rs = find(rc.name(), String.class);
if (rcname.startsWith("property.")) {
rs = find(rcname, String.class);
} else {
rs = find(rc.name(), classtype);
rs = find(rcname, classtype);
}
}
}

View File

@@ -324,6 +324,17 @@ public final class Utility {
return size;
}
/**
* 将两个数字组装成一个long
* <p>
* @param high
* @param low
* @return
*/
public static long merge(long high, long low) {
return high << 32 | low;
}
public static ByteBuffer encodeUTF8(final ByteBuffer buffer, final char[] text, final int start, final int len) {
return encodeUTF8(buffer, encodeUTF8Length(text, start, len), text, start, len);
}