This commit is contained in:
地平线
2015-03-11 17:49:20 +08:00
parent be89d407ac
commit 3a2f802500
169 changed files with 22269 additions and 0 deletions

7
bin/shutdown.bat Normal file
View File

@@ -0,0 +1,7 @@
@ECHO OFF
SET APP_HOME=%~dp0
IF NOT EXIST "%APP_HOME%\conf\application.xml" SET APP_HOME=%~dp0..
java -DSHUTDOWN=true -DAPP_HOME=%APP_HOME% -classpath %APP_HOME%\lib\* com.wentch.redkale.boot.Application

16
bin/shutdown.sh Normal file
View File

@@ -0,0 +1,16 @@
#!/bin/sh
APP_HOME=`dirname "$0"`
if [ ! -a "$APP_HOME"/conf/application.xml ]; then
APP_HOME="$APP_HOME"/..
fi
lib='.'
for jar in `ls $APP_HOME/lib/*.jar`
do
lib=$lib:$jar
done
export CLASSPATH=$CLASSPATH:$lib
echo "$APP_HOME"
java -DSHUTDOWN=true -DAPP_HOME="$APP_HOME" com.wentch.redkale.boot.Application

7
bin/start.bat Normal file
View File

@@ -0,0 +1,7 @@
@ECHO OFF
SET APP_HOME=%~dp0
IF NOT EXIST "%APP_HOME%\conf\application.xml" SET APP_HOME=%~dp0..
java -DAPP_HOME=%APP_HOME% -classpath %APP_HOME%\lib\* com.wentch.redkale.boot.Application

22
bin/start.sh Normal file
View File

@@ -0,0 +1,22 @@
#!/bin/sh
ulimit -c unlimited
ulimit -n 1024000
APP_HOME=`dirname "$0"`
if [ ! -a "$APP_HOME"/conf/application.xml ]; then
APP_HOME="$APP_HOME"/..
fi
lib="$APP_HOME"/lib
for jar in `ls $APP_HOME/lib/*.jar`
do
lib=$lib:$jar
done
export CLASSPATH=$CLASSPATH:$lib
echo "$APP_HOME"
nohup java -DAPP_HOME="$APP_HOME" com.wentch.redkale.boot.Application > "$APP_HOME"/log.out &

10
conf/application.xml Normal file
View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<application port="6560">
<resources>
</resources>
<server protocol="HTTP" host="0.0.0.0" port="6060" root="root">
<services autoload="true"/>
<servlets prefix="/pipes" autoload="true" />
</server>
</application>

21
conf/logging.properties Normal file
View File

@@ -0,0 +1,21 @@
handlers = java.util.logging.ConsoleHandler
############################################################
.level = FINE
org.level = INFO
javax.level = INFO
com.sun.level = INFO
java.util.logging.FileHandler.level = FINE
#100M
java.util.logging.FileHandler.limit = 52428800
java.util.logging.FileHandler.count = 10000
java.util.logging.FileHandler.encoding = UTF-8
java.util.logging.FileHandler.pattern = log.log
java.util.logging.FileHandler.append = true
java.util.logging.ConsoleHandler.level = FINE

15
conf/persistence.xml Normal file
View File

@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
<persistence-unit name="" transaction-type="RESOURCE_LOCAL">
<provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
<exclude-unlisted-classes>false</exclude-unlisted-classes>
<shared-cache-mode>ALL</shared-cache-mode>
<validation-mode>NONE</validation-mode>
<properties>
<property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/center?autoReconnect=true&amp;characterEncoding=utf8"/>
<property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver"/>
<property name="javax.persistence.jdbc.user" value="root"/>
<property name="javax.persistence.jdbc.password" value="1234"/>
</properties>
</persistence-unit>
</persistence>

Binary file not shown.

View File

@@ -0,0 +1,144 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
${APP_HOME} 指当前程序的总目录APP_HOME
node: 进程节点的名称, 默认为空
required 被声明required的属性值不能为空
port: required 程序的管理Server的端口用于关闭或者与监管系统进行数据交互
host: 程序的管理Server的地址; 默认为127.0.0.1。
lib: 加上额外的lib路径,多个路径用分号;隔开; 默认为空。 例如: ${APP_HOME}/lib/a.jar;${APP_HOME}/lib2/b.jar;
-->
<application port="6560" lib="">
<!-- 所有服务所需的资源 -->
<resources>
<!--
远程client地址组资源. 注意: remote的name值不能为LOCAL不区分大小写
protocol 值只能是UDP TCP 默认UDP
-->
<remote name="mygroup" protocol="UDP">
<!--
weight: 权重百分比。 不指定则平均。 weight之和必须<=100
[注: weight尚未实现]
-->
<address addr="127.0.0.1" port="7070" weight="30"/>
<address addr="127.0.0.1" port="7071" weight="30"/>
<address addr="127.0.0.1" port="7072" weight="40"/>
</remote>
<!--
全局的参数配置, 可以通过@Resource(name="property.xxxxxx") 进行注入, 被注解的字段类型只能是String、primitive class
如果name是system.property.开头的值将会在进程启动时进行System.setProperty("yyyy", "YYYYYY")操作。
load: 加载文件,多个用;隔开。
默认置入的system.property.的有:
System.setProperty("convert.bson.pool.size", "128");
System.setProperty("convert.json.pool.size", "128");
System.setProperty("convert.bson.writer.buffer.defsize", "4096");
System.setProperty("convert.json.writer.buffer.defsize", "4096");
-->
<properties load="config.properties">
<property name="system.property.yyyy" value="YYYYYY"/>
<property name="xxxxxx" value="XXXXXXXX"/>
<property name="xxxxxx" value="XXXXXXXX"/>
<property name="xxxxxx" value="XXXXXXXX"/>
</properties>
</resources>
<!--
protocol: required server所启动的协议有HTTP、HTTPS、SNCP 目前只支持HTTP、SNCP;
host: 服务所占address 默认: 0.0.0.0
port: 服务所占端口 ,默认: 80
root: 如果是web类型服务则包含页面 默认:{APP_HOME}/root
lib: server额外的class目录 默认为空
charset: 文本编码, 默认: UTF-8
backlog: 默认10K
threads 线程总数, 默认: CPU核数*16
maxbody: request.body最大值 默认: 64K
capacity: ByteBuffer的初始化大小 默认: 8K
bufferPoolSize ByteBuffer池的大小默认: CPU核数*512
responsePoolSize Response池的大小默认: CPU核数*256
readTimeoutSecond: 读操作超时秒数, 默认0 表示永久不超时
writeTimeoutSecond: 写操作超时秒数, 默认0 表示永久不超时
forwardproxy: 正向代理(支持CONNECT); 默认: false 只有当protocol=HTTP/HTTPS才生效
sslkeypath: 当protocol==HTTPS时需要指定keypath路径 默认值:{classpath}/ssl-keystore.jks [注: 尚未实现]
sslkeypwd: 当protocol==HTTPS时需要指定keypassword路径 [注: 尚未实现]
-->
<server protocol="HTTP" host="127.0.0.1" port="6060" root="root" lib="" sslkeypath="" sslkeypwd="">
<!--
加载所有的Service服务;
在同一个进程中所有LOCAL模式的Service 同一个name的将共用同一个实例
autoload="true" 默认值. 自动加载以下目录如果存在的话下所有的Service类:
server.lib; server.root/lib/*; server.root/classes;
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="mygroup">
<!--
大部分的情况下, 存在多个节点环境中很多service节点配置都一致为此提供group节点来方便配置。
remotenames: 远程模式Service的name名称集合 多个用分号;隔开, 名称必须是在resources节点中定义的remote节点。
以下group节点例子等价于:
<service value="com.xxx.XXX0Service" name="LC001" 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"/>
-->
<group remotenames="RT001;RT002;RT003">
<service value="com.xxx.XXX0Service"/>
</group>
<!-- 显著加载指定的Service的接口类 -->
<service value="com.xxx.XXX1Service"/>
<!--
设定remote=LOCAL时 则表示该service为本地模式 同时忽略<services>节点的remote值。
name表示被@Resource注入时指定的name
设定remote为非空非LOCAL且值在<resources>中有定义时 则表示该service为远程模式 同时忽略<services>节点的remote值。
-->
<service value="com.xxx.XXX2Service" name="" remote="LOCAL"/>
</services>
<!--
加载所有的Servlet服务;
prefix: servlet的ContextPath前缀 默认为空
autoload="true" 默认值. 自动加载以下目录如果存在的话下所有的Servlet类:
${APP_HOME}/lib; ${APP_HOME}/root/lib/*; ${APP_HOME}/root/classes;
autoload="false" 需要显著的指定Service类
includes 当autoload="true" 拉取类名与includes中的正则表达式匹配的类, 多个正则表达式用分号;隔开
excludes 当autoload="true" 排除类名与includes中的正则表达式匹配的类, 多个正则表达式用分号;隔开
-->
<servlets prefix="/pipes" autoload="true" includes="" excludes="">
<!--
当Server为HTTP、HTTP协议时ResourceServlet才有效. 默认存在一个有默认属性的resource-servlet节点
webroot: web资源的根目录 默认取server节点中的root值
-->
<resource-servlet webroot="root">
<!--
资源缓存的配置, 默认存在一个含默认属性的caches节点
limit: 资源缓存最大容量, 默认: 128M, 为0表示不缓存
lengthmax: 可缓存的文件大小上限, 默认: 1M超过1M的文件不会被缓存
-->
<caches limit="128M" lengthmax="1M" />
<!--
支持类似nginx中的rewrite 目前只支持静态资源对静态资源的跳转。
type: 匹配的类型, 目前只支持location(匹配requestURI), 默认: location
match: 匹配的正则表达式
forward: 需跳转后的资源链接
例如下面例子是将/xxx-yyy.html的页面全部跳转到/xxx.html
-->
<rewrite type="location" match="^/([^-]+)-[^-\.]+\.html(.*)" forward="/$1.html"/>
</resource-servlet>
<!-- 显著加载指定的Servlet -->
<servlet value="com.xxx.XXX1Servlet" />
<servlet value="com.xxx.XXX2Servlet" />
<servlet value="com.xxx.XXX3Servlet" />
</servlets>
</server>
<server protocol="SNCP" host="127.0.0.1" port="7070" root="root" lib="">
<!--
参数完全同上
-->
<services autoload="true" includes="" excludes="" />
</server>
</application>

View File

@@ -0,0 +1,16 @@
handlers = java.util.logging.ConsoleHandler,java.util.logging.FileHandler
.handlers = java.util.logging.ConsoleHandler,java.util.logging.FileHandler
############################################################
.level = INFO
#java.util.logging.FileHandler.level = INFO
#100M
java.util.logging.FileHandler.limit = 104857600
java.util.logging.FileHandler.count = 10
java.util.logging.FileHandler.encoding = UTF-8
java.util.logging.FileHandler.pattern = ./logs/log-%u.log
java.util.logging.FileHandler.append = true
#java.util.logging.ConsoleHandler.level = INFO

View File

@@ -0,0 +1,512 @@
/*
* 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.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.*;
import com.wentch.redkale.net.http.*;
import com.wentch.redkale.net.sncp.*;
import com.wentch.redkale.service.*;
import com.wentch.redkale.source.*;
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.concurrent.*;
import java.util.logging.*;
import javax.annotation.*;
import javax.xml.parsers.*;
import org.w3c.dom.*;
/**
* 编译时需要加入: -XDignore.symbol.file=true
*
* 进程启动类程序启动后读取application.xml,进行classpath扫描动态加载Service与Servlet
* 再进行Service、Servlet与其他资源之间的依赖注入。
*
* @author zhangjx
*/
public final class Application {
public static final String RESNAME_TIME = "APP_TIME";
public static final String RESNAME_HOME = "APP_HOME";
public static final String RESNAME_NODE = "APP_NODE";
public static final String RESNAME_ADDR = "APP_ADDR";
public static final String RESNAME_GRES = "APP_GRES";
protected final ResourceFactory factory = ResourceFactory.root();
protected final WatchFactory watch = WatchFactory.root();
protected final HashMap<Class, ServiceEntry> localServices = new HashMap<>();
protected final ArrayList<ServiceEntry> remoteServices = new ArrayList<>();
protected boolean serviceInited = false;
protected final InetAddress localAddress = Utility.localInetAddress();
protected String nodeName = "";
//--------------------------------------------------------------------------------------------
private File home;
private final Logger logger;
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 cdl;
private Application(final AnyValue config) {
this.config = config;
final File root = new File(System.getProperty(RESNAME_HOME));
this.factory.register(RESNAME_TIME, long.class, this.startTime);
this.factory.register(RESNAME_HOME, Path.class, root.toPath());
this.factory.register(RESNAME_HOME, File.class, root);
try {
this.factory.register(RESNAME_HOME, root.getCanonicalPath());
this.home = root.getCanonicalFile();
} catch (IOException e) {
throw new RuntimeException(e);
}
final File logconf = new File(root, "conf/logging.properties");
this.nodeName = config.getValue("node", "");
this.factory.register(RESNAME_NODE, this.nodeName);
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('\\', '/');
FileInputStream fin = new FileInputStream(logconf);
Properties properties = new Properties();
properties.load(fin);
fin.close();
properties.entrySet().stream().forEach(x -> {
x.setValue(x.getValue().toString().replace("${APP_HOME}", rootpath));
});
if (properties.getProperty("java.util.logging.FileHandler.formatter") == null) {
properties.setProperty("java.util.logging.FileHandler.formatter", LogFileHandler.LoggingFormater.class.getName());
}
if (properties.getProperty("java.util.logging.ConsoleHandler.formatter") == null) {
properties.setProperty("java.util.logging.ConsoleHandler.formatter", LogFileHandler.LoggingFormater.class.getName());
}
String fileHandlerPattern = properties.getProperty("java.util.logging.FileHandler.pattern");
if (fileHandlerPattern != null && fileHandlerPattern.contains("%d")) {
final String fileHandlerClass = LogFileHandler.class.getName();
Properties prop = new Properties();
String handlers = properties.getProperty("handlers");
if (handlers != null && handlers.contains("java.util.logging.FileHandler")) {
prop.setProperty("handlers", handlers.replace("java.util.logging.FileHandler", fileHandlerClass));
}
if (!prop.isEmpty()) {
String prefix = fileHandlerClass + ".";
properties.entrySet().stream().forEach(x -> {
if (x.getKey().toString().startsWith("java.util.logging.FileHandler.")) {
prop.put(x.getKey().toString().replace("java.util.logging.FileHandler.", prefix), x.getValue());
}
});
prop.entrySet().stream().forEach(x -> {
properties.put(x.getKey(), x.getValue());
});
}
}
ByteArrayOutputStream out = new ByteArrayOutputStream();
final PrintStream ps = new PrintStream(out);
properties.forEach((x, y) -> ps.println(x + "=" + y));
LogManager.getLogManager().readConfiguration(new ByteArrayInputStream(out.toByteArray()));
} catch (Exception e) {
Logger.getLogger(this.getClass().getSimpleName()).log(Level.WARNING, "init logger configuration error", e);
}
}
logger = Logger.getLogger(this.getClass().getSimpleName());
}
public File getHome() {
return home;
}
private void initLogging() {
}
public void init() throws Exception {
System.setProperty("convert.bson.pool.size", "128");
System.setProperty("convert.json.pool.size", "128");
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));
this.factory.register(BsonFactory.root());
this.factory.register(JsonFactory.root());
this.factory.register(BsonFactory.root().getConvert());
this.factory.register(JsonFactory.root().getConvert());
File persist = new File(root, "conf/persistence.xml");
if (persist.isFile()) System.setProperty(DataJDBCSource.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");
Server.loadLib(logger, lib);
initLogging();
InetAddress addr = Utility.localInetAddress();
if (addr != null) {
byte[] bs = addr.getAddress();
int v = (0xff & bs[bs.length - 2]) % 10 * 100 + (0xff & bs[bs.length - 1]);
this.factory.register("property.datasource.nodeid", "" + v);
}
initResources();
}
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).load(application.config);
application.factory.inject(service);
}
private static Application create() throws IOException {
final String home = new File(System.getProperty(RESNAME_HOME, "")).getCanonicalPath();
System.setProperty(RESNAME_HOME, home);
File appfile = new File(home, "conf/application.xml");
//System.setProperty(DataConnection.PERSIST_FILEPATH, appfile.getCanonicalPath());
return new Application(load(new FileInputStream(appfile)));
}
public static void main(String[] args) throws Exception {
//运行主程序
final Application application = Application.create();
if (System.getProperty("SHUTDOWN") != null) {
application.sendShutDown();
return;
}
application.init();
application.startSelfServer();
application.start();
System.exit(0);
}
private void startSelfServer() throws Exception {
final Application application = this;
new Thread() {
{
setName("Application-Control-Thread");
}
@Override
public void run() {
try {
final DatagramChannel channel = DatagramChannel.open();
channel.configureBlocking(true);
channel.bind(new InetSocketAddress(config.getValue("host", "127.0.0.1"), config.getIntValue("port")));
boolean loop = true;
ByteBuffer buffer = ByteBuffer.allocateDirect(1024);
while (loop) {
buffer.clear();
SocketAddress address = channel.receive(buffer);
buffer.flip();
byte[] bytes = new byte[buffer.remaining()];
buffer.get(bytes);
if ("SHUTDOWN".equalsIgnoreCase(new String(bytes))) {
try {
long s = System.currentTimeMillis();
logger.info(application.getClass().getSimpleName() + " shutdowning");
application.shutdown();
application.cdl.countDown();
buffer.clear();
buffer.put("SHUTDOWN OK".getBytes());
buffer.flip();
channel.send(buffer, address);
long e = System.currentTimeMillis() - s;
logger.info(application.getClass().getSimpleName() + " shutdown in " + e + " ms");
Thread.sleep(100L);
System.exit(0);
} catch (Exception ex) {
logger.log(Level.INFO, "SHUTDOWN FAIL", ex);
buffer.clear();
buffer.put("SHUTDOWN FAIL".getBytes());
buffer.flip();
channel.send(buffer, address);
}
}
}
} catch (Exception e) {
logger.log(Level.INFO, "Control fail", e);
System.exit(1);
}
}
}.start();
}
private void sendShutDown() throws Exception {
final DatagramChannel channel = DatagramChannel.open();
channel.configureBlocking(true);
channel.connect(new InetSocketAddress(config.getValue("host", "127.0.0.1"), config.getIntValue("port")));
ByteBuffer buffer = ByteBuffer.allocate(128);
buffer.put("SHUTDOWN".getBytes());
buffer.flip();
channel.write(buffer);
buffer.clear();
channel.configureBlocking(false);
channel.read(buffer);
buffer.flip();
byte[] bytes = new byte[buffer.remaining()];
buffer.get(bytes);
channel.close();
logger.info(new String(bytes));
Thread.sleep(500);
}
public void start() throws Exception {
final AnyValue[] entrys = config.getAnyValues("server");
cdl = new CountDownLatch(entrys.length + 1);
CountDownLatch timecd = new CountDownLatch(entrys.length);
runServers(timecd, entrys);
timecd.await();
logger.info(this.getClass().getSimpleName() + " started in " + (System.currentTimeMillis() - startTime) + " ms");
cdl.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() {
{
String host = entry.getValue("host", "").replace("0.0.0.0", "");
setName(entry.getValue("protocol", "Server").toUpperCase() + "-" + host + ":" + entry.getIntValue("port", 80) + "-Thread");
this.setDaemon(true);
}
@Override
public void run() {
try {
//Thread ctd = Thread.currentThread();
//ctd.setContextClassLoader(new URLClassLoader(new URL[0], ctd.getContextClassLoader()));
NodeServer server = null;
if ("HTTP".equalsIgnoreCase(entry.getValue("protocol", ""))) {
server = new NodeHttpServer(Application.this, servicecdl, new HttpServer(startTime, watch));
} else if ("SNCP".equalsIgnoreCase(entry.getValue("protocol", ""))) {
server = new NodeSncpServer(Application.this, servicecdl, new SncpServer(startTime, watch));
}
if (server == null) {
logger.log(Level.SEVERE, "Not found Server Class for protocol({0})", entry.getValue("protocol"));
System.exit(0);
}
servers.add(server);
server.load(entry); //必须在init之前
server.init(entry);
server.start();
timecd.countDown();
} catch (Exception ex) {
logger.log(Level.WARNING, entry + " runServers error", ex);
} finally {
cdl.countDown();
}
}
}.start();
}
}
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) {
factory.register(RESNAME_GRES, AnyValue.class, resources);
//------------------------------------------------------------------------
final AnyValue properties = resources.getAnyValue("properties");
if (properties != null) {
String dfloads = properties.getValue("load");
if (dfloads != null) {
for (String dfload : dfloads.split(";")) {
if (dfload.trim().isEmpty()) continue;
dfload = dfload.trim().replace("${APP_HOME}", home.getCanonicalPath()).replace('\\', '/');
final File df = (dfload.indexOf('/') < 0) ? new File(home, "conf/" + dfload) : new File(dfload);
if (df.isFile()) {
Properties ps = new Properties();
InputStream in = new FileInputStream(df);
ps.load(in);
in.close();
ps.forEach((x, y) -> factory.register("property." + x, y));
}
}
}
for (AnyValue prop : properties.getAnyValues("property")) {
String name = prop.getValue("name");
String value = prop.getValue("value");
if (name == null || value == null) continue;
if (name.startsWith("system.property.")) {
System.setProperty(name.substring("system.property.".length()), value);
} else {
factory.register("property." + name, value);
}
}
}
//------------------------------------------------------------------------
final String host = this.localAddress.getHostAddress();
for (AnyValue conf : resources.getAnyValues("remote")) {
final String name = conf.getValue("name");
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"));
}
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 ");
Transport transport = new Transport(name, protocol, watch, 100, addresses[0]);
factory.register(name, Transport.class, transport);
if (this.nodeName.isEmpty() && host.equals(addrs[0].getValue("addr"))) {
this.nodeName = name;
this.factory.register(RESNAME_NODE, this.nodeName);
}
}
}
//------------------------------------------------------------------------
logger.info(RESNAME_NODE + "=" + this.nodeName);
logger.info("datasource.nodeid=" + this.factory.find("property.datasource.nodeid", String.class));
}
private void shutdown() throws Exception {
servers.stream().forEach((server) -> {
try {
server.shutdown();
} catch (Exception t) {
logger.log(Level.WARNING, " shutdown server(" + server.getSocketAddress() + ") error", t);
} finally {
cdl.countDown();
}
});
final StringBuilder sb = logger.isLoggable(Level.INFO) ? new StringBuilder() : null;
localServices.entrySet().parallelStream().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);
} catch (Exception e) {
logger.log(Level.FINER, "close DataSource erroneous", e);
}
}
}
private static AnyValue load(final InputStream in0) {
final DefaultAnyValue any = new DefaultAnyValue();
try (final InputStream in = in0) {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document doc = builder.parse(in);
Element root = doc.getDocumentElement();
load(any, root);
} catch (Exception ex) {
throw new RuntimeException(ex);
}
return any;
}
private static void load(final DefaultAnyValue any, final Node root) {
final String home = System.getProperty(RESNAME_HOME);
NamedNodeMap nodes = root.getAttributes();
if (nodes == null) return;
for (int i = 0; i < nodes.getLength(); i++) {
Node node = nodes.item(i);
any.addValue(node.getNodeName(), node.getNodeValue().replace("${APP_HOME}", home));
}
NodeList children = root.getChildNodes();
if (children == null) return;
for (int i = 0; i < children.getLength(); i++) {
Node node = children.item(i);
if (node.getNodeType() != Node.ELEMENT_NODE) continue;
DefaultAnyValue sub = new DefaultAnyValue();
load(sub, node);
any.addValue(node.getNodeName(), sub);
}
}
}

View File

@@ -0,0 +1,319 @@
/*
* 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.boot;
import com.wentch.redkale.util.Ignore;
import com.wentch.redkale.util.AutoLoad;
import com.wentch.redkale.util.AnyValue;
import java.io.*;
import java.lang.annotation.*;
import java.lang.reflect.*;
import java.net.*;
import java.util.*;
import java.util.jar.*;
import java.util.logging.*;
import java.util.regex.*;
/**
* class过滤器 符合条件的class会保留下来存入FilterEntry。
*
* @author zhangjx
* @param <T>
*/
public final class ClassFilter<T> {
private final Set<FilterEntry<T>> entrys = new HashSet<>();
private boolean refused;
private Class superClass;
private Class<? extends Annotation> annotationClass;
private Pattern[] includePatterns;
private Pattern[] excludePatterns;
public ClassFilter(Class<? extends Annotation> annotationClass, Class superClass) {
this.annotationClass = annotationClass;
this.superClass = superClass;
}
/**
* 获取符合条件的class集合
* <p>
* @return
*/
public final Set<FilterEntry<T>> getFilterEntrys() {
return entrys;
}
/**
* 自动扫描地过滤指定的class
* <p>
* @param property
* @param clazzname
*/
@SuppressWarnings("unchecked")
public final void filter(AnyValue property, String clazzname) {
filter(property, clazzname, true);
}
/**
* 过滤指定的class
* <p>
* @param property application.xml中对应class节点下的property属性项
* @param clazzname class名称
* @param autoscan 为true表示自动扫描的 false表示显著调用filter AutoLoad的注解将被忽略
*/
public final void filter(AnyValue property, String clazzname, boolean autoscan) {
if (!accept(property, clazzname)) return;
try {
Class clazz = Class.forName(clazzname);
if (accept(property, clazz, autoscan)) {
FilterEntry en = new FilterEntry(clazz, property);
if (!entrys.contains(en)) entrys.add(en);
}
} catch (Throwable cfe) {
}
}
private static Pattern[] toPattern(String[] regs) {
if (regs == null) return null;
int i = 0;
Pattern[] rs = new Pattern[regs.length];
for (String reg : regs) {
if (reg == null || reg.trim().isEmpty()) continue;
rs[i++] = Pattern.compile(reg.trim());
}
if (i == 0) return null;
if (i == rs.length) return rs;
Pattern[] ps = new Pattern[i];
System.arraycopy(rs, 0, ps, 0, i);
return ps;
}
/**
* 判断class是否有效
* <p>
* @param property
* @param classname
* @return
*/
public boolean accept(AnyValue property, String classname) {
if (this.refused) return false;
if (classname.startsWith("java.") || classname.startsWith("javax.")) return false;
if (excludePatterns != null) {
for (Pattern reg : excludePatterns) {
if (reg.matcher(classname).matches()) return false;
}
}
if (includePatterns != null) {
for (Pattern reg : includePatterns) {
if (reg.matcher(classname).matches()) return true;
}
}
return includePatterns == null;
}
/**
* 判断class是否有效
* <p>
* @param property
* @param clazz
* @param autoscan
* @return
*/
@SuppressWarnings("unchecked")
public boolean accept(AnyValue property, Class clazz, boolean autoscan) {
if (this.refused || !Modifier.isPublic(clazz.getModifiers())) return false;
if (clazz.getAnnotation(Ignore.class) != null) return false;
if (autoscan) {
AutoLoad auto = (AutoLoad) clazz.getAnnotation(AutoLoad.class);
if (auto != null && !auto.value()) return false;
}
if (annotationClass != null && clazz.getAnnotation(annotationClass) == null) return false;
return superClass == null || (clazz != superClass && superClass.isAssignableFrom(clazz));
}
public void setSuperClass(Class superClass) {
this.superClass = superClass;
}
public void setAnnotationClass(Class<? extends Annotation> annotationClass) {
this.annotationClass = annotationClass;
}
public Pattern[] getIncludePatterns() {
return includePatterns;
}
public void setIncludePatterns(String[] includePatterns) {
this.includePatterns = toPattern(includePatterns);
}
public Pattern[] getExcludePatterns() {
return excludePatterns;
}
public void setExcludePatterns(String[] excludePatterns) {
this.excludePatterns = toPattern(excludePatterns);
}
public Class<? extends Annotation> getAnnotationClass() {
return annotationClass;
}
public Class getSuperClass() {
return superClass;
}
public boolean isRefused() {
return refused;
}
public void setRefused(boolean refused) {
this.refused = refused;
}
/**
* 存放符合条件的class与class指定的属性项
* <p>
* @param <T>
*/
public static final class FilterEntry<T> {
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.property = property;
this.name = property == null ? "" : property.getValue("name", "");
}
@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")) + "]";
}
@Override
public int hashCode() {
return this.type.hashCode();
}
@Override
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));
}
public Class<T> getType() {
return type;
}
public String getName() {
return name;
}
public AnyValue getProperty() {
return property;
}
public Object getAttachment() {
return attachment;
}
public void setAttachment(Object attachment) {
this.attachment = attachment;
}
}
/**
* class加载类
*/
public static class Loader {
protected static final Logger logger = Logger.getLogger(Loader.class.getName());
/**
* 加载当前线程的classpath扫描所有class进行过滤
* <p>
* @param exclude 不需要扫描的文件夹, 可以为null
* @param filters
* @throws IOException
*/
public static void load(final File exclude, final ClassFilter... filters) throws IOException {
URLClassLoader loader = (URLClassLoader) Thread.currentThread().getContextClassLoader();
List<URL> urlfiles = new ArrayList<>(2);
List<URL> urljares = new ArrayList<>(2);
final URL exurl = exclude != null ? exclude.toURI().toURL() : null;
for (URL url : loader.getURLs()) {
if (exurl != null && exurl.sameFile(url)) continue;
if (url.getPath().endsWith(".jar")) {
urljares.add(url);
} else {
urlfiles.add(url);
}
}
List<File> files = new ArrayList<>();
boolean debug = logger.isLoggable(Level.FINEST);
StringBuilder debugstr = new StringBuilder();
for (URL url : urljares) {
try (JarFile jar = new JarFile(URLDecoder.decode(url.getFile(), "UTF-8"))) {
Enumeration<JarEntry> it = jar.entries();
while (it.hasMoreElements()) {
String entryname = it.nextElement().getName().replace('/', '.');
if (entryname.endsWith(".class") && entryname.indexOf('$') < 0) {
String classname = entryname.substring(0, entryname.length() - 6);
if (classname.startsWith("javax.") || classname.startsWith("org.") || classname.startsWith("com.mysql.")) continue;
if (debug) debugstr.append(classname).append("\r\n");
for (final ClassFilter filter : filters) {
if (filter != null) filter.filter(null, classname);
}
}
}
}
}
for (URL url : urlfiles) {
files.clear();
File root = new File(url.getFile());
String rootpath = root.getPath();
loadClassFiles(exclude, root, files);
for (File f : files) {
String classname = f.getPath().substring(rootpath.length() + 1, f.getPath().length() - 6).replace(File.separatorChar, '.');
if (classname.startsWith("javax.") || classname.startsWith("org.") || classname.startsWith("com.mysql.")) continue;
if (debug) debugstr.append(classname).append("\r\n");
for (final ClassFilter filter : filters) {
if (filter != null) filter.filter(null, classname);
}
}
}
//if (debug) logger.log(Level.INFO, "scan classes: \r\n{0}", debugstr);
}
private static void loadClassFiles(File exclude, File root, List<File> files) {
if (root.isFile() && root.getName().endsWith(".class")) {
files.add(root);
} else if (root.isDirectory()) {
if (exclude != null && exclude.equals(root)) return;
for (File f : root.listFiles()) {
loadClassFiles(exclude, f, files);
}
}
}
}
}

View File

@@ -0,0 +1,230 @@
/*
* 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.boot;
import java.io.*;
import java.nio.file.*;
import static java.nio.file.StandardCopyOption.*;
import java.time.*;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.*;
import java.util.logging.*;
import java.util.logging.Formatter;
/**
* 自定义的日志存储类
* <p>
* @author zhangjx
*/
public class LogFileHandler extends Handler {
public static class LoggingFormater extends Formatter {
private static final String format = "%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS.%tL %4$s %2$s\r\n%5$s%6$s\r\n";
@Override
public String format(LogRecord record) {
String source;
if (record.getSourceClassName() != null) {
source = record.getSourceClassName();
if (record.getSourceMethodName() != null) {
source += " " + record.getSourceMethodName();
}
} else {
source = record.getLoggerName();
}
String message = formatMessage(record);
String throwable = "";
if (record.getThrown() != null) {
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw) {
@Override
public void println() {
super.print("\r\n");
}
};
pw.println();
record.getThrown().printStackTrace(pw);
pw.close();
throwable = sw.toString();
}
return String.format(format,
System.currentTimeMillis(),
source,
record.getLoggerName(),
record.getLevel().getName(),
message,
throwable);
}
}
protected final LinkedBlockingQueue<LogRecord> records = new LinkedBlockingQueue();
private String pattern;
private int limit; //文件大小限制
private final AtomicInteger index = new AtomicInteger();
private int count = 1; //文件限制
private long tomorrow;
private boolean append;
private final AtomicLong length = new AtomicLong();
private File logfile;
private OutputStream stream;
public LogFileHandler() {
updateTomorrow();
configure();
open();
}
private void updateTomorrow() {
Calendar cal = Calendar.getInstance();
cal.set(Calendar.HOUR_OF_DAY, 0);
cal.set(Calendar.MINUTE, 0);
cal.set(Calendar.SECOND, 0);
cal.set(Calendar.MILLISECOND, 0);
cal.add(Calendar.DAY_OF_YEAR, 1);
long t = cal.getTimeInMillis();
if (this.tomorrow != t) index.set(0);
this.tomorrow = t;
}
private void open() {
new Thread() {
{
setName("Logging-FileHandler-Thread");
setDaemon(true);
}
@Override
public void run() {
while (true) {
try {
LogRecord record = records.take();
if ((limit > 0 && limit <= length.get()) || tomorrow <= record.getMillis()) {
updateTomorrow();
if (stream != null) {
stream.close();
for (int i = Math.min(count - 2, index.get() - 1); i > 0; i--) {
File greater = new File(logfile.getPath() + "." + i);
if (greater.exists()) Files.move(greater.toPath(), new File(logfile.getPath() + "." + (i + 1)).toPath(), REPLACE_EXISTING, ATOMIC_MOVE);
}
Files.move(logfile.toPath(), new File(logfile.getPath() + ".1").toPath(), REPLACE_EXISTING, ATOMIC_MOVE);
stream = null;
}
}
if (stream == null) {
index.incrementAndGet();
java.time.LocalDate date = LocalDate.now();
logfile = new File(pattern.replace("%d", String.valueOf((date.getYear() * 10000 + date.getMonthValue() * 100 + date.getDayOfMonth()))));
logfile.getParentFile().mkdirs();
length.set(logfile.length());
stream = new FileOutputStream(logfile, append);
}
//----------------------写日志-------------------------
String message = getFormatter().format(record);
String encoding = getEncoding();
byte[] bytes = encoding == null ? message.getBytes() : message.getBytes(encoding);
stream.write(bytes);
length.addAndGet(bytes.length);
} catch (Exception e) {
ErrorManager err = getErrorManager();
if (err != null) err.error(null, e, ErrorManager.WRITE_FAILURE);
}
}
}
}.start();
}
private void configure() {
LogManager manager = LogManager.getLogManager();
String cname = getClass().getName();
pattern = manager.getProperty(cname + ".pattern");
if (pattern == null) pattern = "logs/log-%d.log";
String limitstr = manager.getProperty(cname + ".limit");
try {
if (limitstr != null) limit = Math.abs(Integer.decode(limitstr));
} catch (Exception e) {
}
String countstr = manager.getProperty(cname + ".count");
try {
if (countstr != null) count = Math.max(1, Math.abs(Integer.decode(countstr)));
} catch (Exception e) {
}
String appendstr = manager.getProperty(cname + ".append");
try {
if (appendstr != null) append = "true".equalsIgnoreCase(appendstr) || "1".equals(appendstr);
} catch (Exception e) {
}
String levelstr = manager.getProperty(cname + ".level");
try {
if (levelstr != null) {
Level l = Level.parse(levelstr);
setLevel(l != null ? l : Level.ALL);
}
} catch (Exception e) {
}
String filterstr = manager.getProperty(cname + ".filter");
try {
if (filterstr != null) {
Class<?> clz = ClassLoader.getSystemClassLoader().loadClass(filterstr);
setFilter((Filter) clz.newInstance());
}
} catch (Exception e) {
}
String formatterstr = manager.getProperty(cname + ".formatter");
try {
if (formatterstr != null) {
Class<?> clz = ClassLoader.getSystemClassLoader().loadClass(formatterstr);
setFormatter((Formatter) clz.newInstance());
}
} catch (Exception e) {
}
if (getFormatter() == null) setFormatter(new SimpleFormatter());
String encodingstr = manager.getProperty(cname + ".encoding");
try {
if (encodingstr != null) setEncoding(encodingstr);
} catch (Exception e) {
}
}
@Override
public void publish(LogRecord record) {
records.offer(record);
}
@Override
public void flush() {
try {
if (stream != null) stream.flush();
} catch (Exception e) {
ErrorManager err = getErrorManager();
if (err != null) err.error(null, e, ErrorManager.FLUSH_FAILURE);
}
}
@Override
public void close() throws SecurityException {
try {
if (stream != null) stream.close();
} catch (Exception e) {
ErrorManager err = getErrorManager();
if (err != null) err.error(null, e, ErrorManager.CLOSE_FAILURE);
}
}
}

View File

@@ -0,0 +1,83 @@
/*
* 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.boot;
import com.wentch.redkale.net.http.WebServlet;
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 java.net.InetSocketAddress;
import java.util.*;
import java.util.concurrent.CountDownLatch;
import java.util.logging.*;
/**
*
* @author zhangjx
*/
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;
}
@Override
public void init(AnyValue config) throws Exception {
server.init(config);
super.init(config);
}
@Override
public InetSocketAddress getSocketAddress() {
return server == null ? null : server.getSocketAddress();
}
@Override
public void load(AnyValue config) throws Exception {
super.load(config);
ClassFilter<HttpServlet> httpFilter = createHttpServletClassFilter(application.nodeName, config);
ClassFilter<Service> serviceFilter = createServiceClassFilter(application.nodeName, config);
long s = System.currentTimeMillis();
ClassFilter.Loader.load(application.getHome(), 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);
}
protected static ClassFilter<HttpServlet> createHttpServletClassFilter(final String node, final AnyValue config) {
return createClassFilter(node, config, WebServlet.class, HttpServlet.class, null, "servlets", "servlet");
}
protected void initHttpServlet(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() + "] ";
for (FilterEntry<HttpServlet> en : filter.getFilterEntrys()) {
Class<HttpServlet> clazz = en.getType();
if (Modifier.isAbstract(clazz.getModifiers())) continue;
WebServlet ws = clazz.getAnnotation(WebServlet.class);
if (ws == null || ws.value().length == 0) continue;
final HttpServlet servlet = clazz.newInstance();
application.factory.inject(servlet);
String[] mappings = ws.value();
if (ws.fillurl() && !prefix.isEmpty()) {
for (int i = 0; i < mappings.length; i++) {
mappings[i] = prefix + mappings[i];
}
}
this.server.addHttpServlet(servlet, en.getProperty(), mappings);
if (sb != null) sb.append(threadName).append(" Loaded ").append(clazz.getName()).append(" --> ").append(Arrays.toString(mappings)).append(LINE_SEPARATOR);
}
if (sb != null && sb.length() > 0) logger.log(Level.FINE, sb.toString());
}
}

View File

@@ -0,0 +1,250 @@
/*
* 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.boot;
import com.wentch.redkale.net.sncp.ServiceEntry;
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 java.io.*;
import java.lang.annotation.Annotation;
import java.lang.reflect.*;
import java.net.InetSocketAddress;
import java.util.*;
import java.util.concurrent.*;
import java.util.function.Consumer;
import java.util.logging.*;
/**
*
* @author zhangjx
*/
public abstract class NodeServer {
public static final String LINE_SEPARATOR = "\r\n";
protected final Logger logger;
protected final Application application;
private final CountDownLatch servicecdl;
private final Server server;
protected Consumer<ServiceEntry> consumer;
public NodeServer(Application application, CountDownLatch servicecdl, Server server) {
this.application = application;
this.servicecdl = servicecdl;
this.server = server;
this.logger = Logger.getLogger(this.getClass().getSimpleName());
}
public void load(final AnyValue config) throws Exception {
}
public void init(AnyValue config) throws Exception {
//设置root文件夹
String webroot = config.getValue("root", "root");
File myroot = new File(webroot);
if (!webroot.contains(":") && !webroot.startsWith("/")) {
myroot = new File(System.getProperty(Application.RESNAME_HOME), webroot);
}
final String homepath = myroot.getCanonicalPath();
Server.loadLib(logger, config.getValue("lib", "") + ";" + homepath + "/lib/*;" + homepath + "/classes");
}
public abstract InetSocketAddress getSocketAddress();
public void start() throws IOException {
server.start();
}
public void shutdown() throws IOException {
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);
}
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;
}
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()));
}
}
}
@SuppressWarnings("unchecked")
protected void loadService(final AnyValue servicesConf, ClassFilter serviceFilter) throws Exception {
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");
for (FilterEntry<Service> entry : entrys) { //service实现类
final Class 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);
}
}
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());
}
}
}
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 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,
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);
}
}
}
}
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;
}
}

View File

@@ -0,0 +1,50 @@
/*
* 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.boot;
import com.wentch.redkale.net.sncp.SncpServer;
import com.wentch.redkale.util.AnyValue;
import com.wentch.redkale.service.Service;
import java.net.InetSocketAddress;
import java.util.concurrent.CountDownLatch;
/**
*
* @author zhangjx
*/
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);
}
@Override
public void init(AnyValue config) throws Exception {
server.init(config);
super.init(config);
}
@Override
public InetSocketAddress getSocketAddress() {
return server == null ? null : server.getSocketAddress();
}
@Override
public void load(AnyValue config) throws Exception {
super.load(config);
ClassFilter<Service> serviceFilter = createServiceClassFilter(application.nodeName, config);
long s = System.currentTimeMillis();
ClassFilter.Loader.load(application.getHome(), serviceFilter);
long e = System.currentTimeMillis() - s;
logger.info(this.getClass().getSimpleName() + " load filter class in " + e + " ms");
loadService(config.getAnyValue("services"), serviceFilter); //必须在servlet之前
}
}

View File

@@ -0,0 +1,40 @@
/*
* 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;
import java.lang.reflect.Type;
/**
* 对不明类型的对象进行序列化; BSON序列化时将对象的类名写入WriterJSON则不写入。
*
* @author zhangjx
* @param <T>
*/
public final class AnyEncoder<T> implements Encodeable<Writer, T> {
final Factory factory;
AnyEncoder(Factory factory) {
this.factory = factory;
}
@Override
@SuppressWarnings("unchecked")
public void convertTo(final Writer out, final T value) {
if (value == null) {
out.writeNull();
} else {
out.wirteClassName(value.getClass());
factory.loadEncoder(value.getClass()).convertTo(out, value);
}
}
@Override
public Type getType() {
return Object.class;
}
}

View File

@@ -0,0 +1,79 @@
/*
* 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;
import java.lang.reflect.*;
import java.util.*;
/**
* 对象数组的序列化不包含int[]、long[]这样的primitive class数组.
* 数组长度不能超过 32767。 在BSON中数组长度设定的是short对于大于32767长度的数组传输会影响性能所以没有采用int存储。
* 支持一定程度的泛型。
*
* @author zhangjx
* @param <T>
*/
@SuppressWarnings("unchecked")
public final class ArrayDecoder<T> implements Decodeable<Reader, T[]> {
private final Type type;
private final Type componentType;
private final Class componentClass;
private final Decodeable<Reader, T> decoder;
public ArrayDecoder(final Factory factory, final Type type) {
this.type = type;
if (type instanceof GenericArrayType) {
Type t = ((GenericArrayType) type).getGenericComponentType();
this.componentType = t instanceof TypeVariable ? Object.class : t;
} else if ((type instanceof Class) && ((Class) type).isArray()) {
this.componentType = ((Class) type).getComponentType();
} else {
throw new ConvertException("(" + type + ") is not a array type");
}
if (this.componentType instanceof ParameterizedType) {
this.componentClass = (Class) ((ParameterizedType) this.componentType).getRawType();
} else {
this.componentClass = (Class) this.componentType;
}
factory.register(type, this);
this.decoder = factory.loadDecoder(this.componentType);
}
@Override
public T[] convertFrom(Reader in) {
final int len = in.readArrayB();
if (len == Reader.SIGN_NULL) return null;
final Decodeable<Reader, T> localdecoder = this.decoder;
final List<T> result = new ArrayList();
if (len == Reader.SIGN_NOLENGTH) {
while (in.hasNext()) {
result.add(localdecoder.convertFrom(in));
}
} else {
for (int i = 0; i < len; i++) {
result.add(localdecoder.convertFrom(in));
}
}
in.readArrayE();
T[] rs = (T[]) Array.newInstance((Class) this.componentClass, result.size());
return result.toArray(rs);
}
@Override
public String toString() {
return this.getClass().getSimpleName() + "{componentType:" + this.componentType + ", decoder:" + this.decoder + "}";
}
@Override
public Type getType() {
return type;
}
}

View File

@@ -0,0 +1,75 @@
/*
* 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;
import java.lang.reflect.*;
/**
* 对象数组的反序列化不包含int[]、long[]这样的primitive class数组.
* 数组长度不能超过 32767。 在BSON中数组长度设定的是short对于大于32767长度的数组传输会影响性能所以没有采用int存储。
* 支持一定程度的泛型。
*
* @author zhangjx
* @param <T>
*/
@SuppressWarnings("unchecked")
public final class ArrayEncoder<T> implements Encodeable<Writer, T[]> {
private final Type type;
private final Type componentType;
private final Encodeable anyEncoder;
private final Encodeable<Writer, Object> encoder;
public ArrayEncoder(final Factory factory, final Type type) {
this.type = type;
if (type instanceof GenericArrayType) {
Type t = ((GenericArrayType) type).getGenericComponentType();
this.componentType = t instanceof TypeVariable ? Object.class : t;
} else if ((type instanceof Class) && ((Class) type).isArray()) {
this.componentType = ((Class) type).getComponentType();
} else {
throw new ConvertException("(" + type + ") is not a array type");
}
factory.register(type, this);
this.encoder = factory.loadEncoder(this.componentType);
this.anyEncoder = factory.getAnyEncoder();
}
@Override
public void convertTo(Writer out, T[] value) {
if (value == null) {
out.writeNull();
return;
}
if (value.length == 0) {
out.writeArrayB(0);
out.writeArrayE();
return;
}
out.writeArrayB(value.length);
final Type comp = this.componentType;
boolean first = true;
for (Object v : value) {
if (!first) out.writeArrayMark();
((v.getClass() == comp) ? encoder : anyEncoder).convertTo(out, v);
if (first) first = false;
}
out.writeArrayE();
}
@Override
public String toString() {
return this.getClass().getSimpleName() + "{componentType:" + this.componentType + ", encoder:" + this.encoder + "}";
}
@Override
public Type getType() {
return type;
}
}

View File

@@ -0,0 +1,69 @@
/*
* 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;
import com.wentch.redkale.util.Creator;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Collection;
/**
* 对象集合的反序列化.
* 集合大小不能超过 32767。 在BSON中集合大小设定的是short对于大于32767长度的集合传输会影响性能所以没有采用int存储。
* 支持一定程度的泛型。
*
* @author zhangjx
* @param <T>
*/
@SuppressWarnings("unchecked")
public final class CollectionDecoder<T> implements Decodeable<Reader, Collection<T>> {
private final Type type;
private final Type componentType;
protected Creator<Collection<T>> creator;
private final Decodeable<Reader, T> decoder;
public CollectionDecoder(final Factory factory, final Type type) {
this.type = type;
if (type instanceof ParameterizedType) {
final ParameterizedType pt = (ParameterizedType) type;
this.componentType = pt.getActualTypeArguments()[0];
this.creator = factory.loadCreator((Class) pt.getRawType());
factory.register(type, this);
this.decoder = factory.loadDecoder(this.componentType);
} else {
throw new ConvertException("collectiondecoder not support the type (" + type + ")");
}
}
@Override
public Collection<T> convertFrom(Reader in) {
final int len = in.readArrayB();
if (len == Reader.SIGN_NULL) return null;
final Decodeable<Reader, T> localdecoder = this.decoder;
final Collection<T> result = this.creator.create();
if (len == Reader.SIGN_NOLENGTH) {
while (in.hasNext()) {
result.add(localdecoder.convertFrom(in));
}
} else {
for (int i = 0; i < len; i++) {
result.add(localdecoder.convertFrom(in));
}
}
in.readArrayE();
return result;
}
@Override
public Type getType() {
return type;
}
}

View File

@@ -0,0 +1,65 @@
/*
* 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;
import java.lang.reflect.*;
import java.util.Collection;
/**
* 对象集合的序列化.
* 集合大小不能超过 32767。 在BSON中集合大小设定的是short对于大于32767长度的集合传输会影响性能所以没有采用int存储。
* 支持一定程度的泛型。
*
* @author zhangjx
* @param <T>
*/
@SuppressWarnings("unchecked")
public final class CollectionEncoder<T> implements Encodeable<Writer, Collection<T>> {
private final Type type;
private final Encodeable<Writer, Object> encoder;
public CollectionEncoder(final Factory factory, final Type type) {
this.type = type;
if (type instanceof ParameterizedType) {
Type t = ((ParameterizedType) type).getActualTypeArguments()[0];
if (t instanceof TypeVariable) {
this.encoder = factory.getAnyEncoder();
} else {
this.encoder = factory.loadEncoder(t);
}
} else {
this.encoder = factory.getAnyEncoder();
}
}
@Override
public void convertTo(Writer out, Collection<T> value) {
if (value == null) {
out.writeNull();
return;
}
if (value.isEmpty()) {
out.writeArrayB(0);
out.writeArrayE();
return;
}
out.writeArrayB(value.size());
boolean first = true;
for (Object v : value) {
if (!first) out.writeArrayMark();
encoder.convertTo(out, v);
if (first) first = false;
}
out.writeArrayE();
}
@Override
public Type getType() {
return type;
}
}

View File

@@ -0,0 +1,26 @@
/*
* 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;
/**
* 序列化操作类
*
* @author zhangjx
* @param <R>
* @param <W>
*/
public abstract class Convert<R extends Reader, W extends Writer> {
protected final Factory<R, W> factory;
protected Convert(Factory<R, W> factory) {
this.factory = factory;
}
public Factory<R, W> getFactory() {
return this.factory;
}
}

View File

@@ -0,0 +1,44 @@
/*
* 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;
import java.lang.annotation.*;
import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.*;
/**
* 依附在setter、getter方法、字段进行简单的配置
*
* @author zhangjx
*/
@Inherited
@Documented
@Target({METHOD, FIELD})
@Retention(RUNTIME)
@Repeatable(ConvertColumns.class)
public @interface ConvertColumn {
/**
* 给字段取个别名, 只对JSON有效
*
* @return
*/
String name() default "";
/**
* 解析/序列化时是否屏蔽该字段
*
* @return
*/
boolean ignore() default false;
/**
* 解析/序列化定制化的TYPE
*
* @return
*/
ConvertType type() default ConvertType.ALL;
}

View File

@@ -0,0 +1,67 @@
/*
* 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;
/**
* ConvertColumn 对应的实体类
*
* @author zhangjx
*/
public final class ConvertColumnEntry {
private String name = "";
private boolean ignore;
private ConvertType convertType;
public ConvertColumnEntry() {
}
public ConvertColumnEntry(ConvertColumn column) {
if (column == null) return;
this.name = column.name();
this.ignore = column.ignore();
this.convertType = column.type();
}
public ConvertColumnEntry(String name, boolean ignore) {
this.name = name;
this.ignore = ignore;
this.convertType = ConvertType.ALL;
}
public ConvertColumnEntry(String name, boolean ignore, ConvertType convertType) {
this.name = name;
this.ignore = ignore;
this.convertType = convertType;
}
public String name() {
return name == null ? "" : name;
}
public void setName(String name) {
this.name = name;
}
public boolean ignore() {
return ignore;
}
public void setIgnore(boolean ignore) {
this.ignore = ignore;
}
public ConvertType type() {
return convertType == null ? ConvertType.ALL : convertType;
}
public void setConvertType(ConvertType convertType) {
this.convertType = convertType;
}
}

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.convert;
import java.lang.annotation.*;
import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.*;
/**
* ConvertColumn 的多用类
*
* @author zhangjx
*/
@Inherited
@Documented
@Target({METHOD, FIELD})
@Retention(RUNTIME)
public @interface ConvertColumns {
ConvertColumn[] value();
}

View File

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

View File

@@ -0,0 +1,28 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package com.wentch.redkale.convert;
/**
*
* @author zhangjx
*/
public enum ConvertType {
JSON(1),
BSON(2),
ALL(127);
private final int value;
private ConvertType(int v) {
this.value = v;
}
public boolean contains(ConvertType type) {
if (type == null) return false;
return this.value >= type.value && (this.value & type.value) > 0;
}
}

View File

@@ -0,0 +1,64 @@
/*
* 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;
import com.wentch.redkale.util.Attribute;
/**
*
* @author zhangjx
* @param <R>
* @param <T>
* @param <F>
*/
@SuppressWarnings("unchecked")
public final class DeMember<R extends Reader, T, F> implements Comparable<DeMember<R, T, F>> {
protected final Attribute<T, F> attribute;
protected Decodeable<R, F> decoder;
public DeMember(final Attribute<T, F> attribute) {
this.attribute = attribute;
}
public DeMember(Attribute<T, F> attribute, Decodeable<R, F> decoder) {
this(attribute);
this.decoder = decoder;
}
public final void read(R in, T obj) {
this.attribute.set(obj, decoder.convertFrom(in));
}
public Attribute<T, F> getAttribute() {
return this.attribute;
}
@Override
public final int compareTo(DeMember<R, T, F> o) {
if (o == null) return 1;
return this.attribute.field().compareTo(o.attribute.field());
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (!(obj instanceof DeMember)) return false;
DeMember other = (DeMember) obj;
return compareTo(other) == 0;
}
@Override
public int hashCode() {
return this.attribute.field().hashCode();
}
@Override
public String toString() {
return "DeMember{" + "attribute=" + attribute.field() + ", decoder=" + decoder + '}';
}
}

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.convert;
import java.lang.reflect.Type;
/**
*
* @author zhangjx
* @param <R>
* @param <T>
*/
public interface Decodeable<R extends Reader, T> {
public T convertFrom(final R in);
/**
* 泛型映射接口
*
* @return
*/
public Type getType();
}

View File

@@ -0,0 +1,60 @@
/*
* 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;
import com.wentch.redkale.util.Attribute;
/**
*
* @author zhangjx
* @param <W>
* @param <T>
* @param <F>
*/
@SuppressWarnings("unchecked")
public final class EnMember<W extends Writer, T, F> implements Comparable<EnMember<W, T, F>> {
private final Attribute<T, F> attribute;
final Encodeable<W, F> encoder;
public EnMember(Attribute<T, F> attribute, Encodeable<W, F> encoder) {
this.attribute = attribute;
this.encoder = encoder;
}
public boolean write(final W out, final boolean comma, final T obj) {
F value = attribute.get(obj);
if (value == null) return comma;
out.writeField(comma, attribute);
encoder.convertTo(out, value);
return true;
}
@Override
public final int compareTo(EnMember<W, T, F> o) {
if (o == null) return 1;
return this.attribute.field().compareTo(o.attribute.field());
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (!(obj instanceof EnMember)) return false;
EnMember other = (EnMember) obj;
return compareTo(other) == 0;
}
@Override
public int hashCode() {
return this.attribute.field().hashCode();
}
@Override
public String toString() {
return "EnMember{" + "attribute=" + attribute.field() + ", encoder=" + encoder + '}';
}
}

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.convert;
import java.lang.reflect.Type;
/**
*
* @author zhangjx
* @param <W>
* @param <T>
*/
public interface Encodeable<W extends Writer, T> {
public void convertTo(final W out, T value);
/**
* 泛型映射接口
*
* @return
*/
public Type getType();
}

View File

@@ -0,0 +1,342 @@
/*
* 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;
import com.wentch.redkale.convert.ext.StringArraySimpledCoder;
import com.wentch.redkale.convert.ext.DoubleArraySimpledCoder;
import com.wentch.redkale.convert.ext.LongSimpledCoder;
import com.wentch.redkale.convert.ext.ByteArraySimpledCoder;
import com.wentch.redkale.convert.ext.IntArraySimpledCoder;
import com.wentch.redkale.convert.ext.DoubleSimpledCoder;
import com.wentch.redkale.convert.ext.TwoLongSimpledCoder;
import com.wentch.redkale.convert.ext.CharSimpledCoder;
import com.wentch.redkale.convert.ext.IntSimpledCoder;
import com.wentch.redkale.convert.ext.InetAddressSimpledCoder;
import com.wentch.redkale.convert.ext.LongArraySimpledCoder;
import com.wentch.redkale.convert.ext.DateSimpledCoder;
import com.wentch.redkale.convert.ext.BoolSimpledCoder;
import com.wentch.redkale.convert.ext.CharArraySimpledCoder;
import com.wentch.redkale.convert.ext.EnumSimpledCoder;
import com.wentch.redkale.convert.ext.BigIntegerSimpledCoder;
import com.wentch.redkale.convert.ext.ByteSimpledCoder;
import com.wentch.redkale.convert.ext.StringSimpledCoder;
import com.wentch.redkale.convert.ext.NumberSimpledCoder;
import com.wentch.redkale.convert.ext.TypeSimpledCoder;
import com.wentch.redkale.convert.ext.ShortArraySimpledCoder;
import com.wentch.redkale.convert.ext.BoolArraySimpledCoder;
import com.wentch.redkale.convert.ext.ShortSimpledCoder;
import com.wentch.redkale.convert.ext.FloatArraySimpledCoder;
import com.wentch.redkale.convert.ext.FloatSimpledCoder;
import com.wentch.redkale.util.TwoLong;
import com.wentch.redkale.util.Creator;
import java.lang.reflect.GenericArrayType;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Collection;
import java.util.Map;
import java.lang.reflect.*;
import java.math.BigInteger;
import java.net.*;
import static com.wentch.redkale.convert.ext.InetAddressSimpledCoder.*;
import java.util.*;
/**
*
* @author zhangjx
* @param <R>
* @param <W>
*/
@SuppressWarnings("unchecked")
public abstract class Factory<R extends Reader, W extends Writer> {
private final Factory parent;
protected Convert<R, W> convert;
private final Encodeable<W, ?> anyEncoder = new AnyEncoder(this);
//-----------------------------------------------------------------------------------
private final HashedMap<Class, Creator> creators = new HashedMap();
private final HashedMap<Type, Decodeable<R, ?>> decoders = new HashedMap();
private final HashedMap<Type, Encodeable<W, ?>> encoders = new HashedMap();
private final HashMap<AccessibleObject, ConvertColumnEntry> columnEntrys = new HashMap();
protected Factory(Factory<R, W> parent) {
this.parent = parent;
if (parent == null) {
//---------------------------------------------------------
this.register(boolean.class, BoolSimpledCoder.instance);
this.register(Boolean.class, BoolSimpledCoder.instance);
this.register(byte.class, ByteSimpledCoder.instance);
this.register(Byte.class, ByteSimpledCoder.instance);
this.register(short.class, ShortSimpledCoder.instance);
this.register(Short.class, ShortSimpledCoder.instance);
this.register(char.class, CharSimpledCoder.instance);
this.register(Character.class, CharSimpledCoder.instance);
this.register(int.class, IntSimpledCoder.instance);
this.register(Integer.class, IntSimpledCoder.instance);
this.register(long.class, LongSimpledCoder.instance);
this.register(Long.class, LongSimpledCoder.instance);
this.register(float.class, FloatSimpledCoder.instance);
this.register(Float.class, FloatSimpledCoder.instance);
this.register(double.class, DoubleSimpledCoder.instance);
this.register(Double.class, DoubleSimpledCoder.instance);
this.register(Number.class, NumberSimpledCoder.instance);
this.register(String.class, StringSimpledCoder.instance);
this.register(java.util.Date.class, DateSimpledCoder.instance);
this.register(BigInteger.class, BigIntegerSimpledCoder.instance);
this.register(InetAddress.class, InetAddressSimpledCoder.instance);
this.register(TwoLong.class, TwoLongSimpledCoder.instance);
this.register(Class.class, TypeSimpledCoder.instance);
this.register(InetSocketAddress.class, InetSocketAddressSimpledCoder.instance);
this.register(InetSocketAddress.class, InetSocketAddressSimpledCoder.instance);
//---------------------------------------------------------
this.register(boolean[].class, BoolArraySimpledCoder.instance);
this.register(byte[].class, ByteArraySimpledCoder.instance);
this.register(short[].class, ShortArraySimpledCoder.instance);
this.register(char[].class, CharArraySimpledCoder.instance);
this.register(int[].class, IntArraySimpledCoder.instance);
this.register(long[].class, LongArraySimpledCoder.instance);
this.register(float[].class, FloatArraySimpledCoder.instance);
this.register(double[].class, DoubleArraySimpledCoder.instance);
this.register(String[].class, StringArraySimpledCoder.instance);
//---------------------------------------------------------
}
}
public Factory parent() {
return this.parent;
}
public abstract ConvertType getConvertType();
public abstract boolean isReversible();
public abstract Factory createChild();
public Convert getConvert() {
return convert;
}
public ConvertColumnEntry findRef(AccessibleObject field) {
if (field == null) return null;
ConvertColumnEntry en = this.columnEntrys.get(field);
if (en != null) return en;
final ConvertType ct = this.getConvertType();
for (ConvertColumn ref : field.getAnnotationsByType(ConvertColumn.class)) {
if (ref.type().contains(ct)) return new ConvertColumnEntry(ref);
}
return null;
}
public final boolean register(final Class type, String column, ConvertColumnEntry entry) {
if (type == null || column == null || entry == null) return false;
try {
final Field field = type.getDeclaredField(column);
String get = "get";
if (field.getType() == boolean.class || field.getType() == Boolean.class) get = "is";
char[] cols = column.toCharArray();
cols[0] = Character.toUpperCase(cols[0]);
String col2 = new String(cols);
try {
register(type.getMethod(get + col2), entry);
} catch (Exception ex) {
}
try {
register(type.getMethod("set" + col2, field.getType()), entry);
} catch (Exception ex) {
}
return register(field, entry);
} catch (Exception e) {
return false;
}
}
public final <E> boolean register(final AccessibleObject field, final ConvertColumnEntry entry) {
if (field == null || entry == null) return false;
this.columnEntrys.put(field, entry);
return true;
}
public final <E> void register(final Class<E> clazz, final Creator<? extends E> creator) {
creators.put(clazz, creator);
}
public final <T> Creator<T> findCreator(Class<T> type) {
Creator<T> creator = creators.get(type);
if (creator != null) return creator;
return this.parent == null ? null : this.parent.findCreator(type);
}
public final <T> Creator<T> loadCreator(Class<T> type) {
Creator result = findCreator(type);
if (result == null) {
result = Creator.create(type);
creators.put(type, result);
}
return result;
}
//----------------------------------------------------------------------
public final <E> Encodeable<W, E> getAnyEncoder() {
return (Encodeable<W, E>) anyEncoder;
}
public final <E> void register(final Type clazz, final SimpledCoder<R, W, E> coder) {
decoders.put(clazz, coder);
encoders.put(clazz, coder);
}
public final <E> void register(final Type clazz, final Decodeable<R, E> decoder) {
decoders.put(clazz, decoder);
}
public final <E> void register(final Type clazz, final Encodeable<W, E> printer) {
encoders.put(clazz, printer);
}
public final <E> Decodeable<R, E> findDecoder(final Type type) {
Decodeable<R, E> rs = (Decodeable<R, E>) decoders.get(type);
if (rs != null) return rs;
return this.parent == null ? null : this.parent.findDecoder(type);
}
public final <E> Encodeable<W, E> findEncoder(final Type type) {
Encodeable<W, E> rs = (Encodeable<W, E>) encoders.get(type);
if (rs != null) return rs;
return this.parent == null ? null : this.parent.findEncoder(type);
}
public final <E> Decodeable<R, E> loadDecoder(final Type type) {
Decodeable<R, E> decoder = findDecoder(type);
if (decoder != null) return decoder;
if (type instanceof GenericArrayType) return new ArrayDecoder(this, type);
Class clazz;
if (type instanceof ParameterizedType) {
final ParameterizedType pts = (ParameterizedType) type;
clazz = (Class) (pts).getRawType();
} else if (type instanceof Class) {
clazz = (Class) type;
} else {
throw new ConvertException("not support the type (" + type + ")");
}
decoder = findDecoder(clazz);
if (decoder != null) return decoder;
return createDecoder(type, clazz);
}
public final <E> Decodeable<R, E> createDecoder(final Type type) {
Class clazz;
if (type instanceof ParameterizedType) {
final ParameterizedType pts = (ParameterizedType) type;
clazz = (Class) (pts).getRawType();
} else if (type instanceof Class) {
clazz = (Class) type;
} else {
throw new ConvertException("not support the type (" + type + ")");
}
return createDecoder(type, clazz);
}
private <E> Decodeable<R, E> createDecoder(final Type type, final Class clazz) {
Decodeable<R, E> decoder = null;
ObjectDecoder od = null;
if (clazz.isEnum()) {
decoder = new EnumSimpledCoder(clazz);
} else if (clazz.isArray()) {
decoder = new ArrayDecoder(this, type);
} else if (Collection.class.isAssignableFrom(clazz)) {
decoder = new CollectionDecoder(this, type);
} else if (Map.class.isAssignableFrom(clazz)) {
decoder = new MapDecoder(this, type);
} else if (clazz == Object.class) {
od = new ObjectDecoder(type);
decoder = od;
} else if (!clazz.getName().startsWith("java.")) {
od = new ObjectDecoder(type);
decoder = od;
}
if (decoder == null) throw new ConvertException("not support the type (" + type + ")");
register(type, decoder);
if (od != null) od.init(this);
return decoder;
}
public final <E> Encodeable<W, E> loadEncoder(final Type type) {
Encodeable<W, E> encoder = findEncoder(type);
if (encoder != null) return encoder;
if (type instanceof GenericArrayType) return new ArrayEncoder(this, type);
Class clazz;
if (type instanceof ParameterizedType) {
final ParameterizedType pts = (ParameterizedType) type;
clazz = (Class) (pts).getRawType();
} else if (type instanceof TypeVariable) {
TypeVariable tv = (TypeVariable) type;
Type t = Object.class;
if (tv.getBounds().length == 1) {
t = tv.getBounds()[0];
}
if (!(t instanceof Class)) t = Object.class;
clazz = (Class) t;
} else if (type instanceof Class) {
clazz = (Class) type;
} else {
throw new ConvertException("not support the type (" + type + ")");
}
encoder = findEncoder(clazz);
if (encoder != null) return encoder;
return createEncoder(type, clazz);
}
public final <E> Encodeable<W, E> createEncoder(final Type type) {
Class clazz;
if (type instanceof ParameterizedType) {
final ParameterizedType pts = (ParameterizedType) type;
clazz = (Class) (pts).getRawType();
} else if (type instanceof Class) {
clazz = (Class) type;
} else {
throw new ConvertException("not support the type (" + type + ")");
}
return createEncoder(type, clazz);
}
private <E> Encodeable<W, E> createEncoder(final Type type, final Class clazz) {
Encodeable<W, E> encoder = null;
ObjectEncoder oe = null;
if (clazz.isEnum()) {
encoder = new EnumSimpledCoder(clazz);
} else if (clazz.isArray()) {
encoder = new ArrayEncoder(this, type);
} else if (Collection.class.isAssignableFrom(clazz)) {
encoder = new CollectionEncoder(this, type);
} else if (Map.class.isAssignableFrom(clazz)) {
encoder = new MapEncoder(this, type);
} else if (clazz == Object.class) {
return (Encodeable<W, E>) this.anyEncoder;
} else if (!clazz.getName().startsWith("java.")) {
oe = new ObjectEncoder(type);
encoder = oe;
}
if (encoder == null) throw new ConvertException("not support the type (" + type + ")");
register(type, encoder);
if (oe != null) oe.init(this);
return encoder;
}
}

View File

@@ -0,0 +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;
}
}
}

View File

@@ -0,0 +1,78 @@
/*
* 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;
import com.wentch.redkale.util.Creator;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Map;
/**
*
* @author zhangjx
* @param <K>
* @param <V>
*/
@SuppressWarnings("unchecked")
public final class MapDecoder<K, V> implements Decodeable<Reader, Map<K, V>> {
private final Type type;
private final Type keyType;
private final Type valueType;
protected Creator<Map<K, V>> creator;
private final Decodeable<Reader, K> keyDecoder;
private final Decodeable<Reader, V> valueDecoder;
public MapDecoder(final Factory factory, final Type type) {
this.type = type;
if (type instanceof ParameterizedType) {
final ParameterizedType pt = (ParameterizedType) type;
this.keyType = pt.getActualTypeArguments()[0];
this.valueType = pt.getActualTypeArguments()[1];
this.creator = factory.loadCreator((Class) pt.getRawType());
factory.register(type, this);
this.keyDecoder = factory.loadDecoder(this.keyType);
this.valueDecoder = factory.loadDecoder(this.valueType);
} else {
throw new ConvertException("mapdecoder not support the type (" + type + ")");
}
}
@Override
public Map<K, V> convertFrom(Reader in) {
final int len = in.readMapB();
if (len == Reader.SIGN_NULL) return null;
final Map<K, V> result = this.creator.create();
if (len == Reader.SIGN_NOLENGTH) {
while (in.hasNext()) {
K key = keyDecoder.convertFrom(in);
in.skipBlank();
V value = valueDecoder.convertFrom(in);
result.put(key, value);
}
} else {
for (int i = 0; i < len; i++) {
K key = keyDecoder.convertFrom(in);
in.skipBlank();
V value = valueDecoder.convertFrom(in);
result.put(key, value);
}
}
in.readMapE();
return result;
}
@Override
public Type getType() {
return this.type;
}
}

View File

@@ -0,0 +1,62 @@
/*
* 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;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Map;
/**
*
* @author zhangjx
* @param <K>
* @param <V>
*/
@SuppressWarnings("unchecked")
public final class MapEncoder<K, V> implements Encodeable<Writer, Map<K, V>> {
private final Type type;
private final Encodeable<Writer, K> keyencoder;
private final Encodeable<Writer, V> valencoder;
public MapEncoder(final Factory factory, final Type type) {
this.type = type;
if (type instanceof ParameterizedType) {
final Type[] pt = ((ParameterizedType) type).getActualTypeArguments();
this.keyencoder = factory.loadEncoder(pt[0]);
this.valencoder = factory.loadEncoder(pt[1]);
} else {
this.keyencoder = factory.getAnyEncoder();
this.valencoder = factory.getAnyEncoder();
}
}
@Override
public void convertTo(Writer out, Map<K, V> value) {
final Map<K, V> values = value;
if (values == null) {
out.writeNull();
return;
}
out.writeMapB(values.size());
boolean first = true;
for (Map.Entry<K, V> en : values.entrySet()) {
if (!first) out.writeArrayMark();
this.keyencoder.convertTo(out, en.getKey());
out.writeMapMark();
this.valencoder.convertTo(out, en.getValue());
if (first) first = false;
}
out.writeMapE();
}
@Override
public Type getType() {
return type;
}
}

View File

@@ -0,0 +1,144 @@
/*
* 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;
import static com.wentch.redkale.convert.ObjectEncoder.TYPEZERO;
import com.wentch.redkale.util.Creator;
import java.lang.reflect.*;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
/**
*
* @author zhangjx
* @param <R>
* @param <T>
*/
@SuppressWarnings("unchecked")
public final class ObjectDecoder<R extends Reader, T> implements Decodeable<R, T> {
protected final Type type;
protected final Class typeClass;
protected Creator<T> creator;
protected DeMember<R, T, ?>[] members;
protected Factory factory;
protected ObjectDecoder(Type type) {
this.type = type;
if (type instanceof ParameterizedType) {
final ParameterizedType pt = (ParameterizedType) type;
this.typeClass = (Class) pt.getRawType();
} else {
this.typeClass = (Class) type;
}
this.members = new DeMember[0];
}
public void init(final Factory factory) {
this.factory = factory;
if (type == Object.class) return;
Class clazz = null;
if (type instanceof ParameterizedType) {
final ParameterizedType pts = (ParameterizedType) type;
clazz = (Class) (pts).getRawType();
} else if (!(type instanceof Class)) {
throw new ConvertException("[" + type + "] is no a class");
} else {
clazz = (Class) type;
}
final Type[] virGenericTypes = this.typeClass.getTypeParameters();
final Type[] realGenericTypes = (type instanceof ParameterizedType) ? ((ParameterizedType) type).getActualTypeArguments() : TYPEZERO;
this.creator = factory.loadCreator(clazz);
final Set<DeMember> list = new HashSet<>();
try {
ConvertColumnEntry ref;
for (final Field field : clazz.getFields()) {
if (Modifier.isStatic(field.getModifiers())) continue;
ref = factory.findRef(field);
if (ref != null && ref.ignore()) continue;
Type t = ObjectEncoder.makeGenericType(field.getGenericType(), virGenericTypes, realGenericTypes);
list.add(new DeMember(ObjectEncoder.createAttribute(factory, clazz, field, null, null), factory.loadDecoder(t)));
}
final boolean reversible = factory.isReversible();
for (final Method method : clazz.getMethods()) {
if (Modifier.isStatic(method.getModifiers())) continue;
if (Modifier.isAbstract(method.getModifiers())) continue;
if (method.isSynthetic()) continue;
if (method.getName().length() < 4) continue;
if (!method.getName().startsWith("set")) continue;
if (method.getParameterTypes().length != 1) continue;
if (method.getReturnType() != void.class) continue;
if (reversible) {
boolean is = method.getParameterTypes()[0] == boolean.class || method.getParameterTypes()[0] == Boolean.class;
try {
clazz.getMethod(method.getName().replaceFirst("set", is ? "is" : "get"));
} catch (Exception e) {
continue;
}
}
ref = factory.findRef(method);
if (ref != null && ref.ignore()) continue;
Type t = ObjectEncoder.makeGenericType(method.getGenericParameterTypes()[0], virGenericTypes, realGenericTypes);
list.add(new DeMember(ObjectEncoder.createAttribute(factory, clazz, null, null, method), factory.loadDecoder(t)));
}
this.members = list.toArray(new DeMember[list.size()]);
Arrays.sort(this.members);
} catch (Exception ex) {
throw new ConvertException(ex);
}
}
/**
* 对象格式: [0x1][short字段个数][字段名][字段值]...[0x2]
*
* @param in
* @return
*/
@Override
public final T convertFrom(final R in) {
final String clazz = in.readClassName();
if (clazz != null && !clazz.isEmpty()) {
try {
return (T) factory.loadDecoder(Class.forName(clazz)).convertFrom(in);
} catch (Exception ex) {
throw new ConvertException(ex);
}
}
if (in.readObjectB() == Reader.SIGN_NULL) return null;
final T result = this.creator.create();
final AtomicInteger index = new AtomicInteger();
while (in.hasNext()) {
DeMember member = in.readField(index, members);
in.skipBlank();
if (member == null) {
in.skipValue(); //跳过该属性的值
} else {
member.read(in, result);
}
index.incrementAndGet();
}
in.readObjectE();
return result;
}
@Override
public final Type getType() {
return this.type;
}
@Override
public String toString() {
return "ObjectDecoder{" + "type=" + type + ", members=" + Arrays.toString(members) + '}';
}
}

View File

@@ -0,0 +1,213 @@
/*
* 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;
import com.wentch.redkale.util.Attribute;
import java.lang.reflect.*;
import java.util.*;
/**
*
* @author zhangjx
* @param <W>
* @param <T>
*/
@SuppressWarnings("unchecked")
public final class ObjectEncoder<W extends Writer, T> implements Encodeable<W, T> {
static final Type[] TYPEZERO = new Type[0];
protected final Type type;
protected final Class typeClass;
protected EnMember[] members;
protected Factory factory;
protected ObjectEncoder(Type type) {
this.type = type;
if (type instanceof ParameterizedType) {
final ParameterizedType pt = (ParameterizedType) type;
this.typeClass = (Class) pt.getRawType();
} else {
this.typeClass = (Class) type;
}
this.members = new EnMember[0];
}
static Type makeGenericType(final Type type, final Type[] virGenericTypes, final Type[] realGenericTypes) {
if (type instanceof Class) {
return type;
} else if (type instanceof ParameterizedType) {
final ParameterizedType pt = (ParameterizedType) type;
Type[] paramTypes = pt.getActualTypeArguments();
final Type[] newTypes = new Type[paramTypes.length];
int count = 0;
for (int i = 0; i < newTypes.length; i++) {
newTypes[i] = makeGenericType(paramTypes[i], virGenericTypes, realGenericTypes);
if (paramTypes[i] == newTypes[i]) count++;
}
if (count == paramTypes.length) return pt;
return new ParameterizedType() {
@Override
public Type[] getActualTypeArguments() {
return newTypes;
}
@Override
public Type getRawType() {
return pt.getRawType();
}
@Override
public Type getOwnerType() {
return pt.getOwnerType();
}
};
}
if (realGenericTypes == null) return type;
if (type instanceof WildcardType) {
final WildcardType wt = (WildcardType) type;
for (Type f : wt.getUpperBounds()) {
for (int i = 0; i < virGenericTypes.length; i++) {
if (virGenericTypes[i] == f) return realGenericTypes.length == 0 ? Object.class : realGenericTypes[i];
}
}
} else if (type instanceof TypeVariable) {
for (int i = 0; i < virGenericTypes.length; i++) {
if (virGenericTypes[i] == type) return i >= realGenericTypes.length ? Object.class : realGenericTypes[i];
}
}
return type;
}
private static String readGetSetFieldName(Method method) {
if (method == null) return null;
String fname = method.getName();
if (!fname.startsWith("is") && !fname.startsWith("get") && !fname.startsWith("set")) return fname;
fname = fname.substring(fname.startsWith("is") ? 2 : 3);
if (fname.length() > 1 && !(fname.charAt(1) >= 'A' && fname.charAt(1) <= 'Z')) {
fname = Character.toLowerCase(fname.charAt(0)) + fname.substring(1);
} else if (fname.length() == 1) {
fname = "" + Character.toLowerCase(fname.charAt(0));
}
return fname;
}
static Attribute createAttribute(final Factory factory, Class clazz, final Field field, final Method getter, final Method setter) {
String fieldalias = null;
if (field != null) { // public field
ConvertColumnEntry ref = factory.findRef(field);
fieldalias = ref == null || ref.name().isEmpty() ? field.getName() : ref.name();
} else if (getter != null) {
ConvertColumnEntry ref = factory.findRef(getter);
String mfieldname = readGetSetFieldName(getter);
if (ref == null) {
try {
ref = factory.findRef(clazz.getDeclaredField(mfieldname));
} catch (Exception e) {
}
}
fieldalias = ref == null || ref.name().isEmpty() ? mfieldname : ref.name();
} else { // setter != null
ConvertColumnEntry ref = factory.findRef(setter);
String mfieldname = readGetSetFieldName(setter);
if (ref == null) {
try {
ref = factory.findRef(clazz.getDeclaredField(mfieldname));
} catch (Exception e) {
}
}
fieldalias = ref == null || ref.name().isEmpty() ? mfieldname : ref.name();
}
return Attribute.create(clazz, fieldalias, field, getter, setter);
}
public void init(final Factory factory) {
this.factory = factory;
if (type == Object.class) return;
//if (!(type instanceof Class)) throw new ConvertException("[" + type + "] is no a class");
final Class clazz = this.typeClass;
final Set<EnMember> list = new HashSet<>();
final Type[] virGenericTypes = this.typeClass.getTypeParameters();
final Type[] realGenericTypes = (type instanceof ParameterizedType) ? ((ParameterizedType) type).getActualTypeArguments() : null;
if (realGenericTypes != null) {
// println(type + "," + Arrays.toString(virGenericTypes) + ", " + Arrays.toString(realGenericTypes));
}
try {
ConvertColumnEntry ref;
for (final Field field : clazz.getFields()) {
if (Modifier.isStatic(field.getModifiers())) continue;
ref = factory.findRef(field);
if (ref != null && ref.ignore()) continue;
Type t = makeGenericType(field.getGenericType(), virGenericTypes, realGenericTypes);
list.add(new EnMember(createAttribute(factory, clazz, field, null, null), factory.loadEncoder(t)));
}
final boolean reversible = factory.isReversible();
for (final Method method : clazz.getMethods()) {
if (Modifier.isStatic(method.getModifiers())) continue;
if (Modifier.isAbstract(method.getModifiers())) continue;
if (method.isSynthetic()) continue;
if (method.getName().length() < 3) continue;
if (method.getName().equals("getClass")) continue;
if (!method.getName().startsWith("is") && !method.getName().startsWith("get")) continue;
if (method.getParameterTypes().length != 0) continue;
if (method.getReturnType() == void.class) continue;
if (reversible) {
boolean is = method.getName().startsWith("is");
try {
clazz.getMethod(method.getName().replaceFirst(is ? "is" : "get", "set"), method.getReturnType());
} catch (Exception e) {
continue;
}
}
ref = factory.findRef(method);
if (ref != null && ref.ignore()) continue;
Type t = makeGenericType(method.getGenericReturnType(), virGenericTypes, realGenericTypes);
list.add(new EnMember(createAttribute(factory, clazz, null, method, null), factory.loadEncoder(t)));
}
this.members = list.toArray(new EnMember[list.size()]);
Arrays.sort(this.members);
} catch (Exception ex) {
throw new ConvertException(ex);
}
}
@Override
public final void convertTo(W out, T value) {
if (value == null) {
out.wirteClassName(null);
out.writeNull();
return;
}
if (value != null && value.getClass() != this.typeClass) {
final Class clz = value.getClass();
out.wirteClassName(clz);
factory.loadEncoder(clz).convertTo(out, value);
return;
}
out.writeObjectB(members.length, value);
boolean comma = false;
for (EnMember member : members) {
comma = member.write(out, comma, value);
}
out.writeObjectE(value);
}
@Override
public final Type getType() {
return this.type;
}
@Override
public String toString() {
return "ObjectEncoder{" + "type=" + type + ", members=" + Arrays.toString(members) + '}';
}
}

View File

@@ -0,0 +1,112 @@
/*
* 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;
import java.util.concurrent.atomic.AtomicInteger;
/**
*
* @author zhangjx
*/
public interface Reader {
public static final short SIGN_NULL = -1;
public static final short SIGN_NOLENGTH = -2;
/**
* 是否还存在下个元素或字段
*
* @return
*/
public boolean hasNext();
/**
* 跳过值(不包含值前面的字段)
*/
public void skipValue();
/**
* /跳过字段与值之间的多余内容, json就是跳过:符, map跳过:
*/
public void skipBlank();
/**
* 读取对象的开头 返回字段数
*
* @return
*/
public int readObjectB();
/**
* 读取对象的尾端
*
*/
public void readObjectE();
/**
* 读取数组的开头并返回数组的长度
*
* @return
*/
public int readArrayB();
/**
* 读取数组的尾端
*
*/
public void readArrayE();
/**
* 读取map的开头并返回map的size
*
* @return
*/
public int readMapB();
/**
* 读取数组的尾端
*
*/
public void readMapE();
/**
* 根据字段读取字段对应的DeMember
*
* @param index
* @param members
* @return
*/
public DeMember readField(final AtomicInteger index, final DeMember[] members);
public boolean readBoolean();
public byte readByte();
public char readChar();
public short readShort();
public int readInt();
public long readLong();
public float readFloat();
public double readDouble();
/**
* 读取无转义字符长度不超过255的字符串 例如枚举值、字段名、类名字符串等
*
* @return
*/
public String readSmallString();
public String readClassName();
public String readString();
}

View File

@@ -0,0 +1,42 @@
/*
* 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;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
/**
*
* @author zhangjx
* @param <R>
* @param <W>
* @param <T>
*/
public abstract class SimpledCoder<R extends Reader, W extends Writer, T> implements Decodeable<R, T>, Encodeable<W, T> {
private Type type;
@Override
public abstract void convertTo(final W out, final T value);
@Override
public abstract T convertFrom(final R in);
@Override
@SuppressWarnings("unchecked")
public Class<T> getType() {
if (type == null) {
Type[] ts = ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments();
type = ts[ts.length - 1];
}
return (Class<T>) type;
}
@Override
public String toString() {
return this.getClass().getSimpleName();
}
}

View File

@@ -0,0 +1,113 @@
/*
* 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;
import com.wentch.redkale.util.Attribute;
/**
*
* @author zhangjx
*/
public interface Writer {
/**
* 输出null值
*/
public void writeNull();
/**
*
* @param clazz
*/
public void wirteClassName(Class clazz);
/**
* 输出一个对象前的操作
*
* @param fieldCount 字段个数
*
* @param obj
*/
public void writeObjectB(int fieldCount, Object obj);
/**
* 输出一个对象后的操作
*
* @param obj
*/
public void writeObjectE(Object obj);
/**
* 输出一个数组前的操作
*
* @param size 数组长度
*/
public void writeArrayB(int size);
/**
* 输出数组元素间的间隔符
*
*/
public void writeArrayMark();
/**
* 输出一个数组后的操作
*
*/
public void writeArrayE();
/**
* 输出一个Map前的操作
*
* @param size map大小
*/
public void writeMapB(int size);
/**
* 输出一个Map中key与value间的间隔符
*
*/
public void writeMapMark();
/**
* 输出一个Map后的操作
*
*/
public void writeMapE();
/**
* 输出一个字段
*
* @param comma 是否非第一个字段
* @param attribute
*/
public void writeField(boolean comma, Attribute attribute);
public void writeBoolean(boolean value);
public void writeByte(byte value);
public void writeChar(char value);
public void writeShort(short value);
public void writeInt(int value);
public void writeLong(long value);
public void writeFloat(float value);
public void writeDouble(double value);
/**
* 写入无转义字符长度不超过255的字符串 例如枚举值、字段名、类名字符串等 *
*
* @param value
*/
public void writeSmallString(String value);
public void writeString(String value);
}

View File

@@ -0,0 +1,61 @@
/*
* 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.bson;
import com.wentch.redkale.convert.Convert;
import com.wentch.redkale.convert.Factory;
import com.wentch.redkale.util.ObjectPool;
import java.lang.reflect.Type;
/**
*
* @author zhangjx
*/
public final class BsonConvert extends Convert<BsonReader, BsonWriter> {
private static final ObjectPool<BsonReader> readerPool = new ObjectPool<>(Integer.getInteger("convert.bson.pool.size", 16), BsonReader.class);
private static final ObjectPool<BsonWriter> writerPool = new ObjectPool<>(Integer.getInteger("convert.bson.pool.size", 16), BsonWriter.class);
protected BsonConvert(Factory<BsonReader, BsonWriter> factory) {
super(factory);
}
public <T> T convertFrom(final Type type, final byte[] bytes) {
if (bytes == null) return null;
return convertFrom(type, bytes, 0, bytes.length);
}
public <T> T convertFrom(final Type type, final byte[] bytes, int start, int len) {
if (type == null) return null;
final BsonReader in = readerPool.poll();
in.setBytes(bytes, start, len);
@SuppressWarnings("unchecked")
T rs = (T) factory.loadDecoder(type).convertFrom(in);
readerPool.offer(in);
return rs;
}
public byte[] convertTo(final Type type, Object value) {
if (type == null) return null;
final BsonWriter out = writerPool.poll();
factory.loadEncoder(type).convertTo(out, value);
byte[] result = out.toArray();
writerPool.offer(out);
return result;
}
public byte[] convertTo(Object value) {
if (value == null) {
final BsonWriter out = writerPool.poll();
out.writeNull();
byte[] result = out.toArray();
writerPool.offer(out);
return result;
}
return convertTo(value.getClass(), value);
}
}

View File

@@ -0,0 +1,54 @@
/*
* 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.bson;
import com.wentch.redkale.convert.ConvertType;
import com.wentch.redkale.convert.Factory;
import java.io.Serializable;
/**
*
* @author zhangjx
*/
public final class BsonFactory extends Factory<BsonReader, BsonWriter> {
private static final BsonFactory instance = new BsonFactory(null);
static {
instance.register(Serializable.class, instance.loadDecoder(Object.class));
instance.register(Serializable.class, instance.loadEncoder(Object.class));
}
private BsonFactory(BsonFactory parent) {
super(parent);
this.convert = new BsonConvert(this);
}
public static BsonFactory root() {
return instance;
}
@Override
public final BsonConvert getConvert() {
return (BsonConvert) convert;
}
@Override
public BsonFactory createChild() {
return new BsonFactory(this);
}
@Override
public ConvertType getConvertType() {
return ConvertType.BSON;
}
@Override
public boolean isReversible() {
return true;
}
}

View File

@@ -0,0 +1,245 @@
/*
* 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.bson;
import com.wentch.redkale.convert.ConvertException;
import com.wentch.redkale.convert.DeMember;
import com.wentch.redkale.convert.Reader;
import com.wentch.redkale.util.ObjectPool.Poolable;
import com.wentch.redkale.util.Utility;
import java.util.concurrent.atomic.AtomicInteger;
/**
*
* @author zhangjx
*/
public final class BsonReader implements Reader, Poolable {
public static final short SIGN_OBJECTB = (short) 0xBB;
public static final short SIGN_OBJECTE = (short) 0xEE;
public static final byte SIGN_HASNEXT = 1;
public static final byte SIGN_NONEXT = 0;
public static final byte VERBOSE_NO = 1;
public static final byte VERBOSE_YES = 2;
private int position = -1;
private byte[] content;
public BsonReader() {
}
public BsonReader(byte[] bytes) {
setBytes(bytes, 0, bytes.length);
}
public BsonReader(byte[] bytes, int start, int len) {
setBytes(bytes, start, len);
}
public final void setBytes(byte[] bytes) {
setBytes(bytes, 0, bytes.length);
}
public final void setBytes(byte[] bytes, int start, int len) {
this.content = bytes;
this.position = start - 1;
//this.limit = start + len - 1;
}
@Override
public void prepare() {
}
@Override
public void release() {
this.position = -1;
//this.limit = -1;
this.content = null;
}
public void close() {
this.release();
}
/**
* 跳过属性的值
*/
@Override
public final void skipValue() {
}
/**
* 判断下一个非空白字节是否为{
*
*/
@Override
public int readObjectB() {
short bt = readShort();
if (bt == Reader.SIGN_NULL) return bt;
if (bt != SIGN_OBJECTB) {
throw new ConvertException("a bson object must begin with " + (SIGN_OBJECTB)
+ " (position = " + position + ") but '" + this.content[this.position] + "'");
}
return bt;
}
@Override
public void readObjectE() {
if (readShort() != SIGN_OBJECTE) {
throw new ConvertException("a bson object must end with " + (SIGN_OBJECTE)
+ " (position = " + position + ") but '" + this.content[this.position] + "'");
}
}
@Override
public int readMapB() {
return readArrayB();
}
@Override
public void readMapE() {
}
/**
* 判断下一个非空白字节是否为[
*
* @return
*/
@Override
public int readArrayB() {
return readShort();
}
@Override
public void readArrayE() {
}
/**
* 判断下一个非空白字节是否:
*/
@Override
public void skipBlank() {
}
/**
* 判断对象是否存在下一个属性或者数组是否存在下一个元素
*
* @return
*/
@Override
public boolean hasNext() {
byte b = readByte();
if (b == SIGN_HASNEXT) return true;
if (b != SIGN_NONEXT) throw new ConvertException("hasNext option must be (" + (SIGN_HASNEXT)
+ " or " + (SIGN_NONEXT) + ") but '" + b + "' at position(" + this.position + ")");
return false;
}
@Override
public DeMember readField(final AtomicInteger index, final DeMember[] members) {
final String exceptedfield = readSmallString();
final int len = members.length;
int v = index.get();
if (v >= len) {
v = 0;
index.set(0);
}
for (int k = v; k < len; k++) {
if (exceptedfield.equals(members[k].getAttribute().field())) {
index.set(k);
return members[k];
}
}
for (int k = 0; k < v; k++) {
if (exceptedfield.equals(members[k].getAttribute().field())) {
index.set(k);
return members[k];
}
}
return null;
}
//------------------------------------------------------------
@Override
public boolean readBoolean() {
return content[++this.position] == 1;
}
@Override
public byte readByte() {
return content[++this.position];
}
@Override
public char readChar() {
return (char) ((0xff00 & (content[++this.position] << 8)) | (0xff & content[++this.position]));
}
@Override
public short readShort() {
return (short) ((0xff00 & (content[++this.position] << 8)) | (0xff & content[++this.position]));
}
@Override
public int readInt() {
return ((content[++this.position] & 0xff) << 24) | ((content[++this.position] & 0xff) << 16)
| ((content[++this.position] & 0xff) << 8) | (content[++this.position] & 0xff);
}
@Override
public long readLong() {
return ((((long) content[++this.position] & 0xff) << 56)
| (((long) content[++this.position] & 0xff) << 48)
| (((long) content[++this.position] & 0xff) << 40)
| (((long) content[++this.position] & 0xff) << 32)
| (((long) content[++this.position] & 0xff) << 24)
| (((long) content[++this.position] & 0xff) << 16)
| (((long) content[++this.position] & 0xff) << 8)
| (((long) content[++this.position] & 0xff)));
}
@Override
public float readFloat() {
return Float.intBitsToFloat(readInt());
}
@Override
public double readDouble() {
return Double.longBitsToDouble(readLong());
}
@Override
public String readClassName() {
return readSmallString();
}
@Override
public String readSmallString() {
int len = 0xff & readByte();
if (len == 0) return "";
String value = new String(content, ++this.position, len);
this.position += len - 1;
return value;
}
@Override
public String readString() {
int len = readInt();
if (len == SIGN_NULL) return null;
if (len == 0) return "";
String value = new String(Utility.decodeUTF8(content, ++this.position, len));
this.position += len - 1;
return value;
}
}

View File

@@ -0,0 +1,17 @@
/*
* 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.bson;
import com.wentch.redkale.convert.SimpledCoder;
/**
*
* @author zhangjx
* @param <T>
*/
public abstract class BsonSimpledCoder<T> extends SimpledCoder<BsonReader, BsonWriter, T> {
}

View File

@@ -0,0 +1,236 @@
/*
* 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.bson;
import com.wentch.redkale.util.Utility;
import com.wentch.redkale.util.Attribute;
import com.wentch.redkale.convert.ConvertException;
import com.wentch.redkale.convert.Reader;
import com.wentch.redkale.convert.Writer;
import com.wentch.redkale.util.ObjectPool.Poolable;
/**
*
* @author zhangjx
*/
public final class BsonWriter implements Writer, Poolable {
private static final int defaultSize = Integer.getInteger("convert.bson.writer.buffer.defsize", 1024);
protected int count;
private byte[] content;
public byte[] toArray() {
if (count == content.length) return content;
byte[] newdata = new byte[count];
System.arraycopy(content, 0, newdata, 0, count);
return newdata;
}
public BsonWriter() {
this(defaultSize);
}
public BsonWriter(int size) {
this.content = new byte[size > 32 ? size : 32];
}
//-----------------------------------------------------------------------
//-----------------------------------------------------------------------
/**
* 返回指定至少指定长度的缓冲区
*
* @param len
* @return
*/
public byte[] expand(int len) {
int newcount = count + len;
if (newcount <= content.length) return content;
byte[] newdata = new byte[Math.max(content.length * 3 / 2, newcount)];
System.arraycopy(content, 0, newdata, 0, count);
this.content = newdata;
return newdata;
}
public void writeTo(final byte ch) {
expand(1);
content[count++] = ch;
}
public void writeTo(final byte... chs) {
int len = chs.length;
expand(len);
System.arraycopy(chs, 0, content, count, len);
count += len;
}
public void writeTo(final byte[] chs, final int start, final int end) {
int len = end - start;
expand(len);
System.arraycopy(chs, start, content, count, len);
count += len;
}
@Override
public void prepare() {
}
@Override
public void release() {
this.count = 0;
if (this.content.length > defaultSize) {
this.content = new byte[defaultSize];
}
}
//------------------------------------------------------------------------
public final int count() {
return this.count;
}
public final void count(int count) {
if (count >= 0) this.count = count;
}
//-----------------------------------------------------------------------
@Override
public String toString() {
return new String(content, 0, count);
}
@Override
public void writeBoolean(boolean value) {
writeTo(value ? (byte) 1 : (byte) 0);
}
@Override
public void writeByte(byte value) {
writeTo(value);
}
@Override
public void writeChar(final char value) {
writeTo((byte) ((value & 0xFF00) >> 8), (byte) (value & 0xFF));
}
@Override
public void writeShort(short value) {
writeTo((byte) (value >> 8), (byte) value);
}
@Override
public void writeInt(int value) {
writeTo((byte) (value >> 24), (byte) (value >> 16), (byte) (value >> 8), (byte) value);
}
@Override
public void writeLong(long value) {
writeTo((byte) (value >> 56), (byte) (value >> 48), (byte) (value >> 40), (byte) (value >> 32),
(byte) (value >> 24), (byte) (value >> 16), (byte) (value >> 8), (byte) value);
}
@Override
public void writeFloat(float value) {
writeInt(Float.floatToIntBits(value));
}
@Override
public void writeDouble(double value) {
writeLong(Double.doubleToLongBits(value));
}
@Override
public void wirteClassName(Class clazz) {
writeSmallString(clazz == null ? "" : clazz.getName());
}
@Override
public final void writeObjectB(int fieldCount, Object obj) {
writeSmallString("");
writeShort(BsonReader.SIGN_OBJECTB);
}
@Override
public final void writeObjectE(Object obj) {
writeByte(BsonReader.SIGN_NONEXT);
writeShort(BsonReader.SIGN_OBJECTE);
}
@Override
public final void writeField(boolean comma, Attribute attribute) {
writeByte(BsonReader.SIGN_HASNEXT);
writeSmallString(attribute.field());
}
/**
* 对于类的字段名、枚举值这些长度一般不超过255且不会出现双字节字符的字符串采用writeSmallString处理, readSmallString用于读取
*
* @param value
*/
@Override
public void writeSmallString(String value) {
if (value.isEmpty()) {
writeTo((byte) 0);
return;
}
char[] chars = Utility.charArray(value);
if (chars.length > 255) throw new ConvertException("'" + value + "' has very long length");
byte[] bytes = new byte[chars.length + 1];
bytes[0] = (byte) chars.length;
for (int i = 0; i < chars.length; i++) {
if (chars[i] > Byte.MAX_VALUE) throw new ConvertException("'" + value + "' has double-word");
bytes[i + 1] = (byte) chars[i];
}
writeTo(bytes);
}
@Override
public void writeString(String value) {
if (value == null) {
writeInt(Reader.SIGN_NULL);
return;
} else if (value.isEmpty()) {
writeInt(0);
return;
}
byte[] bytes = Utility.encodeUTF8(value);
writeInt(bytes.length);
writeTo(bytes);
}
@Override
public final void writeNull() {
writeShort(Reader.SIGN_NULL);
}
@Override
public void writeArrayB(int size) {
writeShort((short) size);
}
@Override
public void writeArrayMark() {
}
@Override
public void writeArrayE() {
}
@Override
public void writeMapB(int size) {
writeArrayB(size);
}
@Override
public void writeMapMark() {
}
@Override
public void writeMapE() {
}
}

View File

@@ -0,0 +1,38 @@
/*
* 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.ext;
import com.wentch.redkale.convert.SimpledCoder;
import com.wentch.redkale.convert.Writer;
import com.wentch.redkale.convert.Reader;
import java.math.BigInteger;
/**
*
* @author zhangjx
* @param <R>
* @param <W>
*/
public final class BigIntegerSimpledCoder<R extends Reader, W extends Writer> extends SimpledCoder<R, W, BigInteger> {
public static final BigIntegerSimpledCoder instance = new BigIntegerSimpledCoder();
@Override
public void convertTo(W out, BigInteger value) {
if (value == null) {
out.writeNull();
return;
}
ByteArraySimpledCoder.instance.convertTo(out, value.toByteArray());
}
@Override
public BigInteger convertFrom(R in) {
byte[] bytes = ByteArraySimpledCoder.instance.convertFrom(in);
return bytes == null ? null : new BigInteger(bytes);
}
}

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.ext;
import com.wentch.redkale.convert.Reader;
import com.wentch.redkale.convert.SimpledCoder;
import com.wentch.redkale.convert.Writer;
/**
*
* @author zhangjx
* @param <R>
* @param <W>
*/
public final class BoolArraySimpledCoder<R extends Reader, W extends Writer> extends SimpledCoder<R, W, boolean[]> {
public static final BoolArraySimpledCoder instance = new BoolArraySimpledCoder();
@Override
public void convertTo(W out, boolean[] values) {
if (values == null) {
out.writeNull();
return;
}
out.writeArrayB(values.length);
boolean flag = false;
for (boolean v : values) {
if (flag) out.writeArrayMark();
out.writeBoolean(v);
flag = true;
}
out.writeArrayE();
}
@Override
public boolean[] convertFrom(R in) {
int len = in.readArrayB();
if (len == Reader.SIGN_NULL) {
return null;
} else if (len == Reader.SIGN_NOLENGTH) {
int size = 0;
boolean[] data = new boolean[8];
while (in.hasNext()) {
if (size >= data.length) {
boolean[] newdata = new boolean[data.length + 4];
System.arraycopy(data, 0, newdata, 0, size);
data = newdata;
}
data[size++] = in.readBoolean();
}
in.readArrayE();
boolean[] newdata = new boolean[size];
System.arraycopy(data, 0, newdata, 0, size);
return newdata;
} else {
boolean[] values = new boolean[len];
for (int i = 0; i < values.length; i++) {
values[i] = in.readBoolean();
}
in.readArrayE();
return values;
}
}
}

View File

@@ -0,0 +1,32 @@
/*
* 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.ext;
import com.wentch.redkale.convert.Reader;
import com.wentch.redkale.convert.SimpledCoder;
import com.wentch.redkale.convert.Writer;
/**
*
* @author zhangjx
* @param <R>
* @param <W>
*/
public final class BoolSimpledCoder<R extends Reader, W extends Writer> extends SimpledCoder<R, W, Boolean> {
public static final BoolSimpledCoder instance = new BoolSimpledCoder();
@Override
public void convertTo(W out, Boolean value) {
out.writeBoolean(value);
}
@Override
public Boolean convertFrom(R in) {
return in.readBoolean();
}
}

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.ext;
import com.wentch.redkale.convert.Reader;
import com.wentch.redkale.convert.SimpledCoder;
import com.wentch.redkale.convert.Writer;
/**
*
* @author zhangjx
* @param <R>
* @param <W>
*/
public final class ByteArraySimpledCoder<R extends Reader, W extends Writer> extends SimpledCoder<R, W, byte[]> {
public static final ByteArraySimpledCoder instance = new ByteArraySimpledCoder();
@Override
public void convertTo(W out, byte[] values) {
if (values == null) {
out.writeNull();
return;
}
out.writeArrayB(values.length);
boolean flag = false;
for (byte v : values) {
if (flag) out.writeArrayMark();
out.writeByte(v);
flag = true;
}
out.writeArrayE();
}
@Override
public byte[] convertFrom(R in) {
int len = in.readArrayB();
if (len == Reader.SIGN_NULL) {
return null;
} else if (len == Reader.SIGN_NOLENGTH) {
int size = 0;
byte[] data = new byte[8];
while (in.hasNext()) {
if (size >= data.length) {
byte[] newdata = new byte[data.length + 4];
System.arraycopy(data, 0, newdata, 0, size);
data = newdata;
}
data[size++] = in.readByte();
}
in.readArrayE();
byte[] newdata = new byte[size];
System.arraycopy(data, 0, newdata, 0, size);
return newdata;
} else {
byte[] values = new byte[len];
for (int i = 0; i < values.length; i++) {
values[i] = in.readByte();
}
in.readArrayE();
return values;
}
}
}

View File

@@ -0,0 +1,32 @@
/*
* 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.ext;
import com.wentch.redkale.convert.Reader;
import com.wentch.redkale.convert.SimpledCoder;
import com.wentch.redkale.convert.Writer;
/**
*
* @author zhangjx
* @param <R>
* @param <W>
*/
public final class ByteSimpledCoder<R extends Reader, W extends Writer> extends SimpledCoder<R, W, Byte> {
public static final ByteSimpledCoder instance = new ByteSimpledCoder();
@Override
public void convertTo(W out, Byte value) {
out.writeByte(value);
}
@Override
public Byte convertFrom(R in) {
return in.readByte();
}
}

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.ext;
import com.wentch.redkale.convert.Reader;
import com.wentch.redkale.convert.SimpledCoder;
import com.wentch.redkale.convert.Writer;
/**
*
* @author zhangjx
* @param <R>
* @param <W>
*/
public final class CharArraySimpledCoder<R extends Reader, W extends Writer> extends SimpledCoder<R, W, char[]> {
public static final CharArraySimpledCoder instance = new CharArraySimpledCoder();
@Override
public void convertTo(W out, char[] values) {
if (values == null) {
out.writeNull();
return;
}
out.writeArrayB(values.length);
boolean flag = false;
for (char v : values) {
if (flag) out.writeArrayMark();
out.writeChar(v);
flag = true;
}
out.writeArrayE();
}
@Override
public char[] convertFrom(R in) {
int len = in.readArrayB();
if (len == Reader.SIGN_NULL) {
return null;
} else if (len == Reader.SIGN_NOLENGTH) {
int size = 0;
char[] data = new char[8];
while (in.hasNext()) {
if (size >= data.length) {
char[] newdata = new char[data.length + 4];
System.arraycopy(data, 0, newdata, 0, size);
data = newdata;
}
data[size++] = in.readChar();
}
in.readArrayE();
char[] newdata = new char[size];
System.arraycopy(data, 0, newdata, 0, size);
return newdata;
} else {
char[] values = new char[len];
for (int i = 0; i < values.length; i++) {
values[i] = in.readChar();
}
in.readArrayE();
return values;
}
}
}

View File

@@ -0,0 +1,33 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package com.wentch.redkale.convert.ext;
import com.wentch.redkale.convert.Reader;
import com.wentch.redkale.convert.SimpledCoder;
import com.wentch.redkale.convert.Writer;
/**
*
* @author zhangjx
* @param <R>
* @param <W>
*/
public final class CharSimpledCoder<R extends Reader, W extends Writer> extends SimpledCoder<R, W, Character> {
public static final CharSimpledCoder instance = new CharSimpledCoder();
@Override
public void convertTo(W out, Character value) {
out.writeChar(value);
}
@Override
public Character convertFrom(R in) {
return in.readChar();
}
}

View File

@@ -0,0 +1,33 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package com.wentch.redkale.convert.ext;
import com.wentch.redkale.convert.Reader;
import com.wentch.redkale.convert.SimpledCoder;
import com.wentch.redkale.convert.Writer;
import java.util.Date;
/**
*
* @author zhangjx
* @param <R>
* @param <W>
*/
public final class DateSimpledCoder<R extends Reader, W extends Writer> extends SimpledCoder<R, W, Date> {
public static final DateSimpledCoder instance = new DateSimpledCoder();
@Override
public void convertTo(W out, Date value) {
out.writeLong(value.getTime());
}
@Override
public Date convertFrom(R in) {
return new Date(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.ext;
import com.wentch.redkale.convert.Reader;
import com.wentch.redkale.convert.SimpledCoder;
import com.wentch.redkale.convert.Writer;
/**
*
* @author zhangjx
* @param <R>
* @param <W>
*/
public final class DoubleArraySimpledCoder<R extends Reader, W extends Writer> extends SimpledCoder<R, W, double[]> {
public static final DoubleArraySimpledCoder instance = new DoubleArraySimpledCoder();
@Override
public void convertTo(W out, double[] values) {
if (values == null) {
out.writeNull();
return;
}
out.writeArrayB(values.length);
boolean flag = false;
for (double v : values) {
if (flag) out.writeArrayMark();
out.writeDouble(v);
flag = true;
}
out.writeArrayE();
}
@Override
public double[] convertFrom(R in) {
int len = in.readArrayB();
if (len == Reader.SIGN_NULL) {
return null;
} else if (len == Reader.SIGN_NOLENGTH) {
int size = 0;
double[] data = new double[8];
while (in.hasNext()) {
if (size >= data.length) {
double[] newdata = new double[data.length + 4];
System.arraycopy(data, 0, newdata, 0, size);
data = newdata;
}
data[size++] = in.readDouble();
}
in.readArrayE();
double[] newdata = new double[size];
System.arraycopy(data, 0, newdata, 0, size);
return newdata;
} else {
double[] values = new double[len];
for (int i = 0; i < values.length; i++) {
values[i] = in.readDouble();
}
in.readArrayE();
return values;
}
}
}

View File

@@ -0,0 +1,32 @@
/*
* 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.ext;
import com.wentch.redkale.convert.Reader;
import com.wentch.redkale.convert.SimpledCoder;
import com.wentch.redkale.convert.Writer;
/**
*
* @author zhangjx
* @param <R>
* @param <W>
*/
public final class DoubleSimpledCoder<R extends Reader, W extends Writer> extends SimpledCoder<R, W, Double> {
public static final DoubleSimpledCoder instance = new DoubleSimpledCoder();
@Override
public void convertTo(W out, Double value) {
out.writeDouble(value);
}
@Override
public Double convertFrom(R in) {
return in.readDouble();
}
}

View File

@@ -0,0 +1,44 @@
/*
* 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.ext;
import com.wentch.redkale.convert.Reader;
import com.wentch.redkale.convert.SimpledCoder;
import com.wentch.redkale.convert.Writer;
/**
*
* @author zhangjx
* @param <R>
* @param <W>
* @param <E>
*/
public final class EnumSimpledCoder<R extends Reader, W extends Writer, E extends Enum> extends SimpledCoder<R, W, E> {
private final Class<E> type;
public EnumSimpledCoder(Class<E> type) {
this.type = type;
}
@Override
public void convertTo(final W out, final E value) {
if (value == null) {
out.writeNull();
} else {
out.writeSmallString(value.toString());
}
}
@Override
@SuppressWarnings("unchecked")
public E convertFrom(final R in) {
String value = in.readSmallString();
if (value == null) return null;
return (E) Enum.valueOf(type, value);
}
}

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.ext;
import com.wentch.redkale.convert.Reader;
import com.wentch.redkale.convert.SimpledCoder;
import com.wentch.redkale.convert.Writer;
/**
*
* @author zhangjx
* @param <R>
* @param <W>
*/
public final class FloatArraySimpledCoder<R extends Reader, W extends Writer> extends SimpledCoder<R, W, float[]> {
public static final FloatArraySimpledCoder instance = new FloatArraySimpledCoder();
@Override
public void convertTo(W out, float[] values) {
if (values == null) {
out.writeNull();
return;
}
out.writeArrayB(values.length);
boolean flag = false;
for (float v : values) {
if (flag) out.writeArrayMark();
out.writeFloat(v);
flag = true;
}
out.writeArrayE();
}
@Override
public float[] convertFrom(R in) {
int len = in.readArrayB();
if (len == Reader.SIGN_NULL) {
return null;
} else if (len == Reader.SIGN_NOLENGTH) {
int size = 0;
float[] data = new float[8];
while (in.hasNext()) {
if (size >= data.length) {
float[] newdata = new float[data.length + 4];
System.arraycopy(data, 0, newdata, 0, size);
data = newdata;
}
data[size++] = in.readFloat();
}
in.readArrayE();
float[] newdata = new float[size];
System.arraycopy(data, 0, newdata, 0, size);
return newdata;
} else {
float[] values = new float[len];
for (int i = 0; i < values.length; i++) {
values[i] = in.readFloat();
}
in.readArrayE();
return values;
}
}
}

View File

@@ -0,0 +1,32 @@
/*
* 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.ext;
import com.wentch.redkale.convert.Reader;
import com.wentch.redkale.convert.SimpledCoder;
import com.wentch.redkale.convert.Writer;
/**
*
* @author zhangjx
* @param <R>
* @param <W>
*/
public final class FloatSimpledCoder<R extends Reader, W extends Writer> extends SimpledCoder<R, W, Float> {
public static final FloatSimpledCoder instance = new FloatSimpledCoder();
@Override
public void convertTo(W out, Float value) {
out.writeFloat(value);
}
@Override
public Float convertFrom(R in) {
return in.readFloat();
}
}

View File

@@ -0,0 +1,70 @@
/*
* 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.ext;
import com.wentch.redkale.convert.SimpledCoder;
import com.wentch.redkale.convert.Writer;
import com.wentch.redkale.convert.Reader;
import java.net.*;
/**
*
* @author zhangjx
* @param <R>
* @param <W>
*/
public final class InetAddressSimpledCoder<R extends Reader, W extends Writer> extends SimpledCoder<R, W, InetAddress> {
public static final InetAddressSimpledCoder instance = new InetAddressSimpledCoder();
@Override
public void convertTo(W out, InetAddress value) {
if (value == null) {
out.writeNull();
return;
}
ByteArraySimpledCoder.instance.convertTo(out, value.getAddress());
}
@Override
public InetAddress convertFrom(R in) {
byte[] bytes = ByteArraySimpledCoder.instance.convertFrom(in);
if (bytes == null) return null;
try {
return InetAddress.getByAddress(bytes);
} catch (Exception ex) {
return null;
}
}
public static class InetSocketAddressSimpledCoder<R extends Reader, W extends Writer> extends SimpledCoder<R, W, InetSocketAddress> {
public static final InetSocketAddressSimpledCoder instance = new InetSocketAddressSimpledCoder();
@Override
public void convertTo(W out, InetSocketAddress value) {
if (value == null) {
out.writeNull();
return;
}
ByteArraySimpledCoder.instance.convertTo(out, value.getAddress().getAddress());
out.writeInt(value.getPort());
}
@Override
public InetSocketAddress convertFrom(R in) {
byte[] bytes = ByteArraySimpledCoder.instance.convertFrom(in);
if (bytes == null) return null;
int port = in.readInt();
try {
return new InetSocketAddress(InetAddress.getByAddress(bytes), port);
} catch (Exception ex) {
return null;
}
}
}
}

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.ext;
import com.wentch.redkale.convert.Reader;
import com.wentch.redkale.convert.SimpledCoder;
import com.wentch.redkale.convert.Writer;
/**
*
* @author zhangjx
* @param <R>
* @param <W>
*/
public final class IntArraySimpledCoder<R extends Reader, W extends Writer> extends SimpledCoder<R, W, int[]> {
public static final IntArraySimpledCoder instance = new IntArraySimpledCoder();
@Override
public void convertTo(W out, int[] values) {
if (values == null) {
out.writeNull();
return;
}
out.writeArrayB(values.length);
boolean flag = false;
for (int v : values) {
if (flag) out.writeArrayMark();
out.writeInt(v);
flag = true;
}
out.writeArrayE();
}
@Override
public int[] convertFrom(R in) {
int len = in.readArrayB();
if (len == Reader.SIGN_NULL) {
return null;
} else if (len == Reader.SIGN_NOLENGTH) {
int size = 0;
int[] data = new int[8];
while (in.hasNext()) {
if (size >= data.length) {
int[] newdata = new int[data.length + 4];
System.arraycopy(data, 0, newdata, 0, size);
data = newdata;
}
data[size++] = in.readInt();
}
in.readArrayE();
int[] newdata = new int[size];
System.arraycopy(data, 0, newdata, 0, size);
return newdata;
} else {
int[] values = new int[len];
for (int i = 0; i < values.length; i++) {
values[i] = in.readInt();
}
in.readArrayE();
return values;
}
}
}

View File

@@ -0,0 +1,32 @@
/*
* 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.ext;
import com.wentch.redkale.convert.Reader;
import com.wentch.redkale.convert.SimpledCoder;
import com.wentch.redkale.convert.Writer;
/**
*
* @author zhangjx
* @param <R>
* @param <W>
*/
public final class IntSimpledCoder<R extends Reader, W extends Writer> extends SimpledCoder<R, W, Integer> {
public static final IntSimpledCoder instance = new IntSimpledCoder();
@Override
public void convertTo(W out, Integer value) {
out.writeInt(value);
}
@Override
public Integer convertFrom(R in) {
return in.readInt();
}
}

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.ext;
import com.wentch.redkale.convert.Reader;
import com.wentch.redkale.convert.SimpledCoder;
import com.wentch.redkale.convert.Writer;
/**
*
* @author zhangjx
* @param <R>
* @param <W>
*/
public final class LongArraySimpledCoder<R extends Reader, W extends Writer> extends SimpledCoder<R, W, long[]> {
public static final LongArraySimpledCoder instance = new LongArraySimpledCoder();
@Override
public void convertTo(W out, long[] values) {
if (values == null) {
out.writeNull();
return;
}
out.writeArrayB(values.length);
boolean flag = false;
for (long v : values) {
if (flag) out.writeArrayMark();
out.writeLong(v);
flag = true;
}
out.writeArrayE();
}
@Override
public long[] convertFrom(R in) {
int len = in.readArrayB();
if (len == Reader.SIGN_NULL) {
return null;
} else if (len == Reader.SIGN_NOLENGTH) {
int size = 0;
long[] data = new long[8];
while (in.hasNext()) {
if (size >= data.length) {
long[] newdata = new long[data.length + 4];
System.arraycopy(data, 0, newdata, 0, size);
data = newdata;
}
data[size++] = in.readInt();
}
in.readArrayE();
long[] newdata = new long[size];
System.arraycopy(data, 0, newdata, 0, size);
return newdata;
} else {
long[] values = new long[len];
for (int i = 0; i < values.length; i++) {
values[i] = in.readInt();
}
in.readArrayE();
return values;
}
}
}

View File

@@ -0,0 +1,33 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package com.wentch.redkale.convert.ext;
import com.wentch.redkale.convert.Reader;
import com.wentch.redkale.convert.SimpledCoder;
import com.wentch.redkale.convert.Writer;
/**
*
* @author zhangjx
* @param <R>
* @param <W>
*/
public final class LongSimpledCoder<R extends Reader, W extends Writer> extends SimpledCoder<R, W, Long> {
public static final LongSimpledCoder instance = new LongSimpledCoder();
@Override
public void convertTo(W out, Long value) {
out.writeLong(value);
}
@Override
public Long convertFrom(R in) {
return in.readLong();
}
}

View File

@@ -0,0 +1,32 @@
/*
* 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.ext;
import com.wentch.redkale.convert.Reader;
import com.wentch.redkale.convert.SimpledCoder;
import com.wentch.redkale.convert.Writer;
/**
*
* @author zhangjx
* @param <R>
* @param <W>
*/
public final class NumberSimpledCoder<R extends Reader, W extends Writer> extends SimpledCoder<R, W, Number> {
public static final NumberSimpledCoder instance = new NumberSimpledCoder();
@Override
public void convertTo(W out, Number value) {
out.writeLong(value == null ? 0L : value.longValue());
}
@Override
public Number convertFrom(R in) {
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.ext;
import com.wentch.redkale.convert.Reader;
import com.wentch.redkale.convert.SimpledCoder;
import com.wentch.redkale.convert.Writer;
/**
*
* @author zhangjx
* @param <R>
* @param <W>
*/
public final class ShortArraySimpledCoder<R extends Reader, W extends Writer> extends SimpledCoder<R, W, short[]> {
public static final ShortArraySimpledCoder instance = new ShortArraySimpledCoder();
@Override
public void convertTo(W out, short[] values) {
if (values == null) {
out.writeNull();
return;
}
out.writeArrayB(values.length);
boolean flag = false;
for (short v : values) {
if (flag) out.writeArrayMark();
out.writeShort(v);
flag = true;
}
out.writeArrayE();
}
@Override
public short[] convertFrom(R in) {
int len = in.readArrayB();
if (len == Reader.SIGN_NULL) {
return null;
} else if (len == Reader.SIGN_NOLENGTH) {
int size = 0;
short[] data = new short[8];
while (in.hasNext()) {
if (size >= data.length) {
short[] newdata = new short[data.length + 4];
System.arraycopy(data, 0, newdata, 0, size);
data = newdata;
}
data[size++] = in.readShort();
}
in.readArrayE();
short[] newdata = new short[size];
System.arraycopy(data, 0, newdata, 0, size);
return newdata;
} else {
short[] values = new short[len];
for (int i = 0; i < values.length; i++) {
values[i] = in.readShort();
}
in.readArrayE();
return values;
}
}
}

View File

@@ -0,0 +1,32 @@
/*
* 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.ext;
import com.wentch.redkale.convert.Reader;
import com.wentch.redkale.convert.SimpledCoder;
import com.wentch.redkale.convert.Writer;
/**
*
* @author zhangjx
* @param <R>
* @param <W>
*/
public final class ShortSimpledCoder<R extends Reader, W extends Writer> extends SimpledCoder<R, W, Short> {
public static final ShortSimpledCoder instance = new ShortSimpledCoder();
@Override
public void convertTo(W out, Short value) {
out.writeShort(value);
}
@Override
public Short convertFrom(R in) {
return in.readShort();
}
}

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.ext;
import com.wentch.redkale.convert.Reader;
import com.wentch.redkale.convert.SimpledCoder;
import com.wentch.redkale.convert.Writer;
/**
*
* @author zhangjx
* @param <R>
* @param <W>
*/
public final class StringArraySimpledCoder<R extends Reader, W extends Writer> extends SimpledCoder<R, W, String[]> {
public static final StringArraySimpledCoder instance = new StringArraySimpledCoder();
@Override
public void convertTo(W out, String[] values) {
if (values == null) {
out.writeNull();
return;
}
out.writeArrayB(values.length);
boolean flag = false;
for (String v : values) {
if (flag) out.writeArrayMark();
out.writeString(v);
flag = true;
}
out.writeArrayE();
}
@Override
public String[] convertFrom(R in) {
int len = in.readArrayB();
if (len == Reader.SIGN_NULL) {
return null;
} else if (len == Reader.SIGN_NOLENGTH) {
int size = 0;
String[] data = new String[8];
while (in.hasNext()) {
if (size >= data.length) {
String[] newdata = new String[data.length + 4];
System.arraycopy(data, 0, newdata, 0, size);
data = newdata;
}
data[size++] = in.readString();
}
in.readArrayE();
String[] newdata = new String[size];
System.arraycopy(data, 0, newdata, 0, size);
return newdata;
} else {
String[] values = new String[len];
for (int i = 0; i < values.length; i++) {
values[i] = in.readString();
}
in.readArrayE();
return values;
}
}
}

View File

@@ -0,0 +1,32 @@
/*
* 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.ext;
import com.wentch.redkale.convert.Reader;
import com.wentch.redkale.convert.SimpledCoder;
import com.wentch.redkale.convert.Writer;
/**
*
* @author zhangjx
* @param <R>
* @param <W>
*/
public final class StringSimpledCoder<R extends Reader, W extends Writer> extends SimpledCoder<R, W, String> {
public static final StringSimpledCoder instance = new StringSimpledCoder();
@Override
public void convertTo(W out, String value) {
out.writeString(value);
}
@Override
public String convertFrom(R in) {
return in.readString();
}
}

View File

@@ -0,0 +1,40 @@
/*
* 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.ext;
import com.wentch.redkale.convert.Reader;
import com.wentch.redkale.convert.Writer;
import com.wentch.redkale.convert.SimpledCoder;
import com.wentch.redkale.util.TwoLong;
/**
*
* @author zhangjx
* @param <R>
* @param <W>
*/
public final class TwoLongSimpledCoder<R extends Reader, W extends Writer> extends SimpledCoder<R, W, TwoLong> {
public static final TwoLongSimpledCoder instance = new TwoLongSimpledCoder();
@Override
public void convertTo(final W out, final TwoLong value) {
if (value == null) {
out.writeNull();
} else {
out.writeSmallString(value.getFirst() + "_" + value.getSecond());
}
}
@Override
public TwoLong convertFrom(R in) {
String str = in.readString();
if (str == null) return null;
int pos = str.indexOf('_');
return new TwoLong(Long.parseLong(str.substring(0, pos)), Long.parseLong(str.substring(pos + 1)));
}
}

View File

@@ -0,0 +1,42 @@
/*
* 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.ext;
import com.wentch.redkale.convert.Reader;
import com.wentch.redkale.convert.Writer;
import com.wentch.redkale.convert.SimpledCoder;
/**
*
* @author zhangjx
* @param <R>
* @param <W>
*/
public class TypeSimpledCoder<R extends Reader, W extends Writer> extends SimpledCoder<R, W, Class> {
public static final TypeSimpledCoder instance = new TypeSimpledCoder();
@Override
public void convertTo(final W out, final Class value) {
if (value == null) {
out.writeNull();
} else {
out.writeSmallString(value.getName());
}
}
@Override
public Class convertFrom(R in) {
String str = in.readSmallString();
if (str == null) return null;
try {
return Class.forName(str);
} catch (Exception e) {
return null;
}
}
}

View File

@@ -0,0 +1,81 @@
/*
* 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.util.Utility;
import com.wentch.redkale.util.ObjectPool;
import com.wentch.redkale.convert.Convert;
import java.lang.reflect.Type;
/**
*
* @author zhangjx
*/
@SuppressWarnings("unchecked")
public final class JsonConvert extends Convert<JsonReader, JsonWriter> {
private static final ObjectPool<JsonReader> readerPool = new ObjectPool<>(Integer.getInteger("convert.json.pool.size", 16), JsonReader.class);
private static final ObjectPool<JsonWriter> writerPool = new ObjectPool<>(Integer.getInteger("convert.json.pool.size", 16), JsonWriter.class);
protected JsonConvert(JsonFactory factory) {
super(factory);
}
@Override
public JsonFactory getFactory() {
return (JsonFactory) factory;
}
public <T> T convertFrom(final Type type, final String text) {
if (text == null) return null;
return convertFrom(type, Utility.charArray(text));
}
public <T> T convertFrom(final Type type, final char[] text) {
if (text == null) return null;
return convertFrom(type, text, 0, text.length);
}
public <T> T convertFrom(final Type type, final char[] text, int start, int len) {
if (text == null || type == null) return null;
final JsonReader in = readerPool.poll();
in.setText(text, start, len);
T rs = (T) factory.loadDecoder(type).convertFrom(in);
readerPool.offer(in);
return rs;
}
public String convertTo(final Type type, Object value) {
if (type == null) return null;
if (value == null) return "null";
final JsonWriter out = writerPool.poll();
factory.loadEncoder(type).convertTo(out, value);
String result = out.toString();
writerPool.offer(out);
return result;
}
public String convertTo(Object value) {
if (value == null) return "null";
return convertTo(value.getClass(), value);
}
public byte[] convertToUTF8Bytes(Object value) {
if (value == null) return new byte[]{110, 117, 108, 108};
return convertToUTF8Bytes(value.getClass(), value);
}
public byte[] convertToUTF8Bytes(final Type type, Object value) {
if (type == null) return null;
if (value == null) return new byte[]{110, 117, 108, 108};
final JsonWriter out = writerPool.poll();
factory.loadEncoder(type).convertTo(out, value);
byte[] result = out.toUTF8Bytes();
writerPool.offer(out);
return result;
}
}

View File

@@ -0,0 +1,52 @@
/*
* 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.ConvertType;
import com.wentch.redkale.convert.Factory;
import java.io.Serializable;
/**
*
* @author zhangjx
*/
public final class JsonFactory extends Factory<JsonReader, JsonWriter> {
private static final JsonFactory instance = new JsonFactory(null);
static {
instance.register(Serializable.class, instance.loadEncoder(Object.class));
}
private JsonFactory(JsonFactory parent) {
super(parent);
this.convert = new JsonConvert(this);
}
public static JsonFactory root() {
return instance;
}
@Override
public final JsonConvert getConvert() {
return (JsonConvert) convert;
}
@Override
public JsonFactory createChild() {
return new JsonFactory(this);
}
@Override
public ConvertType getConvertType() {
return ConvertType.JSON;
}
@Override
public boolean isReversible() {
return false;
}
}

View File

@@ -0,0 +1,556 @@
/*
* 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.ConvertException;
import com.wentch.redkale.convert.DeMember;
import com.wentch.redkale.convert.Reader;
import com.wentch.redkale.util.ObjectPool.Poolable;
import com.wentch.redkale.util.Utility;
import java.util.concurrent.atomic.AtomicInteger;
/**
*
* @author zhangjx
*/
public final class JsonReader implements Reader, Poolable {
private int position = -1;
private char[] text;
private int limit;
public JsonReader() {
}
public JsonReader(String json) {
setText(Utility.charArray(json));
}
public JsonReader(char[] text) {
setText(text, 0, text.length);
}
public JsonReader(char[] text, int start, int len) {
setText(text, start, len);
}
public final void setText(String text) {
JsonReader.this.setText(Utility.charArray(text));
}
public final void setText(char[] text) {
setText(text, 0, text.length);
}
public final void setText(char[] text, int start, int len) {
this.text = text;
this.position = start - 1;
this.limit = start + len - 1;
}
@Override
public void prepare() {
}
@Override
public void release() {
this.position = -1;
this.limit = -1;
this.text = null;
}
public void close() {
this.release();
}
/**
* 找到指定的属性值 例如: {id : 1, data : { name : 'a', items : [1,2,3]}} seek('data.items') 直接跳转到 [1,2,3];
*
* @param key
*/
public final void seek(String key) {
if (key == null || key.length() < 1) return;
final String[] keys = key.split("\\.");
nextGoodChar(); //读掉 { [
for (String key1 : keys) {
while (this.hasNext()) {
String field = this.readSmallString();
skipBlank();
if (key1.equals(field)) break;
skipValue();
}
}
}
/**
* 跳过属性的值
*/
@Override
public final void skipValue() {
final char ch = nextGoodChar();
if (ch == '"' || ch == '\'') {
backChar(ch);
readString();
} else if (ch == '{') {
while (hasNext()) {
this.readSmallString(); //读掉field
this.skipBlank();
this.skipValue();
}
} else if (ch == '[') {
while (hasNext()) {
this.skipValue();
}
} else {
char c;
for (;;) {
c = nextChar();
if (c <= ' ') return;
if (c == '}' || c == ']' || c == ',' || c == ':') {
backChar(c);
return;
}
}
}
}
/**
* 读取下一个字符, 不跳过空白字符
*
* @return
*/
protected char nextChar() {
return this.text[++this.position];
}
/**
* 跳过空白字符, 返回一个非空白字符
*
* @return
*/
protected char nextGoodChar() {
char c = nextChar();
if (c > ' ') return c;
for (;;) {
c = nextChar();
if (c > ' ') return c;
}
}
/**
* 回退最后读取的字符
*
* @param ch
*/
protected void backChar(char ch) {
this.position--;
}
/**
* 判断下一个非空白字符是否为{
*
*/
@Override
public int readObjectB() {
char ch = this.text[++this.position];
if (ch == '{') return SIGN_NOLENGTH;
if (ch <= ' ') {
for (;;) {
ch = this.text[++this.position];
if (ch > ' ') break;
}
if (ch == '{') return SIGN_NOLENGTH;
}
if (ch == 'n' && text[++position] == 'u' && text[++position] == 'l' && text[++position] == 'l') return SIGN_NULL;
if (ch == 'N' && text[++position] == 'U' && text[++position] == 'L' && text[++position] == 'L') return SIGN_NULL;
throw new ConvertException("a json object text must begin with '{' (position = " + position + ") but '" + ch + "'");
}
@Override
public void readObjectE() {
}
/**
* 判断下一个非空白字符是否为{
*
*/
@Override
public int readMapB() {
return readArrayB();
}
@Override
public void readMapE() {
}
/**
* 判断下一个非空白字符是否为[
*
* @return
*/
@Override
public int readArrayB() {
char ch = this.text[++this.position];
if (ch == '[') return SIGN_NOLENGTH;
if (ch == '{') return SIGN_NOLENGTH;
if (ch <= ' ') {
for (;;) {
ch = this.text[++this.position];
if (ch > ' ') break;
}
if (ch == '[') return SIGN_NOLENGTH;
if (ch == '{') return SIGN_NOLENGTH;
}
if (ch == 'n' && text[++position] == 'u' && text[++position] == 'l' && text[++position] == 'l') return SIGN_NULL;
if (ch == 'N' && text[++position] == 'U' && text[++position] == 'L' && text[++position] == 'L') return SIGN_NULL;
throw new ConvertException("a json array text must begin with '[' (position = " + position + ") but '" + ch + "'");
}
@Override
public void readArrayE() {
}
/**
* 判断下一个非空白字符是否:
*/
@Override
public void skipBlank() {
char ch = this.text[++this.position];
if (ch == ':') return;
if (ch <= ' ') {
for (;;) {
ch = this.text[++this.position];
if (ch > ' ') break;
}
if (ch == ':') return;
}
throw new ConvertException("'" + new String(text) + "'expected a ':' but '" + ch + "'(position = " + position + ")");
}
/**
* 判断对象是否存在下一个属性或者数组是否存在下一个元素
*
* @return
*/
@Override
public boolean hasNext() {
char ch = this.text[++this.position];
if (ch == ',') return true;
if (ch == '}' || ch == ']') return false;
if (ch <= ' ') {
for (;;) {
ch = this.text[++this.position];
if (ch > ' ') break;
}
if (ch == ',') return true;
if (ch == '}' || ch == ']') return false;
}
this.position--;
return true;
}
@Override
public String readClassName() {
return null;
}
@Override
public String readSmallString() {
final int eof = this.limit;
if (this.position == eof) return null;
final char[] text0 = this.text;
int currpos = this.position;
char ch = text0[++currpos];
if (ch <= ' ') {
for (;;) {
ch = text0[++currpos];
if (ch > ' ') break;
}
}
if (ch == '"' || ch == '\'') {
final char quote = ch;
final int start = currpos + 1;
for (;;) {
ch = text0[++currpos];
if (ch == '\\') {
this.position = currpos - 1;
return readEscapeValue(quote, start);
} else if (ch == quote) {
break;
}
}
this.position = currpos;
char[] chs = new char[currpos - start];
System.arraycopy(text0, start, chs, 0, chs.length);
return new String(chs);
} else {
int start = currpos;
for (;;) {
if (currpos == eof) break;
ch = text0[++currpos];
if (ch == ',' || ch == ']' || ch == '}' || ch <= ' ' || ch == ':') break;
}
int len = currpos - start;
if (len < 1) {
this.position = currpos;
return String.valueOf(ch);
}
this.position = currpos - 1;
if (len == 4 && text0[start] == 'n' && text0[start + 1] == 'u' && text0[start + 2] == 'l' && text0[start + 3] == 'l') return null;
return new String(text0, start, len);
}
}
/**
* 读取一个int
*
* @return
*/
@Override
public final int readInt() {
final char[] text0 = this.text;
final int eof = this.limit;
int currpos = this.position;
char firstchar = text0[++currpos];
if (firstchar <= ' ') {
for (;;) {
firstchar = text0[++currpos];
if (firstchar > ' ') break;
}
}
if (firstchar == '"' || firstchar == '\'') {
firstchar = text0[++currpos];
if (firstchar == '"' || firstchar == '\'') {
this.position = currpos;
return 0;
}
}
int value = 0;
final boolean negative = firstchar == '-';
if (!negative) {
if (firstchar < '0' || firstchar > '9') throw new NumberFormatException("illegal escape(" + firstchar + ") (position = " + currpos + ")");
value = firstchar - '0';
}
for (;;) {
if (currpos == eof) break;
char ch = text0[++currpos];
if (ch >= '0' && ch <= '9') {
value = (value << 3) + (value << 1) + (ch - '0');
} else if (ch == '"' || ch == '\'') {
} else if (ch == ',' || ch == '}' || ch == ']' || ch <= ' ' || ch == ':') {
break;
} else {
throw new NumberFormatException("illegal escape(" + ch + ") (position = " + currpos + ")");
}
}
this.position = currpos - 1;
return negative ? -value : value;
}
/**
* 读取一个long
*
* @return
*/
@Override
public final long readLong() {
final char[] text0 = this.text;
final int eof = this.limit;
int currpos = this.position;
char firstchar = text0[++currpos];
if (firstchar <= ' ') {
for (;;) {
firstchar = text0[++currpos];
if (firstchar > ' ') break;
}
}
if (firstchar == '"' || firstchar == '\'') {
firstchar = text0[++currpos];
if (firstchar == '"' || firstchar == '\'') {
this.position = currpos;
return 0L;
}
}
long value = 0;
final boolean negative = firstchar == '-';
if (!negative) {
if (firstchar < '0' || firstchar > '9') throw new NumberFormatException("illegal escape(" + firstchar + ") (position = " + currpos + ")");
value = firstchar - '0';
}
for (;;) {
if (currpos == eof) break;
char ch = text0[++currpos];
if (ch >= '0' && ch <= '9') {
value = (value << 3) + (value << 1) + (ch - '0');
} else if (ch == '"' || ch == '\'') {
} else if (ch == ',' || ch == '}' || ch == ']' || ch <= ' ' || ch == ':') {
break;
} else {
throw new NumberFormatException("illegal escape(" + ch + ") (position = " + currpos + ") but '" + ch + "'");
}
}
this.position = currpos - 1;
return negative ? -value : value;
}
@Override
public DeMember readField(final AtomicInteger index, final DeMember[] members) {
final String exceptedfield = this.readSmallString();
final int len = members.length;
int v = index.get();
if (v >= len) {
v = 0;
index.set(0);
}
for (int k = v; k < len; k++) {
if (exceptedfield.equals(members[k].getAttribute().field())) {
index.set(k);
return members[k];
}
}
for (int k = 0; k < v; k++) {
if (exceptedfield.equals(members[k].getAttribute().field())) {
index.set(k);
return members[k];
}
}
return null;
//if (result == null && len == 1 && text0[start] == '@') return REFER;
}
//------------------------------------------------------------
@Override
public boolean readBoolean() {
return "true".equalsIgnoreCase(this.readSmallString());
}
@Override
public byte readByte() {
return (byte) readInt();
}
@Override
public char readChar() {
return (char) readInt();
}
@Override
public short readShort() {
return (short) readInt();
}
@Override
public float readFloat() {
String chars = readSmallString();
if (chars == null || chars.isEmpty()) return 0.f;
return Float.parseFloat(chars);
}
@Override
public double readDouble() {
String chars = readSmallString();
if (chars == null || chars.isEmpty()) return 0.0;
return Double.parseDouble(chars);
}
/**
* 读取字符串, 必须是"或者'包围的字符串值
*
* @return
*/
@Override
public String readString() {
final char[] text0 = this.text;
int currpos = this.position;
char expected = text0[++currpos];
if (expected <= ' ') {
for (;;) {
expected = text0[++currpos];
if (expected > ' ') break;
}
}
if (expected != '"' && expected != '\'') {
if (expected == 'n' && text0.length > currpos + 3) {
if (text0[++currpos] == 'u' && text0[++currpos] == 'l' && text0[++currpos] == 'l') {
this.position = currpos;
if (text0.length > currpos + 4) {
char ch = text0[currpos + 1];
if (ch == ',' || ch <= ' ' || ch == '}' || ch == ']' || ch == ':') return null;
} else {
return null;
}
}
}
this.position = currpos;
throw new ConvertException("expected a ':' after a key but '" + text0[position] + "' (position = " + position + ")");
}
final int start = ++currpos;
for (;;) {
char ch = text0[currpos];
if (ch == expected) {
break;
} else if (ch == '\\') {
this.position = currpos - 1;
return readEscapeValue(expected, start);
}
currpos++;
}
this.position = currpos;
return new String(text0, start, currpos - start);
}
private String readEscapeValue(final char expected, int start) {
StringBuilder array = new StringBuilder();
final char[] text0 = this.text;
int pos = this.position;
array.append(text0, start, pos + 1 - start);
char c;
for (;;) {
c = text0[++pos];
if (c == expected) {
this.position = pos;
return array.toString();
} else if (c == '\\') {
c = text0[++pos];
switch (c) {
case '"':
case '\'':
case '\\':
case '/':
array.append(c);
break;
case 'n':
array.append('\n');
break;
case 'r':
array.append('\r');
break;
case 'u':
array.append((char) Integer.parseInt(new String(new char[]{text0[++pos], text0[++pos], text0[++pos], text0[++pos]}), 16));
break;
case 't':
array.append('\t');
break;
case 'b':
array.append('\b');
break;
case 'f':
array.append('\f');
break;
default:
this.position = pos;
throw new ConvertException("illegal escape(" + c + ") (position = " + this.position + ")");
}
} else {
array.append(c);
}
}
}
}

View File

@@ -0,0 +1,17 @@
/*
* 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.SimpledCoder;
/**
*
* @author zhangjx
* @param <T>
*/
public abstract class JsonSimpledCoder<T> extends SimpledCoder<JsonReader, JsonWriter, T> {
}

View File

@@ -0,0 +1,278 @@
/*
* 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.util.Attribute;
import com.wentch.redkale.util.Utility;
import com.wentch.redkale.convert.Writer;
import com.wentch.redkale.util.ObjectPool.Poolable;
/**
*
* writeTo系列的方法输出的字符不能含特殊字符
*
* @author zhangjx
*/
public final class JsonWriter implements Writer, Poolable {
private static final char[] CHARS_TUREVALUE = "true".toCharArray();
private static final char[] CHARS_FALSEVALUE = "false".toCharArray();
private static final int defaultSize = Integer.getInteger("convert.json.writer.buffer.defsize", 1024);
protected int count;
private char[] content;
public JsonWriter() {
this(defaultSize);
}
public JsonWriter(int size) {
this.content = new char[size > 128 ? size : 128];
}
//-----------------------------------------------------------------------
//-----------------------------------------------------------------------
/**
* 返回指定至少指定长度的缓冲区
*
* @param len
* @return
*/
public char[] expand(int len) {
int newcount = count + len;
if (newcount <= content.length) return content;
char[] newdata = new char[Math.max(content.length * 3 / 2, newcount)];
System.arraycopy(content, 0, newdata, 0, count);
this.content = newdata;
return newdata;
}
public void writeTo(final char ch) {
expand(1);
content[count++] = ch;
}
public void writeTo(final char... chs) {
int len = chs.length;
expand(len);
System.arraycopy(chs, 0, content, count, len);
count += len;
}
public void writeTo(final char[] chs, final int start, final int end) {
int len = end - start;
expand(len);
System.arraycopy(chs, start, content, count, len);
count += len;
}
/**
* <b>注意:</b> 该String值不能为null且不会进行转义 只用于不含需要转义字符的字符串例如enum、double、BigInteger转换的String
*
* @param quote
* @param value
*/
public void writeTo(final boolean quote, final String value) {
int len = value.length();
expand(len + (quote ? 2 : 0));
if (quote) content[count++] = '"';
value.getChars(0, len, content, count);
count += len;
if (quote) content[count++] = '"';
}
@Override
public void prepare() {
}
@Override
public void release() {
this.count = 0;
if (this.content.length > defaultSize) {
this.content = new char[defaultSize];
}
}
public char[] toArray() {
if (count == content.length) return content;
char[] newdata = new char[count];
System.arraycopy(content, 0, newdata, 0, count);
return newdata;
}
public byte[] toUTF8Bytes() {
return Utility.encodeUTF8(content, 0, count);
}
//------------------------------------------------------------------------
public final int count() {
return this.count;
}
public final void count(int count) {
if (count >= 0) this.count = count;
}
// @SuppressWarnings("unchecked")
// public final boolean writeRefer(final Object value) {
// if (stack == null) return false;
// int index = stack.indexOf(value);
// if (index > -1) {
// int deep = stack.size() - index;
// if (deep < 0) throw new ConvertException("the refer deep value(" + deep + ") is illegal");
// writeTo('{', '"', '@', '"', ':', '"');
// for (int i = 0; i < deep; i++) {
// writeTo('@');
// }
// writeTo('"', '}');
// return true;
// }
// return false;
// }
//-----------------------------------------------------------------------
@Override
public String toString() {
return new String(content, 0, count);
}
@Override
public void writeBoolean(boolean value) {
writeTo(value ? CHARS_TUREVALUE : CHARS_FALSEVALUE);
}
@Override
public void writeByte(byte value) {
writeInt(value);
}
@Override
public void writeChar(char value) {
writeInt(value);
}
@Override
public void writeShort(short value) {
writeInt(value);
}
@Override
public void writeInt(int value) {
writeSmallString(String.valueOf(value));
}
@Override
public void writeLong(long value) {
writeSmallString(String.valueOf(value));
}
@Override
public void writeFloat(float value) {
writeSmallString(String.valueOf(value));
}
@Override
public void writeDouble(double value) {
writeSmallString(String.valueOf(value));
}
@Override
public void writeString(String value) {
if (value == null) {
writeNull();
return;
}
expand(value.length() * 2 + 2);
content[count++] = '"';
for (char ch : Utility.charArray(value)) {
switch (ch) {
case '\n': content[count++] = '\\';
content[count++] = 'n';
break;
case '\r': content[count++] = '\\';
content[count++] = 'r';
break;
case '\t': content[count++] = '\\';
content[count++] = 't';
break;
case '\\': content[count++] = '\\';
content[count++] = ch;
break;
case '"': content[count++] = '\\';
content[count++] = ch;
break;
default: content[count++] = ch;
break;
}
}
content[count++] = '"';
}
@Override
public void wirteClassName(Class clazz) {
}
@Override
public final void writeObjectB(int fieldCount, Object obj) {
writeTo('{');
}
@Override
public final void writeObjectE(Object obj) {
writeTo('}');
}
@Override
public final void writeField(boolean comma, Attribute attribute) {
if (comma) writeTo(',');
writeTo('"');
writeSmallString(attribute.field());
writeTo('"');
writeTo(':');
}
@Override
public final void writeNull() {
writeTo('n', 'u', 'l', 'l');
}
@Override
public void writeArrayB(int size) {
writeTo('[');
}
@Override
public void writeArrayMark() {
writeTo(',');
}
@Override
public void writeArrayE() {
writeTo(']');
}
@Override
public void writeMapB(int size) {
writeTo('{');
}
@Override
public void writeMapMark() {
writeTo(':');
}
@Override
public void writeMapE() {
writeTo('}');
}
@Override
public void writeSmallString(String value) {
writeTo(false, value);
}
}

View File

@@ -0,0 +1,23 @@
/*
* 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;
import java.lang.annotation.*;
import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
/**
* 当Service是Remote模式时 用该注解标注在方法上可使数据变成异步传输, 该注解只能标注在返回类型为void的public方法上
*
* @author zhangjx
*/
@Inherited
@Documented
@Target({TYPE, METHOD})
@Retention(RUNTIME)
public @interface Async {
}

View File

@@ -0,0 +1,272 @@
/*
* 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;
import java.io.IOException;
import java.net.*;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.concurrent.*;
/**
*
* @author zhangjx
*/
public abstract class AsyncConnection implements AsynchronousByteChannel, AutoCloseable {
protected AsyncPooledConnection pooledConnection;
public abstract SocketAddress getRemoteAddress();
public abstract int getReadTimeoutSecond();
public abstract int getWriteTimeoutSecond();
public abstract void setReadTimeoutSecond(int readTimeoutSecond);
public abstract void setWriteTimeoutSecond(int writeTimeoutSecond);
public abstract void dispose(); //同close 只是去掉throws IOException
public static AsyncConnection create(final String protocol, final SocketAddress address) throws IOException {
return create(protocol, address, 0, 0);
}
/**
* 创建客户端连接
*
* @param protocol
* @param address
* @param readTimeoutSecond0
* @param writeTimeoutSecond0
* @return
* @throws java.io.IOException
*/
public static AsyncConnection create(final String protocol, final SocketAddress address,
final int readTimeoutSecond0, final int writeTimeoutSecond0) throws IOException {
if ("TCP".equalsIgnoreCase(protocol)) {
AsynchronousSocketChannel channel = AsynchronousSocketChannel.open();
try {
channel.connect(address).get(3, TimeUnit.SECONDS);
} catch (Exception e) {
throw new IOException("AsyncConnection connect " + address, e);
}
return create(channel, readTimeoutSecond0, writeTimeoutSecond0);
} else if ("UDP".equalsIgnoreCase(protocol)) {
AsyncDatagramChannel channel = AsyncDatagramChannel.open(null);
channel.connect(address);
return create(channel, address, true, readTimeoutSecond0, writeTimeoutSecond0);
} else {
throw new RuntimeException("AsyncConnection not support protocol " + protocol);
}
}
public static AsyncConnection create(final AsyncDatagramChannel ch, SocketAddress addr, final boolean client0) {
return create(ch, addr, client0, 0, 0);
}
public static AsyncConnection create(final AsyncDatagramChannel ch, SocketAddress addr,
final boolean client0, final int readTimeoutSecond0, final int writeTimeoutSecond0) {
return new AsyncConnection() {
private int readTimeoutSecond;
private int writeTimeoutSecond;
private final AsyncDatagramChannel channel;
private final SocketAddress remoteAddress;
private final boolean client;
{
this.channel = ch;
this.client = client0;
this.readTimeoutSecond = readTimeoutSecond0;
this.writeTimeoutSecond = writeTimeoutSecond0;
this.remoteAddress = addr;
}
@Override
public <A> void read(ByteBuffer dst, A attachment, CompletionHandler<Integer, ? super A> handler) {
if (readTimeoutSecond > 0) {
channel.read(dst, readTimeoutSecond, TimeUnit.SECONDS, attachment, handler);
} else {
channel.read(dst, attachment, handler);
}
}
@Override
public <A> void write(ByteBuffer src, A attachment, CompletionHandler<Integer, ? super A> handler) {
channel.send(src, remoteAddress, attachment, handler);
}
@Override
public void setReadTimeoutSecond(int readTimeoutSecond) {
this.readTimeoutSecond = readTimeoutSecond;
}
@Override
public void setWriteTimeoutSecond(int writeTimeoutSecond) {
this.writeTimeoutSecond = writeTimeoutSecond;
}
@Override
public int getReadTimeoutSecond() {
return this.readTimeoutSecond;
}
@Override
public int getWriteTimeoutSecond() {
return this.writeTimeoutSecond;
}
@Override
public final SocketAddress getRemoteAddress() {
return remoteAddress;
}
@Override
public final Future<Integer> read(ByteBuffer dst) {
return channel.read(dst);
}
@Override
public final Future<Integer> write(ByteBuffer src) {
return channel.write(src);
}
@Override
public final void close() throws IOException {
if (client) {
if (pooledConnection == null) {
channel.close();
} else {
pooledConnection.fireConnectionClosed();
}
}
}
@Override
public void dispose() {
try {
this.close();
} catch (IOException io) {
}
}
@Override
public final boolean isOpen() {
return channel.isOpen();
}
};
}
public static AsyncConnection create(final AsynchronousSocketChannel ch) {
return create(ch, 0, 0);
}
public static AsyncConnection create(final AsynchronousSocketChannel ch, final int readTimeoutSecond0, final int writeTimeoutSecond0) {
return new AsyncConnection() {
private int readTimeoutSecond;
private int writeTimeoutSecond;
private final AsynchronousSocketChannel channel;
private final SocketAddress remoteAddress;
{
this.channel = ch;
this.readTimeoutSecond = readTimeoutSecond0;
this.writeTimeoutSecond = writeTimeoutSecond0;
SocketAddress addr = null;
try {
addr = ch.getRemoteAddress();
} catch (Exception e) {
//do nothing
}
this.remoteAddress = addr;
}
@Override
public <A> void read(ByteBuffer dst, A attachment, CompletionHandler<Integer, ? super A> handler) {
if (readTimeoutSecond > 0) {
channel.read(dst, readTimeoutSecond, TimeUnit.SECONDS, attachment, handler);
} else {
channel.read(dst, attachment, handler);
}
}
@Override
public <A> void write(ByteBuffer src, A attachment, CompletionHandler<Integer, ? super A> handler) {
if (writeTimeoutSecond > 0) {
channel.write(src, writeTimeoutSecond, TimeUnit.SECONDS, attachment, handler);
} else {
channel.write(src, attachment, handler);
}
}
@Override
public void setReadTimeoutSecond(int readTimeoutSecond) {
this.readTimeoutSecond = readTimeoutSecond;
}
@Override
public void setWriteTimeoutSecond(int writeTimeoutSecond) {
this.writeTimeoutSecond = writeTimeoutSecond;
}
@Override
public int getReadTimeoutSecond() {
return this.readTimeoutSecond;
}
@Override
public int getWriteTimeoutSecond() {
return this.writeTimeoutSecond;
}
@Override
public final SocketAddress getRemoteAddress() {
return remoteAddress;
}
@Override
public final Future<Integer> read(ByteBuffer dst) {
return channel.read(dst);
}
@Override
public final Future<Integer> write(ByteBuffer src) {
return channel.write(src);
}
@Override
public final void close() throws IOException {
if (pooledConnection == null) {
channel.close();
} else {
pooledConnection.fireConnectionClosed();
}
}
@Override
public final boolean isOpen() {
return channel.isOpen();
}
@Override
public void dispose() {
try {
this.close();
} catch (IOException io) {
}
}
};
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,50 @@
/*
* 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;
import java.io.IOException;
import java.util.Queue;
import java.util.concurrent.atomic.AtomicLong;
/**
*
* @author zhangjx
*/
public class AsyncPooledConnection implements AutoCloseable {
private final Queue<AsyncPooledConnection> queue;
private final AtomicLong usingCounter;
private final AsyncConnection conn;
public AsyncPooledConnection(Queue<AsyncPooledConnection> queue, AtomicLong usingCounter, AsyncConnection conn) {
this.conn = conn;
this.usingCounter = usingCounter;
this.queue = queue;
}
public AsyncConnection getAsyncConnection() {
return conn;
}
public void fireConnectionClosed() {
this.queue.add(this);
}
@Override
public void close() throws IOException {
usingCounter.decrementAndGet();
conn.close();
}
public void dispose() {
try {
this.close();
} catch (IOException io) {
}
}
}

View File

@@ -0,0 +1,74 @@
/*
* 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;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.logging.Level;
/**
*
* @author zhangjx
* @param <A>
*/
public final class AsyncWriteHandler<A> implements CompletionHandler<Integer, A> {
protected final ByteBuffer buffer;
protected final AsynchronousByteChannel channel;
protected final Context context;
protected final ByteBuffer buffer2;
protected final A attachment;
protected final CompletionHandler hander;
public AsyncWriteHandler(Context context, ByteBuffer buffer, AsynchronousByteChannel channel) {
this(context, buffer, channel, null, null, null);
}
public AsyncWriteHandler(Context context, ByteBuffer buffer, AsynchronousByteChannel channel, ByteBuffer buffer2, A attachment, CompletionHandler hander) {
this.buffer = buffer;
this.channel = channel;
this.context = context;
this.buffer2 = buffer2;
this.attachment = attachment;
this.hander = hander;
}
@Override
public void completed(Integer result, A attachment) {
if (buffer.hasRemaining()) {
this.channel.write(buffer, attachment, this);
return;
}
if (context != null) context.offerBuffer(buffer);
if (hander != null) {
if (buffer2 == null) {
hander.completed(result, attachment);
} else {
this.channel.write(buffer2, attachment, hander);
}
}
}
@Override
public void failed(Throwable exc, A attachment) {
if (context != null) context.offerBuffer(buffer);
if (hander == null) {
try {
this.channel.close();
} catch (Exception e) {
context.logger.log(Level.FINE, "AsyncWriteHandler close channel erroneous", e);
}
} else {
hander.failed(exc, attachment);
}
}
}

View File

@@ -0,0 +1,61 @@
/*
* 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;
import java.nio.ByteBuffer;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.atomic.*;
/**
*
* @author zhangjx
*/
public final class BufferPool {
private final int capacity;
private final ArrayBlockingQueue<ByteBuffer> queue;
private final AtomicLong creatCounter;
private final AtomicLong cycleCounter;
public BufferPool(AtomicLong creatCounter, AtomicLong cycleCounter, int capacity) {
this(creatCounter, cycleCounter, capacity, 0);
}
public BufferPool(AtomicLong creatCounter, AtomicLong cycleCounter, int capacity, int max) {
this.capacity = capacity;
this.queue = new ArrayBlockingQueue<>(Math.max(32, max));
this.creatCounter = creatCounter;
this.cycleCounter = cycleCounter;
}
public ByteBuffer poll() {
ByteBuffer result = queue.poll();
if (result == null) {
creatCounter.incrementAndGet();
result = ByteBuffer.allocateDirect(capacity);
}
return result;
}
public void offer(final ByteBuffer e) {
if (e != null && !e.isReadOnly() && e.capacity() == this.capacity) {
cycleCounter.incrementAndGet();
e.clear();
queue.offer(e);
}
}
public long getCreatCount() {
return creatCounter.longValue();
}
public long getCycleCount() {
return cycleCounter.longValue();
}
}

View File

@@ -0,0 +1,228 @@
/*
* 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;
import java.nio.*;
/**
*
* @author zhangjx
*/
public final class ChunkBuffer {
final ByteBuffer buffer;
private final BufferPool pool;
ChunkBuffer(BufferPool pool, ByteBuffer buffer) {
this.pool = pool;
this.buffer = buffer;
}
public void release() {
//pool.offer(this);
}
public int limit() {
return buffer.limit();
}
public void limit(int limit) {
buffer.limit(limit);
}
public int position() {
return buffer.position();
}
public void position(int position) {
buffer.position(position);
}
public void clear() {
buffer.clear();
}
public ChunkBuffer flip() {
buffer.flip();
return this;
}
public int remaining() {
return buffer.remaining();
}
public boolean hasRemaining() {
return buffer.hasRemaining();
}
public boolean isReadOnly() {
return buffer.isReadOnly();
}
public boolean hasArray() {
return buffer.hasArray();
}
public byte[] array() {
return buffer.array();
}
public int arrayOffset() {
return buffer.arrayOffset();
}
public boolean isDirect() {
return buffer.isDirect();
}
public ChunkBuffer slice() {
buffer.slice();
return this;
}
public ChunkBuffer duplicate() {
buffer.duplicate();
return this;
}
public ChunkBuffer asReadOnlyBuffer() {
buffer.asReadOnlyBuffer();
return this;
}
public byte get() {
return buffer.get();
}
public ChunkBuffer put(byte b) {
buffer.put(b);
return this;
}
public byte get(int index) {
return buffer.get(index);
}
public ChunkBuffer put(int index, byte b) {
buffer.put(index, b);
return this;
}
public ChunkBuffer compact() {
buffer.compact();
return this;
}
public char getChar() {
return buffer.getChar();
}
public ChunkBuffer putChar(char value) {
buffer.putChar(value);
return this;
}
public char getChar(int index) {
return buffer.getChar(index);
}
public ChunkBuffer putChar(int index, char value) {
buffer.putChar(index, value);
return this;
}
public short getShort() {
return buffer.getShort();
}
public ChunkBuffer putShort(short value) {
buffer.putShort(value);
return this;
}
public short getShort(int index) {
return buffer.getShort(index);
}
public ChunkBuffer putShort(int index, short value) {
buffer.putShort(index, value);
return this;
}
public int getInt() {
return buffer.getInt();
}
public ChunkBuffer putInt(int value) {
buffer.putInt(value);
return this;
}
public int getInt(int index) {
return buffer.getInt(index);
}
public ChunkBuffer putInt(int index, int value) {
buffer.putInt(index, value);
return this;
}
public long getLong() {
return buffer.getLong();
}
public ChunkBuffer putLong(long value) {
buffer.putLong(value);
return this;
}
public long getLong(int index) {
return buffer.getLong(index);
}
public ChunkBuffer putLong(int index, long value) {
buffer.putLong(index, value);
return this;
}
public float getFloat() {
return buffer.getFloat();
}
public ChunkBuffer putFloat(float value) {
buffer.putFloat(value);
return this;
}
public float getFloat(int index) {
return buffer.getFloat(index);
}
public ChunkBuffer putFloat(int index, float value) {
buffer.putFloat(index, value);
return this;
}
public double getDouble() {
return buffer.getDouble();
}
public ChunkBuffer putDouble(double value) {
buffer.putDouble(value);
return this;
}
public double getDouble(int index) {
return buffer.getDouble(index);
}
public ChunkBuffer putDouble(int index, double value) {
buffer.putDouble(index, value);
return this;
}
}

View File

@@ -0,0 +1,104 @@
/*
* 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;
import com.wentch.redkale.watch.WatchFactory;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.util.concurrent.ExecutorService;
import java.util.logging.*;
/**
*
* @author zhangjx
*/
public class Context {
private static final Charset UTF8 = Charset.forName("UTF-8");
protected final long serverStartTime;
protected final ExecutorService executor;
protected final BufferPool bufferPool;
protected final ResponsePool responsePool;
protected final PrepareServlet prepare;
private final InetSocketAddress address;
protected final Charset charset;
protected final int maxbody;
protected final int readTimeoutSecond;
protected final int writeTimeoutSecond;
protected final Logger logger;
protected final WatchFactory watch;
public Context(long serverStartTime, Logger logger, ExecutorService executor, BufferPool bufferPool, ResponsePool responsePool,
final int maxbody, Charset charset, InetSocketAddress address, final PrepareServlet prepare, final WatchFactory watch,
final int readTimeoutSecond, final int writeTimeoutSecond) {
this.serverStartTime = serverStartTime;
this.logger = logger;
this.executor = executor;
this.bufferPool = bufferPool;
this.responsePool = responsePool;
this.maxbody = maxbody;
this.charset = UTF8.equals(charset) ? null : charset;
this.address = address;
this.prepare = prepare;
this.watch = watch;
this.readTimeoutSecond = readTimeoutSecond;
this.writeTimeoutSecond = writeTimeoutSecond;
}
public int getMaxbody() {
return maxbody;
}
public InetSocketAddress getServerAddress() {
return address;
}
public long getServerStartTime() {
return serverStartTime;
}
public Charset getCharset() {
return charset;
}
public void submit(Runnable r) {
executor.submit(r);
}
public ByteBuffer pollBuffer() {
return bufferPool.poll();
}
public void offerBuffer(ByteBuffer buffer) {
bufferPool.offer(buffer);
}
public Logger getLogger() {
return logger;
}
public int getReadTimeoutSecond() {
return readTimeoutSecond;
}
public int getWriteTimeoutSecond() {
return writeTimeoutSecond;
}
}

View File

@@ -0,0 +1,99 @@
/*
* 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;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.logging.Level;
/**
*
* @author zhangjx
*/
@SuppressWarnings("unchecked")
public final class PrepareRunner implements Runnable {
private final AsyncConnection channel;
private final Context context;
private ByteBuffer data;
public PrepareRunner(Context context, AsyncConnection channel, ByteBuffer data) {
this.context = context;
this.channel = channel;
this.data = data;
}
@Override
public void run() {
final PrepareServlet prepare = context.prepare;
final ResponsePool responsePool = context.responsePool;
final ByteBuffer buffer = context.pollBuffer();
if (data != null) {
final Response response = responsePool.poll();
response.init(channel);
try {
prepare.prepare(data, response.request, response);
} catch (Throwable t) {
context.logger.log(Level.WARNING, "prepare servlet abort, forece to close channel ", t);
context.offerBuffer(buffer);
response.finish(true);
}
return;
}
try {
channel.read(buffer, null, new CompletionHandler<Integer, Void>() {
@Override
public void completed(Integer count, Void attachment1) {
if (count < 1 && buffer.remaining() == buffer.limit()) {
try {
context.offerBuffer(buffer);
channel.close();
} catch (Exception e) {
context.logger.log(Level.FINEST, "PrepareRunner close channel erroneous on no read bytes", e);
}
return;
}
// { //测试
// buffer.flip();
// byte[] bytes = new byte[buffer.remaining()];
// buffer.get(bytes);
// System.out.println(new String(bytes));
// }
buffer.flip();
final Response response = responsePool.poll();
response.init(channel);
try {
prepare.prepare(buffer, response.request, response);
} catch (Throwable t) {
context.logger.log(Level.WARNING, "prepare servlet abort, forece to close channel ", t);
context.offerBuffer(buffer);
response.finish(true);
}
}
@Override
public void failed(Throwable exc, Void attachment2) {
context.offerBuffer(buffer);
try {
channel.close();
} catch (Exception e) {
}
if (exc != null) context.logger.log(Level.FINEST, "Servlet Handler read channel erroneous, forece to close channel ", exc);
}
});
} catch (Exception te) {
context.offerBuffer(buffer);
try {
channel.close();
} catch (Exception e) {
}
if (te != null) context.logger.log(Level.FINEST, "Servlet read channel erroneous, forece to close channel ", te);
}
}
}

View File

@@ -0,0 +1,72 @@
/*
* 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;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.CompletionHandler;
import java.util.concurrent.atomic.*;
import java.util.logging.Level;
/**
*
* @author zhangjx
* @param <R>
* @param <P>
*/
public abstract class PrepareServlet<R extends Request, P extends Response<R>> implements Servlet<R, P> {
protected final AtomicLong executeCounter = new AtomicLong(); //执行请求次数
protected final AtomicLong illRequestCounter = new AtomicLong(); //错误请求次数
public final void prepare(final ByteBuffer buffer, final R request, final P response) throws IOException {
executeCounter.incrementAndGet();
final int rs = request.readHeader(buffer);
if (rs < 0) {
response.context.offerBuffer(buffer);
illRequestCounter.incrementAndGet();
response.finish(true);
} else if (rs == 0) {
response.context.offerBuffer(buffer);
this.execute(request, response);
} else {
buffer.clear();
final AtomicInteger ai = new AtomicInteger(rs);
request.channel.read(buffer, buffer, new CompletionHandler<Integer, ByteBuffer>() {
@Override
public void completed(Integer result, ByteBuffer attachment) {
buffer.flip();
ai.addAndGet(-buffer.remaining());
request.readBody(buffer);
if (ai.get() > 0) {
buffer.clear();
request.channel.read(buffer, buffer, this);
} else {
response.context.offerBuffer(buffer);
try {
execute(request, response);
} catch (Exception e) {
illRequestCounter.incrementAndGet();
response.finish(true);
request.context.logger.log(Level.WARNING, "prepare servlet abort, forece to close channel ", e);
}
}
}
@Override
public void failed(Throwable exc, ByteBuffer attachment) {
illRequestCounter.incrementAndGet();
response.context.offerBuffer(buffer);
response.finish(true);
if (exc != null) request.context.logger.log(Level.FINER, "Servlet read channel erroneous, forece to close channel ", exc);
}
});
}
}
}

View File

@@ -0,0 +1,159 @@
/*
* 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;
import java.io.IOException;
import java.net.*;
import java.nio.ByteBuffer;
import java.nio.channels.*;
/**
*
* @author zhangjx
*/
public abstract class ProtocolServer {
public abstract void open() throws IOException;
public abstract void bind(SocketAddress local, int backlog) throws IOException;
public abstract <T> void setOption(SocketOption<T> name, T value) throws IOException;
public abstract void accept();
public abstract void close() throws IOException;
public abstract AsynchronousChannelGroup getChannelGroup();
//---------------------------------------------------------------------
public static ProtocolServer create(String protocol, Context context) {
if ("TCP".equalsIgnoreCase(protocol)) return new ProtocolTCPServer(context);
if ("UDP".equalsIgnoreCase(protocol)) return new ProtocolUDPServer(context);
throw new RuntimeException("ProtocolServer not support protocol " + protocol);
}
private static final class ProtocolUDPServer extends ProtocolServer {
private final Context context;
private AsynchronousChannelGroup group;
private AsyncDatagramChannel serverChannel;
public ProtocolUDPServer(Context context) {
this.context = context;
}
@Override
public void open() throws IOException {
this.group = AsynchronousChannelGroup.withCachedThreadPool(context.executor, 1);
this.serverChannel = AsyncDatagramChannel.open(group);
}
@Override
public void bind(SocketAddress local, int backlog) throws IOException {
this.serverChannel.bind(local);
}
@Override
public <T> void setOption(SocketOption<T> name, T value) throws IOException {
this.serverChannel.setOption(name, value);
}
@Override
public void accept() {
final AsyncDatagramChannel serchannel = this.serverChannel;
final ByteBuffer buffer = this.context.pollBuffer();
serchannel.receive(buffer, buffer, new CompletionHandler<SocketAddress, ByteBuffer>() {
@Override
public void completed(final SocketAddress address, ByteBuffer attachment) {
final ByteBuffer buffer2 = context.pollBuffer();
serchannel.receive(buffer2, buffer2, this);
attachment.flip();
AsyncConnection conn = AsyncConnection.create(serchannel, address, false, context.readTimeoutSecond, context.writeTimeoutSecond);
context.submit(new PrepareRunner(context, conn, attachment));
}
@Override
public void failed(Throwable exc, ByteBuffer attachment) {
context.offerBuffer(attachment);
//if (exc != null) context.logger.log(Level.FINEST, AsyncDatagramChannel.class.getSimpleName() + " accept erroneous", exc);
}
});
}
@Override
public void close() throws IOException {
this.serverChannel.close();
}
@Override
public AsynchronousChannelGroup getChannelGroup() {
return this.group;
}
}
private static final class ProtocolTCPServer extends ProtocolServer {
private final Context context;
private AsynchronousChannelGroup group;
private AsynchronousServerSocketChannel serverChannel;
public ProtocolTCPServer(Context context) {
this.context = context;
}
@Override
public void open() throws IOException {
group = AsynchronousChannelGroup.withCachedThreadPool(context.executor, 1);
this.serverChannel = AsynchronousServerSocketChannel.open(group);
}
@Override
public void bind(SocketAddress local, int backlog) throws IOException {
this.serverChannel.bind(local, backlog);
}
@Override
public <T> void setOption(SocketOption<T> name, T value) throws IOException {
this.serverChannel.setOption(name, value);
}
@Override
public void accept() {
final AsynchronousServerSocketChannel serchannel = this.serverChannel;
serchannel.accept(null, new CompletionHandler<AsynchronousSocketChannel, Void>() {
@Override
public void completed(final AsynchronousSocketChannel channel, Void attachment) {
serchannel.accept(null, this);
context.submit(new PrepareRunner(context, AsyncConnection.create(channel, context.readTimeoutSecond, context.writeTimeoutSecond), null));
}
@Override
public void failed(Throwable exc, Void attachment) {
serchannel.accept(null, this);
//if (exc != null) context.logger.log(Level.FINEST, AsynchronousServerSocketChannel.class.getSimpleName() + " accept erroneous", exc);
}
});
}
@Override
public void close() throws IOException {
this.serverChannel.close();
}
@Override
public AsynchronousChannelGroup getChannelGroup() {
return this.group;
}
}
}

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.net;
import java.nio.ByteBuffer;
import java.util.*;
/**
*
* @author zhangjx
*/
public abstract class Request {
protected final Context context;
protected long createtime;
protected boolean keepAlive;
protected AsyncConnection channel;
protected final Map<String, Object> attributes = new HashMap<>();
protected Request(Context context) {
this.context = context;
}
/**
* 返回值: -1数据不合法 0解析完毕 >0: 需再读取的字节数。
*
* @param buffer
* @return
*/
protected abstract int readHeader(ByteBuffer buffer);
protected abstract void readBody(ByteBuffer buffer);
protected void recycle() {
createtime = 0;
keepAlive = false;
attributes.clear();
channel = null; // close it by response
}
public void setAttribute(String name, Object value) {
attributes.put(name, value);
}
@SuppressWarnings("unchecked")
public <T> T getAttribute(String name) {
return (T) attributes.get(name);
}
public void removeAttribute(String name) {
attributes.remove(name);
}
public Map<String, Object> getAttributes() {
return attributes;
}
public Context getContext() {
return this.context;
}
}

View File

@@ -0,0 +1,108 @@
/*
* 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;
import java.nio.ByteBuffer;
import java.nio.channels.*;
/**
*
* @author zhangjx
* @param <R>
*/
@SuppressWarnings("unchecked")
public abstract class Response<R extends Request> {
protected final Context context;
protected final R request;
protected AsyncConnection channel;
protected final CompletionHandler finishHandler = new CompletionHandler<Integer, ByteBuffer>() {
@Override
public void completed(Integer result, ByteBuffer attachment) {
if (attachment.hasRemaining()) {
channel.write(attachment, attachment, this);
} else {
Response.this.context.offerBuffer(attachment);
Response.this.finish();
}
}
@Override
public void failed(Throwable exc, ByteBuffer attachment) {
Response.this.context.offerBuffer(attachment);
Response.this.finish(true);
}
};
protected Response(Context context, final R request) {
this.context = context;
this.request = request;
}
protected AsyncConnection removeChannel() {
AsyncConnection ch = this.channel;
this.channel = null;
return ch;
}
protected void recycle() {
boolean keepAlive = request.keepAlive;
request.recycle();
if (channel != null) {
if (keepAlive) {
this.context.submit(new PrepareRunner(context, channel, null));
} else {
try {
if (channel.isOpen()) channel.close();
} catch (Exception e) {
}
channel = null;
}
}
}
protected void refuseAlive() {
this.request.keepAlive = false;
}
protected void init(AsyncConnection channel) {
this.channel = channel;
this.request.channel = channel;
this.request.createtime = System.currentTimeMillis();
}
public void finish() {
this.finish(false);
}
public void finish(boolean kill) {
//System.out.println("耗时: " + (System.currentTimeMillis() - request.createtime));
if (kill) refuseAlive();
this.context.responsePool.offer(this);
}
public void finish(ByteBuffer buffer) {
finish(buffer, false);
}
public void finish(ByteBuffer buffer, boolean kill) {
if (kill) refuseAlive();
send(buffer, buffer, finishHandler);
}
public <A> void send(ByteBuffer buffer, A attachment, CompletionHandler<Integer, A> handler) {
this.channel.write(buffer, attachment, handler);
}
public Context getContext() {
return context;
}
}

View File

@@ -0,0 +1,69 @@
/*
* 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;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.atomic.*;
/**
*
* @author zhangjx
* @param <T>
*/
public final class ResponsePool<T extends Response> {
public static interface ResponseFactory<T> {
T createResponse();
}
private final AtomicLong creatCounter;
private final AtomicLong cycleCounter;
private ResponseFactory<T> factory;
private final ArrayBlockingQueue<T> queue;
public ResponsePool(AtomicLong creatCounter, AtomicLong cycleCounter) {
this(creatCounter, cycleCounter, 0);
}
public ResponsePool(AtomicLong creatCounter, AtomicLong cycleCounter, int max) {
this.queue = new ArrayBlockingQueue<>(Math.max(32, max));
this.creatCounter = creatCounter;
this.cycleCounter = cycleCounter;
}
public void setResponseFactory(ResponseFactory<T> factory) {
this.factory = factory;
}
public T poll() {
T result = queue.poll();
if (result == null) {
creatCounter.incrementAndGet();
result = factory.createResponse();
}
return result;
}
public void offer(final T e) {
if (e != null) {
cycleCounter.incrementAndGet();
e.recycle();
queue.offer(e);
}
}
public long getCreatCount() {
return creatCounter.longValue();
}
public long getCycleCount() {
return cycleCounter.longValue();
}
}

View File

@@ -0,0 +1,38 @@
/*
* 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;
import java.security.*;
import javax.net.ssl.*;
/**
*
* @author zhangjx
*/
public class SSLBuilder {
private static char[] keypasswd;
public static void main(String[] args) throws Exception {
final KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(null, keypasswd);
final String algorithm = System.getProperty("ssl.algorithm", KeyManagerFactory.getDefaultAlgorithm());
final KeyManagerFactory kmf = KeyManagerFactory.getInstance(algorithm);
kmf.init(keyStore, keypasswd);
final SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(kmf.getKeyManagers(), null, new SecureRandom());
//-------------------------
final SSLEngine sslEngine = sslContext.createSSLEngine();
//sslEngine.setEnabledCipherSuites(null);
//sslEngine.setEnabledProtocols(null);
sslEngine.setUseClientMode(false);
sslEngine.setWantClientAuth(false);
sslEngine.setNeedClientAuth(false);
}
}

View File

@@ -0,0 +1,178 @@
/*
* 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;
import com.wentch.redkale.util.AnyValue;
import com.wentch.redkale.watch.WatchFactory;
import java.io.*;
import java.lang.reflect.Method;
import java.net.*;
import java.nio.charset.Charset;
import java.text.*;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.*;
/**
*
* @author zhangjx
*/
public abstract class Server {
public static final String RESNAME_ROOT = "SER_ROOT";
protected final Logger logger = Logger.getLogger(this.getClass().getSimpleName());
//-------------------------------------------------------------
protected final long serverStartTime;
protected final WatchFactory watch;
protected final String protocol;
protected AnyValue config;
protected Charset charset;
protected InetSocketAddress address;
protected Context context;
protected int backlog;
protected ProtocolServer transport;
protected int capacity;
protected int threads;
protected ExecutorService executor;
protected int bufferPoolSize;
protected int responsePoolSize;
protected int maxbody;
protected int readTimeoutSecond;
protected int writeTimeoutSecond;
private ScheduledThreadPoolExecutor scheduler;
protected Server(long serverStartTime, String protocol, final WatchFactory watch) {
this.serverStartTime = serverStartTime;
this.protocol = protocol;
this.watch = watch;
}
public void init(final AnyValue config) throws Exception {
Objects.requireNonNull(config);
this.config = config;
this.address = new InetSocketAddress(config.getValue("host", "0.0.0.0"), config.getIntValue("port", 80));
this.charset = Charset.forName(config.getValue("charset", "UTF-8"));
this.backlog = config.getIntValue("backlog", 10240);
this.readTimeoutSecond = config.getIntValue("readTimeoutSecond", 0);
this.writeTimeoutSecond = config.getIntValue("writeTimeoutSecond", 0);
this.capacity = config.getIntValue("capacity", 8 * 1024);
this.maxbody = config.getIntValue("maxbody", 64 * 1024);
this.threads = config.getIntValue("threads", Runtime.getRuntime().availableProcessors() * 16);
this.bufferPoolSize = config.getIntValue("bufferPoolSize", Runtime.getRuntime().availableProcessors() * 512);
this.responsePoolSize = config.getIntValue("responsePoolSize", Runtime.getRuntime().availableProcessors() * 256);
final int port = this.address.getPort();
final AtomicInteger counter = new AtomicInteger();
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()));
return t;
});
}
public void destroy(final AnyValue config) throws Exception {
if (scheduler != null) scheduler.shutdownNow();
}
public InetSocketAddress getSocketAddress() {
return address;
}
public Logger getLogger() {
return this.logger;
}
public void start() throws IOException {
this.context = this.createContext();
this.context.prepare.init(this.context, config);
this.watch.inject(this.context.prepare);
this.transport = ProtocolServer.create(this.protocol, context);
this.transport.open();
transport.setOption(StandardSocketOptions.SO_REUSEADDR, true);
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");
}
protected abstract Context createContext();
public void shutdown() throws IOException {
long s = System.currentTimeMillis();
logger.info(this.getClass().getSimpleName() + "-" + this.protocol + " shutdowning");
try {
this.transport.close();
} catch (Exception e) {
}
logger.info(this.getClass().getSimpleName() + "-" + this.protocol + " shutdow prepare servlet");
this.context.prepare.destroy(this.context, config);
long e = System.currentTimeMillis() - s;
logger.info(this.getClass().getSimpleName() + " shutdown in " + e + " ms");
}
protected Format createFormat() {
String sf = "0";
if (this.threads > 10) sf = "00";
if (this.threads > 100) sf = "000";
if (this.threads > 1000) sf = "0000";
return new DecimalFormat(sf);
}
public static void loadLib(final Logger logger, final String lib) throws Exception {
if (lib == null || lib.isEmpty()) return;
final Set<URL> set = new HashSet<>();
for (String s : lib.split(";")) {
if (s.isEmpty()) continue;
if (s.endsWith("*")) {
File root = new File(s.substring(0, s.length() - 1));
if (root.isDirectory()) {
for (File f : root.listFiles()) {
set.add(f.toURI().toURL());
}
}
} else {
File f = new File(s);
if (f.canRead()) set.add(f.toURI().toURL());
}
}
if (set.isEmpty()) return;
ClassLoader cl = Thread.currentThread().getContextClassLoader();
if (cl instanceof URLClassLoader) {
URLClassLoader loader = (URLClassLoader) cl;
final Method method = URLClassLoader.class.getDeclaredMethod("addURL", URL.class);
method.setAccessible(true);
for (URL url : set) {
method.invoke(loader, url);
//if (logger != null) logger.log(Level.INFO, "Server found ClassPath({0})", url);
}
} else {
Thread.currentThread().setContextClassLoader(new URLClassLoader(set.toArray(new URL[set.size()]), cl));
}
}
}

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.net;
import com.wentch.redkale.util.AnyValue;
import java.io.IOException;
/**
*
* @author zhangjx
* @param <R>
* @param <P>
*/
public interface Servlet<R extends Request, P extends Response<R>> {
default void init(Context context, AnyValue config) {
}
public void execute(R request, P response) throws IOException;
default void destroy(Context context, AnyValue config) {
}
}

View File

@@ -0,0 +1,138 @@
/*
* 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;
import com.wentch.redkale.watch.WatchFactory;
import java.io.IOException;
import java.net.*;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.*;
/**
* 传输客户端
*
* @author zhangjx
*/
public final class Transport {
protected SocketAddress[] remoteAddres;
protected BufferPool bufferPool;
protected String name;
protected String protocol;
protected final AsynchronousChannelGroup group;
public Transport(String name, String protocol, WatchFactory watch, int bufferPoolSize, SocketAddress... addresses) {
this.name = name;
this.protocol = protocol;
AsynchronousChannelGroup g = null;
try {
final AtomicInteger counter = new AtomicInteger();
ExecutorService executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() * 8, (Runnable r) -> {
Thread t = new Thread(r);
t.setDaemon(true);
t.setName("Transport-" + name + "-Thread-" + counter.incrementAndGet());
return t;
});
g = AsynchronousChannelGroup.withCachedThreadPool(executor, 1);
} catch (Exception e) {
throw new RuntimeException(e);
}
this.group = g;
AtomicLong createBufferCounter = watch == null ? new AtomicLong() : watch.createWatchNumber(Transport.class.getSimpleName() + "_" + protocol + ".Buffer.creatCounter");
AtomicLong cycleBufferCounter = watch == null ? new AtomicLong() : watch.createWatchNumber(Transport.class.getSimpleName() + "_" + protocol + ".Buffer.cycleCounter");
this.bufferPool = new BufferPool(createBufferCounter, cycleBufferCounter, 8192, bufferPoolSize);
this.remoteAddres = addresses;
}
public ByteBuffer pollBuffer() {
return bufferPool.poll();
}
public void offerBuffer(ByteBuffer buffer) {
bufferPool.offer(buffer);
}
public AsyncConnection pollConnection() {
SocketAddress addr = remoteAddres[0];
try {
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);
}
}
public void offerConnection(AsyncConnection conn) {
try {
conn.close();
} catch (IOException io) {
}
}
public <A> void async(final ByteBuffer buffer, A att, final CompletionHandler<Integer, A> handler) {
final AsyncConnection conn = pollConnection();
conn.write(buffer, buffer, new CompletionHandler<Integer, ByteBuffer>() {
@Override
public void completed(Integer result, ByteBuffer attachment) {
buffer.clear();
conn.read(buffer, buffer, new CompletionHandler<Integer, ByteBuffer>() {
@Override
public void completed(Integer result, ByteBuffer attachment) {
if (handler != null) handler.completed(result, att);
offerBuffer(buffer);
offerConnection(conn);
}
@Override
public void failed(Throwable exc, ByteBuffer attachment) {
offerBuffer(buffer);
offerConnection(conn);
}
});
}
@Override
public void failed(Throwable exc, ByteBuffer attachment) {
offerBuffer(buffer);
offerConnection(conn);
}
});
}
public ByteBuffer send(ByteBuffer buffer) {
AsyncConnection conn = pollConnection();
final int readto = conn.getReadTimeoutSecond();
final int writeto = conn.getWriteTimeoutSecond();
try {
conn.write(buffer).get(writeto > 0 ? writeto : 5, TimeUnit.SECONDS);
buffer.clear();
conn.read(buffer).get(readto > 0 ? readto : 5, TimeUnit.SECONDS);
buffer.flip();
return buffer;
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
offerConnection(conn);
}
}
}

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.net;
import java.util.concurrent.*;
/**
*
* @author zhangjx
*/
public class WorkThread extends Thread {
private final ExecutorService executor;
public WorkThread(ExecutorService executor, Runnable runner) {
super(runner);
this.executor = executor;
this.setDaemon(true);
}
public void submit(Runnable runner) {
executor.submit(runner);
}
}

View File

@@ -0,0 +1,22 @@
/*
* 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 java.lang.annotation.*;
import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
/**
*
* @author zhangjx
*/
@Inherited
@Documented
@Target({METHOD, TYPE})
@Retention(RUNTIME)
public @interface AuthIgnore {
}

View File

@@ -0,0 +1,196 @@
/*
* 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.net.Response;
import com.wentch.redkale.net.Request;
import com.wentch.redkale.net.Context;
import com.wentch.redkale.util.AnyValue;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.*;
import jdk.internal.org.objectweb.asm.*;
import static jdk.internal.org.objectweb.asm.Opcodes.*;
/**
*
* @author zhangjx
*/
public abstract class BasedHttpServlet extends HttpServlet {
private Map.Entry<String, Entry>[] actions;
@Override
public final void execute(HttpRequest request, HttpResponse response) throws IOException {
for (Map.Entry<String, Entry> en : actions) {
if (request.getRequestURI().startsWith(en.getKey())) {
Entry entry = en.getValue();
if (entry.ignore || authenticate(request, response)) entry.servlet.execute(request, response);
return;
}
}
throw new IOException(this.getClass().getName() + " not found method for URI(" + request.getRequestURI() + ")");
}
@Override
public void init(Context context, AnyValue config) {
String path = ((HttpContext) context).getContextPath();
WebServlet ws = this.getClass().getAnnotation(WebServlet.class);
if (ws != null && !ws.fillurl()) path = "";
HashMap<String, Entry> map = load();
this.actions = new Map.Entry[map.size()];
int i = -1;
for (Map.Entry<String, Entry> en : map.entrySet()) {
actions[++i] = new AbstractMap.SimpleEntry<>(path + en.getKey(), en.getValue());
}
}
public abstract boolean authenticate(HttpRequest request, HttpResponse response) throws IOException;
private HashMap<String, Entry> load() {
final boolean typeIgnore = this.getClass().getAnnotation(AuthIgnore.class) != null;
WebServlet module = this.getClass().getAnnotation(WebServlet.class);
final int serviceid = module == null ? 0 : module.moduleid();
final HashMap<String, Entry> map = new HashMap<>();
Set<String> nameset = new HashSet<>();
for (final Method method : this.getClass().getMethods()) {
//-----------------------------------------------
String methodname = method.getName();
if ("service".equals(methodname) || "execute".equals(methodname) || "authenticate".equals(methodname)) continue;
//-----------------------------------------------
Class[] paramTypes = method.getParameterTypes();
if (paramTypes.length != 2 || paramTypes[0] != HttpRequest.class
|| paramTypes[1] != HttpResponse.class) continue;
//-----------------------------------------------
Class[] exps = method.getExceptionTypes();
if (exps.length > 0 && (exps.length != 1 || exps[0] != IOException.class)) continue;
//-----------------------------------------------
String name = methodname;
int actionid = 0;
WebAction action = method.getAnnotation(WebAction.class);
if (action != null) {
actionid = action.actionid();
name = action.url().trim();
}
if (nameset.contains(name)) throw new RuntimeException(this.getClass().getSimpleName() + " has two same " + WebAction.class.getSimpleName() + "(" + name + ")");
for (String n : nameset) {
if (n.contains(name) || name.contains(n)) {
throw new RuntimeException(this.getClass().getSimpleName() + " has two overlap " + WebAction.class.getSimpleName() + "(" + name + ", " + n + ")");
}
}
nameset.add(name);
map.put(name, new Entry(typeIgnore, serviceid, actionid, name, method, createHttpServlet(method)));
}
return map;
}
private HttpServlet createHttpServlet(final Method method) {
//------------------------------------------------------------------------------
final String supDynName = HttpServlet.class.getName().replace('.', '/');
final String interName = this.getClass().getName().replace('.', '/');
final String interDesc = jdk.internal.org.objectweb.asm.Type.getDescriptor(this.getClass());
final String requestSupDesc = jdk.internal.org.objectweb.asm.Type.getDescriptor(Request.class);
final String responseSupDesc = jdk.internal.org.objectweb.asm.Type.getDescriptor(Response.class);
final String requestDesc = jdk.internal.org.objectweb.asm.Type.getDescriptor(HttpRequest.class);
final String responseDesc = jdk.internal.org.objectweb.asm.Type.getDescriptor(HttpResponse.class);
String newDynName = interName + "_Dyn_" + method.getName();
int i = 0;
for (;;) {
try {
Class.forName(newDynName.replace('/', '.'));
newDynName += "_" + (++i);
} catch (Exception ex) {
break;
}
}
//------------------------------------------------------------------------------
ClassWriter cw = new ClassWriter(0);
FieldVisitor fv;
MethodVisitor mv;
AnnotationVisitor av0;
final String factfield = "factServlet";
cw.visit(V1_8, ACC_PUBLIC + ACC_FINAL + ACC_SUPER, newDynName, null, supDynName, null);
{
fv = cw.visitField(ACC_PUBLIC, factfield, interDesc, null, null);
fv.visitEnd();
}
{ //构造函数
mv = (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();
}
{
mv = (cw.visitMethod(ACC_PUBLIC, "execute", "(" + requestDesc + responseDesc + ")V", null, new String[]{"java/io/IOException"}));
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, newDynName, factfield, interDesc);
mv.visitVarInsn(ALOAD, 1);
mv.visitVarInsn(ALOAD, 2);
mv.visitMethodInsn(INVOKEVIRTUAL, interName, method.getName(), "(" + requestDesc + responseDesc + ")V", false);
mv.visitInsn(RETURN);
mv.visitMaxs(3, 3);
mv.visitEnd();
}
{
mv = cw.visitMethod(ACC_PUBLIC + ACC_BRIDGE + ACC_SYNTHETIC, "execute", "(" + requestSupDesc + responseSupDesc + ")V", null, new String[]{"java/io/IOException"});
mv.visitVarInsn(ALOAD, 0);
mv.visitVarInsn(ALOAD, 1);
mv.visitTypeInsn(CHECKCAST, HttpRequest.class.getName().replace('.', '/'));
mv.visitVarInsn(ALOAD, 2);
mv.visitTypeInsn(CHECKCAST, HttpResponse.class.getName().replace('.', '/'));
mv.visitMethodInsn(INVOKEVIRTUAL, newDynName, "execute", "(" + requestDesc + responseDesc + ")V", false);
mv.visitInsn(RETURN);
mv.visitMaxs(3, 3);
mv.visitEnd();
}
cw.visitEnd();
//------------------------------------------------------------------------------
byte[] bytes = cw.toByteArray();
Class<?> newClazz = new ClassLoader(this.getClass().getClassLoader()) {
public final Class<?> loadClass(String name, byte[] b) {
return defineClass(name, b, 0, b.length);
}
}.loadClass(newDynName.replace('/', '.'), bytes);
try {
HttpServlet instance = (HttpServlet) newClazz.newInstance();
instance.getClass().getField(factfield).set(instance, this);
return instance;
} catch (Exception ex) {
throw new RuntimeException(ex);
}
}
private static final class Entry {
public Entry(boolean typeIgnore, int moduleid, int actionid, String name, Method method, HttpServlet servlet) {
this.moduleid = moduleid;
this.actionid = actionid;
this.name = name;
this.method = method;
this.servlet = servlet;
this.ignore = typeIgnore || method.getAnnotation(AuthIgnore.class) != null;
}
public boolean isNeedCheck() {
return this.moduleid != 0 || this.actionid != 0;
}
public final boolean ignore;
public final int moduleid;
public final int actionid;
public final String name;
public final Method method;
public final HttpServlet servlet;
}
}

View File

@@ -0,0 +1,136 @@
/*
* 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.Utility;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
/**
*
* @author zhangjx
*/
public final class ByteArray {
private byte[] content;
private int count;
public ByteArray() {
this(1024);
}
public ByteArray(int size) {
content = new byte[Math.max(128, size)];
}
public void clear() {
this.count = 0;
}
public int find(byte value) {
return find(0, value);
}
public boolean equal(final byte[] bytes) {
if (bytes == null || count != bytes.length) return false;
for (int i = 0; i < count; i++) {
if (content[i] != bytes[i]) return false;
}
return true;
}
public boolean isEmpty() {
return count == 0;
}
public int count() {
return count;
}
public void write(byte[] buf) {
System.arraycopy(this.content, 0, buf, 0, count);
}
public int find(int offset, char value) {
return find(offset, (byte) value);
}
public int find(int offset, byte value) {
return find(offset, -1, value);
}
public int find(int offset, int limit, char value) {
return find(offset, limit, (byte) value);
}
public int find(int offset, int limit, byte value) {
byte[] bytes = this.content;
int end = limit > 0 ? limit : count;
for (int i = offset; i < end; i++) {
if (bytes[i] == value) return i;
}
return -1;
}
public void add(byte value) {
if (count >= content.length - 1) {
byte[] ns = new byte[content.length + 8];
System.arraycopy(content, 0, ns, 0, count);
this.content = ns;
}
content[count++] = value;
}
public void add(ByteBuffer buffer, int len) {
if (len < 1) return;
if (count >= content.length - len) {
byte[] ns = new byte[content.length + len];
System.arraycopy(content, 0, ns, 0, count);
this.content = ns;
}
buffer.get(content, count, len);
count += len;
}
@Override
public String toString() {
return new String(content, 0, count);
}
public String toString(final int offset, int len, final Charset charset) {
if (charset == null) return new String(Utility.decodeUTF8(content, offset, len));
return new String(content, offset, len, charset);
}
public String toDecodeString(final int offset, int len, final Charset charset) {
int index = offset;
for (int i = offset; i < (offset + len); i++) {
if (content[i] == '+') {
content[index] = ' ';
} else if (content[i] == '%') {
content[index] = (byte) ((hexBit(content[++i]) * 16 + hexBit(content[++i])));
} else {
content[index] = content[i];
}
index++;
}
for (int i = index + 1; i < (offset + len); i++) {
content[i] = ' ';
}
len = index - offset;
if (charset == null) return new String(Utility.decodeUTF8(content, offset, len));
return new String(content, offset, len, charset);
}
private static int hexBit(byte b) {
if ('0' <= b && '9' >= b) return b - '0';
if ('a' <= b && 'z' >= b) return b - 'a' + 10;
if ('A' <= b && 'Z' >= b) return b - 'A' + 10;
return b;
}
}

View File

@@ -0,0 +1,69 @@
/*
* 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.convert.json.JsonConvert;
import com.wentch.redkale.convert.json.JsonFactory;
import com.wentch.redkale.net.PrepareServlet;
import com.wentch.redkale.net.ResponsePool;
import com.wentch.redkale.net.Context;
import com.wentch.redkale.net.BufferPool;
import com.wentch.redkale.util.Utility;
import com.wentch.redkale.watch.WatchFactory;
import java.net.InetSocketAddress;
import java.nio.charset.Charset;
import java.security.SecureRandom;
import java.util.concurrent.ExecutorService;
import java.util.logging.Logger;
/**
*
* @author zhangjx
*/
public final class HttpContext extends Context {
protected final String contextPath;
protected final JsonFactory jsonFactory;
protected final SecureRandom random = new SecureRandom();
public HttpContext(long serverStartTime, Logger logger, ExecutorService executor, BufferPool bufferPool,
ResponsePool 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;
this.jsonFactory = JsonFactory.root();
random.setSeed(Math.abs(System.nanoTime()));
}
public String getContextPath() {
return this.contextPath;
}
protected String createSessionid() {
byte[] bytes = new byte[16];
random.nextBytes(bytes);
return new String(Utility.binToHex(bytes));
}
protected WatchFactory getWatchFactory() {
return watch;
}
protected ExecutorService getExecutor() {
return executor;
}
protected ResponsePool getResponsePool() {
return responsePool;
}
public JsonConvert getJsonConvert() {
return jsonFactory.getConvert();
}
}

View File

@@ -0,0 +1,163 @@
/*
* 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.net.PrepareServlet;
import com.wentch.redkale.net.Context;
import com.wentch.redkale.util.AnyValue;
import com.wentch.redkale.util.Utility;
import com.wentch.redkale.util.AnyValue.DefaultAnyValue;
import com.wentch.redkale.watch.WatchFactory;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.*;
import java.util.AbstractMap.SimpleEntry;
import java.util.function.Predicate;
import java.util.logging.Level;
import java.util.regex.Pattern;
/**
*
* @author zhangjx
*/
public final class HttpPrepareServlet extends PrepareServlet<HttpRequest, HttpResponse> {
private ByteBuffer flashPolicyBuffer;
private final List<SimpleEntry<HttpServlet, AnyValue>> servlets = new ArrayList<>();
private final Map<String, HttpServlet> strmaps = new HashMap<>();
private SimpleEntry<Predicate<String>, HttpServlet>[] regArray = new SimpleEntry[0];
private boolean forwardproxy = false;
private final HttpServlet proxyServlet = new HttpProxyServlet();
private HttpServlet resourceHttpServlet = new HttpResourceServlet();
private String flashdomain = "*";
private String flashports = "80,443,$";
@Override
public void init(Context context, AnyValue config) {
this.servlets.stream().forEach((en) -> {
en.getKey().init(context, en.getValue());
});
final WatchFactory watch = ((HttpContext) context).getWatchFactory();
if (watch != null) {
this.servlets.stream().forEach((en) -> {
watch.inject(en.getKey());
});
}
if (config != null) {
AnyValue ssConfig = config.getAnyValue("servlets");
if (ssConfig != null) {
AnyValue resConfig = ssConfig.getAnyValue("resource-servlet");
if (resConfig instanceof DefaultAnyValue) {
if (resConfig.getValue("webroot") == null)
((DefaultAnyValue) resConfig).addValue("webroot", config.getValue("root"));
}
if (resConfig == null) {
DefaultAnyValue dresConfig = new DefaultAnyValue();
dresConfig.addValue("webroot", config.getValue("root"));
resConfig = dresConfig;
}
this.resourceHttpServlet.init(context, resConfig);
}
this.flashdomain = config.getValue("crossdomain-domain", "*");
this.flashports = config.getValue("crossdomain-ports", "80,443,$");
this.forwardproxy = config.getBoolValue("forwardproxy", false);
if (forwardproxy) this.proxyServlet.init(context, config);
}
}
@Override
public void execute(HttpRequest request, HttpResponse response) throws IOException {
try {
if (request.flashPolicy) {
response.skipHeader();
if (flashPolicyBuffer == null) {
flashPolicyBuffer = ByteBuffer.wrap(("<?xml version=\"1.0\"?>"
+ "<!DOCTYPE cross-domain-policy SYSTEM \"http://www.macromedia.com/xml/dtds/cross-domain-policy.dtd\">"
+ "<cross-domain-policy><allow-access-from domain=\"" + flashdomain + "\" to-ports=\""
+ flashports.replace("$", "" + request.getContext().getServerAddress().getPort()) + "\"/>"
+ "</cross-domain-policy>").getBytes()).asReadOnlyBuffer();
}
response.finish(flashPolicyBuffer.duplicate(), true);
return;
}
final String uri = request.getRequestURI();
if (forwardproxy && (uri.charAt(0) != '/' || "CONNECT".equalsIgnoreCase(request.getMethod()))) { //正向代理
proxyServlet.execute(request, response);
return;
}
HttpServlet servlet = this.strmaps.isEmpty() ? null : this.strmaps.get(uri);
if (servlet == null && this.regArray != null) {
for (SimpleEntry<Predicate<String>, HttpServlet> en : regArray) {
if (en.getKey().test(uri)) {
servlet = en.getValue();
break;
}
}
}
if (servlet == null) servlet = this.resourceHttpServlet;
servlet.execute(request, response);
} catch (Exception e) {
request.getContext().getLogger().log(Level.WARNING, "Servlet occur, forece to close channel ", e);
response.finish(505, null);
}
}
public void addHttpServlet(HttpServlet servlet, AnyValue conf, String... mappings) {
for (String mapping : mappings) {
if (contains(mapping, '.', '*', '{', '[', '(', '|', '^', '$', '+', '?', '\\')) { //是否是正则表达式))
if (mapping.charAt(0) != '^') mapping = '^' + mapping;
if (mapping.endsWith("/*")) {
mapping = mapping.substring(0, mapping.length() - 1) + ".*";
} else {
mapping += "$";
}
if (regArray == null) {
regArray = new SimpleEntry[1];
regArray[0] = new SimpleEntry<>(Pattern.compile(mapping).asPredicate(), servlet);
} else {
regArray = Arrays.copyOf(regArray, regArray.length + 1);
regArray[regArray.length - 1] = new SimpleEntry<>(Pattern.compile(mapping).asPredicate(), servlet);
}
} else if (mapping != null && !mapping.isEmpty()) {
strmaps.put(mapping, servlet);
}
}
this.servlets.add(new SimpleEntry<>(servlet, conf));
}
private static boolean contains(String string, char... values) {
if (string == null) return false;
for (char ch : Utility.charArray(string)) {
for (char ch2 : values) {
if (ch == ch2) return true;
}
}
return false;
}
public void setResourceServlet(HttpServlet servlet) {
if (servlet != null) {
this.resourceHttpServlet = servlet;
}
}
@Override
public void destroy(Context context, AnyValue config) {
this.resourceHttpServlet.destroy(context, config);
this.servlets.stream().forEach((en) -> {
en.getKey().destroy(context, en.getValue());
});
}
}

View File

@@ -0,0 +1,197 @@
/*
* 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.net.AsyncConnection;
import com.wentch.redkale.util.AnyValue;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.CompletionHandler;
/**
* 在appliation.xml中的HTTP类型的server节点加上forwardproxy="true"表示该HttpServer支持正向代理
*
* @author zhangjx
*/
public final class HttpProxyServlet extends HttpServlet {
@Override
public void execute(HttpRequest request, HttpResponse response) throws IOException {
response.skipHeader();
if ("CONNECT".equalsIgnoreCase(request.getMethod())) {
connect(request, response);
return;
}
String url = request.getRequestURI();
url = url.substring(url.indexOf("://") + 3);
url = url.substring(url.indexOf('/'));
final ByteBuffer buffer = response.getContext().pollBuffer();
buffer.put((request.getMethod() + " " + url + " HTTP/1.1\r\n").getBytes());
for (AnyValue.Entry<String> en : request.header.getStringEntrys()) {
if (!en.name.startsWith("Proxy-")) {
buffer.put((en.name + ": " + en.getValue() + "\r\n").getBytes());
}
}
if (request.getHost() != null) {
buffer.put(("Host: " + request.getHost() + "\r\n").getBytes());
}
if (request.getContentType() != null) {
buffer.put(("Content-Type: " + request.getContentType() + "\r\n").getBytes());
}
if (request.getContentLength() > 0) {
buffer.put(("Content-Length: " + request.getContentLength() + "\r\n").getBytes());
}
buffer.put(HttpResponse.LINE);
buffer.flip();
final AsyncConnection remote = AsyncConnection.create("TCP", request.getHostSocketAddress(), 6, 6);
remote.write(buffer, null, new CompletionHandler<Integer, Void>() {
@Override
public void completed(Integer result, Void attachment) {
if (buffer.hasRemaining()) {
remote.write(buffer, attachment, this);
return;
}
response.getContext().offerBuffer(buffer);
new ProxyCompletionHandler(remote, request, response).completed(0, null);
}
@Override
public void failed(Throwable exc, Void attachment) {
response.getContext().offerBuffer(buffer);
response.finish(true);
try {
remote.close();
} catch (IOException ex) {
}
}
});
}
private void connect(HttpRequest request, HttpResponse response) throws IOException {
final AsyncConnection remote = AsyncConnection.create("TCP", HttpRequest.parseSocketAddress(request.getRequestURI()), 6, 6);
final ByteBuffer buffer0 = response.getContext().pollBuffer();
buffer0.put("HTTP/1.1 200 Connection established\r\nConnection: close\r\n\r\n".getBytes());
buffer0.flip();
response.send(buffer0, null, new CompletionHandler<Integer, Void>() {
@Override
public void completed(Integer result, Void attachment) {
if (buffer0.hasRemaining()) {
response.send(buffer0, attachment, this);
return;
}
response.getContext().offerBuffer(buffer0);
new ProxyCompletionHandler(remote, request, response).completed(0, null);
}
@Override
public void failed(Throwable exc, Void attachment) {
response.getContext().offerBuffer(buffer0);
response.finish(true);
try {
remote.close();
} catch (IOException ex) {
}
}
});
}
private static class ProxyCompletionHandler implements CompletionHandler<Integer, Void> {
private AsyncConnection remote;
private HttpRequest request;
private HttpResponse response;
public ProxyCompletionHandler(AsyncConnection remote, HttpRequest request, HttpResponse response) {
this.remote = remote;
this.request = request;
this.response = response;
}
@Override
public void completed(Integer result0, Void v0) {
final ByteBuffer rbuffer = request.getContext().pollBuffer();
remote.read(rbuffer, null, new CompletionHandler<Integer, Void>() {
@Override
public void completed(Integer result, Void attachment) {
rbuffer.flip();
CompletionHandler parent = this;
response.send(rbuffer, null, new CompletionHandler<Integer, Void>() {
@Override
public void completed(Integer result, Void attachment) {
rbuffer.clear();
remote.read(rbuffer, attachment, parent);
}
@Override
public void failed(Throwable exc, Void attachment) {
parent.failed(exc, attachment);
}
});
}
@Override
public void failed(Throwable exc, Void attachment) {
response.getContext().offerBuffer(rbuffer);
response.finish(true);
try {
remote.close();
} catch (IOException ex) {
}
}
});
final ByteBuffer qbuffer = request.getContext().pollBuffer();
request.getChannel().read(qbuffer, null, new CompletionHandler<Integer, Void>() {
@Override
public void completed(Integer result, Void attachment) {
qbuffer.flip();
CompletionHandler parent = this;
remote.write(qbuffer, null, new CompletionHandler<Integer, Void>() {
@Override
public void completed(Integer result, Void attachment) {
qbuffer.clear();
request.getChannel().read(qbuffer, null, parent);
}
@Override
public void failed(Throwable exc, Void attachment) {
parent.failed(exc, attachment);
}
});
}
@Override
public void failed(Throwable exc, Void attachment) {
response.getContext().offerBuffer(qbuffer);
response.finish(true);
try {
remote.close();
} catch (IOException ex) {
}
}
});
}
@Override
public void failed(Throwable exc, Void v) {
response.finish(true);
try {
remote.close();
} catch (IOException ex) {
}
}
}
}

Some files were not shown because too many files have changed in this diff Show More