This commit is contained in:
@@ -1,572 +0,0 @@
|
|||||||
/*
|
|
||||||
* To change this license header, choose License Headers in Project Properties.
|
|
||||||
* To change this template file, choose Tools | Templates
|
|
||||||
* and open the template in the editor.
|
|
||||||
*/
|
|
||||||
package org.redkale.boot;
|
|
||||||
|
|
||||||
import org.redkale.boot.ClassFilter.FilterEntry;
|
|
||||||
import org.redkale.util.AnyValue.DefaultAnyValue;
|
|
||||||
import java.io.*;
|
|
||||||
import java.net.*;
|
|
||||||
import java.nio.*;
|
|
||||||
import java.nio.channels.*;
|
|
||||||
import java.nio.file.*;
|
|
||||||
import java.util.*;
|
|
||||||
import java.util.concurrent.*;
|
|
||||||
import java.util.concurrent.atomic.*;
|
|
||||||
import java.util.function.*;
|
|
||||||
import java.util.logging.*;
|
|
||||||
import javax.xml.parsers.*;
|
|
||||||
import org.redkale.convert.bson.*;
|
|
||||||
import org.redkale.convert.json.*;
|
|
||||||
import org.redkale.net.*;
|
|
||||||
import org.redkale.net.http.*;
|
|
||||||
import org.redkale.net.sncp.*;
|
|
||||||
import org.redkale.service.*;
|
|
||||||
import org.redkale.source.*;
|
|
||||||
import org.redkale.util.*;
|
|
||||||
import org.redkale.watch.*;
|
|
||||||
import org.w3c.dom.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 编译时需要加入: -XDignore.symbol.file=true
|
|
||||||
* <p>
|
|
||||||
* 进程启动类,程序启动后读取application.xml,进行classpath扫描动态加载Service与Servlet
|
|
||||||
* 优先加载所有SNCP协议的服务, 再加载其他协议服务,
|
|
||||||
* 最后进行Service、Servlet与其他资源之间的依赖注入。
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* @see http://www.redkale.org
|
|
||||||
* @author zhangjx
|
|
||||||
*/
|
|
||||||
public final class Application {
|
|
||||||
|
|
||||||
//当前进程启动的时间, 类型: long
|
|
||||||
public static final String RESNAME_APP_TIME = "APP_TIME";
|
|
||||||
|
|
||||||
//当前进程的根目录, 类型:String
|
|
||||||
public static final String RESNAME_APP_HOME = "APP_HOME";
|
|
||||||
|
|
||||||
//application.xml 文件中resources节点的内容, 类型: AnyValue
|
|
||||||
public static final String RESNAME_APP_GRES = "APP_GRES";
|
|
||||||
|
|
||||||
//当前进程节点的name, 类型:String
|
|
||||||
public static final String RESNAME_APP_NODE = "APP_NODE";
|
|
||||||
|
|
||||||
//当前进程节点的IP地址, 类型:InetAddress、String
|
|
||||||
public static final String RESNAME_APP_ADDR = "APP_ADDR";
|
|
||||||
|
|
||||||
//当前SNCP Server的IP地址+端口集合 类型: Map<InetSocketAddress, String>、HashMap<InetSocketAddress, String>
|
|
||||||
public static final String RESNAME_APP_NODES = "APP_NODES";
|
|
||||||
|
|
||||||
//当前Service的IP地址+端口 类型: SocketAddress、InetSocketAddress、String
|
|
||||||
public static final String RESNAME_SERVER_ADDR = "SERVER_ADDR"; // SERVER_ADDR
|
|
||||||
|
|
||||||
//当前SNCP Server所属的组 类型: String
|
|
||||||
public static final String RESNAME_SERVER_GROUP = "SERVER_GROUP";
|
|
||||||
|
|
||||||
//当前Service所属的组 类型: Set<String>、String[]
|
|
||||||
public static final String RESNAME_SNCP_GROUPS = Sncp.RESNAME_SNCP_GROUPS; // SNCP_GROUPS
|
|
||||||
|
|
||||||
protected final Map<InetSocketAddress, String> globalNodes = new HashMap<>();
|
|
||||||
|
|
||||||
private final Map<String, Set<InetSocketAddress>> globalGroups = new HashMap<>();
|
|
||||||
|
|
||||||
protected final List<Transport> transports = new ArrayList<>();
|
|
||||||
|
|
||||||
protected final InetAddress localAddress;
|
|
||||||
|
|
||||||
protected final List<DataSource> sources = new CopyOnWriteArrayList<>();
|
|
||||||
|
|
||||||
protected final List<NodeServer> servers = new CopyOnWriteArrayList<>();
|
|
||||||
|
|
||||||
protected CountDownLatch servicecdl; //会出现两次赋值
|
|
||||||
|
|
||||||
//--------------------------------------------------------------------------------------------
|
|
||||||
private final ResourceFactory factory = ResourceFactory.root();
|
|
||||||
|
|
||||||
private final WatchFactory watch = WatchFactory.root();
|
|
||||||
|
|
||||||
private File home;
|
|
||||||
|
|
||||||
private final Logger logger;
|
|
||||||
|
|
||||||
private final AnyValue config;
|
|
||||||
|
|
||||||
private final long startTime = System.currentTimeMillis();
|
|
||||||
|
|
||||||
private final CountDownLatch serversLatch;
|
|
||||||
|
|
||||||
private Application(final AnyValue config) {
|
|
||||||
this.config = config;
|
|
||||||
|
|
||||||
final File root = new File(System.getProperty(RESNAME_APP_HOME));
|
|
||||||
this.factory.register(RESNAME_APP_TIME, long.class, this.startTime);
|
|
||||||
this.factory.register(RESNAME_APP_HOME, Path.class, root.toPath());
|
|
||||||
this.factory.register(RESNAME_APP_HOME, File.class, root);
|
|
||||||
try {
|
|
||||||
this.factory.register(RESNAME_APP_HOME, root.getCanonicalPath());
|
|
||||||
this.home = root.getCanonicalFile();
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
String localaddr = config.getValue("address", "").trim();
|
|
||||||
this.localAddress = localaddr.isEmpty() ? Utility.localInetAddress() : new InetSocketAddress(localaddr, 0).getAddress();
|
|
||||||
Application.this.factory.register(RESNAME_APP_ADDR, Application.this.localAddress.getHostAddress());
|
|
||||||
Application.this.factory.register(RESNAME_APP_ADDR, InetAddress.class, Application.this.localAddress);
|
|
||||||
{
|
|
||||||
String node = config.getValue("node", "").trim();
|
|
||||||
if (node.isEmpty()) {
|
|
||||||
StringBuilder sb = new StringBuilder();
|
|
||||||
byte[] bs = this.localAddress.getAddress();
|
|
||||||
int v1 = bs[bs.length - 2] & 0xff;
|
|
||||||
int v2 = bs[bs.length - 1] & 0xff;
|
|
||||||
if (v1 <= 0xf) sb.append('0');
|
|
||||||
sb.append(Integer.toHexString(v1));
|
|
||||||
if (v2 <= 0xf) sb.append('0');
|
|
||||||
sb.append(Integer.toHexString(v2));
|
|
||||||
node = sb.toString();
|
|
||||||
}
|
|
||||||
Application.this.factory.register(RESNAME_APP_NODE, node);
|
|
||||||
System.setProperty(RESNAME_APP_NODE, node);
|
|
||||||
}
|
|
||||||
//以下是初始化日志配置
|
|
||||||
final File logconf = new File(root, "conf/logging.properties");
|
|
||||||
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();
|
|
||||||
final 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());
|
|
||||||
});
|
|
||||||
}
|
|
||||||
properties.put(SncpClient.class.getSimpleName() + ".handlers", LogFileHandler.SncpLogFileHandler.class.getName());
|
|
||||||
}
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.logger = Logger.getLogger(this.getClass().getSimpleName());
|
|
||||||
this.serversLatch = new CountDownLatch(config.getAnyValues("server").length + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ResourceFactory getResourceFactory() {
|
|
||||||
return factory;
|
|
||||||
}
|
|
||||||
|
|
||||||
public WatchFactory getWatchFactory() {
|
|
||||||
return watch;
|
|
||||||
}
|
|
||||||
|
|
||||||
public File getHome() {
|
|
||||||
return home;
|
|
||||||
}
|
|
||||||
|
|
||||||
public long getStartTime() {
|
|
||||||
return startTime;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initLogging() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public void init() throws Exception {
|
|
||||||
System.setProperty("java.util.concurrent.ForkJoinPool.common.parallelism", "" + Runtime.getRuntime().availableProcessors() * 4);
|
|
||||||
System.setProperty("convert.bson.tiny", "true");
|
|
||||||
System.setProperty("convert.json.tiny", "true");
|
|
||||||
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");
|
|
||||||
|
|
||||||
File persist = new File(this.home, "conf/persistence.xml");
|
|
||||||
final String homepath = this.home.getCanonicalPath();
|
|
||||||
if (persist.isFile()) System.setProperty(DataDefaultSource.DATASOURCE_CONFPATH, persist.getCanonicalPath());
|
|
||||||
logger.log(Level.INFO, RESNAME_APP_HOME + "=" + homepath + "\r\n" + RESNAME_APP_ADDR + "=" + this.localAddress.getHostAddress());
|
|
||||||
String lib = config.getValue("lib", "").trim().replace("${APP_HOME}", homepath);
|
|
||||||
lib = lib.isEmpty() ? (homepath + "/conf") : (lib + ";" + homepath + "/conf");
|
|
||||||
Server.loadLib(logger, lib);
|
|
||||||
initLogging();
|
|
||||||
if (this.localAddress != null) {
|
|
||||||
byte[] bs = this.localAddress.getAddress();
|
|
||||||
int v = (0xff & bs[bs.length - 2]) % 10 * 100 + (0xff & bs[bs.length - 1]);
|
|
||||||
this.factory.register("property.datasource.nodeid", "" + v);
|
|
||||||
}
|
|
||||||
//------------------------------------------------------------------------
|
|
||||||
final AnyValue resources = config.getAnyValue("resources");
|
|
||||||
if (resources != null) {
|
|
||||||
factory.register(RESNAME_APP_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 if (name.startsWith("mimetype.property.")) {
|
|
||||||
MimeType.add(name.substring("mimetype.property.".length()), value);
|
|
||||||
} else {
|
|
||||||
factory.register("property." + name, value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.factory.register(BsonFactory.root());
|
|
||||||
this.factory.register(JsonFactory.root());
|
|
||||||
this.factory.register(BsonFactory.root().getConvert());
|
|
||||||
this.factory.register(JsonFactory.root().getConvert());
|
|
||||||
initResources();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initResources() throws Exception {
|
|
||||||
//-------------------------------------------------------------------------
|
|
||||||
final AnyValue resources = config.getAnyValue("resources");
|
|
||||||
if (resources != null) {
|
|
||||||
//------------------------------------------------------------------------
|
|
||||||
|
|
||||||
for (AnyValue conf : resources.getAnyValues("group")) {
|
|
||||||
final String group = conf.getValue("name", "");
|
|
||||||
String protocol = conf.getValue("protocol", Transport.DEFAULT_PROTOCOL).toUpperCase();
|
|
||||||
if (!"TCP".equalsIgnoreCase(protocol) && !"UDP".equalsIgnoreCase(protocol)) {
|
|
||||||
throw new RuntimeException("Not supported Transport Protocol " + conf.getValue("protocol"));
|
|
||||||
}
|
|
||||||
Set<InetSocketAddress> addrs = globalGroups.get(group);
|
|
||||||
if (addrs == null) {
|
|
||||||
addrs = new LinkedHashSet<>();
|
|
||||||
globalGroups.put(group, addrs);
|
|
||||||
}
|
|
||||||
for (AnyValue node : conf.getAnyValues("node")) {
|
|
||||||
final InetSocketAddress addr = new InetSocketAddress(node.getValue("addr"), node.getIntValue("port"));
|
|
||||||
addrs.add(addr);
|
|
||||||
String oldgroup = globalNodes.get(addr);
|
|
||||||
if (oldgroup != null) throw new RuntimeException(addr + " had one more group " + (globalNodes.get(addr)));
|
|
||||||
globalNodes.put(addr, group);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//------------------------------------------------------------------------
|
|
||||||
}
|
|
||||||
|
|
||||||
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.socket().setSoTimeout(3000);
|
|
||||||
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();
|
|
||||||
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");
|
|
||||||
application.serversLatch.countDown();
|
|
||||||
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");
|
|
||||||
CountDownLatch timecd = new CountDownLatch(entrys.length);
|
|
||||||
final List<AnyValue> sncps = new ArrayList<>();
|
|
||||||
final List<AnyValue> others = new ArrayList<>();
|
|
||||||
for (final AnyValue entry : entrys) {
|
|
||||||
if (entry.getValue("protocol", "").toUpperCase().startsWith("SNCP")) {
|
|
||||||
sncps.add(entry);
|
|
||||||
} else {
|
|
||||||
others.add(entry);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!sncps.isEmpty() && globalNodes.isEmpty()) throw new RuntimeException("found SNCP Server node but not found <group> node info.");
|
|
||||||
|
|
||||||
factory.register(RESNAME_APP_NODES, new TypeToken<Map<InetSocketAddress, String>>() {
|
|
||||||
}.getType(), globalNodes);
|
|
||||||
factory.register(RESNAME_APP_NODES, new TypeToken<HashMap<InetSocketAddress, String>>() {
|
|
||||||
}.getType(), globalNodes);
|
|
||||||
|
|
||||||
factory.register(RESNAME_APP_NODES, new TypeToken<Map<String, Set<InetSocketAddress>>>() {
|
|
||||||
}.getType(), globalGroups);
|
|
||||||
factory.register(RESNAME_APP_NODES, new TypeToken<HashMap<String, Set<InetSocketAddress>>>() {
|
|
||||||
}.getType(), globalGroups);
|
|
||||||
|
|
||||||
runServers(timecd, sncps); //必须确保sncp都启动后再启动其他协议
|
|
||||||
runServers(timecd, others);
|
|
||||||
timecd.await();
|
|
||||||
logger.info(this.getClass().getSimpleName() + " started in " + (System.currentTimeMillis() - startTime) + " ms");
|
|
||||||
this.serversLatch.await();
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
private void runServers(CountDownLatch timecd, final List<AnyValue> serconfs) throws Exception {
|
|
||||||
this.servicecdl = new CountDownLatch(serconfs.size());
|
|
||||||
CountDownLatch sercdl = new CountDownLatch(serconfs.size());
|
|
||||||
final AtomicBoolean inited = new AtomicBoolean(false);
|
|
||||||
final Map<String, Class<? extends NodeServer>> nodeClasses = new HashMap<>();
|
|
||||||
for (final AnyValue serconf : serconfs) {
|
|
||||||
Thread thread = new Thread() {
|
|
||||||
{
|
|
||||||
String host = serconf.getValue("host", "").replace("0.0.0.0", "[0]");
|
|
||||||
setName(serconf.getValue("protocol", "Server").toUpperCase() + "-" + host + ":" + serconf.getIntValue("port") + "-Thread");
|
|
||||||
this.setDaemon(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
try {
|
|
||||||
//Thread ctd = Thread.currentThread();
|
|
||||||
//ctd.setContextClassLoader(new URLClassLoader(new URL[0], ctd.getContextClassLoader()));
|
|
||||||
final String protocol = serconf.getValue("protocol", "").replaceFirst("\\..+", "").toUpperCase();
|
|
||||||
NodeServer server = null;
|
|
||||||
if ("SNCP".equals(protocol)) {
|
|
||||||
server = new NodeSncpServer(Application.this, serconf);
|
|
||||||
} else if ("HTTP".equals(protocol)) {
|
|
||||||
server = new NodeHttpServer(Application.this, serconf);
|
|
||||||
} else {
|
|
||||||
if (!inited.get()) {
|
|
||||||
synchronized (nodeClasses) {
|
|
||||||
if (!inited.get()) {
|
|
||||||
inited.set(true);
|
|
||||||
ClassFilter profilter = new ClassFilter(NodeProtocol.class, NodeServer.class);
|
|
||||||
ClassFilter.Loader.load(home, profilter);
|
|
||||||
final Set<FilterEntry<NodeServer>> entrys = profilter.getFilterEntrys();
|
|
||||||
for (FilterEntry<NodeServer> entry : entrys) {
|
|
||||||
final Class<? extends NodeServer> type = entry.getType();
|
|
||||||
NodeProtocol pros = type.getAnnotation(NodeProtocol.class);
|
|
||||||
for (String p : pros.value()) {
|
|
||||||
p = p.toUpperCase();
|
|
||||||
if ("SNCP".equals(p) || "HTTP".equals(p)) continue;
|
|
||||||
final Class<? extends NodeServer> old = nodeClasses.get(p);
|
|
||||||
if (old != null && old != type) throw new RuntimeException("Protocol(" + p + ") had NodeServer-Class(" + old.getName() + ") but repeat NodeServer-Class(" + type.getName() + ")");
|
|
||||||
nodeClasses.put(p, type);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Class<? extends NodeServer> nodeClass = nodeClasses.get(protocol);
|
|
||||||
if (nodeClass != null) server = NodeServer.create(nodeClass, Application.this, serconf);
|
|
||||||
}
|
|
||||||
if (server == null) {
|
|
||||||
logger.log(Level.SEVERE, "Not found Server Class for protocol({0})", serconf.getValue("protocol"));
|
|
||||||
System.exit(0);
|
|
||||||
}
|
|
||||||
servers.add(server);
|
|
||||||
server.init(serconf);
|
|
||||||
server.start();
|
|
||||||
timecd.countDown();
|
|
||||||
sercdl.countDown();
|
|
||||||
} catch (Exception ex) {
|
|
||||||
logger.log(Level.WARNING, serconf + " runServers error", ex);
|
|
||||||
Application.this.serversLatch.countDown();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
thread.start();
|
|
||||||
}
|
|
||||||
sercdl.await();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static <T extends Service> T singleton(Class<T> serviceClass) throws Exception {
|
|
||||||
return singleton(serviceClass, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static <T extends Service> T singleton(Class<T> serviceClass, boolean remote) throws Exception {
|
|
||||||
final Application application = Application.create();
|
|
||||||
Consumer<Runnable> executor = (x) -> Executors.newFixedThreadPool(8).submit(x);
|
|
||||||
T service = remote ? Sncp.createRemoteService("", executor, serviceClass, null, new LinkedHashSet<>(), null)
|
|
||||||
: Sncp.createLocalService("", executor, serviceClass, null, new LinkedHashSet<>(), null, null);
|
|
||||||
application.init();
|
|
||||||
application.factory.register(service);
|
|
||||||
application.servicecdl = new CountDownLatch(1);
|
|
||||||
final NodeServer server = new NodeHttpServer(application, null);
|
|
||||||
server.init(application.config);
|
|
||||||
server.factory.inject(service, server);
|
|
||||||
return service;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Application create() throws IOException {
|
|
||||||
final String home = new File(System.getProperty(RESNAME_APP_HOME, "")).getCanonicalPath();
|
|
||||||
System.setProperty(RESNAME_APP_HOME, home);
|
|
||||||
File appfile = new File(home, "conf/application.xml");
|
|
||||||
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();
|
|
||||||
try {
|
|
||||||
application.start();
|
|
||||||
} catch (Exception e) {
|
|
||||||
application.logger.log(Level.SEVERE, "Application start error", e);
|
|
||||||
System.exit(0);
|
|
||||||
}
|
|
||||||
System.exit(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
Set<InetSocketAddress> findGlobalGroup(String group) {
|
|
||||||
if (group == null) return null;
|
|
||||||
Set<InetSocketAddress> set = globalGroups.get(group);
|
|
||||||
return set == null ? null : new LinkedHashSet<>(set);
|
|
||||||
}
|
|
||||||
|
|
||||||
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 {
|
|
||||||
serversLatch.countDown();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
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_APP_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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,437 +0,0 @@
|
|||||||
/*
|
|
||||||
* To change this license header, choose License Headers in Project Properties.
|
|
||||||
* To change this template file, choose Tools | Templates
|
|
||||||
* and open the template in the editor.
|
|
||||||
*/
|
|
||||||
package org.redkale.boot;
|
|
||||||
|
|
||||||
import org.redkale.util.Ignore;
|
|
||||||
import org.redkale.util.AutoLoad;
|
|
||||||
import org.redkale.util.AnyValue;
|
|
||||||
import org.redkale.util.AnyValue.DefaultAnyValue;
|
|
||||||
import java.io.*;
|
|
||||||
import java.lang.annotation.*;
|
|
||||||
import java.lang.reflect.*;
|
|
||||||
import java.net.*;
|
|
||||||
import java.util.*;
|
|
||||||
import java.util.concurrent.*;
|
|
||||||
import java.util.jar.*;
|
|
||||||
import java.util.logging.*;
|
|
||||||
import java.util.regex.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* class过滤器, 符合条件的class会保留下来存入FilterEntry。
|
|
||||||
*
|
|
||||||
* @see http://www.redkale.org
|
|
||||||
* @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;
|
|
||||||
|
|
||||||
private List<ClassFilter> ors;
|
|
||||||
|
|
||||||
private List<ClassFilter> ands;
|
|
||||||
|
|
||||||
private AnyValue conf;
|
|
||||||
|
|
||||||
public ClassFilter(Class<? extends Annotation> annotationClass, Class superClass) {
|
|
||||||
this(annotationClass, superClass, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ClassFilter(Class<? extends Annotation> annotationClass, Class superClass, AnyValue conf) {
|
|
||||||
this.annotationClass = annotationClass;
|
|
||||||
this.superClass = superClass;
|
|
||||||
this.conf = conf;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ClassFilter<T> or(ClassFilter<T> filter) {
|
|
||||||
if (ors == null) ors = new ArrayList<>();
|
|
||||||
ors.add(filter);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ClassFilter<T> and(ClassFilter<T> filter) {
|
|
||||||
if (ands == null) ands = new ArrayList<>();
|
|
||||||
ands.add(filter);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取符合条件的class集合
|
|
||||||
* <p>
|
|
||||||
* @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) {
|
|
||||||
boolean r = accept0(property, clazzname);
|
|
||||||
ClassFilter cf = r ? this : null;
|
|
||||||
if (r && ands != null) {
|
|
||||||
for (ClassFilter filter : ands) {
|
|
||||||
if (!filter.accept(property, clazzname)) return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!r && ors != null) {
|
|
||||||
for (ClassFilter filter : ors) {
|
|
||||||
if (filter.accept(property, clazzname)) {
|
|
||||||
cf = filter;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (cf == null) return;
|
|
||||||
try {
|
|
||||||
Class clazz = Class.forName(clazzname);
|
|
||||||
if (!cf.accept(property, clazz, autoscan)) return;
|
|
||||||
if (cf.conf != null) {
|
|
||||||
if (property == null) {
|
|
||||||
property = cf.conf;
|
|
||||||
} else if (property instanceof DefaultAnyValue) {
|
|
||||||
((DefaultAnyValue) property).addAll(cf.conf);
|
|
||||||
} else {
|
|
||||||
DefaultAnyValue dav = new DefaultAnyValue();
|
|
||||||
dav.addAll(property);
|
|
||||||
dav.addAll(cf.conf);
|
|
||||||
property = dav;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
entrys.add(new FilterEntry(clazz, autoscan, property));
|
|
||||||
} 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) {
|
|
||||||
boolean r = accept0(property, classname);
|
|
||||||
if (r && ands != null) {
|
|
||||||
for (ClassFilter filter : ands) {
|
|
||||||
if (!filter.accept(property, classname)) return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!r && ors != null) {
|
|
||||||
for (ClassFilter filter : ors) {
|
|
||||||
if (filter.accept(property, classname)) return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean accept0(AnyValue property, String classname) {
|
|
||||||
if (this.refused) return false;
|
|
||||||
if (classname.startsWith("java.") || classname.startsWith("javax.")) return false;
|
|
||||||
if (excludePatterns != null) {
|
|
||||||
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 HashSet<String> groups = new LinkedHashSet<>();
|
|
||||||
|
|
||||||
private final String name;
|
|
||||||
|
|
||||||
private final Class<T> type;
|
|
||||||
|
|
||||||
private final AnyValue property;
|
|
||||||
|
|
||||||
private final boolean autoload;
|
|
||||||
|
|
||||||
public FilterEntry(Class<T> type, AnyValue property) {
|
|
||||||
this(type, false, property);
|
|
||||||
}
|
|
||||||
|
|
||||||
public FilterEntry(Class<T> type, final boolean autoload, AnyValue property) {
|
|
||||||
this.type = type;
|
|
||||||
String str = property == null ? null : property.getValue("groups");
|
|
||||||
if (str != null) groups.addAll(Arrays.asList(str.split(";")));
|
|
||||||
this.property = property;
|
|
||||||
this.autoload = autoload;
|
|
||||||
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 + ", groups=" + groups + "]";
|
|
||||||
}
|
|
||||||
|
|
||||||
@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.groups.equals(((FilterEntry<?>) obj).groups) && this.name.equals(((FilterEntry<?>) obj).name));
|
|
||||||
}
|
|
||||||
|
|
||||||
public Class<T> getType() {
|
|
||||||
return type;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getName() {
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
|
|
||||||
public AnyValue getProperty() {
|
|
||||||
return property;
|
|
||||||
}
|
|
||||||
|
|
||||||
public HashSet<String> getGroups() {
|
|
||||||
return groups;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isAutoload() {
|
|
||||||
return autoload;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* class加载类
|
|
||||||
*/
|
|
||||||
public static class Loader {
|
|
||||||
|
|
||||||
protected static final Logger logger = Logger.getLogger(Loader.class.getName());
|
|
||||||
|
|
||||||
protected static final ConcurrentMap<URL, Set<String>> cache = new ConcurrentHashMap<>();
|
|
||||||
|
|
||||||
public static void close() {
|
|
||||||
cache.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 加载当前线程的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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
String selfpkg0 = ClassFilter.class.getPackage().getName();
|
|
||||||
selfpkg0 = selfpkg0.substring(0, selfpkg0.lastIndexOf('.') + 1);
|
|
||||||
final String selfpkg = selfpkg0;
|
|
||||||
List<File> files = new ArrayList<>();
|
|
||||||
boolean debug = logger.isLoggable(Level.FINEST);
|
|
||||||
StringBuilder debugstr = new StringBuilder();
|
|
||||||
for (final URL url : urljares) {
|
|
||||||
Set<String> classes = cache.get(url);
|
|
||||||
if (classes == null) {
|
|
||||||
synchronized (cache) {
|
|
||||||
if (cache.get(url) == null) {
|
|
||||||
classes = new CopyOnWriteArraySet<>();
|
|
||||||
cache.put(url, classes);
|
|
||||||
} else {
|
|
||||||
classes = cache.get(url);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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("com.sun.") || classname.startsWith(selfpkg) || classname.startsWith("com.mysql.")) continue;
|
|
||||||
classes.add(classname);
|
|
||||||
if (debug) debugstr.append(classname).append("\r\n");
|
|
||||||
for (final ClassFilter filter : filters) {
|
|
||||||
if (filter != null) filter.filter(null, classname);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for (String classname : classes) {
|
|
||||||
for (final ClassFilter filter : filters) {
|
|
||||||
if (filter != null) filter.filter(null, classname);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (final URL url : urlfiles) {
|
|
||||||
Set<String> classes = cache.get(url);
|
|
||||||
if (classes == null) {
|
|
||||||
synchronized (cache) {
|
|
||||||
if (cache.get(url) == null) {
|
|
||||||
classes = new CopyOnWriteArraySet<>();
|
|
||||||
cache.put(url, classes);
|
|
||||||
} else {
|
|
||||||
classes = cache.get(url);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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;
|
|
||||||
classes.add(classname);
|
|
||||||
if (debug) debugstr.append(classname).append("\r\n");
|
|
||||||
for (final ClassFilter filter : filters) {
|
|
||||||
if (filter != null) filter.filter(null, classname);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for (String classname : classes) {
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,268 +0,0 @@
|
|||||||
/*
|
|
||||||
* To change this license header, choose License Headers in Project Properties.
|
|
||||||
* To change this template file, choose Tools | Templates
|
|
||||||
* and open the template in the editor.
|
|
||||||
*/
|
|
||||||
package org.redkale.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>
|
|
||||||
* @see http://www.redkale.org
|
|
||||||
* @author zhangjx
|
|
||||||
*/
|
|
||||||
public class LogFileHandler extends Handler {
|
|
||||||
|
|
||||||
public static class SncpLogFileHandler extends LogFileHandler {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getPrefix() {
|
|
||||||
return "sncp-";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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() {
|
|
||||||
final String name = "Logging-" + getClass().getSimpleName() + "-Thread";
|
|
||||||
new Thread() {
|
|
||||||
{
|
|
||||||
setName(name);
|
|
||||||
setDaemon(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
while (true) {
|
|
||||||
try {
|
|
||||||
LogRecord record = records.take();
|
|
||||||
final boolean bigger = (limit > 0 && limit <= length.get());
|
|
||||||
if (bigger || tomorrow <= record.getMillis()) {
|
|
||||||
updateTomorrow();
|
|
||||||
if (stream != null) {
|
|
||||||
stream.close();
|
|
||||||
if (bigger) {
|
|
||||||
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("%m", String.valueOf((date.getYear() * 100 + date.getMonthValue()))).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();
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getPrefix() {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
private void configure() {
|
|
||||||
LogManager manager = LogManager.getLogManager();
|
|
||||||
String cname = LogFileHandler.class.getName();
|
|
||||||
pattern = manager.getProperty(cname + ".pattern");
|
|
||||||
if (pattern == null) {
|
|
||||||
pattern = "logs-%m/" + getPrefix() + "log-%d.log";
|
|
||||||
} else {
|
|
||||||
int pos = pattern.lastIndexOf('/');
|
|
||||||
if (pos > 0) {
|
|
||||||
pattern = pattern.substring(0, pos + 1) + getPrefix() + pattern.substring(pos + 1);
|
|
||||||
} else {
|
|
||||||
pattern = getPrefix() + pattern;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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) {
|
|
||||||
final String sourceClassName = record.getSourceClassName();
|
|
||||||
if (sourceClassName == null || true) {
|
|
||||||
StackTraceElement[] ses = new Throwable().getStackTrace();
|
|
||||||
for (int i = 2; i < ses.length; i++) {
|
|
||||||
if (ses[i].getClassName().startsWith("java.util.logging")) continue;
|
|
||||||
record.setSourceClassName('[' + Thread.currentThread().getName() + "] " + ses[i].getClassName());
|
|
||||||
record.setSourceMethodName(ses[i].getMethodName());
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
record.setSourceClassName('[' + Thread.currentThread().getName() + "] " + sourceClassName);
|
|
||||||
}
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,134 +0,0 @@
|
|||||||
/*
|
|
||||||
* To change this license header, choose License Headers in Project Properties.
|
|
||||||
* To change this template file, choose Tools | Templates
|
|
||||||
* and open the template in the editor.
|
|
||||||
*/
|
|
||||||
package org.redkale.boot;
|
|
||||||
|
|
||||||
import org.redkale.net.http.WebServlet;
|
|
||||||
import org.redkale.net.http.HttpServer;
|
|
||||||
import org.redkale.net.http.HttpServlet;
|
|
||||||
import org.redkale.util.AnyValue;
|
|
||||||
import org.redkale.boot.ClassFilter.FilterEntry;
|
|
||||||
import org.redkale.util.AnyValue.DefaultAnyValue;
|
|
||||||
import java.lang.reflect.*;
|
|
||||||
import java.net.InetSocketAddress;
|
|
||||||
import java.util.*;
|
|
||||||
import java.util.logging.*;
|
|
||||||
import javax.annotation.*;
|
|
||||||
import org.redkale.net.*;
|
|
||||||
import org.redkale.net.http.*;
|
|
||||||
import org.redkale.net.sncp.*;
|
|
||||||
import org.redkale.service.*;
|
|
||||||
import org.redkale.util.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* HTTP Server节点的配置Server
|
|
||||||
*
|
|
||||||
* @see http://www.redkale.org
|
|
||||||
* @author zhangjx
|
|
||||||
*/
|
|
||||||
@NodeProtocol({"HTTP"})
|
|
||||||
public final class NodeHttpServer extends NodeServer {
|
|
||||||
|
|
||||||
private final HttpServer httpServer;
|
|
||||||
|
|
||||||
public NodeHttpServer(Application application, AnyValue serconf) {
|
|
||||||
super(application, application.getResourceFactory().createChild(), createServer(application, serconf));
|
|
||||||
this.httpServer = (HttpServer) server;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Server createServer(Application application, AnyValue serconf) {
|
|
||||||
return new HttpServer(application.getStartTime(), application.getWatchFactory());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public InetSocketAddress getSocketAddress() {
|
|
||||||
return httpServer == null ? null : httpServer.getSocketAddress();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected ClassFilter<Servlet> createServletClassFilter() {
|
|
||||||
return createClassFilter(null, WebServlet.class, HttpServlet.class, null, "servlets", "servlet");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void loadServlet(ClassFilter<? extends Servlet> servletFilter) throws Exception {
|
|
||||||
if (httpServer != null) loadHttpServlet(this.nodeConf.getAnyValue("servlets"), servletFilter);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void loadService(ClassFilter serviceFilter) throws Exception {
|
|
||||||
super.loadService(serviceFilter);
|
|
||||||
initWebSocketService();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initWebSocketService() {
|
|
||||||
final NodeServer self = this;
|
|
||||||
final ResourceFactory regFactory = application.getResourceFactory();
|
|
||||||
factory.add(WebSocketNode.class, (ResourceFactory rf, final Object src, Field field, Object attachment) -> {
|
|
||||||
try {
|
|
||||||
Resource rs = field.getAnnotation(Resource.class);
|
|
||||||
if (rs == null) return;
|
|
||||||
if (!(src instanceof WebSocketServlet)) return;
|
|
||||||
String rcname = rs.name();
|
|
||||||
if (rcname.contains(ResourceFactory.RESOURCE_PARENT_NAME)) rcname = rcname.replace(ResourceFactory.RESOURCE_PARENT_NAME, ((WebSocketServlet) src).name());
|
|
||||||
synchronized (regFactory) {
|
|
||||||
Service nodeService = (Service) rf.find(rcname, WebSocketNode.class);
|
|
||||||
if (nodeService == null) {
|
|
||||||
nodeService = Sncp.createLocalService(rcname, getExecutor(), (Class<? extends Service>) WebSocketNodeService.class,
|
|
||||||
getSncpAddress(), sncpDefaultGroups, sncpSameGroupTransports, sncpDiffGroupTransports);
|
|
||||||
regFactory.register(rcname, WebSocketNode.class, nodeService);
|
|
||||||
factory.inject(nodeService, self);
|
|
||||||
logger.fine("[" + Thread.currentThread().getName() + "] Load " + nodeService);
|
|
||||||
if (getSncpAddress() != null) {
|
|
||||||
NodeSncpServer sncpServer = null;
|
|
||||||
for (NodeServer node : application.servers) {
|
|
||||||
if (node.isSNCP() && getSncpAddress().equals(node.getSncpAddress())) {
|
|
||||||
sncpServer = (NodeSncpServer) node;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ServiceWrapper wrapper = new ServiceWrapper(WebSocketNodeService.class, nodeService, rcname, getSncpGroup(), sncpDefaultGroups, null);
|
|
||||||
sncpServer.getSncpServer().addService(wrapper);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
field.set(src, nodeService);
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
logger.log(Level.SEVERE, "WebSocketNode inject error", e);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void loadHttpServlet(final AnyValue conf, final ClassFilter<? extends Servlet> 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<? extends Servlet> en : filter.getFilterEntrys()) {
|
|
||||||
Class<HttpServlet> clazz = (Class<HttpServlet>) 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();
|
|
||||||
factory.inject(servlet, this);
|
|
||||||
String[] mappings = ws.value();
|
|
||||||
if (ws.fillurl() && !prefix.isEmpty()) {
|
|
||||||
for (int i = 0; i < mappings.length; i++) {
|
|
||||||
mappings[i] = prefix + mappings[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
DefaultAnyValue servletConf = (DefaultAnyValue) en.getProperty();
|
|
||||||
WebInitParam[] webparams = ws.initParams();
|
|
||||||
if (webparams.length > 0) {
|
|
||||||
if (servletConf == null) servletConf = new DefaultAnyValue();
|
|
||||||
for (WebInitParam webparam : webparams) {
|
|
||||||
servletConf.addValue(webparam.name(), webparam.value());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.httpServer.addHttpServlet(servlet, servletConf, 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());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
/*
|
|
||||||
* To change this license header, choose License Headers in Project Properties.
|
|
||||||
* To change this template file, choose Tools | Templates
|
|
||||||
* and open the template in the editor.
|
|
||||||
*/
|
|
||||||
package org.redkale.boot;
|
|
||||||
|
|
||||||
import java.lang.annotation.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @see http://www.redkale.org
|
|
||||||
* @author zhangjx
|
|
||||||
*/
|
|
||||||
@Target({ElementType.TYPE})
|
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
|
||||||
@Documented
|
|
||||||
public @interface NodeProtocol {
|
|
||||||
String[] value();
|
|
||||||
}
|
|
||||||
@@ -1,464 +0,0 @@
|
|||||||
/*
|
|
||||||
* To change this license header, choose License Headers in Project Properties.
|
|
||||||
* To change this template file, choose Tools | Templates
|
|
||||||
* and open the template in the editor.
|
|
||||||
*/
|
|
||||||
package org.redkale.boot;
|
|
||||||
|
|
||||||
import static org.redkale.boot.Application.*;
|
|
||||||
import org.redkale.boot.ClassFilter.FilterEntry;
|
|
||||||
import org.redkale.net.sncp.ServiceWrapper;
|
|
||||||
import org.redkale.net.Server;
|
|
||||||
import org.redkale.net.sncp.Sncp;
|
|
||||||
import org.redkale.service.Service;
|
|
||||||
import org.redkale.util.AnyValue;
|
|
||||||
import org.redkale.util.AnyValue.DefaultAnyValue;
|
|
||||||
import java.io.*;
|
|
||||||
import java.lang.annotation.Annotation;
|
|
||||||
import java.lang.reflect.*;
|
|
||||||
import java.net.*;
|
|
||||||
import java.util.*;
|
|
||||||
import java.util.function.Consumer;
|
|
||||||
import java.util.logging.*;
|
|
||||||
import javax.annotation.*;
|
|
||||||
import org.redkale.net.*;
|
|
||||||
import org.redkale.net.http.*;
|
|
||||||
import org.redkale.service.*;
|
|
||||||
import org.redkale.source.*;
|
|
||||||
import org.redkale.util.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @see http://www.redkale.org
|
|
||||||
* @author zhangjx
|
|
||||||
*/
|
|
||||||
public abstract class NodeServer {
|
|
||||||
|
|
||||||
public static final String LINE_SEPARATOR = "\r\n";
|
|
||||||
|
|
||||||
protected final Logger logger;
|
|
||||||
|
|
||||||
protected final boolean fine;
|
|
||||||
|
|
||||||
protected final Application application;
|
|
||||||
|
|
||||||
protected final ResourceFactory factory;
|
|
||||||
|
|
||||||
protected final Server server;
|
|
||||||
|
|
||||||
private String sncpGroup = null; //当前Server的SNCP协议的组
|
|
||||||
|
|
||||||
private String nodeProtocol = Transport.DEFAULT_PROTOCOL;
|
|
||||||
|
|
||||||
private InetSocketAddress sncpAddress; //HttpServer中的sncpAddress 为所属group对应的SncpServer, 为null表示只是单节点,没有分布式结构
|
|
||||||
|
|
||||||
protected Consumer<ServiceWrapper> consumer;
|
|
||||||
|
|
||||||
protected AnyValue nodeConf;
|
|
||||||
|
|
||||||
protected final HashSet<String> sncpDefaultGroups = new LinkedHashSet<>();
|
|
||||||
|
|
||||||
protected final List<Transport> sncpSameGroupTransports = new ArrayList<>();
|
|
||||||
|
|
||||||
protected final List<Transport> sncpDiffGroupTransports = new ArrayList<>();
|
|
||||||
|
|
||||||
protected final Set<ServiceWrapper> localServiceWrappers = new LinkedHashSet<>();
|
|
||||||
|
|
||||||
protected final Set<ServiceWrapper> remoteServiceWrappers = new LinkedHashSet<>();
|
|
||||||
|
|
||||||
public NodeServer(Application application, ResourceFactory factory, Server server) {
|
|
||||||
this.application = application;
|
|
||||||
this.factory = factory;
|
|
||||||
this.server = server;
|
|
||||||
this.logger = Logger.getLogger(this.getClass().getSimpleName());
|
|
||||||
this.fine = logger.isLoggable(Level.FINE);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected Consumer<Runnable> getExecutor() throws Exception {
|
|
||||||
if (server == null) return null;
|
|
||||||
final Field field = Server.class.getDeclaredField("context");
|
|
||||||
field.setAccessible(true);
|
|
||||||
return new Consumer<Runnable>() {
|
|
||||||
|
|
||||||
private Context context;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void accept(Runnable t) {
|
|
||||||
if (context == null && server != null) {
|
|
||||||
try {
|
|
||||||
this.context = (Context) field.get(server);
|
|
||||||
} catch (Exception e) {
|
|
||||||
logger.log(Level.SEVERE, "Server (" + server.getSocketAddress() + ") cannot find Context", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
context.submit(t);
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public static <T extends NodeServer> NodeServer create(Class<T> clazz, Application application, AnyValue serconf) {
|
|
||||||
try {
|
|
||||||
return clazz.getConstructor(Application.class, AnyValue.class).newInstance(application, serconf);
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void init(AnyValue config) throws Exception {
|
|
||||||
this.nodeConf = config == null ? AnyValue.create() : config;
|
|
||||||
if (isSNCP()) { // SNCP协议
|
|
||||||
String host = this.nodeConf.getValue("host", "0.0.0.0").replace("0.0.0.0", "");
|
|
||||||
this.sncpAddress = new InetSocketAddress(host.isEmpty() ? application.localAddress.getHostAddress() : host, this.nodeConf.getIntValue("port"));
|
|
||||||
this.sncpGroup = application.globalNodes.get(this.sncpAddress);
|
|
||||||
if (this.sncpGroup == null) throw new RuntimeException("Server (" + String.valueOf(config).replaceAll("\\s+", " ") + ") not found <group> info");
|
|
||||||
if (server != null) this.nodeProtocol = server.getProtocol();
|
|
||||||
}
|
|
||||||
initGroup();
|
|
||||||
if (this.sncpAddress != null) this.factory.register(RESNAME_SERVER_ADDR, this.sncpAddress);
|
|
||||||
if (this.sncpGroup != null) this.factory.register(RESNAME_SERVER_GROUP, this.sncpGroup);
|
|
||||||
{
|
|
||||||
//设置root文件夹
|
|
||||||
String webroot = config.getValue("root", "root");
|
|
||||||
File myroot = new File(webroot);
|
|
||||||
if (!webroot.contains(":") && !webroot.startsWith("/")) {
|
|
||||||
myroot = new File(System.getProperty(Application.RESNAME_APP_HOME), webroot);
|
|
||||||
}
|
|
||||||
final String homepath = myroot.getCanonicalPath();
|
|
||||||
Server.loadLib(logger, config.getValue("lib", "") + ";" + homepath + "/lib/*;" + homepath + "/classes");
|
|
||||||
if (server != null) server.init(config);
|
|
||||||
}
|
|
||||||
initResource();
|
|
||||||
//prepare();
|
|
||||||
ClassFilter<Servlet> servletFilter = createServletClassFilter();
|
|
||||||
ClassFilter<Service> serviceFilter = createServiceClassFilter();
|
|
||||||
long s = System.currentTimeMillis();
|
|
||||||
if (servletFilter == null) {
|
|
||||||
ClassFilter.Loader.load(application.getHome(), serviceFilter);
|
|
||||||
} else {
|
|
||||||
ClassFilter.Loader.load(application.getHome(), serviceFilter, servletFilter);
|
|
||||||
}
|
|
||||||
long e = System.currentTimeMillis() - s;
|
|
||||||
logger.info(this.getClass().getSimpleName() + " load filter class in " + e + " ms");
|
|
||||||
loadService(serviceFilter); //必须在servlet之前
|
|
||||||
loadServlet(servletFilter);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected abstract void loadServlet(ClassFilter<? extends Servlet> servletFilter) throws Exception;
|
|
||||||
|
|
||||||
private void initResource() {
|
|
||||||
final NodeServer self = this;
|
|
||||||
//---------------------------------------------------------------------------------------------
|
|
||||||
final ResourceFactory regFactory = application.getResourceFactory();
|
|
||||||
factory.add(DataSource.class, (ResourceFactory rf, final Object src, Field field, final Object attachment) -> {
|
|
||||||
try {
|
|
||||||
Resource rs = field.getAnnotation(Resource.class);
|
|
||||||
if (rs == null) return;
|
|
||||||
if ((src instanceof Service) && Sncp.isRemote((Service) src)) return; //远程模式不得注入 DataSource
|
|
||||||
DataSource source = new DataDefaultSource(rs.name());
|
|
||||||
application.sources.add(source);
|
|
||||||
regFactory.register(rs.name(), DataSource.class, source);
|
|
||||||
List<Transport> sameGroupTransports = sncpSameGroupTransports;
|
|
||||||
List<Transport> diffGroupTransports = sncpDiffGroupTransports;
|
|
||||||
try {
|
|
||||||
Field ts = src.getClass().getDeclaredField("_sameGroupTransports");
|
|
||||||
ts.setAccessible(true);
|
|
||||||
Transport[] lts = (Transport[]) ts.get(src);
|
|
||||||
sameGroupTransports = Arrays.asList(lts);
|
|
||||||
|
|
||||||
ts = src.getClass().getDeclaredField("_diffGroupTransports");
|
|
||||||
ts.setAccessible(true);
|
|
||||||
lts = (Transport[]) ts.get(src);
|
|
||||||
diffGroupTransports = Arrays.asList(lts);
|
|
||||||
} catch (Exception e) {
|
|
||||||
//src 不含 MultiRun 方法
|
|
||||||
}
|
|
||||||
if (factory.find(rs.name(), DataCacheListener.class) == null) {
|
|
||||||
Service cacheListenerService = Sncp.createLocalService(rs.name(), getExecutor(), DataCacheListenerService.class, this.sncpAddress, sncpDefaultGroups, sameGroupTransports, diffGroupTransports);
|
|
||||||
regFactory.register(rs.name(), DataCacheListener.class, cacheListenerService);
|
|
||||||
ServiceWrapper wrapper = new ServiceWrapper(DataCacheListenerService.class, cacheListenerService, rs.name(), sncpGroup, sncpDefaultGroups, null);
|
|
||||||
localServiceWrappers.add(wrapper);
|
|
||||||
if (consumer != null) consumer.accept(wrapper);
|
|
||||||
rf.inject(cacheListenerService, self);
|
|
||||||
}
|
|
||||||
field.set(src, source);
|
|
||||||
rf.inject(source, self); // 给 "datasource.nodeid" 赋值
|
|
||||||
} catch (Exception e) {
|
|
||||||
logger.log(Level.SEVERE, "DataSource inject error", e);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initGroup() {
|
|
||||||
final AnyValue[] services = this.nodeConf.getAnyValues("services");
|
|
||||||
final String[] groups = services.length < 1 ? new String[]{""} : services[0].getValue("groups", "").split(";");
|
|
||||||
this.sncpDefaultGroups.addAll(Arrays.asList(groups));
|
|
||||||
if (!isSNCP()) {
|
|
||||||
NodeSncpServer sncpServer = null;
|
|
||||||
for (NodeServer node : application.servers) {
|
|
||||||
if (!node.isSNCP()) continue;
|
|
||||||
if (!this.sncpDefaultGroups.contains(node.sncpGroup)) continue;
|
|
||||||
sncpServer = (NodeSncpServer) node;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (sncpServer == null && (groups.length == 1 && groups[0].isEmpty())) {
|
|
||||||
for (NodeServer node : application.servers) {
|
|
||||||
if (!node.isSNCP()) continue;
|
|
||||||
sncpServer = (NodeSncpServer) node;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (sncpServer != null) {
|
|
||||||
this.sncpAddress = sncpServer.getSncpAddress();
|
|
||||||
this.sncpGroup = sncpServer.getSncpGroup();
|
|
||||||
this.sncpDefaultGroups.clear();
|
|
||||||
this.sncpDefaultGroups.addAll(sncpServer.sncpDefaultGroups);
|
|
||||||
this.sncpSameGroupTransports.addAll(sncpServer.sncpSameGroupTransports);
|
|
||||||
this.sncpDiffGroupTransports.addAll(sncpServer.sncpDiffGroupTransports);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
final Set<InetSocketAddress> sameGroupAddrs = application.findGlobalGroup(this.sncpGroup);
|
|
||||||
final Map<String, Set<InetSocketAddress>> diffGroupAddrs = new HashMap<>();
|
|
||||||
for (String groupitem : groups) {
|
|
||||||
final Set<InetSocketAddress> addrs = application.findGlobalGroup(groupitem);
|
|
||||||
if (addrs == null || groupitem.equals(this.sncpGroup)) continue;
|
|
||||||
diffGroupAddrs.put(groupitem, addrs);
|
|
||||||
}
|
|
||||||
if (sameGroupAddrs != null) {
|
|
||||||
sameGroupAddrs.remove(this.sncpAddress);
|
|
||||||
for (InetSocketAddress iaddr : sameGroupAddrs) {
|
|
||||||
sncpSameGroupTransports.add(loadTransport(this.sncpGroup, getNodeProtocol(), iaddr));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
diffGroupAddrs.forEach((k, v) -> sncpDiffGroupTransports.add(loadTransport(k, getNodeProtocol(), v)));
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
protected void loadService(ClassFilter serviceFilter) throws Exception {
|
|
||||||
if (serviceFilter == null) return;
|
|
||||||
final String threadName = "[" + Thread.currentThread().getName() + "] ";
|
|
||||||
final Set<FilterEntry<Service>> entrys = serviceFilter.getFilterEntrys();
|
|
||||||
ResourceFactory regFactory = isSNCP() ? application.getResourceFactory() : factory;
|
|
||||||
final Set<InetSocketAddress> sg = application.findGlobalGroup(this.sncpGroup);
|
|
||||||
for (FilterEntry<Service> entry : entrys) { //service实现类
|
|
||||||
final Class<? extends Service> type = entry.getType();
|
|
||||||
if (type.isInterface()) continue;
|
|
||||||
if (Modifier.isFinal(type.getModifiers())) continue;
|
|
||||||
if (!Modifier.isPublic(type.getModifiers())) continue;
|
|
||||||
if (Modifier.isAbstract(type.getModifiers())) continue;
|
|
||||||
if (!isSNCP() && factory.find(entry.getName(), type) != null) continue;
|
|
||||||
final Set<InetSocketAddress> sameGroupAddrs = new LinkedHashSet<>();
|
|
||||||
final Map<String, Set<InetSocketAddress>> diffGroupAddrs = new HashMap<>();
|
|
||||||
final HashSet<String> groups = entry.getGroups();
|
|
||||||
for (String g : groups) {
|
|
||||||
if (g.isEmpty()) continue;
|
|
||||||
if (g.equals(this.sncpGroup) && sg != null) sameGroupAddrs.addAll(sg);
|
|
||||||
Set<InetSocketAddress> set = application.findGlobalGroup(g);
|
|
||||||
if (set == null) throw new RuntimeException(type.getName() + " has illegal group (" + groups + ")");
|
|
||||||
if (!g.equals(this.sncpGroup)) {
|
|
||||||
diffGroupAddrs.put(g, set);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
List<Transport> diffGroupTransports = new ArrayList<>();
|
|
||||||
diffGroupAddrs.forEach((k, v) -> diffGroupTransports.add(loadTransport(k, server.getProtocol(), v)));
|
|
||||||
|
|
||||||
ServiceWrapper wrapper;
|
|
||||||
if ((sameGroupAddrs.isEmpty() && diffGroupAddrs.isEmpty()) || sameGroupAddrs.contains(this.sncpAddress) || type.getAnnotation(LocalService.class) != null) { //本地模式
|
|
||||||
sameGroupAddrs.remove(this.sncpAddress);
|
|
||||||
List<Transport> sameGroupTransports = new ArrayList<>();
|
|
||||||
for (InetSocketAddress iaddr : sameGroupAddrs) {
|
|
||||||
Set<InetSocketAddress> tset = new HashSet<>();
|
|
||||||
tset.add(iaddr);
|
|
||||||
sameGroupTransports.add(loadTransport(this.sncpGroup, server.getProtocol(), tset));
|
|
||||||
}
|
|
||||||
Service service = Sncp.createLocalService(entry.getName(), getExecutor(), type, this.sncpAddress, groups, sameGroupTransports, diffGroupTransports);
|
|
||||||
wrapper = new ServiceWrapper(type, service, this.sncpGroup, entry);
|
|
||||||
if (fine) logger.fine("[" + Thread.currentThread().getName() + "] " + service + " loaded");
|
|
||||||
} else {
|
|
||||||
sameGroupAddrs.remove(this.sncpAddress);
|
|
||||||
StringBuilder g = new StringBuilder();
|
|
||||||
diffGroupAddrs.forEach((k, v) -> {
|
|
||||||
if (g.length() > 0) g.append(';');
|
|
||||||
g.append(k);
|
|
||||||
sameGroupAddrs.addAll(v);
|
|
||||||
});
|
|
||||||
if (sameGroupAddrs.isEmpty()) throw new RuntimeException(type.getName() + " has no remote address on group (" + groups + ")");
|
|
||||||
Service service = Sncp.createRemoteService(entry.getName(), getExecutor(), type, this.sncpAddress, groups, loadTransport(g.toString(), server.getProtocol(), sameGroupAddrs));
|
|
||||||
wrapper = new ServiceWrapper(type, service, "", entry);
|
|
||||||
if (fine) logger.fine("[" + Thread.currentThread().getName() + "] " + service + " loaded");
|
|
||||||
}
|
|
||||||
if (factory.find(wrapper.getName(), wrapper.getType()) == null) {
|
|
||||||
regFactory.register(wrapper.getName(), wrapper.getType(), wrapper.getService());
|
|
||||||
if (wrapper.getService() instanceof DataSource) {
|
|
||||||
regFactory.register(wrapper.getName(), DataSource.class, wrapper.getService());
|
|
||||||
} else if (wrapper.getService() instanceof DataCacheListener) {
|
|
||||||
regFactory.register(wrapper.getName(), DataCacheListener.class, wrapper.getService());
|
|
||||||
} else if (wrapper.getService() instanceof DataSQLListener) {
|
|
||||||
regFactory.register(wrapper.getName(), DataSQLListener.class, wrapper.getService());
|
|
||||||
} else if (wrapper.getService() instanceof WebSocketNode) {
|
|
||||||
regFactory.register(wrapper.getName(), WebSocketNode.class, wrapper.getService());
|
|
||||||
}
|
|
||||||
if (wrapper.isRemote()) {
|
|
||||||
remoteServiceWrappers.add(wrapper);
|
|
||||||
} else {
|
|
||||||
localServiceWrappers.add(wrapper);
|
|
||||||
if (consumer != null) consumer.accept(wrapper);
|
|
||||||
}
|
|
||||||
} else if (isSNCP() && !entry.isAutoload()) {
|
|
||||||
throw new RuntimeException(ServiceWrapper.class.getSimpleName() + "(class:" + type.getName() + ", name:" + entry.getName() + ", group:" + groups + ") is repeat.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
application.servicecdl.countDown();
|
|
||||||
application.servicecdl.await();
|
|
||||||
|
|
||||||
final StringBuilder sb = logger.isLoggable(Level.INFO) ? new StringBuilder() : null;
|
|
||||||
//---------------- inject ----------------
|
|
||||||
new HashSet<>(localServiceWrappers).forEach(y -> {
|
|
||||||
factory.inject(y.getService(), NodeServer.this);
|
|
||||||
});
|
|
||||||
remoteServiceWrappers.forEach(y -> {
|
|
||||||
factory.inject(y.getService(), NodeServer.this);
|
|
||||||
if (sb != null) {
|
|
||||||
sb.append(threadName).append("RemoteService(").append(y.getType()).append(':').append(y.getName()).append(") loaded").append(LINE_SEPARATOR);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
//----------------- init -----------------
|
|
||||||
localServiceWrappers.parallelStream().forEach(y -> {
|
|
||||||
long s = System.currentTimeMillis();
|
|
||||||
y.getService().init(y.getConf());
|
|
||||||
long e = System.currentTimeMillis() - s;
|
|
||||||
if (e > 2 && sb != null) {
|
|
||||||
sb.append(threadName).append("LocalService(").append(y.getType()).append(':').append(y.getName()).append(") init ").append(e).append("ms").append(LINE_SEPARATOR);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
if (sb != null && sb.length() > 0) logger.log(Level.INFO, sb.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
protected Transport loadTransport(String group, String protocol, InetSocketAddress addr) {
|
|
||||||
if (addr == null) return null;
|
|
||||||
Set<InetSocketAddress> set = new HashSet<>();
|
|
||||||
set.add(addr);
|
|
||||||
return loadTransport(group, protocol, set);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected Transport loadTransport(String group, String protocol, Set<InetSocketAddress> addrs) {
|
|
||||||
Transport transport = null;
|
|
||||||
if (!addrs.isEmpty()) {
|
|
||||||
synchronized (application.transports) {
|
|
||||||
for (Transport tran : application.transports) {
|
|
||||||
if (tran.match(addrs)) {
|
|
||||||
transport = tran;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (transport == null) {
|
|
||||||
transport = new Transport(group + "_" + application.transports.size(), protocol, application.getWatchFactory(), 32, addrs);
|
|
||||||
logger.info(transport + " created");
|
|
||||||
application.transports.add(transport);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return transport;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected abstract ClassFilter<Servlet> createServletClassFilter();
|
|
||||||
|
|
||||||
protected ClassFilter<Service> createServiceClassFilter() {
|
|
||||||
return createClassFilter(this.sncpGroup, null, Service.class, Annotation.class, "services", "service");
|
|
||||||
}
|
|
||||||
|
|
||||||
protected ClassFilter createClassFilter(final String localGroup, Class<? extends Annotation> ref,
|
|
||||||
Class inter, Class<? extends Annotation> ref2, String properties, String property) {
|
|
||||||
ClassFilter cf = new ClassFilter(ref, inter, null);
|
|
||||||
if (properties == null && properties == null) return cf;
|
|
||||||
if (this.nodeConf == null) return cf;
|
|
||||||
AnyValue[] proplist = this.nodeConf.getAnyValues(properties);
|
|
||||||
if (proplist == null || proplist.length < 1) return cf;
|
|
||||||
cf = null;
|
|
||||||
for (AnyValue list : proplist) {
|
|
||||||
DefaultAnyValue prop = null;
|
|
||||||
String sc = list.getValue("groups");
|
|
||||||
if (sc == null) sc = localGroup;
|
|
||||||
if (sc != null) {
|
|
||||||
prop = new AnyValue.DefaultAnyValue();
|
|
||||||
prop.addValue("groups", sc);
|
|
||||||
}
|
|
||||||
ClassFilter filter = new ClassFilter(ref, inter, prop);
|
|
||||||
for (AnyValue av : list.getAnyValues(property)) {
|
|
||||||
final AnyValue[] items = av.getAnyValues("property");
|
|
||||||
if (av instanceof DefaultAnyValue && items.length > 0) {
|
|
||||||
DefaultAnyValue dav = DefaultAnyValue.create();
|
|
||||||
final AnyValue.Entry<String>[] strings = av.getStringEntrys();
|
|
||||||
if (strings != null) {
|
|
||||||
for (AnyValue.Entry<String> en : strings) {
|
|
||||||
dav.addValue(en.name, en.getValue());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
final AnyValue.Entry<AnyValue>[] anys = av.getAnyEntrys();
|
|
||||||
if (anys != null) {
|
|
||||||
for (AnyValue.Entry<AnyValue> en : anys) {
|
|
||||||
if (!"property".equals(en.name)) dav.addValue(en.name, en.getValue());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
DefaultAnyValue ps = DefaultAnyValue.create();
|
|
||||||
for (AnyValue item : items) {
|
|
||||||
ps.addValue(item.getValue("name"), item.getValue("value"));
|
|
||||||
}
|
|
||||||
dav.addValue("property", ps);
|
|
||||||
av = dav;
|
|
||||||
}
|
|
||||||
filter.filter(av, av.getValue("value"), false);
|
|
||||||
}
|
|
||||||
if (list.getBoolValue("autoload", true)) {
|
|
||||||
String includes = list.getValue("includes", "");
|
|
||||||
String excludes = list.getValue("excludes", "");
|
|
||||||
filter.setIncludePatterns(includes.split(";"));
|
|
||||||
filter.setExcludePatterns(excludes.split(";"));
|
|
||||||
} else if (ref2 == null || ref2 == Annotation.class) { //service如果是autoload=false则不需要加载
|
|
||||||
filter.setRefused(true);
|
|
||||||
} else if (ref2 != Annotation.class) {
|
|
||||||
filter.setAnnotationClass(ref2);
|
|
||||||
}
|
|
||||||
cf = (cf == null) ? filter : cf.or(filter);
|
|
||||||
}
|
|
||||||
return cf;
|
|
||||||
}
|
|
||||||
|
|
||||||
public abstract InetSocketAddress getSocketAddress();
|
|
||||||
|
|
||||||
public boolean isSNCP() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public InetSocketAddress getSncpAddress() {
|
|
||||||
return sncpAddress;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getSncpGroup() {
|
|
||||||
return sncpGroup;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getNodeProtocol() {
|
|
||||||
return nodeProtocol;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void start() throws IOException {
|
|
||||||
server.start();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void shutdown() throws IOException {
|
|
||||||
final StringBuilder sb = logger.isLoggable(Level.INFO) ? new StringBuilder() : null;
|
|
||||||
localServiceWrappers.forEach(y -> {
|
|
||||||
long s = System.currentTimeMillis();
|
|
||||||
y.getService().destroy(y.getConf());
|
|
||||||
long e = System.currentTimeMillis() - s;
|
|
||||||
if (e > 2 && sb != null) {
|
|
||||||
sb.append("LocalService(").append(y.getType()).append(':').append(y.getName()).append(") destroy ").append(e).append("ms").append(LINE_SEPARATOR);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
if (sb != null && sb.length() > 0) logger.log(Level.INFO, sb.toString());
|
|
||||||
server.shutdown();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,70 +0,0 @@
|
|||||||
/*
|
|
||||||
* To change this license header, choose License Headers in Project Properties.
|
|
||||||
* To change this template file, choose Tools | Templates
|
|
||||||
* and open the template in the editor.
|
|
||||||
*/
|
|
||||||
package org.redkale.boot;
|
|
||||||
|
|
||||||
import java.net.*;
|
|
||||||
import java.util.logging.*;
|
|
||||||
import org.redkale.net.*;
|
|
||||||
import org.redkale.net.sncp.*;
|
|
||||||
import org.redkale.util.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @see http://www.redkale.org
|
|
||||||
* @author zhangjx
|
|
||||||
*/
|
|
||||||
@NodeProtocol({"SNCP"})
|
|
||||||
public final class NodeSncpServer extends NodeServer {
|
|
||||||
|
|
||||||
private final SncpServer sncpServer;
|
|
||||||
|
|
||||||
public NodeSncpServer(Application application, AnyValue serconf) {
|
|
||||||
super(application, application.getResourceFactory().createChild(), createServer(application, serconf));
|
|
||||||
this.sncpServer = (SncpServer) this.server;
|
|
||||||
this.consumer = sncpServer == null ? null : x -> sncpServer.addService(x);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Server createServer(Application application, AnyValue serconf) {
|
|
||||||
return new SncpServer(application.getStartTime(), application.getWatchFactory());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public InetSocketAddress getSocketAddress() {
|
|
||||||
return sncpServer == null ? null : sncpServer.getSocketAddress();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void init(AnyValue config) throws Exception {
|
|
||||||
super.init(config);
|
|
||||||
//-------------------------------------------------------------------
|
|
||||||
if (sncpServer == null) return; //调试时server才可能为null
|
|
||||||
final StringBuilder sb = logger.isLoggable(Level.FINE) ? new StringBuilder() : null;
|
|
||||||
final String threadName = "[" + Thread.currentThread().getName() + "] ";
|
|
||||||
for (SncpServlet en : sncpServer.getSncpServlets()) {
|
|
||||||
if (sb != null) sb.append(threadName).append(" Loaded ").append(en).append(LINE_SEPARATOR);
|
|
||||||
}
|
|
||||||
if (sb != null && sb.length() > 0) logger.log(Level.FINE, sb.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isSNCP() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public SncpServer getSncpServer() {
|
|
||||||
return sncpServer;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void loadServlet(ClassFilter<? extends Servlet> servletFilter) throws Exception {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected ClassFilter<Servlet> createServletClassFilter() {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
/*
|
|
||||||
* To change this license header, choose License Headers in Project Properties.
|
|
||||||
* To change this template file, choose Tools | Templates
|
|
||||||
* and open the template in the editor.
|
|
||||||
*/
|
|
||||||
package org.redkale.convert;
|
|
||||||
|
|
||||||
import java.lang.reflect.Type;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 对不明类型的对象进行序列化; BSON序列化时将对象的类名写入Writer,JSON则不写入。
|
|
||||||
*
|
|
||||||
* @see http://www.redkale.org
|
|
||||||
* @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(factory.getEntity(value.getClass()));
|
|
||||||
factory.loadEncoder(value.getClass()).convertTo(out, value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Type getType() {
|
|
||||||
return Object.class;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,80 +0,0 @@
|
|||||||
/*
|
|
||||||
* To change this license header, choose License Headers in Project Properties.
|
|
||||||
* To change this template file, choose Tools | Templates
|
|
||||||
* and open the template in the editor.
|
|
||||||
*/
|
|
||||||
package org.redkale.convert;
|
|
||||||
|
|
||||||
import java.lang.reflect.*;
|
|
||||||
import java.util.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 对象数组的序列化,不包含int[]、long[]这样的primitive class数组.
|
|
||||||
* 数组长度不能超过 32767。 在BSON中数组长度设定的是short,对于大于32767长度的数组传输会影响性能,所以没有采用int存储。
|
|
||||||
* 支持一定程度的泛型。
|
|
||||||
*
|
|
||||||
* @see http://www.redkale.org
|
|
||||||
* @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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,76 +0,0 @@
|
|||||||
/*
|
|
||||||
* To change this license header, choose License Headers in Project Properties.
|
|
||||||
* To change this template file, choose Tools | Templates
|
|
||||||
* and open the template in the editor.
|
|
||||||
*/
|
|
||||||
package org.redkale.convert;
|
|
||||||
|
|
||||||
import java.lang.reflect.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 对象数组的反序列化,不包含int[]、long[]这样的primitive class数组.
|
|
||||||
* 数组长度不能超过 32767。 在BSON中数组长度设定的是short,对于大于32767长度的数组传输会影响性能,所以没有必要采用int存储。
|
|
||||||
* 支持一定程度的泛型。
|
|
||||||
*
|
|
||||||
* @see http://www.redkale.org
|
|
||||||
* @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 != null && 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,70 +0,0 @@
|
|||||||
/*
|
|
||||||
* To change this license header, choose License Headers in Project Properties.
|
|
||||||
* To change this template file, choose Tools | Templates
|
|
||||||
* and open the template in the editor.
|
|
||||||
*/
|
|
||||||
package org.redkale.convert;
|
|
||||||
|
|
||||||
import org.redkale.util.Creator;
|
|
||||||
import java.lang.reflect.ParameterizedType;
|
|
||||||
import java.lang.reflect.Type;
|
|
||||||
import java.util.Collection;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 对象集合的反序列化.
|
|
||||||
* 集合大小不能超过 32767。 在BSON中集合大小设定的是short,对于大于32767长度的集合传输会影响性能,所以没有采用int存储。
|
|
||||||
* 支持一定程度的泛型。
|
|
||||||
*
|
|
||||||
* @see http://www.redkale.org
|
|
||||||
* @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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,66 +0,0 @@
|
|||||||
/*
|
|
||||||
* To change this license header, choose License Headers in Project Properties.
|
|
||||||
* To change this template file, choose Tools | Templates
|
|
||||||
* and open the template in the editor.
|
|
||||||
*/
|
|
||||||
package org.redkale.convert;
|
|
||||||
|
|
||||||
import java.lang.reflect.*;
|
|
||||||
import java.util.Collection;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 对象集合的序列化.
|
|
||||||
* 集合大小不能超过 32767。 在BSON中集合大小设定的是short,对于大于32767长度的集合传输会影响性能,所以没有采用int存储。
|
|
||||||
* 支持一定程度的泛型。
|
|
||||||
*
|
|
||||||
* @see http://www.redkale.org
|
|
||||||
* @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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
/*
|
|
||||||
* To change this license header, choose License Headers in Project Properties.
|
|
||||||
* To change this template file, choose Tools | Templates
|
|
||||||
* and open the template in the editor.
|
|
||||||
*/
|
|
||||||
package org.redkale.convert;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 序列化操作类
|
|
||||||
*
|
|
||||||
* @see http://www.redkale.org
|
|
||||||
* @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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,45 +0,0 @@
|
|||||||
/*
|
|
||||||
* To change this license header, choose License Headers in Project Properties.
|
|
||||||
* To change this template file, choose Tools | Templates
|
|
||||||
* and open the template in the editor.
|
|
||||||
*/
|
|
||||||
package org.redkale.convert;
|
|
||||||
|
|
||||||
import java.lang.annotation.*;
|
|
||||||
import static java.lang.annotation.ElementType.*;
|
|
||||||
import static java.lang.annotation.RetentionPolicy.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 依附在setter、getter方法、字段进行简单的配置
|
|
||||||
*
|
|
||||||
* @see http://www.redkale.org
|
|
||||||
* @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;
|
|
||||||
}
|
|
||||||
@@ -1,68 +0,0 @@
|
|||||||
/*
|
|
||||||
* To change this license header, choose License Headers in Project Properties.
|
|
||||||
* To change this template file, choose Tools | Templates
|
|
||||||
* and open the template in the editor.
|
|
||||||
*/
|
|
||||||
package org.redkale.convert;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ConvertColumn 对应的实体类
|
|
||||||
*
|
|
||||||
* @see http://www.redkale.org
|
|
||||||
* @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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
/*
|
|
||||||
* To change this license header, choose License Headers in Project Properties.
|
|
||||||
* To change this template file, choose Tools | Templates
|
|
||||||
* and open the template in the editor.
|
|
||||||
*/
|
|
||||||
package org.redkale.convert;
|
|
||||||
|
|
||||||
import java.lang.annotation.*;
|
|
||||||
import static java.lang.annotation.ElementType.*;
|
|
||||||
import static java.lang.annotation.RetentionPolicy.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ConvertColumn 的多用类
|
|
||||||
*
|
|
||||||
* @see http://www.redkale.org
|
|
||||||
* @author zhangjx
|
|
||||||
*/
|
|
||||||
@Inherited
|
|
||||||
@Documented
|
|
||||||
@Target({METHOD, FIELD})
|
|
||||||
@Retention(RUNTIME)
|
|
||||||
public @interface ConvertColumns {
|
|
||||||
|
|
||||||
ConvertColumn[] value();
|
|
||||||
}
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
/*
|
|
||||||
* To change this license header, choose License Headers in Project Properties.
|
|
||||||
* To change this template file, choose Tools | Templates
|
|
||||||
* and open the template in the editor.
|
|
||||||
*/
|
|
||||||
package org.redkale.convert;
|
|
||||||
|
|
||||||
import java.lang.annotation.*;
|
|
||||||
import static java.lang.annotation.ElementType.TYPE;
|
|
||||||
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 用于类名的别名, 类似javax.persistence.Table
|
|
||||||
* 该值必须是全局唯一
|
|
||||||
*
|
|
||||||
* @see http://www.redkale.org
|
|
||||||
* @author zhangjx
|
|
||||||
*/
|
|
||||||
@Inherited
|
|
||||||
@Documented
|
|
||||||
@Target({TYPE})
|
|
||||||
@Retention(RUNTIME)
|
|
||||||
public @interface ConvertEntity {
|
|
||||||
|
|
||||||
String value();
|
|
||||||
}
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
/*
|
|
||||||
* To change this template, choose Tools | Templates
|
|
||||||
* and open the template in the editor.
|
|
||||||
*/
|
|
||||||
package org.redkale.convert;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @see http://www.redkale.org
|
|
||||||
* @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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
/*
|
|
||||||
* To change this license header, choose License Headers in Project Properties.
|
|
||||||
* To change this template file, choose Tools | Templates
|
|
||||||
* and open the template in the editor.
|
|
||||||
*/
|
|
||||||
package org.redkale.convert;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @see http://www.redkale.org
|
|
||||||
* @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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,65 +0,0 @@
|
|||||||
/*
|
|
||||||
* To change this license header, choose License Headers in Project Properties.
|
|
||||||
* To change this template file, choose Tools | Templates
|
|
||||||
* and open the template in the editor.
|
|
||||||
*/
|
|
||||||
package org.redkale.convert;
|
|
||||||
|
|
||||||
import org.redkale.util.Attribute;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @see http://www.redkale.org
|
|
||||||
* @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 + '}';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
/*
|
|
||||||
* To change this license header, choose License Headers in Project Properties.
|
|
||||||
* To change this template file, choose Tools | Templates
|
|
||||||
* and open the template in the editor.
|
|
||||||
*/
|
|
||||||
package org.redkale.convert;
|
|
||||||
|
|
||||||
import java.lang.reflect.Type;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @see http://www.redkale.org
|
|
||||||
* @author zhangjx
|
|
||||||
* @param <R>
|
|
||||||
* @param <T>
|
|
||||||
*/
|
|
||||||
public interface Decodeable<R extends Reader, T> {
|
|
||||||
|
|
||||||
public T convertFrom(final R in);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 泛型映射接口
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
public Type getType();
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,77 +0,0 @@
|
|||||||
/*
|
|
||||||
* To change this license header, choose License Headers in Project Properties.
|
|
||||||
* To change this template file, choose Tools | Templates
|
|
||||||
* and open the template in the editor.
|
|
||||||
*/
|
|
||||||
package org.redkale.convert;
|
|
||||||
|
|
||||||
import org.redkale.util.Attribute;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @see http://www.redkale.org
|
|
||||||
* @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;
|
|
||||||
|
|
||||||
private final boolean istring;
|
|
||||||
|
|
||||||
//private final boolean isnumber;
|
|
||||||
private final boolean isbool;
|
|
||||||
|
|
||||||
public EnMember(Attribute<T, F> attribute, Encodeable<W, F> encoder) {
|
|
||||||
this.attribute = attribute;
|
|
||||||
this.encoder = encoder;
|
|
||||||
Class t = attribute.type();
|
|
||||||
this.istring = CharSequence.class.isAssignableFrom(t);
|
|
||||||
this.isbool = t == Boolean.class || t == boolean.class;
|
|
||||||
//this.isnumber = Number.class.isAssignableFrom(t) || (!this.isbool && t.isPrimitive());
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean write(final W out, final boolean comma, final T obj) {
|
|
||||||
F value = attribute.get(obj);
|
|
||||||
if (value == null) return comma;
|
|
||||||
if (out.isTiny()) {
|
|
||||||
if (istring) {
|
|
||||||
if (((CharSequence) value).length() == 0) return comma;
|
|
||||||
} else if (isbool) {
|
|
||||||
if (!((Boolean) value)) 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 + '}';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
/*
|
|
||||||
* To change this license header, choose License Headers in Project Properties.
|
|
||||||
* To change this template file, choose Tools | Templates
|
|
||||||
* and open the template in the editor.
|
|
||||||
*/
|
|
||||||
package org.redkale.convert;
|
|
||||||
|
|
||||||
import java.lang.reflect.Type;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @see http://www.redkale.org
|
|
||||||
* @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();
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,387 +0,0 @@
|
|||||||
/*
|
|
||||||
* To change this license header, choose License Headers in Project Properties.
|
|
||||||
* To change this template file, choose Tools | Templates
|
|
||||||
* and open the template in the editor.
|
|
||||||
*/
|
|
||||||
package org.redkale.convert;
|
|
||||||
|
|
||||||
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 org.redkale.convert.ext.InetAddressSimpledCoder.*;
|
|
||||||
import java.util.*;
|
|
||||||
import java.util.concurrent.*;
|
|
||||||
import java.util.regex.*;
|
|
||||||
import org.redkale.convert.ext.*;
|
|
||||||
import org.redkale.util.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @see http://www.redkale.org
|
|
||||||
* @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;
|
|
||||||
|
|
||||||
protected boolean tiny;
|
|
||||||
|
|
||||||
private final Encodeable<W, ?> anyEncoder = new AnyEncoder(this);
|
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------------
|
|
||||||
private final HashedMap<Class, Creator> creators = new HashedMap();
|
|
||||||
|
|
||||||
private final Map<String, Class> entitys = new ConcurrentHashMap();
|
|
||||||
|
|
||||||
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();
|
|
||||||
|
|
||||||
private final Set<Class> skipIgnores = new HashSet();
|
|
||||||
|
|
||||||
private boolean skipAllIgnore = false;
|
|
||||||
|
|
||||||
protected Factory(Factory<R, W> parent, boolean tiny) {
|
|
||||||
this.tiny = tiny;
|
|
||||||
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(CharSequence.class, CharSequenceSimpledCoder.instance);
|
|
||||||
this.register(java.util.Date.class, DateSimpledCoder.instance);
|
|
||||||
this.register(BigInteger.class, BigIntegerSimpledCoder.instance);
|
|
||||||
this.register(InetAddress.class, InetAddressSimpledCoder.instance);
|
|
||||||
this.register(DLong.class, DLongSimpledCoder.instance);
|
|
||||||
this.register(Class.class, TypeSimpledCoder.instance);
|
|
||||||
this.register(InetSocketAddress.class, InetSocketAddressSimpledCoder.instance);
|
|
||||||
this.register(Pattern.class, PatternSimpledCoder.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 abstract Factory createChild(boolean tiny);
|
|
||||||
|
|
||||||
public Convert getConvert() {
|
|
||||||
return convert;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setTiny(boolean tiny) {
|
|
||||||
this.tiny = tiny;
|
|
||||||
}
|
|
||||||
|
|
||||||
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)) {
|
|
||||||
ConvertColumnEntry entry = new ConvertColumnEntry(ref);
|
|
||||||
if (skipAllIgnore) {
|
|
||||||
entry.setIgnore(false);
|
|
||||||
return entry;
|
|
||||||
}
|
|
||||||
if (skipIgnores.isEmpty()) return entry;
|
|
||||||
if (skipIgnores.contains(((Member) field).getDeclaringClass())) entry.setIgnore(false);
|
|
||||||
return entry;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
final String getEntity(Class clazz) {
|
|
||||||
ConvertEntity ce = (ConvertEntity) clazz.getAnnotation(ConvertEntity.class);
|
|
||||||
if (ce != null && findEntity(ce.value()) == null) entitys.put(ce.value(), clazz);
|
|
||||||
return ce == null ? clazz.getName() : ce.value();
|
|
||||||
}
|
|
||||||
|
|
||||||
private Class findEntity(String name) {
|
|
||||||
Class clazz = entitys.get(name);
|
|
||||||
return parent == null ? clazz : parent.findEntity(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
final Class getEntity(String name) {
|
|
||||||
Class clazz = findEntity(name);
|
|
||||||
try {
|
|
||||||
return clazz == null ? Class.forName(name) : clazz;
|
|
||||||
} catch (Exception ex) {
|
|
||||||
throw new ConvertException("convert entity is " + name, ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 使所有类的所有被声明为ConvertColumn.ignore = true 的字段或方法变为ConvertColumn.ignore = false
|
|
||||||
* <p>
|
|
||||||
* @param skipIgnore
|
|
||||||
*/
|
|
||||||
public final void registerSkipAllIgnore(final boolean skipIgnore) {
|
|
||||||
this.skipAllIgnore = skipIgnore;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 使该类所有被声明为ConvertColumn.ignore = true 的字段或方法变为ConvertColumn.ignore = false
|
|
||||||
* <p>
|
|
||||||
* @param type
|
|
||||||
*/
|
|
||||||
public final void registerSkipIgnore(final Class type) {
|
|
||||||
skipIgnores.add(type);
|
|
||||||
}
|
|
||||||
|
|
||||||
public final void register(final Class type, boolean ignore, String... columns) {
|
|
||||||
for (String column : columns) {
|
|
||||||
register(type, column, new ConvertColumnEntry(column, ignore));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,72 +0,0 @@
|
|||||||
/*
|
|
||||||
* To change this template, choose Tools | Templates
|
|
||||||
* and open the template in the editor.
|
|
||||||
*/
|
|
||||||
package org.redkale.convert;
|
|
||||||
|
|
||||||
import java.lang.reflect.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 只增不减的伪Map类
|
|
||||||
*
|
|
||||||
* @see http://www.redkale.org
|
|
||||||
* @author zhangjx
|
|
||||||
* @param <K>
|
|
||||||
* @param <V>
|
|
||||||
*/
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public final class HashedMap<K extends Type, 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) {
|
|
||||||
V old = entry.value;
|
|
||||||
entry.value = value;
|
|
||||||
return old;
|
|
||||||
}
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,79 +0,0 @@
|
|||||||
/*
|
|
||||||
* To change this license header, choose License Headers in Project Properties.
|
|
||||||
* To change this template file, choose Tools | Templates
|
|
||||||
* and open the template in the editor.
|
|
||||||
*/
|
|
||||||
package org.redkale.convert;
|
|
||||||
|
|
||||||
import org.redkale.util.Creator;
|
|
||||||
import java.lang.reflect.ParameterizedType;
|
|
||||||
import java.lang.reflect.Type;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @see http://www.redkale.org
|
|
||||||
* @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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,63 +0,0 @@
|
|||||||
/*
|
|
||||||
* To change this license header, choose License Headers in Project Properties.
|
|
||||||
* To change this template file, choose Tools | Templates
|
|
||||||
* and open the template in the editor.
|
|
||||||
*/
|
|
||||||
package org.redkale.convert;
|
|
||||||
|
|
||||||
import java.lang.reflect.ParameterizedType;
|
|
||||||
import java.lang.reflect.Type;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @see http://www.redkale.org
|
|
||||||
* @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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,158 +0,0 @@
|
|||||||
/*
|
|
||||||
* To change this license header, choose License Headers in Project Properties.
|
|
||||||
* To change this template file, choose Tools | Templates
|
|
||||||
* and open the template in the editor.
|
|
||||||
*/
|
|
||||||
package org.redkale.convert;
|
|
||||||
|
|
||||||
import static org.redkale.convert.ObjectEncoder.TYPEZERO;
|
|
||||||
import org.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;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @see http://www.redkale.org
|
|
||||||
* @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;
|
|
||||||
|
|
||||||
private boolean inited = false;
|
|
||||||
|
|
||||||
private final Object lock = new Object();
|
|
||||||
|
|
||||||
protected ObjectDecoder(Type type) {
|
|
||||||
this.type = ((type instanceof Class) && ((Class) type).isInterface()) ? Object.class : 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;
|
|
||||||
try {
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
inited = true;
|
|
||||||
synchronized (lock) {
|
|
||||||
lock.notifyAll();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 对象格式: [0x1][short字段个数][字段名][字段值]...[0x2]
|
|
||||||
*
|
|
||||||
* @param in
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public final T convertFrom(final R in) {
|
|
||||||
final String clazz = in.readClassName();
|
|
||||||
if (clazz != null && !clazz.isEmpty()) return (T) factory.loadDecoder(factory.getEntity(clazz)).convertFrom(in);
|
|
||||||
if (in.readObjectB() == Reader.SIGN_NULL) return null;
|
|
||||||
if (!this.inited) {
|
|
||||||
synchronized (lock) {
|
|
||||||
try {
|
|
||||||
lock.wait();
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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) + '}';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,234 +0,0 @@
|
|||||||
/*
|
|
||||||
* To change this license header, choose License Headers in Project Properties.
|
|
||||||
* To change this template file, choose Tools | Templates
|
|
||||||
* and open the template in the editor.
|
|
||||||
*/
|
|
||||||
package org.redkale.convert;
|
|
||||||
|
|
||||||
import org.redkale.util.Attribute;
|
|
||||||
import java.lang.reflect.*;
|
|
||||||
import java.util.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @see http://www.redkale.org
|
|
||||||
* @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;
|
|
||||||
|
|
||||||
private boolean inited = false;
|
|
||||||
|
|
||||||
private final Object lock = new Object();
|
|
||||||
|
|
||||||
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;
|
|
||||||
try {
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
inited = true;
|
|
||||||
synchronized (lock) {
|
|
||||||
lock.notifyAll();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public final void convertTo(W out, T value) {
|
|
||||||
if (value == null) {
|
|
||||||
out.wirteClassName(null);
|
|
||||||
out.writeNull();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!this.inited) {
|
|
||||||
synchronized (lock) {
|
|
||||||
try {
|
|
||||||
lock.wait();
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (value != null && value.getClass() != this.typeClass) {
|
|
||||||
final Class clz = value.getClass();
|
|
||||||
out.wirteClassName(factory.getEntity(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) + '}';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,113 +0,0 @@
|
|||||||
/*
|
|
||||||
* To change this license header, choose License Headers in Project Properties.
|
|
||||||
* To change this template file, choose Tools | Templates
|
|
||||||
* and open the template in the editor.
|
|
||||||
*/
|
|
||||||
package org.redkale.convert;
|
|
||||||
|
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @see http://www.redkale.org
|
|
||||||
* @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();
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
/*
|
|
||||||
* To change this license header, choose License Headers in Project Properties.
|
|
||||||
* To change this template file, choose Tools | Templates
|
|
||||||
* and open the template in the editor.
|
|
||||||
*/
|
|
||||||
package org.redkale.convert;
|
|
||||||
|
|
||||||
import java.lang.reflect.ParameterizedType;
|
|
||||||
import java.lang.reflect.Type;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @see http://www.redkale.org
|
|
||||||
* @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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,121 +0,0 @@
|
|||||||
/*
|
|
||||||
* To change this license header, choose License Headers in Project Properties.
|
|
||||||
* To change this template file, choose Tools | Templates
|
|
||||||
* and open the template in the editor.
|
|
||||||
*/
|
|
||||||
package org.redkale.convert;
|
|
||||||
|
|
||||||
import org.redkale.util.Attribute;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @see http://www.redkale.org
|
|
||||||
* @author zhangjx
|
|
||||||
*/
|
|
||||||
public interface Writer {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 当tiny=true时, 字符串为空、boolean为false的字段值都会被跳过, 不会输出。
|
|
||||||
* <p>
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
public boolean isTiny();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 输出null值
|
|
||||||
*/
|
|
||||||
public void writeNull();
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @param clazz
|
|
||||||
*/
|
|
||||||
public void wirteClassName(String 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);
|
|
||||||
}
|
|
||||||
@@ -1,135 +0,0 @@
|
|||||||
/*
|
|
||||||
* To change this license header, choose License Headers in Project Properties.
|
|
||||||
* To change this template file, choose Tools | Templates
|
|
||||||
* and open the template in the editor.
|
|
||||||
*/
|
|
||||||
package org.redkale.convert.bson;
|
|
||||||
|
|
||||||
import java.nio.*;
|
|
||||||
import java.util.function.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @see http://www.redkale.org
|
|
||||||
* @author zhangjx
|
|
||||||
*/
|
|
||||||
public final class BsonByteBufferWriter extends BsonWriter {
|
|
||||||
|
|
||||||
private final Supplier<ByteBuffer> supplier;
|
|
||||||
|
|
||||||
private ByteBuffer[] buffers;
|
|
||||||
|
|
||||||
private int index;
|
|
||||||
|
|
||||||
protected BsonByteBufferWriter(Supplier<ByteBuffer> supplier) {
|
|
||||||
super((byte[]) null);
|
|
||||||
this.supplier = supplier;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ByteBuffer[] toBuffers() {
|
|
||||||
if (buffers == null) return new ByteBuffer[0];
|
|
||||||
for (int i = index; i < this.buffers.length; i++) {
|
|
||||||
ByteBuffer buf = this.buffers[i];
|
|
||||||
if (buf.position() != 0) buf.flip();
|
|
||||||
}
|
|
||||||
return this.buffers;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public byte[] toArray() {
|
|
||||||
if (buffers == null) return new byte[0];
|
|
||||||
int pos = 0;
|
|
||||||
byte[] bytes = new byte[this.count];
|
|
||||||
for (ByteBuffer buf : toBuffers()) {
|
|
||||||
int r = buf.remaining();
|
|
||||||
buf.get(bytes, pos, r);
|
|
||||||
buf.flip();
|
|
||||||
pos += r;
|
|
||||||
}
|
|
||||||
return bytes;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return this.getClass().getSimpleName() + "[count=" + this.count + "]";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public BsonByteBufferWriter setTiny(boolean tiny) {
|
|
||||||
this.tiny = tiny;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected int expand(final int byteLength) {
|
|
||||||
if (this.buffers == null) {
|
|
||||||
this.index = 0;
|
|
||||||
this.buffers = new ByteBuffer[]{supplier.get()};
|
|
||||||
}
|
|
||||||
ByteBuffer buffer = this.buffers[index];
|
|
||||||
if (!buffer.hasRemaining()) {
|
|
||||||
buffer.flip();
|
|
||||||
buffer = supplier.get();
|
|
||||||
ByteBuffer[] bufs = new ByteBuffer[this.buffers.length + 1];
|
|
||||||
System.arraycopy(this.buffers, 0, bufs, 0, this.buffers.length);
|
|
||||||
bufs[this.buffers.length] = buffer;
|
|
||||||
this.buffers = bufs;
|
|
||||||
this.index++;
|
|
||||||
}
|
|
||||||
int len = buffer.remaining();
|
|
||||||
int size = 0;
|
|
||||||
while (len < byteLength) {
|
|
||||||
buffer = supplier.get();
|
|
||||||
ByteBuffer[] bufs = new ByteBuffer[this.buffers.length + 1];
|
|
||||||
System.arraycopy(this.buffers, 0, bufs, 0, this.buffers.length);
|
|
||||||
bufs[this.buffers.length] = buffer;
|
|
||||||
this.buffers = bufs;
|
|
||||||
len += buffer.remaining();
|
|
||||||
size++;
|
|
||||||
}
|
|
||||||
return size;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void writeTo(final byte[] chs, final int start, final int len) {
|
|
||||||
if (expand(len) == 0) {
|
|
||||||
this.buffers[index].put(chs, start, len);
|
|
||||||
} else {
|
|
||||||
ByteBuffer buffer = this.buffers[index];
|
|
||||||
final int end = start + len;
|
|
||||||
int remain = len; //还剩多少没有写
|
|
||||||
while (remain > 0) {
|
|
||||||
final int br = buffer.remaining();
|
|
||||||
if (remain > br) { //一个buffer写不完
|
|
||||||
buffer.put(chs, end - remain, br);
|
|
||||||
buffer = nextByteBuffer();
|
|
||||||
remain -= br;
|
|
||||||
} else {
|
|
||||||
buffer.put(chs, end - remain, remain);
|
|
||||||
remain = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.count += len;
|
|
||||||
}
|
|
||||||
|
|
||||||
private ByteBuffer nextByteBuffer() {
|
|
||||||
this.buffers[this.index].flip();
|
|
||||||
return this.buffers[++this.index];
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void writeTo(final byte ch) {
|
|
||||||
expand(1);
|
|
||||||
this.buffers[index].put(ch);
|
|
||||||
count++;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected boolean recycle() {
|
|
||||||
this.index = 0;
|
|
||||||
this.buffers = null;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,156 +0,0 @@
|
|||||||
/*
|
|
||||||
* To change this license header, choose License Headers in Project Properties.
|
|
||||||
* To change this template file, choose Tools | Templates
|
|
||||||
* and open the template in the editor.
|
|
||||||
*/
|
|
||||||
package org.redkale.convert.bson;
|
|
||||||
|
|
||||||
import java.lang.reflect.*;
|
|
||||||
import java.nio.*;
|
|
||||||
import java.util.function.*;
|
|
||||||
import org.redkale.convert.*;
|
|
||||||
import org.redkale.util.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* BSON协议格式:
|
|
||||||
* 1). 基本数据类型: 直接转换成byte[]
|
|
||||||
* 2). SmallString(无特殊字符且长度小于256的字符串): length(1 byte) + byte[](utf8); 通常用于类名、字段名、枚举。
|
|
||||||
* 3). String: length(4 bytes) + byte[](utf8);
|
|
||||||
* 4). 数组: length(4 bytes) + byte[]...
|
|
||||||
* 5). Object:
|
|
||||||
* 1. realclass (SmallString) (如果指定格式化的class与实体对象的class不一致才会有该值)
|
|
||||||
* 2. 空字符串(SmallString)
|
|
||||||
* 3. SIGN_OBJECTB 标记位,值固定为0xBB (short)
|
|
||||||
* 4. 循环字段值:
|
|
||||||
* 4.1 SIGN_HASNEXT 标记位,值固定为1 (byte)
|
|
||||||
* 4.2 字段类型; 1-9为基本类型&字符串; 101-109为基本类型&字符串的数组; 127为Object
|
|
||||||
* 4.3 字段名 (SmallString)
|
|
||||||
* 4.4 字段的值Object
|
|
||||||
* 5. SIGN_NONEXT 标记位,值固定为0 (byte)
|
|
||||||
* 6. SIGN_OBJECTE 标记位,值固定为0xEE (short)
|
|
||||||
*
|
|
||||||
* @see http://www.redkale.org
|
|
||||||
* @author zhangjx
|
|
||||||
*/
|
|
||||||
public final class BsonConvert extends Convert<BsonReader, BsonWriter> {
|
|
||||||
|
|
||||||
private static final ObjectPool<BsonReader> readerPool = BsonReader.createPool(Integer.getInteger("convert.bson.pool.size", 16));
|
|
||||||
|
|
||||||
private static final ObjectPool<BsonWriter> writerPool = BsonWriter.createPool(Integer.getInteger("convert.bson.pool.size", 16));
|
|
||||||
|
|
||||||
private final boolean tiny;
|
|
||||||
|
|
||||||
protected BsonConvert(Factory<BsonReader, BsonWriter> factory, boolean tiny) {
|
|
||||||
super(factory);
|
|
||||||
this.tiny = tiny;
|
|
||||||
}
|
|
||||||
|
|
||||||
public BsonByteBufferWriter pollBsonWriter(final Supplier<ByteBuffer> supplier) {
|
|
||||||
return new BsonByteBufferWriter(supplier).setTiny(tiny);
|
|
||||||
}
|
|
||||||
|
|
||||||
public BsonWriter pollBsonWriter() {
|
|
||||||
return writerPool.get().setTiny(tiny);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void offerBsonWriter(BsonWriter out) {
|
|
||||||
if (out != null) writerPool.offer(out);
|
|
||||||
}
|
|
||||||
|
|
||||||
public BsonReader pollBsonReader() {
|
|
||||||
return readerPool.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void offerBsonReader(BsonReader in) {
|
|
||||||
if (in != null) readerPool.offer(in);
|
|
||||||
}
|
|
||||||
|
|
||||||
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.get();
|
|
||||||
in.setBytes(bytes, start, len);
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
T rs = (T) factory.loadDecoder(type).convertFrom(in);
|
|
||||||
readerPool.offer(in);
|
|
||||||
return rs;
|
|
||||||
}
|
|
||||||
|
|
||||||
public <T> T convertFrom(final BsonReader in, final Type type) {
|
|
||||||
if (type == null) return null;
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
T rs = (T) factory.loadDecoder(type).convertFrom(in);
|
|
||||||
return rs;
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte[] convertTo(final Type type, Object value) {
|
|
||||||
if (type == null) return null;
|
|
||||||
final BsonWriter out = writerPool.get().setTiny(tiny);
|
|
||||||
factory.loadEncoder(type).convertTo(out, value);
|
|
||||||
byte[] result = out.toArray();
|
|
||||||
writerPool.offer(out);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void convertTo(final BsonWriter out, final Type type, Object value) {
|
|
||||||
if (type == null) return;
|
|
||||||
factory.loadEncoder(type).convertTo(out, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ByteBuffer[] convertTo(final Supplier<ByteBuffer> supplier, final Type type, Object value) {
|
|
||||||
if (supplier == null || type == null) return null;
|
|
||||||
BsonByteBufferWriter out = new BsonByteBufferWriter(supplier);
|
|
||||||
if (value == null) {
|
|
||||||
out.writeNull();
|
|
||||||
} else {
|
|
||||||
factory.loadEncoder(type).convertTo(out, value);
|
|
||||||
}
|
|
||||||
return out.toBuffers();
|
|
||||||
}
|
|
||||||
|
|
||||||
public ByteBuffer[] convertTo(final Supplier<ByteBuffer> supplier, Object value) {
|
|
||||||
if (supplier == null) return null;
|
|
||||||
BsonByteBufferWriter out = new BsonByteBufferWriter(supplier);
|
|
||||||
if (value == null) {
|
|
||||||
out.writeNull();
|
|
||||||
} else {
|
|
||||||
factory.loadEncoder(value.getClass()).convertTo(out, value);
|
|
||||||
}
|
|
||||||
return out.toBuffers();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void convertTo(final BsonWriter out, Object value) {
|
|
||||||
if (value == null) {
|
|
||||||
out.writeNull();
|
|
||||||
} else {
|
|
||||||
factory.loadEncoder(value.getClass()).convertTo(out, value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte[] convertTo(Object value) {
|
|
||||||
if (value == null) {
|
|
||||||
final BsonWriter out = writerPool.get().setTiny(tiny);
|
|
||||||
out.writeNull();
|
|
||||||
byte[] result = out.toArray();
|
|
||||||
writerPool.offer(out);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
return convertTo(value.getClass(), value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public BsonWriter convertToWriter(final Type type, Object value) {
|
|
||||||
if (type == null) return null;
|
|
||||||
final BsonWriter out = writerPool.get().setTiny(tiny);
|
|
||||||
factory.loadEncoder(type).convertTo(out, value);
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
public BsonWriter convertToWriter(Object value) {
|
|
||||||
if (value == null) return null;
|
|
||||||
return convertToWriter(value.getClass(), value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,63 +0,0 @@
|
|||||||
/*
|
|
||||||
* To change this license header, choose License Headers in Project Properties.
|
|
||||||
* To change this template file, choose Tools | Templates
|
|
||||||
* and open the template in the editor.
|
|
||||||
*/
|
|
||||||
package org.redkale.convert.bson;
|
|
||||||
|
|
||||||
import java.io.Serializable;
|
|
||||||
import org.redkale.convert.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @see http://www.redkale.org
|
|
||||||
* @author zhangjx
|
|
||||||
*/
|
|
||||||
public final class BsonFactory extends Factory<BsonReader, BsonWriter> {
|
|
||||||
|
|
||||||
private static final BsonFactory instance = new BsonFactory(null, Boolean.getBoolean("convert.bson.tiny"));
|
|
||||||
|
|
||||||
static final Decodeable objectDecoder = instance.loadDecoder(Object.class);
|
|
||||||
|
|
||||||
static final Encodeable objectEncoder = instance.loadEncoder(Object.class);
|
|
||||||
|
|
||||||
static {
|
|
||||||
instance.register(Serializable.class, objectDecoder);
|
|
||||||
instance.register(Serializable.class, objectEncoder);
|
|
||||||
}
|
|
||||||
|
|
||||||
private BsonFactory(BsonFactory parent, boolean tiny) {
|
|
||||||
super(parent, tiny);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static BsonFactory root() {
|
|
||||||
return instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public final BsonConvert getConvert() {
|
|
||||||
if (convert == null) convert = new BsonConvert(this, tiny);
|
|
||||||
return (BsonConvert) convert;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public BsonFactory createChild() {
|
|
||||||
return new BsonFactory(this, this.tiny);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public BsonFactory createChild(boolean tiny) {
|
|
||||||
return new BsonFactory(this, tiny);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ConvertType getConvertType() {
|
|
||||||
return ConvertType.BSON;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isReversible() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,324 +0,0 @@
|
|||||||
/*
|
|
||||||
* To change this license header, choose License Headers in Project Properties.
|
|
||||||
* To change this template file, choose Tools | Templates
|
|
||||||
* and open the template in the editor.
|
|
||||||
*/
|
|
||||||
package org.redkale.convert.bson;
|
|
||||||
|
|
||||||
import java.util.concurrent.atomic.*;
|
|
||||||
import java.util.function.*;
|
|
||||||
import org.redkale.convert.*;
|
|
||||||
import static org.redkale.convert.Reader.SIGN_NULL;
|
|
||||||
import org.redkale.convert.ext.*;
|
|
||||||
import org.redkale.util.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @see http://www.redkale.org
|
|
||||||
* @author zhangjx
|
|
||||||
*/
|
|
||||||
public final class BsonReader implements Reader {
|
|
||||||
|
|
||||||
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 typeval; //字段的类型值 对应 BsonWriter.writeField
|
|
||||||
|
|
||||||
private byte[] content;
|
|
||||||
|
|
||||||
public BsonReader() {
|
|
||||||
}
|
|
||||||
|
|
||||||
public static ObjectPool<BsonReader> createPool(int max) {
|
|
||||||
return new ObjectPool<BsonReader>(max, new Creator<BsonReader>() {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public BsonReader create(Object... params) {
|
|
||||||
return new BsonReader();
|
|
||||||
}
|
|
||||||
}, null, new Predicate<BsonReader>() {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean test(BsonReader t) {
|
|
||||||
return t.recycle();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
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) {
|
|
||||||
if (bytes == null) {
|
|
||||||
this.position = 0;
|
|
||||||
} else {
|
|
||||||
setBytes(bytes, 0, bytes.length);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public final void setBytes(byte[] bytes, int start, int len) {
|
|
||||||
if (bytes == null) {
|
|
||||||
this.position = 0;
|
|
||||||
} else {
|
|
||||||
this.content = bytes;
|
|
||||||
this.position = start - 1;
|
|
||||||
//this.limit = start + len - 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected boolean recycle() {
|
|
||||||
this.position = -1;
|
|
||||||
this.typeval = 0;
|
|
||||||
//this.limit = -1;
|
|
||||||
this.content = null;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void close() {
|
|
||||||
this.recycle();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 跳过属性的值
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public final void skipValue() {
|
|
||||||
if (typeval == 0) return;
|
|
||||||
final byte val = this.typeval;
|
|
||||||
this.typeval = 0;
|
|
||||||
switch (val) {
|
|
||||||
case 1: readBoolean();
|
|
||||||
break;
|
|
||||||
case 2: readByte();
|
|
||||||
break;
|
|
||||||
case 3: readShort();
|
|
||||||
break;
|
|
||||||
case 4: readChar();
|
|
||||||
break;
|
|
||||||
case 5: readInt();
|
|
||||||
break;
|
|
||||||
case 6: readLong();
|
|
||||||
break;
|
|
||||||
case 7: readFloat();
|
|
||||||
break;
|
|
||||||
case 8: readDouble();
|
|
||||||
break;
|
|
||||||
case 9: readString();
|
|
||||||
break;
|
|
||||||
case 101:
|
|
||||||
BoolArraySimpledCoder.instance.convertFrom(this);
|
|
||||||
break;
|
|
||||||
case 102:
|
|
||||||
ByteArraySimpledCoder.instance.convertFrom(this);
|
|
||||||
break;
|
|
||||||
case 103:
|
|
||||||
ShortArraySimpledCoder.instance.convertFrom(this);
|
|
||||||
break;
|
|
||||||
case 104:
|
|
||||||
CharArraySimpledCoder.instance.convertFrom(this);
|
|
||||||
break;
|
|
||||||
case 105:
|
|
||||||
IntArraySimpledCoder.instance.convertFrom(this);
|
|
||||||
break;
|
|
||||||
case 106:
|
|
||||||
LongArraySimpledCoder.instance.convertFrom(this);
|
|
||||||
break;
|
|
||||||
case 107:
|
|
||||||
FloatArraySimpledCoder.instance.convertFrom(this);
|
|
||||||
break;
|
|
||||||
case 108:
|
|
||||||
DoubleArraySimpledCoder.instance.convertFrom(this);
|
|
||||||
break;
|
|
||||||
case 109:
|
|
||||||
StringArraySimpledCoder.instance.convertFrom(this);
|
|
||||||
break;
|
|
||||||
case 127:
|
|
||||||
BsonFactory.objectDecoder.convertFrom(this);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 判断下一个非空白字节是否为{
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
@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() {
|
|
||||||
short bt = readShort();
|
|
||||||
if (bt == Reader.SIGN_NULL) return bt;
|
|
||||||
return (bt & 0xffff) << 16 | ((content[++this.position] & 0xff) << 8) | (content[++this.position] & 0xff);
|
|
||||||
}
|
|
||||||
|
|
||||||
@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();
|
|
||||||
this.typeval = readByte();
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
/*
|
|
||||||
* To change this license header, choose License Headers in Project Properties.
|
|
||||||
* To change this template file, choose Tools | Templates
|
|
||||||
* and open the template in the editor.
|
|
||||||
*/
|
|
||||||
package org.redkale.convert.bson;
|
|
||||||
|
|
||||||
import org.redkale.convert.SimpledCoder;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @see http://www.redkale.org
|
|
||||||
* @author zhangjx
|
|
||||||
* @param <T>
|
|
||||||
*/
|
|
||||||
public abstract class BsonSimpledCoder<T> extends SimpledCoder<BsonReader, BsonWriter, T> {
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,298 +0,0 @@
|
|||||||
/*
|
|
||||||
* To change this license header, choose License Headers in Project Properties.
|
|
||||||
* To change this template file, choose Tools | Templates
|
|
||||||
* and open the template in the editor.
|
|
||||||
*/
|
|
||||||
package org.redkale.convert.bson;
|
|
||||||
|
|
||||||
import java.nio.*;
|
|
||||||
import java.util.function.*;
|
|
||||||
import org.redkale.convert.*;
|
|
||||||
import org.redkale.util.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @see http://www.redkale.org
|
|
||||||
* @author zhangjx
|
|
||||||
*/
|
|
||||||
public class BsonWriter implements Writer {
|
|
||||||
|
|
||||||
private static final int defaultSize = Integer.getInteger("convert.bson.writer.buffer.defsize", 1024);
|
|
||||||
|
|
||||||
private byte[] content;
|
|
||||||
|
|
||||||
protected int count;
|
|
||||||
|
|
||||||
protected boolean tiny;
|
|
||||||
|
|
||||||
public static ObjectPool<BsonWriter> createPool(int max) {
|
|
||||||
return new ObjectPool<BsonWriter>(max, new Creator<BsonWriter>() {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public BsonWriter create(Object... params) {
|
|
||||||
return new BsonWriter();
|
|
||||||
}
|
|
||||||
}, null, new Predicate<BsonWriter>() {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean test(BsonWriter t) {
|
|
||||||
return t.recycle();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte[] toArray() {
|
|
||||||
if (count == content.length) return content;
|
|
||||||
byte[] newdata = new byte[count];
|
|
||||||
System.arraycopy(content, 0, newdata, 0, count);
|
|
||||||
return newdata;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ByteBuffer[] toBuffers() {
|
|
||||||
return new ByteBuffer[]{ByteBuffer.wrap(content, 0, count)};
|
|
||||||
}
|
|
||||||
|
|
||||||
protected BsonWriter(byte[] bs) {
|
|
||||||
this.content = bs;
|
|
||||||
}
|
|
||||||
|
|
||||||
public BsonWriter() {
|
|
||||||
this(defaultSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
public BsonWriter(int size) {
|
|
||||||
this.content = new byte[size > 128 ? size : 128];
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public final boolean isTiny() {
|
|
||||||
return tiny;
|
|
||||||
}
|
|
||||||
|
|
||||||
public BsonWriter setTiny(boolean tiny) {
|
|
||||||
this.tiny = tiny;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
//-----------------------------------------------------------------------
|
|
||||||
//-----------------------------------------------------------------------
|
|
||||||
/**
|
|
||||||
* 扩充指定长度的缓冲区
|
|
||||||
*
|
|
||||||
* @param len
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
protected int expand(int len) {
|
|
||||||
int newcount = count + len;
|
|
||||||
if (newcount <= content.length) return 0;
|
|
||||||
byte[] newdata = new byte[Math.max(content.length * 3 / 2, newcount)];
|
|
||||||
System.arraycopy(content, 0, newdata, 0, count);
|
|
||||||
this.content = newdata;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void writeTo(final byte ch) {
|
|
||||||
expand(1);
|
|
||||||
content[count++] = ch;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final void writeTo(final byte... chs) {
|
|
||||||
writeTo(chs, 0, chs.length);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void writeTo(final byte[] chs, final int start, final int len) {
|
|
||||||
expand(len);
|
|
||||||
System.arraycopy(chs, start, content, count, len);
|
|
||||||
count += len;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected boolean recycle() {
|
|
||||||
this.count = 0;
|
|
||||||
if (this.content.length > defaultSize) {
|
|
||||||
this.content = new byte[defaultSize];
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return this.getClass().getSimpleName() + "[count=" + this.count + "]";
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------
|
|
||||||
public final int count() {
|
|
||||||
return this.count;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public final void writeBoolean(boolean value) {
|
|
||||||
writeTo(value ? (byte) 1 : (byte) 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public final void writeByte(byte value) {
|
|
||||||
writeTo(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public final void writeChar(final char value) {
|
|
||||||
writeTo((byte) ((value & 0xFF00) >> 8), (byte) (value & 0xFF));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public final void writeShort(short value) {
|
|
||||||
writeTo((byte) (value >> 8), (byte) value);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public final void writeInt(int value) {
|
|
||||||
writeTo((byte) (value >> 24), (byte) (value >> 16), (byte) (value >> 8), (byte) value);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public final 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 final void writeFloat(float value) {
|
|
||||||
writeInt(Float.floatToIntBits(value));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public final void writeDouble(double value) {
|
|
||||||
writeLong(Double.doubleToLongBits(value));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public final void wirteClassName(String clazz) {
|
|
||||||
writeSmallString(clazz == null ? "" : clazz);
|
|
||||||
}
|
|
||||||
|
|
||||||
@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());
|
|
||||||
byte typeval = 127; //字段的类型值
|
|
||||||
final Class type = attribute.type();
|
|
||||||
if (type == boolean.class || type == Boolean.class) {
|
|
||||||
typeval = 1;
|
|
||||||
} else if (type == byte.class || type == Byte.class) {
|
|
||||||
typeval = 2;
|
|
||||||
} else if (type == short.class || type == Short.class) {
|
|
||||||
typeval = 3;
|
|
||||||
} else if (type == char.class || type == Character.class) {
|
|
||||||
typeval = 4;
|
|
||||||
} else if (type == int.class || type == Integer.class) {
|
|
||||||
typeval = 5;
|
|
||||||
} else if (type == long.class || type == Long.class) {
|
|
||||||
typeval = 6;
|
|
||||||
} else if (type == float.class || type == Float.class) {
|
|
||||||
typeval = 7;
|
|
||||||
} else if (type == double.class || type == Double.class) {
|
|
||||||
typeval = 8;
|
|
||||||
} else if (type == String.class) {
|
|
||||||
typeval = 9;
|
|
||||||
} else if (type == boolean[].class || type == Boolean[].class) {
|
|
||||||
typeval = 101;
|
|
||||||
} else if (type == byte[].class || type == Byte[].class) {
|
|
||||||
typeval = 102;
|
|
||||||
} else if (type == short[].class || type == Short[].class) {
|
|
||||||
typeval = 103;
|
|
||||||
} else if (type == char[].class || type == Character[].class) {
|
|
||||||
typeval = 104;
|
|
||||||
} else if (type == int[].class || type == Integer[].class) {
|
|
||||||
typeval = 105;
|
|
||||||
} else if (type == long[].class || type == Long[].class) {
|
|
||||||
typeval = 106;
|
|
||||||
} else if (type == float[].class || type == Float[].class) {
|
|
||||||
typeval = 107;
|
|
||||||
} else if (type == double[].class || type == Double[].class) {
|
|
||||||
typeval = 108;
|
|
||||||
} else if (type == String[].class) {
|
|
||||||
typeval = 109;
|
|
||||||
}
|
|
||||||
writeByte(typeval);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 对于类的字段名、枚举值这些长度一般不超过255且不会出现双字节字符的字符串采用writeSmallString处理, readSmallString用于读取
|
|
||||||
*
|
|
||||||
* @param value
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public final 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 final 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 final void writeArrayB(int size) {
|
|
||||||
writeInt(size);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public final void writeArrayMark() {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public final void writeArrayE() {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void writeMapB(int size) {
|
|
||||||
writeArrayB(size);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public final void writeMapMark() {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public final void writeMapE() {
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
/*
|
|
||||||
* To change this license header, choose License Headers in Project Properties.
|
|
||||||
* To change this template file, choose Tools | Templates
|
|
||||||
* and open the template in the editor.
|
|
||||||
*/
|
|
||||||
package org.redkale.convert.ext;
|
|
||||||
|
|
||||||
import org.redkale.convert.SimpledCoder;
|
|
||||||
import org.redkale.convert.Writer;
|
|
||||||
import org.redkale.convert.Reader;
|
|
||||||
import java.math.BigInteger;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @see http://www.redkale.org
|
|
||||||
* @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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,68 +0,0 @@
|
|||||||
/*
|
|
||||||
* To change this license header, choose License Headers in Project Properties.
|
|
||||||
* To change this template file, choose Tools | Templates
|
|
||||||
* and open the template in the editor.
|
|
||||||
*/
|
|
||||||
package org.redkale.convert.ext;
|
|
||||||
|
|
||||||
import org.redkale.convert.Reader;
|
|
||||||
import org.redkale.convert.SimpledCoder;
|
|
||||||
import org.redkale.convert.Writer;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @see http://www.redkale.org
|
|
||||||
* @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;
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
/*
|
|
||||||
* To change this license header, choose License Headers in Project Properties.
|
|
||||||
* To change this template file, choose Tools | Templates
|
|
||||||
* and open the template in the editor.
|
|
||||||
*/
|
|
||||||
package org.redkale.convert.ext;
|
|
||||||
|
|
||||||
import org.redkale.convert.Reader;
|
|
||||||
import org.redkale.convert.SimpledCoder;
|
|
||||||
import org.redkale.convert.Writer;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @see http://www.redkale.org
|
|
||||||
* @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();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,68 +0,0 @@
|
|||||||
/*
|
|
||||||
* To change this license header, choose License Headers in Project Properties.
|
|
||||||
* To change this template file, choose Tools | Templates
|
|
||||||
* and open the template in the editor.
|
|
||||||
*/
|
|
||||||
package org.redkale.convert.ext;
|
|
||||||
|
|
||||||
import org.redkale.convert.Reader;
|
|
||||||
import org.redkale.convert.SimpledCoder;
|
|
||||||
import org.redkale.convert.Writer;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @see http://www.redkale.org
|
|
||||||
* @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;
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
/*
|
|
||||||
* To change this license header, choose License Headers in Project Properties.
|
|
||||||
* To change this template file, choose Tools | Templates
|
|
||||||
* and open the template in the editor.
|
|
||||||
*/
|
|
||||||
package org.redkale.convert.ext;
|
|
||||||
|
|
||||||
import org.redkale.convert.Reader;
|
|
||||||
import org.redkale.convert.SimpledCoder;
|
|
||||||
import org.redkale.convert.Writer;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @see http://www.redkale.org
|
|
||||||
* @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();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,68 +0,0 @@
|
|||||||
/*
|
|
||||||
* To change this license header, choose License Headers in Project Properties.
|
|
||||||
* To change this template file, choose Tools | Templates
|
|
||||||
* and open the template in the editor.
|
|
||||||
*/
|
|
||||||
package org.redkale.convert.ext;
|
|
||||||
|
|
||||||
import org.redkale.convert.Reader;
|
|
||||||
import org.redkale.convert.SimpledCoder;
|
|
||||||
import org.redkale.convert.Writer;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @see http://www.redkale.org
|
|
||||||
* @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;
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
/*
|
|
||||||
* To change this license header, choose License Headers in Project Properties.
|
|
||||||
* To change this template file, choose Tools | Templates
|
|
||||||
* and open the template in the editor.
|
|
||||||
*/
|
|
||||||
package org.redkale.convert.ext;
|
|
||||||
|
|
||||||
import org.redkale.convert.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @author zhangjx
|
|
||||||
* @param <R>
|
|
||||||
* @param <W>
|
|
||||||
*/
|
|
||||||
public class CharSequenceSimpledCoder<R extends Reader, W extends Writer> extends SimpledCoder<R, W, CharSequence> {
|
|
||||||
|
|
||||||
public static final CharSequenceSimpledCoder instance = new CharSequenceSimpledCoder();
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void convertTo(W out, CharSequence value) {
|
|
||||||
out.writeString(value == null ? null : value.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public CharSequence convertFrom(R in) {
|
|
||||||
return in.readString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
/*
|
|
||||||
* To change this license header, choose License Headers in Project Properties.
|
|
||||||
* To change this template file, choose Tools | Templates
|
|
||||||
* and open the template in the editor.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.redkale.convert.ext;
|
|
||||||
|
|
||||||
import org.redkale.convert.Reader;
|
|
||||||
import org.redkale.convert.SimpledCoder;
|
|
||||||
import org.redkale.convert.Writer;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @see http://www.redkale.org
|
|
||||||
* @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();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
/*
|
|
||||||
* To change this license header, choose License Headers in Project Properties.
|
|
||||||
* To change this template file, choose Tools | Templates
|
|
||||||
* and open the template in the editor.
|
|
||||||
*/
|
|
||||||
package org.redkale.convert.ext;
|
|
||||||
|
|
||||||
import org.redkale.convert.Reader;
|
|
||||||
import org.redkale.convert.Writer;
|
|
||||||
import org.redkale.convert.SimpledCoder;
|
|
||||||
import org.redkale.util.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @see http://www.redkale.org
|
|
||||||
* @author zhangjx
|
|
||||||
* @param <R>
|
|
||||||
* @param <W>
|
|
||||||
*/
|
|
||||||
public final class DLongSimpledCoder<R extends Reader, W extends Writer> extends SimpledCoder<R, W, DLong> {
|
|
||||||
|
|
||||||
private static final ByteArraySimpledCoder bsSimpledCoder = ByteArraySimpledCoder.instance;
|
|
||||||
|
|
||||||
public static final DLongSimpledCoder instance = new DLongSimpledCoder();
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void convertTo(final W out, final DLong value) {
|
|
||||||
if (value == null) {
|
|
||||||
out.writeNull();
|
|
||||||
} else {
|
|
||||||
bsSimpledCoder.convertTo(out, value.directBytes());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public DLong convertFrom(R in) {
|
|
||||||
byte[] bs = bsSimpledCoder.convertFrom(in);
|
|
||||||
if (bs == null) return null;
|
|
||||||
return new DLong(bs);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
/*
|
|
||||||
* To change this license header, choose License Headers in Project Properties.
|
|
||||||
* To change this template file, choose Tools | Templates
|
|
||||||
* and open the template in the editor.
|
|
||||||
*/
|
|
||||||
package org.redkale.convert.ext;
|
|
||||||
|
|
||||||
import org.redkale.convert.Reader;
|
|
||||||
import org.redkale.convert.SimpledCoder;
|
|
||||||
import org.redkale.convert.Writer;
|
|
||||||
import java.util.Date;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @see http://www.redkale.org
|
|
||||||
* @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());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,68 +0,0 @@
|
|||||||
/*
|
|
||||||
* To change this license header, choose License Headers in Project Properties.
|
|
||||||
* To change this template file, choose Tools | Templates
|
|
||||||
* and open the template in the editor.
|
|
||||||
*/
|
|
||||||
package org.redkale.convert.ext;
|
|
||||||
|
|
||||||
import org.redkale.convert.Reader;
|
|
||||||
import org.redkale.convert.SimpledCoder;
|
|
||||||
import org.redkale.convert.Writer;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @see http://www.redkale.org
|
|
||||||
* @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;
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
/*
|
|
||||||
* To change this license header, choose License Headers in Project Properties.
|
|
||||||
* To change this template file, choose Tools | Templates
|
|
||||||
* and open the template in the editor.
|
|
||||||
*/
|
|
||||||
package org.redkale.convert.ext;
|
|
||||||
|
|
||||||
import org.redkale.convert.Reader;
|
|
||||||
import org.redkale.convert.SimpledCoder;
|
|
||||||
import org.redkale.convert.Writer;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @see http://www.redkale.org
|
|
||||||
* @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();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,45 +0,0 @@
|
|||||||
/*
|
|
||||||
* To change this license header, choose License Headers in Project Properties.
|
|
||||||
* To change this template file, choose Tools | Templates
|
|
||||||
* and open the template in the editor.
|
|
||||||
*/
|
|
||||||
package org.redkale.convert.ext;
|
|
||||||
|
|
||||||
import org.redkale.convert.Reader;
|
|
||||||
import org.redkale.convert.SimpledCoder;
|
|
||||||
import org.redkale.convert.Writer;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @see http://www.redkale.org
|
|
||||||
* @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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,68 +0,0 @@
|
|||||||
/*
|
|
||||||
* To change this license header, choose License Headers in Project Properties.
|
|
||||||
* To change this template file, choose Tools | Templates
|
|
||||||
* and open the template in the editor.
|
|
||||||
*/
|
|
||||||
package org.redkale.convert.ext;
|
|
||||||
|
|
||||||
import org.redkale.convert.Reader;
|
|
||||||
import org.redkale.convert.SimpledCoder;
|
|
||||||
import org.redkale.convert.Writer;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @see http://www.redkale.org
|
|
||||||
* @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;
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
/*
|
|
||||||
* To change this license header, choose License Headers in Project Properties.
|
|
||||||
* To change this template file, choose Tools | Templates
|
|
||||||
* and open the template in the editor.
|
|
||||||
*/
|
|
||||||
package org.redkale.convert.ext;
|
|
||||||
|
|
||||||
import org.redkale.convert.Reader;
|
|
||||||
import org.redkale.convert.SimpledCoder;
|
|
||||||
import org.redkale.convert.Writer;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @see http://www.redkale.org
|
|
||||||
* @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();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,71 +0,0 @@
|
|||||||
/*
|
|
||||||
* To change this license header, choose License Headers in Project Properties.
|
|
||||||
* To change this template file, choose Tools | Templates
|
|
||||||
* and open the template in the editor.
|
|
||||||
*/
|
|
||||||
package org.redkale.convert.ext;
|
|
||||||
|
|
||||||
import org.redkale.convert.SimpledCoder;
|
|
||||||
import org.redkale.convert.Writer;
|
|
||||||
import org.redkale.convert.Reader;
|
|
||||||
import java.net.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @see http://www.redkale.org
|
|
||||||
* @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 final 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,68 +0,0 @@
|
|||||||
/*
|
|
||||||
* To change this license header, choose License Headers in Project Properties.
|
|
||||||
* To change this template file, choose Tools | Templates
|
|
||||||
* and open the template in the editor.
|
|
||||||
*/
|
|
||||||
package org.redkale.convert.ext;
|
|
||||||
|
|
||||||
import org.redkale.convert.Reader;
|
|
||||||
import org.redkale.convert.SimpledCoder;
|
|
||||||
import org.redkale.convert.Writer;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @see http://www.redkale.org
|
|
||||||
* @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;
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
/*
|
|
||||||
* To change this license header, choose License Headers in Project Properties.
|
|
||||||
* To change this template file, choose Tools | Templates
|
|
||||||
* and open the template in the editor.
|
|
||||||
*/
|
|
||||||
package org.redkale.convert.ext;
|
|
||||||
|
|
||||||
import org.redkale.convert.Reader;
|
|
||||||
import org.redkale.convert.SimpledCoder;
|
|
||||||
import org.redkale.convert.Writer;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @see http://www.redkale.org
|
|
||||||
* @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();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,68 +0,0 @@
|
|||||||
/*
|
|
||||||
* To change this license header, choose License Headers in Project Properties.
|
|
||||||
* To change this template file, choose Tools | Templates
|
|
||||||
* and open the template in the editor.
|
|
||||||
*/
|
|
||||||
package org.redkale.convert.ext;
|
|
||||||
|
|
||||||
import org.redkale.convert.Reader;
|
|
||||||
import org.redkale.convert.SimpledCoder;
|
|
||||||
import org.redkale.convert.Writer;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @see http://www.redkale.org
|
|
||||||
* @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;
|
|
||||||
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.readLong();
|
|
||||||
}
|
|
||||||
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.readLong();
|
|
||||||
}
|
|
||||||
in.readArrayE();
|
|
||||||
return values;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
/*
|
|
||||||
* To change this license header, choose License Headers in Project Properties.
|
|
||||||
* To change this template file, choose Tools | Templates
|
|
||||||
* and open the template in the editor.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.redkale.convert.ext;
|
|
||||||
|
|
||||||
import org.redkale.convert.Reader;
|
|
||||||
import org.redkale.convert.SimpledCoder;
|
|
||||||
import org.redkale.convert.Writer;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @see http://www.redkale.org
|
|
||||||
* @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();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
/*
|
|
||||||
* To change this license header, choose License Headers in Project Properties.
|
|
||||||
* To change this template file, choose Tools | Templates
|
|
||||||
* and open the template in the editor.
|
|
||||||
*/
|
|
||||||
package org.redkale.convert.ext;
|
|
||||||
|
|
||||||
import org.redkale.convert.Reader;
|
|
||||||
import org.redkale.convert.SimpledCoder;
|
|
||||||
import org.redkale.convert.Writer;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @see http://www.redkale.org
|
|
||||||
* @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();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
/*
|
|
||||||
* To change this license header, choose License Headers in Project Properties.
|
|
||||||
* To change this template file, choose Tools | Templates
|
|
||||||
* and open the template in the editor.
|
|
||||||
*/
|
|
||||||
package org.redkale.convert.ext;
|
|
||||||
|
|
||||||
import java.util.regex.*;
|
|
||||||
import org.redkale.convert.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @see http://www.redkale.org
|
|
||||||
* @author zhangjx
|
|
||||||
* @param <R>
|
|
||||||
* @param <W>
|
|
||||||
*/
|
|
||||||
public class PatternSimpledCoder<R extends Reader, W extends Writer> extends SimpledCoder<R, W, Pattern> {
|
|
||||||
|
|
||||||
public static final PatternSimpledCoder instance = new PatternSimpledCoder();
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void convertTo(W out, Pattern value) {
|
|
||||||
if (value == null) {
|
|
||||||
out.writeNull();
|
|
||||||
} else {
|
|
||||||
out.writeString(value.flags() + "," + value.pattern());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Pattern convertFrom(R in) {
|
|
||||||
String value = in.readString();
|
|
||||||
if (value == null) return null;
|
|
||||||
int pos = value.indexOf(',');
|
|
||||||
return Pattern.compile(value.substring(pos + 1), Integer.parseInt(value.substring(0, pos)));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,68 +0,0 @@
|
|||||||
/*
|
|
||||||
* To change this license header, choose License Headers in Project Properties.
|
|
||||||
* To change this template file, choose Tools | Templates
|
|
||||||
* and open the template in the editor.
|
|
||||||
*/
|
|
||||||
package org.redkale.convert.ext;
|
|
||||||
|
|
||||||
import org.redkale.convert.Reader;
|
|
||||||
import org.redkale.convert.SimpledCoder;
|
|
||||||
import org.redkale.convert.Writer;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @see http://www.redkale.org
|
|
||||||
* @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;
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
/*
|
|
||||||
* To change this license header, choose License Headers in Project Properties.
|
|
||||||
* To change this template file, choose Tools | Templates
|
|
||||||
* and open the template in the editor.
|
|
||||||
*/
|
|
||||||
package org.redkale.convert.ext;
|
|
||||||
|
|
||||||
import org.redkale.convert.Reader;
|
|
||||||
import org.redkale.convert.SimpledCoder;
|
|
||||||
import org.redkale.convert.Writer;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @see http://www.redkale.org
|
|
||||||
* @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();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,68 +0,0 @@
|
|||||||
/*
|
|
||||||
* To change this license header, choose License Headers in Project Properties.
|
|
||||||
* To change this template file, choose Tools | Templates
|
|
||||||
* and open the template in the editor.
|
|
||||||
*/
|
|
||||||
package org.redkale.convert.ext;
|
|
||||||
|
|
||||||
import org.redkale.convert.Reader;
|
|
||||||
import org.redkale.convert.SimpledCoder;
|
|
||||||
import org.redkale.convert.Writer;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @see http://www.redkale.org
|
|
||||||
* @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;
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
/*
|
|
||||||
* To change this license header, choose License Headers in Project Properties.
|
|
||||||
* To change this template file, choose Tools | Templates
|
|
||||||
* and open the template in the editor.
|
|
||||||
*/
|
|
||||||
package org.redkale.convert.ext;
|
|
||||||
|
|
||||||
import org.redkale.convert.Reader;
|
|
||||||
import org.redkale.convert.SimpledCoder;
|
|
||||||
import org.redkale.convert.Writer;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @see http://www.redkale.org
|
|
||||||
* @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();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
/*
|
|
||||||
* To change this license header, choose License Headers in Project Properties.
|
|
||||||
* To change this template file, choose Tools | Templates
|
|
||||||
* and open the template in the editor.
|
|
||||||
*/
|
|
||||||
package org.redkale.convert.ext;
|
|
||||||
|
|
||||||
import org.redkale.convert.Reader;
|
|
||||||
import org.redkale.convert.Writer;
|
|
||||||
import org.redkale.convert.SimpledCoder;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @see http://www.redkale.org
|
|
||||||
* @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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
/*
|
|
||||||
* To change this license header, choose License Headers in Project Properties.
|
|
||||||
* To change this template file, choose Tools | Templates
|
|
||||||
* and open the template in the editor.
|
|
||||||
*/
|
|
||||||
package org.redkale.convert.json;
|
|
||||||
|
|
||||||
import org.redkale.util.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @see http://www.redkale.org
|
|
||||||
* @author zhangjx
|
|
||||||
*/
|
|
||||||
public class DLongJsonSimpledCoder extends JsonSimpledCoder<DLong> {
|
|
||||||
|
|
||||||
public static final DLongJsonSimpledCoder instance = new DLongJsonSimpledCoder();
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void convertTo(final JsonWriter out, final DLong value) {
|
|
||||||
if (value == null) {
|
|
||||||
out.writeNull();
|
|
||||||
} else {
|
|
||||||
out.writeSmallString(value.toString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public DLong convertFrom(JsonReader in) {
|
|
||||||
final String str = in.readString();
|
|
||||||
if (str == null) return null;
|
|
||||||
return new DLong(Utility.hexToBin(str));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,69 +0,0 @@
|
|||||||
/*
|
|
||||||
* To change this license header, choose License Headers in Project Properties.
|
|
||||||
* To change this template file, choose Tools | Templates
|
|
||||||
* and open the template in the editor.
|
|
||||||
*/
|
|
||||||
package org.redkale.convert.json;
|
|
||||||
|
|
||||||
import java.net.*;
|
|
||||||
import org.redkale.convert.*;
|
|
||||||
import org.redkale.convert.ext.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @see http://www.redkale.org
|
|
||||||
* @author zhangjx
|
|
||||||
* @param <R>
|
|
||||||
* @param <W>
|
|
||||||
*/
|
|
||||||
public final class InetAddressJsonSimpledCoder<R extends Reader, W extends Writer> extends SimpledCoder<JsonReader, JsonWriter, InetAddress> {
|
|
||||||
|
|
||||||
public static final InetAddressJsonSimpledCoder instance = new InetAddressJsonSimpledCoder();
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void convertTo(JsonWriter out, InetAddress value) {
|
|
||||||
if (value == null) {
|
|
||||||
out.writeNull();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
StringSimpledCoder.instance.convertTo(out, value.getHostAddress());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public InetAddress convertFrom(JsonReader in) {
|
|
||||||
String str = StringSimpledCoder.instance.convertFrom(in);
|
|
||||||
if (str == null) return null;
|
|
||||||
try {
|
|
||||||
return InetAddress.getByName(str);
|
|
||||||
} catch (Exception ex) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public final static class InetSocketAddressJsonSimpledCoder<R extends Reader, W extends Writer> extends SimpledCoder<JsonReader, JsonWriter, InetSocketAddress> {
|
|
||||||
|
|
||||||
public static final InetSocketAddressJsonSimpledCoder instance = new InetSocketAddressJsonSimpledCoder();
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void convertTo(JsonWriter out, InetSocketAddress value) {
|
|
||||||
if (value == null) {
|
|
||||||
out.writeNull();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
StringSimpledCoder.instance.convertTo(out, value.getHostString() + ":" + value.getPort());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public InetSocketAddress convertFrom(JsonReader in) {
|
|
||||||
String str = StringSimpledCoder.instance.convertFrom(in);
|
|
||||||
if (str == null) return null;
|
|
||||||
try {
|
|
||||||
int pos = str.indexOf(':');
|
|
||||||
return new InetSocketAddress(str.substring(0, pos), Integer.parseInt(str.substring(pos + 1)));
|
|
||||||
} catch (Exception ex) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,349 +0,0 @@
|
|||||||
/*
|
|
||||||
* To change this license header, choose License Headers in Project Properties.
|
|
||||||
* To change this template file, choose Tools | Templates
|
|
||||||
* and open the template in the editor.
|
|
||||||
*/
|
|
||||||
package org.redkale.convert.json;
|
|
||||||
|
|
||||||
import java.nio.*;
|
|
||||||
import java.nio.charset.*;
|
|
||||||
import java.util.*;
|
|
||||||
import java.util.function.*;
|
|
||||||
import org.redkale.util.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @see http://www.redkale.org
|
|
||||||
* @author zhangjx
|
|
||||||
*/
|
|
||||||
public final class JsonByteBufferWriter extends JsonWriter {
|
|
||||||
|
|
||||||
private static final Charset UTF8 = Charset.forName("UTF-8");
|
|
||||||
|
|
||||||
private final Charset charset;
|
|
||||||
|
|
||||||
private final Supplier<ByteBuffer> supplier;
|
|
||||||
|
|
||||||
private ByteBuffer[] buffers;
|
|
||||||
|
|
||||||
private int index;
|
|
||||||
|
|
||||||
protected JsonByteBufferWriter(Supplier<ByteBuffer> supplier) {
|
|
||||||
this(null, supplier);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected JsonByteBufferWriter(Charset charset, Supplier<ByteBuffer> supplier) {
|
|
||||||
this.charset = UTF8.equals(charset) ? null : charset;
|
|
||||||
this.supplier = supplier;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public JsonByteBufferWriter setTiny(boolean tiny) {
|
|
||||||
this.tiny = tiny;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected boolean recycle() {
|
|
||||||
this.index = 0;
|
|
||||||
this.buffers = null;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ByteBuffer[] toBuffers() {
|
|
||||||
if (buffers == null) return new ByteBuffer[0];
|
|
||||||
for (int i = index; i < this.buffers.length; i++) {
|
|
||||||
ByteBuffer buf = this.buffers[i];
|
|
||||||
if (buf.position() != 0) buf.flip();
|
|
||||||
}
|
|
||||||
return this.buffers;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int count() {
|
|
||||||
if (this.buffers == null) return 0;
|
|
||||||
int len = 0;
|
|
||||||
for (ByteBuffer buffer : buffers) {
|
|
||||||
len += buffer.remaining();
|
|
||||||
}
|
|
||||||
return len;
|
|
||||||
}
|
|
||||||
|
|
||||||
private int expand(final int byteLength) {
|
|
||||||
if (this.buffers == null) {
|
|
||||||
this.index = 0;
|
|
||||||
this.buffers = new ByteBuffer[]{supplier.get()};
|
|
||||||
}
|
|
||||||
ByteBuffer buffer = this.buffers[index];
|
|
||||||
if (!buffer.hasRemaining()) {
|
|
||||||
buffer.flip();
|
|
||||||
buffer = supplier.get();
|
|
||||||
ByteBuffer[] bufs = new ByteBuffer[this.buffers.length + 1];
|
|
||||||
System.arraycopy(this.buffers, 0, bufs, 0, this.buffers.length);
|
|
||||||
bufs[this.buffers.length] = buffer;
|
|
||||||
this.buffers = bufs;
|
|
||||||
this.index++;
|
|
||||||
}
|
|
||||||
int len = buffer.remaining();
|
|
||||||
int size = 0;
|
|
||||||
while (len < byteLength) {
|
|
||||||
buffer = supplier.get();
|
|
||||||
ByteBuffer[] bufs = new ByteBuffer[this.buffers.length + 1];
|
|
||||||
System.arraycopy(this.buffers, 0, bufs, 0, this.buffers.length);
|
|
||||||
bufs[this.buffers.length] = buffer;
|
|
||||||
this.buffers = bufs;
|
|
||||||
len += buffer.remaining();
|
|
||||||
size++;
|
|
||||||
}
|
|
||||||
return size;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void writeTo(final char ch) {
|
|
||||||
if (ch > Byte.MAX_VALUE) throw new RuntimeException("writeTo char(int.value = " + (int) ch + ") must be less 127");
|
|
||||||
expand(1);
|
|
||||||
this.buffers[index].put((byte) ch);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void writeTo(final char[] chs, final int start, final int len) {
|
|
||||||
writeTo(-1, false, chs, start, len);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void writeTo(int expandsize, final boolean quote, final char[] chs, final int start, final int len) {
|
|
||||||
int byteLength = quote ? 2 : 0;
|
|
||||||
ByteBuffer bb = null;
|
|
||||||
if (charset == null) {
|
|
||||||
byteLength += encodeUTF8Length(chs, start, len);
|
|
||||||
} else {
|
|
||||||
bb = charset.encode(CharBuffer.wrap(chs, start, len));
|
|
||||||
byteLength += bb.remaining();
|
|
||||||
}
|
|
||||||
if (expandsize < 0) expandsize = expand(byteLength);
|
|
||||||
if (expandsize == 0) { // 只需要一个buffer
|
|
||||||
final ByteBuffer buffer = this.buffers[index];
|
|
||||||
if (quote) buffer.put((byte) '"');
|
|
||||||
|
|
||||||
if (charset == null) { //UTF-8
|
|
||||||
final int limit = start + len;
|
|
||||||
for (int i = start; i < limit; i++) {
|
|
||||||
char c = chs[i];
|
|
||||||
if (c < 0x80) {
|
|
||||||
buffer.put((byte) c);
|
|
||||||
} else if (c < 0x800) {
|
|
||||||
buffer.put((byte) (0xc0 | (c >> 6)));
|
|
||||||
buffer.put((byte) (0x80 | (c & 0x3f)));
|
|
||||||
} else {
|
|
||||||
buffer.put((byte) (0xe0 | ((c >> 12))));
|
|
||||||
buffer.put((byte) (0x80 | ((c >> 6) & 0x3f)));
|
|
||||||
buffer.put((byte) (0x80 | (c & 0x3f)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
buffer.put(bb);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (quote) buffer.put((byte) '"');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
ByteBuffer buffer = this.buffers[index];
|
|
||||||
if (quote) {
|
|
||||||
if (!buffer.hasRemaining()) buffer = nextByteBuffer();
|
|
||||||
buffer.put((byte) '"');
|
|
||||||
}
|
|
||||||
if (charset == null) { //UTF-8
|
|
||||||
final int limit = start + len;
|
|
||||||
for (int i = start; i < limit; i++) {
|
|
||||||
buffer = putChar(buffer, chs[i]);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
while (bb.hasRemaining()) {
|
|
||||||
if (!buffer.hasRemaining()) buffer = nextByteBuffer();
|
|
||||||
buffer.put(bb.get());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (quote) {
|
|
||||||
if (!buffer.hasRemaining()) buffer = nextByteBuffer();
|
|
||||||
buffer.put((byte) '"');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private ByteBuffer putChar(ByteBuffer buffer, char c) {
|
|
||||||
if (c < 0x80) {
|
|
||||||
if (!buffer.hasRemaining()) buffer = nextByteBuffer();
|
|
||||||
buffer.put((byte) c);
|
|
||||||
} else if (c < 0x800) {
|
|
||||||
if (!buffer.hasRemaining()) buffer = nextByteBuffer();
|
|
||||||
buffer.put((byte) (0xc0 | (c >> 6)));
|
|
||||||
if (!buffer.hasRemaining()) buffer = nextByteBuffer();
|
|
||||||
buffer.put((byte) (0x80 | (c & 0x3f)));
|
|
||||||
} else {
|
|
||||||
if (!buffer.hasRemaining()) buffer = nextByteBuffer();
|
|
||||||
buffer.put((byte) (0xe0 | ((c >> 12))));
|
|
||||||
if (!buffer.hasRemaining()) buffer = nextByteBuffer();
|
|
||||||
buffer.put((byte) (0x80 | ((c >> 6) & 0x3f)));
|
|
||||||
if (!buffer.hasRemaining()) buffer = nextByteBuffer();
|
|
||||||
buffer.put((byte) (0x80 | (c & 0x3f)));
|
|
||||||
}
|
|
||||||
return buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
private ByteBuffer nextByteBuffer() {
|
|
||||||
this.buffers[this.index].flip();
|
|
||||||
return this.buffers[++this.index];
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int encodeUTF8Length(final char[] text, final int start, final int len) {
|
|
||||||
char c;
|
|
||||||
int size = 0;
|
|
||||||
final char[] chars = text;
|
|
||||||
final int limit = start + len;
|
|
||||||
for (int i = start; i < limit; i++) {
|
|
||||||
c = chars[i];
|
|
||||||
size += (c < 0x80 ? 1 : (c < 0x800 ? 2 : 3));
|
|
||||||
}
|
|
||||||
return size;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int encodeEscapeUTF8Length(final char[] text, final int start, final int len) {
|
|
||||||
char c;
|
|
||||||
int size = 0;
|
|
||||||
final char[] chars = text;
|
|
||||||
final int limit = start + len;
|
|
||||||
for (int i = start; i < limit; i++) {
|
|
||||||
c = chars[i];
|
|
||||||
switch (c) {
|
|
||||||
case '\n': size += 2;
|
|
||||||
break;
|
|
||||||
case '\r': size += 2;
|
|
||||||
break;
|
|
||||||
case '\t': size += 2;
|
|
||||||
break;
|
|
||||||
case '\\': size += 2;
|
|
||||||
break;
|
|
||||||
case '"': size += 2;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
size += (c < 0x80 ? 1 : (c < 0x800 ? 2 : 3));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return size;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <b>注意:</b> 该String值不能为null且不会进行转义, 只用于不含需要转义字符的字符串,例如enum、double、BigInteger转换的String
|
|
||||||
*
|
|
||||||
* @param quote
|
|
||||||
* @param value
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void writeTo(final boolean quote, final String value) {
|
|
||||||
char[] chs = Utility.charArray(value);
|
|
||||||
writeTo(-1, quote, chs, 0, chs.length);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void writeString(String value) {
|
|
||||||
if (value == null) {
|
|
||||||
writeNull();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
final char[] chs = Utility.charArray(value);
|
|
||||||
int len = 0;
|
|
||||||
for (char ch : chs) {
|
|
||||||
switch (ch) {
|
|
||||||
case '\n': len += 2;
|
|
||||||
break;
|
|
||||||
case '\r': len += 2;
|
|
||||||
break;
|
|
||||||
case '\t': len += 2;
|
|
||||||
break;
|
|
||||||
case '\\': len += 2;
|
|
||||||
break;
|
|
||||||
case '"': len += 2;
|
|
||||||
break;
|
|
||||||
default: len++;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (len == chs.length) {
|
|
||||||
writeTo(-1, true, chs, 0, len);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
int expandsize = -1;
|
|
||||||
if (this.charset == null) { //UTF-8
|
|
||||||
final int byteLength = 2 + encodeEscapeUTF8Length(chs, 0, chs.length);
|
|
||||||
expandsize = expand(byteLength);
|
|
||||||
if (expandsize == 0) { // 只需要一个buffer
|
|
||||||
final ByteBuffer buffer = this.buffers[index];
|
|
||||||
buffer.put((byte) '"');
|
|
||||||
for (char c : chs) {
|
|
||||||
switch (c) {
|
|
||||||
case '\n': buffer.put((byte) '\\').put((byte) 'n');
|
|
||||||
break;
|
|
||||||
case '\r': buffer.put((byte) '\\').put((byte) 'r');
|
|
||||||
break;
|
|
||||||
case '\t': buffer.put((byte) '\\').put((byte) 't');
|
|
||||||
break;
|
|
||||||
case '\\': buffer.put((byte) '\\').put((byte) '\\');
|
|
||||||
break;
|
|
||||||
case '"': buffer.put((byte) '\\').put((byte) '"');
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
if (c < 0x80) {
|
|
||||||
buffer.put((byte) c);
|
|
||||||
} else if (c < 0x800) {
|
|
||||||
buffer.put((byte) (0xc0 | (c >> 6)));
|
|
||||||
buffer.put((byte) (0x80 | (c & 0x3f)));
|
|
||||||
} else {
|
|
||||||
buffer.put((byte) (0xe0 | ((c >> 12))));
|
|
||||||
buffer.put((byte) (0x80 | ((c >> 6) & 0x3f)));
|
|
||||||
buffer.put((byte) (0x80 | (c & 0x3f)));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
buffer.put((byte) '"');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
StringBuilder sb = new StringBuilder(len);
|
|
||||||
for (char ch : chs) {
|
|
||||||
switch (ch) {
|
|
||||||
case '\n': sb.append("\\n");
|
|
||||||
break;
|
|
||||||
case '\r': sb.append("\\r");
|
|
||||||
break;
|
|
||||||
case '\t': sb.append("\\t");
|
|
||||||
break;
|
|
||||||
case '\\': sb.append("\\\\");
|
|
||||||
break;
|
|
||||||
case '"': sb.append("\\\"");
|
|
||||||
break;
|
|
||||||
default: sb.append(ch);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
char[] cs = Utility.charArray(sb);
|
|
||||||
writeTo(expandsize, true, cs, 0, sb.length());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void writeField(boolean comma, Attribute attribute) {
|
|
||||||
if (comma) writeTo(',');
|
|
||||||
writeTo(true, attribute.field());
|
|
||||||
writeTo(':');
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void writeSmallString(String value) {
|
|
||||||
writeTo(false, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return Objects.toString(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,138 +0,0 @@
|
|||||||
/*
|
|
||||||
* To change this license header, choose License Headers in Project Properties.
|
|
||||||
* To change this template file, choose Tools | Templates
|
|
||||||
* and open the template in the editor.
|
|
||||||
*/
|
|
||||||
package org.redkale.convert.json;
|
|
||||||
|
|
||||||
import java.lang.reflect.*;
|
|
||||||
import java.nio.*;
|
|
||||||
import java.nio.charset.*;
|
|
||||||
import java.util.function.*;
|
|
||||||
import org.redkale.convert.*;
|
|
||||||
import org.redkale.util.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @see http://www.redkale.org
|
|
||||||
* @author zhangjx
|
|
||||||
*/
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public final class JsonConvert extends Convert<JsonReader, JsonWriter> {
|
|
||||||
|
|
||||||
public static final Type TYPE_MAP_STRING_STRING = new TypeToken<java.util.LinkedHashMap<String, String>>() {
|
|
||||||
}.getType();
|
|
||||||
|
|
||||||
private static final ObjectPool<JsonReader> readerPool = JsonReader.createPool(Integer.getInteger("convert.json.pool.size", 16));
|
|
||||||
|
|
||||||
private static final ObjectPool<JsonWriter> writerPool = JsonWriter.createPool(Integer.getInteger("convert.json.pool.size", 16));
|
|
||||||
|
|
||||||
private final boolean tiny;
|
|
||||||
|
|
||||||
protected JsonConvert(JsonFactory factory, boolean tiny) {
|
|
||||||
super(factory);
|
|
||||||
this.tiny = tiny;
|
|
||||||
}
|
|
||||||
|
|
||||||
public JsonByteBufferWriter pollJsonWriter(final Supplier<ByteBuffer> supplier) {
|
|
||||||
return new JsonByteBufferWriter(supplier).setTiny(tiny);
|
|
||||||
}
|
|
||||||
|
|
||||||
public JsonByteBufferWriter pollJsonWriter(final Charset charset, final Supplier<ByteBuffer> supplier) {
|
|
||||||
return new JsonByteBufferWriter(charset, supplier).setTiny(tiny);
|
|
||||||
}
|
|
||||||
|
|
||||||
public JsonWriter pollJsonWriter() {
|
|
||||||
return writerPool.get().setTiny(tiny);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void offerJsonWriter(JsonWriter out) {
|
|
||||||
if (out != null) writerPool.offer(out);
|
|
||||||
}
|
|
||||||
|
|
||||||
@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.get();
|
|
||||||
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.get().setTiny(tiny);
|
|
||||||
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 void convertTo(final JsonWriter out, final Type type, Object value) {
|
|
||||||
if (type == null) return;
|
|
||||||
if (value == null) {
|
|
||||||
out.writeNull();
|
|
||||||
} else {
|
|
||||||
factory.loadEncoder(type).convertTo(out, value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void convertTo(final JsonWriter out, Object value) {
|
|
||||||
if (value == null) {
|
|
||||||
out.writeNull();
|
|
||||||
} else {
|
|
||||||
factory.loadEncoder(value.getClass()).convertTo(out, value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public ByteBuffer[] convertTo(final Supplier<ByteBuffer> supplier, final Type type, Object value) {
|
|
||||||
return convertTo(null, supplier, type, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ByteBuffer[] convertTo(final Charset charset, final Supplier<ByteBuffer> supplier, final Type type, Object value) {
|
|
||||||
if (supplier == null || type == null) return null;
|
|
||||||
JsonByteBufferWriter out = new JsonByteBufferWriter(charset, supplier);
|
|
||||||
if (value == null) {
|
|
||||||
out.writeNull();
|
|
||||||
} else {
|
|
||||||
factory.loadEncoder(type).convertTo(out, value);
|
|
||||||
}
|
|
||||||
return out.toBuffers();
|
|
||||||
}
|
|
||||||
|
|
||||||
public ByteBuffer[] convertTo(final Supplier<ByteBuffer> supplier, Object value) {
|
|
||||||
return convertTo(null, supplier, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ByteBuffer[] convertTo(final Charset charset, final Supplier<ByteBuffer> supplier, Object value) {
|
|
||||||
if (supplier == null) return null;
|
|
||||||
JsonByteBufferWriter out = new JsonByteBufferWriter(charset, supplier);
|
|
||||||
if (value == null) {
|
|
||||||
out.writeNull();
|
|
||||||
} else {
|
|
||||||
factory.loadEncoder(value.getClass()).convertTo(out, value);
|
|
||||||
}
|
|
||||||
return out.toBuffers();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,63 +0,0 @@
|
|||||||
/*
|
|
||||||
* To change this license header, choose License Headers in Project Properties.
|
|
||||||
* To change this template file, choose Tools | Templates
|
|
||||||
* and open the template in the editor.
|
|
||||||
*/
|
|
||||||
package org.redkale.convert.json;
|
|
||||||
|
|
||||||
import org.redkale.convert.ConvertType;
|
|
||||||
import org.redkale.convert.Factory;
|
|
||||||
import java.io.Serializable;
|
|
||||||
import java.net.*;
|
|
||||||
import org.redkale.util.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @see http://www.redkale.org
|
|
||||||
* @author zhangjx
|
|
||||||
*/
|
|
||||||
public final class JsonFactory extends Factory<JsonReader, JsonWriter> {
|
|
||||||
|
|
||||||
private static final JsonFactory instance = new JsonFactory(null, Boolean.getBoolean("convert.json.tiny"));
|
|
||||||
|
|
||||||
static {
|
|
||||||
instance.register(InetAddress.class, InetAddressJsonSimpledCoder.instance);
|
|
||||||
instance.register(InetSocketAddress.class, InetAddressJsonSimpledCoder.InetSocketAddressJsonSimpledCoder.instance);
|
|
||||||
instance.register(DLong.class, DLongJsonSimpledCoder.instance);
|
|
||||||
instance.register(Serializable.class, instance.loadEncoder(Object.class));
|
|
||||||
}
|
|
||||||
|
|
||||||
private JsonFactory(JsonFactory parent, boolean tiny) {
|
|
||||||
super(parent, tiny);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static JsonFactory root() {
|
|
||||||
return instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public final JsonConvert getConvert() {
|
|
||||||
if (convert == null) convert = new JsonConvert(this, tiny);
|
|
||||||
return (JsonConvert) convert;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public JsonFactory createChild() {
|
|
||||||
return new JsonFactory(this, this.tiny);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public JsonFactory createChild(boolean tiny) {
|
|
||||||
return new JsonFactory(this, tiny);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ConvertType getConvertType() {
|
|
||||||
return ConvertType.JSON;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isReversible() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,578 +0,0 @@
|
|||||||
/*
|
|
||||||
* To change this license header, choose License Headers in Project Properties.
|
|
||||||
* To change this template file, choose Tools | Templates
|
|
||||||
* and open the template in the editor.
|
|
||||||
*/
|
|
||||||
package org.redkale.convert.json;
|
|
||||||
|
|
||||||
import java.util.concurrent.atomic.*;
|
|
||||||
import java.util.function.*;
|
|
||||||
import org.redkale.convert.*;
|
|
||||||
import static org.redkale.convert.Reader.*;
|
|
||||||
import org.redkale.util.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @see http://www.redkale.org
|
|
||||||
* @author zhangjx
|
|
||||||
*/
|
|
||||||
public final class JsonReader implements Reader {
|
|
||||||
|
|
||||||
private int position = -1;
|
|
||||||
|
|
||||||
private char[] text;
|
|
||||||
|
|
||||||
private int limit;
|
|
||||||
|
|
||||||
public static ObjectPool<JsonReader> createPool(int max) {
|
|
||||||
return new ObjectPool<JsonReader>(max, new Creator<JsonReader>() { //为了兼容 JDK 6
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public JsonReader create(Object... params) {
|
|
||||||
return new JsonReader();
|
|
||||||
}
|
|
||||||
}, null, new Predicate<JsonReader>() {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean test(JsonReader t) {
|
|
||||||
return t.recycle();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected boolean recycle() {
|
|
||||||
this.position = -1;
|
|
||||||
this.limit = -1;
|
|
||||||
this.text = null;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void close() {
|
|
||||||
this.recycle();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 找到指定的属性值 例如: {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 + "' in (" + new String(this.text) + ")");
|
|
||||||
}
|
|
||||||
|
|
||||||
@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 + "' in (" + new String(this.text) + ")");
|
|
||||||
}
|
|
||||||
|
|
||||||
@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 + ") in (" + new String(this.text) + ")");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 判断对象是否存在下一个属性或者数组是否存在下一个元素
|
|
||||||
*
|
|
||||||
* @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 + ") in (" + new String(this.text) + ")");
|
|
||||||
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 + ") in (" + new String(this.text) + ")");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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 + ") in (" + new String(this.text) + ")");
|
|
||||||
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 + "' in (" + new String(this.text) + ")");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
final int start = currpos;
|
|
||||||
for (;;) {
|
|
||||||
char ch = text0[currpos];
|
|
||||||
if (ch == ',' || ch <= ' ' || ch == '}' || ch == ']' || ch == ':') break;
|
|
||||||
currpos++;
|
|
||||||
}
|
|
||||||
if (currpos == start) throw new ConvertException("expected a string after a key but '" + text0[position] + "' (position = " + position + ") in (" + new String(this.text) + ")");
|
|
||||||
this.position = currpos - 1;
|
|
||||||
return new String(text0, start, currpos - start);
|
|
||||||
}
|
|
||||||
this.position = currpos;
|
|
||||||
throw new ConvertException("expected a ':' after a key but '" + text0[position] + "' (position = " + position + ") in (" + new String(this.text) + ")");
|
|
||||||
}
|
|
||||||
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 + ") in (" + new String(this.text) + ")");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
array.append(c);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
/*
|
|
||||||
* To change this license header, choose License Headers in Project Properties.
|
|
||||||
* To change this template file, choose Tools | Templates
|
|
||||||
* and open the template in the editor.
|
|
||||||
*/
|
|
||||||
package org.redkale.convert.json;
|
|
||||||
|
|
||||||
import org.redkale.convert.SimpledCoder;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @see http://www.redkale.org
|
|
||||||
* @author zhangjx
|
|
||||||
* @param <T>
|
|
||||||
*/
|
|
||||||
public abstract class JsonSimpledCoder<T> extends SimpledCoder<JsonReader, JsonWriter, T> {
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,269 +0,0 @@
|
|||||||
/*
|
|
||||||
* To change this license header, choose License Headers in Project Properties.
|
|
||||||
* To change this template file, choose Tools | Templates
|
|
||||||
* and open the template in the editor.
|
|
||||||
*/
|
|
||||||
package org.redkale.convert.json;
|
|
||||||
|
|
||||||
import java.nio.*;
|
|
||||||
import java.util.function.*;
|
|
||||||
import org.redkale.convert.*;
|
|
||||||
import org.redkale.util.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* writeTo系列的方法输出的字符不能含特殊字符
|
|
||||||
*
|
|
||||||
* @see http://www.redkale.org
|
|
||||||
* @author zhangjx
|
|
||||||
*/
|
|
||||||
public class JsonWriter implements Writer {
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
private int count;
|
|
||||||
|
|
||||||
private char[] content;
|
|
||||||
|
|
||||||
protected boolean tiny;
|
|
||||||
|
|
||||||
public static ObjectPool<JsonWriter> createPool(int max) {
|
|
||||||
return new ObjectPool<JsonWriter>(max, new Creator<JsonWriter>() {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public JsonWriter create(Object... params) {
|
|
||||||
return new JsonWriter();
|
|
||||||
}
|
|
||||||
}, null, new Predicate<JsonWriter>() {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean test(JsonWriter t) {
|
|
||||||
return t.recycle();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public JsonWriter() {
|
|
||||||
this(defaultSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
public JsonWriter(int size) {
|
|
||||||
this.content = new char[size > 128 ? size : 128];
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isTiny() {
|
|
||||||
return tiny;
|
|
||||||
}
|
|
||||||
|
|
||||||
public JsonWriter setTiny(boolean tiny) {
|
|
||||||
this.tiny = tiny;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
//-----------------------------------------------------------------------
|
|
||||||
//-----------------------------------------------------------------------
|
|
||||||
/**
|
|
||||||
* 返回指定至少指定长度的缓冲区
|
|
||||||
*
|
|
||||||
* @param len
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
private 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) { //只能是 0 - 127 的字符
|
|
||||||
expand(1);
|
|
||||||
content[count++] = ch;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void writeTo(final char[] chs, final int start, final int len) { //只能是 0 - 127 的字符
|
|
||||||
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++] = '"';
|
|
||||||
}
|
|
||||||
|
|
||||||
protected boolean recycle() {
|
|
||||||
this.count = 0;
|
|
||||||
if (this.content.length > defaultSize) {
|
|
||||||
this.content = new char[defaultSize];
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ByteBuffer[] toBuffers() {
|
|
||||||
return new ByteBuffer[]{ByteBuffer.wrap(Utility.encodeUTF8(content, 0, count))};
|
|
||||||
}
|
|
||||||
|
|
||||||
public int count() {
|
|
||||||
return this.count;
|
|
||||||
}
|
|
||||||
|
|
||||||
@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 writeField(boolean comma, Attribute attribute) {
|
|
||||||
if (comma) writeTo(',');
|
|
||||||
writeTo(true, attribute.field());
|
|
||||||
writeTo(':');
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void writeSmallString(String value) {
|
|
||||||
writeTo(false, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return new String(content, 0, count);
|
|
||||||
}
|
|
||||||
|
|
||||||
//----------------------------------------------------------------------------------------------
|
|
||||||
public final void writeTo(final char... chs) { //只能是 0 - 127 的字符
|
|
||||||
writeTo(chs, 0, chs.length);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public final void writeBoolean(boolean value) {
|
|
||||||
writeTo(value ? CHARS_TUREVALUE : CHARS_FALSEVALUE);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public final void writeByte(byte value) {
|
|
||||||
writeInt(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public final void writeChar(char value) {
|
|
||||||
writeInt(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public final void writeShort(short value) {
|
|
||||||
writeInt(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public final void writeInt(int value) {
|
|
||||||
writeSmallString(String.valueOf(value));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public final void writeLong(long value) {
|
|
||||||
writeSmallString(String.valueOf(value));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public final void writeFloat(float value) {
|
|
||||||
writeSmallString(String.valueOf(value));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public final void writeDouble(double value) {
|
|
||||||
writeSmallString(String.valueOf(value));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public final void wirteClassName(String clazz) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public final void writeObjectB(int fieldCount, Object obj) {
|
|
||||||
writeTo('{');
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public final void writeObjectE(Object obj) {
|
|
||||||
writeTo('}');
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public final void writeNull() {
|
|
||||||
writeTo('n', 'u', 'l', 'l');
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public final void writeArrayB(int size) {
|
|
||||||
writeTo('[');
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public final void writeArrayMark() {
|
|
||||||
writeTo(',');
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public final void writeArrayE() {
|
|
||||||
writeTo(']');
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public final void writeMapB(int size) {
|
|
||||||
writeTo('{');
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public final void writeMapMark() {
|
|
||||||
writeTo(':');
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public final void writeMapE() {
|
|
||||||
writeTo('}');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,571 +0,0 @@
|
|||||||
/*
|
|
||||||
* To change this license header, choose License Headers in Project Properties.
|
|
||||||
* To change this template file, choose Tools | Templates
|
|
||||||
* and open the template in the editor.
|
|
||||||
*/
|
|
||||||
package org.redkale.net;
|
|
||||||
|
|
||||||
import java.io.*;
|
|
||||||
import java.net.*;
|
|
||||||
import java.nio.*;
|
|
||||||
import java.nio.channels.*;
|
|
||||||
import java.util.*;
|
|
||||||
import java.util.concurrent.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @see http://www.redkale.org
|
|
||||||
* @author zhangjx
|
|
||||||
*/
|
|
||||||
public abstract class AsyncConnection implements AsynchronousByteChannel, AutoCloseable {
|
|
||||||
|
|
||||||
protected Map<String, Object> attributes;
|
|
||||||
|
|
||||||
public abstract boolean isTCP();
|
|
||||||
|
|
||||||
public abstract SocketAddress getRemoteAddress();
|
|
||||||
|
|
||||||
public abstract SocketAddress getLocalAddress();
|
|
||||||
|
|
||||||
public abstract int getReadTimeoutSecond();
|
|
||||||
|
|
||||||
public abstract int getWriteTimeoutSecond();
|
|
||||||
|
|
||||||
public abstract void setReadTimeoutSecond(int readTimeoutSecond);
|
|
||||||
|
|
||||||
public abstract void setWriteTimeoutSecond(int writeTimeoutSecond);
|
|
||||||
|
|
||||||
public final <A> void write(ByteBuffer[] srcs, A attachment, CompletionHandler<Integer, ? super A> handler) {
|
|
||||||
write(srcs, 0, srcs.length, attachment, handler);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected abstract <A> void write(ByteBuffer[] srcs, int offset, int length, A attachment, CompletionHandler<Integer, ? super A> handler);
|
|
||||||
|
|
||||||
public void dispose() {//同close, 只是去掉throws IOException
|
|
||||||
try {
|
|
||||||
this.close();
|
|
||||||
} catch (IOException io) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void close() throws IOException {
|
|
||||||
if (attributes == null) return;
|
|
||||||
try {
|
|
||||||
for (Object obj : attributes.values()) {
|
|
||||||
if (obj instanceof AutoCloseable) ((AutoCloseable) obj).close();
|
|
||||||
}
|
|
||||||
} catch (Exception io) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setAttribute(String name, Object value) {
|
|
||||||
if (attributes == null) attributes = new HashMap<>();
|
|
||||||
attributes.put(name, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public final <T> T getAttribute(String name) {
|
|
||||||
return (T) (attributes == null ? null : attributes.get(name));
|
|
||||||
}
|
|
||||||
|
|
||||||
public final void removeAttribute(String name) {
|
|
||||||
if (attributes != null) attributes.remove(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
public final Map<String, Object> getAttributes() {
|
|
||||||
return attributes;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final void clearAttribute() {
|
|
||||||
if (attributes != null) attributes.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------------------------------------------------------
|
|
||||||
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, address, readTimeoutSecond0, writeTimeoutSecond0);
|
|
||||||
} else if ("UDP".equalsIgnoreCase(protocol)) {
|
|
||||||
DatagramChannel channel = DatagramChannel.open();
|
|
||||||
channel.configureBlocking(true);
|
|
||||||
channel.connect(address);
|
|
||||||
return create(channel, address, true, readTimeoutSecond0, writeTimeoutSecond0);
|
|
||||||
} else {
|
|
||||||
throw new RuntimeException("AsyncConnection not support protocol " + protocol);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class BIOUDPAsyncConnection extends AsyncConnection {
|
|
||||||
|
|
||||||
private int readTimeoutSecond;
|
|
||||||
|
|
||||||
private int writeTimeoutSecond;
|
|
||||||
|
|
||||||
private final DatagramChannel channel;
|
|
||||||
|
|
||||||
private final SocketAddress remoteAddress;
|
|
||||||
|
|
||||||
private final boolean client;
|
|
||||||
|
|
||||||
public BIOUDPAsyncConnection(final DatagramChannel ch, SocketAddress addr,
|
|
||||||
final boolean client0, final int readTimeoutSecond0, final int writeTimeoutSecond0) {
|
|
||||||
this.channel = ch;
|
|
||||||
this.client = client0;
|
|
||||||
this.readTimeoutSecond = readTimeoutSecond0;
|
|
||||||
this.writeTimeoutSecond = writeTimeoutSecond0;
|
|
||||||
this.remoteAddress = addr;
|
|
||||||
}
|
|
||||||
|
|
||||||
@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 SocketAddress getLocalAddress() {
|
|
||||||
try {
|
|
||||||
return channel.getLocalAddress();
|
|
||||||
} catch (IOException e) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected <A> void write(ByteBuffer[] srcs, int offset, int length, A attachment, CompletionHandler<Integer, ? super A> handler) {
|
|
||||||
try {
|
|
||||||
int rs = 0;
|
|
||||||
for (int i = offset; i < offset + length; i++) {
|
|
||||||
rs += channel.send(srcs[i], remoteAddress);
|
|
||||||
if(i != offset) Thread.sleep(10);
|
|
||||||
}
|
|
||||||
if (handler != null) handler.completed(rs, attachment);
|
|
||||||
} catch (Exception e) {
|
|
||||||
if (handler != null) handler.failed(e, attachment);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public <A> void read(ByteBuffer dst, A attachment, CompletionHandler<Integer, ? super A> handler) {
|
|
||||||
try {
|
|
||||||
int rs = channel.read(dst);
|
|
||||||
if (handler != null) handler.completed(rs, attachment);
|
|
||||||
} catch (IOException e) {
|
|
||||||
if (handler != null) handler.failed(e, attachment);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Future<Integer> read(ByteBuffer dst) {
|
|
||||||
try {
|
|
||||||
int rs = channel.read(dst);
|
|
||||||
return new SimpleFuture(rs);
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public <A> void write(ByteBuffer src, A attachment, CompletionHandler<Integer, ? super A> handler) {
|
|
||||||
try {
|
|
||||||
int rs = channel.send(src, remoteAddress);
|
|
||||||
if (handler != null) handler.completed(rs, attachment);
|
|
||||||
} catch (IOException e) {
|
|
||||||
if (handler != null) handler.failed(e, attachment);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Future<Integer> write(ByteBuffer src) {
|
|
||||||
try {
|
|
||||||
int rs = channel.send(src, remoteAddress);
|
|
||||||
return new SimpleFuture(rs);
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public final void close() throws IOException {
|
|
||||||
super.close();
|
|
||||||
if (client) {
|
|
||||||
channel.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public final boolean isOpen() {
|
|
||||||
return channel.isOpen();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public final boolean isTCP() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static AsyncConnection create(final DatagramChannel ch, SocketAddress addr,
|
|
||||||
final boolean client0, final int readTimeoutSecond0, final int writeTimeoutSecond0) {
|
|
||||||
return new BIOUDPAsyncConnection(ch, addr, client0, readTimeoutSecond0, writeTimeoutSecond0);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class SimpleFuture implements Future<Integer> {
|
|
||||||
|
|
||||||
private final int rs;
|
|
||||||
|
|
||||||
public SimpleFuture(int rs) {
|
|
||||||
this.rs = rs;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean cancel(boolean mayInterruptIfRunning) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isCancelled() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isDone() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Integer get() throws InterruptedException, ExecutionException {
|
|
||||||
return rs;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Integer get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
|
|
||||||
return rs;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class BIOTCPAsyncConnection extends AsyncConnection {
|
|
||||||
|
|
||||||
private int readTimeoutSecond;
|
|
||||||
|
|
||||||
private int writeTimeoutSecond;
|
|
||||||
|
|
||||||
private final Socket socket;
|
|
||||||
|
|
||||||
private final ReadableByteChannel readChannel;
|
|
||||||
|
|
||||||
private final WritableByteChannel writeChannel;
|
|
||||||
|
|
||||||
private final SocketAddress remoteAddress;
|
|
||||||
|
|
||||||
public BIOTCPAsyncConnection(final Socket socket, final SocketAddress addr0, final int readTimeoutSecond0, final int writeTimeoutSecond0) {
|
|
||||||
this.socket = socket;
|
|
||||||
ReadableByteChannel rc = null;
|
|
||||||
WritableByteChannel wc = null;
|
|
||||||
try {
|
|
||||||
socket.setSoTimeout(Math.max(readTimeoutSecond0, writeTimeoutSecond0));
|
|
||||||
rc = Channels.newChannel(socket.getInputStream());
|
|
||||||
wc = Channels.newChannel(socket.getOutputStream());
|
|
||||||
} catch (IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
this.readChannel = rc;
|
|
||||||
this.writeChannel = wc;
|
|
||||||
this.readTimeoutSecond = readTimeoutSecond0;
|
|
||||||
this.writeTimeoutSecond = writeTimeoutSecond0;
|
|
||||||
SocketAddress addr = addr0;
|
|
||||||
if (addr == null) {
|
|
||||||
try {
|
|
||||||
addr = socket.getRemoteSocketAddress();
|
|
||||||
} catch (Exception e) {
|
|
||||||
//do nothing
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.remoteAddress = addr;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isTCP() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public SocketAddress getRemoteAddress() {
|
|
||||||
return remoteAddress;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public SocketAddress getLocalAddress() {
|
|
||||||
return socket.getLocalSocketAddress();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getReadTimeoutSecond() {
|
|
||||||
return readTimeoutSecond;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getWriteTimeoutSecond() {
|
|
||||||
return writeTimeoutSecond;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setReadTimeoutSecond(int readTimeoutSecond) {
|
|
||||||
this.readTimeoutSecond = readTimeoutSecond;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setWriteTimeoutSecond(int writeTimeoutSecond) {
|
|
||||||
this.writeTimeoutSecond = writeTimeoutSecond;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected <A> void write(ByteBuffer[] srcs, int offset, int length, A attachment, CompletionHandler<Integer, ? super A> handler) {
|
|
||||||
try {
|
|
||||||
int rs = 0;
|
|
||||||
for (int i = offset; i < offset + length; i++) {
|
|
||||||
rs += writeChannel.write(srcs[i]);
|
|
||||||
}
|
|
||||||
if (handler != null) handler.completed(rs, attachment);
|
|
||||||
} catch (IOException e) {
|
|
||||||
if (handler != null) handler.failed(e, attachment);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public <A> void read(ByteBuffer dst, A attachment, CompletionHandler<Integer, ? super A> handler) {
|
|
||||||
try {
|
|
||||||
int rs = readChannel.read(dst);
|
|
||||||
if (handler != null) handler.completed(rs, attachment);
|
|
||||||
} catch (IOException e) {
|
|
||||||
if (handler != null) handler.failed(e, attachment);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Future<Integer> read(ByteBuffer dst) {
|
|
||||||
try {
|
|
||||||
int rs = readChannel.read(dst);
|
|
||||||
return new SimpleFuture(rs);
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public <A> void write(ByteBuffer src, A attachment, CompletionHandler<Integer, ? super A> handler) {
|
|
||||||
try {
|
|
||||||
int rs = writeChannel.write(src);
|
|
||||||
if (handler != null) handler.completed(rs, attachment);
|
|
||||||
} catch (IOException e) {
|
|
||||||
if (handler != null) handler.failed(e, attachment);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Future<Integer> write(ByteBuffer src) {
|
|
||||||
try {
|
|
||||||
int rs = writeChannel.write(src);
|
|
||||||
return new SimpleFuture(rs);
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void close() throws IOException {
|
|
||||||
super.close();
|
|
||||||
this.socket.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isOpen() {
|
|
||||||
return !socket.isClosed();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 通常用于 ssl socket
|
|
||||||
* @param socket
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
public static AsyncConnection create(final Socket socket) {
|
|
||||||
return create(socket, null, 0, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static AsyncConnection create(final Socket socket, final SocketAddress addr0, final int readTimeoutSecond0, final int writeTimeoutSecond0) {
|
|
||||||
return new BIOTCPAsyncConnection(socket, addr0, readTimeoutSecond0, writeTimeoutSecond0);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class AIOTCPAsyncConnection extends AsyncConnection {
|
|
||||||
|
|
||||||
private int readTimeoutSecond;
|
|
||||||
|
|
||||||
private int writeTimeoutSecond;
|
|
||||||
|
|
||||||
private final AsynchronousSocketChannel channel;
|
|
||||||
|
|
||||||
private final SocketAddress remoteAddress;
|
|
||||||
|
|
||||||
public AIOTCPAsyncConnection(final AsynchronousSocketChannel ch, final SocketAddress addr0, final int readTimeoutSecond0, final int writeTimeoutSecond0) {
|
|
||||||
this.channel = ch;
|
|
||||||
this.readTimeoutSecond = readTimeoutSecond0;
|
|
||||||
this.writeTimeoutSecond = writeTimeoutSecond0;
|
|
||||||
SocketAddress addr = addr0;
|
|
||||||
if (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 <A> void write(ByteBuffer[] srcs, int offset, int length, A attachment, CompletionHandler<Integer, ? super A> handler) {
|
|
||||||
channel.write(srcs, offset, length, writeTimeoutSecond > 0 ? writeTimeoutSecond : 60, TimeUnit.SECONDS,
|
|
||||||
attachment, new CompletionHandler<Long, A>() {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void completed(Long result, A attachment) {
|
|
||||||
handler.completed(result.intValue(), attachment);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void failed(Throwable exc, A attachment) {
|
|
||||||
handler.failed(exc, attachment);
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@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 SocketAddress getLocalAddress() {
|
|
||||||
try {
|
|
||||||
return channel.getLocalAddress();
|
|
||||||
} catch (IOException e) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@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 {
|
|
||||||
super.close();
|
|
||||||
channel.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public final boolean isOpen() {
|
|
||||||
return channel.isOpen();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public final boolean isTCP() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public static AsyncConnection create(final AsynchronousSocketChannel ch) {
|
|
||||||
return create(ch, null, 0, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static AsyncConnection create(final AsynchronousSocketChannel ch, final SocketAddress addr0, final int readTimeoutSecond0, final int writeTimeoutSecond0) {
|
|
||||||
return new AIOTCPAsyncConnection(ch, addr0, readTimeoutSecond0, writeTimeoutSecond0);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,133 +0,0 @@
|
|||||||
/*
|
|
||||||
* To change this license header, choose License Headers in Project Properties.
|
|
||||||
* To change this template file, choose Tools | Templates
|
|
||||||
* and open the template in the editor.
|
|
||||||
*/
|
|
||||||
package org.redkale.net;
|
|
||||||
|
|
||||||
import java.net.*;
|
|
||||||
import java.nio.*;
|
|
||||||
import java.nio.charset.*;
|
|
||||||
import java.util.concurrent.*;
|
|
||||||
import java.util.function.*;
|
|
||||||
import java.util.logging.*;
|
|
||||||
import org.redkale.convert.bson.*;
|
|
||||||
import org.redkale.convert.json.*;
|
|
||||||
import org.redkale.util.*;
|
|
||||||
import org.redkale.watch.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @see http://www.redkale.org
|
|
||||||
* @author zhangjx
|
|
||||||
*/
|
|
||||||
public class Context {
|
|
||||||
|
|
||||||
private static final Charset UTF8 = Charset.forName("UTF-8");
|
|
||||||
|
|
||||||
protected final long serverStartTime;
|
|
||||||
|
|
||||||
protected final ExecutorService executor;
|
|
||||||
|
|
||||||
protected final int bufferCapacity;
|
|
||||||
|
|
||||||
protected final ObjectPool<ByteBuffer> bufferPool;
|
|
||||||
|
|
||||||
protected final ObjectPool<Response> 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 BsonFactory bsonFactory;
|
|
||||||
|
|
||||||
protected final JsonFactory jsonFactory;
|
|
||||||
|
|
||||||
protected final WatchFactory watch;
|
|
||||||
|
|
||||||
public Context(long serverStartTime, Logger logger, ExecutorService executor, int bufferCapacity, ObjectPool<ByteBuffer> bufferPool, ObjectPool<Response> 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.bufferCapacity = bufferCapacity;
|
|
||||||
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;
|
|
||||||
this.jsonFactory = JsonFactory.root();
|
|
||||||
this.bsonFactory = BsonFactory.root();
|
|
||||||
}
|
|
||||||
|
|
||||||
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 int getBufferCapacity() {
|
|
||||||
return bufferCapacity;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Supplier<ByteBuffer> getBufferSupplier() {
|
|
||||||
return bufferPool;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ByteBuffer pollBuffer() {
|
|
||||||
return bufferPool.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void offerBuffer(ByteBuffer buffer) {
|
|
||||||
bufferPool.offer(buffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Logger getLogger() {
|
|
||||||
return logger;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getReadTimeoutSecond() {
|
|
||||||
return readTimeoutSecond;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getWriteTimeoutSecond() {
|
|
||||||
return writeTimeoutSecond;
|
|
||||||
}
|
|
||||||
|
|
||||||
public JsonConvert getJsonConvert() {
|
|
||||||
return jsonFactory.getConvert();
|
|
||||||
}
|
|
||||||
|
|
||||||
public BsonConvert getBsonConvert() {
|
|
||||||
return bsonFactory.getConvert();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,99 +0,0 @@
|
|||||||
/*
|
|
||||||
* To change this license header, choose License Headers in Project Properties.
|
|
||||||
* To change this template file, choose Tools | Templates
|
|
||||||
* and open the template in the editor.
|
|
||||||
*/
|
|
||||||
package org.redkale.net;
|
|
||||||
|
|
||||||
import java.nio.*;
|
|
||||||
import java.nio.channels.*;
|
|
||||||
import java.util.logging.*;
|
|
||||||
import org.redkale.util.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @see http://www.redkale.org
|
|
||||||
* @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 ObjectPool<? extends Response> responsePool = context.responsePool;
|
|
||||||
if (data != null) {
|
|
||||||
final Response response = responsePool.get();
|
|
||||||
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);
|
|
||||||
response.finish(true);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
final ByteBuffer buffer = context.pollBuffer();
|
|
||||||
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[] bs = new byte[buffer.remaining()];
|
|
||||||
// buffer.get(bs);
|
|
||||||
// System.println(new String(bs));
|
|
||||||
// }
|
|
||||||
buffer.flip();
|
|
||||||
final Response response = responsePool.get();
|
|
||||||
response.init(channel);
|
|
||||||
try {
|
|
||||||
prepare.prepare(buffer, response.request, response);
|
|
||||||
} catch (Throwable t) { //此处不可 context.offerBuffer(buffer); 以免prepare.prepare内部异常导致重复 offerBuffer
|
|
||||||
context.logger.log(Level.WARNING, "prepare servlet abort, forece to close channel ", t);
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,74 +0,0 @@
|
|||||||
/*
|
|
||||||
* To change this license header, choose License Headers in Project Properties.
|
|
||||||
* To change this template file, choose Tools | Templates
|
|
||||||
* and open the template in the editor.
|
|
||||||
*/
|
|
||||||
package org.redkale.net;
|
|
||||||
|
|
||||||
import java.io.*;
|
|
||||||
import java.nio.*;
|
|
||||||
import java.nio.channels.*;
|
|
||||||
import java.util.concurrent.atomic.*;
|
|
||||||
import java.util.logging.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @see http://www.redkale.org
|
|
||||||
* @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);
|
|
||||||
if (rs != Integer.MIN_VALUE) illRequestCounter.incrementAndGet();
|
|
||||||
response.finish(true);
|
|
||||||
} else if (rs == 0) {
|
|
||||||
response.context.offerBuffer(buffer);
|
|
||||||
request.prepare();
|
|
||||||
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(-request.readBody(buffer));
|
|
||||||
if (ai.get() > 0) {
|
|
||||||
buffer.clear();
|
|
||||||
request.channel.read(buffer, buffer, this);
|
|
||||||
} else {
|
|
||||||
response.context.offerBuffer(buffer);
|
|
||||||
request.prepare();
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,184 +0,0 @@
|
|||||||
/*
|
|
||||||
* To change this license header, choose License Headers in Project Properties.
|
|
||||||
* To change this template file, choose Tools | Templates
|
|
||||||
* and open the template in the editor.
|
|
||||||
*/
|
|
||||||
package org.redkale.net;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.net.*;
|
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
import java.nio.channels.*;
|
|
||||||
import java.util.*;
|
|
||||||
import java.util.concurrent.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @see http://www.redkale.org
|
|
||||||
* @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> Set<SocketOption<?>> supportedOptions();
|
|
||||||
|
|
||||||
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 boolean running;
|
|
||||||
|
|
||||||
private final Context context;
|
|
||||||
|
|
||||||
private DatagramChannel serverChannel;
|
|
||||||
|
|
||||||
public ProtocolUDPServer(Context context) {
|
|
||||||
this.context = context;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void open() throws IOException {
|
|
||||||
DatagramChannel ch = DatagramChannel.open();
|
|
||||||
ch.configureBlocking(true);
|
|
||||||
this.serverChannel = ch;
|
|
||||||
}
|
|
||||||
|
|
||||||
@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 <T> Set<SocketOption<?>> supportedOptions() {
|
|
||||||
return this.serverChannel.supportedOptions();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void accept() {
|
|
||||||
final DatagramChannel serchannel = this.serverChannel;
|
|
||||||
final int readTimeoutSecond = this.context.readTimeoutSecond;
|
|
||||||
final int writeTimeoutSecond = this.context.writeTimeoutSecond;
|
|
||||||
final CountDownLatch cdl = new CountDownLatch(1);
|
|
||||||
this.running = true;
|
|
||||||
new Thread() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
cdl.countDown();
|
|
||||||
while (running) {
|
|
||||||
final ByteBuffer buffer = context.pollBuffer();
|
|
||||||
try {
|
|
||||||
SocketAddress address = serchannel.receive(buffer);
|
|
||||||
buffer.flip();
|
|
||||||
AsyncConnection conn = AsyncConnection.create(serchannel, address, false, readTimeoutSecond, writeTimeoutSecond);
|
|
||||||
context.submit(new PrepareRunner(context, conn, buffer));
|
|
||||||
} catch (Exception e) {
|
|
||||||
context.offerBuffer(buffer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}.start();
|
|
||||||
try {
|
|
||||||
cdl.await();
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void close() throws IOException {
|
|
||||||
this.running = false;
|
|
||||||
this.serverChannel.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public AsynchronousChannelGroup getChannelGroup() {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
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 <T> Set<SocketOption<?>> supportedOptions() {
|
|
||||||
return this.serverChannel.supportedOptions();
|
|
||||||
}
|
|
||||||
|
|
||||||
@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, null, 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,113 +0,0 @@
|
|||||||
/*
|
|
||||||
* To change this license header, choose License Headers in Project Properties.
|
|
||||||
* To change this template file, choose Tools | Templates
|
|
||||||
* and open the template in the editor.
|
|
||||||
*/
|
|
||||||
package org.redkale.net;
|
|
||||||
|
|
||||||
import java.nio.*;
|
|
||||||
import java.util.*;
|
|
||||||
import org.redkale.convert.bson.*;
|
|
||||||
import org.redkale.convert.json.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @see http://www.redkale.org
|
|
||||||
* @author zhangjx
|
|
||||||
*/
|
|
||||||
public abstract class Request {
|
|
||||||
|
|
||||||
protected final Context context;
|
|
||||||
|
|
||||||
protected final BsonConvert bsonConvert;
|
|
||||||
|
|
||||||
protected final JsonConvert jsonConvert;
|
|
||||||
|
|
||||||
protected long createtime;
|
|
||||||
|
|
||||||
protected boolean keepAlive;
|
|
||||||
|
|
||||||
protected AsyncConnection channel;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* properties 与 attributes 的区别在于:调用recycle时, attributes会被清空而properties会保留;
|
|
||||||
* properties 通常存放需要永久绑定在request里的一些对象
|
|
||||||
*/
|
|
||||||
private final Map<String, Object> properties = new HashMap<>();
|
|
||||||
|
|
||||||
protected final Map<String, Object> attributes = new HashMap<>();
|
|
||||||
|
|
||||||
protected Request(Context context) {
|
|
||||||
this.context = context;
|
|
||||||
this.bsonConvert = context.getBsonConvert();
|
|
||||||
this.jsonConvert = context.getJsonConvert();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 返回值:Integer.MIN_VALUE: 帧数据; -1:数据不合法; 0:解析完毕; >0: 需再读取的字节数。
|
|
||||||
*
|
|
||||||
* @param buffer
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
protected abstract int readHeader(ByteBuffer buffer);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 读取buffer,并返回读取的有效数据长度
|
|
||||||
*
|
|
||||||
* @param buffer
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
protected abstract int readBody(ByteBuffer buffer);
|
|
||||||
|
|
||||||
protected abstract void prepare();
|
|
||||||
|
|
||||||
protected void recycle() {
|
|
||||||
createtime = 0;
|
|
||||||
keepAlive = false;
|
|
||||||
attributes.clear();
|
|
||||||
channel = null; // close it by response
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void setProperty(String name, Object value) {
|
|
||||||
properties.put(name, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
protected <T> T getProperty(String name) {
|
|
||||||
return (T) properties.get(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void removeProperty(String name) {
|
|
||||||
properties.remove(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected Map<String, Object> getProperties() {
|
|
||||||
return properties;
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
public long getCreatetime() {
|
|
||||||
return createtime;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,229 +0,0 @@
|
|||||||
/*
|
|
||||||
* To change this license header, choose License Headers in Project Properties.
|
|
||||||
* To change this template file, choose Tools | Templates
|
|
||||||
* and open the template in the editor.
|
|
||||||
*/
|
|
||||||
package org.redkale.net;
|
|
||||||
|
|
||||||
import java.nio.*;
|
|
||||||
import java.nio.channels.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @see http://www.redkale.org
|
|
||||||
* @author zhangjx
|
|
||||||
* @param <R>
|
|
||||||
*/
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public abstract class Response<R extends Request> {
|
|
||||||
|
|
||||||
protected final Context context;
|
|
||||||
|
|
||||||
protected final R request;
|
|
||||||
|
|
||||||
protected AsyncConnection channel;
|
|
||||||
|
|
||||||
private boolean inited = true;
|
|
||||||
|
|
||||||
protected Runnable recycleListener;
|
|
||||||
|
|
||||||
private final CompletionHandler finishHandler = new CompletionHandler<Integer, ByteBuffer>() {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void completed(Integer result, ByteBuffer attachment) {
|
|
||||||
if (attachment.hasRemaining()) {
|
|
||||||
channel.write(attachment, attachment, this);
|
|
||||||
} else {
|
|
||||||
context.offerBuffer(attachment);
|
|
||||||
finish();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void failed(Throwable exc, ByteBuffer attachment) {
|
|
||||||
context.offerBuffer(attachment);
|
|
||||||
finish(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
private final CompletionHandler finishHandler2 = new CompletionHandler<Integer, ByteBuffer[]>() {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void completed(Integer result, ByteBuffer[] attachments) {
|
|
||||||
int index = -1;
|
|
||||||
for (int i = 0; i < attachments.length; i++) {
|
|
||||||
if (attachments[i].hasRemaining()) {
|
|
||||||
index = i;
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
context.offerBuffer(attachments[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (index == 0) {
|
|
||||||
channel.write(attachments, attachments, this);
|
|
||||||
} else if (index > 0) {
|
|
||||||
ByteBuffer[] newattachs = new ByteBuffer[attachments.length - index];
|
|
||||||
System.arraycopy(attachments, index, newattachs, 0, newattachs.length);
|
|
||||||
channel.write(newattachs, newattachs, this);
|
|
||||||
} else {
|
|
||||||
finish();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void failed(Throwable exc, ByteBuffer[] attachments) {
|
|
||||||
for (ByteBuffer attachment : attachments) {
|
|
||||||
context.offerBuffer(attachment);
|
|
||||||
}
|
|
||||||
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 prepare() {
|
|
||||||
inited = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected boolean recycle() {
|
|
||||||
if (!inited) return false;
|
|
||||||
boolean keepAlive = request.keepAlive;
|
|
||||||
if (recycleListener != null) {
|
|
||||||
try {
|
|
||||||
recycleListener.run();
|
|
||||||
} catch (Exception e) {
|
|
||||||
System.err.println(request);
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
recycleListener = null;
|
|
||||||
}
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
this.inited = false;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
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 setRecycleListener(Runnable recycleListener) {
|
|
||||||
this.recycleListener = recycleListener;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void finish() {
|
|
||||||
this.finish(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void finish(boolean kill) {
|
|
||||||
//System.println("耗时: " + (System.currentTimeMillis() - request.createtime));
|
|
||||||
if (kill) refuseAlive();
|
|
||||||
this.context.responsePool.offer(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void finish(ByteBuffer buffer) {
|
|
||||||
this.channel.write(buffer, buffer, finishHandler);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void finish(boolean kill, ByteBuffer buffer) {
|
|
||||||
if (kill) refuseAlive();
|
|
||||||
this.channel.write(buffer, buffer, finishHandler);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void finish(ByteBuffer... buffers) {
|
|
||||||
this.channel.write(buffers, buffers, finishHandler2);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void finish(boolean kill, ByteBuffer... buffers) {
|
|
||||||
if (kill) refuseAlive();
|
|
||||||
this.channel.write(buffers, buffers, finishHandler2);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected <A> void send(final ByteBuffer buffer, final A attachment, final CompletionHandler<Integer, A> handler) {
|
|
||||||
this.channel.write(buffer, attachment, new CompletionHandler<Integer, A>() {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void completed(Integer result, A attachment) {
|
|
||||||
if (buffer.hasRemaining()) {
|
|
||||||
channel.write(buffer, attachment, this);
|
|
||||||
} else {
|
|
||||||
context.offerBuffer(buffer);
|
|
||||||
if (handler != null) handler.completed(result, attachment);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void failed(Throwable exc, A attachment) {
|
|
||||||
context.offerBuffer(buffer);
|
|
||||||
if (handler != null) handler.failed(exc, attachment);
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
protected <A> void send(final ByteBuffer[] buffers, A attachment, CompletionHandler<Integer, A> handler) {
|
|
||||||
this.channel.write(buffers, attachment, new CompletionHandler<Integer, A>() {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void completed(Integer result, A attachment) {
|
|
||||||
int index = -1;
|
|
||||||
for (int i = 0; i < buffers.length; i++) {
|
|
||||||
if (buffers[i].hasRemaining()) {
|
|
||||||
index = i;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
context.offerBuffer(buffers[i]);
|
|
||||||
}
|
|
||||||
if (index == 0) {
|
|
||||||
channel.write(buffers, attachment, this);
|
|
||||||
} else if (index > 0) {
|
|
||||||
ByteBuffer[] newattachs = new ByteBuffer[buffers.length - index];
|
|
||||||
System.arraycopy(buffers, index, newattachs, 0, newattachs.length);
|
|
||||||
channel.write(newattachs, attachment, this);
|
|
||||||
} else {
|
|
||||||
if (handler != null) handler.completed(result, attachment);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void failed(Throwable exc, A attachment) {
|
|
||||||
for (ByteBuffer buffer : buffers) {
|
|
||||||
context.offerBuffer(buffer);
|
|
||||||
}
|
|
||||||
if (handler != null) handler.failed(exc, attachment);
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public Context getContext() {
|
|
||||||
return context;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,189 +0,0 @@
|
|||||||
/*
|
|
||||||
* To change this license header, choose License Headers in Project Properties.
|
|
||||||
* To change this template file, choose Tools | Templates
|
|
||||||
* and open the template in the editor.
|
|
||||||
*/
|
|
||||||
package org.redkale.net;
|
|
||||||
|
|
||||||
import java.io.*;
|
|
||||||
import java.lang.reflect.*;
|
|
||||||
import java.net.*;
|
|
||||||
import java.nio.charset.*;
|
|
||||||
import java.text.*;
|
|
||||||
import java.util.*;
|
|
||||||
import java.util.concurrent.*;
|
|
||||||
import java.util.concurrent.atomic.*;
|
|
||||||
import java.util.logging.*;
|
|
||||||
import org.redkale.util.*;
|
|
||||||
import org.redkale.watch.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @see http://www.redkale.org
|
|
||||||
* @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 final PrepareServlet prepare;
|
|
||||||
|
|
||||||
protected AnyValue config;
|
|
||||||
|
|
||||||
protected Charset charset;
|
|
||||||
|
|
||||||
protected InetSocketAddress address;
|
|
||||||
|
|
||||||
protected Context context;
|
|
||||||
|
|
||||||
protected int backlog;
|
|
||||||
|
|
||||||
protected ProtocolServer serverChannel;
|
|
||||||
|
|
||||||
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, PrepareServlet servlet, final WatchFactory watch) {
|
|
||||||
this.serverStartTime = serverStartTime;
|
|
||||||
this.protocol = protocol;
|
|
||||||
this.prepare = servlet;
|
|
||||||
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", 8 * 1024);
|
|
||||||
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-" + protocol + "-" + port + "-Thread-" + f.format(counter.incrementAndGet()));
|
|
||||||
return t;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public void destroy(final AnyValue config) throws Exception {
|
|
||||||
this.prepare.destroy(context, config);
|
|
||||||
if (scheduler != null) scheduler.shutdownNow();
|
|
||||||
}
|
|
||||||
|
|
||||||
public InetSocketAddress getSocketAddress() {
|
|
||||||
return address;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getProtocol() {
|
|
||||||
return protocol;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Logger getLogger() {
|
|
||||||
return this.logger;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void start() throws IOException {
|
|
||||||
this.context = this.createContext();
|
|
||||||
this.prepare.init(this.context, config);
|
|
||||||
if (this.watch != null) this.watch.inject(this.prepare);
|
|
||||||
this.serverChannel = ProtocolServer.create(this.protocol, context);
|
|
||||||
this.serverChannel.open();
|
|
||||||
if (this.serverChannel.supportedOptions().contains(StandardSocketOptions.TCP_NODELAY)) {
|
|
||||||
this.serverChannel.setOption(StandardSocketOptions.TCP_NODELAY, true);
|
|
||||||
}
|
|
||||||
serverChannel.bind(address, backlog);
|
|
||||||
serverChannel.accept();
|
|
||||||
final String threadName = "[" + Thread.currentThread().getName() + "] ";
|
|
||||||
logger.info(threadName + this.getClass().getSimpleName() + "." + protocol + " listen: " + address
|
|
||||||
+ ", threads: " + threads + ", bufferCapacity: " + capacity + ", bufferPoolSize: " + bufferPoolSize + ", responsePoolSize: " + responsePoolSize
|
|
||||||
+ ", 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.serverChannel.close();
|
|
||||||
} catch (Exception e) {
|
|
||||||
}
|
|
||||||
logger.info(this.getClass().getSimpleName() + "-" + this.protocol + " shutdow prepare servlet");
|
|
||||||
this.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));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
/*
|
|
||||||
* To change this license header, choose License Headers in Project Properties.
|
|
||||||
* To change this template file, choose Tools | Templates
|
|
||||||
* and open the template in the editor.
|
|
||||||
*/
|
|
||||||
package org.redkale.net;
|
|
||||||
|
|
||||||
import org.redkale.util.AnyValue;
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @see http://www.redkale.org
|
|
||||||
* @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) {
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,255 +0,0 @@
|
|||||||
/*
|
|
||||||
* To change this license header, choose License Headers in Project Properties.
|
|
||||||
* To change this template file, choose Tools | Templates
|
|
||||||
* and open the template in the editor.
|
|
||||||
*/
|
|
||||||
package org.redkale.net;
|
|
||||||
|
|
||||||
import java.net.*;
|
|
||||||
import java.nio.*;
|
|
||||||
import java.nio.channels.*;
|
|
||||||
import java.util.*;
|
|
||||||
import java.util.concurrent.*;
|
|
||||||
import java.util.concurrent.atomic.*;
|
|
||||||
import java.util.function.*;
|
|
||||||
import org.redkale.util.*;
|
|
||||||
import org.redkale.watch.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 传输客户端
|
|
||||||
*
|
|
||||||
* @see http://www.redkale.org
|
|
||||||
* @author zhangjx
|
|
||||||
*/
|
|
||||||
public final class Transport {
|
|
||||||
|
|
||||||
public static final String DEFAULT_PROTOCOL = "TCP";
|
|
||||||
|
|
||||||
protected static final int MAX_POOL_LIMIT = Runtime.getRuntime().availableProcessors() * 16;
|
|
||||||
|
|
||||||
protected static final boolean supportTcpNoDelay;
|
|
||||||
|
|
||||||
static {
|
|
||||||
boolean tcpNoDelay = false;
|
|
||||||
try {
|
|
||||||
AsynchronousSocketChannel channel = AsynchronousSocketChannel.open();
|
|
||||||
tcpNoDelay = channel.supportedOptions().contains(StandardSocketOptions.TCP_NODELAY);
|
|
||||||
channel.close();
|
|
||||||
} catch (Exception e) {
|
|
||||||
}
|
|
||||||
supportTcpNoDelay = tcpNoDelay;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected final String name;
|
|
||||||
|
|
||||||
protected final int bufferPoolSize;
|
|
||||||
|
|
||||||
protected final int bufferCapacity;
|
|
||||||
|
|
||||||
protected final boolean tcp;
|
|
||||||
|
|
||||||
protected final String protocol;
|
|
||||||
|
|
||||||
protected final AsynchronousChannelGroup group;
|
|
||||||
|
|
||||||
protected final InetSocketAddress[] remoteAddres;
|
|
||||||
|
|
||||||
protected final ObjectPool<ByteBuffer> bufferPool;
|
|
||||||
|
|
||||||
protected final ConcurrentHashMap<SocketAddress, BlockingQueue<AsyncConnection>> connPool = new ConcurrentHashMap<>();
|
|
||||||
|
|
||||||
public Transport(Transport transport, InetSocketAddress localAddress, Collection<Transport> transports) {
|
|
||||||
this(transport.name, transport.protocol, null, transport.bufferPoolSize, parse(localAddress, transports));
|
|
||||||
}
|
|
||||||
|
|
||||||
public Transport(String name, WatchFactory watch, int bufferPoolSize, Collection<InetSocketAddress> addresses) {
|
|
||||||
this(name, DEFAULT_PROTOCOL, watch, bufferPoolSize, addresses);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Transport(String name, String protocol, WatchFactory watch, int bufferPoolSize, Collection<InetSocketAddress> addresses) {
|
|
||||||
this.name = name;
|
|
||||||
this.protocol = protocol;
|
|
||||||
this.tcp = "TCP".equalsIgnoreCase(protocol);
|
|
||||||
this.bufferPoolSize = bufferPoolSize;
|
|
||||||
this.bufferCapacity = 8192;
|
|
||||||
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() + "-" + name + "-" + protocol + ".Buffer.creatCounter");
|
|
||||||
AtomicLong cycleBufferCounter = watch == null ? new AtomicLong() : watch.createWatchNumber(Transport.class.getSimpleName() + "-" + name + "-" + protocol + ".Buffer.cycleCounter");
|
|
||||||
final int rcapacity = bufferCapacity;
|
|
||||||
this.bufferPool = new ObjectPool<>(createBufferCounter, cycleBufferCounter, bufferPoolSize,
|
|
||||||
(Object... params) -> ByteBuffer.allocateDirect(rcapacity), null, (e) -> {
|
|
||||||
if (e == null || e.isReadOnly() || e.capacity() != rcapacity) return false;
|
|
||||||
e.clear();
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
this.remoteAddres = addresses.toArray(new InetSocketAddress[addresses.size()]);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Collection<InetSocketAddress> parse(InetSocketAddress addr, Collection<Transport> transports) {
|
|
||||||
final Set<InetSocketAddress> set = new LinkedHashSet<>();
|
|
||||||
for (Transport t : transports) {
|
|
||||||
set.addAll(Arrays.asList(t.remoteAddres));
|
|
||||||
}
|
|
||||||
set.remove(addr);
|
|
||||||
return set;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void close() {
|
|
||||||
connPool.forEach((k, v) -> v.forEach(c -> c.dispose()));
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean match(Collection<InetSocketAddress> addrs) {
|
|
||||||
if (addrs == null) return false;
|
|
||||||
if (addrs.size() != this.remoteAddres.length) return false;
|
|
||||||
for (InetSocketAddress addr : this.remoteAddres) {
|
|
||||||
if (!addrs.contains(addr)) return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public InetSocketAddress[] getRemoteAddress() {
|
|
||||||
return remoteAddres;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return Transport.class.getSimpleName() + "{name=" + name + ",protocol=" + protocol + ",remoteAddres=" + Arrays.toString(remoteAddres) + "}";
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getBufferCapacity() {
|
|
||||||
return bufferCapacity;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ByteBuffer pollBuffer() {
|
|
||||||
return bufferPool.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Supplier<ByteBuffer> getBufferSupplier() {
|
|
||||||
return bufferPool;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void offerBuffer(ByteBuffer buffer) {
|
|
||||||
bufferPool.offer(buffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void offerBuffer(ByteBuffer... buffers) {
|
|
||||||
for (ByteBuffer buffer : buffers) offerBuffer(buffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isTCP() {
|
|
||||||
return tcp;
|
|
||||||
}
|
|
||||||
|
|
||||||
public AsyncConnection pollConnection(SocketAddress addr) {
|
|
||||||
if (addr == null && remoteAddres.length == 1) addr = remoteAddres[0];
|
|
||||||
final boolean rand = addr == null;
|
|
||||||
if (rand && remoteAddres.length < 1) throw new RuntimeException("Transport (" + this.name + ") has no remoteAddress list");
|
|
||||||
try {
|
|
||||||
if (tcp) {
|
|
||||||
AsynchronousSocketChannel channel = null;
|
|
||||||
if (rand) { //取地址
|
|
||||||
for (int i = 0; i < remoteAddres.length; i++) {
|
|
||||||
addr = remoteAddres[i];
|
|
||||||
BlockingQueue<AsyncConnection> queue = connPool.get(addr);
|
|
||||||
if (queue != null && !queue.isEmpty()) {
|
|
||||||
AsyncConnection conn;
|
|
||||||
while ((conn = queue.poll()) != null) {
|
|
||||||
if (conn.isOpen()) return conn;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (channel == null) {
|
|
||||||
channel = AsynchronousSocketChannel.open(group);
|
|
||||||
if (supportTcpNoDelay) channel.setOption(StandardSocketOptions.TCP_NODELAY, true);
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
channel.connect(addr).get(2, TimeUnit.SECONDS);
|
|
||||||
break;
|
|
||||||
} catch (Exception iex) {
|
|
||||||
iex.printStackTrace();
|
|
||||||
if (i == remoteAddres.length - 1) channel = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
channel = AsynchronousSocketChannel.open(group);
|
|
||||||
if (supportTcpNoDelay) channel.setOption(StandardSocketOptions.TCP_NODELAY, true);
|
|
||||||
channel.connect(addr).get(2, TimeUnit.SECONDS);
|
|
||||||
}
|
|
||||||
if (channel == null) return null;
|
|
||||||
return AsyncConnection.create(channel, addr, 3000, 3000);
|
|
||||||
} else { // UDP
|
|
||||||
if (rand) addr = remoteAddres[0];
|
|
||||||
DatagramChannel channel = DatagramChannel.open();
|
|
||||||
channel.configureBlocking(true);
|
|
||||||
channel.connect(addr);
|
|
||||||
return AsyncConnection.create(channel, addr, true, 3000, 3000);
|
|
||||||
// AsyncDatagramChannel channel = AsyncDatagramChannel.open(group);
|
|
||||||
// channel.connect(addr);
|
|
||||||
// return AsyncConnection.create(channel, addr, true, 3000, 3000);
|
|
||||||
}
|
|
||||||
} catch (Exception ex) {
|
|
||||||
throw new RuntimeException("transport address = " + addr, ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void offerConnection(final boolean forceClose, AsyncConnection conn) {
|
|
||||||
if (!forceClose && conn.isTCP()) { //暂时每次都关闭
|
|
||||||
if (conn.isOpen()) {
|
|
||||||
BlockingQueue<AsyncConnection> queue = connPool.get(conn.getRemoteAddress());
|
|
||||||
if (queue == null) {
|
|
||||||
queue = new ArrayBlockingQueue<>(MAX_POOL_LIMIT);
|
|
||||||
connPool.put(conn.getRemoteAddress(), queue);
|
|
||||||
}
|
|
||||||
if (!queue.offer(conn)) conn.dispose();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
conn.dispose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public <A> void async(SocketAddress addr, final ByteBuffer buffer, A att, final CompletionHandler<Integer, A> handler) {
|
|
||||||
final AsyncConnection conn = pollConnection(addr);
|
|
||||||
conn.write(buffer, buffer, new CompletionHandler<Integer, ByteBuffer>() {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void completed(Integer result, ByteBuffer attachment) {
|
|
||||||
buffer.clear();
|
|
||||||
conn.read(buffer, buffer, new CompletionHandler<Integer, ByteBuffer>() {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void completed(Integer result, ByteBuffer attachment) {
|
|
||||||
if (handler != null) handler.completed(result, att);
|
|
||||||
offerBuffer(buffer);
|
|
||||||
offerConnection(false, conn);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void failed(Throwable exc, ByteBuffer attachment) {
|
|
||||||
offerBuffer(buffer);
|
|
||||||
offerConnection(true, conn);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void failed(Throwable exc, ByteBuffer attachment) {
|
|
||||||
offerBuffer(buffer);
|
|
||||||
offerConnection(true, conn);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
/*
|
|
||||||
* To change this license header, choose License Headers in Project Properties.
|
|
||||||
* To change this template file, choose Tools | Templates
|
|
||||||
* and open the template in the editor.
|
|
||||||
*/
|
|
||||||
package org.redkale.net;
|
|
||||||
|
|
||||||
import java.util.concurrent.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @see http://www.redkale.org
|
|
||||||
* @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);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ExecutorService getExecutor() {
|
|
||||||
return executor;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,316 +0,0 @@
|
|||||||
/*
|
|
||||||
* To change this license header, choose License Headers in Project Properties.
|
|
||||||
* To change this template file, choose Tools | Templates
|
|
||||||
* and open the template in the editor.
|
|
||||||
*/
|
|
||||||
package org.redkale.net.http;
|
|
||||||
|
|
||||||
import org.redkale.net.Response;
|
|
||||||
import org.redkale.net.Request;
|
|
||||||
import org.redkale.net.Context;
|
|
||||||
import org.redkale.util.AnyValue;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.lang.annotation.*;
|
|
||||||
import static java.lang.annotation.ElementType.*;
|
|
||||||
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
|
||||||
import java.lang.reflect.Method;
|
|
||||||
import java.nio.*;
|
|
||||||
import java.util.*;
|
|
||||||
import java.util.concurrent.*;
|
|
||||||
import jdk.internal.org.objectweb.asm.*;
|
|
||||||
import static jdk.internal.org.objectweb.asm.Opcodes.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @see http://www.redkale.org
|
|
||||||
* @author zhangjx
|
|
||||||
*/
|
|
||||||
public abstract class BasedHttpServlet extends HttpServlet {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 配合 BasedHttpServlet 使用。
|
|
||||||
* 当标记为 @AuthIgnore 的方法不会再调用之前调用authenticate 方法。
|
|
||||||
*
|
|
||||||
* @see http://www.redkale.org
|
|
||||||
* @author zhangjx
|
|
||||||
*/
|
|
||||||
@Inherited
|
|
||||||
@Documented
|
|
||||||
@Target({METHOD, TYPE})
|
|
||||||
@Retention(RUNTIME)
|
|
||||||
public static @interface AuthIgnore {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 配合 BasedHttpServlet 使用。
|
|
||||||
* 用于对@WebServlet对应的url进行细分。 其 url
|
|
||||||
*
|
|
||||||
* @see http://www.redkale.org
|
|
||||||
* @author zhangjx
|
|
||||||
*/
|
|
||||||
@Target({ElementType.METHOD})
|
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
|
||||||
@Documented
|
|
||||||
public static @interface WebAction {
|
|
||||||
|
|
||||||
int actionid() default 0;
|
|
||||||
|
|
||||||
String url();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 配合 BasedHttpServlet 使用。
|
|
||||||
* 当标记为 @HttpCacheable 的方法使用response.finish的参数将被缓存一定时间(默认值timeout=15秒)。
|
|
||||||
* 通常情况下 @HttpCacheable 需要与 @AuthIgnore 一起使用,因为没有标记@AuthIgnore的方法一般输出的结果与当前用户信息有关。
|
|
||||||
*
|
|
||||||
* @see http://www.redkale.org
|
|
||||||
* @author zhangjx
|
|
||||||
*/
|
|
||||||
@Target({ElementType.METHOD})
|
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
|
||||||
@Documented
|
|
||||||
public static @interface HttpCacheable {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 超时的秒数
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
int timeout() default 15;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Map.Entry<String, Entry>[] actions;
|
|
||||||
|
|
||||||
public boolean preExecute(HttpRequest request, HttpResponse response) throws IOException {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public final void execute(HttpRequest request, HttpResponse response) throws IOException {
|
|
||||||
if (!preExecute(request, response)) return;
|
|
||||||
for (Map.Entry<String, Entry> en : actions) {
|
|
||||||
if (request.getRequestURI().startsWith(en.getKey())) {
|
|
||||||
Entry entry = en.getValue();
|
|
||||||
if (entry.ignore || authenticate(entry.moduleid, entry.actionid, request, response)) {
|
|
||||||
if (entry.cachetimeout > 0) {//有缓存设置
|
|
||||||
CacheEntry ce = entry.cache.get(request.getRequestURI());
|
|
||||||
if (ce != null && ce.time + entry.cachetimeout > System.currentTimeMillis()) { //缓存有效
|
|
||||||
response.setStatus(ce.status);
|
|
||||||
response.setContentType(ce.contentType);
|
|
||||||
response.finish(ce.getBuffers());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
response.setInterceptor(entry.cacheInterceptor);
|
|
||||||
}
|
|
||||||
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(int module, int actionid, 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) || "preExecute".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;
|
|
||||||
//-----------------------------------------------
|
|
||||||
|
|
||||||
final WebAction action = method.getAnnotation(WebAction.class);
|
|
||||||
if (action == null) continue;
|
|
||||||
final int actionid = action.actionid();
|
|
||||||
final String 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 sub-contains " + 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;
|
|
||||||
HttpCacheable hc = method.getAnnotation(HttpCacheable.class);
|
|
||||||
this.cachetimeout = hc == null ? 0 : hc.timeout() * 1000;
|
|
||||||
this.cache = cachetimeout > 0 ? new ConcurrentHashMap() : null;
|
|
||||||
this.cacheInterceptor = cachetimeout > 0 ? (HttpResponse response, ByteBuffer[] buffers) -> {
|
|
||||||
int status = response.getStatus();
|
|
||||||
if (status != 200) return null;
|
|
||||||
CacheEntry ce = new CacheEntry(response.getStatus(), response.getContentType(), buffers);
|
|
||||||
cache.put(response.getRequest().getRequestURI(), ce);
|
|
||||||
return ce.getBuffers();
|
|
||||||
} : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isNeedCheck() {
|
|
||||||
return this.moduleid != 0 || this.actionid != 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final HttpResponse.Interceptor cacheInterceptor;
|
|
||||||
|
|
||||||
public final ConcurrentHashMap<String, CacheEntry> cache;
|
|
||||||
|
|
||||||
public final int cachetimeout;
|
|
||||||
|
|
||||||
public final boolean ignore;
|
|
||||||
|
|
||||||
public final int moduleid;
|
|
||||||
|
|
||||||
public final int actionid;
|
|
||||||
|
|
||||||
public final String name;
|
|
||||||
|
|
||||||
public final Method method;
|
|
||||||
|
|
||||||
public final HttpServlet servlet;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static final class CacheEntry {
|
|
||||||
|
|
||||||
public final long time = System.currentTimeMillis();
|
|
||||||
|
|
||||||
private final ByteBuffer[] buffers;
|
|
||||||
|
|
||||||
private final int status;
|
|
||||||
|
|
||||||
private final String contentType;
|
|
||||||
|
|
||||||
public CacheEntry(int status, String contentType, ByteBuffer[] bufs) {
|
|
||||||
this.status = status;
|
|
||||||
this.contentType = contentType;
|
|
||||||
final ByteBuffer[] newBuffers = new ByteBuffer[bufs.length];
|
|
||||||
for (int i = 0; i < newBuffers.length; i++) {
|
|
||||||
newBuffers[i] = bufs[i].duplicate().asReadOnlyBuffer();
|
|
||||||
}
|
|
||||||
this.buffers = newBuffers;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ByteBuffer[] getBuffers() {
|
|
||||||
final ByteBuffer[] newBuffers = new ByteBuffer[buffers.length];
|
|
||||||
for (int i = 0; i < newBuffers.length; i++) {
|
|
||||||
newBuffers[i] = buffers[i].duplicate();
|
|
||||||
}
|
|
||||||
return newBuffers;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,60 +0,0 @@
|
|||||||
/*
|
|
||||||
* To change this license header, choose License Headers in Project Properties.
|
|
||||||
* To change this template file, choose Tools | Templates
|
|
||||||
* and open the template in the editor.
|
|
||||||
*/
|
|
||||||
package org.redkale.net.http;
|
|
||||||
|
|
||||||
import java.net.*;
|
|
||||||
import java.nio.*;
|
|
||||||
import java.nio.charset.*;
|
|
||||||
import java.security.*;
|
|
||||||
import java.util.concurrent.*;
|
|
||||||
import java.util.logging.*;
|
|
||||||
import org.redkale.net.*;
|
|
||||||
import org.redkale.util.*;
|
|
||||||
import org.redkale.watch.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @see http://www.redkale.org
|
|
||||||
* @author zhangjx
|
|
||||||
*/
|
|
||||||
public final class HttpContext extends Context {
|
|
||||||
|
|
||||||
protected final String contextPath;
|
|
||||||
|
|
||||||
protected final SecureRandom random = new SecureRandom();
|
|
||||||
|
|
||||||
public HttpContext(long serverStartTime, Logger logger, ExecutorService executor, int bufferCapacity, ObjectPool<ByteBuffer> bufferPool,
|
|
||||||
ObjectPool<Response> responsePool, int maxbody, Charset charset, InetSocketAddress address, PrepareServlet prepare,
|
|
||||||
WatchFactory watch, int readTimeoutSecond, int writeTimeoutSecond, String contextPath) {
|
|
||||||
super(serverStartTime, logger, executor, bufferCapacity, bufferPool, responsePool, maxbody, charset,
|
|
||||||
address, prepare, watch, readTimeoutSecond, writeTimeoutSecond);
|
|
||||||
this.contextPath = contextPath;
|
|
||||||
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 ObjectPool<Response> getResponsePool() {
|
|
||||||
return responsePool;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,153 +0,0 @@
|
|||||||
/*
|
|
||||||
* To change this license header, choose License Headers in Project Properties.
|
|
||||||
* To change this template file, choose Tools | Templates
|
|
||||||
* and open the template in the editor.
|
|
||||||
*/
|
|
||||||
package org.redkale.net.http;
|
|
||||||
|
|
||||||
import org.redkale.util.AnyValue.DefaultAnyValue;
|
|
||||||
import java.io.*;
|
|
||||||
import java.nio.*;
|
|
||||||
import java.util.*;
|
|
||||||
import java.util.AbstractMap.SimpleEntry;
|
|
||||||
import java.util.function.*;
|
|
||||||
import java.util.logging.*;
|
|
||||||
import java.util.regex.*;
|
|
||||||
import org.redkale.net.*;
|
|
||||||
import org.redkale.util.*;
|
|
||||||
import org.redkale.watch.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @see http://www.redkale.org
|
|
||||||
* @author zhangjx
|
|
||||||
*/
|
|
||||||
public final class HttpPrepareServlet extends PrepareServlet<HttpRequest, HttpResponse<HttpRequest>> {
|
|
||||||
|
|
||||||
private ByteBuffer flashPolicyBuffer;
|
|
||||||
|
|
||||||
private final List<HttpServlet> servlets = new ArrayList<>();
|
|
||||||
|
|
||||||
private final Map<String, HttpServlet> strmaps = new HashMap<>();
|
|
||||||
|
|
||||||
private SimpleEntry<Predicate<String>, HttpServlet>[] regArray = new SimpleEntry[0];
|
|
||||||
|
|
||||||
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(s -> {
|
|
||||||
s.init(context, s.conf);
|
|
||||||
});
|
|
||||||
final WatchFactory watch = ((HttpContext) context).getWatchFactory();
|
|
||||||
if (watch != null) {
|
|
||||||
this.servlets.stream().forEach(s -> {
|
|
||||||
watch.inject(s);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
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,$");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@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(true, flashPolicyBuffer.duplicate());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
final String uri = request.getRequestURI();
|
|
||||||
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. request = " + request, e);
|
|
||||||
response.finish(500, 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
servlet.conf = conf;
|
|
||||||
this.servlets.add(servlet);
|
|
||||||
}
|
|
||||||
|
|
||||||
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(s -> {
|
|
||||||
s.destroy(context, s.conf);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,538 +0,0 @@
|
|||||||
/*
|
|
||||||
* To change this license header, choose License Headers in Project Properties.
|
|
||||||
* To change this template file, choose Tools | Templates
|
|
||||||
* and open the template in the editor.
|
|
||||||
*/
|
|
||||||
package org.redkale.net.http;
|
|
||||||
|
|
||||||
import org.redkale.util.ByteArray;
|
|
||||||
import org.redkale.util.AnyValue.DefaultAnyValue;
|
|
||||||
import java.io.*;
|
|
||||||
import java.net.*;
|
|
||||||
import java.nio.*;
|
|
||||||
import java.nio.channels.*;
|
|
||||||
import java.nio.charset.*;
|
|
||||||
import org.redkale.convert.json.*;
|
|
||||||
import org.redkale.net.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Http请求包 与javax.servlet.http.HttpServletRequest 基本类似。
|
|
||||||
* 同时提供json的解析接口: public Object getJsonParameter(Class clazz, String name)
|
|
||||||
* RedKale提倡带简单的参数的GET请求采用类似REST风格, 因此提供了 getRequstURIPath 系列接口。
|
|
||||||
* 例如简单的翻页查询 /pipes/record/query/page:2/size:20
|
|
||||||
* 获取页号: int page = request.getRequstURIPath("page:", 1);
|
|
||||||
* 获取行数: int size = request.getRequstURIPath("size:", 10);
|
|
||||||
*
|
|
||||||
* @see http://www.redkale.org
|
|
||||||
* @author zhangjx
|
|
||||||
*/
|
|
||||||
public class HttpRequest extends Request {
|
|
||||||
|
|
||||||
protected static final Charset UTF8 = Charset.forName("UTF-8");
|
|
||||||
|
|
||||||
protected static final String SESSIONID_NAME = "JSESSIONID";
|
|
||||||
|
|
||||||
private static final byte[] flashRequestContent1 = "<policy-file-request/>\0".getBytes();
|
|
||||||
|
|
||||||
private static final byte[] flashRequestContent2 = "<policy-file-request/>".getBytes();
|
|
||||||
|
|
||||||
private String method;
|
|
||||||
|
|
||||||
private String protocol;
|
|
||||||
|
|
||||||
protected String requestURI;
|
|
||||||
|
|
||||||
private long contentLength = -1;
|
|
||||||
|
|
||||||
private String contentType;
|
|
||||||
|
|
||||||
private String host;
|
|
||||||
|
|
||||||
private String connection;
|
|
||||||
|
|
||||||
protected String cookiestr;
|
|
||||||
|
|
||||||
private HttpCookie[] cookies;
|
|
||||||
|
|
||||||
protected String newsessionid;
|
|
||||||
|
|
||||||
protected final DefaultAnyValue header = new DefaultAnyValue();
|
|
||||||
|
|
||||||
protected final DefaultAnyValue params = new DefaultAnyValue();
|
|
||||||
|
|
||||||
private final ByteArray array = new ByteArray();
|
|
||||||
|
|
||||||
private boolean bodyparsed = false;
|
|
||||||
|
|
||||||
protected boolean flashPolicy = false;
|
|
||||||
|
|
||||||
protected boolean boundary = false;
|
|
||||||
|
|
||||||
private final String remoteAddrHeader;
|
|
||||||
|
|
||||||
public HttpRequest(Context context, String remoteAddrHeader) {
|
|
||||||
super(context);
|
|
||||||
this.remoteAddrHeader = remoteAddrHeader;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void setKeepAlive(boolean keepAlive) {
|
|
||||||
this.keepAlive = keepAlive;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected boolean isKeepAlive() {
|
|
||||||
return this.keepAlive;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected AsyncConnection getChannel() {
|
|
||||||
return this.channel;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected JsonConvert getJsonConvert() {
|
|
||||||
return this.jsonConvert;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected int readHeader(final ByteBuffer buffer) {
|
|
||||||
if (!readLine(buffer, array)) {
|
|
||||||
if (array.equal(flashRequestContent1) || array.equal(flashRequestContent2)) { //兼容 flash socket <policy-file-request/>
|
|
||||||
this.flashPolicy = true;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
Charset charset = this.context.getCharset();
|
|
||||||
int index = 0;
|
|
||||||
int offset = array.find(index, ' ');
|
|
||||||
if (offset <= 0) return -1;
|
|
||||||
this.method = array.toString(index, offset, charset).trim();
|
|
||||||
index = ++offset;
|
|
||||||
offset = array.find(index, ' ');
|
|
||||||
if (offset <= 0) return -1;
|
|
||||||
int off = array.find(index, '#');
|
|
||||||
if (off > 0) offset = off;
|
|
||||||
int qst = array.find(index, offset, (byte) '?');
|
|
||||||
if (qst > 0) {
|
|
||||||
this.requestURI = array.toDecodeString(index, qst - index, charset).trim();
|
|
||||||
addParameter(array, qst + 1, offset - qst - 1);
|
|
||||||
} else {
|
|
||||||
this.requestURI = array.toDecodeString(index, offset - index, charset).trim();
|
|
||||||
}
|
|
||||||
if (this.requestURI.contains("../")) return -1;
|
|
||||||
index = ++offset;
|
|
||||||
this.protocol = array.toString(index, array.count() - index, charset).trim();
|
|
||||||
while (readLine(buffer, array)) {
|
|
||||||
if (array.count() < 2) break;
|
|
||||||
index = 0;
|
|
||||||
offset = array.find(index, ':');
|
|
||||||
if (offset <= 0) return -1;
|
|
||||||
String name = array.toString(index, offset, charset).trim();
|
|
||||||
index = offset + 1;
|
|
||||||
String value = array.toString(index, array.count() - index, charset).trim();
|
|
||||||
switch (name) {
|
|
||||||
case "Content-Type":
|
|
||||||
this.contentType = value;
|
|
||||||
break;
|
|
||||||
case "Content-Length":
|
|
||||||
this.contentLength = Long.decode(value);
|
|
||||||
break;
|
|
||||||
case "Host":
|
|
||||||
this.host = value;
|
|
||||||
break;
|
|
||||||
case "Cookie":
|
|
||||||
if (this.cookiestr == null || this.cookiestr.isEmpty()) {
|
|
||||||
this.cookiestr = value;
|
|
||||||
} else {
|
|
||||||
this.cookiestr += ";" + value;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case "Connection":
|
|
||||||
this.connection = value;
|
|
||||||
this.setKeepAlive(!"close".equalsIgnoreCase(value));
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
header.addValue(name, value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
array.clear();
|
|
||||||
if (buffer.hasRemaining()) array.add(buffer, buffer.remaining());
|
|
||||||
if (this.contentType != null && this.contentType.contains("boundary=")) {
|
|
||||||
this.boundary = true;
|
|
||||||
}
|
|
||||||
if (this.contentLength > 0 && (this.contentType == null || !this.boundary)) {
|
|
||||||
if (this.contentLength > context.getMaxbody()) return -1;
|
|
||||||
int lr = (int) this.contentLength - array.count();
|
|
||||||
return lr > 0 ? lr : 0;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected int readBody(ByteBuffer buffer) {
|
|
||||||
int len = buffer.remaining();
|
|
||||||
array.add(buffer, len);
|
|
||||||
return len;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void prepare() {
|
|
||||||
}
|
|
||||||
|
|
||||||
private void parseBody() {
|
|
||||||
if (this.boundary || bodyparsed) return;
|
|
||||||
addParameter(array, 0, array.count());
|
|
||||||
bodyparsed = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void addParameter(final ByteArray array, final int offset, final int len) {
|
|
||||||
if (len < 1) return;
|
|
||||||
Charset charset = this.context.getCharset();
|
|
||||||
int limit = offset + len;
|
|
||||||
int keypos = array.find(offset, limit, '=');
|
|
||||||
int valpos = array.find(offset, limit, '&');
|
|
||||||
if (keypos <= 0 || (valpos >= 0 && valpos < keypos)) {
|
|
||||||
if (valpos > 0) addParameter(array, valpos + 1, limit - valpos - 1);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
String name = array.toDecodeString(offset, keypos - offset, charset);
|
|
||||||
++keypos;
|
|
||||||
String value = array.toDecodeString(keypos, (valpos < 0) ? (limit - keypos) : (valpos - keypos), charset);
|
|
||||||
this.params.addValue(name, value);
|
|
||||||
if (valpos >= 0) {
|
|
||||||
addParameter(array, valpos + 1, limit - valpos - 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean readLine(ByteBuffer buffer, ByteArray bytes) {
|
|
||||||
byte lasted = '\r';
|
|
||||||
bytes.clear();
|
|
||||||
for (;;) {
|
|
||||||
if (!buffer.hasRemaining()) {
|
|
||||||
if (lasted != '\r') bytes.add(lasted);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
byte b = buffer.get();
|
|
||||||
if (b == -1 || (lasted == '\r' && b == '\n')) break;
|
|
||||||
if (lasted != '\r') bytes.add(lasted);
|
|
||||||
lasted = b;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void setProperty(String name, Object value) {
|
|
||||||
super.setProperty(name, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
protected <T> T getProperty(String name) {
|
|
||||||
return super.getProperty(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void removeProperty(String name) {
|
|
||||||
super.removeProperty(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public HttpContext getContext() {
|
|
||||||
return (HttpContext) this.context;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getRemoteAddr() {
|
|
||||||
if (remoteAddrHeader != null) {
|
|
||||||
String val = getHeader(remoteAddrHeader);
|
|
||||||
if (val != null) return val;
|
|
||||||
}
|
|
||||||
SocketAddress addr = getRemoteAddress();
|
|
||||||
if (addr == null) return "";
|
|
||||||
if (addr instanceof InetSocketAddress) return ((InetSocketAddress) addr).getAddress().getHostAddress();
|
|
||||||
return String.valueOf(addr);
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getBody(final Charset charset) {
|
|
||||||
return array.toString(charset);
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getBody() {
|
|
||||||
return array.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getBodyUTF8() {
|
|
||||||
return array.toString(UTF8);
|
|
||||||
}
|
|
||||||
|
|
||||||
public SocketAddress getRemoteAddress() {
|
|
||||||
return this.channel.getRemoteAddress();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
parseBody();
|
|
||||||
return this.getClass().getSimpleName() + "{method:" + this.method + ", requestURI:" + this.requestURI
|
|
||||||
+ ", contentType:" + this.contentType + ", connection:" + this.connection + ", protocol:" + this.protocol
|
|
||||||
+ ", contentLength:" + this.contentLength + ", cookies:" + this.cookiestr
|
|
||||||
+ ", host:" + this.host + ", params:" + this.params + ", header:" + this.header + "}";
|
|
||||||
}
|
|
||||||
|
|
||||||
public final MultiContext getMultiContext() {
|
|
||||||
return new MultiContext(context.getCharset(), this.getContentType(),
|
|
||||||
new BufferedInputStream(Channels.newInputStream(this.channel), Math.max(array.count(), 8192)) {
|
|
||||||
{
|
|
||||||
array.write(this.buf);
|
|
||||||
this.count = array.count();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void recycle() {
|
|
||||||
this.cookiestr = null;
|
|
||||||
this.cookies = null;
|
|
||||||
this.newsessionid = null;
|
|
||||||
this.method = null;
|
|
||||||
this.protocol = null;
|
|
||||||
this.requestURI = null;
|
|
||||||
this.contentType = null;
|
|
||||||
this.host = null;
|
|
||||||
this.connection = null;
|
|
||||||
this.contentLength = -1;
|
|
||||||
this.boundary = false;
|
|
||||||
this.bodyparsed = false;
|
|
||||||
this.flashPolicy = false;
|
|
||||||
|
|
||||||
this.header.clear();
|
|
||||||
this.params.clear();
|
|
||||||
this.array.clear();
|
|
||||||
super.recycle();
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getSessionid(boolean create) {
|
|
||||||
String sessionid = getCookie(SESSIONID_NAME, null);
|
|
||||||
if (create && (sessionid == null || sessionid.isEmpty())) {
|
|
||||||
sessionid = ((HttpContext) context).createSessionid();
|
|
||||||
this.newsessionid = sessionid;
|
|
||||||
}
|
|
||||||
return sessionid;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String changeSessionid() {
|
|
||||||
this.newsessionid = ((HttpContext) context).createSessionid();
|
|
||||||
return newsessionid;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void invalidateSession() {
|
|
||||||
this.newsessionid = ""; //为空表示删除sessionid
|
|
||||||
}
|
|
||||||
|
|
||||||
public HttpCookie[] getCookies() {
|
|
||||||
if (this.cookies == null) this.cookies = parseCookies(this.cookiestr);
|
|
||||||
return this.cookies;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getCookie(String name) {
|
|
||||||
return getCookie(name, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getCookie(String name, String dfvalue) {
|
|
||||||
for (HttpCookie cookie : getCookies()) {
|
|
||||||
if (name.equals(cookie.getName())) return cookie.getValue();
|
|
||||||
}
|
|
||||||
return dfvalue;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static HttpCookie[] parseCookies(String cookiestr) {
|
|
||||||
if (cookiestr == null || cookiestr.isEmpty()) return new HttpCookie[0];
|
|
||||||
String str = cookiestr.replaceAll("(^;)|(;$)", "").replaceAll(";+", ";");
|
|
||||||
if (str.isEmpty()) return new HttpCookie[0];
|
|
||||||
String[] strs = str.split(";");
|
|
||||||
HttpCookie[] cookies = new HttpCookie[strs.length];
|
|
||||||
for (int i = 0; i < strs.length; i++) {
|
|
||||||
String s = strs[i];
|
|
||||||
int pos = s.indexOf('=');
|
|
||||||
String v = (pos < 0 ? "" : s.substring(pos + 1));
|
|
||||||
if (v.indexOf('"') == 0 && v.lastIndexOf('"') == v.length() - 1) v = v.substring(1, v.length() - 1);
|
|
||||||
cookies[i] = new HttpCookie((pos < 0 ? s : s.substring(0, pos)), v);
|
|
||||||
}
|
|
||||||
return cookies;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getConnection() {
|
|
||||||
return connection;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getMethod() {
|
|
||||||
return method;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getProtocol() {
|
|
||||||
return protocol;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getHost() {
|
|
||||||
return host;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected static InetSocketAddress parseSocketAddress(String host) {
|
|
||||||
if (host == null || host.isEmpty()) return null;
|
|
||||||
int pos = host.indexOf(':');
|
|
||||||
String hostname = pos < 0 ? host : host.substring(0, pos);
|
|
||||||
int port = pos < 0 ? 80 : Integer.parseInt(host.substring(pos + 1));
|
|
||||||
return new InetSocketAddress(hostname, port);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected InetSocketAddress getHostSocketAddress() {
|
|
||||||
return parseSocketAddress(host);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 截取getRequestURI最后的一个/后面的部分
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
public String getRequstURILastPath() {
|
|
||||||
if (requestURI == null) return "";
|
|
||||||
return requestURI.substring(requestURI.lastIndexOf('/') + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* 从prefix之后截取getRequestURI再对"/"进行分隔
|
|
||||||
* <p>
|
|
||||||
* @param prefix
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
public String[] getRequstURIPaths(String prefix) {
|
|
||||||
if (requestURI == null || prefix == null) return new String[0];
|
|
||||||
return requestURI.substring(requestURI.indexOf(prefix) + prefix.length() + (prefix.endsWith("/") ? 0 : 1)).split("/");
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getRequstURIPath(String prefix, String defvalue) {
|
|
||||||
if (requestURI == null || prefix == null) return defvalue;
|
|
||||||
int pos = requestURI.indexOf(prefix);
|
|
||||||
if (pos < 0) return defvalue;
|
|
||||||
String sub = requestURI.substring(pos + prefix.length());
|
|
||||||
pos = sub.indexOf('/');
|
|
||||||
return pos < 0 ? sub : sub.substring(0, pos);
|
|
||||||
}
|
|
||||||
|
|
||||||
public short getRequstURIPath(String prefix, short defvalue) {
|
|
||||||
String val = getRequstURIPath(prefix, null);
|
|
||||||
return val == null ? defvalue : Short.parseShort(val);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getRequstURIPath(String prefix, int defvalue) {
|
|
||||||
String val = getRequstURIPath(prefix, null);
|
|
||||||
return val == null ? defvalue : Integer.parseInt(val);
|
|
||||||
}
|
|
||||||
|
|
||||||
public long getRequstURIPath(String prefix, long defvalue) {
|
|
||||||
String val = getRequstURIPath(prefix, null);
|
|
||||||
return val == null ? defvalue : Long.parseLong(val);
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getRequestURI() {
|
|
||||||
return requestURI;
|
|
||||||
}
|
|
||||||
|
|
||||||
public long getContentLength() {
|
|
||||||
return contentLength;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getContentType() {
|
|
||||||
return contentType;
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
public String[] getHeaderNames() {
|
|
||||||
return header.getNames();
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getHeader(String name) {
|
|
||||||
return header.getValue(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
public <T> T getJsonHeader(Class<T> clazz, String name) {
|
|
||||||
String v = getHeader(name);
|
|
||||||
return v == null || v.isEmpty() ? null : jsonConvert.convertFrom(clazz, v);
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean getBooleanHeader(String name, boolean defaultValue) {
|
|
||||||
return header.getBoolValue(name, defaultValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
public short getShortHeader(String name, short defaultValue) {
|
|
||||||
return header.getShortValue(name, defaultValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getIntHeader(String name, int defaultValue) {
|
|
||||||
return header.getIntValue(name, defaultValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
public long getLongHeader(String name, long defaultValue) {
|
|
||||||
return header.getLongValue(name, defaultValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
public float getFloatHeader(String name, float defaultValue) {
|
|
||||||
return header.getFloatValue(name, defaultValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
public double getDoubleHeader(String name, double defaultValue) {
|
|
||||||
return header.getDoubleValue(name, defaultValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getHeader(String name, String defaultValue) {
|
|
||||||
return header.getValue(name, defaultValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
public String[] getParameterNames() {
|
|
||||||
parseBody();
|
|
||||||
return params.getNames();
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getParameter(String name) {
|
|
||||||
parseBody();
|
|
||||||
return params.getValue(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
public <T> T getJsonParameter(Class<T> clazz, String name) {
|
|
||||||
String v = getParameter(name);
|
|
||||||
return v == null || v.isEmpty() ? null : jsonConvert.convertFrom(clazz, v);
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean getBooleanParameter(String name, boolean defaultValue) {
|
|
||||||
parseBody();
|
|
||||||
return params.getBoolValue(name, defaultValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
public short getShortParameter(String name, short defaultValue) {
|
|
||||||
parseBody();
|
|
||||||
return params.getShortValue(name, defaultValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getIntParameter(String name, int defaultValue) {
|
|
||||||
parseBody();
|
|
||||||
return params.getIntValue(name, defaultValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
public long getLongParameter(String name, long defaultValue) {
|
|
||||||
parseBody();
|
|
||||||
return params.getLongValue(name, defaultValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
public float getFloatParameter(String name, float defaultValue) {
|
|
||||||
parseBody();
|
|
||||||
return params.getFloatValue(name, defaultValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
public double getDoubleParameter(String name, double defaultValue) {
|
|
||||||
parseBody();
|
|
||||||
return params.getDoubleValue(name, defaultValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getParameter(String name, String defaultValue) {
|
|
||||||
parseBody();
|
|
||||||
return params.getValue(name, defaultValue);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,261 +0,0 @@
|
|||||||
/*
|
|
||||||
* To change this license header, choose License Headers in Project Properties.
|
|
||||||
* To change this template file, choose Tools | Templates
|
|
||||||
* and open the template in the editor.
|
|
||||||
*/
|
|
||||||
package org.redkale.net.http;
|
|
||||||
|
|
||||||
import java.io.*;
|
|
||||||
import java.nio.*;
|
|
||||||
import java.nio.file.*;
|
|
||||||
import static java.nio.file.StandardWatchEventKinds.*;
|
|
||||||
import java.util.*;
|
|
||||||
import java.util.AbstractMap.SimpleEntry;
|
|
||||||
import java.util.concurrent.*;
|
|
||||||
import java.util.concurrent.atomic.*;
|
|
||||||
import java.util.function.*;
|
|
||||||
import java.util.logging.*;
|
|
||||||
import java.util.regex.*;
|
|
||||||
import org.redkale.net.*;
|
|
||||||
import org.redkale.util.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @see http://www.redkale.org
|
|
||||||
* @author zhangjx
|
|
||||||
*/
|
|
||||||
public final class HttpResourceServlet extends HttpServlet {
|
|
||||||
|
|
||||||
private static final Logger logger = Logger.getLogger(HttpResourceServlet.class.getSimpleName());
|
|
||||||
|
|
||||||
protected class WatchThread extends Thread {
|
|
||||||
|
|
||||||
protected final File root;
|
|
||||||
|
|
||||||
protected final WatchService watcher;
|
|
||||||
|
|
||||||
public WatchThread(File root) throws IOException {
|
|
||||||
this.root = root;
|
|
||||||
this.setName("Servlet-ResourceWatch-Thread");
|
|
||||||
this.setDaemon(true);
|
|
||||||
this.watcher = this.root.toPath().getFileSystem().newWatchService();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
try {
|
|
||||||
final String rootstr = root.getCanonicalPath();
|
|
||||||
while (!this.isInterrupted()) {
|
|
||||||
final WatchKey key = watcher.take();
|
|
||||||
final Path parent = keymaps.get(key);
|
|
||||||
if (parent == null) {
|
|
||||||
key.cancel();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
key.pollEvents().stream().forEach((event) -> {
|
|
||||||
try {
|
|
||||||
Path path = parent.resolve((Path) event.context());
|
|
||||||
final String uri = path.toString().substring(rootstr.length()).replace('\\', '/');
|
|
||||||
//logger.log(Level.FINEST, "file(" + uri + ") happen " + event.kind() + " event");
|
|
||||||
if (event.kind() == ENTRY_DELETE) {
|
|
||||||
files.remove(uri);
|
|
||||||
} else if (event.kind() == ENTRY_MODIFY) {
|
|
||||||
FileEntry en = files.get(uri);
|
|
||||||
if (en != null) {
|
|
||||||
Thread.sleep(5000L); //等待update file完毕
|
|
||||||
en.update();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (Exception ex) {
|
|
||||||
logger.log(Level.FINE, event.context() + " occur erroneous", ex);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
key.reset();
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//缓存总大小, 默认128M
|
|
||||||
protected long cachelimit = 128 * 1024 * 1024L;
|
|
||||||
|
|
||||||
protected final LongAdder cachedLength = new LongAdder();
|
|
||||||
|
|
||||||
//最大可缓存的文件大小, 大于该值的文件将不被缓存
|
|
||||||
protected long cachelengthmax = 1 * 1024 * 1024;
|
|
||||||
|
|
||||||
protected File root = new File("./root/");
|
|
||||||
|
|
||||||
protected final ConcurrentHashMap<String, FileEntry> files = new ConcurrentHashMap<>();
|
|
||||||
|
|
||||||
protected final ConcurrentHashMap<WatchKey, Path> keymaps = new ConcurrentHashMap<>();
|
|
||||||
|
|
||||||
protected SimpleEntry<Pattern, String>[] locationRewrites;
|
|
||||||
|
|
||||||
protected WatchThread watchThread;
|
|
||||||
|
|
||||||
protected Predicate<String> ranges;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void init(Context context, AnyValue config) {
|
|
||||||
if (config != null) {
|
|
||||||
String rootstr = config.getValue("webroot", "root");
|
|
||||||
if (rootstr.indexOf(':') < 0 && rootstr.indexOf('/') != 0 && System.getProperty("APP_HOME") != null) {
|
|
||||||
rootstr = new File(System.getProperty("APP_HOME"), rootstr).getPath();
|
|
||||||
}
|
|
||||||
String rangesValue = config.getValue("ranges");
|
|
||||||
this.ranges = rangesValue != null ? Pattern.compile(rangesValue).asPredicate() : null;
|
|
||||||
try {
|
|
||||||
this.root = new File(rootstr).getCanonicalFile();
|
|
||||||
} catch (IOException ioe) {
|
|
||||||
this.root = new File(rootstr);
|
|
||||||
}
|
|
||||||
AnyValue cacheconf = config.getAnyValue("caches");
|
|
||||||
if (cacheconf != null) {
|
|
||||||
this.cachelimit = parseLenth(cacheconf.getValue("limit"), 128 * 1024 * 1024L);
|
|
||||||
this.cachelengthmax = parseLenth(cacheconf.getValue("lengthmax"), 1 * 1024 * 1024L);
|
|
||||||
}
|
|
||||||
List<SimpleEntry<Pattern, String>> locations = new ArrayList<>();
|
|
||||||
for (AnyValue av : config.getAnyValues("rewrite")) {
|
|
||||||
if ("location".equals(av.getValue("type"))) {
|
|
||||||
String m = av.getValue("match");
|
|
||||||
String f = av.getValue("forward");
|
|
||||||
if (m != null && f != null) {
|
|
||||||
locations.add(new SimpleEntry<>(Pattern.compile(m), f));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.locationRewrites = locations.isEmpty() ? null : locations.toArray(new SimpleEntry[locations.size()]);
|
|
||||||
}
|
|
||||||
if (this.cachelimit < 1) return; //不缓存不需要开启WatchThread监听
|
|
||||||
if (this.root != null) {
|
|
||||||
try {
|
|
||||||
this.watchThread = new WatchThread(this.root);
|
|
||||||
this.watchThread.start();
|
|
||||||
} catch (IOException ex) {
|
|
||||||
logger.log(Level.WARNING, HttpResourceServlet.class.getSimpleName() + " start watch-thread error", ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void destroy(Context context, AnyValue config) {
|
|
||||||
if (this.watchThread != null) {
|
|
||||||
try {
|
|
||||||
this.watchThread.watcher.close();
|
|
||||||
} catch (IOException ex) {
|
|
||||||
logger.log(Level.WARNING, HttpResourceServlet.class.getSimpleName() + " close watch-thread error", ex);
|
|
||||||
}
|
|
||||||
if (this.watchThread.isAlive()) this.watchThread.interrupt();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static long parseLenth(String value, long defValue) {
|
|
||||||
if (value == null) return defValue;
|
|
||||||
value = value.toUpperCase().replace("B", "");
|
|
||||||
if (value.endsWith("G")) return Long.decode(value.replace("G", "")) * 1024 * 1024 * 1024;
|
|
||||||
if (value.endsWith("M")) return Long.decode(value.replace("M", "")) * 1024 * 1024;
|
|
||||||
if (value.endsWith("K")) return Long.decode(value.replace("K", "")) * 1024;
|
|
||||||
return Long.decode(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void execute(HttpRequest request, HttpResponse response) throws IOException {
|
|
||||||
String uri = request.getRequestURI();
|
|
||||||
if (locationRewrites != null) {
|
|
||||||
for (SimpleEntry<Pattern, String> entry : locationRewrites) {
|
|
||||||
Matcher matcher = entry.getKey().matcher(uri);
|
|
||||||
if (matcher.find()) {
|
|
||||||
StringBuffer sb = new StringBuffer(uri.length());
|
|
||||||
matcher.appendReplacement(sb, entry.getValue());
|
|
||||||
matcher.appendTail(sb);
|
|
||||||
uri = sb.toString();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (uri.length() == 0 || uri.equals("/")) uri = "/index.html";
|
|
||||||
//System.out.println(request);
|
|
||||||
FileEntry entry;
|
|
||||||
if (watchThread == null) {
|
|
||||||
entry = createFileEntry(uri);
|
|
||||||
} else { //有缓存
|
|
||||||
entry = files.computeIfAbsent(uri, x -> createFileEntry(x));
|
|
||||||
}
|
|
||||||
if (entry == null) {
|
|
||||||
response.finish404();
|
|
||||||
} else {
|
|
||||||
response.finishFile(entry.file, entry.content);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private FileEntry createFileEntry(String uri) {
|
|
||||||
File file = new File(root, uri);
|
|
||||||
if (file.isDirectory()) file = new File(file, "index.html");
|
|
||||||
if (!file.isFile() || !file.canRead()) return null;
|
|
||||||
FileEntry en = new FileEntry(this, file);
|
|
||||||
if (watchThread == null) return en;
|
|
||||||
try {
|
|
||||||
Path p = file.getParentFile().toPath();
|
|
||||||
keymaps.put(p.register(watchThread.watcher, ENTRY_MODIFY, ENTRY_DELETE), p);
|
|
||||||
} catch (IOException e) {
|
|
||||||
logger.log(Level.INFO, HttpResourceServlet.class.getSimpleName() + " watch FileEntry(" + uri + ") erroneous", e);
|
|
||||||
}
|
|
||||||
return en;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static final class FileEntry {
|
|
||||||
|
|
||||||
final File file;
|
|
||||||
|
|
||||||
private final HttpResourceServlet servlet;
|
|
||||||
|
|
||||||
ByteBuffer content;
|
|
||||||
|
|
||||||
public FileEntry(final HttpResourceServlet servlet, File file) {
|
|
||||||
this.servlet = servlet;
|
|
||||||
this.file = file;
|
|
||||||
update();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void update() {
|
|
||||||
if (this.content != null) {
|
|
||||||
this.servlet.cachedLength.add(0L - this.content.remaining());
|
|
||||||
this.content = null;
|
|
||||||
}
|
|
||||||
long length = this.file.length();
|
|
||||||
if (length > this.servlet.cachelengthmax) return;
|
|
||||||
if (this.servlet.cachedLength.longValue() + length > this.servlet.cachelimit) return; //超过缓存总容量
|
|
||||||
try {
|
|
||||||
FileInputStream in = new FileInputStream(file);
|
|
||||||
ByteArrayOutputStream out = new ByteArrayOutputStream((int) file.length());
|
|
||||||
byte[] bytes = new byte[10240];
|
|
||||||
int pos;
|
|
||||||
while ((pos = in.read(bytes)) != -1) {
|
|
||||||
out.write(bytes, 0, pos);
|
|
||||||
}
|
|
||||||
in.close();
|
|
||||||
byte[] bs = out.toByteArray();
|
|
||||||
ByteBuffer buf = ByteBuffer.allocateDirect(bs.length);
|
|
||||||
buf.put(bs);
|
|
||||||
buf.flip();
|
|
||||||
this.content = buf.asReadOnlyBuffer();
|
|
||||||
this.servlet.cachedLength.add(this.content.remaining());
|
|
||||||
} catch (Exception e) {
|
|
||||||
logger.log(Level.INFO, HttpResourceServlet.class.getSimpleName() + " update FileEntry(" + file + ") erroneous", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void finalize() throws Throwable {
|
|
||||||
if (this.content != null) this.servlet.cachedLength.add(0L - this.content.remaining());
|
|
||||||
super.finalize();
|
|
||||||
}
|
|
||||||
|
|
||||||
public long getCachedLength() {
|
|
||||||
return this.content == null ? 0L : this.content.remaining();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,562 +0,0 @@
|
|||||||
/*
|
|
||||||
* To change this license header, choose License Headers in Project Properties.
|
|
||||||
* To change this template file, choose Tools | Templates
|
|
||||||
* and open the template in the editor.
|
|
||||||
*/
|
|
||||||
package org.redkale.net.http;
|
|
||||||
|
|
||||||
import org.redkale.util.AnyValue.DefaultAnyValue;
|
|
||||||
import org.redkale.util.AnyValue.Entry;
|
|
||||||
import java.io.*;
|
|
||||||
import java.lang.reflect.*;
|
|
||||||
import java.net.*;
|
|
||||||
import java.nio.*;
|
|
||||||
import java.nio.channels.*;
|
|
||||||
import java.nio.file.*;
|
|
||||||
import java.text.*;
|
|
||||||
import java.util.*;
|
|
||||||
import java.util.concurrent.atomic.*;
|
|
||||||
import org.redkale.net.*;
|
|
||||||
import org.redkale.util.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @see http://www.redkale.org
|
|
||||||
* @author zhangjx
|
|
||||||
* @param <R>
|
|
||||||
*/
|
|
||||||
public class HttpResponse<R extends HttpRequest> extends Response<R> {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* HttpResponse.finish 方法内调用
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public static interface Interceptor {
|
|
||||||
|
|
||||||
public ByteBuffer[] invoke(final HttpResponse response, final ByteBuffer[] buffers);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static final ByteBuffer buffer304 = ByteBuffer.wrap("HTTP/1.1 304 Not Modified\r\n\r\n".getBytes()).asReadOnlyBuffer();
|
|
||||||
|
|
||||||
private static final ByteBuffer buffer404 = ByteBuffer.wrap("HTTP/1.1 404 Not Found\r\nContent-Length:0\r\n\r\n".getBytes()).asReadOnlyBuffer();
|
|
||||||
|
|
||||||
protected static final byte[] LINE = new byte[]{'\r', '\n'};
|
|
||||||
|
|
||||||
private static final Set<OpenOption> options = new HashSet<>();
|
|
||||||
|
|
||||||
private static final DateFormat GMT_DATE_FORMAT = new SimpleDateFormat("EEE, dd-MMM-yyyy HH:mm:ss z", Locale.ENGLISH);
|
|
||||||
|
|
||||||
private static final Map<Integer, String> httpCodes = new HashMap<>();
|
|
||||||
|
|
||||||
static {
|
|
||||||
options.add(StandardOpenOption.READ);
|
|
||||||
GMT_DATE_FORMAT.setTimeZone(TimeZone.getTimeZone("GMT"));
|
|
||||||
|
|
||||||
httpCodes.put(100, "Continue");
|
|
||||||
httpCodes.put(101, "Switching Protocols");
|
|
||||||
|
|
||||||
httpCodes.put(200, "OK");
|
|
||||||
httpCodes.put(201, "Created");
|
|
||||||
httpCodes.put(202, "Accepted");
|
|
||||||
httpCodes.put(203, "Non-Authoritative Information");
|
|
||||||
httpCodes.put(204, "No Content");
|
|
||||||
httpCodes.put(205, "Reset Content");
|
|
||||||
httpCodes.put(206, "Partial Content");
|
|
||||||
|
|
||||||
httpCodes.put(300, "Multiple Choices");
|
|
||||||
httpCodes.put(301, "Moved Permanently");
|
|
||||||
httpCodes.put(302, "Found");
|
|
||||||
httpCodes.put(303, "See Other");
|
|
||||||
httpCodes.put(304, "Not Modified");
|
|
||||||
httpCodes.put(305, "Use Proxy");
|
|
||||||
httpCodes.put(307, "Temporary Redirect");
|
|
||||||
|
|
||||||
httpCodes.put(400, "Bad Request");
|
|
||||||
httpCodes.put(401, "Unauthorized");
|
|
||||||
httpCodes.put(402, "Payment Required");
|
|
||||||
httpCodes.put(403, "Forbidden");
|
|
||||||
httpCodes.put(404, "Not Found");
|
|
||||||
httpCodes.put(405, "Method Not Allowed");
|
|
||||||
httpCodes.put(406, "Not Acceptable");
|
|
||||||
httpCodes.put(407, "Proxy Authentication Required");
|
|
||||||
httpCodes.put(408, "Request Timeout");
|
|
||||||
httpCodes.put(409, "Conflict");
|
|
||||||
httpCodes.put(410, "Gone");
|
|
||||||
httpCodes.put(411, "Length Required");
|
|
||||||
httpCodes.put(412, "Precondition Failed");
|
|
||||||
httpCodes.put(413, "Request Entity Too Large");
|
|
||||||
httpCodes.put(414, "Request URI Too Long");
|
|
||||||
httpCodes.put(415, "Unsupported Media Type");
|
|
||||||
httpCodes.put(416, "Requested Range Not Satisfiable");
|
|
||||||
httpCodes.put(417, "Expectation Failed");
|
|
||||||
|
|
||||||
httpCodes.put(500, "Internal Server Error");
|
|
||||||
httpCodes.put(501, "Not Implemented");
|
|
||||||
httpCodes.put(502, "Bad Gateway");
|
|
||||||
httpCodes.put(503, "Service Unavailable");
|
|
||||||
httpCodes.put(504, "Gateway Timeout");
|
|
||||||
httpCodes.put(505, "HTTP Version Not Supported");
|
|
||||||
}
|
|
||||||
|
|
||||||
private int status = 200;
|
|
||||||
|
|
||||||
private String contentType = "text/plain; charset=utf-8";
|
|
||||||
|
|
||||||
private long contentLength = -1;
|
|
||||||
|
|
||||||
private HttpCookie[] cookies;
|
|
||||||
|
|
||||||
private boolean headsended = false;
|
|
||||||
|
|
||||||
private Interceptor interceptor;
|
|
||||||
//------------------------------------------------
|
|
||||||
|
|
||||||
private final DefaultAnyValue header = new DefaultAnyValue();
|
|
||||||
|
|
||||||
private final String[][] defaultAddHeaders;
|
|
||||||
|
|
||||||
private final String[][] defaultSetHeaders;
|
|
||||||
|
|
||||||
private final HttpCookie defcookie;
|
|
||||||
|
|
||||||
public static ObjectPool<Response> createPool(AtomicLong creatCounter, AtomicLong cycleCounter, int max, Creator<Response> creator) {
|
|
||||||
return new ObjectPool<>(creatCounter, cycleCounter, max, creator, (x) -> ((HttpResponse) x).prepare(), (x) -> ((HttpResponse) x).recycle());
|
|
||||||
}
|
|
||||||
|
|
||||||
public HttpResponse(Context context, R request, String[][] defaultAddHeaders, String[][] defaultSetHeaders, HttpCookie defcookie) {
|
|
||||||
super(context, request);
|
|
||||||
this.defaultAddHeaders = defaultAddHeaders;
|
|
||||||
this.defaultSetHeaders = defaultSetHeaders;
|
|
||||||
this.defcookie = defcookie;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected AsyncConnection removeChannel() {
|
|
||||||
return super.removeChannel();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected boolean recycle() {
|
|
||||||
this.status = 200;
|
|
||||||
this.contentLength = -1;
|
|
||||||
this.contentType = null;
|
|
||||||
this.cookies = null;
|
|
||||||
this.headsended = false;
|
|
||||||
this.header.clear();
|
|
||||||
this.interceptor = null;
|
|
||||||
return super.recycle();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void init(AsyncConnection channel) {
|
|
||||||
super.init(channel);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected String getHttpCode(int status) {
|
|
||||||
return httpCodes.get(status);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected HttpRequest getRequest() {
|
|
||||||
return request;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected String getHttpCode(int status, String defValue) {
|
|
||||||
String v = httpCodes.get(status);
|
|
||||||
return v == null ? defValue : v;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public HttpContext getContext() {
|
|
||||||
return (HttpContext) context;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addCookie(HttpCookie... cookies) {
|
|
||||||
if (this.cookies == null) {
|
|
||||||
this.cookies = cookies;
|
|
||||||
} else {
|
|
||||||
HttpCookie[] news = new HttpCookie[this.cookies.length + cookies.length];
|
|
||||||
System.arraycopy(this.cookies, 0, news, 0, this.cookies.length);
|
|
||||||
System.arraycopy(cookies, 0, news, this.cookies.length, cookies.length);
|
|
||||||
this.cookies = news;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void finishJson(Object obj) {
|
|
||||||
this.contentType = "text/plain; charset=utf-8";
|
|
||||||
finish(request.getJsonConvert().convertTo(context.getCharset(), context.getBufferSupplier(), obj));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void finishJson(Type type, Object obj) {
|
|
||||||
this.contentType = "text/plain; charset=utf-8";
|
|
||||||
finish(request.getJsonConvert().convertTo(context.getCharset(), context.getBufferSupplier(), type, obj));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void finishJson(Object... objs) {
|
|
||||||
this.contentType = "text/plain; charset=utf-8";
|
|
||||||
finish(request.getJsonConvert().convertTo(context.getCharset(), context.getBufferSupplier(), objs));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void finish(String obj) {
|
|
||||||
if (obj == null || obj.isEmpty()) {
|
|
||||||
final ByteBuffer headbuf = createHeader();
|
|
||||||
headbuf.flip();
|
|
||||||
super.finish(headbuf);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (context.getCharset() == null) {
|
|
||||||
if (interceptor != null) {
|
|
||||||
interceptor.invoke(this, new ByteBuffer[]{ByteBuffer.wrap(Utility.encodeUTF8(obj))});
|
|
||||||
}
|
|
||||||
final char[] chars = Utility.charArray(obj);
|
|
||||||
this.contentLength = Utility.encodeUTF8Length(chars);
|
|
||||||
final ByteBuffer headbuf = createHeader();
|
|
||||||
ByteBuffer buf2 = Utility.encodeUTF8(headbuf, (int) this.contentLength, chars);
|
|
||||||
headbuf.flip();
|
|
||||||
if (buf2 == null) {
|
|
||||||
super.finish(headbuf);
|
|
||||||
} else {
|
|
||||||
super.finish(headbuf, buf2);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
ByteBuffer buffer = context.getCharset().encode(obj);
|
|
||||||
if (interceptor != null) {
|
|
||||||
ByteBuffer[] bufs = interceptor.invoke(this, new ByteBuffer[]{buffer});
|
|
||||||
if (bufs != null) buffer = bufs[0];
|
|
||||||
}
|
|
||||||
this.contentLength = buffer.remaining();
|
|
||||||
final ByteBuffer headbuf = createHeader();
|
|
||||||
headbuf.flip();
|
|
||||||
super.finish(headbuf, buffer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void finish(int status, String message) {
|
|
||||||
this.status = status;
|
|
||||||
if (status != 200) super.refuseAlive();
|
|
||||||
finish(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void finish304() {
|
|
||||||
super.finish(buffer304.duplicate());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void finish404() {
|
|
||||||
super.finish(buffer404.duplicate());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void finish(ByteBuffer buffer) {
|
|
||||||
finish(false, buffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void finish(boolean kill, ByteBuffer buffer) {
|
|
||||||
if (!this.headsended) {
|
|
||||||
this.contentLength = buffer == null ? 0 : buffer.remaining();
|
|
||||||
ByteBuffer headbuf = createHeader();
|
|
||||||
headbuf.flip();
|
|
||||||
if (buffer == null) {
|
|
||||||
super.finish(kill, headbuf);
|
|
||||||
} else {
|
|
||||||
super.finish(kill, new ByteBuffer[]{headbuf, buffer});
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
super.finish(kill, buffer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void finish(ByteBuffer... buffers) {
|
|
||||||
finish(false, buffers);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void finish(boolean kill, ByteBuffer... buffers) {
|
|
||||||
if (interceptor != null) {
|
|
||||||
ByteBuffer[] bufs = interceptor.invoke(this, buffers);
|
|
||||||
if (bufs != null) buffers = bufs;
|
|
||||||
}
|
|
||||||
if (kill) refuseAlive();
|
|
||||||
if (!this.headsended) {
|
|
||||||
long len = 0;
|
|
||||||
for (ByteBuffer buf : buffers) {
|
|
||||||
len += buf.remaining();
|
|
||||||
}
|
|
||||||
this.contentLength = len;
|
|
||||||
ByteBuffer headbuf = createHeader();
|
|
||||||
headbuf.flip();
|
|
||||||
if (buffers == null) {
|
|
||||||
super.finish(kill, headbuf);
|
|
||||||
} else {
|
|
||||||
ByteBuffer[] newbuffers = new ByteBuffer[buffers.length + 1];
|
|
||||||
newbuffers[0] = headbuf;
|
|
||||||
System.arraycopy(buffers, 0, newbuffers, 1, buffers.length);
|
|
||||||
super.finish(kill, newbuffers);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
super.finish(kill, buffers);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public <A> void sendBody(ByteBuffer buffer, A attachment, CompletionHandler<Integer, A> handler) {
|
|
||||||
if (!this.headsended) {
|
|
||||||
if (this.contentLength < 0) this.contentLength = buffer == null ? 0 : buffer.remaining();
|
|
||||||
ByteBuffer headbuf = createHeader();
|
|
||||||
headbuf.flip();
|
|
||||||
if (buffer == null) {
|
|
||||||
super.send(headbuf, attachment, handler);
|
|
||||||
} else {
|
|
||||||
super.send(new ByteBuffer[]{headbuf, buffer}, attachment, handler);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
super.send(buffer, attachment, handler);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public <A> void finish(File file) throws IOException {
|
|
||||||
finishFile(file, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected <A> void finishFile(final File file, ByteBuffer fileBody) throws IOException {
|
|
||||||
if (file == null || !file.isFile() || !file.canRead()) {
|
|
||||||
finish404();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (fileBody != null) fileBody = fileBody.duplicate().asReadOnlyBuffer();
|
|
||||||
final long length = file.length();
|
|
||||||
final String match = request.getHeader("If-None-Match");
|
|
||||||
if (match != null && (file.lastModified() + "-" + length).equals(match)) {
|
|
||||||
finish304();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.contentLength = fileBody == null ? file.length() : fileBody.remaining();
|
|
||||||
this.contentType = MimeType.getByFilename(file.getName());
|
|
||||||
if (this.contentType == null) this.contentType = "application/octet-stream";
|
|
||||||
String range = request.getHeader("Range");
|
|
||||||
if (range != null && (!range.startsWith("bytes=") || range.indexOf(',') >= 0)) range = null;
|
|
||||||
long start = -1;
|
|
||||||
long len = -1;
|
|
||||||
if (range != null) {
|
|
||||||
range = range.substring("bytes=".length());
|
|
||||||
int pos = range.indexOf('-');
|
|
||||||
start = pos == 0 ? 0 : Integer.parseInt(range.substring(0, pos));
|
|
||||||
long end = (pos == range.length() - 1) ? -1 : Long.parseLong(range.substring(pos + 1));
|
|
||||||
long clen = end > 0 ? (end - start + 1) : (file.length() - start);
|
|
||||||
this.status = 206;
|
|
||||||
addHeader("Accept-Ranges", "bytes");
|
|
||||||
addHeader("Content-Range", "bytes " + start + "-" + (end > 0 ? end : length - 1) + "/" + length);
|
|
||||||
this.contentLength = clen;
|
|
||||||
len = end > 0 ? clen : end;
|
|
||||||
}
|
|
||||||
this.addHeader("ETag", file.lastModified() + "-" + length);
|
|
||||||
ByteBuffer hbuffer = createHeader();
|
|
||||||
hbuffer.flip();
|
|
||||||
if (fileBody == null) {
|
|
||||||
finishFile(hbuffer, file, start, len);
|
|
||||||
} else {
|
|
||||||
if (start >= 0) {
|
|
||||||
fileBody.position((int) start);
|
|
||||||
if (len > 0) fileBody.limit((int) (fileBody.position() + len));
|
|
||||||
}
|
|
||||||
super.finish(hbuffer, fileBody);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private <A> void finishFile(ByteBuffer hbuffer, File file, long offset, long length) throws IOException {
|
|
||||||
this.channel.write(hbuffer, hbuffer, new TransferFileHandler(AsynchronousFileChannel.open(file.toPath(), options, ((HttpContext) context).getExecutor()), offset, length));
|
|
||||||
}
|
|
||||||
|
|
||||||
private ByteBuffer createHeader() {
|
|
||||||
this.headsended = true;
|
|
||||||
ByteBuffer buffer = this.context.pollBuffer();
|
|
||||||
buffer.put(("HTTP/1.1 " + this.status + " " + (this.status == 200 ? "OK" : httpCodes.get(this.status)) + "\r\n").getBytes());
|
|
||||||
|
|
||||||
buffer.put(("Content-Type: " + (this.contentType == null ? "text/plain; charset=utf-8" : this.contentType) + "\r\n").getBytes());
|
|
||||||
|
|
||||||
if (this.contentLength > 0) {
|
|
||||||
buffer.put(("Content-Length: " + this.contentLength + "\r\n").getBytes());
|
|
||||||
}
|
|
||||||
if (!this.request.isKeepAlive()) {
|
|
||||||
buffer.put("Connection: close\r\n".getBytes());
|
|
||||||
}
|
|
||||||
if (this.defaultAddHeaders != null) {
|
|
||||||
for (String[] headers : this.defaultAddHeaders) {
|
|
||||||
if (headers.length > 2) {
|
|
||||||
String v = request.getHeader(headers[2]);
|
|
||||||
if (v != null) this.header.addValue(headers[0], v);
|
|
||||||
} else {
|
|
||||||
this.header.addValue(headers[0], headers[1]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (this.defaultSetHeaders != null) {
|
|
||||||
for (String[] headers : this.defaultSetHeaders) {
|
|
||||||
if (headers.length > 2) {
|
|
||||||
this.header.setValue(headers[0], request.getHeader(headers[2]));
|
|
||||||
} else {
|
|
||||||
this.header.setValue(headers[0], headers[1]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (Entry<String> en : this.header.getStringEntrys()) {
|
|
||||||
buffer.put((en.name + ": " + en.getValue() + "\r\n").getBytes());
|
|
||||||
}
|
|
||||||
if (request.newsessionid != null) {
|
|
||||||
String domain = defcookie == null ? null : defcookie.getDomain();
|
|
||||||
if (domain == null) {
|
|
||||||
domain = "";
|
|
||||||
} else {
|
|
||||||
domain = "Domain=" + domain + "; ";
|
|
||||||
}
|
|
||||||
String path = defcookie == null ? null : defcookie.getPath();
|
|
||||||
if (path == null) path = "/";
|
|
||||||
if (request.newsessionid.isEmpty()) {
|
|
||||||
buffer.put(("Set-Cookie: " + HttpRequest.SESSIONID_NAME + "=; " + domain + "Path=" + path + "; Max-Age=0; HttpOnly\r\n").getBytes());
|
|
||||||
} else {
|
|
||||||
buffer.put(("Set-Cookie: " + HttpRequest.SESSIONID_NAME + "=" + request.newsessionid + "; " + domain + "Path=" + path + "; HttpOnly\r\n").getBytes());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (this.cookies != null) {
|
|
||||||
for (HttpCookie cookie : this.cookies) {
|
|
||||||
if (cookie == null) continue;
|
|
||||||
if (defcookie != null) {
|
|
||||||
if (defcookie.getDomain() != null && cookie.getDomain() == null) cookie.setDomain(defcookie.getDomain());
|
|
||||||
if (defcookie.getPath() != null && cookie.getPath() == null) cookie.setPath(defcookie.getPath());
|
|
||||||
}
|
|
||||||
buffer.put(("Set-Cookie: " + genString(cookie) + "\r\n").getBytes());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
buffer.put(LINE);
|
|
||||||
return buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
private CharSequence genString(HttpCookie cookie) {
|
|
||||||
StringBuilder sb = new StringBuilder();
|
|
||||||
sb.append(cookie.getName()).append("=\"").append(cookie.getValue()).append('"').append("; Version=1");
|
|
||||||
if (cookie.getDomain() != null) sb.append("; Domain=").append(cookie.getDomain());
|
|
||||||
if (cookie.getPath() != null) sb.append("; Path=").append(cookie.getPath());
|
|
||||||
if (cookie.getPortlist() != null) sb.append("; Port=").append(cookie.getPortlist());
|
|
||||||
if (cookie.getMaxAge() > 0) {
|
|
||||||
sb.append("; Max-Age=").append(cookie.getMaxAge());
|
|
||||||
synchronized (GMT_DATE_FORMAT) {
|
|
||||||
sb.append("; Expires=").append(GMT_DATE_FORMAT.format(new Date(System.currentTimeMillis() + cookie.getMaxAge() * 1000)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (cookie.getSecure()) sb.append("; Secure");
|
|
||||||
if (cookie.isHttpOnly()) sb.append("; HttpOnly");
|
|
||||||
return sb;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void skipHeader() {
|
|
||||||
this.headsended = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected DefaultAnyValue duplicateHeader() {
|
|
||||||
return this.header.duplicate();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setHeader(String name, Object value) {
|
|
||||||
this.header.setValue(name, String.valueOf(value));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addHeader(String name, Object value) {
|
|
||||||
this.header.addValue(name, String.valueOf(value));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setStatus(int status) {
|
|
||||||
this.status = status;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getStatus() {
|
|
||||||
return this.status;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getContentType() {
|
|
||||||
return contentType;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setContentType(String contentType) {
|
|
||||||
this.contentType = contentType;
|
|
||||||
}
|
|
||||||
|
|
||||||
public long getContentLength() {
|
|
||||||
return contentLength;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setContentLength(long contentLength) {
|
|
||||||
this.contentLength = contentLength;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Interceptor getInterceptor() {
|
|
||||||
return interceptor;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setInterceptor(Interceptor interceptor) {
|
|
||||||
this.interceptor = interceptor;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected final class TransferFileHandler implements CompletionHandler<Integer, ByteBuffer> {
|
|
||||||
|
|
||||||
private final AsynchronousFileChannel filechannel;
|
|
||||||
|
|
||||||
private final long max; //需要读取的字节数, -1表示读到文件结尾
|
|
||||||
|
|
||||||
private long count;//读取文件的字节数
|
|
||||||
|
|
||||||
private long position = 0;
|
|
||||||
|
|
||||||
private boolean next = false;
|
|
||||||
|
|
||||||
private boolean read = true;
|
|
||||||
|
|
||||||
public TransferFileHandler(AsynchronousFileChannel channel) {
|
|
||||||
this.filechannel = channel;
|
|
||||||
this.max = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
public TransferFileHandler(AsynchronousFileChannel channel, long offset, long len) {
|
|
||||||
this.filechannel = channel;
|
|
||||||
this.position = offset <= 0 ? 0 : offset;
|
|
||||||
this.max = len;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void completed(Integer result, ByteBuffer attachment) {
|
|
||||||
if (result < 0 || (max > 0 && count >= max)) {
|
|
||||||
failed(null, attachment);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (read) {
|
|
||||||
read = false;
|
|
||||||
if (next) {
|
|
||||||
position += result;
|
|
||||||
} else {
|
|
||||||
next = true;
|
|
||||||
}
|
|
||||||
attachment.clear();
|
|
||||||
filechannel.read(attachment, position, attachment, this);
|
|
||||||
} else {
|
|
||||||
read = true;
|
|
||||||
if (max > 0) {
|
|
||||||
count += result;
|
|
||||||
if (count > max) {
|
|
||||||
attachment.limit((int) (attachment.position() + max - count));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
attachment.flip();
|
|
||||||
channel.write(attachment, attachment, this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void failed(Throwable exc, ByteBuffer attachment) {
|
|
||||||
getContext().offerBuffer(attachment);
|
|
||||||
finish(true);
|
|
||||||
try {
|
|
||||||
filechannel.close();
|
|
||||||
} catch (IOException e) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,128 +0,0 @@
|
|||||||
/*
|
|
||||||
* To change this license header, choose License Headers in Project Properties.
|
|
||||||
* To change this template file, choose Tools | Templates
|
|
||||||
* and open the template in the editor.
|
|
||||||
*/
|
|
||||||
package org.redkale.net.http;
|
|
||||||
|
|
||||||
import java.net.*;
|
|
||||||
import java.nio.*;
|
|
||||||
import java.util.*;
|
|
||||||
import java.util.concurrent.atomic.*;
|
|
||||||
import org.redkale.net.*;
|
|
||||||
import org.redkale.util.*;
|
|
||||||
import org.redkale.watch.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @see http://www.redkale.org
|
|
||||||
* @author zhangjx
|
|
||||||
*/
|
|
||||||
public final class HttpServer extends Server {
|
|
||||||
|
|
||||||
private String contextPath;
|
|
||||||
|
|
||||||
public HttpServer() {
|
|
||||||
this(System.currentTimeMillis(), null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public HttpServer(long serverStartTime, final WatchFactory watch) {
|
|
||||||
super(serverStartTime, "TCP", new HttpPrepareServlet(), watch);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void init(AnyValue config) throws Exception {
|
|
||||||
super.init(config);
|
|
||||||
AnyValue conf = config == null ? null : config.getAnyValue("servlets");
|
|
||||||
this.contextPath = conf == null ? "" : conf.getValue("prefix", "");
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addHttpServlet(HttpServlet servlet, AnyValue conf, String... mappings) {
|
|
||||||
((HttpPrepareServlet) this.prepare).addHttpServlet(servlet, conf, mappings);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
protected Context createContext() {
|
|
||||||
final int port = this.address.getPort();
|
|
||||||
AtomicLong createBufferCounter = watch == null ? new AtomicLong() : watch.createWatchNumber("HTTP_" + port + ".Buffer.creatCounter");
|
|
||||||
AtomicLong cycleBufferCounter = watch == null ? new AtomicLong() : watch.createWatchNumber("HTTP_" + port + ".Buffer.cycleCounter");
|
|
||||||
final int rcapacity = Math.max(this.capacity, 16 * 1024 + 8); //兼容 HTTP 2.0
|
|
||||||
ObjectPool<ByteBuffer> bufferPool = new ObjectPool<>(createBufferCounter, cycleBufferCounter, this.bufferPoolSize,
|
|
||||||
(Object... params) -> ByteBuffer.allocateDirect(rcapacity), null, (e) -> {
|
|
||||||
if (e == null || e.isReadOnly() || e.capacity() != rcapacity) return false;
|
|
||||||
e.clear();
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
final List<String[]> defaultAddHeaders = new ArrayList<>();
|
|
||||||
final List<String[]> defaultSetHeaders = new ArrayList<>();
|
|
||||||
HttpCookie defaultCookie = null;
|
|
||||||
String remoteAddrHeader = null;
|
|
||||||
if (config != null) {
|
|
||||||
AnyValue reqs = config == null ? null : config.getAnyValue("request");
|
|
||||||
if (reqs != null) {
|
|
||||||
AnyValue raddr = reqs.getAnyValue("remoteaddr");
|
|
||||||
remoteAddrHeader = raddr == null ? null : raddr.getValue("value");
|
|
||||||
if (remoteAddrHeader != null) {
|
|
||||||
if (remoteAddrHeader.startsWith("request.headers.")) {
|
|
||||||
remoteAddrHeader = remoteAddrHeader.substring("request.headers.".length());
|
|
||||||
} else {
|
|
||||||
remoteAddrHeader = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
AnyValue resps = config == null ? null : config.getAnyValue("response");
|
|
||||||
if (resps != null) {
|
|
||||||
AnyValue[] addHeaders = resps.getAnyValues("addheader");
|
|
||||||
if (addHeaders.length > 0) {
|
|
||||||
for (int i = 0; i < addHeaders.length; i++) {
|
|
||||||
String val = addHeaders[i].getValue("value");
|
|
||||||
if (val == null) continue;
|
|
||||||
if (val.startsWith("request.headers.")) {
|
|
||||||
defaultAddHeaders.add(new String[]{addHeaders[i].getValue("name"), val, val.substring("request.headers.".length())});
|
|
||||||
} else if (val.startsWith("system.property.")) {
|
|
||||||
String v = System.getProperty(val.substring("system.property.".length()));
|
|
||||||
if (v != null) defaultAddHeaders.add(new String[]{addHeaders[i].getValue("name"), v});
|
|
||||||
} else {
|
|
||||||
defaultAddHeaders.add(new String[]{addHeaders[i].getValue("name"), val});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
AnyValue[] setHeaders = resps.getAnyValues("setheader");
|
|
||||||
if (setHeaders.length > 0) {
|
|
||||||
for (int i = 0; i < setHeaders.length; i++) {
|
|
||||||
String val = setHeaders[i].getValue("value");
|
|
||||||
if (val != null && val.startsWith("request.headers.")) {
|
|
||||||
defaultSetHeaders.add(new String[]{setHeaders[i].getValue("name"), val, val.substring("request.headers.".length())});
|
|
||||||
} else {
|
|
||||||
defaultSetHeaders.add(new String[]{setHeaders[i].getValue("name"), val});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
AnyValue defcookieValue = resps.getAnyValue("defcookie");
|
|
||||||
if (defcookieValue != null) {
|
|
||||||
String domain = defcookieValue.getValue("domain");
|
|
||||||
String path = defcookieValue.getValue("path");
|
|
||||||
if (domain != null || path != null) {
|
|
||||||
defaultCookie = new HttpCookie("DEFAULTCOOKIE", "");
|
|
||||||
defaultCookie.setDomain(domain);
|
|
||||||
defaultCookie.setPath(path);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
final String[][] addHeaders = defaultAddHeaders.isEmpty() ? null : defaultAddHeaders.toArray(new String[defaultAddHeaders.size()][]);
|
|
||||||
final String[][] setHeaders = defaultSetHeaders.isEmpty() ? null : defaultSetHeaders.toArray(new String[defaultSetHeaders.size()][]);
|
|
||||||
final HttpCookie defCookie = defaultCookie;
|
|
||||||
final String addrHeader = remoteAddrHeader;
|
|
||||||
AtomicLong createResponseCounter = watch == null ? new AtomicLong() : watch.createWatchNumber("HTTP_" + port + ".Response.creatCounter");
|
|
||||||
AtomicLong cycleResponseCounter = watch == null ? new AtomicLong() : watch.createWatchNumber("HTTP_" + port + ".Response.cycleCounter");
|
|
||||||
ObjectPool<Response> responsePool = HttpResponse.createPool(createResponseCounter, cycleResponseCounter, this.responsePoolSize, null);
|
|
||||||
HttpContext httpcontext = new HttpContext(this.serverStartTime, this.logger, executor, rcapacity, bufferPool, responsePool,
|
|
||||||
this.maxbody, this.charset, this.address, this.prepare, this.watch, this.readTimeoutSecond, this.writeTimeoutSecond, contextPath);
|
|
||||||
responsePool.setCreator((Object... params) -> new HttpResponse(httpcontext, new HttpRequest(httpcontext, addrHeader), addHeaders, setHeaders, defCookie));
|
|
||||||
return httpcontext;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
/*
|
|
||||||
* To change this license header, choose License Headers in Project Properties.
|
|
||||||
* To change this template file, choose Tools | Templates
|
|
||||||
* and open the template in the editor.
|
|
||||||
*/
|
|
||||||
package org.redkale.net.http;
|
|
||||||
|
|
||||||
import org.redkale.net.Servlet;
|
|
||||||
import org.redkale.util.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @see http://www.redkale.org
|
|
||||||
* @author zhangjx
|
|
||||||
*/
|
|
||||||
public abstract class HttpServlet implements Servlet<HttpRequest, HttpResponse<HttpRequest>> {
|
|
||||||
|
|
||||||
AnyValue conf; //当前HttpServlet的配置
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public final boolean equals(Object obj) {
|
|
||||||
return obj != null && obj.getClass() == this.getClass();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public final int hashCode() {
|
|
||||||
return this.getClass().hashCode();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,199 +0,0 @@
|
|||||||
/*
|
|
||||||
* To change this license header, choose License Headers in Project Properties.
|
|
||||||
* To change this template file, choose Tools | Templates
|
|
||||||
* and open the template in the editor.
|
|
||||||
*/
|
|
||||||
package org.redkale.net.http;
|
|
||||||
|
|
||||||
import java.util.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @see http://www.redkale.org
|
|
||||||
* @author zhangjx
|
|
||||||
*/
|
|
||||||
public class MimeType {
|
|
||||||
|
|
||||||
private final static Map<String, String> contentTypes = new HashMap<>();
|
|
||||||
|
|
||||||
static {
|
|
||||||
contentTypes.put("abs", "audio/x-mpeg");
|
|
||||||
contentTypes.put("ai", "application/postscript");
|
|
||||||
contentTypes.put("aif", "audio/x-aiff");
|
|
||||||
contentTypes.put("aifc", "audio/x-aiff");
|
|
||||||
contentTypes.put("aiff", "audio/x-aiff");
|
|
||||||
contentTypes.put("aim", "application/x-aim");
|
|
||||||
contentTypes.put("art", "image/x-jg");
|
|
||||||
contentTypes.put("asf", "video/x-ms-asf");
|
|
||||||
contentTypes.put("asx", "video/x-ms-asf");
|
|
||||||
contentTypes.put("au", "audio/basic");
|
|
||||||
contentTypes.put("avi", "video/x-msvideo");
|
|
||||||
contentTypes.put("avx", "video/x-rad-screenplay");
|
|
||||||
contentTypes.put("bcpio", "application/x-bcpio");
|
|
||||||
contentTypes.put("bin", "application/octet-stream");
|
|
||||||
contentTypes.put("bmp", "image/bmp");
|
|
||||||
contentTypes.put("body", "text/html");
|
|
||||||
contentTypes.put("cdf", "application/x-cdf");
|
|
||||||
contentTypes.put("cer", "application/x-x509-ca-cert");
|
|
||||||
contentTypes.put("class", "application/java");
|
|
||||||
contentTypes.put("cpio", "application/x-cpio");
|
|
||||||
contentTypes.put("csh", "application/x-csh");
|
|
||||||
contentTypes.put("css", "text/css");
|
|
||||||
contentTypes.put("dib", "image/bmp");
|
|
||||||
contentTypes.put("doc", "application/msword");
|
|
||||||
contentTypes.put("dtd", "application/xml-dtd");
|
|
||||||
contentTypes.put("dv", "video/x-dv");
|
|
||||||
contentTypes.put("dvi", "application/x-dvi");
|
|
||||||
contentTypes.put("eps", "application/postscript");
|
|
||||||
contentTypes.put("etx", "text/x-setext");
|
|
||||||
contentTypes.put("exe", "application/octet-stream");
|
|
||||||
contentTypes.put("gif", "image/gif");
|
|
||||||
contentTypes.put("gk", "application/octet-stream");
|
|
||||||
contentTypes.put("gtar", "application/x-gtar");
|
|
||||||
contentTypes.put("gz", "application/x-gzip");
|
|
||||||
contentTypes.put("hdf", "application/x-hdf");
|
|
||||||
contentTypes.put("hqx", "application/mac-binhex40");
|
|
||||||
contentTypes.put("htc", "text/x-component");
|
|
||||||
contentTypes.put("htm", "text/html");
|
|
||||||
contentTypes.put("html", "text/html");
|
|
||||||
contentTypes.put("hqx", "application/mac-binhex40");
|
|
||||||
contentTypes.put("ief", "image/ief");
|
|
||||||
contentTypes.put("jad", "text/vnd.sun.j2me.app-descriptor");
|
|
||||||
contentTypes.put("jar", "application/java-archive");
|
|
||||||
contentTypes.put("java", "text/plain");
|
|
||||||
contentTypes.put("jnlp", "application/x-java-jnlp-file");
|
|
||||||
contentTypes.put("jpe", "image/jpeg");
|
|
||||||
contentTypes.put("jpeg", "image/jpeg");
|
|
||||||
contentTypes.put("jpg", "image/jpeg");
|
|
||||||
contentTypes.put("js", "text/javascript");
|
|
||||||
contentTypes.put("kar", "audio/x-midi");
|
|
||||||
contentTypes.put("latex", "application/x-latex");
|
|
||||||
contentTypes.put("log", "text/plain");
|
|
||||||
contentTypes.put("m3u", "audio/x-mpegurl");
|
|
||||||
contentTypes.put("mac", "image/x-macpaint");
|
|
||||||
contentTypes.put("man", "application/x-troff-man");
|
|
||||||
contentTypes.put("mathml", "application/mathml+xml");
|
|
||||||
contentTypes.put("me", "application/x-troff-me");
|
|
||||||
contentTypes.put("mid", "audio/x-midi");
|
|
||||||
contentTypes.put("midi", "audio/x-midi");
|
|
||||||
contentTypes.put("mif", "application/x-mif");
|
|
||||||
contentTypes.put("mov", "video/quicktime");
|
|
||||||
contentTypes.put("movie", "video/x-sgi-movie");
|
|
||||||
contentTypes.put("mp1", "audio/x-mpeg");
|
|
||||||
contentTypes.put("mp2", "audio/x-mpeg");
|
|
||||||
contentTypes.put("mp3", "audio/x-mpeg");
|
|
||||||
contentTypes.put("mpa", "audio/x-mpeg");
|
|
||||||
contentTypes.put("mp4", "video/mp4");
|
|
||||||
contentTypes.put("ogv", "video/ogv");
|
|
||||||
contentTypes.put("webm", "video/webm");
|
|
||||||
contentTypes.put("flv", "video/x-flv");
|
|
||||||
contentTypes.put("mpe", "video/mpeg");
|
|
||||||
contentTypes.put("mpeg", "video/mpeg");
|
|
||||||
contentTypes.put("mpega", "audio/x-mpeg");
|
|
||||||
contentTypes.put("mpg", "video/mpeg");
|
|
||||||
contentTypes.put("mpv2", "video/mpeg2");
|
|
||||||
contentTypes.put("ms", "application/x-wais-source");
|
|
||||||
contentTypes.put("nc", "application/x-netcdf");
|
|
||||||
contentTypes.put("oda", "application/oda");
|
|
||||||
contentTypes.put("ogg", "application/ogg");
|
|
||||||
contentTypes.put("out", "text/plain");
|
|
||||||
contentTypes.put("pbm", "image/x-portable-bitmap");
|
|
||||||
contentTypes.put("pct", "image/pict");
|
|
||||||
contentTypes.put("pdf", "application/pdf");
|
|
||||||
contentTypes.put("pgm", "image/x-portable-graymap");
|
|
||||||
contentTypes.put("pic", "image/pict");
|
|
||||||
contentTypes.put("pict", "image/pict");
|
|
||||||
contentTypes.put("pls", "audio/x-scpls");
|
|
||||||
contentTypes.put("png", "image/png");
|
|
||||||
contentTypes.put("pnm", "image/x-portable-anymap");
|
|
||||||
contentTypes.put("pnt", "image/x-macpaint");
|
|
||||||
contentTypes.put("ppm", "image/x-portable-pixmap");
|
|
||||||
contentTypes.put("ppt", "application/powerpoint");
|
|
||||||
contentTypes.put("ps", "application/postscript");
|
|
||||||
contentTypes.put("psd", "image/x-photoshop");
|
|
||||||
contentTypes.put("qt", "video/quicktime");
|
|
||||||
contentTypes.put("qti", "image/x-quicktime");
|
|
||||||
contentTypes.put("qtif", "image/x-quicktime");
|
|
||||||
contentTypes.put("ras", "image/x-cmu-raster");
|
|
||||||
contentTypes.put("rdf", "application/rdf+xml");
|
|
||||||
contentTypes.put("rgb", "image/x-rgb");
|
|
||||||
contentTypes.put("rm", "application/vnd.rn-realmedia");
|
|
||||||
contentTypes.put("roff", "application/x-troff");
|
|
||||||
contentTypes.put("rtf", "application/rtf");
|
|
||||||
contentTypes.put("rtx", "text/richtext");
|
|
||||||
contentTypes.put("sh", "application/x-sh");
|
|
||||||
contentTypes.put("shar", "application/x-shar");
|
|
||||||
contentTypes.put("shtml", "text/x-server-parsed-html");
|
|
||||||
contentTypes.put("sit", "application/x-stuffit");
|
|
||||||
contentTypes.put("smf", "audio/x-midi");
|
|
||||||
contentTypes.put("snd", "audio/basic");
|
|
||||||
contentTypes.put("src", "application/x-wais-source");
|
|
||||||
contentTypes.put("sv4cpio", "application/x-sv4cpio");
|
|
||||||
contentTypes.put("sv4crc", "application/x-sv4crc");
|
|
||||||
contentTypes.put("svg", "image/svg+xml");
|
|
||||||
contentTypes.put("svgz", "image/svg+xml");
|
|
||||||
contentTypes.put("swf", "application/x-shockwave-flash");
|
|
||||||
contentTypes.put("t", "application/x-troff");
|
|
||||||
contentTypes.put("tar", "application/x-tar");
|
|
||||||
contentTypes.put("tcl", "application/x-tcl");
|
|
||||||
contentTypes.put("tex", "application/x-tex");
|
|
||||||
contentTypes.put("texi", "application/x-texinfo");
|
|
||||||
contentTypes.put("texinfo", "application/x-texinfo");
|
|
||||||
contentTypes.put("tif", "image/tiff");
|
|
||||||
contentTypes.put("tiff", "image/tiff");
|
|
||||||
contentTypes.put("tr", "application/x-troff");
|
|
||||||
contentTypes.put("tsv", "text/tab-separated-values");
|
|
||||||
contentTypes.put("txt", "text/plain");
|
|
||||||
contentTypes.put("ulw", "audio/basic");
|
|
||||||
contentTypes.put("ustar", "application/x-ustar");
|
|
||||||
contentTypes.put("xbm", "image/x-xbitmap");
|
|
||||||
contentTypes.put("xml", "application/xml");
|
|
||||||
contentTypes.put("xpm", "image/x-xpixmap");
|
|
||||||
contentTypes.put("xsl", "application/xml");
|
|
||||||
contentTypes.put("xslt", "application/xslt+xml");
|
|
||||||
contentTypes.put("xwd", "image/x-xwindowdump");
|
|
||||||
contentTypes.put("vsd", "application/x-visio");
|
|
||||||
contentTypes.put("vxml", "application/voicexml+xml");
|
|
||||||
contentTypes.put("wav", "audio/x-wav");
|
|
||||||
contentTypes.put("wbmp", "image/vnd.wap.wbmp");
|
|
||||||
contentTypes.put("wml", "text/vnd.wap.wml");
|
|
||||||
contentTypes.put("wmlc", "application/vnd.wap.wmlc");
|
|
||||||
contentTypes.put("wmls", "text/vnd.wap.wmls");
|
|
||||||
contentTypes.put("wmlscriptc", "application/vnd.wap.wmlscriptc");
|
|
||||||
contentTypes.put("wrl", "x-world/x-vrml");
|
|
||||||
contentTypes.put("xht", "application/xhtml+xml");
|
|
||||||
contentTypes.put("xhtml", "application/xhtml+xml");
|
|
||||||
contentTypes.put("xls", "application/vnd.ms-excel");
|
|
||||||
contentTypes.put("xul", "application/vnd.mozilla.xul+xml");
|
|
||||||
contentTypes.put("Z", "application/x-compress");
|
|
||||||
contentTypes.put("z", "application/x-compress");
|
|
||||||
contentTypes.put("zip", "application/zip");
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String get(String extension) {
|
|
||||||
return contentTypes.getOrDefault(extension, "text/plain");
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String get(String extension, String defaultCt) {
|
|
||||||
return contentTypes.getOrDefault(extension, defaultCt);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean contains(String extension) {
|
|
||||||
return contentTypes.containsKey(extension);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void add(String extension, String contentType) {
|
|
||||||
if (extension != null && extension.length() != 0 && contentType != null && contentType.length() != 0) {
|
|
||||||
contentTypes.put(extension, contentType);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String getByFilename(String fileName) {
|
|
||||||
int length = fileName.length();
|
|
||||||
int newEnd = fileName.lastIndexOf('#');
|
|
||||||
if (newEnd == -1) newEnd = length;
|
|
||||||
int i = fileName.lastIndexOf('.', newEnd);
|
|
||||||
return (i < 0) ? null : get(fileName.substring(i + 1, newEnd));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,285 +0,0 @@
|
|||||||
/*
|
|
||||||
* To change this license header, choose License Headers input Project Properties.
|
|
||||||
* To change this template file, choose Tools | Templates
|
|
||||||
* and open the template input the editor.
|
|
||||||
*/
|
|
||||||
package org.redkale.net.http;
|
|
||||||
|
|
||||||
import org.redkale.util.ByteArray;
|
|
||||||
import java.io.*;
|
|
||||||
import java.nio.charset.*;
|
|
||||||
import java.util.*;
|
|
||||||
import java.util.concurrent.atomic.*;
|
|
||||||
import java.util.logging.*;
|
|
||||||
import java.util.regex.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @see http://www.redkale.org
|
|
||||||
* @author zhangjx
|
|
||||||
*/
|
|
||||||
public final class MultiContext {
|
|
||||||
|
|
||||||
private static final Logger logger = Logger.getLogger(MultiContext.class.getSimpleName());
|
|
||||||
|
|
||||||
private static final Charset UTF8 = Charset.forName("UTF-8");
|
|
||||||
|
|
||||||
private final String contentType;
|
|
||||||
|
|
||||||
private final InputStream in;
|
|
||||||
|
|
||||||
private final Charset charset;
|
|
||||||
|
|
||||||
private final String boundary;
|
|
||||||
|
|
||||||
private final byte[] endboundarray;
|
|
||||||
|
|
||||||
private final ByteArray buf = new ByteArray(64);
|
|
||||||
|
|
||||||
private final Map<String, String> parameters = new HashMap<>();
|
|
||||||
|
|
||||||
private final Pattern fielnamePattern;
|
|
||||||
|
|
||||||
private static final Iterable<MultiPart> emptyIterable = () -> new Iterator<MultiPart>() {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean hasNext() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public MultiPart next() {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
public MultiContext(final String contentType, final InputStream in) {
|
|
||||||
this(null, contentType, in);
|
|
||||||
}
|
|
||||||
|
|
||||||
public MultiContext(final Charset charsetName, final String contentType, final InputStream in) {
|
|
||||||
this(charsetName, contentType, in, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public MultiContext(final String contentType, final InputStream in, String extregex) {
|
|
||||||
this(null, contentType, in, extregex);
|
|
||||||
}
|
|
||||||
|
|
||||||
public MultiContext(final Charset charsetName, final String contentType, final InputStream in, String fielnameRegex) {
|
|
||||||
this.charset = charsetName == null ? UTF8 : charsetName;
|
|
||||||
this.contentType = contentType.trim();
|
|
||||||
this.boundary = parseBoundary(this.contentType);
|
|
||||||
this.endboundarray = ("--" + this.boundary + "--").getBytes();
|
|
||||||
this.in = in instanceof BufferedInputStream ? in : new BufferedInputStream(in);
|
|
||||||
this.fielnamePattern = fielnameRegex == null || fielnameRegex.isEmpty() ? null : Pattern.compile(fielnameRegex);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Map<String, String> getParameters() {
|
|
||||||
return parameters;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getParameter(String name) {
|
|
||||||
return getParameters().get(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
public final String getParameter(String name, String defaultValue) {
|
|
||||||
String value = this.getParameter(name);
|
|
||||||
return value == null ? defaultValue : value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final int getIntParameter(String name, int defaultValue) {
|
|
||||||
String value = this.getParameter(name);
|
|
||||||
try {
|
|
||||||
return value == null ? defaultValue : Integer.decode(value);
|
|
||||||
} catch (NumberFormatException e) {
|
|
||||||
return defaultValue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public final long getLongParameter(String name, long defaultValue) {
|
|
||||||
String value = this.getParameter(name);
|
|
||||||
try {
|
|
||||||
return value == null ? defaultValue : Long.decode(value);
|
|
||||||
} catch (NumberFormatException e) {
|
|
||||||
return defaultValue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private String parseBoundary(String contentType) {
|
|
||||||
if (!contentType.startsWith("multipart/")) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
for (String str : contentType.split(";")) {
|
|
||||||
int pos = str.indexOf("boundary=");
|
|
||||||
if (pos >= 0) return str.substring(pos + "boundary=".length()).trim();
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isMultipart() {
|
|
||||||
return this.boundary != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Iterable<MultiPart> listMultiPart() throws IOException {
|
|
||||||
if (!isMultipart()) return emptyIterable;
|
|
||||||
final boolean debug = true;
|
|
||||||
final String boundarystr = "--" + this.boundary;
|
|
||||||
final Pattern fielnameReg = this.fielnamePattern;
|
|
||||||
final String endboundary = boundarystr + "--";
|
|
||||||
final byte[] boundarray = ("\n" + boundarystr).getBytes();
|
|
||||||
final byte[] buffer = new byte[boundarray.length];
|
|
||||||
final InputStream input = this.in;
|
|
||||||
final Map<String, String> params = this.parameters;
|
|
||||||
final AtomicBoolean finaled = new AtomicBoolean(false);
|
|
||||||
return () -> new Iterator<MultiPart>() {
|
|
||||||
|
|
||||||
private String boundaryline;
|
|
||||||
|
|
||||||
private MultiPart lastentry;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean hasNext() {
|
|
||||||
try {
|
|
||||||
if (lastentry != null) {
|
|
||||||
lastentry.skip();
|
|
||||||
if (finaled.get()) return false;
|
|
||||||
}
|
|
||||||
if (boundaryline == null) boundaryline = readBoundary();
|
|
||||||
//if (debug) System.out.print("boundaryline=" + boundaryline + " ");
|
|
||||||
if (endboundary.equals(boundaryline) || !boundarystr.equals(boundaryline)) { //结尾或异常
|
|
||||||
lastentry = null;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
final String disposition = readLine();
|
|
||||||
//if (debug) System.out.println("disposition=" + disposition);
|
|
||||||
if (disposition.contains("; filename=\"")) { //是上传文件
|
|
||||||
String contentType = readLine();
|
|
||||||
//if (debug) System.out.println("file.contentType=" + contentType);
|
|
||||||
contentType = contentType.substring(contentType.indexOf(':') + 1).trim();
|
|
||||||
readLine(); //读掉空白行
|
|
||||||
String name = parseValue(disposition, "name");
|
|
||||||
String filename = parseValue(disposition, "filename");
|
|
||||||
if (filename == null || filename.isEmpty()) { //没有上传
|
|
||||||
readLine(); //读掉空白行
|
|
||||||
this.boundaryline = null;
|
|
||||||
this.lastentry = null;
|
|
||||||
return this.hasNext();
|
|
||||||
} else {
|
|
||||||
int p1 = filename.lastIndexOf('/');
|
|
||||||
if (p1 < 0) p1 = filename.lastIndexOf('\\');
|
|
||||||
if (p1 >= 0) filename = filename.substring(p1 + 1);
|
|
||||||
}
|
|
||||||
final AtomicLong counter = new AtomicLong(0);
|
|
||||||
InputStream source = new InputStream() {
|
|
||||||
|
|
||||||
private int bufposition = buffer.length;
|
|
||||||
|
|
||||||
private boolean end;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int read() throws IOException {
|
|
||||||
if (end) return -1;
|
|
||||||
final byte[] buf = buffer;
|
|
||||||
int ch = (this.bufposition < buf.length) ? (buf[this.bufposition++] & 0xff) : input.read();
|
|
||||||
if ((ch == '\r' && readBuffer())) return -1;
|
|
||||||
counter.incrementAndGet();
|
|
||||||
return ch;
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean readBuffer() throws IOException {
|
|
||||||
final byte[] buf = buffer;
|
|
||||||
final int pos = this.bufposition;
|
|
||||||
int s = 0;
|
|
||||||
for (int i = pos; i < buf.length; i++) {
|
|
||||||
buf[s++] = buf[i];
|
|
||||||
}
|
|
||||||
int readed = 0;
|
|
||||||
while ((readed += input.read(buf, s + readed, pos - readed)) != pos);
|
|
||||||
this.bufposition = 0;
|
|
||||||
if (Arrays.equals(boundarray, buf)) {
|
|
||||||
this.end = true;
|
|
||||||
int c1 = input.read();
|
|
||||||
int c2 = input.read();
|
|
||||||
finaled.set(c1 == '-' && c2 == '-');
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public long skip(long count) throws IOException {
|
|
||||||
if (end) return -1;
|
|
||||||
if (count <= 0) return 0;
|
|
||||||
long s = 0;
|
|
||||||
while (read() != -1) {
|
|
||||||
s++;
|
|
||||||
if (--count <= 0) break;
|
|
||||||
}
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
this.lastentry = new MultiPart(filename, name, contentType, counter, source);
|
|
||||||
if (fielnameReg != null && !fielnameReg.matcher(filename).matches()) {
|
|
||||||
return this.hasNext();
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
} else { //不是文件
|
|
||||||
readLine(); //读掉空白
|
|
||||||
params.put(parseValue(disposition, "name"), readLine());
|
|
||||||
this.boundaryline = null;
|
|
||||||
this.lastentry = null;
|
|
||||||
return this.hasNext();
|
|
||||||
}
|
|
||||||
} catch (IOException ex) {
|
|
||||||
logger.log(Level.FINER, "listMultiPart abort", ex);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public MultiPart next() {
|
|
||||||
return lastentry;
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private String readLine() throws IOException {
|
|
||||||
return readLine(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
private String readBoundary() throws IOException {
|
|
||||||
return readLine(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
private String readLine(boolean bd) throws IOException { // bd : 是否是读取boundary
|
|
||||||
byte lasted = '\r';
|
|
||||||
buf.clear();
|
|
||||||
final int bc = this.endboundarray.length;
|
|
||||||
int c = 0;
|
|
||||||
for (;;) {
|
|
||||||
int b = in.read();
|
|
||||||
c++;
|
|
||||||
if (b == -1 || (lasted == '\r' && b == '\n')) break;
|
|
||||||
if (lasted != '\r') buf.add(lasted);
|
|
||||||
lasted = (byte) b;
|
|
||||||
if (bd && bc == c) {
|
|
||||||
buf.add(lasted);
|
|
||||||
if (buf.equal(this.endboundarray)) break;
|
|
||||||
buf.removeLastByte();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (buf.count() == 0) return "";
|
|
||||||
return buf.toString(this.charset).trim();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String parseValue(final String str, String name) {
|
|
||||||
if (str == null) return null;
|
|
||||||
final String key = "; " + name + "=\"";
|
|
||||||
int pos = str.indexOf(key);
|
|
||||||
if (pos < 0) return null;
|
|
||||||
String sub = str.substring(pos + key.length());
|
|
||||||
return sub.substring(0, sub.indexOf('"'));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,116 +0,0 @@
|
|||||||
/*
|
|
||||||
* To change this license header, choose License Headers in Project Properties.
|
|
||||||
* To change this template file, choose Tools | Templates
|
|
||||||
* and open the template in the editor.
|
|
||||||
*/
|
|
||||||
package org.redkale.net.http;
|
|
||||||
|
|
||||||
import java.io.*;
|
|
||||||
import java.util.concurrent.atomic.AtomicLong;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @see http://www.redkale.org
|
|
||||||
* @author zhangjx
|
|
||||||
*/
|
|
||||||
public final class MultiPart {
|
|
||||||
|
|
||||||
private final String filename;
|
|
||||||
|
|
||||||
private final String name;
|
|
||||||
|
|
||||||
private final String contentType;
|
|
||||||
|
|
||||||
private final InputStream in;
|
|
||||||
|
|
||||||
private final AtomicLong received;
|
|
||||||
|
|
||||||
MultiPart(String filename, String name, String contentType, AtomicLong received, InputStream in) {
|
|
||||||
this.filename = filename;
|
|
||||||
this.name = name;
|
|
||||||
this.in = in;
|
|
||||||
this.contentType = contentType;
|
|
||||||
this.received = received;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return this.getClass().getSimpleName() + "{" + "name=" + name + ", filename=" + filename + ", contentType=" + contentType + ", received=" + received + '}';
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean save(File file) throws IOException {
|
|
||||||
return save(Long.MAX_VALUE, file);
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean save(long max, File file) throws IOException {
|
|
||||||
OutputStream out = new FileOutputStream(file);
|
|
||||||
boolean rs = save(max, out);
|
|
||||||
out.close();
|
|
||||||
return rs;
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte[] getContentBytes() throws IOException {
|
|
||||||
return getContentBytes(Long.MAX_VALUE);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 将文件流读进bytes, 如果超出max指定的值则返回null
|
|
||||||
*
|
|
||||||
* @param max 最大长度限制
|
|
||||||
* @return
|
|
||||||
* @throws IOException
|
|
||||||
*/
|
|
||||||
public byte[] getContentBytes(long max) throws IOException {
|
|
||||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
|
||||||
return save(max, out) ? out.toByteArray() : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean save(OutputStream out) throws IOException {
|
|
||||||
return save(Long.MAX_VALUE, out);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 将文件流写进out, 如果超出max指定的值则中断并返回false
|
|
||||||
*
|
|
||||||
* @param max 最大长度限制
|
|
||||||
* @param out
|
|
||||||
* @return
|
|
||||||
* @throws IOException
|
|
||||||
*/
|
|
||||||
public boolean save(long max, OutputStream out) throws IOException {
|
|
||||||
byte[] bytes = new byte[4096];
|
|
||||||
int pos;
|
|
||||||
InputStream in0 = this.getInputStream();
|
|
||||||
while ((pos = in0.read(bytes)) != -1) {
|
|
||||||
if (max < 0) return false;
|
|
||||||
out.write(bytes, 0, pos);
|
|
||||||
max -= pos;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getContentType() {
|
|
||||||
return contentType;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getFilename() {
|
|
||||||
return filename;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getName() {
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
|
|
||||||
public InputStream getInputStream() {
|
|
||||||
return in;
|
|
||||||
}
|
|
||||||
|
|
||||||
public long getReceived() {
|
|
||||||
return received.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void skip() throws IOException {
|
|
||||||
in.skip(Long.MAX_VALUE);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
/*
|
|
||||||
* To change this license header, choose License Headers in Project Properties.
|
|
||||||
* To change this template file, choose Tools | Templates
|
|
||||||
* and open the template in the editor.
|
|
||||||
*/
|
|
||||||
package org.redkale.net.http;
|
|
||||||
|
|
||||||
import java.lang.annotation.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 功能同JSR 315 (java-servlet 3.0) 规范中的 @WebInitParam
|
|
||||||
*
|
|
||||||
* @see http://www.redkale.org
|
|
||||||
* @author zhangjx
|
|
||||||
*/
|
|
||||||
@Target({ElementType.TYPE})
|
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
|
||||||
@Documented
|
|
||||||
public @interface WebInitParam {
|
|
||||||
|
|
||||||
String name();
|
|
||||||
|
|
||||||
String value();
|
|
||||||
|
|
||||||
String description() default "";
|
|
||||||
}
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
/*
|
|
||||||
* To change this license header, choose License Headers in Project Properties.
|
|
||||||
* To change this template file, choose Tools | Templates
|
|
||||||
* and open the template in the editor.
|
|
||||||
*/
|
|
||||||
package org.redkale.net.http;
|
|
||||||
|
|
||||||
import java.lang.annotation.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 功能同JSR 315 (java-servlet 3.0) 规范中的 @WebServlet
|
|
||||||
*
|
|
||||||
* @see http://www.redkale.org
|
|
||||||
* @author zhangjx
|
|
||||||
*/
|
|
||||||
@Documented
|
|
||||||
@Target({ElementType.TYPE})
|
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
|
||||||
public @interface WebServlet {
|
|
||||||
|
|
||||||
String name() default "";
|
|
||||||
|
|
||||||
boolean fillurl() default true;
|
|
||||||
|
|
||||||
String[] value() default {};
|
|
||||||
|
|
||||||
int moduleid() default 0;
|
|
||||||
|
|
||||||
WebInitParam[] initParams() default {};
|
|
||||||
}
|
|
||||||
@@ -1,388 +0,0 @@
|
|||||||
/*
|
|
||||||
* To change this license header, choose License Headers in Project Properties.
|
|
||||||
* To change this template file, choose Tools | Templates
|
|
||||||
* and open the template in the editor.
|
|
||||||
*/
|
|
||||||
package org.redkale.net.http;
|
|
||||||
|
|
||||||
import org.redkale.net.http.WebSocketPacket.FrameType;
|
|
||||||
import java.io.*;
|
|
||||||
import java.util.*;
|
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
|
||||||
import org.redkale.net.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 一个WebSocket连接对应一个WebSocket实体,即一个WebSocket会绑定一个TCP连接。
|
|
||||||
* WebSocket 有两种模式:
|
|
||||||
* 1) 普通模式: 协议上符合HTML5规范, 其流程顺序如下:
|
|
||||||
* 1.1 onOpen 如果方法返回null,则视为WebSocket的连接不合法,框架会强制关闭WebSocket连接;通常用于判断登录态。
|
|
||||||
* 1.2 createGroupid 如果方法返回null,则视为WebSocket的连接不合法,框架会强制关闭WebSocket连接;通常用于判断用户权限是否符合。
|
|
||||||
* 1.3 onConnected WebSocket成功连接后在准备接收数据前回调此方法。
|
|
||||||
* 1.4 onMessage/onFragment+ WebSocket接收到消息后回调此消息类方法。
|
|
||||||
* 1.5 onClose WebSocket被关闭后回调此方法。
|
|
||||||
*
|
|
||||||
* 此模式下 以上方法都应该被重载。
|
|
||||||
*
|
|
||||||
* 2) 原始二进制模式: 此模式有别于HTML5规范,可以视为原始的TCP连接。通常用于音频视频通讯场景。期流程顺序如下:
|
|
||||||
* 2.1 onOpen 如果方法返回null,则视为WebSocket的连接不合法,框架会强制关闭WebSocket连接;通常用于判断登录态。
|
|
||||||
* 2.2 createGroupid 如果方法返回null,则视为WebSocket的连接不合法,框架会强制关闭WebSocket连接;通常用于判断用户权限是否符合。
|
|
||||||
* 2.3 onRead WebSocket成功连接后回调此方法, 由此方法处理原始的TCP连接, 同时业务代码去控制WebSocket的关闭。
|
|
||||||
*
|
|
||||||
* 此模式下 以上方法都应该被重载。
|
|
||||||
* <p>
|
|
||||||
*
|
|
||||||
* @see http://www.redkale.org
|
|
||||||
* @author zhangjx
|
|
||||||
*/
|
|
||||||
public abstract class WebSocket {
|
|
||||||
|
|
||||||
//消息不合法
|
|
||||||
public static final int RETCODE_SEND_ILLPACKET = 1 << 1; //2
|
|
||||||
|
|
||||||
//ws已经关闭
|
|
||||||
public static final int RETCODE_WSOCKET_CLOSED = 1 << 2; //4
|
|
||||||
|
|
||||||
//socket的buffer不合法
|
|
||||||
public static final int RETCODE_ILLEGALBUFFER = 1 << 3; //8
|
|
||||||
|
|
||||||
//ws发送消息异常
|
|
||||||
public static final int RETCODE_SENDEXCEPTION = 1 << 4; //16
|
|
||||||
|
|
||||||
public static final int RETCODE_ENGINE_NULL = 1 << 5; //32
|
|
||||||
|
|
||||||
public static final int RETCODE_NODESERVICE_NULL = 1 << 6; //64
|
|
||||||
|
|
||||||
public static final int RETCODE_GROUP_EMPTY = 1 << 7; //128
|
|
||||||
|
|
||||||
public static final int RETCODE_WSOFFLINE = 1 << 8; //256
|
|
||||||
|
|
||||||
WebSocketRunner runner;
|
|
||||||
|
|
||||||
WebSocketEngine engine;
|
|
||||||
|
|
||||||
WebSocketGroup group;
|
|
||||||
|
|
||||||
WebSocketNode node;
|
|
||||||
|
|
||||||
Serializable sessionid;
|
|
||||||
|
|
||||||
Serializable groupid;
|
|
||||||
|
|
||||||
private final long createtime = System.currentTimeMillis();
|
|
||||||
|
|
||||||
private final Map<String, Object> attributes = new ConcurrentHashMap<>();
|
|
||||||
|
|
||||||
protected WebSocket() {
|
|
||||||
}
|
|
||||||
|
|
||||||
//----------------------------------------------------------------
|
|
||||||
/**
|
|
||||||
* 发送消息体, 包含二进制/文本
|
|
||||||
* <p>
|
|
||||||
* @param packet
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
public final int send(WebSocketPacket packet) {
|
|
||||||
if (this.runner != null) return this.runner.sendMessage(packet);
|
|
||||||
return RETCODE_WSOCKET_CLOSED;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 显式地关闭WebSocket
|
|
||||||
*/
|
|
||||||
public final void close() {
|
|
||||||
if (this.runner != null) this.runner.closeRunner();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 发送单一的文本消息
|
|
||||||
* <p>
|
|
||||||
* @param text 不可为空
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
public final int send(String text) {
|
|
||||||
return send(text, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 发送文本消息
|
|
||||||
* <p>
|
|
||||||
* @param text 不可为空
|
|
||||||
* @param last 是否最后一条
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
public final int send(String text, boolean last) {
|
|
||||||
return send(new WebSocketPacket(text, last));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 发送单一的二进制消息
|
|
||||||
* <p>
|
|
||||||
* @param data
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
public final int send(byte[] data) {
|
|
||||||
return send(data, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
public final int sendPing(byte[] data) {
|
|
||||||
return send(new WebSocketPacket(FrameType.PING, data));
|
|
||||||
}
|
|
||||||
|
|
||||||
public final int sendPong(byte[] data) {
|
|
||||||
return send(new WebSocketPacket(FrameType.PONG, data));
|
|
||||||
}
|
|
||||||
|
|
||||||
public final long getCreatetime() {
|
|
||||||
return createtime;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 发送二进制消息
|
|
||||||
* <p>
|
|
||||||
* @param data 不可为空
|
|
||||||
* @param last 是否最后一条
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
public final int send(byte[] data, boolean last) {
|
|
||||||
return send(new WebSocketPacket(data, last));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 发送消息, 消息类型是String或byte[]
|
|
||||||
* <p>
|
|
||||||
* @param message 不可为空, 只能是String或者byte[]
|
|
||||||
* @param last 是否最后一条
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
public final int send(Serializable message, boolean last) {
|
|
||||||
return send(new WebSocketPacket(message, last));
|
|
||||||
}
|
|
||||||
|
|
||||||
//----------------------------------------------------------------
|
|
||||||
/**
|
|
||||||
* 给指定groupid的WebSocketGroup下所有WebSocket节点发送文本消息
|
|
||||||
* <p>
|
|
||||||
* @param groupid
|
|
||||||
* @param text 不可为空
|
|
||||||
* @return 为0表示成功, 其他值表示异常
|
|
||||||
*/
|
|
||||||
public final int sendEachMessage(Serializable groupid, String text) {
|
|
||||||
return sendEachMessage(groupid, text, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 给指定groupid的WebSocketGroup下所有WebSocket节点发送二进制消息
|
|
||||||
* <p>
|
|
||||||
* @param groupid
|
|
||||||
* @param data 不可为空
|
|
||||||
* @return 为0表示成功, 其他值表示异常
|
|
||||||
*/
|
|
||||||
public final int sendEachMessage(Serializable groupid, byte[] data) {
|
|
||||||
return WebSocket.this.sendEachMessage(groupid, data, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 给指定groupid的WebSocketGroup下所有WebSocket节点发送文本消息
|
|
||||||
* <p>
|
|
||||||
* @param groupid
|
|
||||||
* @param text 不可为空
|
|
||||||
* @param last
|
|
||||||
* @return 为0表示成功, 其他值表示异常
|
|
||||||
*/
|
|
||||||
public final int sendEachMessage(Serializable groupid, String text, boolean last) {
|
|
||||||
return sendMessage(groupid, false, text, last);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 给指定groupid的WebSocketGroup下所有WebSocket节点发送二进制消息
|
|
||||||
* <p>
|
|
||||||
* @param groupid
|
|
||||||
* @param data 不可为空
|
|
||||||
* @param last 是否最后一条
|
|
||||||
* @return 为0表示成功, 其他值表示异常
|
|
||||||
*/
|
|
||||||
public final int sendEachMessage(Serializable groupid, byte[] data, boolean last) {
|
|
||||||
return sendMessage(groupid, false, data, last);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 给指定groupid的WebSocketGroup下最近活跃的WebSocket节点发送文本消息
|
|
||||||
* <p>
|
|
||||||
* @param groupid
|
|
||||||
* @param text 不可为空
|
|
||||||
* @return 为0表示成功, 其他值表示异常
|
|
||||||
*/
|
|
||||||
public final int sendRecentMessage(Serializable groupid, String text) {
|
|
||||||
return sendRecentMessage(groupid, text, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 给指定groupid的WebSocketGroup下最近活跃的WebSocket节点发送二进制消息
|
|
||||||
* <p>
|
|
||||||
* @param groupid
|
|
||||||
* @param data 不可为空
|
|
||||||
* @return 为0表示成功, 其他值表示异常
|
|
||||||
*/
|
|
||||||
public final int sendRecentMessage(Serializable groupid, byte[] data) {
|
|
||||||
return sendRecentMessage(groupid, data, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 给指定groupid的WebSocketGroup下最近活跃的WebSocket节点发送文本消息
|
|
||||||
* <p>
|
|
||||||
* @param groupid
|
|
||||||
* @param text 不可为空
|
|
||||||
* @param last 是否最后一条
|
|
||||||
* @return 为0表示成功, 其他值表示异常
|
|
||||||
*/
|
|
||||||
public final int sendRecentMessage(Serializable groupid, String text, boolean last) {
|
|
||||||
return sendMessage(groupid, true, text, last);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 给指定groupid的WebSocketGroup下最近活跃的WebSocket节点发送二进制消息
|
|
||||||
* <p>
|
|
||||||
* @param groupid
|
|
||||||
* @param data 不可为空
|
|
||||||
* @param last 是否最后一条
|
|
||||||
* @return 为0表示成功, 其他值表示异常
|
|
||||||
*/
|
|
||||||
public final int sendRecentMessage(Serializable groupid, byte[] data, boolean last) {
|
|
||||||
return sendMessage(groupid, true, data, last);
|
|
||||||
}
|
|
||||||
|
|
||||||
private int sendMessage(Serializable groupid, boolean recent, String text, boolean last) {
|
|
||||||
if (node == null) return RETCODE_NODESERVICE_NULL;
|
|
||||||
return node.sendMessage(groupid, recent, text, last);
|
|
||||||
}
|
|
||||||
|
|
||||||
private int sendMessage(Serializable groupid, boolean recent, byte[] data, boolean last) {
|
|
||||||
if (node == null) return RETCODE_NODESERVICE_NULL;
|
|
||||||
return node.sendMessage(groupid, recent, data, last);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取当前WebSocket下的属性
|
|
||||||
* <p>
|
|
||||||
* @param <T>
|
|
||||||
* @param name
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public final <T> T getAttribute(String name) {
|
|
||||||
return (T) attributes.get(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 移出当前WebSocket下的属性
|
|
||||||
* <p>
|
|
||||||
* @param <T>
|
|
||||||
* @param name
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
public final <T> T removeAttribute(String name) {
|
|
||||||
return (T) attributes.remove(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 给当前WebSocket下的增加属性
|
|
||||||
* <p>
|
|
||||||
* @param name
|
|
||||||
* @param value
|
|
||||||
*/
|
|
||||||
public final void setAttribute(String name, Object value) {
|
|
||||||
attributes.put(name, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取当前WebSocket所属的groupid
|
|
||||||
* <p>
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
public final Serializable getGroupid() {
|
|
||||||
return groupid;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取当前WebSocket的会话ID, 不会为null
|
|
||||||
* <p>
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
public final Serializable getSessionid() {
|
|
||||||
return sessionid;
|
|
||||||
}
|
|
||||||
|
|
||||||
//-------------------------------------------------------------------
|
|
||||||
/**
|
|
||||||
* 获取当前WebSocket所属的WebSocketGroup, 不会为null
|
|
||||||
* <p>
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
protected final WebSocketGroup getWebSocketGroup() {
|
|
||||||
return group;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取指定groupid的WebSocketGroup, 没有返回null
|
|
||||||
* <p>
|
|
||||||
* @param groupid
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
protected final WebSocketGroup getWebSocketGroup(Serializable groupid) {
|
|
||||||
return engine.getWebSocketGroup(groupid);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected final Collection<WebSocketGroup> getWebSocketGroups() {
|
|
||||||
return engine.getWebSocketGroups();
|
|
||||||
}
|
|
||||||
|
|
||||||
//-------------------------------------------------------------------
|
|
||||||
/**
|
|
||||||
* 返回sessionid, null表示连接不合法或异常
|
|
||||||
*
|
|
||||||
* @param request
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
public Serializable onOpen(final HttpRequest request) {
|
|
||||||
return request.getSessionid(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 创建groupid, null表示异常
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
protected abstract Serializable createGroupid();
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @param channel
|
|
||||||
*/
|
|
||||||
public void onRead(AsyncConnection channel) {
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onConnected() {
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onMessage(String text) {
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onPing(byte[] bytes) {
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onPong(byte[] bytes) {
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onMessage(byte[] bytes) {
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onFragment(String text, boolean last) {
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onFragment(byte[] bytes, boolean last) {
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onClose(int code, String reason) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
/*
|
|
||||||
* To change this license header, choose License Headers in Project Properties.
|
|
||||||
* To change this template file, choose Tools | Templates
|
|
||||||
* and open the template in the editor.
|
|
||||||
*/
|
|
||||||
package org.redkale.net.http;
|
|
||||||
|
|
||||||
import java.lang.annotation.*;
|
|
||||||
import static java.lang.annotation.ElementType.*;
|
|
||||||
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 被标记为 @WebSocketBinary 的WebSocketServlet 将使用原始的TCP传输, 通常用于类似音频/视频传输场景
|
|
||||||
*
|
|
||||||
* @see http://www.redkale.org
|
|
||||||
* @author zhangjx
|
|
||||||
*/
|
|
||||||
@Inherited
|
|
||||||
@Documented
|
|
||||||
@Target({TYPE})
|
|
||||||
@Retention(RUNTIME)
|
|
||||||
public @interface WebSocketBinary {
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,93 +0,0 @@
|
|||||||
/*
|
|
||||||
* To change this license header, choose License Headers in Project Properties.
|
|
||||||
* To change this template file, choose Tools | Templates
|
|
||||||
* and open the template in the editor.
|
|
||||||
*/
|
|
||||||
package org.redkale.net.http;
|
|
||||||
|
|
||||||
import static org.redkale.net.http.WebSocketPacket.DEFAULT_PING_PACKET;
|
|
||||||
import static org.redkale.net.http.WebSocketServlet.DEFAILT_LIVEINTERVAL;
|
|
||||||
import java.io.*;
|
|
||||||
import java.util.*;
|
|
||||||
import java.util.concurrent.*;
|
|
||||||
import java.util.concurrent.atomic.*;
|
|
||||||
import java.util.logging.*;
|
|
||||||
import org.redkale.util.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @see http://www.redkale.org
|
|
||||||
* @author zhangjx
|
|
||||||
*/
|
|
||||||
public final class WebSocketEngine {
|
|
||||||
|
|
||||||
private static final AtomicInteger sequence = new AtomicInteger();
|
|
||||||
|
|
||||||
private final int index;
|
|
||||||
|
|
||||||
private final String engineid;
|
|
||||||
|
|
||||||
private final Map<Serializable, WebSocketGroup> containers = new ConcurrentHashMap<>();
|
|
||||||
|
|
||||||
private ScheduledThreadPoolExecutor scheduler;
|
|
||||||
|
|
||||||
protected final Logger logger;
|
|
||||||
|
|
||||||
protected final boolean finest;
|
|
||||||
|
|
||||||
protected WebSocketEngine(String engineid, Logger logger) {
|
|
||||||
this.engineid = engineid;
|
|
||||||
this.logger = logger;
|
|
||||||
this.index = sequence.getAndIncrement();
|
|
||||||
this.finest = logger.isLoggable(Level.FINEST);
|
|
||||||
}
|
|
||||||
|
|
||||||
void init(AnyValue conf) {
|
|
||||||
final int liveinterval = conf == null ? DEFAILT_LIVEINTERVAL : conf.getIntValue("liveinterval", DEFAILT_LIVEINTERVAL);
|
|
||||||
if (liveinterval == 0) return;
|
|
||||||
if (scheduler != null) return;
|
|
||||||
this.scheduler = new ScheduledThreadPoolExecutor(1, (Runnable r) -> {
|
|
||||||
final Thread t = new Thread(r, engineid + "-WebSocket-LiveInterval-Thread");
|
|
||||||
t.setDaemon(true);
|
|
||||||
return t;
|
|
||||||
});
|
|
||||||
long delay = (liveinterval - System.currentTimeMillis() / 1000 % liveinterval) + index * 5;
|
|
||||||
scheduler.scheduleWithFixedDelay(() -> {
|
|
||||||
getWebSocketGroups().stream().forEach(x -> x.sendEach(DEFAULT_PING_PACKET));
|
|
||||||
//if (finest) logger.finest(engineid + " ping...");
|
|
||||||
}, delay, liveinterval, TimeUnit.SECONDS);
|
|
||||||
if (finest) logger.finest(this.getClass().getSimpleName() + "(" + engineid + ")" + " start keeplive(delay:" + delay + ", interval:" + liveinterval + "s) scheduler executor");
|
|
||||||
}
|
|
||||||
|
|
||||||
void add(WebSocket socket) {
|
|
||||||
WebSocketGroup group = containers.get(socket.groupid);
|
|
||||||
if (group == null) {
|
|
||||||
group = new WebSocketGroup(socket.groupid);
|
|
||||||
containers.put(socket.groupid, group);
|
|
||||||
}
|
|
||||||
group.add(socket);
|
|
||||||
}
|
|
||||||
|
|
||||||
void remove(WebSocket socket) {
|
|
||||||
WebSocketGroup group = containers.get(socket.groupid);
|
|
||||||
if (group == null) return;
|
|
||||||
group.remove(socket);
|
|
||||||
if (group.isEmpty()) containers.remove(socket.groupid);
|
|
||||||
}
|
|
||||||
|
|
||||||
Collection<WebSocketGroup> getWebSocketGroups() {
|
|
||||||
return containers.values();
|
|
||||||
}
|
|
||||||
|
|
||||||
public WebSocketGroup getWebSocketGroup(Serializable groupid) {
|
|
||||||
return containers.get(groupid);
|
|
||||||
}
|
|
||||||
|
|
||||||
void close() {
|
|
||||||
if (scheduler != null) scheduler.shutdownNow();
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getEngineid() {
|
|
||||||
return engineid;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user