Redkale 2.4.0 结束

This commit is contained in:
Redkale
2021-06-06 19:06:05 +08:00
parent 1917ccf35c
commit 2464c360c0
77 changed files with 2749 additions and 1082 deletions

View File

@@ -33,6 +33,14 @@
--> -->
<resources> <resources>
<!--
【节点全局唯一】 @since 2.3.0
全局Serivce执行的线程池 Application.workExecutor, 没配置该节点将自动创建一个。
threads 线程数为0表示不启用workExecutor只用IO线程。默认: CPU核数, 核数=1的情况下默认值为2
hash: 是否使用ThreadHashExecutor作为线程池默认值为false
-->
<executor threads="4" hash="false"/>
<!-- <!--
【节点全局唯一】 【节点全局唯一】
transport节点只能有一个用于配置所有Transport的池参数没配置该节点将自动创建一个。 transport节点只能有一个用于配置所有Transport的池参数没配置该节点将自动创建一个。
@@ -156,7 +164,7 @@
excludelibs: 排除lib.path与excludes中的正则表达式匹配的路径, 多个正则表达式用分号;隔开 excludelibs: 排除lib.path与excludes中的正则表达式匹配的路径, 多个正则表达式用分号;隔开
charset: 文本编码, 默认: UTF-8 charset: 文本编码, 默认: UTF-8
backlog: 默认10K backlog: 默认10K
threads 线程数, 默认: CPU核数*2最小8个 threads 线程数, 默认: CPU核数*2最小8个【已废弃 @since 2.3.0】
maxconns 最大连接数, 小于1表示无限制 默认: 0 maxconns 最大连接数, 小于1表示无限制 默认: 0
maxbody: request.body最大值 默认: 64K maxbody: request.body最大值 默认: 64K
bufferCapacity: ByteBuffer的初始化大小 TCP默认: 32K; (HTTP 2.0、WebSocket必须要16k以上); UDP默认: 1350B bufferCapacity: ByteBuffer的初始化大小 TCP默认: 32K; (HTTP 2.0、WebSocket必须要16k以上); UDP默认: 1350B
@@ -165,7 +173,7 @@
aliveTimeoutSeconds: KeepAlive读操作超时秒数 默认30 0表示永久不超时; -1表示禁止KeepAlive aliveTimeoutSeconds: KeepAlive读操作超时秒数 默认30 0表示永久不超时; -1表示禁止KeepAlive
readTimeoutSeconds: 读操作超时秒数, 默认0 表示永久不超时 readTimeoutSeconds: 读操作超时秒数, 默认0 表示永久不超时
writeTimeoutSeconds: 写操作超时秒数, 默认0 表示永久不超时 writeTimeoutSeconds: 写操作超时秒数, 默认0 表示永久不超时
netimpl: ProtocolServer的实现类。TCP情况下值可以是aio或nio默认值为aioUDP情况下值可以是bio默认值为bio iogroup: 流线程组AsyncGroup对象如果值为client表示和Application.asyncGroup对象共用
interceptor: 启动/关闭NodeServer时被调用的拦截器实现类必须是org.redkale.boot.NodeInterceptor的子类默认为null interceptor: 启动/关闭NodeServer时被调用的拦截器实现类必须是org.redkale.boot.NodeInterceptor的子类默认为null
--> -->
<server protocol="HTTP" host="127.0.0.1" port="6060" root="root" lib=""> <server protocol="HTTP" host="127.0.0.1" port="6060" root="root" lib="">
@@ -300,8 +308,9 @@
【节点在<server>中唯一】 【节点在<server>中唯一】
当Server为HTTP协议时render才有效. 指定输出引擎的实现类 当Server为HTTP协议时render才有效. 指定输出引擎的实现类
value: 输出引擎的实现类, 必须是org.redkale.net.http.HttpRender的子类 value: 输出引擎的实现类, 必须是org.redkale.net.http.HttpRender的子类
suffixs: 引擎文件名后缀,多个用;隔开,默认值为: .htel
--> -->
<render value="org.redkalex.htel.HttpTemplateRender"/> <render value="org.redkalex.htel.HttpTemplateRender" suffixs=".htel"/>
<!-- <!--
【节点在<server>中唯一】 【节点在<server>中唯一】
当Server为HTTP协议时ResourceServlet才有效. 默认存在一个有默认属性的resource-servlet节点 当Server为HTTP协议时ResourceServlet才有效. 默认存在一个有默认属性的resource-servlet节点

View File

@@ -13,6 +13,7 @@
--> -->
<property name="javax.persistence.cachemode" value="ALL"/> <property name="javax.persistence.cachemode" value="ALL"/>
<!-- 多个URL用;隔开如分布式SearchSource需要配多个URL -->
<property name="javax.persistence.jdbc.url" value="jdbc:mysql://127.0.0.1:3306/dbuser?characterEncoding=utf8"/> <property name="javax.persistence.jdbc.url" value="jdbc:mysql://127.0.0.1:3306/dbuser?characterEncoding=utf8"/>
<!-- <!--
javax.persistence.jdbc.driver在JPA的值是JDBC驱动Redkale有所不同值应该是javax.sql.DataSource的子类。 javax.persistence.jdbc.driver在JPA的值是JDBC驱动Redkale有所不同值应该是javax.sql.DataSource的子类。

View File

@@ -24,6 +24,7 @@ module org.redkale {
exports org.redkale.convert.json; exports org.redkale.convert.json;
exports org.redkale.mq; exports org.redkale.mq;
exports org.redkale.net; exports org.redkale.net;
exports org.redkale.net.client;
exports org.redkale.net.http; exports org.redkale.net.http;
exports org.redkale.net.sncp; exports org.redkale.net.sncp;
exports org.redkale.service; exports org.redkale.service;

View File

@@ -123,9 +123,8 @@ public final class Application {
* *
* @deprecated 2.3.0 使用RESNAME_APP_EXECUTOR * @deprecated 2.3.0 使用RESNAME_APP_EXECUTOR
*/ */
@Deprecated //@Deprecated
public static final String RESNAME_SERVER_EXECUTOR2 = Server.RESNAME_SERVER_EXECUTOR2; //public static final String RESNAME_SERVER_EXECUTOR2 = Server.RESNAME_SERVER_EXECUTOR2;
/** /**
* 当前Server的ResourceFactory * 当前Server的ResourceFactory
*/ */
@@ -228,6 +227,7 @@ public final class Application {
this.resourceFactory.register(RESNAME_APP_HOME, URI.class, root.toURI()); this.resourceFactory.register(RESNAME_APP_HOME, URI.class, root.toURI());
try { try {
this.resourceFactory.register(RESNAME_APP_HOME, root.getCanonicalPath()); this.resourceFactory.register(RESNAME_APP_HOME, root.getCanonicalPath());
if (System.getProperty(RESNAME_APP_HOME) == null) System.setProperty(RESNAME_APP_HOME, root.getCanonicalPath());
this.home = root.getCanonicalFile(); this.home = root.getCanonicalFile();
String confsubpath = System.getProperty(RESNAME_APP_CONF, "conf"); String confsubpath = System.getProperty(RESNAME_APP_CONF, "conf");
if (confsubpath.contains("://")) { if (confsubpath.contains("://")) {
@@ -344,11 +344,11 @@ public final class Application {
try { try {
String classval = clusterConf.getValue("value"); String classval = clusterConf.getValue("value");
if (classval == null || classval.isEmpty()) { if (classval == null || classval.isEmpty()) {
Iterator<ClusterAgent> it = ServiceLoader.load(ClusterAgent.class, classLoader).iterator(); Iterator<ClusterAgentLoader> it = ServiceLoader.load(ClusterAgentLoader.class, classLoader).iterator();
while (it.hasNext()) { while (it.hasNext()) {
ClusterAgent agent = it.next(); ClusterAgentLoader agent = it.next();
if (agent.match(clusterConf)) { if (agent != null && agent.match(clusterConf)) {
cluster = agent; cluster = agent.agentClass().getConstructor().newInstance();
cluster.setConfig(clusterConf); cluster.setConfig(clusterConf);
break; break;
} }
@@ -380,7 +380,7 @@ public final class Application {
mqs = new MessageAgent[mqConfs.length]; mqs = new MessageAgent[mqConfs.length];
Set<String> mqnames = new HashSet<>(); Set<String> mqnames = new HashSet<>();
for (int i = 0; i < mqConfs.length; i++) { for (int i = 0; i < mqConfs.length; i++) {
AnyValue mqConf = mqConfs[0]; AnyValue mqConf = mqConfs[i];
String mqname = mqConf.getValue("name", ""); String mqname = mqConf.getValue("name", "");
if (mqnames.contains(mqname)) throw new RuntimeException("mq.name(" + mqname + ") is repeat"); if (mqnames.contains(mqname)) throw new RuntimeException("mq.name(" + mqname + ") is repeat");
mqnames.add(mqname); mqnames.add(mqname);
@@ -395,11 +395,11 @@ public final class Application {
try { try {
String classval = mqConf.getValue("value"); String classval = mqConf.getValue("value");
if (classval == null || classval.isEmpty()) { if (classval == null || classval.isEmpty()) {
Iterator<MessageAgent> it = ServiceLoader.load(MessageAgent.class, classLoader).iterator(); Iterator<MessageAgentLoader> it = ServiceLoader.load(MessageAgentLoader.class, classLoader).iterator();
while (it.hasNext()) { while (it.hasNext()) {
MessageAgent messageAgent = it.next(); MessageAgentLoader messageAgent = it.next();
if (messageAgent.match(mqConf)) { if (messageAgent != null && messageAgent.match(mqConf)) {
mqs[i] = messageAgent; mqs[i] = messageAgent.agentClass().getConstructor().newInstance();
mqs[i].setConfig(mqConf); mqs[i].setConfig(mqConf);
break; break;
} }
@@ -649,7 +649,7 @@ public final class Application {
} else if (type == NodeSncpServer.class) { } else if (type == NodeSncpServer.class) {
NodeServer server = null; NodeServer server = null;
for (NodeServer ns : application.getNodeServers()) { for (NodeServer ns : application.getNodeServers()) {
if (ns.getClass() == NodeSncpServer.class) continue; if (ns.getClass() != NodeSncpServer.class) continue;
if (res.name().equals(ns.server.getName())) { if (res.name().equals(ns.server.getName())) {
server = ns; server = ns;
break; break;
@@ -659,7 +659,7 @@ public final class Application {
} else if (type == NodeHttpServer.class) { } else if (type == NodeHttpServer.class) {
NodeServer server = null; NodeServer server = null;
for (NodeServer ns : application.getNodeServers()) { for (NodeServer ns : application.getNodeServers()) {
if (ns.getClass() == NodeHttpServer.class) continue; if (ns.getClass() != NodeHttpServer.class) continue;
if (res.name().equals(ns.server.getName())) { if (res.name().equals(ns.server.getName())) {
server = ns; server = ns;
break; break;
@@ -669,7 +669,7 @@ public final class Application {
} else if (type == NodeWatchServer.class) { } else if (type == NodeWatchServer.class) {
NodeServer server = null; NodeServer server = null;
for (NodeServer ns : application.getNodeServers()) { for (NodeServer ns : application.getNodeServers()) {
if (ns.getClass() == NodeWatchServer.class) continue; if (ns.getClass() != NodeWatchServer.class) continue;
if (res.name().equals(ns.server.getName())) { if (res.name().equals(ns.server.getName())) {
server = ns; server = ns;
break; break;
@@ -738,7 +738,29 @@ public final class Application {
resourceFactory.register((ResourceFactory rf, final Object src, String resourceName, Field field, final Object attachment) -> { resourceFactory.register((ResourceFactory rf, final Object src, String resourceName, Field field, final Object attachment) -> {
try { try {
if (field.getAnnotation(Resource.class) == null) return; if (field.getAnnotation(Resource.class) == null) return;
if (clusterAgent == null) return; if (clusterAgent == null) {
NodeHttpServer nodeHttpServer = null;
for (NodeServer n : getNodeServers()) {
if (n.getClass() == NodeHttpServer.class && Objects.equals(resourceName, ((NodeHttpServer) n).getHttpServer().getName())) {
nodeHttpServer = (NodeHttpServer) n;
break;
}
}
if (nodeHttpServer == null) {
for (NodeServer n : getNodeServers()) {
if (n.getClass() == NodeHttpServer.class) {
nodeHttpServer = (NodeHttpServer) n;
break;
}
}
}
if (nodeHttpServer == null) return;
HttpMessageClient messageClient = new HttpMessageLocalClient(nodeHttpServer.getHttpServer());
field.set(src, messageClient);
rf.inject(messageClient, null); // 给其可能包含@Resource的字段赋值;
rf.register(resourceName, HttpMessageClient.class, messageClient);
return;
}
HttpMessageClient messageClient = new HttpMessageClusterClient(clusterAgent); HttpMessageClient messageClient = new HttpMessageClusterClient(clusterAgent);
field.set(src, messageClient); field.set(src, messageClient);
rf.inject(messageClient, null); // 给其可能包含@Resource的字段赋值; rf.inject(messageClient, null); // 给其可能包含@Resource的字段赋值;
@@ -758,11 +780,11 @@ public final class Application {
try { try {
Class sourceType = CacheMemorySource.class; Class sourceType = CacheMemorySource.class;
if (classval == null || classval.isEmpty()) { if (classval == null || classval.isEmpty()) {
Iterator<CacheSource> it = ServiceLoader.load(CacheSource.class, serverClassLoader).iterator(); Iterator<CacheSourceLoader> it = ServiceLoader.load(CacheSourceLoader.class, serverClassLoader).iterator();
while (it.hasNext()) { while (it.hasNext()) {
CacheSource s = it.next(); CacheSourceLoader s = it.next();
if (s.match(sourceConf)) { if (s != null && s.match(sourceConf)) {
sourceType = s.getClass(); sourceType = s.sourceClass();
break; break;
} }
} }

View File

@@ -208,7 +208,7 @@ public final class ClassFilter<T> {
} catch (Throwable cfe) { } catch (Throwable cfe) {
if (finest && !clazzname.startsWith("sun.") && !clazzname.startsWith("javax.") if (finest && !clazzname.startsWith("sun.") && !clazzname.startsWith("javax.")
&& !clazzname.startsWith("com.sun.") && !clazzname.startsWith("jdk.") && !clazzname.startsWith("META-INF") && !clazzname.startsWith("com.sun.") && !clazzname.startsWith("jdk.") && !clazzname.startsWith("META-INF")
&& !clazzname.startsWith("com.mysql.") && !clazzname.startsWith("com.microsoft.") && !clazzname.startsWith("com.mysql.") && !clazzname.startsWith("com.microsoft.") && !clazzname.startsWith("freemarker.")
&& !clazzname.startsWith("org.redkale") && (clazzname.contains("Service") || clazzname.contains("Servlet"))) { && !clazzname.startsWith("org.redkale") && (clazzname.contains("Service") || clazzname.contains("Servlet"))) {
//&& (!(cfe instanceof NoClassDefFoundError) || (cfe instanceof UnsupportedClassVersionError) || ((NoClassDefFoundError) cfe).getMessage().startsWith("java.lang.NoClassDefFoundError: java"))) { //&& (!(cfe instanceof NoClassDefFoundError) || (cfe instanceof UnsupportedClassVersionError) || ((NoClassDefFoundError) cfe).getMessage().startsWith("java.lang.NoClassDefFoundError: java"))) {
logger.log(Level.FINEST, ClassFilter.class.getSimpleName() + " filter error for class: " + clazzname + (url == null ? "" : (" in " + url)), cfe); logger.log(Level.FINEST, ClassFilter.class.getSimpleName() + " filter error for class: " + clazzname + (url == null ? "" : (" in " + url)), cfe);

View File

@@ -79,7 +79,23 @@ public class LogFileHandler extends Handler {
message, message,
throwable); throwable);
} }
}
public static void initDebugLogConfig() {
try {
ByteArrayOutputStream out = new ByteArrayOutputStream();
final PrintStream ps = new PrintStream(out);
ps.println("handlers = java.util.logging.ConsoleHandler");
ps.println(".level = FINEST");
ps.println("jdk.level = INFO");
ps.println("sun.level = INFO");
ps.println("com.sun.level = INFO");
ps.println("javax.level = INFO");
ps.println("java.util.logging.ConsoleHandler.level = FINEST");
ps.println("java.util.logging.ConsoleHandler.formatter = " + LogFileHandler.LoggingFormater.class.getName());
LogManager.getLogManager().readConfiguration(new ByteArrayInputStream(out.toByteArray()));
} catch (Exception e) {
}
} }
protected final LinkedBlockingQueue<LogRecord> logqueue = new LinkedBlockingQueue(); protected final LinkedBlockingQueue<LogRecord> logqueue = new LinkedBlockingQueue();

View File

@@ -214,11 +214,11 @@ public abstract class NodeServer {
String classval = sourceConf.getValue("value"); String classval = sourceConf.getValue("value");
Class type = null; Class type = null;
if (classval == null || classval.isEmpty()) { if (classval == null || classval.isEmpty()) {
Iterator<CacheSource> it = ServiceLoader.load(CacheSource.class, serverClassLoader).iterator(); Iterator<CacheSourceLoader> it = ServiceLoader.load(CacheSourceLoader.class, serverClassLoader).iterator();
while (it.hasNext()) { while (it.hasNext()) {
CacheSource s = it.next(); CacheSourceLoader s = it.next();
if (s.match(sourceConf)) { if (s != null && s.match(sourceConf)) {
type = s.getClass(); type = s.sourceClass();
break; break;
} }
} }
@@ -333,6 +333,9 @@ public abstract class NodeServer {
} }
application.dataSources.add(source); application.dataSources.add(source);
if (source instanceof SearchSource) {
appResFactory.register(resourceName, SearchSource.class, source);
}
appResFactory.register(resourceName, DataSource.class, source); appResFactory.register(resourceName, DataSource.class, source);
field.set(src, source); field.set(src, source);
@@ -365,11 +368,11 @@ public abstract class NodeServer {
if (sourceConf != null) { if (sourceConf != null) {
String classval = sourceConf.getValue("value"); String classval = sourceConf.getValue("value");
if (classval == null || classval.isEmpty()) { if (classval == null || classval.isEmpty()) {
Iterator<CacheSource> it = ServiceLoader.load(CacheSource.class, serverClassLoader).iterator(); Iterator<CacheSourceLoader> it = ServiceLoader.load(CacheSourceLoader.class, serverClassLoader).iterator();
while (it.hasNext()) { while (it.hasNext()) {
CacheSource s = it.next(); CacheSourceLoader s = it.next();
if (s.match(sourceConf)) { if (s != null && s.match(sourceConf)) {
sourceType0 = s.getClass(); sourceType0 = s.sourceClass();
break; break;
} }
} }

View File

@@ -0,0 +1,25 @@
/*
* 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.cluster;
import org.redkale.util.AnyValue;
/**
* 自定义的ClusterAgent加载器
*
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
* @since 2.4.0
*/
public interface ClusterAgentLoader {
public boolean match(AnyValue config);
public Class<? extends ClusterAgent> agentClass();
}

View File

@@ -31,8 +31,14 @@ public final class AnyEncoder<T> implements Encodeable<Writer, T> {
out.writeClassName(null); out.writeClassName(null);
out.writeNull(); out.writeNull();
} else { } else {
if (out.needWriteClassName()) out.writeClassName(factory.getEntityAlias(value.getClass())); Class clazz = value.getClass();
factory.loadEncoder(value.getClass()).convertTo(out, value); if (clazz == Object.class) {
out.writeObjectB(value);
out.writeObjectE(value);
return;
}
if (out.needWriteClassName()) out.writeClassName(factory.getEntityAlias(clazz));
factory.loadEncoder(clazz).convertTo(out, value);
} }
} }

View File

@@ -68,7 +68,8 @@ public class ArrayEncoder<T> implements Encodeable<Writer, T[]> {
out.writeNull(); out.writeNull();
return; return;
} }
if (value.length == 0) { int iMax = value.length - 1;
if (iMax == -1) {
out.writeArrayB(0, this, componentEncoder, value); out.writeArrayB(0, this, componentEncoder, value);
out.writeArrayE(); out.writeArrayE();
return; return;
@@ -87,28 +88,27 @@ public class ArrayEncoder<T> implements Encodeable<Writer, T[]> {
Encodeable<Writer, Object> itemEncoder = this.componentEncoder; Encodeable<Writer, Object> itemEncoder = this.componentEncoder;
if (subtypefinal) { if (subtypefinal) {
if (out.writeArrayB(value.length, this, itemEncoder, value) < 0) { if (out.writeArrayB(value.length, this, itemEncoder, value) < 0) {
boolean first = true; for (int i = 0;; i++) {
for (Object v : value) { writeMemberValue(out, member, itemEncoder, value[i], i);
if (!first) out.writeArrayMark(); if (i == iMax) break;
writeMemberValue(out, member, itemEncoder, v, first); out.writeArrayMark();
if (first) first = false;
} }
} }
} else { } else {
if (out.writeArrayB(value.length, this, itemEncoder, value) < 0) { if (out.writeArrayB(value.length, this, itemEncoder, value) < 0) {
final Type comp = this.componentType; final Type comp = this.componentType;
boolean first = true; for (int i = 0;; i++) {
for (Object v : value) { Object v = value[i];
if (!first) out.writeArrayMark(); writeMemberValue(out, member, ((v != null && (v.getClass() == comp || out.specify() == comp)) ? itemEncoder : anyEncoder), v, i);
writeMemberValue(out, member, ((v != null && (v.getClass() == comp || out.specify() == comp)) ? itemEncoder : anyEncoder), v, first); if (i == iMax) break;
if (first) first = false; out.writeArrayMark();
} }
} }
} }
out.writeArrayE(); out.writeArrayE();
} }
protected void writeMemberValue(Writer out, EnMember member, Encodeable<Writer, Object> encoder, Object value, boolean first) { protected void writeMemberValue(Writer out, EnMember member, Encodeable<Writer, Object> encoder, Object value, int index) {
encoder.convertTo(out, value); encoder.convertTo(out, value);
} }

View File

@@ -71,6 +71,7 @@ public abstract class ConvertFactory<R extends Reader, W extends Writer> {
this.parent = parent; this.parent = parent;
if (parent == null) { if (parent == null) {
//--------------------------------------------------------- //---------------------------------------------------------
this.register(boolean.class, BoolSimpledCoder.instance); this.register(boolean.class, BoolSimpledCoder.instance);
this.register(Boolean.class, BoolSimpledCoder.instance); this.register(Boolean.class, BoolSimpledCoder.instance);

View File

@@ -156,7 +156,7 @@ public class ObjectEncoder<W extends Writer, T> implements Encodeable<W, T> {
Arrays.sort(this.members, (a, b) -> a.compareTo(factory.isFieldSort(), b)); Arrays.sort(this.members, (a, b) -> a.compareTo(factory.isFieldSort(), b));
} catch (Exception ex) { } catch (Exception ex) {
throw new ConvertException(ex); throw new ConvertException("ObjectEncoder init type=" + this.type + " error", ex);
} }
} finally { } finally {
inited = true; inited = true;

View File

@@ -30,6 +30,16 @@ public class JsonBytesWriter extends JsonWriter implements ByteTuple {
private static final byte[] BYTES_FALSEVALUE = "false".getBytes(); private static final byte[] BYTES_FALSEVALUE = "false".getBytes();
private static final int TENTHOUSAND_MAX = 10001;
private static final byte[][] TENTHOUSAND_BYTES = new byte[TENTHOUSAND_MAX][];
static {
for (int i = 0; i < TENTHOUSAND_BYTES.length; i++) {
TENTHOUSAND_BYTES[i] = String.valueOf(i).getBytes();
}
}
private int count; private int count;
private byte[] content; private byte[] content;
@@ -138,11 +148,17 @@ public class JsonBytesWriter extends JsonWriter implements ByteTuple {
public void writeLatin1To(final boolean quote, final String value) { public void writeLatin1To(final boolean quote, final String value) {
byte[] bs = Utility.byteArray(value); byte[] bs = Utility.byteArray(value);
int len = bs.length; int len = bs.length;
expand(len + (quote ? 2 : 0)); if (quote) {
if (quote) content[count++] = '"'; byte[] src = expand(len + 2);
System.arraycopy(bs, 0, content, count, bs.length); src[count++] = '"';
count += len; System.arraycopy(bs, 0, src, count, bs.length);
if (quote) content[count++] = '"'; count += len;
src[count++] = '"';
} else {
byte[] src = expand(len);
System.arraycopy(bs, 0, src, count, bs.length);
count += len;
}
} }
public JsonBytesWriter clear() { public JsonBytesWriter clear() {
@@ -281,8 +297,8 @@ public class JsonBytesWriter extends JsonWriter implements ByteTuple {
public String toString() { public String toString() {
return new String(content, 0, count, StandardCharsets.UTF_8); return new String(content, 0, count, StandardCharsets.UTF_8);
} }
//----------------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------------
@Override @Override
public void writeBoolean(boolean value) { public void writeBoolean(boolean value) {
byte[] bs = value ? BYTES_TUREVALUE : BYTES_FALSEVALUE; byte[] bs = value ? BYTES_TUREVALUE : BYTES_FALSEVALUE;
@@ -293,6 +309,13 @@ public class JsonBytesWriter extends JsonWriter implements ByteTuple {
@Override @Override
public void writeInt(int value) { public void writeInt(int value) {
if (value >= 0 && value < TENTHOUSAND_MAX) {
byte[] bs = TENTHOUSAND_BYTES[value];
expand(bs.length);
System.arraycopy(bs, 0, content, count, bs.length);
count += bs.length;
return;
}
final char sign = value >= 0 ? 0 : '-'; final char sign = value >= 0 ? 0 : '-';
if (value < 0) value = -value; if (value < 0) value = -value;
int size; int size;
@@ -333,6 +356,13 @@ public class JsonBytesWriter extends JsonWriter implements ByteTuple {
@Override @Override
public void writeLong(long value) { public void writeLong(long value) {
if (value >= 0 && value < TENTHOUSAND_MAX) {
byte[] bs = TENTHOUSAND_BYTES[(int) value];
expand(bs.length);
System.arraycopy(bs, 0, content, count, bs.length);
count += bs.length;
return;
}
final char sign = value >= 0 ? 0 : '-'; final char sign = value >= 0 ? 0 : '-';
if (value < 0) value = -value; if (value < 0) value = -value;
int size = 19; int size = 19;

View File

@@ -24,6 +24,16 @@ public class JsonCharsWriter extends JsonWriter {
private static final char[] CHARS_FALSEVALUE = "false".toCharArray(); private static final char[] CHARS_FALSEVALUE = "false".toCharArray();
private static final int TENTHOUSAND_MAX = 10001;
private static final char[][] TENTHOUSAND_BYTES = new char[TENTHOUSAND_MAX][];
static {
for (int i = 0; i < TENTHOUSAND_BYTES.length; i++) {
TENTHOUSAND_BYTES[i] = String.valueOf(i).toCharArray();
}
}
private int count; private int count;
private char[] content; private char[] content;
@@ -171,6 +181,10 @@ public class JsonCharsWriter extends JsonWriter {
@Override @Override
public void writeInt(int value) { public void writeInt(int value) {
if (value >= 0 && value < TENTHOUSAND_MAX) {
writeTo(TENTHOUSAND_BYTES[value]);
return;
}
final char sign = value >= 0 ? 0 : '-'; final char sign = value >= 0 ? 0 : '-';
if (value < 0) value = -value; if (value < 0) value = -value;
int size; int size;
@@ -211,6 +225,10 @@ public class JsonCharsWriter extends JsonWriter {
@Override @Override
public void writeLong(long value) { public void writeLong(long value) {
if (value >= 0 && value < TENTHOUSAND_MAX) {
writeTo(TENTHOUSAND_BYTES[(int) value]);
return;
}
final char sign = value >= 0 ? 0 : '-'; final char sign = value >= 0 ? 0 : '-';
if (value < 0) value = -value; if (value < 0) value = -value;
int size = 19; int size = 19;

View File

@@ -74,7 +74,12 @@ public abstract class JsonDynEncoder<T> implements Encodeable<JsonWriter, T> {
if (factory.loadEncoder(t) instanceof JsonDynEncoder) return true; if (factory.loadEncoder(t) instanceof JsonDynEncoder) return true;
} }
} }
if (factory.loadEncoder(type) instanceof JsonDynEncoder) return true; if (type instanceof TypeVariable) return false;
try {
if (factory.loadEncoder(type) instanceof JsonDynEncoder) return true;
} catch (Exception e) {
return false;
}
return false; return false;
} }

View File

@@ -119,21 +119,21 @@ public class HttpMessageClient extends MessageClient {
broadcastMessage(topic, userid, groupid, request, null); broadcastMessage(topic, userid, groupid, request, null);
} }
public final <T> CompletableFuture<T> sendMessage(HttpSimpleRequest request, Type type) { public <T> CompletableFuture<T> sendMessage(HttpSimpleRequest request, Type type) {
return sendMessage(generateHttpReqTopic(request, null), 0, null, request, null).thenApply((HttpResult<byte[]> httbs) -> { return sendMessage(generateHttpReqTopic(request, null), 0, null, request, null).thenApply((HttpResult<byte[]> httbs) -> {
if (httbs == null || httbs.getResult() == null) return null; if (httbs == null || httbs.getResult() == null) return null;
return JsonConvert.root().convertFrom(type, httbs.getResult()); return JsonConvert.root().convertFrom(type, httbs.getResult());
}); });
} }
public final <T> CompletableFuture<T> sendMessage(int userid, HttpSimpleRequest request, Type type) { public <T> CompletableFuture<T> sendMessage(int userid, HttpSimpleRequest request, Type type) {
return sendMessage(generateHttpReqTopic(request, null), userid, null, request, null).thenApply((HttpResult<byte[]> httbs) -> { return sendMessage(generateHttpReqTopic(request, null), userid, null, request, null).thenApply((HttpResult<byte[]> httbs) -> {
if (httbs == null || httbs.getResult() == null) return null; if (httbs == null || httbs.getResult() == null) return null;
return JsonConvert.root().convertFrom(type, httbs.getResult()); return JsonConvert.root().convertFrom(type, httbs.getResult());
}); });
} }
public final <T> CompletableFuture<T> sendMessage(int userid, String groupid, HttpSimpleRequest request, Type type) { public <T> CompletableFuture<T> sendMessage(int userid, String groupid, HttpSimpleRequest request, Type type) {
return sendMessage(generateHttpReqTopic(request, null), userid, groupid, request, null).thenApply((HttpResult<byte[]> httbs) -> { return sendMessage(generateHttpReqTopic(request, null), userid, groupid, request, null).thenApply((HttpResult<byte[]> httbs) -> {
if (httbs == null || httbs.getResult() == null) return null; if (httbs == null || httbs.getResult() == null) return null;
return JsonConvert.root().convertFrom(type, httbs.getResult()); return JsonConvert.root().convertFrom(type, httbs.getResult());

View File

@@ -30,8 +30,7 @@ public class HttpMessageClusterClient extends HttpMessageClient {
//jdk.internal.net.http.common.Utils.DISALLOWED_HEADERS_SET //jdk.internal.net.http.common.Utils.DISALLOWED_HEADERS_SET
private static final Set<String> DISALLOWED_HEADERS_SET = Utility.ofSet("connection", "content-length", private static final Set<String> DISALLOWED_HEADERS_SET = Utility.ofSet("connection", "content-length",
"date", "expect", "from", "host", "origin", "date", "expect", "from", "host", "origin", "referer", "upgrade", "via", "warning");
"referer", "upgrade", "via", "warning");
protected ClusterAgent clusterAgent; protected ClusterAgent clusterAgent;
@@ -39,6 +38,7 @@ public class HttpMessageClusterClient extends HttpMessageClient {
protected HttpClient httpClient; protected HttpClient httpClient;
//protected java.net.http.HttpClient httpClient; //protected java.net.http.HttpClient httpClient;
public HttpMessageClusterClient(ClusterAgent clusterAgent) { public HttpMessageClusterClient(ClusterAgent clusterAgent) {
super(null); super(null);
Objects.requireNonNull(clusterAgent); Objects.requireNonNull(clusterAgent);

View File

@@ -0,0 +1,216 @@
/*
* 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.mq;
import java.lang.reflect.Type;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicLong;
import org.redkale.convert.*;
import org.redkale.convert.json.JsonConvert;
import org.redkale.net.http.*;
/**
* 没有配置MQ且也没有ClusterAgent的情况下实现的默认HttpMessageClient实例
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*
* @since 2.4.0
*/
public class HttpMessageLocalClient extends HttpMessageClient {
protected final HttpServer server;
public HttpMessageLocalClient(HttpServer server) {
super(null);
this.server = server;
}
@Override
public <T> CompletableFuture<T> sendMessage(HttpSimpleRequest request, Type type) {
HttpPrepareServlet servlet = (HttpPrepareServlet) server.getPrepareServlet();
HttpRequest req = new HttpMessageLocalRequest(server.getContext(), request);
CompletableFuture future = new CompletableFuture();
HttpResponse resp = new HttpMessageLocalResponse(req, future);
try {
servlet.execute(req, resp);
} catch (Exception e) {
future.completeExceptionally(e);
}
return future;
}
@Override
public <T> CompletableFuture<T> sendMessage(int userid, HttpSimpleRequest request, Type type) {
HttpPrepareServlet servlet = (HttpPrepareServlet) server.getPrepareServlet();
HttpRequest req = new HttpMessageLocalRequest(server.getContext(), request);
CompletableFuture future = new CompletableFuture();
HttpResponse resp = new HttpMessageLocalResponse(req, future);
try {
servlet.execute(req, resp);
} catch (Exception e) {
future.completeExceptionally(e);
}
return future;
}
@Override
public <T> CompletableFuture<T> sendMessage(int userid, String groupid, HttpSimpleRequest request, Type type) {
HttpPrepareServlet servlet = (HttpPrepareServlet) server.getPrepareServlet();
HttpRequest req = new HttpMessageLocalRequest(server.getContext(), request);
CompletableFuture future = new CompletableFuture();
HttpResponse resp = new HttpMessageLocalResponse(req, future);
try {
servlet.execute(req, resp);
} catch (Exception e) {
future.completeExceptionally(e);
}
return future;
}
@Override
public CompletableFuture<HttpResult<byte[]>> sendMessage(String topic, int userid, String groupid, HttpSimpleRequest request, AtomicLong counter) {
HttpPrepareServlet servlet = (HttpPrepareServlet) server.getPrepareServlet();
HttpRequest req = new HttpMessageLocalRequest(server.getContext(), request);
CompletableFuture future = new CompletableFuture();
HttpResponse resp = new HttpMessageLocalResponse(req, future);
try {
servlet.execute(req, resp);
} catch (Exception e) {
future.completeExceptionally(e);
}
return future.thenApply(rs -> {
if (rs == null) return new HttpResult();
if (rs instanceof HttpResult) {
Object result = ((HttpResult) rs).getResult();
if (result == null || result instanceof byte[]) return (HttpResult) rs;
return new HttpResult(JsonConvert.root().convertToBytes(result));
}
return new HttpResult(JsonConvert.root().convertToBytes(rs));
});
}
@Override
public void produceMessage(String topic, int userid, String groupid, HttpSimpleRequest request, AtomicLong counter) {
HttpPrepareServlet servlet = (HttpPrepareServlet) server.getPrepareServlet();
HttpRequest req = new HttpMessageLocalRequest(server.getContext(), request);
HttpResponse resp = new HttpMessageLocalResponse(req, null);
try {
servlet.execute(req, resp);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
@Override
public void broadcastMessage(String topic, int userid, String groupid, HttpSimpleRequest request, AtomicLong counter) {
HttpPrepareServlet servlet = (HttpPrepareServlet) server.getPrepareServlet();
HttpRequest req = new HttpMessageLocalRequest(server.getContext(), request);
HttpResponse resp = new HttpMessageLocalResponse(req, null);
try {
servlet.execute(req, resp);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static class HttpMessageLocalRequest extends HttpRequest {
public HttpMessageLocalRequest(HttpContext context, HttpSimpleRequest req) {
super(context, req);
}
}
public static class HttpMessageLocalResponse extends HttpResponse {
private CompletableFuture future;
public HttpMessageLocalResponse(HttpRequest req, CompletableFuture future) {
super(req.getContext(), req, null);
this.future = future;
}
@Override
public void finishJson(org.redkale.service.RetResult ret) {
if (future == null) return;
future.complete(ret);
}
@Override
public void finish(String obj) {
if (future == null) return;
future.complete(obj == null ? "" : obj);
}
@Override
public void finish404() {
finish(404, null);
}
@Override
public void finish(int status, String msg) {
if (future == null) return;
if (status == 0 || status == 200) {
future.complete(msg == null ? "" : msg);
} else {
future.complete(new HttpResult(msg == null ? "" : msg).status(status));
}
}
@Override
public void finish(final Convert convert, HttpResult result) {
if (future == null) return;
if (convert != null) result.convert(convert);
future.complete(result);
}
@Override
public void finish(boolean kill, final byte[] bs, int offset, int length) {
if (future == null) return;
if (offset == 0 && bs.length == length) {
future.complete(bs);
} else {
future.complete(Arrays.copyOfRange(bs, offset, offset + length));
}
}
@Override
public void finish(boolean kill, final String contentType, final byte[] bs, int offset, int length) {
if (future == null) return;
byte[] rs = (offset == 0 && bs.length == length) ? bs : Arrays.copyOfRange(bs, offset, offset + length);
future.complete(rs);
}
@Override
public void finish(boolean kill, ByteBuffer buffer) {
if (future == null) return;
byte[] bs = new byte[buffer.remaining()];
buffer.get(bs);
future.complete(bs);
}
@Override
public void finish(boolean kill, ByteBuffer... buffers) {
if (future == null) return;
int size = 0;
for (ByteBuffer buf : buffers) {
size += buf.remaining();
}
byte[] bs = new byte[size];
int index = 0;
for (ByteBuffer buf : buffers) {
int r = buf.remaining();
buf.get(bs, index, r);
index += r;
}
future.complete(bs);
}
}
}

View File

@@ -216,6 +216,10 @@ public abstract class MessageAgent {
public final synchronized void putService(NodeHttpServer ns, Service service, HttpServlet servlet) { public final synchronized void putService(NodeHttpServer ns, Service service, HttpServlet servlet) {
AutoLoad al = service.getClass().getAnnotation(AutoLoad.class); AutoLoad al = service.getClass().getAnnotation(AutoLoad.class);
if (al != null && !al.value() && service.getClass().getAnnotation(Local.class) != null) return; if (al != null && !al.value() && service.getClass().getAnnotation(Local.class) != null) return;
{ //标记@RestService(name = " ") 需要跳过, 一般作为模板引擎
RestService rest = service.getClass().getAnnotation(RestService.class);
if (rest != null && !rest.name().isEmpty() && rest.name().trim().isEmpty()) return;
}
String[] topics = generateHttpReqTopics(service); String[] topics = generateHttpReqTopics(service);
String consumerid = generateHttpConsumerid(topics, service); String consumerid = generateHttpConsumerid(topics, service);
if (messageNodes.containsKey(consumerid)) throw new RuntimeException("consumerid(" + consumerid + ") is repeat"); if (messageNodes.containsKey(consumerid)) throw new RuntimeException("consumerid(" + consumerid + ") is repeat");

View File

@@ -0,0 +1,25 @@
/*
* 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.mq;
import org.redkale.util.AnyValue;
/**
* 自定义的MessageAgent加载器
*
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
* @since 2.4.0
*/
public interface MessageAgentLoader {
public boolean match(AnyValue config);
public Class<? extends MessageAgent> agentClass();
}

View File

@@ -269,6 +269,11 @@ class AsyncAioTcpConnection extends AsyncConnection {
}; };
} }
@Override
public InputStream newInputStream() {
return Channels.newInputStream(readableByteChannel());
}
@Override @Override
public final void close() throws IOException { public final void close() throws IOException {
super.close(); super.close();

View File

@@ -22,7 +22,7 @@ import org.redkale.util.*;
* *
* @author zhangjx * @author zhangjx
*/ */
public abstract class AsyncConnection implements AutoCloseable { public abstract class AsyncConnection implements ChannelContext, Channel, AutoCloseable {
private SSLContext sslContext; private SSLContext sslContext;
@@ -134,6 +134,8 @@ public abstract class AsyncConnection implements AutoCloseable {
public abstract WritableByteChannel writableByteChannel(); public abstract WritableByteChannel writableByteChannel();
protected abstract InputStream newInputStream();
public abstract void read(CompletionHandler<Integer, ByteBuffer> handler); public abstract void read(CompletionHandler<Integer, ByteBuffer> handler);
//src会写完才会回调 //src会写完才会回调
@@ -145,26 +147,29 @@ public abstract class AsyncConnection implements AutoCloseable {
} }
public final void write(byte[] bytes, CompletionHandler<Integer, Void> handler) { public final void write(byte[] bytes, CompletionHandler<Integer, Void> handler) {
write(bytes, 0, bytes.length, null, 0, 0, handler); write(bytes, 0, bytes.length, null, 0, 0, null, null, handler);
} }
public final void write(ByteTuple array, CompletionHandler<Integer, Void> handler) { public final void write(ByteTuple array, CompletionHandler<Integer, Void> handler) {
write(array.content(), array.offset(), array.length(), null, 0, 0, handler); write(array.content(), array.offset(), array.length(), null, 0, 0, null, null, handler);
} }
public final void write(byte[] bytes, int offset, int length, CompletionHandler<Integer, Void> handler) { public final void write(byte[] bytes, int offset, int length, CompletionHandler<Integer, Void> handler) {
write(bytes, offset, length, null, 0, 0, handler); write(bytes, offset, length, null, 0, 0, null, null, handler);
} }
public final void write(ByteTuple header, ByteTuple body, CompletionHandler<Integer, Void> handler) { public final void write(ByteTuple header, ByteTuple body, CompletionHandler<Integer, Void> handler) {
write(header.content(), header.offset(), header.length(), body == null ? null : body.content(), body == null ? 0 : body.offset(), body == null ? 0 : body.length(), handler); write(header.content(), header.offset(), header.length(), body == null ? null : body.content(), body == null ? 0 : body.offset(), body == null ? 0 : body.length(), null, null, handler);
} }
public void write(byte[] headerContent, int headerOffset, int headerLength, byte[] bodyContent, int bodyOffset, int bodyLength, CompletionHandler<Integer, Void> handler) { public void write(byte[] headerContent, int headerOffset, int headerLength, byte[] bodyContent, int bodyOffset, int bodyLength, Consumer bodyCallback, Object bodyAttachment, CompletionHandler<Integer, Void> handler) {
final ByteBuffer buffer = pollWriteBuffer(); final ByteBuffer buffer = pollWriteBuffer();
if (buffer.remaining() >= headerLength + bodyLength) { if (buffer.remaining() >= headerLength + bodyLength) {
buffer.put(headerContent, headerOffset, headerLength); buffer.put(headerContent, headerOffset, headerLength);
if (bodyLength > 0) buffer.put(bodyContent, bodyOffset, bodyLength); if (bodyLength > 0) {
buffer.put(bodyContent, bodyOffset, bodyLength);
if (bodyCallback != null) bodyCallback.accept(bodyAttachment);
}
buffer.flip(); buffer.flip();
CompletionHandler<Integer, Void> newhandler = new CompletionHandler<Integer, Void>() { CompletionHandler<Integer, Void> newhandler = new CompletionHandler<Integer, Void>() {
@Override @Override
@@ -183,7 +188,10 @@ public abstract class AsyncConnection implements AutoCloseable {
} else { } else {
ByteBufferWriter writer = ByteBufferWriter.create(bufferSupplier, buffer); ByteBufferWriter writer = ByteBufferWriter.create(bufferSupplier, buffer);
writer.put(headerContent, headerOffset, headerLength); writer.put(headerContent, headerOffset, headerLength);
if (bodyLength > 0) writer.put(bodyContent, bodyOffset, bodyLength); if (bodyLength > 0) {
writer.put(bodyContent, bodyOffset, bodyLength);
if (bodyCallback != null) bodyCallback.accept(bodyAttachment);
}
final ByteBuffer[] buffers = writer.toBuffers(); final ByteBuffer[] buffers = writer.toBuffers();
CompletionHandler<Integer, Void> newhandler = new CompletionHandler<Integer, Void>() { CompletionHandler<Integer, Void> newhandler = new CompletionHandler<Integer, Void>() {
@Override @Override
@@ -285,6 +293,44 @@ public abstract class AsyncConnection implements AutoCloseable {
} }
} }
//返回 是否over
public final boolean writePipelineData(int pipelineIndex, int pipelineCount, ByteTuple header, ByteTuple body) {
return writePipelineData(pipelineIndex, pipelineCount, header.content(), header.offset(), header.length(), body == null ? null : body.content(), body == null ? 0 : body.offset(), body == null ? 0 : body.length());
}
//返回 是否over
public boolean writePipelineData(int pipelineIndex, int pipelineCount, byte[] headerContent, int headerOffset, int headerLength, byte[] bodyContent, int bodyOffset, int bodyLength) {
synchronized (this) {
ByteBufferWriter writer = this.pipelineWriter;
if (writer == null) {
writer = ByteBufferWriter.create(getBufferSupplier());
this.pipelineWriter = writer;
}
if (this.pipelineDataNode == null && pipelineIndex == writer.getWriteBytesCounter() + 1) {
writer.put(headerContent, headerOffset, headerLength, bodyContent, bodyOffset, bodyLength);
return (pipelineIndex == pipelineCount);
} else {
PipelineDataNode dataNode = this.pipelineDataNode;
if (dataNode == null) {
dataNode = new PipelineDataNode();
this.pipelineDataNode = dataNode;
}
if (pipelineIndex == pipelineCount) { //此时pipelineCount为最大值
dataNode.pipelineCount = pipelineCount;
}
dataNode.put(pipelineIndex, headerContent, headerOffset, headerLength, bodyContent, bodyOffset, bodyLength);
if (writer.getWriteBytesCounter() + dataNode.itemsize == dataNode.pipelineCount) {
for (PipelineDataItem item : dataNode.arrayItems()) {
writer.put(item.data);
}
this.pipelineDataNode = null;
return true;
}
return false;
}
}
}
private static class PipelineDataNode { private static class PipelineDataNode {
public int pipelineCount; public int pipelineCount;
@@ -320,6 +366,19 @@ public abstract class AsyncConnection implements AutoCloseable {
} }
itemsize++; itemsize++;
} }
public void put(int pipelineIndex, byte[] headerContent, int headerOffset, int headerLength, byte[] bodyContent, int bodyOffset, int bodyLength) {
if (tail == null) {
head = new PipelineDataItem(pipelineIndex, headerContent, headerOffset, headerLength, bodyContent, bodyOffset, bodyLength);
tail = head;
} else {
PipelineDataItem item = new PipelineDataItem(pipelineIndex, headerContent, headerOffset, headerLength, bodyContent, bodyOffset, bodyLength);
tail.next = item;
tail = item;
}
itemsize++;
}
} }
private static class PipelineDataItem implements Comparable<PipelineDataItem> { private static class PipelineDataItem implements Comparable<PipelineDataItem> {
@@ -335,6 +394,19 @@ public abstract class AsyncConnection implements AutoCloseable {
this.data = Arrays.copyOfRange(bs, offset, offset + length); this.data = Arrays.copyOfRange(bs, offset, offset + length);
} }
public PipelineDataItem(int index, byte[] headerContent, int headerOffset, int headerLength, byte[] bodyContent, int bodyOffset, int bodyLength) {
this.index = index;
this.data = bodyLength > 0 ? copyOfRange(headerContent, headerOffset, headerLength, bodyContent, bodyOffset, bodyLength)
: Arrays.copyOfRange(headerContent, headerOffset, headerOffset + headerLength);
}
private static byte[] copyOfRange(byte[] headerContent, int headerOffset, int headerLength, byte[] bodyContent, int bodyOffset, int bodyLength) {
byte[] result = new byte[headerLength + bodyLength];
System.arraycopy(headerContent, headerOffset, result, 0, headerLength);
System.arraycopy(bodyContent, bodyOffset, result, headerLength, bodyLength);
return result;
}
@Override @Override
public int compareTo(PipelineDataItem o) { public int compareTo(PipelineDataItem o) {
return this.index - o.index; return this.index - o.index;
@@ -409,6 +481,7 @@ public abstract class AsyncConnection implements AutoCloseable {
for (Object obj : attributes.values()) { for (Object obj : attributes.values()) {
if (obj instanceof AutoCloseable) ((AutoCloseable) obj).close(); if (obj instanceof AutoCloseable) ((AutoCloseable) obj).close();
} }
attributes.clear();
} catch (Exception io) { } catch (Exception io) {
} }
} }
@@ -422,24 +495,29 @@ public abstract class AsyncConnection implements AutoCloseable {
this.subobject = value; this.subobject = value;
} }
@Override
public void setAttribute(String name, Object value) { public void setAttribute(String name, Object value) {
if (this.attributes == null) this.attributes = new HashMap<>(); if (this.attributes == null) this.attributes = new HashMap<>();
this.attributes.put(name, value); this.attributes.put(name, value);
} }
@Override
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public final <T> T getAttribute(String name) { public final <T> T getAttribute(String name) {
return (T) (this.attributes == null ? null : this.attributes.get(name)); return (T) (this.attributes == null ? null : this.attributes.get(name));
} }
@Override
public final void removeAttribute(String name) { public final void removeAttribute(String name) {
if (this.attributes != null) this.attributes.remove(name); if (this.attributes != null) this.attributes.remove(name);
} }
@Override
public final Map<String, Object> getAttributes() { public final Map<String, Object> getAttributes() {
return this.attributes; return this.attributes;
} }
@Override
public final void clearAttribute() { public final void clearAttribute() {
if (this.attributes != null) this.attributes.clear(); if (this.attributes != null) this.attributes.clear();
} }

View File

@@ -20,7 +20,9 @@ import java.util.concurrent.*;
*/ */
class AsyncNioCompletionHandler<A> implements CompletionHandler<Integer, A>, Runnable { class AsyncNioCompletionHandler<A> implements CompletionHandler<Integer, A>, Runnable {
private final CompletionHandler<Integer, A> handler; private final AsyncNioConnection conn;
private CompletionHandler<Integer, A> handler;
private A attachment; private A attachment;
@@ -28,20 +30,35 @@ class AsyncNioCompletionHandler<A> implements CompletionHandler<Integer, A>, Run
private ByteBuffer[] buffers; private ByteBuffer[] buffers;
private AsyncNioConnection conn; private ByteBuffer buffer;
public AsyncNioCompletionHandler(CompletionHandler<Integer, A> handler, A attachment) { public AsyncNioCompletionHandler(AsyncNioConnection conn) {
this.conn = conn;
}
public void handler(CompletionHandler<Integer, A> handler, A attachment) {
this.handler = handler; this.handler = handler;
this.attachment = attachment; this.attachment = attachment;
} }
public void setConnBuffers(AsyncNioConnection conn, ByteBuffer... buffs) { public void attachment(A attachment) {
this.conn = conn; this.attachment = attachment;
}
public void buffers(ByteBuffer... buffs) {
this.buffers = buffs; this.buffers = buffs;
} }
public void setAttachment(A attachment) { public void buffer(ByteBuffer buff) {
this.attachment = attachment; this.buffer = buff;
}
private void clear() {
this.handler = null;
this.attachment = null;
this.timeoutFuture = null;
this.buffers = null;
this.buffer = null;
} }
@Override @Override
@@ -51,10 +68,17 @@ class AsyncNioCompletionHandler<A> implements CompletionHandler<Integer, A>, Run
this.timeoutFuture = null; this.timeoutFuture = null;
future.cancel(true); future.cancel(true);
} }
if (conn != null && buffers != null) { if (conn != null) {
conn.offerBuffer(buffers); if (buffers != null) {
conn.offerBuffer(buffers);
} else if (buffer != null) {
conn.offerBuffer(buffer);
}
} }
handler.completed(result, attachment); CompletionHandler<Integer, A> handler0 = handler;
A attachment0 = attachment;
clear();
handler0.completed(result, attachment0);
} }
@Override @Override
@@ -64,18 +88,32 @@ class AsyncNioCompletionHandler<A> implements CompletionHandler<Integer, A>, Run
this.timeoutFuture = null; this.timeoutFuture = null;
future.cancel(true); future.cancel(true);
} }
if (conn != null && buffers != null) { if (conn != null) {
conn.offerBuffer(buffers); if (buffers != null) {
conn.offerBuffer(buffers);
} else if (buffer != null) {
conn.offerBuffer(buffer);
}
} }
handler.failed(exc, attachment); CompletionHandler<Integer, A> handler0 = handler;
A attachment0 = attachment;
clear();
handler0.failed(exc, attachment0);
} }
@Override @Override
public void run() { public void run() {
if (conn != null && buffers != null) { if (conn != null) {
conn.offerBuffer(buffers); if (buffers != null) {
conn.offerBuffer(buffers);
} else if (buffer != null) {
conn.offerBuffer(buffer);
}
} }
handler.failed(new TimeoutException(), attachment); CompletionHandler<Integer, A> handler0 = handler;
A attachment0 = attachment;
clear();
handler0.failed(new TimeoutException(), attachment0);
} }
} }

View File

@@ -47,6 +47,8 @@ abstract class AsyncNioConnection extends AsyncConnection {
protected SelectionKey connectKey; protected SelectionKey connectKey;
//-------------------------------- 读操作 -------------------------------------- //-------------------------------- 读操作 --------------------------------------
protected final AsyncNioCompletionHandler<ByteBuffer> readTimeoutCompletionHandler = new AsyncNioCompletionHandler<>(this);
protected int readTimeoutSeconds; protected int readTimeoutSeconds;
int currReadInvoker; int currReadInvoker;
@@ -60,6 +62,8 @@ abstract class AsyncNioConnection extends AsyncConnection {
protected SelectionKey readKey; protected SelectionKey readKey;
//-------------------------------- 写操作 -------------------------------------- //-------------------------------- 写操作 --------------------------------------
protected final AsyncNioCompletionHandler<Object> writeTimeoutCompletionHandler = new AsyncNioCompletionHandler<>(this);
protected int writeTimeoutSeconds; protected int writeTimeoutSeconds;
int currWriteInvoker; int currWriteInvoker;
@@ -76,6 +80,10 @@ abstract class AsyncNioConnection extends AsyncConnection {
protected int writeByteTuple2Length; protected int writeByteTuple2Length;
protected Consumer writeByteTuple2Callback;
protected Object writeByteTuple2Attachment;
//写操作, 二选一要么writeByteBuffer有值要么writeByteBuffers、writeOffset、writeLength有值 //写操作, 二选一要么writeByteBuffer有值要么writeByteBuffers、writeOffset、writeLength有值
protected ByteBuffer writeByteBuffer; protected ByteBuffer writeByteBuffer;
@@ -163,17 +171,18 @@ abstract class AsyncNioConnection extends AsyncConnection {
} }
this.readPending = true; this.readPending = true;
if (this.readTimeoutSeconds > 0) { if (this.readTimeoutSeconds > 0) {
AsyncNioCompletionHandler newhandler = new AsyncNioCompletionHandler(handler, this.readByteBuffer); AsyncNioCompletionHandler newhandler = this.readTimeoutCompletionHandler;
newhandler.handler(handler, this.readByteBuffer); // new AsyncNioCompletionHandler(handler, this.readByteBuffer);
this.readCompletionHandler = newhandler; this.readCompletionHandler = newhandler;
newhandler.timeoutFuture = ioGroup.scheduleTimeout(newhandler, this.readTimeoutSeconds, TimeUnit.SECONDS); newhandler.timeoutFuture = ioGroup.scheduleTimeout(newhandler, this.readTimeoutSeconds, TimeUnit.SECONDS);
} else { } else {
this.readCompletionHandler = handler; this.readCompletionHandler = handler;
} }
doRead(currReadInvoker < MAX_INVOKER_ONSTACK && this.ioThread.inCurrThread()); doRead(currReadInvoker < MAX_INVOKER_ONSTACK || this.ioThread.inCurrThread()); //同一线程中Selector.wakeup无效
} }
@Override @Override
public void write(byte[] headerContent, int headerOffset, int headerLength, byte[] bodyContent, int bodyOffset, int bodyLength, CompletionHandler<Integer, Void> handler) { public void write(byte[] headerContent, int headerOffset, int headerLength, byte[] bodyContent, int bodyOffset, int bodyLength, Consumer bodyCallback, Object bodyAttachment, CompletionHandler<Integer, Void> handler) {
Objects.requireNonNull(headerContent); Objects.requireNonNull(headerContent);
Objects.requireNonNull(handler); Objects.requireNonNull(handler);
if (!this.isConnected()) { if (!this.isConnected()) {
@@ -191,15 +200,20 @@ abstract class AsyncNioConnection extends AsyncConnection {
this.writeByteTuple2Array = bodyContent; this.writeByteTuple2Array = bodyContent;
this.writeByteTuple2Offset = bodyOffset; this.writeByteTuple2Offset = bodyOffset;
this.writeByteTuple2Length = bodyLength; this.writeByteTuple2Length = bodyLength;
this.writeByteTuple2Callback = bodyCallback;
this.writeByteTuple2Attachment = bodyAttachment;
this.writeAttachment = null; this.writeAttachment = null;
if (this.writeTimeoutSeconds > 0) { if (this.writeTimeoutSeconds > 0) {
AsyncNioCompletionHandler newhandler = new AsyncNioCompletionHandler(handler, null); AsyncNioCompletionHandler newhandler = this.writeTimeoutCompletionHandler;
newhandler.handler(handler, null); // new AsyncNioCompletionHandler(handler, null);
this.writeCompletionHandler = newhandler; this.writeCompletionHandler = newhandler;
newhandler.timeoutFuture = ioGroup.scheduleTimeout(newhandler, this.writeTimeoutSeconds, TimeUnit.SECONDS); newhandler.timeoutFuture = ioGroup.scheduleTimeout(newhandler, this.writeTimeoutSeconds, TimeUnit.SECONDS);
} else { } else {
this.writeCompletionHandler = new AsyncNioCompletionHandler(handler, null); AsyncNioCompletionHandler newhandler = this.writeTimeoutCompletionHandler;
newhandler.handler(handler, null); // new AsyncNioCompletionHandler(handler, null);
this.writeCompletionHandler = newhandler;
} }
doWrite(true || !client || currWriteInvoker < MAX_INVOKER_ONSTACK); // this.ioThread.inCurrThread() // !client && ioThread.workExecutor == null doWrite(true); //如果不是true则bodyCallback的执行可能会切换线程
} }
@Override @Override
@@ -218,7 +232,8 @@ abstract class AsyncNioConnection extends AsyncConnection {
this.writeByteBuffer = src; this.writeByteBuffer = src;
this.writeAttachment = attachment; this.writeAttachment = attachment;
if (this.writeTimeoutSeconds > 0) { if (this.writeTimeoutSeconds > 0) {
AsyncNioCompletionHandler newhandler = new AsyncNioCompletionHandler(handler, attachment); AsyncNioCompletionHandler newhandler = this.writeTimeoutCompletionHandler;
newhandler.handler(handler, attachment); // new AsyncNioCompletionHandler(handler, attachment);
this.writeCompletionHandler = newhandler; this.writeCompletionHandler = newhandler;
newhandler.timeoutFuture = ioGroup.scheduleTimeout(newhandler, this.writeTimeoutSeconds, TimeUnit.SECONDS); newhandler.timeoutFuture = ioGroup.scheduleTimeout(newhandler, this.writeTimeoutSeconds, TimeUnit.SECONDS);
} else { } else {
@@ -245,7 +260,8 @@ abstract class AsyncNioConnection extends AsyncConnection {
this.writeLength = length; this.writeLength = length;
this.writeAttachment = attachment; this.writeAttachment = attachment;
if (this.writeTimeoutSeconds > 0) { if (this.writeTimeoutSeconds > 0) {
AsyncNioCompletionHandler newhandler = new AsyncNioCompletionHandler(handler, attachment); AsyncNioCompletionHandler newhandler = this.writeTimeoutCompletionHandler;
newhandler.handler(handler, attachment); // new AsyncNioCompletionHandler(handler, attachment);
this.writeCompletionHandler = newhandler; this.writeCompletionHandler = newhandler;
newhandler.timeoutFuture = ioGroup.scheduleTimeout(newhandler, this.writeTimeoutSeconds, TimeUnit.SECONDS); newhandler.timeoutFuture = ioGroup.scheduleTimeout(newhandler, this.writeTimeoutSeconds, TimeUnit.SECONDS);
} else { } else {
@@ -262,8 +278,8 @@ abstract class AsyncNioConnection extends AsyncConnection {
currReadInvoker++; currReadInvoker++;
if (this.readByteBuffer == null) { if (this.readByteBuffer == null) {
this.readByteBuffer = pollReadBuffer(); this.readByteBuffer = pollReadBuffer();
if (this.readTimeoutSeconds > 0 && this.readCompletionHandler != null) { if (this.readTimeoutSeconds > 0) {
((AsyncNioCompletionHandler) this.readCompletionHandler).setAttachment(this.readByteBuffer); this.readTimeoutCompletionHandler.attachment(this.readByteBuffer);
} }
} }
readCount = implRead(readByteBuffer); readCount = implRead(readByteBuffer);
@@ -295,12 +311,15 @@ abstract class AsyncNioConnection extends AsyncConnection {
int totalCount = 0; int totalCount = 0;
boolean hasRemain = true; boolean hasRemain = true;
if (invokeDirect) currWriteInvoker++; if (invokeDirect) currWriteInvoker++;
while (invokeDirect && hasRemain) { while (invokeDirect && hasRemain) { //必须要将buffer写完为止
if (writeByteTuple1Array != null) { if (writeByteTuple1Array != null) {
final ByteBuffer buffer = pollWriteBuffer(); final ByteBuffer buffer = pollWriteBuffer();
if (buffer.remaining() >= writeByteTuple1Length + writeByteTuple2Length) { if (buffer.remaining() >= writeByteTuple1Length + writeByteTuple2Length) {
buffer.put(writeByteTuple1Array, writeByteTuple1Offset, writeByteTuple1Length); buffer.put(writeByteTuple1Array, writeByteTuple1Offset, writeByteTuple1Length);
if (writeByteTuple2Length > 0) buffer.put(writeByteTuple2Array, writeByteTuple2Offset, writeByteTuple2Length); if (writeByteTuple2Length > 0) {
buffer.put(writeByteTuple2Array, writeByteTuple2Offset, writeByteTuple2Length);
if (writeByteTuple2Callback != null) writeByteTuple2Callback.accept(writeByteTuple2Attachment);
}
buffer.flip(); buffer.flip();
writeByteBuffer = buffer; writeByteBuffer = buffer;
writeByteTuple1Array = null; writeByteTuple1Array = null;
@@ -309,10 +328,15 @@ abstract class AsyncNioConnection extends AsyncConnection {
writeByteTuple2Array = null; writeByteTuple2Array = null;
writeByteTuple2Offset = 0; writeByteTuple2Offset = 0;
writeByteTuple2Length = 0; writeByteTuple2Length = 0;
writeByteTuple2Callback = null;
writeByteTuple2Attachment = null;
} else { } else {
ByteBufferWriter writer = ByteBufferWriter.create(getBufferSupplier(), buffer); ByteBufferWriter writer = ByteBufferWriter.create(getBufferSupplier(), buffer);
writer.put(writeByteTuple1Array, writeByteTuple1Offset, writeByteTuple1Length); writer.put(writeByteTuple1Array, writeByteTuple1Offset, writeByteTuple1Length);
if (writeByteTuple2Length > 0) writer.put(writeByteTuple2Array, writeByteTuple2Offset, writeByteTuple2Length); if (writeByteTuple2Length > 0) {
writer.put(writeByteTuple2Array, writeByteTuple2Offset, writeByteTuple2Length);
if (writeByteTuple2Callback != null) writeByteTuple2Callback.accept(writeByteTuple2Attachment);
}
final ByteBuffer[] buffers = writer.toBuffers(); final ByteBuffer[] buffers = writer.toBuffers();
writeByteBuffers = buffers; writeByteBuffers = buffers;
writeOffset = 0; writeOffset = 0;
@@ -323,11 +347,15 @@ abstract class AsyncNioConnection extends AsyncConnection {
writeByteTuple2Array = null; writeByteTuple2Array = null;
writeByteTuple2Offset = 0; writeByteTuple2Offset = 0;
writeByteTuple2Length = 0; writeByteTuple2Length = 0;
writeByteTuple2Callback = null;
writeByteTuple2Attachment = null;
} }
if (writeByteBuffer == null) { if (this.writeCompletionHandler == this.writeTimeoutCompletionHandler) {
((AsyncNioCompletionHandler) writeCompletionHandler).setConnBuffers(this, writeByteBuffers); if (writeByteBuffer == null) {
} else { this.writeTimeoutCompletionHandler.buffers(writeByteBuffers);
((AsyncNioCompletionHandler) writeCompletionHandler).setConnBuffers(this, writeByteBuffer); } else {
this.writeTimeoutCompletionHandler.buffer(writeByteBuffer);
}
} }
} }
int writeCount; int writeCount;

View File

@@ -198,4 +198,76 @@ class AsyncNioTcpConnection extends AsyncNioConnection {
if (this.readKey != null) this.readKey.cancel(); if (this.readKey != null) this.readKey.cancel();
if (this.writeKey != null) this.writeKey.cancel(); if (this.writeKey != null) this.writeKey.cancel();
} }
@Override
public InputStream newInputStream() {
return new InputStream() {
ByteBuffer bb;
int count;
@Override
public synchronized int read() throws IOException {
if (bb == null || !bb.hasRemaining()) {
int r = readBuffer();
if (r < 1) return -1;
}
return bb.get() & 0xff;
}
@Override
public synchronized int read(byte b[], int off, int len) throws IOException {
if (b == null) {
throw new NullPointerException();
} else if (off < 0 || len < 0 || len > b.length - off) {
throw new IndexOutOfBoundsException();
} else if (len == 0) {
return 0;
}
if (bb == null || !bb.hasRemaining()) {
int r = readBuffer();
if (r < 1) return -1;
}
int size = Math.min(b.length, Math.min(len, bb.remaining()));
bb.get(b, off, size);
return size;
}
@Override
public void close() throws IOException {
if (bb != null) {
offerBuffer(bb);
bb = null;
}
channel.close();
}
@Override
public int available() throws IOException {
if (bb == null || !bb.hasRemaining()) return 0;
return bb.remaining();
}
private int readBuffer() throws IOException {
if (bb == null) {
bb = pollReadBuffer();
} else {
bb.clear();
}
try {
int size = channel.read(bb);
bb.flip();
return size;
} catch (IOException ioe) {
throw ioe;
} catch (Exception e) {
throw new IOException(e);
}
}
};
}
} }

View File

@@ -168,4 +168,9 @@ class AsyncNioUdpConnection extends AsyncNioConnection {
if (this.writeKey != null) this.writeKey.cancel(); if (this.writeKey != null) this.writeKey.cancel();
} }
@Override
protected InputStream newInputStream() {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
} }

View File

@@ -0,0 +1,31 @@
/*
* 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.*;
/**
* 当前一个Request绑定的AsyncConnection 类似Session但概念上不同于sessionid对应的对象
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
* @since 2.4.0
*/
public interface ChannelContext {
public void setAttribute(String name, Object value);
@SuppressWarnings("unchecked")
public <T> T getAttribute(String name);
public void removeAttribute(String name);
public Map<String, Object> getAttributes();
public void clearAttribute();
}

View File

@@ -5,6 +5,7 @@
*/ */
package org.redkale.net; package org.redkale.net;
import java.io.*;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.util.*; import java.util.*;
import org.redkale.convert.bson.BsonConvert; import org.redkale.convert.bson.BsonConvert;
@@ -107,6 +108,10 @@ public abstract class Request<C extends Context> {
return properties; return properties;
} }
protected InputStream newInputStream() {
return channel.newInputStream();
}
public <T> T setAttribute(String name, T value) { public <T> T setAttribute(String name, T value) {
attributes.put(name, value); attributes.put(name, value);
return value; return value;
@@ -126,6 +131,10 @@ public abstract class Request<C extends Context> {
return attributes; return attributes;
} }
public ChannelContext getChannelContext() {
return channel;
}
public C getContext() { public C getContext() {
return this.context; return this.context;
} }

View File

@@ -256,6 +256,27 @@ public abstract class Response<C extends Context, R extends Request<C>> {
} }
} }
public <A> void finish(boolean kill, final byte[] bs, int offset, int length, final byte[] bs2, int offset2, int length2, Consumer<A> callback, A attachment) {
if (!this.inited) return; //避免重复关闭
if (kill) refuseAlive();
if (this.channel.hasPipelineData()) {
this.channel.flushPipelineData(null, new CompletionHandler<Integer, Void>() {
@Override
public void completed(Integer result, Void attachment) {
channel.write(bs, offset, length, bs2, offset2, length2, callback, attachment, finishBytesHandler);
}
@Override
public void failed(Throwable exc, Void attachment) {
finishBytesHandler.failed(exc, attachment);
}
});
} else {
this.channel.write(bs, offset, length, bs2, offset2, length2, callback, attachment, finishBytesHandler);
}
}
protected final void finish(ByteBuffer buffer) { protected final void finish(ByteBuffer buffer) {
finish(false, buffer); finish(false, buffer);
} }

View File

@@ -32,8 +32,8 @@ public abstract class Server<K extends Serializable, C extends Context, R extend
public static final String RESNAME_SERVER_ROOT = "SERVER_ROOT"; public static final String RESNAME_SERVER_ROOT = "SERVER_ROOT";
@Deprecated //@deprecated 2.3.0 使用RESNAME_APP_EXECUTOR //@Deprecated //@deprecated 2.3.0 使用RESNAME_APP_EXECUTOR
public static final String RESNAME_SERVER_EXECUTOR2 = "SERVER_EXECUTOR"; //public static final String RESNAME_SERVER_EXECUTOR2 = "SERVER_EXECUTOR";
public static final String RESNAME_SERVER_RESFACTORY = "SERVER_RESFACTORY"; public static final String RESNAME_SERVER_RESFACTORY = "SERVER_RESFACTORY";

View File

@@ -157,14 +157,18 @@ public abstract class Client<R extends ClientRequest, P> {
} }
public CompletableFuture<P> sendAsync(R request) { public CompletableFuture<P> sendAsync(R request) {
return connect().thenCompose(conn -> conn.writeChannel(request)); return connect(null).thenCompose(conn -> conn.writeChannel(request));
}
public CompletableFuture<P> sendAsync(ChannelContext context, R request) {
return connect(context).thenCompose(conn -> conn.writeChannel(request));
} }
protected CompletableFuture<P> writeChannel(ClientConnection conn, R request) { protected CompletableFuture<P> writeChannel(ClientConnection conn, R request) {
return conn.writeChannel(request); return conn.writeChannel(request);
} }
protected CompletableFuture<ClientConnection> connect() { protected CompletableFuture<ClientConnection> connect(ChannelContext context) {
ClientConnection minRunningConn = null; ClientConnection minRunningConn = null;
for (int i = 0; i < this.connArray.length; i++) { for (int i = 0; i < this.connArray.length; i++) {
final int index = i; final int index = i;

View File

@@ -30,15 +30,30 @@ public class HttpContext extends Context {
protected final String remoteAddrHeader; protected final String remoteAddrHeader;
protected final boolean lazyHeaders; protected boolean lazyHeaders; //存在动态改值
// protected RequestURINode[] uriCacheNodes;
public HttpContext(HttpContextConfig config) { public HttpContext(HttpContextConfig config) {
super(config); super(config);
this.remoteAddrHeader = config.remoteAddrHeader; this.remoteAddrHeader = config.remoteAddrHeader;
this.lazyHeaders = config.lazyHeaders;
random.setSeed(Math.abs(System.nanoTime())); random.setSeed(Math.abs(System.nanoTime()));
} }
// protected RequestURINode[] getUriCacheNodes() {
// return uriCacheNodes;
// }
//
// protected void addRequestURINode(String path) {
// RequestURINode node = new RequestURINode(path);
// synchronized (this) {
// if (this.uriCacheNodes != null) {
// for (int i = 0; i < uriCacheNodes.length; i++) {
// if (uriCacheNodes[i].path.equals(path)) return;
// }
// }
// this.uriCacheNodes = Utility.append(this.uriCacheNodes, node);
// }
// }
protected String createSessionid() { protected String createSessionid() {
byte[] bytes = new byte[16]; byte[] bytes = new byte[16];
random.nextBytes(bytes); random.nextBytes(bytes);
@@ -162,6 +177,23 @@ public class HttpContext extends Context {
public String remoteAddrHeader; public String remoteAddrHeader;
public boolean lazyHeaders; }
protected static class RequestURINode {
public final byte[] bytes;
public final String path;
public RequestURINode(String path) {
this.path = path;
this.bytes = path.getBytes();
}
@Override
public String toString() {
return "RequestURINode{" + "path=" + path + '}';
}
} }
} }

View File

@@ -46,10 +46,6 @@ public @interface HttpMapping {
*/ */
int cacheseconds() default 0; int cacheseconds() default 0;
//json结果的长度, 临时功能, 仅供Rest的HttpResponse.finishJson(final int length, final Object obj) 方法使用
@Deprecated
int length() default 0;
/** /**
* 是否只接受RPC请求 默认为false * 是否只接受RPC请求 默认为false
* *

View File

@@ -41,12 +41,14 @@ public class HttpPrepareServlet extends PrepareServlet<String, HttpContext, Http
private final Object excludeLock = new Object(); private final Object excludeLock = new Object();
protected HttpContext context;
protected boolean lazyHeaders = true;
private Map<String, BiPredicate<String, String>> forbidURIMaps; //禁用的URL的正则表达式, 必须与 forbidURIPredicates 保持一致 private Map<String, BiPredicate<String, String>> forbidURIMaps; //禁用的URL的正则表达式, 必须与 forbidURIPredicates 保持一致
private BiPredicate<String, String>[] forbidURIPredicates; //禁用的URL的Predicate, 必须与 forbidURIMaps 保持一致 private BiPredicate<String, String>[] forbidURIPredicates; //禁用的URL的Predicate, 必须与 forbidURIMaps 保持一致
final List<HttpRender> renders = new ArrayList<>();
private List<HttpServlet> removeHttpServlet(final Predicate<MappingEntry> predicateEntry, final Predicate<Map.Entry<String, WebSocketServlet>> predicateFilter) { private List<HttpServlet> removeHttpServlet(final Predicate<MappingEntry> predicateEntry, final Predicate<Map.Entry<String, WebSocketServlet>> predicateFilter) {
List<HttpServlet> servlets = new ArrayList<>(); List<HttpServlet> servlets = new ArrayList<>();
synchronized (allMapStrings) { synchronized (allMapStrings) {
@@ -204,6 +206,8 @@ public class HttpPrepareServlet extends PrepareServlet<String, HttpContext, Http
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public void init(HttpContext context, AnyValue config) { public void init(HttpContext context, AnyValue config) {
super.init(context, config); //必须要执行 super.init(context, config); //必须要执行
this.context = context;
context.lazyHeaders = lazyHeaders;
Collection<HttpServlet> servlets = getServlets(); Collection<HttpServlet> servlets = getServlets();
servlets.forEach(s -> { servlets.forEach(s -> {
s.preInit(context, getServletConf(s)); s.preInit(context, getServletConf(s));
@@ -234,29 +238,16 @@ public class HttpPrepareServlet extends PrepareServlet<String, HttpContext, Http
this.resourceHttpServlet = new HttpResourceServlet(); this.resourceHttpServlet = new HttpResourceServlet();
logger.log(Level.WARNING, "init HttpResourceSerlvet(" + resServlet + ") error", e); logger.log(Level.WARNING, "init HttpResourceSerlvet(" + resServlet + ") error", e);
} }
{ //获取render的suffixs
AnyValue renderConfig = config.getAnyValue("render");
if (renderConfig != null) {
String[] suffixs = renderConfig.getValue("suffixs", ".htel").toLowerCase().split(";");
((HttpResourceServlet) this.resourceHttpServlet).renderSuffixs = suffixs;
}
}
context.getResourceFactory().inject(this.resourceHttpServlet); context.getResourceFactory().inject(this.resourceHttpServlet);
this.resourceHttpServlet.init(context, resConfig); this.resourceHttpServlet.init(context, resConfig);
} }
{ //设置TemplateEngine
AnyValue[] renderConfigs = config.getAnyValues("render");
if (renderConfigs != null) {
for (AnyValue renderConfig : renderConfigs) {
String renderType = renderConfig.getValue("value");
try {
HttpRender render = (HttpRender) Thread.currentThread().getContextClassLoader().loadClass(renderType).getDeclaredConstructor().newInstance();
for (HttpRender one : renders) {
if (one.getType().equals(render.getType())) throw new RuntimeException("HttpRender(" + renderType + ") repeat");
}
context.getResourceFactory().inject(render);
render.init(context, renderConfig);
renders.add(render);
} catch (Throwable e) {
logger.log(Level.WARNING, "init HttpRender(" + renderType + ") error", e);
}
}
renders.sort((o1, o2) -> o1.getType().isAssignableFrom(o2.getType()) ? 1 : -1);
}
}
} }
@Override @Override
@@ -334,12 +325,16 @@ public class HttpPrepareServlet extends PrepareServlet<String, HttpContext, Http
if (!ws.repair()) prefix = "";//被设置为不自动追加前缀则清空prefix if (!ws.repair()) prefix = "";//被设置为不自动追加前缀则清空prefix
} }
} }
if (lazyHeaders && !Rest.isSimpleRestDyn(servlet)) {
lazyHeaders = false;
if (context != null) context.lazyHeaders = false; //启动后运行过程中执行addServlet
}
synchronized (allMapStrings) { //需要整段锁住 synchronized (allMapStrings) { //需要整段锁住
for (String mappingpath : mappingpaths) { for (String mappingpath : mappingpaths) {
if (mappingpath == null) continue; if (mappingpath == null) continue;
if (!prefix.toString().isEmpty()) mappingpath = prefix + mappingpath; if (!prefix.toString().isEmpty()) mappingpath = prefix + mappingpath;
if (Utility.contains(mappingpath, '.', '*', '{', '[', '(', '|', '^', '$', '+', '?', '\\')) { //是否是正则表达式)) if (Utility.contains(mappingpath, '*', '{', '[', '(', '|', '^', '$', '+', '?', '\\')) { //是否是正则表达式))
if (mappingpath.charAt(0) != '^') mappingpath = '^' + mappingpath; if (mappingpath.charAt(0) != '^') mappingpath = '^' + mappingpath;
if (mappingpath.endsWith("/*")) { if (mappingpath.endsWith("/*")) {
mappingpath = mappingpath.substring(0, mappingpath.length() - 1) + ".*"; mappingpath = mappingpath.substring(0, mappingpath.length() - 1) + ".*";
@@ -364,6 +359,7 @@ public class HttpPrepareServlet extends PrepareServlet<String, HttpContext, Http
} }
} else if (mappingpath != null && !mappingpath.isEmpty()) { } else if (mappingpath != null && !mappingpath.isEmpty()) {
if (servlet._actionmap != null && servlet._actionmap.containsKey(mappingpath)) { if (servlet._actionmap != null && servlet._actionmap.containsKey(mappingpath)) {
//context.addRequestURINode(mappingpath);
putMapping(mappingpath, new HttpServlet.HttpActionServlet(servlet._actionmap.get(mappingpath), servlet)); putMapping(mappingpath, new HttpServlet.HttpActionServlet(servlet._actionmap.get(mappingpath), servlet));
} else { } else {
putMapping(mappingpath, servlet); putMapping(mappingpath, servlet);

View File

@@ -15,9 +15,8 @@ import org.redkale.util.AnyValue;
* HttpResponse.finish(Object obj)内置对如下数据类型进行了特殊处理: * HttpResponse.finish(Object obj)内置对如下数据类型进行了特殊处理:
* CharSequence/String * CharSequence/String
* byte[] * byte[]
* ByteBuffer
* ByteBuffer[]
* File * File
* RetResult
* HttpResult * HttpResult
* </pre> * </pre>
* <p> * <p>
@@ -26,13 +25,11 @@ import org.redkale.util.AnyValue;
* 详情见: https://redkale.org * 详情见: https://redkale.org
* *
* @author zhangjx * @author zhangjx
* @param <T> 泛型
*/ */
public interface HttpRender<T> { public interface HttpRender {
public void init(HttpContext context, AnyValue config); public void init(HttpContext context, AnyValue config);
public <V extends T> void renderTo(HttpRequest request, HttpResponse response, Convert convert, V scope); public void renderTo(HttpRequest request, HttpResponse response, Convert convert, HttpScope scope);
public Class<T> getType();
} }

View File

@@ -10,9 +10,9 @@ import java.lang.annotation.Annotation;
import java.lang.reflect.Array; import java.lang.reflect.Array;
import java.net.*; import java.net.*;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.nio.channels.Channels;
import java.nio.charset.*; import java.nio.charset.*;
import java.util.*; import java.util.*;
import java.util.function.Supplier;
import java.util.logging.Level; import java.util.logging.Level;
import org.redkale.convert.*; import org.redkale.convert.*;
import org.redkale.convert.json.JsonConvert; import org.redkale.convert.json.JsonConvert;
@@ -34,6 +34,8 @@ import org.redkale.util.*;
*/ */
public class HttpRequest extends Request<HttpContext> { public class HttpRequest extends Request<HttpContext> {
private static final boolean pipelineSameHeaders = Boolean.getBoolean("http.request.pipeline.sameheaders");
protected static final Serializable CURRUSERID_NIL = new Serializable() { protected static final Serializable CURRUSERID_NIL = new Serializable() {
}; };
@@ -90,7 +92,7 @@ public class HttpRequest extends Request<HttpContext> {
// @since 2.1.0 // @since 2.1.0
protected Serializable currentUserid = CURRUSERID_NIL; protected Serializable currentUserid = CURRUSERID_NIL;
protected Object currentUser; protected Supplier<Object> currentUserSupplier;
protected boolean frombody; protected boolean frombody;
@@ -130,6 +132,10 @@ public class HttpRequest extends Request<HttpContext> {
protected String remoteAddr; protected String remoteAddr;
private String lastRequestURIString;
private byte[] lastRequestURIBytes;
private final ByteArray array; private final ByteArray array;
private byte[] headerBytes; private byte[] headerBytes;
@@ -289,7 +295,7 @@ public class HttpRequest extends Request<HttpContext> {
this.headerBytes = httplast.headerBytes; this.headerBytes = httplast.headerBytes;
this.headerParsed = httplast.headerParsed; this.headerParsed = httplast.headerParsed;
this.headers.putAll(httplast.headers); this.headers.putAll(httplast.headers);
} else if (context.lazyHeaders && getmethod) { } else if (context.lazyHeaders && getmethod) { //非GET必须要读header会有Content-Length
int rs = loadHeaderBytes(buffer); int rs = loadHeaderBytes(buffer);
if (rs != 0) return rs; if (rs != 0) return rs;
this.headerParsed = false; this.headerParsed = false;
@@ -444,13 +450,30 @@ public class HttpRequest extends Request<HttpContext> {
if (qst > 0) { if (qst > 0) {
this.requestURI = decodeable ? bytes.toDecodeString(0, qst, charset) : bytes.toString(0, qst, charset); this.requestURI = decodeable ? bytes.toDecodeString(0, qst, charset) : bytes.toString(0, qst, charset);
this.queryBytes = bytes.getBytes(qst + 1, size - qst - 1); this.queryBytes = bytes.getBytes(qst + 1, size - qst - 1);
this.lastRequestURIString = null;
this.lastRequestURIBytes = null;
try { try {
addParameter(bytes, qst + 1, size - qst - 1); addParameter(bytes, qst + 1, size - qst - 1);
} catch (Exception e) { } catch (Exception e) {
this.context.getLogger().log(Level.WARNING, "HttpRequest.addParameter error: " + bytes.toString(), e); this.context.getLogger().log(Level.WARNING, "HttpRequest.addParameter error: " + bytes.toString(), e);
} }
} else { } else {
this.requestURI = decodeable ? bytes.toDecodeString(charset) : bytes.toString(charset); if (decodeable) {
this.requestURI = bytes.toDecodeString(charset);
this.lastRequestURIString = null;
this.lastRequestURIBytes = null;
} else if (context.lazyHeaders) {
byte[] lastURIBytes = lastRequestURIBytes;
if (lastURIBytes != null && lastURIBytes.length == size && bytes.equal(lastURIBytes)) {
this.requestURI = this.lastRequestURIString;
} else {
this.requestURI = bytes.toString(charset);
this.lastRequestURIString = this.requestURI;
this.lastRequestURIBytes = bytes.getBytes();
}
} else {
this.requestURI = bytes.toString(charset);
}
this.queryBytes = EMPTY_BYTES; this.queryBytes = EMPTY_BYTES;
} }
bytes.clear(); bytes.clear();
@@ -700,7 +723,7 @@ public class HttpRequest extends Request<HttpContext> {
@Override @Override
protected HttpRequest copyHeader() { protected HttpRequest copyHeader() {
if (!context.lazyHeaders) return null; if (!pipelineSameHeaders || !context.lazyHeaders) return null;
HttpRequest req = new HttpRequest(context, this.array); HttpRequest req = new HttpRequest(context, this.array);
req.headerLength = this.headerLength; req.headerLength = this.headerLength;
req.headerBytes = this.headerBytes; req.headerBytes = this.headerBytes;
@@ -715,7 +738,7 @@ public class HttpRequest extends Request<HttpContext> {
req.rpc = this.rpc; req.rpc = this.rpc;
req.hashid = this.hashid; req.hashid = this.hashid;
req.currentUserid = this.currentUserid; req.currentUserid = this.currentUserid;
req.currentUser = this.currentUser; req.currentUserSupplier = this.currentUserSupplier;
req.frombody = this.frombody; req.frombody = this.frombody;
req.reqConvertType = this.reqConvertType; req.reqConvertType = this.reqConvertType;
req.reqConvert = this.reqConvert; req.reqConvert = this.reqConvert;
@@ -746,7 +769,7 @@ public class HttpRequest extends Request<HttpContext> {
this.rpc = false; this.rpc = false;
this.readState = READ_STATE_ROUTE; this.readState = READ_STATE_ROUTE;
this.currentUserid = CURRUSERID_NIL; this.currentUserid = CURRUSERID_NIL;
this.currentUser = null; this.currentUserSupplier = null;
this.frombody = false; this.frombody = false;
this.reqConvertType = null; this.reqConvertType = null;
this.reqConvert = null; this.reqConvert = null;
@@ -837,6 +860,20 @@ public class HttpRequest extends Request<HttpContext> {
return this; return this;
} }
/**
* 获取当前用户ID的int值<br>
*
* @return 用户ID
*
* @since 2.4.0
*/
@SuppressWarnings("unchecked")
public int currentIntUserid() {
if (currentUserid == CURRUSERID_NIL || currentUserid == null) return 0;
if (this.currentUserid instanceof Number) return ((Number) this.currentUserid).intValue();
return Integer.parseInt(this.currentUserid.toString());
}
/** /**
* 获取当前用户ID<br> * 获取当前用户ID<br>
* *
@@ -872,14 +909,14 @@ public class HttpRequest extends Request<HttpContext> {
* 设置当前用户信息, 通常在HttpServlet.preExecute方法里设置currentUser <br> * 设置当前用户信息, 通常在HttpServlet.preExecute方法里设置currentUser <br>
* 数据类型由&#64;HttpUserType指定 * 数据类型由&#64;HttpUserType指定
* *
* @param <T> 泛型 * @param supplier currentUser对象方法
* @param user 用户信息 *
* @since 2.4.0
* *
* @return HttpRequest * @return HttpRequest
*/ */
@Deprecated public HttpRequest setCurrentUserSupplier(Supplier supplier) {
public <T> HttpRequest setCurrentUser(T user) { this.currentUserSupplier = supplier;
this.currentUser = user;
return this; return this;
} }
@@ -892,10 +929,10 @@ public class HttpRequest extends Request<HttpContext> {
* *
* @return 用户信息 * @return 用户信息
*/ */
@Deprecated
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public <T> T currentUser() { public <T> T currentUser() {
return (T) this.currentUser; Supplier<Object> supplier = this.currentUserSupplier;
return (T) (supplier == null ? null : supplier.get());
} }
/** /**
@@ -1110,8 +1147,9 @@ public class HttpRequest extends Request<HttpContext> {
*/ */
@ConvertDisabled @ConvertDisabled
public final MultiContext getMultiContext() { public final MultiContext getMultiContext() {
final InputStream in = newInputStream();
return new MultiContext(context.getCharset(), this.getContentType(), this.params, return new MultiContext(context.getCharset(), this.getContentType(), this.params,
new BufferedInputStream(Channels.newInputStream(this.channel.readableByteChannel()), Math.max(array.length(), 8192)) { new BufferedInputStream(in, Math.max(array.length(), 8192)) {
{ {
array.copyTo(this.buf); array.copyTo(this.buf);
this.count = array.length(); this.count = array.length();
@@ -1119,6 +1157,15 @@ public class HttpRequest extends Request<HttpContext> {
}, null); }, null);
} }
/**
* 是否上传文件请求
*
* @return boolean
*/
public final boolean isMultipart() {
return boundary;
}
/** /**
* 获取文件上传信息列表 * 获取文件上传信息列表
* *

View File

@@ -6,7 +6,6 @@
package org.redkale.net.http; package org.redkale.net.http;
import java.io.*; import java.io.*;
import java.nio.ByteBuffer;
import static java.nio.file.StandardWatchEventKinds.*; import static java.nio.file.StandardWatchEventKinds.*;
import java.nio.file.*; import java.nio.file.*;
import java.util.AbstractMap.SimpleEntry; import java.util.AbstractMap.SimpleEntry;
@@ -15,7 +14,7 @@ import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.LongAdder; import java.util.concurrent.atomic.LongAdder;
import java.util.logging.*; import java.util.logging.*;
import java.util.regex.*; import java.util.regex.*;
import org.redkale.util.AnyValue; import org.redkale.util.*;
/** /**
* 静态资源HttpServlet * 静态资源HttpServlet
@@ -107,6 +106,8 @@ public class HttpResourceServlet extends HttpServlet {
protected WatchThread watchThread; protected WatchThread watchThread;
protected String[] renderSuffixs;
@Override @Override
public void init(HttpContext context, AnyValue config) { public void init(HttpContext context, AnyValue config) {
if (config != null) { if (config != null) {
@@ -211,6 +212,16 @@ public class HttpResourceServlet extends HttpServlet {
if (uri.length() == 0 || uri.equals("/")) { if (uri.length() == 0 || uri.equals("/")) {
uri = this.indexHtml.indexOf('/') == 0 ? this.indexHtml : ("/" + this.indexHtml); uri = this.indexHtml.indexOf('/') == 0 ? this.indexHtml : ("/" + this.indexHtml);
} }
//跳过模板引擎的后缀文件
if (renderSuffixs != null) {
String suri = uri.toLowerCase();
for (String suffix : renderSuffixs) {
if (suri.endsWith(suffix)) {
response.finish404();
return;
}
}
}
//System.out.println(request); //System.out.println(request);
FileEntry entry; FileEntry entry;
if (watchThread == null && files.isEmpty()) { if (watchThread == null && files.isEmpty()) {
@@ -219,7 +230,7 @@ public class HttpResourceServlet extends HttpServlet {
entry = files.computeIfAbsent(uri, x -> createFileEntry(x)); entry = files.computeIfAbsent(uri, x -> createFileEntry(x));
} }
if (entry == null) { if (entry == null) {
if (logger.isLoggable(Level.FINER)) logger.log(Level.FINER, "Not found resource (404), request = " + request); if (logger.isLoggable(Level.FINER)) logger.log(Level.FINER, "Not found resource (404), url = " + request.getRequestURI());
response.finish404(); response.finish404();
} else { } else {
//file = null 表示资源内容在内存而不是在File中 //file = null 表示资源内容在内存而不是在File中
@@ -253,8 +264,9 @@ public class HttpResourceServlet extends HttpServlet {
protected final HttpResourceServlet servlet; protected final HttpResourceServlet servlet;
protected ByteBuffer content; protected ByteArray content;
@SuppressWarnings("OverridableMethodCallInConstructor")
public FileEntry(final HttpResourceServlet servlet, File file) { public FileEntry(final HttpResourceServlet servlet, File file) {
this.servlet = servlet; this.servlet = servlet;
this.file = file; this.file = file;
@@ -262,37 +274,32 @@ public class HttpResourceServlet extends HttpServlet {
update(); update();
} }
public FileEntry(final HttpResourceServlet servlet, String filename, ByteBuffer content) { public FileEntry(final HttpResourceServlet servlet, String filename, ByteArray content) {
this.servlet = servlet; this.servlet = servlet;
this.file = null; this.file = null;
this.filename = filename; this.filename = filename;
this.content = content.asReadOnlyBuffer(); this.content = content;
this.servlet.cachedLength.add(this.content.remaining()); this.servlet.cachedLength.add(this.content.length());
} }
public FileEntry(final HttpResourceServlet servlet, String filename, InputStream in) throws IOException { public FileEntry(final HttpResourceServlet servlet, String filename, InputStream in) throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream(); ByteArray out = new ByteArray();
byte[] bytes = new byte[10240]; byte[] bytes = new byte[10240];
int pos; int pos;
while ((pos = in.read(bytes)) != -1) { while ((pos = in.read(bytes)) != -1) {
out.write(bytes, 0, pos); out.put(bytes, 0, pos);
} }
byte[] bs = out.toByteArray();
ByteBuffer buf = ByteBuffer.allocateDirect(bs.length);
buf.put(bs);
buf.flip();
this.servlet = servlet; this.servlet = servlet;
this.file = null; this.file = null;
this.filename = filename; this.filename = filename;
this.content = buf.asReadOnlyBuffer(); this.content = out;
this.servlet.cachedLength.add(this.content.remaining()); this.servlet.cachedLength.add(this.content.length());
} }
public void update() { public void update() {
if (this.file == null) return; if (this.file == null) return;
if (this.content != null) { if (this.content != null) {
this.servlet.cachedLength.add(0L - this.content.remaining()); this.servlet.cachedLength.add(0L - this.content.length());
this.content = null; this.content = null;
} }
long length = this.file.length(); long length = this.file.length();
@@ -300,30 +307,26 @@ public class HttpResourceServlet extends HttpServlet {
if (this.servlet.cachedLength.longValue() + length > this.servlet.cachelimit) return; //超过缓存总容量 if (this.servlet.cachedLength.longValue() + length > this.servlet.cachelimit) return; //超过缓存总容量
try { try {
FileInputStream in = new FileInputStream(file); FileInputStream in = new FileInputStream(file);
ByteArrayOutputStream out = new ByteArrayOutputStream((int) file.length()); ByteArray out = new ByteArray((int) file.length());
byte[] bytes = new byte[10240]; byte[] bytes = new byte[10240];
int pos; int pos;
while ((pos = in.read(bytes)) != -1) { while ((pos = in.read(bytes)) != -1) {
out.write(bytes, 0, pos); out.put(bytes, 0, pos);
} }
in.close(); in.close();
byte[] bs = out.toByteArray(); this.content = out;
ByteBuffer buf = ByteBuffer.allocateDirect(bs.length); this.servlet.cachedLength.add(this.content.length());
buf.put(bs);
buf.flip();
this.content = buf.asReadOnlyBuffer();
this.servlet.cachedLength.add(this.content.remaining());
} catch (Exception e) { } catch (Exception e) {
this.servlet.logger.log(Level.INFO, HttpResourceServlet.class.getSimpleName() + " update FileEntry(" + file + ") erroneous", e); this.servlet.logger.log(Level.INFO, HttpResourceServlet.class.getSimpleName() + " update FileEntry(" + file + ") erroneous", e);
} }
} }
public void remove() { public void remove() {
if (this.content != null) this.servlet.cachedLength.add(0L - this.content.remaining()); if (this.content != null) this.servlet.cachedLength.add(0L - this.content.length());
} }
public long getCachedLength() { public long getCachedLength() {
return this.content == null ? 0L : this.content.remaining(); return this.content == null ? 0L : this.content.length();
} }
} }

View File

@@ -16,7 +16,7 @@ import static java.time.format.DateTimeFormatter.RFC_1123_DATE_TIME;
import java.util.*; import java.util.*;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import java.util.function.*; import java.util.function.*;
import java.util.logging.Level; import java.util.logging.*;
import org.redkale.convert.*; import org.redkale.convert.*;
import org.redkale.convert.json.JsonConvert; import org.redkale.convert.json.JsonConvert;
import org.redkale.net.*; import org.redkale.net.*;
@@ -50,7 +50,9 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
protected static final byte[] connectAliveBytes = "none".equalsIgnoreCase(System.getProperty("http.response.header.connection")) ? new byte[0] : "Connection: keep-alive\r\n".getBytes(); protected static final byte[] connectAliveBytes = "none".equalsIgnoreCase(System.getProperty("http.response.header.connection")) ? new byte[0] : "Connection: keep-alive\r\n".getBytes();
private static final int cacheMaxContentLength = 999; protected static final String contentTypeHtmlUTF8 = "text/html; charset=utf-8";
private static final int cacheMaxContentLength = 1000;
private static final byte[] status200_server_live_Bytes = append(append(status200Bytes, serverNameBytes), connectAliveBytes); private static final byte[] status200_server_live_Bytes = append(append(status200Bytes, serverNameBytes), connectAliveBytes);
@@ -62,7 +64,7 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
private static final Map<Integer, String> httpCodes = new HashMap<>(); private static final Map<Integer, String> httpCodes = new HashMap<>();
private static final Map<Integer, byte[]> contentLengthMap = new HashMap<>(); private static final byte[][] contentLengthArray = new byte[cacheMaxContentLength][];
static { static {
@@ -111,8 +113,8 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
httpCodes.put(504, "Gateway Timeout"); httpCodes.put(504, "Gateway Timeout");
httpCodes.put(505, "HTTP Version Not Supported"); httpCodes.put(505, "HTTP Version Not Supported");
for (int i = 0; i <= cacheMaxContentLength; i++) { for (int i = 0; i < cacheMaxContentLength; i++) {
contentLengthMap.put(i, ("Content-Length: " + i + "\r\n").getBytes()); contentLengthArray[i] = ("Content-Length: " + i + "\r\n").getBytes();
} }
} }
@@ -132,13 +134,6 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
private BiFunction<HttpRequest, org.redkale.service.RetResult, org.redkale.service.RetResult> retResultHandler; private BiFunction<HttpRequest, org.redkale.service.RetResult, org.redkale.service.RetResult> retResultHandler;
private Map<Integer, byte[]> lastContentLengthMap;//lazyHeaders=true下缓存, recycle不会清空
private int lastContentLength; //lazyHeaders=true下缓存, recycle不会清空
private byte[] lastContentLengthBytes; //lazyHeaders=true下缓存, recycle不会清空
//private Supplier<ByteBuffer> bodyBufferSupplier;
//------------------------------------------------ //------------------------------------------------
private final String plainContentType; private final String plainContentType;
@@ -160,21 +155,17 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
private final HttpCookie defaultCookie; private final HttpCookie defaultCookie;
private final List<HttpRender> renders; private final HttpRender httpRender;
private final boolean hasRender;
private final HttpRender onlyoneHttpRender;
private final ByteArray headerArray = new ByteArray(); private final ByteArray headerArray = new ByteArray();
private final Map<Integer, byte[]> plainLiveContentLengthMap; private final byte[][] plainLiveContentLengthArray;
private final Map<Integer, byte[]> jsonLiveContentLengthMap; private final byte[][] jsonLiveContentLengthArray;
private final Map<Integer, byte[]> plainCloseContentLengthMap; private final byte[][] plainCloseContentLengthArray;
private final Map<Integer, byte[]> jsonCloseContentLengthMap; private final byte[][] jsonCloseContentLengthArray;
protected final CompletionHandler<Integer, Void> pipelineWriteHandler = new CompletionHandler<Integer, Void>() { protected final CompletionHandler<Integer, Void> pipelineWriteHandler = new CompletionHandler<Integer, Void>() {
@@ -204,18 +195,16 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
this.defaultCookie = config == null ? null : config.defaultCookie; this.defaultCookie = config == null ? null : config.defaultCookie;
this.autoOptions = config == null ? false : config.autoOptions; this.autoOptions = config == null ? false : config.autoOptions;
this.dateSupplier = config == null ? null : config.dateSupplier; this.dateSupplier = config == null ? null : config.dateSupplier;
this.renders = config == null ? null : config.renders; this.httpRender = config == null ? null : config.httpRender;
this.hasRender = renders != null && !renders.isEmpty();
this.onlyoneHttpRender = renders != null && renders.size() == 1 ? renders.get(0) : null;
this.plainContentType = config == null ? "text/plain; charset=utf-8" : config.plainContentType; this.plainContentType = config == null ? "text/plain; charset=utf-8" : config.plainContentType;
this.jsonContentType = config == null ? "application/json; charset=utf-8" : config.jsonContentType; this.jsonContentType = config == null ? "application/json; charset=utf-8" : config.jsonContentType;
this.plainContentTypeBytes = config == null ? ("Content-Type: " + this.plainContentType + "\r\n").getBytes() : config.plainContentTypeBytes; this.plainContentTypeBytes = config == null ? ("Content-Type: " + this.plainContentType + "\r\n").getBytes() : config.plainContentTypeBytes;
this.jsonContentTypeBytes = config == null ? ("Content-Type: " + this.jsonContentType + "\r\n").getBytes() : config.jsonContentTypeBytes; this.jsonContentTypeBytes = config == null ? ("Content-Type: " + this.jsonContentType + "\r\n").getBytes() : config.jsonContentTypeBytes;
this.plainLiveContentLengthMap = config == null ? new HashMap<>() : config.plainLiveContentLengthMap; this.plainLiveContentLengthArray = config == null ? null : config.plainLiveContentLengthArray;
this.plainCloseContentLengthMap = config == null ? new HashMap<>() : config.plainCloseContentLengthMap; this.plainCloseContentLengthArray = config == null ? null : config.plainCloseContentLengthArray;
this.jsonLiveContentLengthMap = config == null ? new HashMap<>() : config.jsonLiveContentLengthMap; this.jsonLiveContentLengthArray = config == null ? null : config.jsonLiveContentLengthArray;
this.jsonCloseContentLengthMap = config == null ? new HashMap<>() : config.jsonCloseContentLengthMap; this.jsonCloseContentLengthArray = config == null ? null : config.jsonCloseContentLengthArray;
this.contentType = this.plainContentType; this.contentType = this.plainContentType;
} }
@@ -352,42 +341,6 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
request.getRespConvert().convertToBytes(obj, convertHandler); request.getRespConvert().convertToBytes(obj, convertHandler);
} }
/**
* 指定json内容长度将对象以JSON格式输出, 临时功能
*
* @param length json内容长度
* @param obj 输出对象
*/
@Deprecated
public void finishJson(final int length, final Object obj) {
this.contentType = this.jsonContentType;
this.contentLength = length;
if (this.recycleListener != null) this.output = obj;
createHeader();
ByteArray data = headerArray;
request.getRespConvert().convertToBytes(data, obj);
int pipelineIndex = request.getPipelineIndex();
if (pipelineIndex > 0) {
boolean over = this.channel.writePipelineData(pipelineIndex, request.getPipelineCount(), data);
if (over) {
request.setPipelineOver(true);
this.channel.flushPipelineData(this.pipelineWriteHandler);
} else {
removeChannel();
this.responseConsumer.accept(this);
}
} else {
if (this.channel.hasPipelineData()) {
this.channel.writePipelineData(pipelineIndex, request.getPipelineCount(), data);
this.channel.flushPipelineData(this.pipelineWriteHandler);
} else {
//不能用finish(boolean kill, final ByteTuple array) 否则会调this.finish
super.finish(false, data.content(), 0, data.length());
}
}
}
/** /**
* 将对象以JSON格式输出 * 将对象以JSON格式输出
* *
@@ -430,13 +383,12 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
* *
* @param objs 输出对象 * @param objs 输出对象
*/ */
@Deprecated //@since 2.3.0 // @Deprecated //@since 2.3.0
void finishJson(final Object... objs) { // void finishJson(final Object... objs) {
this.contentType = this.jsonContentType; // this.contentType = this.jsonContentType;
if (this.recycleListener != null) this.output = objs; // if (this.recycleListener != null) this.output = objs;
request.getRespConvert().convertToBytes(objs, convertHandler); // request.getRespConvert().convertToBytes(objs, convertHandler);
} // }
/** /**
* 将RetResult对象以JSON格式输出 * 将RetResult对象以JSON格式输出
* *
@@ -476,6 +428,15 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
convert.convertToBytes(ret, convertHandler); convert.convertToBytes(ret, convertHandler);
} }
/**
* 将HttpResult对象输出
*
* @param result HttpResult输出对象
*/
public void finish(HttpResult result) {
finish(request.getRespConvert(), result);
}
/** /**
* 将HttpResult对象输出 * 将HttpResult对象输出
* *
@@ -496,6 +457,36 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
} }
} }
/**
* 将HttpScope对象输出
*
* @param result HttpScope输出对象
*/
public void finish(HttpScope result) {
finish(request.getRespConvert(), result);
}
/**
* 将HttpScope对象输出
*
* @param convert 指定的Convert
* @param result HttpScope输出对象
*/
public void finish(final Convert convert, HttpScope result) {
if (result == null) {
finish("null");
return;
}
if (httpRender != null) {
setContentType(contentTypeHtmlUTF8);
if (result.getHeaders() != null) addHeader(result.getHeaders());
if (result.getCookies() != null) addCookie(result.getCookies());
httpRender.renderTo(this.request, this, convert, result);
return;
}
finish("");
}
/** /**
* 将CompletableFuture的结果对象以JSON格式输出 * 将CompletableFuture的结果对象以JSON格式输出
* *
@@ -585,23 +576,9 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
finishJson((org.redkale.service.RetResult) obj); finishJson((org.redkale.service.RetResult) obj);
} else if (obj instanceof HttpResult) { } else if (obj instanceof HttpResult) {
finish(convert, (HttpResult) obj); finish(convert, (HttpResult) obj);
} else if (obj instanceof HttpScope) {
finish(convert, (HttpScope) obj);
} else { } else {
if (hasRender) {
if (onlyoneHttpRender != null) {
if (onlyoneHttpRender.getType().isAssignableFrom(obj.getClass())) {
onlyoneHttpRender.renderTo(this.request, this, convert, obj);
return;
}
} else {
Class objt = obj.getClass();
for (HttpRender render : this.renders) {
if (render.getType().isAssignableFrom(objt)) {
render.renderTo(this.request, this, convert, obj);
return;
}
}
}
}
if (convert instanceof JsonConvert) { if (convert instanceof JsonConvert) {
this.contentType = this.jsonContentType; this.contentType = this.jsonContentType;
} else if (convert instanceof TextConvert) { } else if (convert instanceof TextConvert) {
@@ -646,7 +623,7 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
if (isClosed()) return; if (isClosed()) return;
this.status = status; this.status = status;
if (status != 200) super.refuseAlive(); if (status != 200) super.refuseAlive();
final byte[] val = message == null ? new byte[0] : (context.getCharset() == null ? Utility.encodeUTF8(message) : message.getBytes(context.getCharset())); final byte[] val = message == null ? HttpRequest.EMPTY_BYTES : (context.getCharset() == null ? Utility.encodeUTF8(message) : message.getBytes(context.getCharset()));
finish(false, null, val, 0, val.length, null, null); finish(false, null, val, 0, val.length, null, null);
} }
@@ -696,12 +673,12 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
*/ */
protected <A> void finish(boolean kill, final String contentType, final byte[] bs, int offset, int length, Consumer<A> callback, A attachment) { protected <A> void finish(boolean kill, final String contentType, final byte[] bs, int offset, int length, Consumer<A> callback, A attachment) {
if (isClosed()) return; //避免重复关闭 if (isClosed()) return; //避免重复关闭
ByteArray data = headerArray;
if (this.headWritedSize < 0) { if (this.headWritedSize < 0) {
if (contentType != null) this.contentType = contentType; if (contentType != null) this.contentType = contentType;
this.contentLength = length; this.contentLength = length;
createHeader(); createHeader();
} }
ByteArray data = headerArray;
data.put(bs, offset, length); data.put(bs, offset, length);
if (callback != null) callback.accept(attachment); if (callback != null) callback.accept(attachment);
if (cacheHandler != null) cacheHandler.accept(this, data.getBytes()); if (cacheHandler != null) cacheHandler.accept(this, data.getBytes());
@@ -725,6 +702,32 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
super.finish(false, data.content(), 0, data.length()); super.finish(false, data.content(), 0, data.length());
} }
} }
// ByteArray data = headerArray;
// int pipelineIndex = request.getPipelineIndex();
// if (pipelineIndex > 0) {
// boolean over = this.channel.writePipelineData(pipelineIndex, request.getPipelineCount(), data.content(), 0, data.length(), bs, offset, length);
// if (callback != null) callback.accept(attachment);
// if (cacheHandler != null) cacheHandler.accept(this, Utility.append(data.getBytes(), bs, offset, length));
//
// if (over) {
// request.setPipelineOver(true);
// this.channel.flushPipelineData(this.pipelineWriteHandler);
// } else {
// removeChannel();
// this.responseConsumer.accept(this);
// }
// } else {
// if (this.channel.hasPipelineData()) {
// this.channel.writePipelineData(pipelineIndex, request.getPipelineCount(), data.content(), 0, data.length(), bs, offset, length);
// if (callback != null) callback.accept(attachment);
// if (cacheHandler != null) cacheHandler.accept(this, Utility.append(data.getBytes(), bs, offset, length));
// this.channel.flushPipelineData(this.pipelineWriteHandler);
// } else {
// //不能用finish(boolean kill, final ByteTuple array) 否则会调this.finish
// super.finish(false, data.content(), 0, data.length(), bs, offset, length, callback, attachment);
// }
// }
} }
/** /**
@@ -747,32 +750,20 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
protected void createHeader() { protected void createHeader() {
if (this.status == 200 && !this.respHeadContainsConnection && !this.request.isWebSocket() if (this.status == 200 && !this.respHeadContainsConnection && !this.request.isWebSocket()
&& (this.contentType == null || this.contentType == this.jsonContentType || this.contentType == this.plainContentType) && (this.contentType == null || this.contentType == this.jsonContentType || this.contentType == this.plainContentType)
&& (this.contentLength >= 0 && this.contentLength < jsonLiveContentLengthMap.size())) { && (this.contentLength >= 0 && this.contentLength < jsonLiveContentLengthArray.length)) {
Map<Integer, byte[]> lengthMap = this.plainLiveContentLengthMap; byte[][] lengthArray = this.plainLiveContentLengthArray;
if (this.request.isKeepAlive()) { if (this.request.isKeepAlive()) {
if (this.contentType == this.jsonContentType) { if (this.contentType == this.jsonContentType) {
lengthMap = this.jsonLiveContentLengthMap; lengthArray = this.jsonLiveContentLengthArray;
} }
} else { } else {
if (this.contentType == this.jsonContentType) { if (this.contentType == this.jsonContentType) {
lengthMap = this.jsonCloseContentLengthMap; lengthArray = this.jsonCloseContentLengthArray;
} else { } else {
lengthMap = this.plainCloseContentLengthMap; lengthArray = this.plainCloseContentLengthArray;
} }
} }
if (context.lazyHeaders) { headerArray.put(lengthArray[(int) this.contentLength]);
if (this.lastContentLength == this.contentLength && this.lastContentLengthMap == lengthMap) {
headerArray.put(this.lastContentLengthBytes);
} else {
byte[] lenbs = lengthMap.get((int) this.contentLength);
this.lastContentLength = (int) this.contentLength;
this.lastContentLengthMap = lengthMap;
this.lastContentLengthBytes = lenbs;
headerArray.put(lenbs);
}
} else {
headerArray.put(lengthMap.get((int) this.contentLength));
}
} else { } else {
if (this.status == 200 && !this.respHeadContainsConnection && !this.request.isWebSocket()) { if (this.status == 200 && !this.respHeadContainsConnection && !this.request.isWebSocket()) {
if (this.request.isKeepAlive()) { if (this.request.isKeepAlive()) {
@@ -801,8 +792,8 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
} }
} }
if (this.contentLength >= 0) { if (this.contentLength >= 0) {
if (this.contentLength < contentLengthMap.size()) { if (this.contentLength < contentLengthArray.length) {
headerArray.put(contentLengthMap.get((int) this.contentLength)); headerArray.put(contentLengthArray[(int) this.contentLength]);
} else { } else {
headerArray.put(("Content-Length: " + this.contentLength + "\r\n").getBytes()); headerArray.put(("Content-Length: " + this.contentLength + "\r\n").getBytes());
} }
@@ -938,7 +929,7 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
* *
* @throws IOException IO异常 * @throws IOException IO异常
*/ */
protected void finishFile(final File file, ByteBuffer fileBody) throws IOException { protected void finishFile(final File file, ByteArray fileBody) throws IOException {
finishFile(null, file, fileBody); finishFile(null, file, fileBody);
} }
@@ -953,13 +944,12 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
* *
* @throws IOException IO异常 * @throws IOException IO异常
*/ */
protected void finishFile(final String filename, final File file, ByteBuffer fileBody) throws IOException { protected void finishFile(final String filename, final File file, ByteArray fileBody) throws IOException {
if ((file == null || !file.isFile() || !file.canRead()) && fileBody == null) { if ((file == null || !file.isFile() || !file.canRead()) && fileBody == null) {
finish404(); finish404();
return; return;
} }
if (fileBody != null) fileBody = fileBody.duplicate().asReadOnlyBuffer(); final long length = file == null ? fileBody.length() : file.length();
final long length = file == null ? fileBody.remaining() : file.length();
final String match = request.getHeader("If-None-Match"); final String match = request.getHeader("If-None-Match");
final String etag = (file == null ? 0L : file.lastModified()) + "-" + length; final String etag = (file == null ? 0L : file.lastModified()) + "-" + length;
if (match != null && etag.equals(match)) { if (match != null && etag.equals(match)) {
@@ -992,24 +982,54 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
} }
this.addHeader("ETag", etag); this.addHeader("ETag", etag);
createHeader(); createHeader();
ByteBuffer hbuffer = channel.pollWriteBuffer(); ByteArray data = headerArray;
hbuffer.put(headerArray.content(), 0, headerArray.length());
hbuffer.flip();
if (fileBody == null) { if (fileBody == null) {
if (this.recycleListener != null) this.output = file; if (this.recycleListener != null) this.output = file;
finishFile(hbuffer, file, start, len); finishFile(data, file, start, len);
} else { } else { //一般HttpResourceServlet缓存file内容时fileBody不为空
if (start >= 0) { if (start >= 0) data.put(fileBody, (int) start, (int) ((len > 0) ? len : fileBody.length() - start));
fileBody.position((int) start); super.finish(false, data.content(), 0, data.length());
if (len > 0) fileBody.limit((int) (fileBody.position() + len));
}
if (this.recycleListener != null) this.output = fileBody;
super.finish(hbuffer, fileBody);
} }
} }
private void finishFile(ByteBuffer hbuffer, File file, long offset, long length) throws IOException { //offset、length 为 -1 表示输出整个文件
this.channel.write(hbuffer, hbuffer, new TransferFileHandler(file, offset, length)); private void finishFile(ByteArray headerData, File file, long offset, long length) throws IOException {
//this.channel.write(headerData, new TransferFileHandler(file, offset, length));
final Logger logger = context.getLogger();
this.channel.write(headerData, new CompletionHandler<Integer, Void>() {
@Override
public void completed(Integer result, Void attachment) {
FileChannel fileChannel = null;
try {
fileChannel = FileChannel.open(file.toPath(), StandardOpenOption.READ);
WritableByteChannel writeChannel = channel.writableByteChannel();
long remain = length > 0 ? length : file.length();
long start = offset < 0 ? 0 : offset;
while (remain > 0) {
long c = fileChannel.transferTo(start, remain, writeChannel);
start += c;
remain -= c;
}
fileChannel.close();
finish();
} catch (Exception e) {
if (fileChannel != null) {
try {
fileChannel.close();
} catch (IOException ie) {
}
}
failed(e, attachment);
}
}
@Override
public void failed(Throwable exc, Void attachment) {
if (logger.isLoggable(Level.FINER)) logger.log(Level.FINER, "finishFile error", exc);
finish(true);
}
});
} }
/** /**
@@ -1172,93 +1192,91 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
this.retResultHandler = retResultHandler; this.retResultHandler = retResultHandler;
} }
protected final class TransferFileHandler implements CompletionHandler<Integer, ByteBuffer> { // protected final class TransferFileHandler implements CompletionHandler<Integer, Void> {
//
private final File file; // private final File file;
//
private final AsynchronousFileChannel filechannel; // private final AsynchronousFileChannel filechannel;
//
private final long max; //需要读取的字节数, -1表示读到文件结尾 // private final long max; //需要读取的字节数, -1表示读到文件结尾
//
private long count;//读取文件的字节数 // private long count;//读取文件的字节数
//
private long readpos = 0; // private long readpos = 0;
//
private boolean hdwrite = true; //写入Header // private boolean hdwrite = true; //写入Header
//
private boolean read = false; // private boolean read = false;
//
public TransferFileHandler(File file) throws IOException { // public TransferFileHandler(File file) throws IOException {
this.file = file; // this.file = file;
this.filechannel = AsynchronousFileChannel.open(file.toPath(), options); // this.filechannel = AsynchronousFileChannel.open(file.toPath(), options);
this.readpos = 0; // this.readpos = 0;
this.max = file.length(); // this.max = file.length();
} // }
//
public TransferFileHandler(File file, long offset, long len) throws IOException { // public TransferFileHandler(File file, long offset, long len) throws IOException {
this.file = file; // this.file = file;
this.filechannel = AsynchronousFileChannel.open(file.toPath(), options); // this.filechannel = AsynchronousFileChannel.open(file.toPath(), options);
this.readpos = offset <= 0 ? 0 : offset; // this.readpos = offset <= 0 ? 0 : offset;
this.max = len <= 0 ? file.length() : len; // this.max = len <= 0 ? file.length() : len;
} // }
//
@Override // @Override
public void completed(Integer result, ByteBuffer attachment) { // public void completed(Integer result, Void attachment) {
//(Utility.now() + "---" + Thread.currentThread().getName() + "-----------" + file + "-------------------result: " + result + ", max = " + max + ", readpos = " + readpos + ", count = " + count + ", " + (hdwrite ? "正在写Header" : (read ? "准备读" : "准备写"))); // //(Utility.now() + "---" + Thread.currentThread().getName() + "-----------" + file + "-------------------result: " + result + ", max = " + max + ", readpos = " + readpos + ", count = " + count + ", " + (hdwrite ? "正在写Header" : (read ? "准备读" : "准备写")));
if (result < 0 || count >= max) { // if (result < 0 || count >= max) {
failed(null, attachment); // failed(null, attachment);
return; // return;
} // }
if (hdwrite && attachment.hasRemaining()) { //Header还没写完 // if (hdwrite && attachment.hasRemaining()) { //Header还没写完
channel.write(attachment, attachment, this); // channel.write(attachment, attachment, this);
return; // return;
} // }
if (hdwrite) { // if (hdwrite) {
//(Utility.now() + "---" + Thread.currentThread().getName() + "-----------" + file + "-------------------Header写入完毕 准备读取文件."); // //(Utility.now() + "---" + Thread.currentThread().getName() + "-----------" + file + "-------------------Header写入完毕 准备读取文件.");
hdwrite = false; // hdwrite = false;
read = true; // read = true;
result = 0; // result = 0;
} // }
if (read) { // if (read) {
count += result; // count += result;
} else { // } else {
readpos += result; // readpos += result;
} // }
if (read && attachment.hasRemaining()) { //Buffer还没写完 // if (read && attachment.hasRemaining()) { //Buffer还没写完
channel.write(attachment, attachment, this); // channel.write(attachment, attachment, this);
return; // return;
} // }
//
if (read) { // if (read) {
read = false; // read = false;
attachment.clear(); // attachment.clear();
filechannel.read(attachment, readpos, attachment, this); // filechannel.read(attachment, readpos, attachment, this);
} else { // } else {
read = true; // read = true;
if (count > max) { // if (count > max) {
attachment.limit((int) (attachment.position() + max - count)); // attachment.limit((int) (attachment.position() + max - count));
} // }
attachment.flip(); // attachment.flip();
if (attachment.hasRemaining()) { // if (attachment.hasRemaining()) {
channel.write(attachment, attachment, this); // channel.write(attachment, attachment, this);
} else { // } else {
failed(null, attachment); // failed(null, attachment);
} // }
} // }
} // }
//
@Override // @Override
public void failed(Throwable exc, ByteBuffer attachment) { // public void failed(Throwable exc, Void attachment) {
channel.offerBuffer(attachment); // finish(true);
finish(true); // try {
try { // filechannel.close();
filechannel.close(); // } catch (IOException e) {
} catch (IOException e) { // }
} // }
} //
// }
}
public static class HttpResponseConfig { public static class HttpResponseConfig {
public String plainContentType; public String plainContentType;
@@ -1279,15 +1297,15 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
public Supplier<byte[]> dateSupplier; public Supplier<byte[]> dateSupplier;
public List< HttpRender> renders; public HttpRender httpRender;
public final Map<Integer, byte[]> plainLiveContentLengthMap = new HashMap<>(); public final byte[][] plainLiveContentLengthArray = new byte[cacheMaxContentLength][];
public final Map<Integer, byte[]> jsonLiveContentLengthMap = new HashMap<>(); public final byte[][] jsonLiveContentLengthArray = new byte[cacheMaxContentLength][];
public final Map<Integer, byte[]> plainCloseContentLengthMap = new HashMap<>(); public final byte[][] plainCloseContentLengthArray = new byte[cacheMaxContentLength][];
public final Map<Integer, byte[]> jsonCloseContentLengthMap = new HashMap<>(); public final byte[][] jsonCloseContentLengthArray = new byte[cacheMaxContentLength][];
public HttpResponseConfig init(AnyValue config) { public HttpResponseConfig init(AnyValue config) {
if (this.plainContentTypeBytes == null) { if (this.plainContentTypeBytes == null) {
@@ -1297,12 +1315,12 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
this.jsonContentType = jsonct; this.jsonContentType = jsonct;
this.plainContentTypeBytes = ("Content-Type: " + plainct + "\r\n").getBytes(); this.plainContentTypeBytes = ("Content-Type: " + plainct + "\r\n").getBytes();
this.jsonContentTypeBytes = ("Content-Type: " + jsonct + "\r\n").getBytes(); this.jsonContentTypeBytes = ("Content-Type: " + jsonct + "\r\n").getBytes();
for (int i = 0; i <= cacheMaxContentLength; i++) { for (int i = 0; i < cacheMaxContentLength; i++) {
byte[] lenbytes = ("Content-Length: " + i + "\r\n").getBytes(); byte[] lenbytes = ("Content-Length: " + i + "\r\n").getBytes();
plainLiveContentLengthMap.put(i, append(append(status200_server_live_Bytes, plainContentTypeBytes), lenbytes)); plainLiveContentLengthArray[i] = append(append(status200_server_live_Bytes, plainContentTypeBytes), lenbytes);
plainCloseContentLengthMap.put(i, append(append(status200_server_close_Bytes, plainContentTypeBytes), lenbytes)); plainCloseContentLengthArray[i] = append(append(status200_server_close_Bytes, plainContentTypeBytes), lenbytes);
jsonLiveContentLengthMap.put(i, append(append(status200_server_live_Bytes, jsonContentTypeBytes), lenbytes)); jsonLiveContentLengthArray[i] = append(append(status200_server_live_Bytes, jsonContentTypeBytes), lenbytes);
jsonCloseContentLengthMap.put(i, append(append(status200_server_close_Bytes, jsonContentTypeBytes), lenbytes)); jsonCloseContentLengthArray[i] = append(append(status200_server_close_Bytes, jsonContentTypeBytes), lenbytes);
} }
} }
return this; return this;

View File

@@ -26,7 +26,7 @@ public class HttpResult<T> {
public static final String SESSIONID_COOKIENAME = HttpRequest.SESSIONID_NAME; public static final String SESSIONID_COOKIENAME = HttpRequest.SESSIONID_NAME;
@ConvertColumn(index = 1) @ConvertColumn(index = 1)
protected int status = 0; //不设置则为 200 protected int status = 200; //不设置则为 200
@ConvertColumn(index = 2) @ConvertColumn(index = 2)
protected String contentType; protected String contentType;
@@ -110,7 +110,7 @@ public class HttpResult<T> {
} }
public String getHeader(String name, String dfvalue) { public String getHeader(String name, String dfvalue) {
return headers == null ? null : headers.getOrDefault(name, dfvalue); return headers == null ? dfvalue : headers.getOrDefault(name, dfvalue);
} }
public CompletableFuture<HttpResult<T>> toFuture() { public CompletableFuture<HttpResult<T>> toFuture() {

View File

@@ -5,8 +5,11 @@
*/ */
package org.redkale.net.http; package org.redkale.net.http;
import java.io.Serializable;
import java.net.HttpCookie;
import java.util.*; import java.util.*;
import java.util.function.BiConsumer; import java.util.function.*;
import javax.persistence.Transient;
import org.redkale.convert.*; import org.redkale.convert.*;
import org.redkale.convert.json.JsonConvert; import org.redkale.convert.json.JsonConvert;
@@ -36,16 +39,56 @@ import org.redkale.convert.json.JsonConvert;
*/ */
public class HttpScope { public class HttpScope {
public static final Object NIL = new Object();
@ConvertColumn(index = 1)
protected String referid; protected String referid;
@ConvertColumn(index = 2)
protected Map<String, Object> attributes; protected Map<String, Object> attributes;
//@since 2.4.0
@Transient
protected Function<String, Object> attrFunction;
//@since 2.4.0
@ConvertColumn(index = 3)
protected Map<String, String> headers;
//@since 2.4.0
@ConvertColumn(index = 4)
protected List<HttpCookie> cookies;
public static HttpScope refer(String template) { public static HttpScope refer(String template) {
HttpScope rs = new HttpScope(); HttpScope rs = new HttpScope();
rs.setReferid(template); rs.setReferid(template);
return rs; return rs;
} }
public HttpScope attrFunc(Function<String, Object> attrFunction) {
this.attrFunction = attrFunction;
return this;
}
public HttpScope appendAttrFunc(final String key, Supplier supplier) {
if (supplier == null) return this;
return appendAttrFunc(k -> k.equals(key) ? supplier.get() : null);
}
public HttpScope appendAttrFunc(final Function<String, Object> attrFunc) {
if (attrFunc == null) return this;
final Function<String, Object> old = this.attrFunction;
if (old == null) {
this.attrFunction = attrFunc;
} else {
this.attrFunction = key -> {
Object r = old.apply(key);
return r == null ? attrFunc.apply(key) : r;
};
}
return this;
}
public HttpScope attr(Map<String, Object> map) { public HttpScope attr(Map<String, Object> map) {
if (map == null) return this; if (map == null) return this;
if (this.attributes == null) this.attributes = new LinkedHashMap<>(); if (this.attributes == null) this.attributes = new LinkedHashMap<>();
@@ -54,6 +97,7 @@ public class HttpScope {
} }
public HttpScope attr(String name, Object value) { public HttpScope attr(String name, Object value) {
if (name == null || value == null) return this;
if (this.attributes == null) this.attributes = new LinkedHashMap<>(); if (this.attributes == null) this.attributes = new LinkedHashMap<>();
this.attributes.put(name, value); this.attributes.put(name, value);
return this; return this;
@@ -76,6 +120,52 @@ public class HttpScope {
this.attributes.forEach(action); this.attributes.forEach(action);
} }
public HttpScope header(String name, Serializable value) {
if (this.headers == null) this.headers = new HashMap<>();
this.headers.put(name, String.valueOf(value));
return this;
}
public HttpScope cookie(String name, Serializable value) {
return cookie(new HttpCookie(name, String.valueOf(value)));
}
public HttpScope cookie(String name, Serializable value, boolean httpOnly) {
HttpCookie c = new HttpCookie(name, String.valueOf(value));
c.setHttpOnly(httpOnly);
return cookie(c);
}
public HttpScope cookie(HttpCookie cookie) {
if (this.cookies == null) this.cookies = new ArrayList<>();
this.cookies.add(cookie);
return this;
}
public String getHeader(String name) {
return headers == null ? null : headers.get(name);
}
public String getHeader(String name, String dfvalue) {
return headers == null ? null : headers.getOrDefault(name, dfvalue);
}
public Map<String, String> getHeaders() {
return headers;
}
public void setHeaders(Map<String, String> headers) {
this.headers = headers;
}
public List<HttpCookie> getCookies() {
return cookies;
}
public void setCookies(List<HttpCookie> cookies) {
this.cookies = cookies;
}
public String getReferid() { public String getReferid() {
return referid; return referid;
} }
@@ -85,7 +175,24 @@ public class HttpScope {
} }
public Map<String, Object> getAttributes() { public Map<String, Object> getAttributes() {
return attributes; final Function<String, Object> attrFunc = this.attrFunction;
if (attrFunc != null) {
if (this.attributes == null) this.attributes = new LinkedHashMap<>();
return new LinkedHashMap(this.attributes) {
@Override
public Object get(Object key) {
if (containsKey(key)) {
return super.get(key);
} else {
Object val = attrFunc.apply(key.toString());
if (val == NIL) return null;
put(key.toString(), val);
return val;
}
}
};
}
return this.attributes;
} }
@ConvertDisabled(type = ConvertType.JSON) @ConvertDisabled(type = ConvertType.JSON)

View File

@@ -80,15 +80,6 @@ public class HttpServer extends Server<String, HttpContext, HttpRequest, HttpRes
return this.prepare.getFilters(); return this.prepare.getFilters();
} }
/**
* 获取HttpRender列表
*
* @return HttpRender列表
*/
public List<HttpRender> getHttpRenders() {
return ((HttpPrepareServlet) this.prepare).renders;
}
/** /**
* 获取静态资源HttpServlet * 获取静态资源HttpServlet
* *
@@ -324,9 +315,8 @@ public class HttpServer extends Server<String, HttpContext, HttpRequest, HttpRes
String jsonContentType = null; String jsonContentType = null;
HttpCookie defaultCookie = null; HttpCookie defaultCookie = null;
String remoteAddrHeader = null; String remoteAddrHeader = null;
boolean lazyHeaders = false;
if (config != null) { if (config != null) {
lazyHeaders = config.getBoolValue("lazy", false);
AnyValue reqs = config.getAnyValue("request"); AnyValue reqs = config.getAnyValue("request");
if (reqs != null) { if (reqs != null) {
AnyValue raddr = reqs.getAnyValue("remoteaddr"); AnyValue raddr = reqs.getAnyValue("remoteaddr");
@@ -424,6 +414,21 @@ public class HttpServer extends Server<String, HttpContext, HttpRequest, HttpRes
dateSupplier = () -> currDateBytes; dateSupplier = () -> currDateBytes;
} }
} }
HttpRender httpRender = null;
AnyValue renderConfig = null;
{ //设置TemplateEngine
renderConfig = config.getAnyValue("render");
if (renderConfig != null) {
String renderType = renderConfig.getValue("value");
try {
HttpRender render = (HttpRender) Thread.currentThread().getContextClassLoader().loadClass(renderType).getDeclaredConstructor().newInstance();
getResourceFactory().inject(render);
httpRender = render;
} catch (Throwable e) {
logger.log(Level.WARNING, "init HttpRender(" + renderType + ") error", e);
}
}
}
final String addrHeader = remoteAddrHeader; final String addrHeader = remoteAddrHeader;
@@ -435,7 +440,7 @@ public class HttpServer extends Server<String, HttpContext, HttpRequest, HttpRes
respConfig.defaultCookie = defaultCookie; respConfig.defaultCookie = defaultCookie;
respConfig.autoOptions = autoOptions; respConfig.autoOptions = autoOptions;
respConfig.dateSupplier = dateSupplier; respConfig.dateSupplier = dateSupplier;
respConfig.renders = ((HttpPrepareServlet) prepare).renders; respConfig.httpRender = httpRender;
respConfig.init(config); respConfig.init(config);
final HttpContextConfig contextConfig = new HttpContextConfig(); final HttpContextConfig contextConfig = new HttpContextConfig();
@@ -454,9 +459,10 @@ public class HttpServer extends Server<String, HttpContext, HttpRequest, HttpRes
contextConfig.readTimeoutSeconds = this.readTimeoutSeconds; contextConfig.readTimeoutSeconds = this.readTimeoutSeconds;
contextConfig.writeTimeoutSeconds = this.writeTimeoutSeconds; contextConfig.writeTimeoutSeconds = this.writeTimeoutSeconds;
contextConfig.remoteAddrHeader = addrHeader; contextConfig.remoteAddrHeader = addrHeader;
contextConfig.lazyHeaders = lazyHeaders;
return new HttpContext(contextConfig); HttpContext c = new HttpContext(contextConfig);
if (httpRender != null) httpRender.init(c, renderConfig);
return c;
} }
@Override @Override

View File

@@ -248,7 +248,7 @@ public class HttpServlet extends Servlet<HttpContext, HttpRequest, HttpResponse>
this.rpconly = rpconly; this.rpconly = rpconly;
this.auth = auth; this.auth = auth;
this.cacheseconds = cacheseconds; this.cacheseconds = cacheseconds;
if (Utility.contains(name, '.', '*', '{', '[', '(', '|', '^', '$', '+', '?', '\\') || name.endsWith("/")) { //是否是正则表达式 if (Utility.contains(name, '*', '{', '[', '(', '|', '^', '$', '+', '?', '\\') || name.endsWith("/")) { //是否是正则表达式
this.modeOneCache = false; this.modeOneCache = false;
this.cache = cacheseconds > 0 ? new ConcurrentHashMap<>() : null; this.cache = cacheseconds > 0 ? new ConcurrentHashMap<>() : null;
this.cacheHandler = cacheseconds > 0 ? (HttpResponse response, byte[] content) -> { this.cacheHandler = cacheseconds > 0 ? (HttpResponse response, byte[] content) -> {

View File

@@ -19,7 +19,6 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME;
* *
* @author zhangjx * @author zhangjx
*/ */
@Deprecated
@Inherited @Inherited
@Documented @Documented
@Target({TYPE}) @Target({TYPE})

View File

@@ -273,7 +273,11 @@ public final class MultiContext {
buf[s++] = buf[i]; buf[s++] = buf[i];
} }
int readed = 0; int readed = 0;
while ((readed += input.read(buf, s + readed, pos - readed)) != pos); int t = 0;
while ((t = input.read(buf, s + readed, pos - readed)) > 0) {
readed += t;
if (readed == pos) break;
}
this.bufposition = 0; this.bufposition = 0;
if (Arrays.equals(boundarray, buf)) { if (Arrays.equals(boundarray, buf)) {
this.end = true; this.end = true;

View File

@@ -23,7 +23,7 @@ import org.redkale.asm.Type;
import org.redkale.convert.*; import org.redkale.convert.*;
import org.redkale.convert.json.*; import org.redkale.convert.json.*;
import org.redkale.mq.*; import org.redkale.mq.*;
import org.redkale.net.Cryptor; import org.redkale.net.*;
import org.redkale.net.sncp.Sncp; import org.redkale.net.sncp.Sncp;
import org.redkale.service.*; import org.redkale.service.*;
import org.redkale.util.*; import org.redkale.util.*;
@@ -80,6 +80,8 @@ public final class Rest {
@Retention(RUNTIME) @Retention(RUNTIME)
public static @interface RestDyn { public static @interface RestDyn {
//是否不需要解析HttpHeader对应HttpContext.lazyHeaders
boolean simple() default false;
} }
/** /**
@@ -205,6 +207,18 @@ public final class Rest {
return servlet.getClass().getAnnotation(RestDyn.class) != null; return servlet.getClass().getAnnotation(RestDyn.class) != null;
} }
/**
* 判断HttpServlet是否为Rest动态生成的,且simple
*
* @param servlet 检测的HttpServlet
*
* @return 是否是动态生成的RestHttpServlet
*/
static boolean isSimpleRestDyn(HttpServlet servlet) {
RestDyn dyn = servlet.getClass().getAnnotation(RestDyn.class);
return dyn != null && dyn.simple();
}
/** /**
* 获取Rest动态生成HttpServlet里的Service对象若不是Rest动态生成的HttpServlet返回null * 获取Rest动态生成HttpServlet里的Service对象若不是Rest动态生成的HttpServlet返回null
* *
@@ -767,7 +781,13 @@ public final class Rest {
if (!HttpServlet.class.isAssignableFrom(baseServletType)) throw new RuntimeException(baseServletType + " is not HttpServlet Class on createRestServlet"); if (!HttpServlet.class.isAssignableFrom(baseServletType)) throw new RuntimeException(baseServletType + " is not HttpServlet Class on createRestServlet");
int mod = baseServletType.getModifiers(); int mod = baseServletType.getModifiers();
if (!java.lang.reflect.Modifier.isPublic(mod)) throw new RuntimeException(baseServletType + " is not Public Class on createRestServlet"); if (!java.lang.reflect.Modifier.isPublic(mod)) throw new RuntimeException(baseServletType + " is not Public Class on createRestServlet");
if (java.lang.reflect.Modifier.isAbstract(mod)) throw new RuntimeException(baseServletType + " cannot a abstract Class on createRestServlet"); if (java.lang.reflect.Modifier.isAbstract(mod)) {
for (Method m : baseServletType.getDeclaredMethods()) {
if (java.lang.reflect.Modifier.isAbstract(m.getModifiers())) { //@since 2.4.0
throw new RuntimeException(baseServletType + " cannot contains a abstract Method on " + baseServletType);
}
}
}
final String restInternalName = Type.getInternalName(Rest.class); final String restInternalName = Type.getInternalName(Rest.class);
final String serviceDesc = Type.getDescriptor(serviceType); final String serviceDesc = Type.getDescriptor(serviceType);
@@ -779,8 +799,11 @@ public final class Rest {
final String typeDesc = Type.getDescriptor(java.lang.reflect.Type.class); final String typeDesc = Type.getDescriptor(java.lang.reflect.Type.class);
final String jsonConvertDesc = Type.getDescriptor(JsonConvert.class); final String jsonConvertDesc = Type.getDescriptor(JsonConvert.class);
final String retDesc = Type.getDescriptor(RetResult.class); final String retDesc = Type.getDescriptor(RetResult.class);
final String httpretDesc = Type.getDescriptor(HttpResult.class);
final String scopeDesc = Type.getDescriptor(HttpScope.class);
final String futureDesc = Type.getDescriptor(CompletableFuture.class); final String futureDesc = Type.getDescriptor(CompletableFuture.class);
final String flipperDesc = Type.getDescriptor(Flipper.class); final String flipperDesc = Type.getDescriptor(Flipper.class);
final String channelDesc = Type.getDescriptor(ChannelContext.class);
final String httpServletName = HttpServlet.class.getName().replace('.', '/'); final String httpServletName = HttpServlet.class.getName().replace('.', '/');
final String actionEntryName = HttpServlet.ActionEntry.class.getName().replace('.', '/'); final String actionEntryName = HttpServlet.ActionEntry.class.getName().replace('.', '/');
final String attrDesc = Type.getDescriptor(org.redkale.util.Attribute.class); final String attrDesc = Type.getDescriptor(org.redkale.util.Attribute.class);
@@ -830,16 +853,12 @@ public final class Rest {
List<Map<String, Object>> mappingMaps = new ArrayList<>(); List<Map<String, Object>> mappingMaps = new ArrayList<>();
cw.visit(V1_8, ACC_PUBLIC + ACC_FINAL + ACC_SUPER, newDynName, null, supDynName, null); cw.visit(V1_8, ACC_PUBLIC + ACC_FINAL + ACC_SUPER, newDynName, null, supDynName, null);
{ //RestDyn
av0 = cw.visitAnnotation(Type.getDescriptor(RestDyn.class), true);
av0.visitEnd();
}
{ //RestDynSourceType { //RestDynSourceType
av0 = cw.visitAnnotation(Type.getDescriptor(RestDynSourceType.class), true); av0 = cw.visitAnnotation(Type.getDescriptor(RestDynSourceType.class), true);
av0.visit("value", Type.getType(Type.getDescriptor(serviceType))); av0.visit("value", Type.getType(Type.getDescriptor(serviceType)));
av0.visitEnd(); av0.visitEnd();
} }
boolean dynsimple = true;
final List<MappingEntry> entrys = new ArrayList<>(); final List<MappingEntry> entrys = new ArrayList<>();
final Map<String, org.redkale.util.Attribute> restAttributes = new LinkedHashMap<>(); final Map<String, org.redkale.util.Attribute> restAttributes = new LinkedHashMap<>();
//获取所有可以转换成HttpMapping的方法 //获取所有可以转换成HttpMapping的方法
@@ -918,6 +937,11 @@ public final class Rest {
if (defmodulename.isEmpty() || (!pound && entrys.size() <= 2)) { if (defmodulename.isEmpty() || (!pound && entrys.size() <= 2)) {
for (MappingEntry entry : entrys) { for (MappingEntry entry : entrys) {
String suburl = (catalog.isEmpty() ? "/" : ("/" + catalog + "/")) + (defmodulename.isEmpty() ? "" : (defmodulename + "/")) + entry.name; String suburl = (catalog.isEmpty() ? "/" : ("/" + catalog + "/")) + (defmodulename.isEmpty() ? "" : (defmodulename + "/")) + entry.name;
if ("//".equals(suburl)) {
suburl = "/";
} else if (suburl.length() > 2 && suburl.endsWith("/")) {
suburl += "*";
}
urlpath += "," + suburl; urlpath += "," + suburl;
av1.visit(null, suburl); av1.visit(null, suburl);
} }
@@ -1146,6 +1170,19 @@ public final class Rest {
if (!TYPE_MAP_STRING_STRING.equals(param.getParameterizedType())) throw new RuntimeException("@RestHeaders must on Map<String, String> Parameter in " + method); if (!TYPE_MAP_STRING_STRING.equals(param.getParameterizedType())) throw new RuntimeException("@RestHeaders must on Map<String, String> Parameter in " + method);
comment = ""; comment = "";
} }
RestParams annparams = param.getAnnotation(RestParams.class);
if (annparams != null) {
if (annhead != null) throw new RuntimeException("@RestParams and @RestHeader cannot on the same Parameter in " + method);
if (anncookie != null) throw new RuntimeException("@RestParams and @RestCookie cannot on the same Parameter in " + method);
if (annsid != null) throw new RuntimeException("@RestParams and @RestSessionid cannot on the same Parameter in " + method);
if (annaddr != null) throw new RuntimeException("@RestParams and @RestAddress cannot on the same Parameter in " + method);
if (annbody != null) throw new RuntimeException("@RestParams and @RestBody cannot on the same Parameter in " + method);
if (annfile != null) throw new RuntimeException("@RestParams and @RestUploadFile cannot on the same Parameter in " + method);
if (userid != null) throw new RuntimeException("@RestParams and @RestUserid cannot on the same Parameter in " + method);
if (annheaders != null) throw new RuntimeException("@RestParams and @RestHeaders cannot on the same Parameter in " + method);
if (!TYPE_MAP_STRING_STRING.equals(param.getParameterizedType())) throw new RuntimeException("@RestParams must on Map<String, String> Parameter in " + method);
comment = "";
}
RestParam annpara = param.getAnnotation(RestParam.class); RestParam annpara = param.getAnnotation(RestParam.class);
if (annpara != null) radix = annpara.radix(); if (annpara != null) radix = annpara.radix();
@@ -1181,7 +1218,7 @@ public final class Rest {
} while ((loop = loop.getSuperclass()) != Object.class); } while ((loop = loop.getSuperclass()) != Object.class);
} }
java.lang.reflect.Type paramtype = TypeToken.getGenericType(param.getParameterizedType(), serviceType); java.lang.reflect.Type paramtype = TypeToken.getGenericType(param.getParameterizedType(), serviceType);
paramlist.add(new Object[]{param, n, ptype, radix, comment, required, annpara, annsid, annaddr, annhead, anncookie, annbody, annfile, annuri, userid, annheaders, paramtype}); paramlist.add(new Object[]{param, n, ptype, radix, comment, required, annpara, annsid, annaddr, annhead, anncookie, annbody, annfile, annuri, userid, annheaders, annparams, paramtype});
} }
Map<String, Object> mappingMap = new LinkedHashMap<>(); Map<String, Object> mappingMap = new LinkedHashMap<>();
@@ -1196,6 +1233,7 @@ public final class Rest {
} }
av0 = mv.visitAnnotation(mappingDesc, true); av0 = mv.visitAnnotation(mappingDesc, true);
String url = (catalog.isEmpty() ? "/" : ("/" + catalog + "/")) + (defmodulename.isEmpty() ? "" : (defmodulename + "/")) + entry.name + (reqpath ? "/" : ""); String url = (catalog.isEmpty() ? "/" : ("/" + catalog + "/")) + (defmodulename.isEmpty() ? "" : (defmodulename + "/")) + entry.name + (reqpath ? "/" : "");
if ("//".equals(url)) url = "/";
av0.visit("url", url); av0.visit("url", url);
av0.visit("rpconly", entry.rpconly); av0.visit("rpconly", entry.rpconly);
av0.visit("auth", entry.auth); av0.visit("auth", entry.auth);
@@ -1303,8 +1341,9 @@ public final class Rest {
RestURI annuri = (RestURI) ps[13]; RestURI annuri = (RestURI) ps[13];
RestUserid userid = (RestUserid) ps[14]; RestUserid userid = (RestUserid) ps[14];
RestHeaders annheaders = (RestHeaders) ps[15]; RestHeaders annheaders = (RestHeaders) ps[15];
RestParams annparams = (RestParams) ps[16];
java.lang.reflect.Type pgentype = (java.lang.reflect.Type) ps[16]; java.lang.reflect.Type pgentype = (java.lang.reflect.Type) ps[17];
if (dynsimple && (annsid != null || annaddr != null || annhead != null || anncookie != null || annfile != null || annheaders != null)) dynsimple = false;
final boolean ishead = annhead != null; //是否取getHeader 而不是 getParameter final boolean ishead = annhead != null; //是否取getHeader 而不是 getParameter
final boolean iscookie = anncookie != null; //是否取getCookie final boolean iscookie = anncookie != null; //是否取getCookie
@@ -1343,6 +1382,11 @@ public final class Rest {
mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getHeaders", "()Ljava/util/Map;", false); mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getHeaders", "()Ljava/util/Map;", false);
mv.visitVarInsn(ASTORE, maxLocals); mv.visitVarInsn(ASTORE, maxLocals);
varInsns.add(new int[]{ALOAD, maxLocals}); varInsns.add(new int[]{ALOAD, maxLocals});
} else if (annparams != null) { //HttpRequest.getParameters
mv.visitVarInsn(ALOAD, 1);
mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getParameters", "()Ljava/util/Map;", false);
mv.visitVarInsn(ASTORE, maxLocals);
varInsns.add(new int[]{ALOAD, maxLocals});
} else if (annbody != null) { //HttpRequest.getBodyUTF8 / HttpRequest.getBody } else if (annbody != null) { //HttpRequest.getBodyUTF8 / HttpRequest.getBody
if (ptype == String.class) { if (ptype == String.class) {
mv.visitVarInsn(ALOAD, 1); mv.visitVarInsn(ALOAD, 1);
@@ -1634,6 +1678,11 @@ public final class Rest {
mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, iscookie ? "getCookie" : (ishead ? "getHeader" : "getParameter"), "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;", false); mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, iscookie ? "getCookie" : (ishead ? "getHeader" : "getParameter"), "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;", false);
mv.visitVarInsn(ASTORE, maxLocals); mv.visitVarInsn(ASTORE, maxLocals);
varInsns.add(new int[]{ALOAD, maxLocals}); varInsns.add(new int[]{ALOAD, maxLocals});
} else if (ptype == ChannelContext.class) {
mv.visitVarInsn(ALOAD, 1);
mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getChannelContext", "()" + channelDesc, false);
mv.visitVarInsn(ASTORE, maxLocals);
varInsns.add(new int[]{ALOAD, maxLocals});
} else if (ptype == Flipper.class) { } else if (ptype == Flipper.class) {
mv.visitVarInsn(ALOAD, 1); mv.visitVarInsn(ALOAD, 1);
mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getFlipper", "()" + flipperDesc, false); mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getFlipper", "()" + flipperDesc, false);
@@ -1920,29 +1969,23 @@ public final class Rest {
if (!CompletableFuture.class.isAssignableFrom(returnType) && !org.redkale.service.RetResult.class.isAssignableFrom(returnType) if (!CompletableFuture.class.isAssignableFrom(returnType) && !org.redkale.service.RetResult.class.isAssignableFrom(returnType)
&& !HttpResult.class.isAssignableFrom(returnType) && !HttpScope.class.isAssignableFrom(returnType)) { && !HttpResult.class.isAssignableFrom(returnType) && !HttpScope.class.isAssignableFrom(returnType)) {
mv.visitVarInsn(ASTORE, maxLocals); mv.visitVarInsn(ASTORE, maxLocals);
if (entry.contentLength > 0) { mv.visitVarInsn(ALOAD, 2); //response
mv.visitVarInsn(ALOAD, 2); //response mv.visitVarInsn(ALOAD, maxLocals);
pushInt(mv, entry.contentLength); mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finishJson", "(Ljava/lang/Object;)V", false);
mv.visitVarInsn(ALOAD, maxLocals);
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finishJson", "(ILjava/lang/Object;)V", false);
} else {
mv.visitVarInsn(ALOAD, 2); //response
mv.visitVarInsn(ALOAD, maxLocals);
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finishJson", "(Ljava/lang/Object;)V", false);
}
mv.visitInsn(RETURN); mv.visitInsn(RETURN);
maxLocals++; maxLocals++;
} else { } else {
mv.visitVarInsn(ASTORE, maxLocals); mv.visitVarInsn(ASTORE, maxLocals);
mv.visitVarInsn(ALOAD, 2); //response mv.visitVarInsn(ALOAD, 2); //response
String objdesc = HttpScope.class.isAssignableFrom(returnType) ? scopeDesc : (HttpResult.class.isAssignableFrom(returnType) ? httpretDesc : "Ljava/lang/Object;");
if (rcs != null && rcs.length > 0) { if (rcs != null && rcs.length > 0) {
mv.visitVarInsn(ALOAD, 0); mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, newDynName, REST_JSONCONVERT_FIELD_PREFIX + restConverts.size(), jsonConvertDesc); mv.visitFieldInsn(GETFIELD, newDynName, REST_JSONCONVERT_FIELD_PREFIX + restConverts.size(), jsonConvertDesc);
mv.visitVarInsn(ALOAD, maxLocals); mv.visitVarInsn(ALOAD, maxLocals);
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finish", "(" + convertDesc + "Ljava/lang/Object;)V", false); mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finish", "(" + convertDesc + objdesc + ")V", false);
} else { } else {
mv.visitVarInsn(ALOAD, maxLocals); mv.visitVarInsn(ALOAD, maxLocals);
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finish", "(Ljava/lang/Object;)V", false); mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finish", "(" + objdesc + ")V", false);
} }
mv.visitInsn(RETURN); mv.visitInsn(RETURN);
maxLocals++; maxLocals++;
@@ -2075,6 +2118,12 @@ public final class Rest {
mv.visitEnd(); mv.visitEnd();
} }
{ //RestDyn
av0 = cw.visitAnnotation(Type.getDescriptor(RestDyn.class), true);
av0.visit("simple", (Boolean) dynsimple);
av0.visitEnd();
}
cw.visitEnd(); cw.visitEnd();
newLoader.addClass(newDynName.replace('/', '.'), cw.toByteArray()); newLoader.addClass(newDynName.replace('/', '.'), cw.toByteArray());
try { try {
@@ -2213,7 +2262,6 @@ public final class Rest {
this.rpconly = serrpconly || mapping.rpconly(); this.rpconly = serrpconly || mapping.rpconly();
this.actionid = mapping.actionid(); this.actionid = mapping.actionid();
this.cacheseconds = mapping.cacheseconds(); this.cacheseconds = mapping.cacheseconds();
this.contentLength = mapping.length();
this.comment = mapping.comment(); this.comment = mapping.comment();
boolean pound = false; boolean pound = false;
Parameter[] params = method.getParameters(); Parameter[] params = method.getParameters();
@@ -2229,8 +2277,6 @@ public final class Rest {
this.newActionClassName = "_Dyn_" + this.newMethodName + "_ActionHttpServlet"; this.newActionClassName = "_Dyn_" + this.newMethodName + "_ActionHttpServlet";
} }
public final int contentLength;
public final int methodidx; // _paramtypes 的下标从0开始 public final int methodidx; // _paramtypes 的下标从0开始
public final Method mappingMethod; public final Method mappingMethod;

View File

@@ -74,10 +74,6 @@ public @interface RestMapping {
*/ */
int cacheseconds() default 0; int cacheseconds() default 0;
//json结果的长度, 临时功能, 对应&#64;HttpMapping.length
@Deprecated
int length() default 0;
/** /**
* 允许方法(不区分大小写),如:GET/POST/PUT,为空表示允许所有方法, 对应&#64;HttpMapping.methods * 允许方法(不区分大小写),如:GET/POST/PUT,为空表示允许所有方法, 对应&#64;HttpMapping.methods
* *

View File

@@ -0,0 +1,28 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.net.http;
import java.lang.annotation.*;
import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
/**
* 只能注解于RestService类的方法的参数或参数内的Map&#60;String, String&#62;字段
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*
* @since 2.4.0
*/
@Inherited
@Documented
@Target({PARAMETER, FIELD})
@Retention(RUNTIME)
public @interface RestParams {
}

View File

@@ -22,6 +22,7 @@ import org.redkale.source.*;
import org.redkale.util.*; import org.redkale.util.*;
/** /**
* 注: 部署了WebSocketNodeService就必然要配置SNCP协议的Server不然无法做到WebSocketNode.sendMessage方法的有效性
* *
* <p> * <p>
* 详情见: https://redkale.org * 详情见: https://redkale.org
@@ -284,7 +285,7 @@ public abstract class WebSocketNode {
CompletableFuture<Boolean> localFuture = null; CompletableFuture<Boolean> localFuture = null;
if (this.localEngine != null) localFuture = CompletableFuture.completedFuture(localEngine.existsLocalWebSocket(userid)); if (this.localEngine != null) localFuture = CompletableFuture.completedFuture(localEngine.existsLocalWebSocket(userid));
if (this.source == null || this.remoteNode == null) { if (this.source == null || this.remoteNode == null) {
if (logger.isLoggable(Level.FINEST)) logger.finest("websocket remote node is null"); if (logger.isLoggable(Level.FINEST)) logger.finest("websocket " + (this.remoteNode == null ? (this.source == null ? "remote and source" : "remote") : "source") + " node is null");
//没有CacheSource就不会有分布式节点 //没有CacheSource就不会有分布式节点
return localFuture == null ? CompletableFuture.completedFuture(false) : localFuture; return localFuture == null ? CompletableFuture.completedFuture(false) : localFuture;
} }
@@ -317,7 +318,7 @@ public abstract class WebSocketNode {
public CompletableFuture<Boolean> existsWebSocket(final WebSocketUserAddress userAddress) { public CompletableFuture<Boolean> existsWebSocket(final WebSocketUserAddress userAddress) {
if (this.localEngine != null && localEngine.existsLocalWebSocket(userAddress.userid())) return CompletableFuture.completedFuture(true); if (this.localEngine != null && localEngine.existsLocalWebSocket(userAddress.userid())) return CompletableFuture.completedFuture(true);
if (this.source == null || this.remoteNode == null) { if (this.source == null || this.remoteNode == null) {
if (logger.isLoggable(Level.FINEST)) logger.finest("websocket remote node is null"); if (logger.isLoggable(Level.FINEST)) logger.finest("websocket " + (this.remoteNode == null ? (this.source == null ? "remote and source" : "remote") : "source") + " node is null");
//没有CacheSource就不会有分布式节点 //没有CacheSource就不会有分布式节点
return CompletableFuture.completedFuture(false); return CompletableFuture.completedFuture(false);
} }
@@ -365,7 +366,7 @@ public abstract class WebSocketNode {
CompletableFuture<Integer> localFuture = null; CompletableFuture<Integer> localFuture = null;
if (this.localEngine != null) localFuture = CompletableFuture.completedFuture(localEngine.forceCloseLocalWebSocket(userAddress == null ? userid : userAddress.userid())); if (this.localEngine != null) localFuture = CompletableFuture.completedFuture(localEngine.forceCloseLocalWebSocket(userAddress == null ? userid : userAddress.userid()));
if (this.source == null || this.remoteNode == null) { if (this.source == null || this.remoteNode == null) {
if (logger.isLoggable(Level.FINEST)) logger.finest("websocket remote node is null"); if (logger.isLoggable(Level.FINEST)) logger.finest("websocket " + (this.remoteNode == null ? (this.source == null ? "remote and source" : "remote") : "source") + " node is null");
//没有CacheSource就不会有分布式节点 //没有CacheSource就不会有分布式节点
return localFuture == null ? CompletableFuture.completedFuture(0) : localFuture; return localFuture == null ? CompletableFuture.completedFuture(0) : localFuture;
} }
@@ -629,7 +630,7 @@ public abstract class WebSocketNode {
CompletableFuture<Integer> localFuture = null; CompletableFuture<Integer> localFuture = null;
if (this.localEngine != null) localFuture = localEngine.sendLocalMessage(message, last, userid); if (this.localEngine != null) localFuture = localEngine.sendLocalMessage(message, last, userid);
if (this.source == null || this.remoteNode == null) { if (this.source == null || this.remoteNode == null) {
if (logger.isLoggable(Level.FINEST)) logger.finest("websocket remote node is null"); if (logger.isLoggable(Level.FINEST)) logger.finest("websocket " + (this.remoteNode == null ? (this.source == null ? "remote and source" : "remote") : "source") + " node is null");
//没有CacheSource就不会有分布式节点 //没有CacheSource就不会有分布式节点
return localFuture == null ? CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY) : localFuture; return localFuture == null ? CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY) : localFuture;
} }
@@ -664,7 +665,7 @@ public abstract class WebSocketNode {
return this.localEngine == null ? CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY) : localEngine.sendLocalMessage(message, last, userids); return this.localEngine == null ? CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY) : localEngine.sendLocalMessage(message, last, userids);
} }
if (this.source == null || this.remoteNode == null) { if (this.source == null || this.remoteNode == null) {
if (logger.isLoggable(Level.FINEST)) logger.finest("websocket remote node is null"); if (logger.isLoggable(Level.FINEST)) logger.finest("websocket " + (this.remoteNode == null ? (this.source == null ? "remote and source" : "remote") : "source") + " node is null");
//没有CacheSource就不会有分布式节点 //没有CacheSource就不会有分布式节点
return CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY); return CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY);
} }
@@ -954,7 +955,7 @@ public abstract class WebSocketNode {
CompletableFuture<Integer> localFuture = null; CompletableFuture<Integer> localFuture = null;
if (this.localEngine != null) localFuture = localEngine.sendLocalAction(action, userid); if (this.localEngine != null) localFuture = localEngine.sendLocalAction(action, userid);
if (this.source == null || this.remoteNode == null) { if (this.source == null || this.remoteNode == null) {
if (logger.isLoggable(Level.FINEST)) logger.finest("websocket remote node is null"); if (logger.isLoggable(Level.FINEST)) logger.finest("websocket " + (this.remoteNode == null ? (this.source == null ? "remote and source" : "remote") : "source") + " node is null");
//没有CacheSource就不会有分布式节点 //没有CacheSource就不会有分布式节点
return localFuture == null ? CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY) : localFuture; return localFuture == null ? CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY) : localFuture;
} }
@@ -987,7 +988,7 @@ public abstract class WebSocketNode {
return this.localEngine == null ? CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY) : localEngine.sendLocalAction(action, userids); return this.localEngine == null ? CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY) : localEngine.sendLocalAction(action, userids);
} }
if (this.source == null || this.remoteNode == null) { if (this.source == null || this.remoteNode == null) {
if (logger.isLoggable(Level.FINEST)) logger.finest("websocket remote node is null"); if (logger.isLoggable(Level.FINEST)) logger.finest("websocket " + (this.remoteNode == null ? (this.source == null ? "remote and source" : "remote") : "source") + " node is null");
//没有CacheSource就不会有分布式节点 //没有CacheSource就不会有分布式节点
return CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY); return CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY);
} }

View File

@@ -93,11 +93,11 @@ public class WebSocketReadHandler implements CompletionHandler<Integer, ByteBuff
* | Payload Data continued | * | Payload Data continued |
* +-----------------------------------------------------------------------+ * +-----------------------------------------------------------------------+
* *
* @param realbuf * @param realbuf ByteBuffer
* *
*/ */
protected void readDecode(final ByteBuffer realbuf) { protected void readDecode(final ByteBuffer realbuf) {
if (debug) logger.log(Level.FINEST, "read websocket message's length = " + realbuf.remaining()); if (debug && realbuf.remaining() > 6) logger.log(Level.FINEST, "read websocket message's length = " + realbuf.remaining());
if (!realbuf.hasRemaining()) return; if (!realbuf.hasRemaining()) return;
ByteBuffer buffer = realbuf; ByteBuffer buffer = realbuf;
byte frameOpcode; byte frameOpcode;

View File

@@ -59,7 +59,8 @@ public abstract class AbstractService implements Service {
if (workExecutor != null) return workExecutor; if (workExecutor != null) return workExecutor;
Thread thread = Thread.currentThread(); Thread thread = Thread.currentThread();
if (thread instanceof WorkThread) { if (thread instanceof WorkThread) {
return ((WorkThread) thread).getWorkExecutor(); ExecutorService e = ((WorkThread) thread).getWorkExecutor();
if (e != null) return e;
} }
return ForkJoinPool.commonPool(); return ForkJoinPool.commonPool();
} }

View File

@@ -5,12 +5,14 @@
*/ */
package org.redkale.service; package org.redkale.service;
import java.io.*;
import java.lang.annotation.*; import java.lang.annotation.*;
import static java.lang.annotation.ElementType.*; import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.RUNTIME; import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.reflect.*; import java.lang.reflect.*;
import java.util.*; import java.util.*;
import java.util.function.BiFunction; import java.util.function.BiFunction;
import static org.redkale.boot.Application.*;
/** /**
* 用于定义错误码的注解 <br> * 用于定义错误码的注解 <br>
@@ -50,7 +52,7 @@ public @interface RetLabel {
public static abstract class RetLoader { public static abstract class RetLoader {
public static Map<String, Map<Integer, String>> loadMap(Class clazz) { public static Map<String, Map<Integer, String>> loadMap(Class clazz) {
final Map<String, Map<Integer, String>> rets = new HashMap<>(); final Map<String, Map<Integer, String>> rets = new LinkedHashMap<>();
ServiceLoader<RetInfoTransfer> loader = ServiceLoader.load(RetInfoTransfer.class); ServiceLoader<RetInfoTransfer> loader = ServiceLoader.load(RetInfoTransfer.class);
Iterator<RetInfoTransfer> it = loader.iterator(); Iterator<RetInfoTransfer> it = loader.iterator();
RetInfoTransfer func = it.hasNext() ? it.next() : null; RetInfoTransfer func = it.hasNext() ? it.next() : null;
@@ -67,15 +69,40 @@ public @interface RetLabel {
continue; continue;
} }
for (RetLabel info : infos) { for (RetLabel info : infos) {
rets.computeIfAbsent(info.locale(), (k) -> new HashMap<>()).put(value, func == null ? info.value() : func.apply(value, info.value())); rets.computeIfAbsent(info.locale(), (k) -> new LinkedHashMap<>()).put(value, func == null ? info.value() : func.apply(value, info.value()));
} }
} }
try {
File propPath = new File(System.getProperty(RESNAME_APP_CONF, new File(System.getProperty(RESNAME_APP_HOME, ""), "conf").getPath()));
if (propPath.isDirectory() && propPath.canRead()) {
final String prefix = clazz.getSimpleName().toLowerCase();
for (File propFile : propPath.listFiles(f -> f.getName().startsWith(prefix) && f.getName().endsWith(".properties"))) {
if (propFile.isFile() && propFile.canRead()) {
String locale = propFile.getName().substring(prefix.length()).replaceAll("\\.\\d+", "");
locale = locale.substring(0, locale.indexOf(".properties"));
Map<Integer, String> defrets = rets.get(locale);
if (defrets != null) {
InputStreamReader in = new InputStreamReader(new FileInputStream(propFile), "UTF-8");
Properties prop = new Properties();
prop.load(in);
in.close();
prop.forEach((k, v) -> {
int retcode = Integer.parseInt(k.toString());
if (defrets.containsKey(retcode)) defrets.put(retcode, v.toString());
});
}
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
return rets; return rets;
} }
@Deprecated // @Deprecated
public static Map<Integer, String> load(Class clazz) { // public static Map<Integer, String> load(Class clazz) {
return loadMap(clazz).computeIfAbsent("", (k) -> new HashMap<>()); // return loadMap(clazz).computeIfAbsent("", (k) -> new LinkedHashMap<>());
} // }
} }
} }

View File

@@ -22,7 +22,6 @@ import org.redkale.util.*;
/** /**
* CacheSource的默认实现--内存缓存 * CacheSource的默认实现--内存缓存
* *
* @param <V> value类型
* <p> * <p>
* 详情见: https://redkale.org * 详情见: https://redkale.org
* *
@@ -32,7 +31,7 @@ import org.redkale.util.*;
@AutoLoad(false) @AutoLoad(false)
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@ResourceType(CacheSource.class) @ResourceType(CacheSource.class)
public final class CacheMemorySource<V extends Object> extends AbstractService implements CacheSource<V>, Service, AutoCloseable, Resourcable { public final class CacheMemorySource extends AbstractService implements CacheSource, Service, AutoCloseable, Resourcable {
private static final Type STRING_ENTRY_TYPE = new TypeToken<CacheEntry<String>>() { private static final Type STRING_ENTRY_TYPE = new TypeToken<CacheEntry<String>>() {
}.getType(); }.getType();
@@ -71,23 +70,9 @@ public final class CacheMemorySource<V extends Object> extends AbstractService i
if (t != null) logger.log(Level.SEVERE, "CompletableFuture complete error", (Throwable) t); if (t != null) logger.log(Level.SEVERE, "CompletableFuture complete error", (Throwable) t);
}; };
@RpcRemote
protected CacheSource<V> remoteSource;
public CacheMemorySource() { public CacheMemorySource() {
} }
@Override
public final void initValueType(Type valueType) {
this.objValueType = valueType;
this.initTransient(this.objValueType == null);
}
@Override
public final void initTransient(boolean flag) {
this.needStore = !flag;
}
@Override @Override
public final String getType() { public final String getType() {
return "memory"; return "memory";
@@ -110,17 +95,6 @@ public final class CacheMemorySource<V extends Object> extends AbstractService i
if (this.convert == null) this.convert = JsonConvert.root(); if (this.convert == null) this.convert = JsonConvert.root();
final CacheMemorySource self = this; final CacheMemorySource self = this;
AnyValue prop = conf == null ? null : conf.getAnyValue("properties"); AnyValue prop = conf == null ? null : conf.getAnyValue("properties");
if (prop != null) {
String storeValueStr = prop.getValue("value-type");
if (storeValueStr != null) {
try {
this.initValueType(Thread.currentThread().getContextClassLoader().loadClass(storeValueStr));
} catch (Throwable e) {
logger.log(Level.SEVERE, self.getClass().getSimpleName() + " load key & value store class (" + storeValueStr + ") error", e);
}
}
this.initTransient(prop.getBoolValue("store-ignore", false));
}
String expireHandlerClass = prop == null ? null : prop.getValue("expirehandler"); String expireHandlerClass = prop == null ? null : prop.getValue("expirehandler");
if (expireHandlerClass != null) { if (expireHandlerClass != null) {
try { try {
@@ -192,28 +166,6 @@ public final class CacheMemorySource<V extends Object> extends AbstractService i
logger.log(Level.SEVERE, CacheSource.class.getSimpleName() + "(" + resourceName() + ") load store file error ", e); logger.log(Level.SEVERE, CacheSource.class.getSimpleName() + "(" + resourceName() + ") load store file error ", e);
} }
} }
if (remoteSource != null && !Sncp.isRemote(this)) {
SncpClient client = Sncp.getSncpClient((Service) remoteSource);
if (client != null && client.getRemoteGroupTransport() != null
&& client.getRemoteGroupTransport().getRemoteAddresses().length > 0) {
super.runAsync(() -> {
try {
CompletableFuture<List<CacheEntry<Object>>> listFuture = remoteSource.queryListAsync();
listFuture.whenComplete((list, exp) -> {
if (exp != null) {
if (logger.isLoggable(Level.FINEST)) logger.log(Level.FINEST, CacheSource.class.getSimpleName() + "(" + resourceName() + ") queryListAsync error", exp);
} else {
for (CacheEntry<Object> entry : list) {
container.put(entry.key, entry);
}
}
});
} catch (Exception e) {
if (logger.isLoggable(Level.FINEST)) logger.log(Level.FINEST, CacheSource.class.getSimpleName() + "(" + resourceName() + ") queryListAsync error, maybe remote node connot connect ", e);
}
});
}
}
} }
public static void main(String[] args) throws Exception { public static void main(String[] args) throws Exception {
@@ -222,44 +174,43 @@ public final class CacheMemorySource<V extends Object> extends AbstractService i
CacheMemorySource source = new CacheMemorySource(); CacheMemorySource source = new CacheMemorySource();
source.defaultConvert = JsonFactory.root().getConvert(); source.defaultConvert = JsonFactory.root().getConvert();
source.initValueType(String.class); //value用String类型
source.initTransient(false);
source.init(conf); source.init(conf);
System.out.println("------------------------------------"); System.out.println("------------------------------------");
source.remove("key1"); source.remove("key1");
source.remove("key2"); source.remove("key2");
source.remove("300"); source.remove("300");
source.set("key1", "value1"); source.setString("key1", "value1");
source.setString("keystr1", "strvalue1"); source.setString("keystr1", "strvalue1");
source.setLong("keylong1", 333L); source.setLong("keylong1", 333L);
source.set("300", "4000"); source.setString("300", "4000");
source.getAndRefresh("key1", 3500); source.getStringAndRefresh("key1", 3500);
System.out.println("[有值] 300 GET : " + source.get("300")); System.out.println("[有值] 300 GET : " + source.get("300", String.class));
System.out.println("[有值] key1 GET : " + source.get("key1")); System.out.println("[有值] key1 GET : " + source.get("key1", String.class));
System.out.println("[无值] key2 GET : " + source.get("key2")); System.out.println("[无值] key2 GET : " + source.get("key2", String.class));
System.out.println("[有值] keylong1 GET : " + source.getLong("keylong1", 0L)); System.out.println("[有值] keylong1 GET : " + source.getLong("keylong1", 0L));
System.out.println("[有值] key1 EXISTS : " + source.exists("key1")); System.out.println("[有值] key1 EXISTS : " + source.exists("key1"));
System.out.println("[无值] key2 EXISTS : " + source.exists("key2")); System.out.println("[无值] key2 EXISTS : " + source.exists("key2"));
source.remove("keys3"); source.remove("keys3");
source.appendListItem("keys3", "vals1"); source.appendStringListItem("keys3", "vals1");
source.appendListItem("keys3", "vals2"); source.appendStringListItem("keys3", "vals2");
System.out.println("-------- keys3 追加了两个值 --------"); System.out.println("-------- keys3 追加了两个值 --------");
System.out.println("[两值] keys3 VALUES : " + source.getCollection("keys3")); System.out.println("[两值] keys3 VALUES : " + source.getStringCollection("keys3"));
System.out.println("[有值] keys3 EXISTS : " + source.exists("keys3")); System.out.println("[有值] keys3 EXISTS : " + source.exists("keys3"));
source.removeListItem("keys3", "vals1"); source.removeStringListItem("keys3", "vals1");
System.out.println("[一值] keys3 VALUES : " + source.getCollection("keys3")); System.out.println("[一值] keys3 VALUES : " + source.getStringCollection("keys3"));
source.getCollectionAndRefresh("keys3", 3000); source.getStringCollectionAndRefresh("keys3", 3000);
source.remove("sets3"); source.remove("sets3");
source.appendSetItem("sets3", "setvals1"); source.appendStringSetItem("sets3", "setvals1");
source.appendSetItem("sets3", "setvals2"); source.appendStringSetItem("sets3", "setvals2");
source.appendSetItem("sets3", "setvals1"); source.appendStringSetItem("sets3", "setvals1");
System.out.println("[两值] sets3 VALUES : " + source.getCollection("sets3")); System.out.println("[两值] sets3 VALUES : " + source.getStringCollection("sets3"));
System.out.println("[有值] sets3 EXISTS : " + source.exists("sets3")); System.out.println("[有值] sets3 EXISTS : " + source.exists("sets3"));
source.removeSetItem("sets3", "setvals1"); source.removeStringSetItem("sets3", "setvals1");
System.out.println("[一值] sets3 VALUES : " + source.getCollection("sets3")); System.out.println("[一值] sets3 VALUES : " + source.getStringCollection("sets3"));
System.out.println("sets3 大小 : " + source.getCollectionSize("sets3")); System.out.println("sets3 大小 : " + source.getCollectionSize("sets3"));
System.out.println("all keys: " + source.queryKeys()); System.out.println("all keys: " + source.queryKeys());
System.out.println("newnum 值 : " + source.incr("newnum")); System.out.println("newnum 值 : " + source.incr("newnum"));
@@ -340,6 +291,7 @@ public final class CacheMemorySource<V extends Object> extends AbstractService i
logger.log(Level.SEVERE, CacheSource.class.getSimpleName() + "(" + resourceName() + ") store to file error ", e); logger.log(Level.SEVERE, CacheSource.class.getSimpleName() + "(" + resourceName() + ") store to file error ", e);
} }
} }
//----------- hxxx -------------- //----------- hxxx --------------
@Override @Override
@@ -523,19 +475,13 @@ public final class CacheMemorySource<V extends Object> extends AbstractService i
} }
@Override @Override
@SuppressWarnings("unchecked") public <T> T get(final String key, final Type type) {
public V get(String key) {
if (key == null) return null; if (key == null) return null;
CacheEntry entry = container.get(key); CacheEntry entry = container.get(key);
if (entry == null || entry.isExpired()) return null; if (entry == null || entry.isExpired()) return null;
if (entry.isListCacheType()) return (V) (entry.listValue == null ? null : new ArrayList(entry.listValue)); if (entry.isListCacheType()) return (T) (entry.listValue == null ? null : new ArrayList(entry.listValue));
if (entry.isSetCacheType()) return (V) (entry.csetValue == null ? null : new HashSet(entry.csetValue)); if (entry.isSetCacheType()) return (T) (entry.csetValue == null ? null : new HashSet(entry.csetValue));
return (V) entry.objectValue; return (T) entry.objectValue;
}
@Override
public <T> T get(final String key, final Type type) {
return (T) get(key);
} }
@Override @Override
@@ -656,14 +602,9 @@ public final class CacheMemorySource<V extends Object> extends AbstractService i
} }
//----------- hxxx -------------- //----------- hxxx --------------
@Override
public CompletableFuture<V> getAsync(final String key) {
return CompletableFuture.supplyAsync(() -> get(key), getExecutor());
}
@Override @Override
public <T> CompletableFuture<T> getAsync(final String key, final Type type) { public <T> CompletableFuture<T> getAsync(final String key, final Type type) {
return CompletableFuture.supplyAsync(() -> (T) get(key), getExecutor()); return CompletableFuture.supplyAsync(() -> (T) get(key, type), getExecutor());
} }
@Override @Override
@@ -677,21 +618,15 @@ public final class CacheMemorySource<V extends Object> extends AbstractService i
} }
@Override @Override
@SuppressWarnings("unchecked") public <T> T getAndRefresh(final String key, final int expireSeconds, final Type type) {
public V getAndRefresh(String key, final int expireSeconds) {
if (key == null) return null; if (key == null) return null;
CacheEntry entry = container.get(key); CacheEntry entry = container.get(key);
if (entry == null || entry.isExpired()) return null; if (entry == null || entry.isExpired()) return null;
entry.lastAccessed = (int) (System.currentTimeMillis() / 1000); entry.lastAccessed = (int) (System.currentTimeMillis() / 1000);
entry.expireSeconds = expireSeconds; entry.expireSeconds = expireSeconds;
if (entry.isListCacheType()) return (V) (entry.listValue == null ? null : new ArrayList(entry.listValue)); if (entry.isListCacheType()) return (T) (entry.listValue == null ? null : new ArrayList(entry.listValue));
if (entry.isSetCacheType()) return (V) (entry.csetValue == null ? null : new HashSet(entry.csetValue)); if (entry.isSetCacheType()) return (T) (entry.csetValue == null ? null : new HashSet(entry.csetValue));
return (V) entry.objectValue; return (T) entry.objectValue;
}
@Override
public <T> T getAndRefresh(final String key, final int expireSeconds, final Type type) {
return (T) getAndRefresh(key, expireSeconds);
} }
@Override @Override
@@ -716,11 +651,6 @@ public final class CacheMemorySource<V extends Object> extends AbstractService i
} }
@Override
public CompletableFuture<V> getAndRefreshAsync(final String key, final int expireSeconds) {
return CompletableFuture.supplyAsync(() -> getAndRefresh(key, expireSeconds), getExecutor());
}
@Override @Override
public <T> CompletableFuture<T> getAndRefreshAsync(final String key, final int expireSeconds, final Type type) { public <T> CompletableFuture<T> getAndRefreshAsync(final String key, final int expireSeconds, final Type type) {
return CompletableFuture.supplyAsync(() -> getAndRefresh(key, expireSeconds, type), getExecutor()); return CompletableFuture.supplyAsync(() -> getAndRefresh(key, expireSeconds, type), getExecutor());
@@ -777,11 +707,6 @@ public final class CacheMemorySource<V extends Object> extends AbstractService i
} }
} }
@Override
public void set(String key, V value) {
set(CacheEntryType.OBJECT, key, value);
}
@Override @Override
public <T> void set(String key, Convert convert, T value) { public <T> void set(String key, Convert convert, T value) {
set(CacheEntryType.OBJECT, key, value); set(CacheEntryType.OBJECT, key, value);
@@ -807,11 +732,6 @@ public final class CacheMemorySource<V extends Object> extends AbstractService i
set(CacheEntryType.LONG, key, value); set(CacheEntryType.LONG, key, value);
} }
@Override
public CompletableFuture<Void> setAsync(String key, V value) {
return CompletableFuture.runAsync(() -> set(key, value), getExecutor()).whenComplete(futureCompleteConsumer);
}
@Override @Override
public <T> CompletableFuture<Void> setAsync(String key, Convert convert, T value) { public <T> CompletableFuture<Void> setAsync(String key, Convert convert, T value) {
return CompletableFuture.runAsync(() -> set(key, convert, value), getExecutor()).whenComplete(futureCompleteConsumer); return CompletableFuture.runAsync(() -> set(key, convert, value), getExecutor()).whenComplete(futureCompleteConsumer);
@@ -850,11 +770,6 @@ public final class CacheMemorySource<V extends Object> extends AbstractService i
} }
} }
@Override
public void set(int expireSeconds, String key, V value) {
set(CacheEntryType.OBJECT, expireSeconds, key, value);
}
@Override @Override
public <T> void set(final int expireSeconds, String key, Convert convert, T value) { public <T> void set(final int expireSeconds, String key, Convert convert, T value) {
set(CacheEntryType.OBJECT, expireSeconds, key, value); set(CacheEntryType.OBJECT, expireSeconds, key, value);
@@ -880,11 +795,6 @@ public final class CacheMemorySource<V extends Object> extends AbstractService i
set(CacheEntryType.LONG, expireSeconds, key, value); set(CacheEntryType.LONG, expireSeconds, key, value);
} }
@Override
public CompletableFuture<Void> setAsync(int expireSeconds, String key, V value) {
return CompletableFuture.runAsync(() -> set(expireSeconds, key, value), getExecutor()).whenComplete(futureCompleteConsumer);
}
@Override @Override
public <T> CompletableFuture<Void> setAsync(int expireSeconds, String key, Convert convert, T value) { public <T> CompletableFuture<Void> setAsync(int expireSeconds, String key, Convert convert, T value) {
return CompletableFuture.runAsync(() -> set(expireSeconds, key, convert, value), getExecutor()).whenComplete(futureCompleteConsumer); return CompletableFuture.runAsync(() -> set(expireSeconds, key, convert, value), getExecutor()).whenComplete(futureCompleteConsumer);
@@ -984,21 +894,16 @@ public final class CacheMemorySource<V extends Object> extends AbstractService i
return CompletableFuture.supplyAsync(() -> remove(key), getExecutor()).whenComplete(futureCompleteConsumer); return CompletableFuture.supplyAsync(() -> remove(key), getExecutor()).whenComplete(futureCompleteConsumer);
} }
@Override
public Collection<V> getCollection(final String key) {
return (Collection<V>) get(key);
}
@Override @Override
public <T> Collection<T> getCollection(final String key, final Type componentType) { public <T> Collection<T> getCollection(final String key, final Type componentType) {
return (Collection<T>) get(key); return (Collection<T>) get(key, componentType);
} }
@Override @Override
public <T> Map<String, Collection<T>> getCollectionMap(final boolean set, final Type componentType, final String... keys) { public <T> Map<String, Collection<T>> getCollectionMap(final boolean set, final Type componentType, final String... keys) {
Map<String, Collection<T>> map = new HashMap<>(); Map<String, Collection<T>> map = new HashMap<>();
for (String key : keys) { for (String key : keys) {
Collection<T> s = (Collection<T>) get(key); Collection<T> s = (Collection<T>) get(key, componentType);
if (s != null) map.put(key, s); if (s != null) map.put(key, s);
} }
return map; return map;
@@ -1006,14 +911,14 @@ public final class CacheMemorySource<V extends Object> extends AbstractService i
@Override @Override
public Collection<String> getStringCollection(final String key) { public Collection<String> getStringCollection(final String key) {
return (Collection<String>) get(key); return (Collection<String>) get(key, String.class);
} }
@Override @Override
public Map<String, Collection<String>> getStringCollectionMap(final boolean set, final String... keys) { public Map<String, Collection<String>> getStringCollectionMap(final boolean set, final String... keys) {
Map<String, Collection<String>> map = new HashMap<>(); Map<String, Collection<String>> map = new HashMap<>();
for (String key : keys) { for (String key : keys) {
Collection<String> s = (Collection<String>) get(key); Collection<String> s = (Collection<String>) get(key, String.class);
if (s != null) map.put(key, s); if (s != null) map.put(key, s);
} }
return map; return map;
@@ -1023,7 +928,7 @@ public final class CacheMemorySource<V extends Object> extends AbstractService i
public Map<String, Long> getLongMap(final String... keys) { public Map<String, Long> getLongMap(final String... keys) {
Map<String, Long> map = new LinkedHashMap<>(); Map<String, Long> map = new LinkedHashMap<>();
for (String key : keys) { for (String key : keys) {
Number n = (Number) get(key); Number n = (Number) get(key, long.class);
map.put(key, n == null ? null : n.longValue()); map.put(key, n == null ? null : n.longValue());
} }
return map; return map;
@@ -1034,7 +939,7 @@ public final class CacheMemorySource<V extends Object> extends AbstractService i
Long[] rs = new Long[keys.length]; Long[] rs = new Long[keys.length];
int index = -1; int index = -1;
for (String key : keys) { for (String key : keys) {
Number n = (Number) get(key); Number n = (Number) get(key, long.class);
rs[++index] = n == null ? null : n.longValue(); rs[++index] = n == null ? null : n.longValue();
} }
return rs; return rs;
@@ -1054,7 +959,7 @@ public final class CacheMemorySource<V extends Object> extends AbstractService i
public Map<String, String> getStringMap(final String... keys) { public Map<String, String> getStringMap(final String... keys) {
Map<String, String> map = new LinkedHashMap<>(); Map<String, String> map = new LinkedHashMap<>();
for (String key : keys) { for (String key : keys) {
Object n = get(key); Object n = get(key, String.class);
map.put(key, n == null ? null : n.toString()); map.put(key, n == null ? null : n.toString());
} }
return map; return map;
@@ -1065,7 +970,7 @@ public final class CacheMemorySource<V extends Object> extends AbstractService i
String[] rs = new String[keys.length]; String[] rs = new String[keys.length];
int index = -1; int index = -1;
for (String key : keys) { for (String key : keys) {
Object n = get(key); Object n = get(key, String.class);
rs[++index] = n == null ? null : n.toString(); rs[++index] = n == null ? null : n.toString();
} }
return rs; return rs;
@@ -1085,7 +990,7 @@ public final class CacheMemorySource<V extends Object> extends AbstractService i
public <T> Map<String, T> getMap(final Type componentType, final String... keys) { public <T> Map<String, T> getMap(final Type componentType, final String... keys) {
Map<String, T> map = new LinkedHashMap<>(); Map<String, T> map = new LinkedHashMap<>();
for (String key : keys) { for (String key : keys) {
map.put(key, (T) get(key)); map.put(key, (T) get(key, componentType));
} }
return map; return map;
} }
@@ -1097,31 +1002,21 @@ public final class CacheMemorySource<V extends Object> extends AbstractService i
@Override @Override
public Collection<Long> getLongCollection(final String key) { public Collection<Long> getLongCollection(final String key) {
return (Collection<Long>) get(key); return (Collection<Long>) get(key, long.class);
} }
@Override @Override
public Map<String, Collection<Long>> getLongCollectionMap(final boolean set, final String... keys) { public Map<String, Collection<Long>> getLongCollectionMap(final boolean set, final String... keys) {
Map<String, Collection<Long>> map = new HashMap<>(); Map<String, Collection<Long>> map = new HashMap<>();
for (String key : keys) { for (String key : keys) {
Collection<Long> s = (Collection<Long>) get(key); Collection<Long> s = (Collection<Long>) get(key, long.class);
if (s != null) map.put(key, s); if (s != null) map.put(key, s);
} }
return map; return map;
} }
@Override @Override
public CompletableFuture<Collection<V>> getCollectionAsync(final String key) { public <T> CompletableFuture<Map<String, Collection<T>>> getCollectionMapAsync(boolean set, Type componentType, String... keys) {
return CompletableFuture.supplyAsync(() -> getCollection(key), getExecutor());
}
@Override
public CompletableFuture<Collection<V>> getCollectionAsync(final String key, final Type componentType) {
return CompletableFuture.supplyAsync(() -> getCollection(key, componentType), getExecutor());
}
@Override
public CompletableFuture<Map<String, Collection<V>>> getCollectionMapAsync(final boolean set, final Type componentType, final String... keys) {
return CompletableFuture.supplyAsync(() -> getCollectionMap(set, componentType, keys), getExecutor()); return CompletableFuture.supplyAsync(() -> getCollectionMap(set, componentType, keys), getExecutor());
} }
@@ -1145,9 +1040,14 @@ public final class CacheMemorySource<V extends Object> extends AbstractService i
return CompletableFuture.supplyAsync(() -> getLongCollectionMap(set, keys), getExecutor()); return CompletableFuture.supplyAsync(() -> getLongCollectionMap(set, keys), getExecutor());
} }
@Override
public <T> CompletableFuture<Collection<T>> getCollectionAsync(String key, Type componentType) {
return CompletableFuture.supplyAsync(() -> getCollection(key, componentType), getExecutor());
}
@Override @Override
public int getCollectionSize(final String key) { public int getCollectionSize(final String key) {
Collection<V> collection = (Collection<V>) get(key); Collection collection = (Collection) get(key, Object.class);
return collection == null ? 0 : collection.size(); return collection == null ? 0 : collection.size();
} }
@@ -1156,11 +1056,6 @@ public final class CacheMemorySource<V extends Object> extends AbstractService i
return CompletableFuture.supplyAsync(() -> getCollectionSize(key), getExecutor()); return CompletableFuture.supplyAsync(() -> getCollectionSize(key), getExecutor());
} }
@Override
public Collection<V> getCollectionAndRefresh(final String key, final int expireSeconds) {
return (Collection<V>) getAndRefresh(key, expireSeconds);
}
@Override @Override
public <T> Collection<T> getCollectionAndRefresh(final String key, final int expireSeconds, final Type componentType) { public <T> Collection<T> getCollectionAndRefresh(final String key, final int expireSeconds, final Type componentType) {
return (Collection<T>) getAndRefresh(key, expireSeconds, componentType); return (Collection<T>) getAndRefresh(key, expireSeconds, componentType);
@@ -1168,26 +1063,15 @@ public final class CacheMemorySource<V extends Object> extends AbstractService i
@Override @Override
public Collection<String> getStringCollectionAndRefresh(final String key, final int expireSeconds) { public Collection<String> getStringCollectionAndRefresh(final String key, final int expireSeconds) {
return (Collection<String>) getAndRefresh(key, expireSeconds); return (Collection<String>) getAndRefresh(key, expireSeconds, String.class);
}
@Override
public boolean existsSetItem(final String key, final V value) {
Collection<V> list = getCollection(key);
return list != null && list.contains(value);
} }
@Override @Override
public <T> boolean existsSetItem(final String key, final Type type, final T value) { public <T> boolean existsSetItem(final String key, final Type type, final T value) {
Collection list = getCollection(key); Collection list = getCollection(key, type);
return list != null && list.contains(value); return list != null && list.contains(value);
} }
@Override
public CompletableFuture<Boolean> existsSetItemAsync(final String key, final V value) {
return CompletableFuture.supplyAsync(() -> existsSetItem(key, value), getExecutor());
}
@Override @Override
public <T> CompletableFuture<Boolean> existsSetItemAsync(final String key, final Type type, final T value) { public <T> CompletableFuture<Boolean> existsSetItemAsync(final String key, final Type type, final T value) {
return CompletableFuture.supplyAsync(() -> existsSetItem(key, type, value), getExecutor()); return CompletableFuture.supplyAsync(() -> existsSetItem(key, type, value), getExecutor());
@@ -1216,13 +1100,8 @@ public final class CacheMemorySource<V extends Object> extends AbstractService i
} }
@Override @Override
public Collection<Long> getLongCollectionAndRefresh(final String key, final int expireSeconds) { public Collection<Long> getLongCollectionAndRefresh(String key, int expireSeconds) {
return (Collection<Long>) getAndRefresh(key, expireSeconds); return (Collection<Long>) getAndRefresh(key, expireSeconds, long.class);
}
@Override
public CompletableFuture<Collection<V>> getCollectionAndRefreshAsync(final String key, final int expireSeconds) {
return CompletableFuture.supplyAsync(() -> getCollectionAndRefresh(key, expireSeconds), getExecutor());
} }
@Override @Override
@@ -1254,11 +1133,6 @@ public final class CacheMemorySource<V extends Object> extends AbstractService i
} }
} }
@Override
public void appendListItem(String key, V value) {
appendListItem(CacheEntryType.OBJECT_LIST, key, value);
}
@Override @Override
public <T> void appendListItem(String key, Type componentType, T value) { public <T> void appendListItem(String key, Type componentType, T value) {
appendListItem(CacheEntryType.OBJECT_LIST, key, value); appendListItem(CacheEntryType.OBJECT_LIST, key, value);
@@ -1274,11 +1148,6 @@ public final class CacheMemorySource<V extends Object> extends AbstractService i
appendListItem(CacheEntryType.LONG_LIST, key, value); appendListItem(CacheEntryType.LONG_LIST, key, value);
} }
@Override
public CompletableFuture<Void> appendListItemAsync(final String key, final V value) {
return CompletableFuture.runAsync(() -> appendListItem(key, value), getExecutor()).whenComplete(futureCompleteConsumer);
}
@Override @Override
public <T> CompletableFuture<Void> appendListItemAsync(final String key, final Type componentType, final T value) { public <T> CompletableFuture<Void> appendListItemAsync(final String key, final Type componentType, final T value) {
return CompletableFuture.runAsync(() -> appendListItem(key, componentType, value), getExecutor()).whenComplete(futureCompleteConsumer); return CompletableFuture.runAsync(() -> appendListItem(key, componentType, value), getExecutor()).whenComplete(futureCompleteConsumer);
@@ -1294,15 +1163,6 @@ public final class CacheMemorySource<V extends Object> extends AbstractService i
return CompletableFuture.runAsync(() -> appendLongListItem(key, value), getExecutor()).whenComplete(futureCompleteConsumer); return CompletableFuture.runAsync(() -> appendLongListItem(key, value), getExecutor()).whenComplete(futureCompleteConsumer);
} }
@Override
public int removeListItem(String key, V value) {
if (key == null) return 0;
CacheEntry entry = container.get(key);
if (entry == null || entry.listValue == null) return 0;
return entry.listValue.remove(value) ? 1 : 0;
}
@Override @Override
public <T> int removeListItem(String key, final Type componentType, T value) { public <T> int removeListItem(String key, final Type componentType, T value) {
if (key == null) return 0; if (key == null) return 0;
@@ -1327,11 +1187,6 @@ public final class CacheMemorySource<V extends Object> extends AbstractService i
return entry.listValue.remove(value) ? 1 : 0; return entry.listValue.remove(value) ? 1 : 0;
} }
@Override
public CompletableFuture<Integer> removeListItemAsync(final String key, final V value) {
return CompletableFuture.supplyAsync(() -> removeListItem(key, value), getExecutor()).whenComplete(futureCompleteConsumer);
}
@Override @Override
public <T> CompletableFuture<Integer> removeListItemAsync(final String key, final Type componentType, T value) { public <T> CompletableFuture<Integer> removeListItemAsync(final String key, final Type componentType, T value) {
return CompletableFuture.supplyAsync(() -> removeListItem(key, componentType, value), getExecutor()).whenComplete(futureCompleteConsumer); return CompletableFuture.supplyAsync(() -> removeListItem(key, componentType, value), getExecutor()).whenComplete(futureCompleteConsumer);
@@ -1420,11 +1275,6 @@ public final class CacheMemorySource<V extends Object> extends AbstractService i
} }
} }
@Override
public void appendSetItem(String key, V value) {
appendSetItem(CacheEntryType.OBJECT_SET, key, value);
}
@Override @Override
public <T> void appendSetItem(String key, final Type componentType, T value) { public <T> void appendSetItem(String key, final Type componentType, T value) {
appendSetItem(CacheEntryType.OBJECT_SET, key, value); appendSetItem(CacheEntryType.OBJECT_SET, key, value);
@@ -1440,11 +1290,6 @@ public final class CacheMemorySource<V extends Object> extends AbstractService i
appendSetItem(CacheEntryType.OBJECT_SET, key, value); appendSetItem(CacheEntryType.OBJECT_SET, key, value);
} }
@Override
public CompletableFuture<Void> appendSetItemAsync(final String key, final V value) {
return CompletableFuture.runAsync(() -> appendSetItem(key, value), getExecutor()).whenComplete(futureCompleteConsumer);
}
@Override @Override
public <T> CompletableFuture<Void> appendSetItemAsync(final String key, final Type componentType, T value) { public <T> CompletableFuture<Void> appendSetItemAsync(final String key, final Type componentType, T value) {
return CompletableFuture.runAsync(() -> appendSetItem(key, componentType, value), getExecutor()).whenComplete(futureCompleteConsumer); return CompletableFuture.runAsync(() -> appendSetItem(key, componentType, value), getExecutor()).whenComplete(futureCompleteConsumer);
@@ -1460,14 +1305,6 @@ public final class CacheMemorySource<V extends Object> extends AbstractService i
return CompletableFuture.runAsync(() -> appendLongSetItem(key, value), getExecutor()).whenComplete(futureCompleteConsumer); return CompletableFuture.runAsync(() -> appendLongSetItem(key, value), getExecutor()).whenComplete(futureCompleteConsumer);
} }
@Override
public int removeSetItem(String key, V value) {
if (key == null) return 0;
CacheEntry entry = container.get(key);
if (entry == null || entry.csetValue == null) return 0;
return entry.csetValue.remove(value) ? 1 : 0;
}
@Override @Override
public <T> int removeSetItem(String key, Type type, T value) { public <T> int removeSetItem(String key, Type type, T value) {
if (key == null) return 0; if (key == null) return 0;
@@ -1492,11 +1329,6 @@ public final class CacheMemorySource<V extends Object> extends AbstractService i
return entry.csetValue.remove(value) ? 1 : 0; return entry.csetValue.remove(value) ? 1 : 0;
} }
@Override
public CompletableFuture<Integer> removeSetItemAsync(final String key, final V value) {
return CompletableFuture.supplyAsync(() -> removeSetItem(key, value), getExecutor()).whenComplete(futureCompleteConsumer);
}
@Override @Override
public <T> CompletableFuture<Integer> removeSetItemAsync(final String key, final Type componentType, final T value) { public <T> CompletableFuture<Integer> removeSetItemAsync(final String key, final Type componentType, final T value) {
return CompletableFuture.supplyAsync(() -> removeSetItem(key, componentType, value), getExecutor()).whenComplete(futureCompleteConsumer); return CompletableFuture.supplyAsync(() -> removeSetItem(key, componentType, value), getExecutor()).whenComplete(futureCompleteConsumer);
@@ -1665,4 +1497,5 @@ public final class CacheMemorySource<V extends Object> extends AbstractService i
public CompletableFuture<List<Long>> spopLongSetItemAsync(String key, int count) { public CompletableFuture<List<Long>> spopLongSetItemAsync(String key, int count) {
return CompletableFuture.supplyAsync(() -> spopLongSetItem(key, count), getExecutor()).whenComplete(futureCompleteConsumer); return CompletableFuture.supplyAsync(() -> spopLongSetItem(key, count), getExecutor()).whenComplete(futureCompleteConsumer);
} }
} }

View File

@@ -9,7 +9,6 @@ import java.io.Serializable;
import java.lang.reflect.Type; import java.lang.reflect.Type;
import java.util.*; import java.util.*;
import java.util.concurrent.*; import java.util.concurrent.*;
import java.util.function.Function;
import org.redkale.convert.*; import org.redkale.convert.*;
import org.redkale.convert.json.JsonFactory; import org.redkale.convert.json.JsonFactory;
import org.redkale.util.*; import org.redkale.util.*;
@@ -21,22 +20,16 @@ import org.redkale.util.*;
* Long统一用setLong、getLong、incr等系列方法。<br> * Long统一用setLong、getLong、incr等系列方法。<br>
* 其他则供自定义数据类型使用。 * 其他则供自定义数据类型使用。
* *
* @param <V> value的类型 * param V value的类型 移除 @2.4.0
* <p> * <p>
* 详情见: https://redkale.org * 详情见: https://redkale.org
* *
* @author zhangjx * @author zhangjx
*/ */
public interface CacheSource<V extends Object> { public interface CacheSource {
public String getType(); public String getType();
@Deprecated
public void initValueType(Type valueType);
@Deprecated
public void initTransient(boolean flag);
//ServiceLoader时判断配置是否符合当前实现类 //ServiceLoader时判断配置是否符合当前实现类
public boolean match(AnyValue config); public boolean match(AnyValue config);
@@ -46,36 +39,10 @@ public interface CacheSource<V extends Object> {
public boolean exists(final String key); public boolean exists(final String key);
@Deprecated
public V get(final String key);
public <T> T get(final String key, final Type type); public <T> T get(final String key, final Type type);
@Deprecated
default V getIfAbsent(final String key, Function<String, ? extends V> mappingFunction) {
V rs = get(key);
if (rs == null) {
rs = mappingFunction.apply(key);
if (rs != null) set(key, rs);
}
return rs;
}
@Deprecated
public V getAndRefresh(final String key, final int expireSeconds);
public <T> T getAndRefresh(final String key, final int expireSeconds, final Type type); public <T> T getAndRefresh(final String key, final int expireSeconds, final Type type);
@Deprecated
default V getAndRefreshIfAbsent(final String key, final int expireSeconds, Function<String, ? extends V> mappingFunction) {
V rs = getAndRefresh(key, expireSeconds);
if (rs == null) {
rs = mappingFunction.apply(key);
if (rs != null) set(expireSeconds, key, rs);
}
return rs;
}
//----------- hxxx -------------- //----------- hxxx --------------
public int hremove(final String key, String... fields); public int hremove(final String key, String... fields);
@@ -120,18 +87,12 @@ public interface CacheSource<V extends Object> {
public void refresh(final String key, final int expireSeconds); public void refresh(final String key, final int expireSeconds);
@Deprecated
public void set(final String key, final V value);
public <T> void set(final String key, final Convert convert, final T value); public <T> void set(final String key, final Convert convert, final T value);
public <T> void set(final String key, final Type type, final T value); public <T> void set(final String key, final Type type, final T value);
public <T> void set(final String key, final Convert convert, final Type type, final T value); public <T> void set(final String key, final Convert convert, final Type type, final T value);
@Deprecated
public void set(final int expireSeconds, final String key, final V value);
public <T> void set(final int expireSeconds, final String key, final Convert convert, final T value); public <T> void set(final int expireSeconds, final String key, final Convert convert, final T value);
public <T> void set(final int expireSeconds, final String key, final Type type, final T value); public <T> void set(final int expireSeconds, final String key, final Type type, final T value);
@@ -152,35 +113,14 @@ public interface CacheSource<V extends Object> {
public <T> Map<String, T> getMap(final Type componentType, final String... keys); public <T> Map<String, T> getMap(final Type componentType, final String... keys);
@Deprecated
public Collection<V> getCollection(final String key);
public <T> Collection<T> getCollection(final String key, final Type componentType); public <T> Collection<T> getCollection(final String key, final Type componentType);
public <T> Map<String, Collection<T>> getCollectionMap(final boolean set, final Type componentType, final String... keys); public <T> Map<String, Collection<T>> getCollectionMap(final boolean set, final Type componentType, final String... keys);
public int getCollectionSize(final String key); public int getCollectionSize(final String key);
@Deprecated
public Collection<V> getCollectionAndRefresh(final String key, final int expireSeconds);
public <T> Collection<T> getCollectionAndRefresh(final String key, final int expireSeconds, final Type componentType); public <T> Collection<T> getCollectionAndRefresh(final String key, final int expireSeconds, final Type componentType);
@Deprecated
public void appendListItem(final String key, final V value);
@Deprecated
public int removeListItem(final String key, final V value);
@Deprecated
public boolean existsSetItem(final String key, final V value);
@Deprecated
public void appendSetItem(final String key, final V value);
@Deprecated
public int removeSetItem(final String key, final V value);
public <T> void appendListItem(final String key, final Type componentType, final T value); public <T> void appendListItem(final String key, final Type componentType, final T value);
public <T> int removeListItem(final String key, final Type componentType, final T value); public <T> int removeListItem(final String key, final Type componentType, final T value);
@@ -288,54 +228,14 @@ public interface CacheSource<V extends Object> {
public <T> CompletableFuture<T> getAndRefreshAsync(final String key, final int expireSeconds, final Type type); public <T> CompletableFuture<T> getAndRefreshAsync(final String key, final int expireSeconds, final Type type);
@Deprecated
public CompletableFuture<V> getAsync(final String key);
@Deprecated
default CompletableFuture<V> getIfAbsentAsync(final String key, Function<String, ? extends V> mappingFunction) {
return getAsync(key).thenCompose((V rs) -> {
if (rs == null) {
rs = mappingFunction.apply(key);
if (rs != null) {
final V v = rs;
return setAsync(key, rs).thenApply((k) -> v);
}
}
return CompletableFuture.completedFuture(rs);
});
}
@Deprecated
public CompletableFuture<V> getAndRefreshAsync(final String key, final int expireSeconds);
@Deprecated
default CompletableFuture<V> getAndRefreshIfAbsentAsync(final String key, final int expireSeconds, Function<String, ? extends V> mappingFunction) {
return getAndRefreshAsync(key, expireSeconds).thenCompose((V rs) -> {
if (rs == null) {
rs = mappingFunction.apply(key);
if (rs != null) {
final V v = rs;
return setAsync(expireSeconds, key, rs).thenApply((k) -> v);
}
}
return CompletableFuture.completedFuture(rs);
});
}
public CompletableFuture<Void> refreshAsync(final String key, final int expireSeconds); public CompletableFuture<Void> refreshAsync(final String key, final int expireSeconds);
@Deprecated
public CompletableFuture<Void> setAsync(final String key, final V value);
public <T> CompletableFuture<Void> setAsync(final String key, final Convert convert, final T value); public <T> CompletableFuture<Void> setAsync(final String key, final Convert convert, final T value);
public <T> CompletableFuture<Void> setAsync(final String key, final Type type, final T value); public <T> CompletableFuture<Void> setAsync(final String key, final Type type, final T value);
public <T> CompletableFuture<Void> setAsync(final String key, final Convert convert, final Type type, final T value); public <T> CompletableFuture<Void> setAsync(final String key, final Convert convert, final Type type, final T value);
@Deprecated
public CompletableFuture<Void> setAsync(final int expireSeconds, final String key, final V value);
public <T> CompletableFuture<Void> setAsync(final int expireSeconds, final String key, final Convert convert, final T value); public <T> CompletableFuture<Void> setAsync(final int expireSeconds, final String key, final Convert convert, final T value);
public <T> CompletableFuture<Void> setAsync(final int expireSeconds, final String key, final Type type, final T value); public <T> CompletableFuture<Void> setAsync(final int expireSeconds, final String key, final Type type, final T value);
@@ -398,37 +298,18 @@ public interface CacheSource<V extends Object> {
public <T> CompletableFuture<Map<String, T>> getMapAsync(final Type componentType, final String... keys); public <T> CompletableFuture<Map<String, T>> getMapAsync(final Type componentType, final String... keys);
@Deprecated
public CompletableFuture<Collection<V>> getCollectionAsync(final String key);
public <T> CompletableFuture<Collection<T>> getCollectionAsync(final String key, final Type componentType); public <T> CompletableFuture<Collection<T>> getCollectionAsync(final String key, final Type componentType);
public <T> CompletableFuture<Map<String, Collection<T>>> getCollectionMapAsync(final boolean set, final Type componentType, final String... keys); public <T> CompletableFuture<Map<String, Collection<T>>> getCollectionMapAsync(final boolean set, final Type componentType, final String... keys);
public CompletableFuture<Integer> getCollectionSizeAsync(final String key); public CompletableFuture<Integer> getCollectionSizeAsync(final String key);
@Deprecated
public CompletableFuture<Collection<V>> getCollectionAndRefreshAsync(final String key, final int expireSeconds);
public <T> CompletableFuture<Collection<T>> getCollectionAndRefreshAsync(final String key, final int expireSeconds, final Type componentType); public <T> CompletableFuture<Collection<T>> getCollectionAndRefreshAsync(final String key, final int expireSeconds, final Type componentType);
@Deprecated
public CompletableFuture<Void> appendListItemAsync(final String key, final V value);
public <T> CompletableFuture<T> spopSetItemAsync(final String key, final Type componentType); public <T> CompletableFuture<T> spopSetItemAsync(final String key, final Type componentType);
public <T> CompletableFuture<List<T>> spopSetItemAsync(final String key, final int count, final Type componentType); public <T> CompletableFuture<List<T>> spopSetItemAsync(final String key, final int count, final Type componentType);
@Deprecated
public CompletableFuture<Integer> removeListItemAsync(final String key, final V value);
@Deprecated
public CompletableFuture<Boolean> existsSetItemAsync(final String key, final V value);
public CompletableFuture<Void> appendSetItemAsync(final String key, final V value);
public CompletableFuture<Integer> removeSetItemAsync(final String key, final V value);
public <T> CompletableFuture<Void> appendListItemAsync(final String key, final Type componentType, final T value); public <T> CompletableFuture<Void> appendListItemAsync(final String key, final Type componentType, final T value);
public <T> CompletableFuture<Integer> removeListItemAsync(final String key, final Type componentType, final T value); public <T> CompletableFuture<Integer> removeListItemAsync(final String key, final Type componentType, final T value);

View File

@@ -0,0 +1,25 @@
/*
* 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.source;
import org.redkale.util.AnyValue;
/**
* 自定义的CacheSource加载器
*
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
* @since 2.4.0
*/
public interface CacheSourceLoader {
public boolean match(AnyValue config);
public Class<? extends CacheSource> sourceClass();
}

View File

@@ -74,6 +74,19 @@ public class ColumnValue {
return new ColumnValue(column, INC, value); return new ColumnValue(column, INC, value);
} }
/**
* 返回 {column} = {column} + 1 操作
*
* @param column 字段名
*
* @return ColumnValue
*
* @since 2.4.0
*/
public static ColumnValue inc(String column) {
return new ColumnValue(column, INC, 1);
}
/** /**
* 返回 {column} = {column} - {value} 操作 * 返回 {column} = {column} - {value} 操作
* *
@@ -85,7 +98,20 @@ public class ColumnValue {
public static ColumnValue dec(String column, Serializable value) { public static ColumnValue dec(String column, Serializable value) {
return new ColumnValue(column, DEC, value); return new ColumnValue(column, DEC, value);
} }
/**
* 返回 {column} = {column} - 1 操作
*
* @param column 字段名
*
* @return ColumnValue
*
* @since 2.4.0
*/
public static ColumnValue dec(String column) {
return new ColumnValue(column, DEC, 1);
}
/** /**
* 返回 {column} = {column} * {value} 操作 * 返回 {column} = {column} * {value} 操作
* *
@@ -109,7 +135,7 @@ public class ColumnValue {
public static ColumnValue div(String column, Serializable value) { public static ColumnValue div(String column, Serializable value) {
return new ColumnValue(column, DIV, value); return new ColumnValue(column, DIV, value);
} }
/** /**
* 返回 {column} = {column} % {value} 操作 * 返回 {column} = {column} % {value} 操作
* *
@@ -118,11 +144,10 @@ public class ColumnValue {
* *
* @return ColumnValue * @return ColumnValue
*/ */
//不常用防止开发者容易在mov时误输入mod //不常用防止开发者容易在mov时误输入mod
// public static ColumnValue mod(String column, Serializable value) { // public static ColumnValue mod(String column, Serializable value) {
// return new ColumnValue(column, MOD, value); // return new ColumnValue(column, MOD, value);
// } // }
/** /**
* 返回 {column} = {column} &#38; {value} 操作 * 返回 {column} = {column} &#38; {value} 操作
* *

View File

@@ -13,7 +13,7 @@ import java.util.concurrent.*;
import java.util.concurrent.atomic.*; import java.util.concurrent.atomic.*;
import java.util.function.*; import java.util.function.*;
import java.util.logging.Level; import java.util.logging.Level;
import org.redkale.net.AsyncGroup; import org.redkale.net.*;
import org.redkale.service.Local; import org.redkale.service.Local;
import org.redkale.util.*; import org.redkale.util.*;
@@ -29,7 +29,7 @@ import org.redkale.util.*;
@AutoLoad(false) @AutoLoad(false)
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@ResourceType(DataSource.class) @ResourceType(DataSource.class)
public class DataJdbcSource extends DataSqlSource<Connection> { public class DataJdbcSource extends DataSqlSource {
public DataJdbcSource(String unitName, URL persistxml, Properties readprop, Properties writeprop) { public DataJdbcSource(String unitName, URL persistxml, Properties readprop, Properties writeprop) {
super(unitName, persistxml, readprop, writeprop); super(unitName, persistxml, readprop, writeprop);
@@ -46,7 +46,7 @@ public class DataJdbcSource extends DataSqlSource<Connection> {
} }
@Override @Override
protected PoolSource<Connection> createPoolSource(DataSource source, AsyncGroup asyncGroup, String rwtype, ArrayBlockingQueue queue, Semaphore semaphore, Properties prop) { protected PoolSource createPoolSource(DataSource source, AsyncGroup asyncGroup, String rwtype, ArrayBlockingQueue queue, Semaphore semaphore, Properties prop) {
return new PoolJdbcSource(this.name, this.persistxml, rwtype, queue, semaphore, prop, this.logger); return new PoolJdbcSource(this.name, this.persistxml, rwtype, queue, semaphore, prop, this.logger);
} }
@@ -256,7 +256,7 @@ public class DataJdbcSource extends DataSqlSource<Connection> {
} }
@Override @Override
protected <T> CompletableFuture<Integer> updateDB(EntityInfo<T> info, T... entitys) { protected <T> CompletableFuture<Integer> updateDB(EntityInfo<T> info, ChannelContext context, T... entitys) {
Connection conn = null; Connection conn = null;
try { try {
conn = writePool.poll(); conn = writePool.poll();
@@ -471,7 +471,7 @@ public class DataJdbcSource extends DataSqlSource<Connection> {
} }
@Override @Override
protected <T> CompletableFuture<T> findDB(EntityInfo<T> info, String sql, boolean onlypk, SelectColumn selects) { protected <T> CompletableFuture<T> findDB(EntityInfo<T> info, ChannelContext context, String sql, boolean onlypk, SelectColumn selects) {
Connection conn = null; Connection conn = null;
try { try {
conn = readPool.poll(); conn = readPool.poll();

View File

@@ -11,7 +11,7 @@ import java.sql.ResultSet;
import java.util.*; import java.util.*;
import java.util.concurrent.*; import java.util.concurrent.*;
import java.util.function.*; import java.util.function.*;
import org.redkale.net.AsyncGroup; import org.redkale.net.*;
import org.redkale.service.Local; import org.redkale.service.Local;
import org.redkale.util.*; import org.redkale.util.*;
@@ -33,7 +33,7 @@ import org.redkale.util.*;
@AutoLoad(false) @AutoLoad(false)
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@ResourceType(DataSource.class) @ResourceType(DataSource.class)
public class DataMemorySource extends DataSqlSource<Void> { public class DataMemorySource extends DataSqlSource {
public DataMemorySource(String unitName, URL persistxml, Properties readprop, Properties writeprop) { public DataMemorySource(String unitName, URL persistxml, Properties readprop, Properties writeprop) {
super(unitName, persistxml, readprop, writeprop); super(unitName, persistxml, readprop, writeprop);
@@ -80,7 +80,7 @@ public class DataMemorySource extends DataSqlSource<Void> {
} }
@Override @Override
protected PoolSource<Void> createPoolSource(DataSource source, AsyncGroup asyncGroup, String rwtype, ArrayBlockingQueue queue, Semaphore semaphore, Properties prop) { protected PoolSource createPoolSource(DataSource source, AsyncGroup asyncGroup, String rwtype, ArrayBlockingQueue queue, Semaphore semaphore, Properties prop) {
return null; return null;
} }
@@ -105,7 +105,7 @@ public class DataMemorySource extends DataSqlSource<Void> {
} }
@Override @Override
protected <T> CompletableFuture<Integer> updateDB(EntityInfo<T> info, T... entitys) { protected <T> CompletableFuture<Integer> updateDB(EntityInfo<T> info, ChannelContext context, T... entitys) {
return CompletableFuture.completedFuture(0); return CompletableFuture.completedFuture(0);
} }
@@ -135,7 +135,7 @@ public class DataMemorySource extends DataSqlSource<Void> {
} }
@Override @Override
protected <T> CompletableFuture<T> findDB(EntityInfo<T> info, String sql, boolean onlypk, SelectColumn selects) { protected <T> CompletableFuture<T> findDB(EntityInfo<T> info, ChannelContext context, String sql, boolean onlypk, SelectColumn selects) {
return CompletableFuture.completedFuture(null); return CompletableFuture.completedFuture(null);
} }

File diff suppressed because it is too large Load Diff

View File

@@ -5,6 +5,8 @@
*/ */
package org.redkale.source; package org.redkale.source;
import org.redkale.util.AnyValue;
/** /**
* 自定义的DataSource加载器 * 自定义的DataSource加载器
* *
@@ -13,10 +15,11 @@ package org.redkale.source;
* 详情见: https://redkale.org * 详情见: https://redkale.org
* *
* @author zhangjx * @author zhangjx
* @since 2.4.0
*/ */
public interface SourceLoader { public interface DataSourceLoader {
public String dbtype(); public boolean match(AnyValue config);
public Class<? extends DataSource> dataSourceClass(); public Class<? extends DataSource> sourceClass();
} }

View File

@@ -54,6 +54,9 @@ public final class DataSources {
public static final String JDBC_SOURCE = "javax.persistence.jdbc.source"; public static final String JDBC_SOURCE = "javax.persistence.jdbc.source";
//@since 2.4.0 for SearchSource default value: true
public static final String JDBC_AUTO_MAPPING = "javax.persistence.jdbc.auto-mapping";
private DataSources() { private DataSources() {
} }
@@ -88,26 +91,33 @@ public final class DataSources {
final String url = readprop.getProperty(JDBC_URL); final String url = readprop.getProperty(JDBC_URL);
String dbtype = null; String dbtype = null;
{ {
/* jdbc:mysql:// jdbc:microsoft:sqlserver:// 取://之前的到最后一个:之间的字符串 */ if (url.startsWith("http://") || url.startsWith("https://")) { //elasticsearch or opensearch
int pos = url.indexOf("://"); dbtype = "search";
if (pos > 0) { } else {
String url0 = url.substring(0, pos); /* jdbc:mysql:// jdbc:microsoft:sqlserver:// 取://之前的到最后一个:之间的字符串 */
pos = url0.lastIndexOf(':'); int pos = url.indexOf("://");
if (pos > 0) dbtype = url0.substring(pos + 1); if (pos > 0) {
} else { //jdbc:oracle:thin:@localhost:1521 String url0 = url.substring(0, pos);
String url0 = url.substring(url.indexOf(":") + 1); pos = url0.lastIndexOf(':');
pos = url0.indexOf(':'); if (pos > 0) dbtype = url0.substring(pos + 1);
if (pos > 0) dbtype = url0.substring(0, pos); } else { //jdbc:oracle:thin:@localhost:1521
String url0 = url.substring(url.indexOf(":") + 1);
pos = url0.indexOf(':');
if (pos > 0) dbtype = url0.substring(0, pos);
}
} }
} }
if (dbtype == null) throw new RuntimeException("not found datasource implements class, url=" + url); if (dbtype == null) throw new RuntimeException("not found datasource implements class, url=" + url);
Iterator<SourceLoader> it = ServiceLoader.load(SourceLoader.class).iterator(); Iterator<DataSourceLoader> it = ServiceLoader.load(DataSourceLoader.class).iterator();
Class dsClass = null; Class dsClass = null;
final AnyValue.DefaultAnyValue lc = new AnyValue.DefaultAnyValue();
readprop.forEach((k, v) -> lc.addValue(k.toString(), v.toString()));
lc.setValue("dbtype", dbtype);
while (it.hasNext()) { while (it.hasNext()) {
SourceLoader loader = it.next(); DataSourceLoader loader = it.next();
if (dbtype.equalsIgnoreCase(loader.dbtype())) { if (loader != null && loader.match(lc)) {
dsClass = loader.dataSourceClass(); dsClass = loader.sourceClass();
if (dsClass != null) break; if (dsClass != null) break;
} }
} }

View File

@@ -19,6 +19,7 @@ import org.redkale.service.*;
import static org.redkale.source.DataSources.*; import static org.redkale.source.DataSources.*;
import org.redkale.util.*; import org.redkale.util.*;
import static org.redkale.boot.Application.RESNAME_APP_GROUP; import static org.redkale.boot.Application.RESNAME_APP_GROUP;
import org.redkale.net.*;
/** /**
* DataSource的SQL抽象实现类 <br> * DataSource的SQL抽象实现类 <br>
@@ -28,13 +29,12 @@ import static org.redkale.boot.Application.RESNAME_APP_GROUP;
* 详情见: https://redkale.org * 详情见: https://redkale.org
* *
* @author zhangjx * @author zhangjx
* @param <DBChannel> 数据库连接
*/ */
@Local @Local
@AutoLoad(false) @AutoLoad(false)
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@ResourceType(DataSource.class) @ResourceType(DataSource.class)
public abstract class DataSqlSource<DBChannel> extends AbstractService implements DataSource, Function<Class, EntityInfo>, AutoCloseable, Resourcable { public abstract class DataSqlSource extends AbstractService implements DataSource, Function<Class, EntityInfo>, AutoCloseable, Resourcable {
protected static final Flipper FLIPPER_ONE = new Flipper(1); protected static final Flipper FLIPPER_ONE = new Flipper(1);
@@ -50,9 +50,9 @@ public abstract class DataSqlSource<DBChannel> extends AbstractService implement
protected boolean cacheForbidden; protected boolean cacheForbidden;
protected PoolSource<DBChannel> readPool; protected PoolSource readPool;
protected PoolSource<DBChannel> writePool; protected PoolSource writePool;
@Resource(name = RESNAME_APP_GROUP) @Resource(name = RESNAME_APP_GROUP)
protected AsyncGroup asyncGroup; protected AsyncGroup asyncGroup;
@@ -82,7 +82,7 @@ public abstract class DataSqlSource<DBChannel> extends AbstractService implement
int maxconns = Math.max(8, Integer.decode(readprop.getProperty(JDBC_CONNECTIONS_LIMIT, "" + Runtime.getRuntime().availableProcessors() * 32))); int maxconns = Math.max(8, Integer.decode(readprop.getProperty(JDBC_CONNECTIONS_LIMIT, "" + Runtime.getRuntime().availableProcessors() * 32)));
if (readprop != writeprop) maxconns = 0; if (readprop != writeprop) maxconns = 0;
this.cacheForbidden = "NONE".equalsIgnoreCase(readprop.getProperty(JDBC_CACHE_MODE)); this.cacheForbidden = "NONE".equalsIgnoreCase(readprop.getProperty(JDBC_CACHE_MODE));
ArrayBlockingQueue<DBChannel> queue = maxconns > 0 ? new ArrayBlockingQueue(maxconns) : null; ArrayBlockingQueue queue = maxconns > 0 ? new ArrayBlockingQueue(maxconns) : null;
Semaphore semaphore = maxconns > 0 ? new Semaphore(maxconns) : null; Semaphore semaphore = maxconns > 0 ? new Semaphore(maxconns) : null;
this.readPool = createPoolSource(this, this.asyncGroup, "read", queue, semaphore, readprop); this.readPool = createPoolSource(this, this.asyncGroup, "read", queue, semaphore, readprop);
this.writePool = createPoolSource(this, this.asyncGroup, "write", queue, semaphore, writeprop); this.writePool = createPoolSource(this, this.asyncGroup, "write", queue, semaphore, writeprop);
@@ -131,7 +131,7 @@ public abstract class DataSqlSource<DBChannel> extends AbstractService implement
protected abstract String prepareParamSign(int index); protected abstract String prepareParamSign(int index);
//创建连接池 //创建连接池
protected abstract PoolSource<DBChannel> createPoolSource(DataSource source, AsyncGroup asyncGroup, String rwtype, ArrayBlockingQueue queue, Semaphore semaphore, Properties prop); protected abstract PoolSource createPoolSource(DataSource source, AsyncGroup asyncGroup, String rwtype, ArrayBlockingQueue queue, Semaphore semaphore, Properties prop);
//插入纪录 //插入纪录
protected abstract <T> CompletableFuture<Integer> insertDB(final EntityInfo<T> info, T... entitys); protected abstract <T> CompletableFuture<Integer> insertDB(final EntityInfo<T> info, T... entitys);
@@ -146,7 +146,7 @@ public abstract class DataSqlSource<DBChannel> extends AbstractService implement
protected abstract <T> CompletableFuture<Integer> dropTableDB(final EntityInfo<T> info, final String table, final String sql); protected abstract <T> CompletableFuture<Integer> dropTableDB(final EntityInfo<T> info, final String table, final String sql);
//更新纪录 //更新纪录
protected abstract <T> CompletableFuture<Integer> updateDB(final EntityInfo<T> info, T... entitys); protected abstract <T> CompletableFuture<Integer> updateDB(final EntityInfo<T> info, final ChannelContext context, T... entitys);
//更新纪录 //更新纪录
protected abstract <T> CompletableFuture<Integer> updateDB(final EntityInfo<T> info, Flipper flipper, final String sql, final boolean prepared, Object... params); protected abstract <T> CompletableFuture<Integer> updateDB(final EntityInfo<T> info, Flipper flipper, final String sql, final boolean prepared, Object... params);
@@ -164,7 +164,7 @@ public abstract class DataSqlSource<DBChannel> extends AbstractService implement
protected abstract <T, K extends Serializable, N extends Number> CompletableFuture<Map<K[], N[]>> queryColumnMapDB(final EntityInfo<T> info, final String sql, final ColumnNode[] funcNodes, final String[] groupByColumns); protected abstract <T, K extends Serializable, N extends Number> CompletableFuture<Map<K[], N[]>> queryColumnMapDB(final EntityInfo<T> info, final String sql, final ColumnNode[] funcNodes, final String[] groupByColumns);
//查询单条记录 //查询单条记录
protected abstract <T> CompletableFuture<T> findDB(final EntityInfo<T> info, final String sql, final boolean onlypk, final SelectColumn selects); protected abstract <T> CompletableFuture<T> findDB(final EntityInfo<T> info, final ChannelContext context, final String sql, final boolean onlypk, final SelectColumn selects);
//查询单条记录的单个字段 //查询单条记录的单个字段
protected abstract <T> CompletableFuture<Serializable> findColumnDB(final EntityInfo<T> info, final String sql, final boolean onlypk, final String column, final Serializable defValue); protected abstract <T> CompletableFuture<Serializable> findColumnDB(final EntityInfo<T> info, final String sql, final boolean onlypk, final String column, final Serializable defValue);
@@ -222,12 +222,12 @@ public abstract class DataSqlSource<DBChannel> extends AbstractService implement
} }
@Local @Local
public PoolSource<DBChannel> getReadPoolSource() { public PoolSource getReadPoolSource() {
return readPool; return readPool;
} }
@Local @Local
public PoolSource<DBChannel> getWritePoolSource() { public PoolSource getWritePoolSource() {
return writePool; return writePool;
} }
@@ -701,7 +701,7 @@ public abstract class DataSqlSource<DBChannel> extends AbstractService implement
final Class<T> clazz = (Class<T>) entitys[0].getClass(); final Class<T> clazz = (Class<T>) entitys[0].getClass();
final EntityInfo<T> info = loadEntityInfo(clazz); final EntityInfo<T> info = loadEntityInfo(clazz);
if (isOnlyCache(info)) return updateCache(info, -1, entitys); if (isOnlyCache(info)) return updateCache(info, -1, entitys);
return updateDB(info, entitys).whenComplete((rs, t) -> { return updateDB(info, null, entitys).whenComplete((rs, t) -> {
if (t != null) { if (t != null) {
futureCompleteConsumer.accept(rs, t); futureCompleteConsumer.accept(rs, t);
} else { } else {
@@ -712,6 +712,11 @@ public abstract class DataSqlSource<DBChannel> extends AbstractService implement
@Override @Override
public <T> CompletableFuture<Integer> updateAsync(final T... entitys) { public <T> CompletableFuture<Integer> updateAsync(final T... entitys) {
return updateAsync((ChannelContext) null, entitys);
}
@Override
public <T> CompletableFuture<Integer> updateAsync(final ChannelContext context, final T... entitys) {
if (entitys.length == 0) return CompletableFuture.completedFuture(-1); if (entitys.length == 0) return CompletableFuture.completedFuture(-1);
CompletableFuture future = checkEntity("update", true, entitys); CompletableFuture future = checkEntity("update", true, entitys);
if (future != null) return future; if (future != null) return future;
@@ -720,14 +725,14 @@ public abstract class DataSqlSource<DBChannel> extends AbstractService implement
if (isOnlyCache(info)) { if (isOnlyCache(info)) {
return CompletableFuture.completedFuture(updateCache(info, -1, entitys)); return CompletableFuture.completedFuture(updateCache(info, -1, entitys));
} }
if (isAsync()) return updateDB(info, entitys).whenComplete((rs, t) -> { if (isAsync()) return updateDB(info, context, entitys).whenComplete((rs, t) -> {
if (t != null) { if (t != null) {
futureCompleteConsumer.accept(rs, t); futureCompleteConsumer.accept(rs, t);
} else { } else {
updateCache(info, rs, entitys); updateCache(info, rs, entitys);
} }
}); });
return CompletableFuture.supplyAsync(() -> updateDB(info, entitys).join(), getExecutor()).whenComplete((rs, t) -> { return CompletableFuture.supplyAsync(() -> updateDB(info, context, entitys).join(), getExecutor()).whenComplete((rs, t) -> {
if (t != null) { if (t != null) {
futureCompleteConsumer.accept(rs, t); futureCompleteConsumer.accept(rs, t);
} else { } else {
@@ -1616,6 +1621,11 @@ public abstract class DataSqlSource<DBChannel> extends AbstractService implement
return findAsync(clazz, (SelectColumn) null, pk); return findAsync(clazz, (SelectColumn) null, pk);
} }
@Override
public <T> CompletableFuture<T> findAsync(final Class<T> clazz, ChannelContext context, final Serializable pk) {
return findAsync(clazz, context, (SelectColumn) null, pk);
}
@Override @Override
public <T> T find(Class<T> clazz, final SelectColumn selects, Serializable pk) { public <T> T find(Class<T> clazz, final SelectColumn selects, Serializable pk) {
final EntityInfo<T> info = loadEntityInfo(clazz); final EntityInfo<T> info = loadEntityInfo(clazz);
@@ -1624,26 +1634,30 @@ public abstract class DataSqlSource<DBChannel> extends AbstractService implement
T rs = selects == null ? cache.find(pk) : cache.find(selects, pk); T rs = selects == null ? cache.find(pk) : cache.find(selects, pk);
if (cache.isFullLoaded() || rs != null) return rs; if (cache.isFullLoaded() || rs != null) return rs;
} }
return findCompose(info, selects, pk).join(); return findCompose(info, null, selects, pk).join();
} }
@Override @Override
public <T> CompletableFuture<T> findAsync(final Class<T> clazz, final SelectColumn selects, final Serializable pk) { public <T> CompletableFuture<T> findAsync(final Class<T> clazz, final SelectColumn selects, final Serializable pk) {
return findAsync(clazz, null, selects, pk);
}
protected <T> CompletableFuture<T> findAsync(final Class<T> clazz, final ChannelContext context, final SelectColumn selects, final Serializable pk) {
final EntityInfo<T> info = loadEntityInfo(clazz); final EntityInfo<T> info = loadEntityInfo(clazz);
final EntityCache<T> cache = info.getCache(); final EntityCache<T> cache = info.getCache();
if (cache != null) { if (cache != null) {
T rs = selects == null ? cache.find(pk) : cache.find(selects, pk); T rs = selects == null ? cache.find(pk) : cache.find(selects, pk);
if (cache.isFullLoaded() || rs != null) return CompletableFuture.completedFuture(rs); if (cache.isFullLoaded() || rs != null) return CompletableFuture.completedFuture(rs);
} }
if (isAsync()) return findCompose(info, selects, pk); if (isAsync()) return findCompose(info, context, selects, pk);
return CompletableFuture.supplyAsync(() -> findCompose(info, selects, pk).join(), getExecutor()); return CompletableFuture.supplyAsync(() -> findCompose(info, context, selects, pk).join(), getExecutor());
} }
protected <T> CompletableFuture<T> findCompose(final EntityInfo<T> info, final SelectColumn selects, Serializable pk) { protected <T> CompletableFuture<T> findCompose(final EntityInfo<T> info, final ChannelContext context, final SelectColumn selects, Serializable pk) {
String column = info.getPrimarySQLColumn(); String column = info.getPrimarySQLColumn();
final String sql = "SELECT " + info.getQueryColumns(null, selects) + " FROM " + info.getTable(pk) + " WHERE " + column + "=" + info.formatSQLValue(column, pk, sqlFormatter); final String sql = "SELECT " + info.getQueryColumns(null, selects) + " FROM " + info.getTable(pk) + " WHERE " + column + "=" + info.formatSQLValue(column, pk, sqlFormatter);
if (info.isLoggable(logger, Level.FINEST, sql)) logger.finest(info.getType().getSimpleName() + " find sql=" + sql); if (info.isLoggable(logger, Level.FINEST, sql)) logger.finest(info.getType().getSimpleName() + " find sql=" + sql);
return findDB(info, sql, true, selects); return findDB(info, context, sql, true, selects);
} }
@Override @Override
@@ -1711,7 +1725,7 @@ public abstract class DataSqlSource<DBChannel> extends AbstractService implement
final CharSequence where = node == null ? null : node.createSQLExpress(info, joinTabalis); final CharSequence where = node == null ? null : node.createSQLExpress(info, joinTabalis);
final String sql = "SELECT " + info.getQueryColumns("a", selects) + " FROM " + info.getTable(node) + " a" + (join == null ? "" : join) + ((where == null || where.length() == 0) ? "" : (" WHERE " + where)); final String sql = "SELECT " + info.getQueryColumns("a", selects) + " FROM " + info.getTable(node) + " a" + (join == null ? "" : join) + ((where == null || where.length() == 0) ? "" : (" WHERE " + where));
if (info.isLoggable(logger, Level.FINEST, sql)) logger.finest(info.getType().getSimpleName() + " find sql=" + sql); if (info.isLoggable(logger, Level.FINEST, sql)) logger.finest(info.getType().getSimpleName() + " find sql=" + sql);
return findDB(info, sql, false, selects); return findDB(info, null, sql, false, selects);
} }
@Override @Override

View File

@@ -32,8 +32,6 @@ public final class EntityCache<T> {
//日志 //日志
private static final Logger logger = Logger.getLogger(EntityCache.class.getName()); private static final Logger logger = Logger.getLogger(EntityCache.class.getName());
private Object[] array;
//主键与对象的键值对 //主键与对象的键值对
private ConcurrentHashMap<Serializable, T> map = new ConcurrentHashMap(); private ConcurrentHashMap<Serializable, T> map = new ConcurrentHashMap();
@@ -75,7 +73,7 @@ public final class EntityCache<T> {
//&#064;Cacheable的定时器 //&#064;Cacheable的定时器
private ScheduledThreadPoolExecutor scheduler; private ScheduledThreadPoolExecutor scheduler;
private CompletableFuture<List> loadFuture; private CompletableFuture<List<T>> loadFuture;
public EntityCache(final EntityInfo<T> info, final Cacheable c) { public EntityCache(final EntityInfo<T> info, final Cacheable c) {
this.info = info; this.info = info;
@@ -106,24 +104,25 @@ public final class EntityCache<T> {
}); });
} }
public void fullLoadAsync() { public CompletableFuture<List<T>> fullLoadAsync() {
if (loading.getAndSet(true)) return; if (this.fullloaded) return this.loadFuture;
if (loading.getAndSet(true)) return this.loadFuture;
if (info.fullloader == null) { if (info.fullloader == null) {
this.list = new ConcurrentLinkedQueue(); this.list = new ConcurrentLinkedQueue();
this.map = new ConcurrentHashMap(); this.map = new ConcurrentHashMap();
this.fullloaded = true; this.fullloaded = true;
loading.set(false); loading.set(false);
return; return this.loadFuture;
} }
this.fullloaded = false; this.fullloaded = false;
CompletableFuture<List> allFuture = info.fullloader.apply(info.source, info); CompletableFuture<List> allFuture = info.fullloader.apply(info.source, info);
this.loadFuture = allFuture; this.loadFuture = (CompletableFuture) allFuture;
if (allFuture == null) { if (allFuture == null) {
this.list = new ConcurrentLinkedQueue(); this.list = new ConcurrentLinkedQueue();
this.map = new ConcurrentHashMap(); this.map = new ConcurrentHashMap();
this.fullloaded = true; this.fullloaded = true;
loading.set(false); loading.set(false);
return; return this.loadFuture;
} }
if (this.interval > 0 && this.scheduler == null && info.fullloader != null) { if (this.interval > 0 && this.scheduler == null && info.fullloader != null) {
this.scheduler = new ScheduledThreadPoolExecutor(1, (Runnable r) -> { this.scheduler = new ScheduledThreadPoolExecutor(1, (Runnable r) -> {
@@ -164,6 +163,7 @@ public final class EntityCache<T> {
this.fullloaded = true; this.fullloaded = true;
loading.set(false); loading.set(false);
}); });
return this.loadFuture;
} }
public Class<T> getType() { public Class<T> getType() {
@@ -185,20 +185,6 @@ public final class EntityCache<T> {
return fullloaded; return fullloaded;
} }
//临时功能
@Deprecated
public EntityCache<T> array() {
if (!isFullLoaded()) this.loadFuture.join();
this.array = this.list.toArray();
return this;
}
//临时功能
@Deprecated
public T findAt(int pk) {
return (T) this.array[pk - 1];
}
public T find(Serializable pk) { public T find(Serializable pk) {
if (pk == null) return null; if (pk == null) return null;
T rs = map.get(pk); T rs = map.get(pk);

View File

@@ -8,7 +8,7 @@ package org.redkale.source;
/** /**
* FilterBean用于过滤条件 所有的FilterBean都必须可以转换成FilterNode <br> * FilterBean用于过滤条件 所有的FilterBean都必须可以转换成FilterNode <br>
* *
* 不被标记为&#64;javax.persistence.Transient 的字段均视为过滤条件 <br> * 标记为&#64;FilterColumn.ignore=true 的字段会被忽略, 不参与生成过滤条件 <br>
* *
* <p> * <p>
* 详情见: https://redkale.org * 详情见: https://redkale.org

View File

@@ -37,6 +37,14 @@ public @interface FilterColumn {
*/ */
long least() default 1; long least() default 1;
/**
* 生成过滤条件时是否屏蔽该字段
*
* @return 是否屏蔽该字段
* @since 2.4.0
*/
boolean ignore() default false;
/** /**
* express的默认值根据字段类型的不同而不同: <br> * express的默认值根据字段类型的不同而不同: <br>
* 数组 --&gt; IN <br> * 数组 --&gt; IN <br>

View File

@@ -173,6 +173,7 @@ public final class FilterNodeBean<T extends FilterBean> implements Comparable<Fi
if (Modifier.isStatic(field.getModifiers())) continue; if (Modifier.isStatic(field.getModifiers())) continue;
if (fields.contains(field.getName())) continue; if (fields.contains(field.getName())) continue;
if (field.getAnnotation(Transient.class) != null) continue; if (field.getAnnotation(Transient.class) != null) continue;
if (field.getAnnotation(FilterColumn.class) != null && field.getAnnotation(FilterColumn.class).ignore()) continue;
final boolean pubmod = Modifier.isPublic(field.getModifiers()); final boolean pubmod = Modifier.isPublic(field.getModifiers());

View File

@@ -21,7 +21,7 @@ import static org.redkale.source.DataSources.*;
* *
* @author zhangjx * @author zhangjx
*/ */
public class PoolJdbcSource extends PoolSource<Connection> { public class PoolJdbcSource extends PoolSource {
protected final ConnectionPoolDataSource source; protected final ConnectionPoolDataSource source;
@@ -187,7 +187,8 @@ public class PoolJdbcSource extends PoolSource<Connection> {
} }
@Override @Override
public void offerConnection(final Connection conn) { public <C> void offerConnection(final C connection) {
Connection conn = (Connection) connection;
if (conn == null) return; if (conn == null) return;
try { try {
conn.close(); conn.close();

View File

@@ -19,9 +19,8 @@ import static org.redkale.source.DataSources.*;
* 详情见: https://redkale.org * 详情见: https://redkale.org
* *
* @author zhangjx * @author zhangjx
* @param <DBChannel> 连接泛型
*/ */
public abstract class PoolSource<DBChannel> { public abstract class PoolSource {
protected final AtomicLong closeCounter = new AtomicLong(); protected final AtomicLong closeCounter = new AtomicLong();
@@ -152,11 +151,11 @@ public abstract class PoolSource<DBChannel> {
public abstract void change(Properties property); public abstract void change(Properties property);
public abstract DBChannel poll(); public abstract <C> C poll();
public abstract CompletableFuture<DBChannel> pollAsync(); public abstract <C> CompletableFuture<C> pollAsync();
public abstract void offerConnection(final DBChannel conn); public abstract <C> void offerConnection(final C conn);
public abstract void close(); public abstract void close();

View File

@@ -19,7 +19,7 @@ import org.redkale.util.*;
* *
* @author zhangjx * @author zhangjx
*/ */
public abstract class PoolTcpSource extends PoolSource<AsyncConnection> { public abstract class PoolTcpSource extends PoolSource {
protected AsyncGroup asyncGroup; protected AsyncGroup asyncGroup;
@@ -84,7 +84,8 @@ public abstract class PoolTcpSource extends PoolSource<AsyncConnection> {
} }
@Override @Override
public void offerConnection(final AsyncConnection conn) { public <C> void offerConnection(final C connection) {
AsyncConnection conn = (AsyncConnection) connection;
if (conn == null) return; if (conn == null) return;
if (conn.isOpen()) { if (conn.isOpen()) {
CompletableFuture<AsyncConnection> future = pollQueue.poll(); CompletableFuture<AsyncConnection> future = pollQueue.poll();

View File

@@ -0,0 +1,398 @@
/*
* 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.source;
import java.util.*;
import org.redkale.convert.ConvertColumn;
import org.redkale.convert.json.JsonConvert;
/**
* SearchFilterBean用于搜索条件 所有的FilterBean都必须可以转换成FilterNode <br>
*
* 不被标记为&#64;javax.persistence.Transient 的字段均视为过滤条件 <br>
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*
* @since 2.4.0
*/
public interface SearchBean extends java.io.Serializable {
public static final String SEARCH_FILTER_NAME = "#search";
public static SearchSimpleBean create() {
return new SearchSimpleBean();
}
public static SearchSimpleBean create(String keyword, String... fields) {
return new SearchSimpleBean(keyword, fields);
}
/**
* 需要搜索的index集合无值则使用当前entity类
*
* @return Class[]
*/
public Class[] searchClasses();
/**
* 搜索字段集合, 必须字段值
*
* @return String[]
*/
public String[] searchFields();
/**
* 搜索关键字, 必须字段值
*
* @return String
*/
public String searchKeyword();
/**
* 搜索分词器,可以为空
*
* @return String
*/
public String searchAnalyzer();
/**
* 扩展的信息
*
* @return Map
*/
default Map<String, Object> extras() {
return null;
}
/**
* 高亮显示
*
* @return SearchHighlightBean
*/
public SearchHighlightBean highlight();
public static interface SearchHighlightBean {
public static SearchSimpleHighlightBean create() {
return new SearchSimpleHighlightBean();
}
public String preTag();
public String postTag();
public String boundaryLocale();
public int fragmentSize();
default int fragmentCount() {
return 1;
}
default Map<String, Object> extras() {
return null;
}
}
public static class SearchSimpleBean implements SearchBean {
@ConvertColumn(index = 1)
@FilterColumn(ignore = true)
private Class[] classes;
@ConvertColumn(index = 2)
@FilterColumn(ignore = true)
private String[] fields;
@ConvertColumn(index = 3)
@FilterColumn(ignore = true)
private String keyword;
@ConvertColumn(index = 4)
@FilterColumn(ignore = true)
private String analyzer;
@ConvertColumn(index = 5)
@FilterColumn(ignore = true)
private SearchHighlightBean highlight;
@ConvertColumn(index = 6)
@FilterColumn(ignore = true)
private Map<String, Object> extras;
public SearchSimpleBean() {
}
public SearchSimpleBean(String keyword, String... fields) {
this.keyword = keyword;
this.fields = fields;
if (fields == null || fields.length < 1) throw new IllegalArgumentException("fields is empty");
}
public SearchSimpleBean keyword(String keyword) {
this.keyword = keyword;
return this;
}
public SearchSimpleBean analyzer(String analyzer) {
this.analyzer = analyzer;
return this;
}
public SearchSimpleBean fields(String... fields) {
if (fields == null || fields.length < 1) throw new IllegalArgumentException("fields is empty");
this.fields = fields;
return this;
}
public SearchSimpleBean classes(Class[] classes) {
this.classes = classes;
return this;
}
public SearchSimpleBean highlight(SearchHighlightBean highlight) {
this.highlight = highlight;
return this;
}
public SearchSimpleBean extras(Map<String, Object> map) {
this.extras = map;
return this;
}
public SearchSimpleBean extras(String key, Object value) {
if (this.extras == null) this.extras = new LinkedHashMap<>();
this.extras.put(key, value);
return this;
}
@Override
public String searchKeyword() {
return keyword;
}
@Override
public Class[] searchClasses() {
return classes;
}
@Override
public String[] searchFields() {
return fields;
}
@Override
public Map<String, Object> extras() {
return extras;
}
@Override
public String searchAnalyzer() {
return analyzer;
}
@Override
public SearchHighlightBean highlight() {
return highlight;
}
public Class[] getClasses() {
return classes;
}
public void setClasses(Class[] classes) {
this.classes = classes;
}
public String[] getFields() {
return fields;
}
public void setFields(String[] fields) {
this.fields = fields;
}
public String getKeyword() {
return keyword;
}
public void setKeyword(String keyword) {
this.keyword = keyword;
}
public String getAnalyzer() {
return analyzer;
}
public void setAnalyzer(String analyzer) {
this.analyzer = analyzer;
}
public SearchHighlightBean getHighlight() {
return highlight;
}
public void setHighlight(SearchHighlightBean highlight) {
this.highlight = highlight;
}
public Map<String, Object> getExtras() {
return extras;
}
public void setExtras(Map<String, Object> extras) {
this.extras = extras;
}
@Override
public String toString() {
return JsonConvert.root().convertTo(this);
}
}
public static class SearchSimpleHighlightBean implements SearchHighlightBean {
@ConvertColumn(index = 1)
private String preTag;
@ConvertColumn(index = 2)
private String postTag;
@ConvertColumn(index = 3)
private String boundaryLocale;
@ConvertColumn(index = 4)
private int fragmentSize = 100;
@ConvertColumn(index = 5)
private int fragmentCount = 1;
@ConvertColumn(index = 6)
@FilterColumn(ignore = true)
private Map<String, Object> extras;
public SearchSimpleHighlightBean tag(String preTag, String postTag) {
this.preTag = preTag;
this.postTag = postTag;
return this;
}
public SearchSimpleHighlightBean boundaryLocale(String boundaryLocale) {
this.boundaryLocale = boundaryLocale;
return this;
}
public SearchSimpleHighlightBean fragmentSize(int fragmentSize) {
this.fragmentSize = fragmentSize;
return this;
}
public SearchSimpleHighlightBean fragmentCount(int fragmentCount) {
this.fragmentCount = fragmentCount;
return this;
}
public SearchSimpleHighlightBean extras(Map<String, Object> map) {
this.extras = map;
return this;
}
public SearchSimpleHighlightBean extras(String key, Object value) {
if (this.extras == null) this.extras = new LinkedHashMap<>();
this.extras.put(key, value);
return this;
}
@Override
public Map<String, Object> extras() {
return extras;
}
@Override
public String preTag() {
return preTag;
}
@Override
public String postTag() {
return postTag;
}
@Override
public String boundaryLocale() {
return boundaryLocale;
}
@Override
public int fragmentSize() {
return fragmentSize;
}
@Override
public int fragmentCount() {
return fragmentCount;
}
public String getPreTag() {
return preTag;
}
public void setPreTag(String preTag) {
this.preTag = preTag;
}
public String getPostTag() {
return postTag;
}
public void setPostTag(String postTag) {
this.postTag = postTag;
}
public String getBoundaryLocale() {
return boundaryLocale;
}
public void setBoundaryLocale(String boundaryLocale) {
this.boundaryLocale = boundaryLocale;
}
public int getFragmentSize() {
return fragmentSize;
}
public void setFragmentSize(int fragmentSize) {
this.fragmentSize = fragmentSize;
}
public int getFragmentCount() {
return fragmentCount;
}
public void setFragmentCount(int fragmentCount) {
this.fragmentCount = fragmentCount;
}
public Map<String, Object> getExtras() {
return extras;
}
public void setExtras(Map<String, Object> extras) {
this.extras = extras;
}
@Override
public String toString() {
return JsonConvert.root().convertTo(this);
}
}
}

View File

@@ -0,0 +1,87 @@
/*
* 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.source;
import java.lang.annotation.Target;
import java.lang.annotation.Retention;
import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
/**
*
* 搜索引擎的数据Entity依附在setter、getter方法、字段进行简单的配置 <br>
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
* @since 2.4.0
*/
@Target({FIELD})
@Retention(RUNTIME)
public @interface SearchColumn {
//高亮显示参数
public static class HighLights {
public static final String HIGHLIGHT_NAME_ID = "#[id]";
public static final String HIGHLIGHT_NAME_INDEX = "#[index]";
}
/**
* 是否全文搜索
*
* @return boolean
*/
boolean text() default false;
/**
* 高亮对应的Column.name字段名被标记的字段为虚拟字段不会映射表中的字段 <br>
* 被标记的字段必须是String类型 <br>
* 有值时ignore必须为true
*
* @return String
*/
String highlight() default "";
/**
* 解析/存储时是否屏蔽该字段
*
* @return boolean
*/
boolean ignore() default false;
/**
* 设置索引参数
*
* @return String
*/
String options() default "";
/**
* 内容是否html格式
*
* @return boolean
*/
boolean html() default false;
/**
* 设置索引分词器
*
* @return String
*/
String analyzer() default "";
/**
* 设置搜索索引分词器
*
* @return String
*/
String searchAnalyzer() default "";
}

View File

@@ -0,0 +1,26 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.source;
import java.util.concurrent.CompletableFuture;
/**
*
* 搜索引擎的数据源, 接口与DataSource基本一致。 <br>
* 返回类型为CompletableFuture的接口为异步接口
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
* @since 2.4.0
*/
public interface SearchSource extends DataSource {
public <T> int updateMapping(final Class<T> clazz);
public <T> CompletableFuture<Integer> updateMappingAsync(final Class<T> clazz);
}

View File

@@ -279,8 +279,8 @@ public abstract class AnyValue {
} }
public DefaultAnyValue clear() { public DefaultAnyValue clear() {
this.stringEntrys = new Entry[0]; if (this.stringEntrys != null && this.stringEntrys.length > 0) this.stringEntrys = new Entry[0];
this.anyEntrys = new Entry[0]; if (this.anyEntrys != null && this.anyEntrys.length > 0) this.anyEntrys = new Entry[0];
return this; return this;
} }

View File

@@ -5,6 +5,7 @@
*/ */
package org.redkale.util; package org.redkale.util;
import java.lang.reflect.TypeVariable;
import java.util.*; import java.util.*;
import java.util.function.*; import java.util.function.*;
import org.redkale.asm.*; import org.redkale.asm.*;
@@ -857,7 +858,11 @@ public interface Attribute<T, F> {
throw new RuntimeException(ex); //不可能会发生 throw new RuntimeException(ex); //不可能会发生
} }
} }
mv.visitFieldInsn(PUTFIELD, interName, tfield.getName(), Type.getDescriptor(pcolumn)); if (!tfield.getType().isPrimitive() && tfield.getGenericType() instanceof TypeVariable) {
mv.visitFieldInsn(PUTFIELD, interName, tfield.getName(), "Ljava/lang/Object;");
} else {
mv.visitFieldInsn(PUTFIELD, interName, tfield.getName(), Type.getDescriptor(pcolumn));
}
} }
} else { } else {
mv.visitVarInsn(ALOAD, 1); mv.visitVarInsn(ALOAD, 1);

View File

@@ -5,6 +5,7 @@
*/ */
package org.redkale.util; package org.redkale.util;
import java.io.*;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.nio.charset.*; import java.nio.charset.*;
import java.util.Arrays; import java.util.Arrays;
@@ -46,6 +47,20 @@ public final class ByteArray implements ByteTuple {
return this; return this;
} }
/**
* 生成一个OutputStream
*
* @return OutputStream
*/
public OutputStream newOutputStream() {
return new OutputStream() {
@Override
public void write(int b) throws IOException {
put((byte) b);
}
};
}
/** /**
* 比较内容是否相同 * 比较内容是否相同
* *
@@ -54,9 +69,12 @@ public final class ByteArray implements ByteTuple {
* @return 是否相同 * @return 是否相同
*/ */
public boolean equal(final byte[] bytes) { public boolean equal(final byte[] bytes) {
if (bytes == null || count != bytes.length) return false; if (bytes == null) return false;
for (int i = 0; i < count; i++) { int len = count;
if (content[i] != bytes[i]) return false; if (len != bytes.length) return false;
byte[] ba = content;
for (int i = 0; i < len; i++) {
if (ba[i] != bytes[i]) return false;
} }
return true; return true;
} }
@@ -577,6 +595,23 @@ public final class ByteArray implements ByteTuple {
System.arraycopy(values, offset, content, poffset, length); System.arraycopy(values, offset, content, poffset, length);
} }
/**
* 写入ByteArray中的一部分
*
* @param array ByteArray
* @param offset 偏移量
* @param length 长度
*/
public void put(ByteArray array, int offset, int length) {
if (count >= content.length - length) {
byte[] ns = new byte[content.length + Math.max(16, length)];
System.arraycopy(content, 0, ns, 0, count);
this.content = ns;
}
System.arraycopy(array.content, offset, content, count, length);
count += length;
}
/** /**
* 写入ByteBuffer指定长度的数据 * 写入ByteBuffer指定长度的数据
* *

View File

@@ -6,6 +6,7 @@
package org.redkale.util; package org.redkale.util;
/** /**
* 版本
* <p> * <p>
* 详情见: https://redkale.org * 详情见: https://redkale.org
* *
@@ -17,7 +18,7 @@ public final class Redkale {
} }
public static String getDotedVersion() { public static String getDotedVersion() {
return "2.3.0"; return "2.4.0";
} }
public static int getMajorVersion() { public static int getMajorVersion() {
@@ -25,6 +26,6 @@ public final class Redkale {
} }
public static int getMinorVersion() { public static int getMinorVersion() {
return 3; return 4;
} }
} }

View File

@@ -70,17 +70,16 @@ public class SelectColumn implements Predicate<String> {
} }
/** /**
* @deprecated
* class中的字段名 * class中的字段名
* *
* @param columns 包含的字段名集合 * @param columns 包含的字段名集合
* *
* @return SelectColumn * @return SelectColumn
*/ */
@Deprecated // @Deprecated
public static SelectColumn createIncludes(String... columns) { // public static SelectColumn createIncludes(String... columns) {
return new SelectColumn(columns, false); // return new SelectColumn(columns, false);
} // }
/** /**
* class中的字段名 * class中的字段名
@@ -94,7 +93,6 @@ public class SelectColumn implements Predicate<String> {
} }
/** /**
* @deprecated
* class中的字段名 * class中的字段名
* *
* @param cols 包含的字段名集合 * @param cols 包含的字段名集合
@@ -102,10 +100,10 @@ public class SelectColumn implements Predicate<String> {
* *
* @return SelectColumn * @return SelectColumn
*/ */
@Deprecated // @Deprecated
public static SelectColumn createIncludes(String[] cols, String... columns) { // public static SelectColumn createIncludes(String[] cols, String... columns) {
return new SelectColumn(Utility.append(cols, columns), false); // return new SelectColumn(Utility.append(cols, columns), false);
} // }
/** /**
* class中的字段名 * class中的字段名
@@ -120,7 +118,6 @@ public class SelectColumn implements Predicate<String> {
} }
/** /**
* @deprecated
* *
* class中的字段名 * class中的字段名
* *
@@ -128,10 +125,10 @@ public class SelectColumn implements Predicate<String> {
* *
* @return SelectColumn * @return SelectColumn
*/ */
@Deprecated // @Deprecated
public static SelectColumn createExcludes(String... columns) { // public static SelectColumn createExcludes(String... columns) {
return new SelectColumn(columns, true); // return new SelectColumn(columns, true);
} // }
/** /**
* class中的字段名 * class中的字段名
@@ -145,7 +142,6 @@ public class SelectColumn implements Predicate<String> {
} }
/** /**
* @deprecated
* class中的字段名 * class中的字段名
* *
* @param cols 排除的字段名集合 * @param cols 排除的字段名集合
@@ -153,10 +149,10 @@ public class SelectColumn implements Predicate<String> {
* *
* @return SelectColumn * @return SelectColumn
*/ */
@Deprecated // @Deprecated
public static SelectColumn createExcludes(String[] cols, String... columns) { // public static SelectColumn createExcludes(String[] cols, String... columns) {
return new SelectColumn(Utility.append(cols, columns), true); // return new SelectColumn(Utility.append(cols, columns), true);
} // }
/** /**
* *

View File

@@ -60,16 +60,25 @@ public final class Utility {
private static final class FutureTimeout implements Runnable { private static final class FutureTimeout implements Runnable {
final CompletableFuture<?> f; final CompletableFuture f;
FutureTimeout(CompletableFuture<?> f) { final boolean or;
final Object u;
FutureTimeout(CompletableFuture f, boolean or, Object u) {
this.f = f; this.f = f;
this.or = or;
this.u = u;
} }
@Override @Override
public void run() { public void run() {
if (f != null && !f.isDone()) if (or) {
f.completeExceptionally(new TimeoutException()); if (f != null) f.complete(u);
} else {
if (f != null && !f.isDone()) f.completeExceptionally(new TimeoutException());
}
} }
} }
@@ -314,14 +323,14 @@ public final class Utility {
return greatejdk8; return greatejdk8;
} }
public static <T> CompletableFuture orTimeout(CompletableFuture future, long timeout, TimeUnit unit) { public static <T> CompletableFuture<T> orTimeout(CompletableFuture future, long timeout, TimeUnit unit) {
//if (greatejdk8) return future.orTimeout(timeout, unit); //if (greatejdk8) return future.orTimeout(timeout, unit);
return future.whenComplete(new FutureCanceller(futureDelayer.schedule(new FutureTimeout(future), timeout, unit))); return future.whenComplete(new FutureCanceller(futureDelayer.schedule(new FutureTimeout(future, false, null), timeout, unit)));
} }
public static void main(String[] args) throws Throwable { public static <T> CompletableFuture<T> completeOnTimeout(CompletableFuture future, T value, long timeout, TimeUnit unit) {
byte[] bs = readBytes(new FileInputStream("D:\\Java-Projects\\JavaApplication20\\dist\\AnonymousUnsafeByteBooleanFunction.class")); //if (greatejdk8) return future.completeOnTimeout(value, timeout, unit);
System.out.println(binToHex(bs)); return future.whenComplete(new FutureCanceller(futureDelayer.schedule(new FutureTimeout(future, true, value), timeout, unit)));
} }
/** /**
@@ -422,6 +431,19 @@ public final class Utility {
return items; return items;
} }
/**
* 裁剪List使其size不超过limit大小 <br>
*
* @param list 集合
* @param limit 大小
*
* @return List
*/
public static <T> List<T> limit(List<T> list, int limit) {
if (list == null || list.isEmpty() || list.size() <= limit) return list;
return list.subList(0, limit);
}
/** /**
* 获取不带"-"的UUID值 * 获取不带"-"的UUID值
* *
@@ -677,6 +699,68 @@ public final class Utility {
return sb.toString(); return sb.toString();
} }
/**
* 将对象数组用分隔符拼接成字符串
*
* @param <T> 泛型
* @param array 数组
* @param delimiter 分隔符
*
* @return String
*/
public static <T> String joining(final String[] array, final char delimiter) {
if (array == null || array.length == 0) return "";
StringBuilder sb = new StringBuilder();
for (String i : array) {
if (sb.length() > 0) sb.append(delimiter);
sb.append(i);
}
return sb.toString();
}
/**
* 将对象数组用分隔符拼接成字符串
*
* @param <T> 泛型
* @param array 数组
* @param delimiter 分隔符
*
* @return String
*/
public static <T> String joiningHex(final byte[] array, final char delimiter) {
if (array == null || array.length == 0) return "";
StringBuilder sb = new StringBuilder();
for (byte i : array) {
if (sb.length() > 0) sb.append(delimiter);
String s = Integer.toHexString(i & 0xff);
sb.append(s.length() > 1 ? "0x" : "0x0").append(s);
}
return sb.toString();
}
/**
* 将对象数组用分隔符拼接成字符串
*
* @param <T> 泛型
* @param array 数组
* @param offset 偏移量
* @param length 长度
* @param delimiter 分隔符
*
* @return String
*/
public static <T> String joiningHex(final byte[] array, int offset, int length, final char delimiter) {
if (array == null || array.length == 0) return "";
StringBuilder sb = new StringBuilder();
int len = offset + length;
for (int i = offset; i < len; i++) {
if (sb.length() > 0) sb.append(delimiter);
String s = Integer.toHexString(array[i] & 0xff);
sb.append(s.length() > 1 ? "0x" : "0x0").append(s);
}
return sb.toString();
}
/** /**
* 将一个或多个byte新元素添加到byte数组结尾 * 将一个或多个byte新元素添加到byte数组结尾
* *
@@ -2703,6 +2787,10 @@ public final class Utility {
return out; return out;
} }
public static byte[] readBytes(File file) throws IOException {
return readBytesThenClose(new FileInputStream(file));
}
public static byte[] readBytes(InputStream in) throws IOException { public static byte[] readBytes(InputStream in) throws IOException {
return readStream(in).toByteArray(); return readStream(in).toByteArray();
} }