Redkale 2.4.0 结束
This commit is contained in:
@@ -33,6 +33,14 @@
|
||||
-->
|
||||
<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的池参数,没配置该节点将自动创建一个。
|
||||
@@ -156,7 +164,7 @@
|
||||
excludelibs: 排除lib.path与excludes中的正则表达式匹配的路径, 多个正则表达式用分号;隔开
|
||||
charset: 文本编码, 默认: UTF-8
|
||||
backlog: 默认10K
|
||||
threads: 线程数, 默认: CPU核数*2,最小8个
|
||||
threads: 线程数, 默认: CPU核数*2,最小8个【已废弃 @since 2.3.0】
|
||||
maxconns: 最大连接数, 小于1表示无限制, 默认: 0
|
||||
maxbody: request.body最大值, 默认: 64K
|
||||
bufferCapacity: ByteBuffer的初始化大小, TCP默认: 32K; (HTTP 2.0、WebSocket,必须要16k以上); UDP默认: 1350B
|
||||
@@ -165,7 +173,7 @@
|
||||
aliveTimeoutSeconds: KeepAlive读操作超时秒数, 默认30, 0表示永久不超时; -1表示禁止KeepAlive
|
||||
readTimeoutSeconds: 读操作超时秒数, 默认0, 表示永久不超时
|
||||
writeTimeoutSeconds: 写操作超时秒数, 默认0, 表示永久不超时
|
||||
netimpl: ProtocolServer的实现类。TCP情况下值可以是aio或nio,默认值为aio;UDP情况下值可以是bio,默认值为bio;
|
||||
iogroup: 流线程组AsyncGroup对象,如果值为client,表示和Application.asyncGroup对象共用
|
||||
interceptor: 启动/关闭NodeServer时被调用的拦截器实现类,必须是org.redkale.boot.NodeInterceptor的子类,默认为null
|
||||
-->
|
||||
<server protocol="HTTP" host="127.0.0.1" port="6060" root="root" lib="">
|
||||
@@ -300,8 +308,9 @@
|
||||
【节点在<server>中唯一】
|
||||
当Server为HTTP协议时,render才有效. 指定输出引擎的实现类
|
||||
value: 输出引擎的实现类, 必须是org.redkale.net.http.HttpRender的子类
|
||||
suffixs: 引擎文件名后缀,多个用;隔开,默认值为: .htel
|
||||
-->
|
||||
<render value="org.redkalex.htel.HttpTemplateRender"/>
|
||||
<render value="org.redkalex.htel.HttpTemplateRender" suffixs=".htel"/>
|
||||
<!--
|
||||
【节点在<server>中唯一】
|
||||
当Server为HTTP协议时,ResourceServlet才有效. 默认存在一个有默认属性的resource-servlet节点
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
-->
|
||||
<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"/>
|
||||
<!--
|
||||
javax.persistence.jdbc.driver在JPA的值是JDBC驱动,Redkale有所不同,值应该是javax.sql.DataSource的子类。
|
||||
|
||||
@@ -24,6 +24,7 @@ module org.redkale {
|
||||
exports org.redkale.convert.json;
|
||||
exports org.redkale.mq;
|
||||
exports org.redkale.net;
|
||||
exports org.redkale.net.client;
|
||||
exports org.redkale.net.http;
|
||||
exports org.redkale.net.sncp;
|
||||
exports org.redkale.service;
|
||||
|
||||
@@ -123,9 +123,8 @@ public final class Application {
|
||||
*
|
||||
* @deprecated 2.3.0 使用RESNAME_APP_EXECUTOR
|
||||
*/
|
||||
@Deprecated
|
||||
public static final String RESNAME_SERVER_EXECUTOR2 = Server.RESNAME_SERVER_EXECUTOR2;
|
||||
|
||||
//@Deprecated
|
||||
//public static final String RESNAME_SERVER_EXECUTOR2 = Server.RESNAME_SERVER_EXECUTOR2;
|
||||
/**
|
||||
* 当前Server的ResourceFactory
|
||||
*/
|
||||
@@ -228,6 +227,7 @@ public final class Application {
|
||||
this.resourceFactory.register(RESNAME_APP_HOME, URI.class, root.toURI());
|
||||
try {
|
||||
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();
|
||||
String confsubpath = System.getProperty(RESNAME_APP_CONF, "conf");
|
||||
if (confsubpath.contains("://")) {
|
||||
@@ -344,11 +344,11 @@ public final class Application {
|
||||
try {
|
||||
String classval = clusterConf.getValue("value");
|
||||
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()) {
|
||||
ClusterAgent agent = it.next();
|
||||
if (agent.match(clusterConf)) {
|
||||
cluster = agent;
|
||||
ClusterAgentLoader agent = it.next();
|
||||
if (agent != null && agent.match(clusterConf)) {
|
||||
cluster = agent.agentClass().getConstructor().newInstance();
|
||||
cluster.setConfig(clusterConf);
|
||||
break;
|
||||
}
|
||||
@@ -380,7 +380,7 @@ public final class Application {
|
||||
mqs = new MessageAgent[mqConfs.length];
|
||||
Set<String> mqnames = new HashSet<>();
|
||||
for (int i = 0; i < mqConfs.length; i++) {
|
||||
AnyValue mqConf = mqConfs[0];
|
||||
AnyValue mqConf = mqConfs[i];
|
||||
String mqname = mqConf.getValue("name", "");
|
||||
if (mqnames.contains(mqname)) throw new RuntimeException("mq.name(" + mqname + ") is repeat");
|
||||
mqnames.add(mqname);
|
||||
@@ -395,11 +395,11 @@ public final class Application {
|
||||
try {
|
||||
String classval = mqConf.getValue("value");
|
||||
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()) {
|
||||
MessageAgent messageAgent = it.next();
|
||||
if (messageAgent.match(mqConf)) {
|
||||
mqs[i] = messageAgent;
|
||||
MessageAgentLoader messageAgent = it.next();
|
||||
if (messageAgent != null && messageAgent.match(mqConf)) {
|
||||
mqs[i] = messageAgent.agentClass().getConstructor().newInstance();
|
||||
mqs[i].setConfig(mqConf);
|
||||
break;
|
||||
}
|
||||
@@ -649,7 +649,7 @@ public final class Application {
|
||||
} else if (type == NodeSncpServer.class) {
|
||||
NodeServer server = null;
|
||||
for (NodeServer ns : application.getNodeServers()) {
|
||||
if (ns.getClass() == NodeSncpServer.class) continue;
|
||||
if (ns.getClass() != NodeSncpServer.class) continue;
|
||||
if (res.name().equals(ns.server.getName())) {
|
||||
server = ns;
|
||||
break;
|
||||
@@ -659,7 +659,7 @@ public final class Application {
|
||||
} else if (type == NodeHttpServer.class) {
|
||||
NodeServer server = null;
|
||||
for (NodeServer ns : application.getNodeServers()) {
|
||||
if (ns.getClass() == NodeHttpServer.class) continue;
|
||||
if (ns.getClass() != NodeHttpServer.class) continue;
|
||||
if (res.name().equals(ns.server.getName())) {
|
||||
server = ns;
|
||||
break;
|
||||
@@ -669,7 +669,7 @@ public final class Application {
|
||||
} else if (type == NodeWatchServer.class) {
|
||||
NodeServer server = null;
|
||||
for (NodeServer ns : application.getNodeServers()) {
|
||||
if (ns.getClass() == NodeWatchServer.class) continue;
|
||||
if (ns.getClass() != NodeWatchServer.class) continue;
|
||||
if (res.name().equals(ns.server.getName())) {
|
||||
server = ns;
|
||||
break;
|
||||
@@ -738,7 +738,29 @@ public final class Application {
|
||||
resourceFactory.register((ResourceFactory rf, final Object src, String resourceName, Field field, final Object attachment) -> {
|
||||
try {
|
||||
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);
|
||||
field.set(src, messageClient);
|
||||
rf.inject(messageClient, null); // 给其可能包含@Resource的字段赋值;
|
||||
@@ -758,11 +780,11 @@ public final class Application {
|
||||
try {
|
||||
Class sourceType = CacheMemorySource.class;
|
||||
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()) {
|
||||
CacheSource s = it.next();
|
||||
if (s.match(sourceConf)) {
|
||||
sourceType = s.getClass();
|
||||
CacheSourceLoader s = it.next();
|
||||
if (s != null && s.match(sourceConf)) {
|
||||
sourceType = s.sourceClass();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -208,7 +208,7 @@ public final class ClassFilter<T> {
|
||||
} catch (Throwable cfe) {
|
||||
if (finest && !clazzname.startsWith("sun.") && !clazzname.startsWith("javax.")
|
||||
&& !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"))) {
|
||||
//&& (!(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);
|
||||
|
||||
@@ -79,7 +79,23 @@ public class LogFileHandler extends Handler {
|
||||
message,
|
||||
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();
|
||||
|
||||
@@ -214,11 +214,11 @@ public abstract class NodeServer {
|
||||
String classval = sourceConf.getValue("value");
|
||||
Class type = null;
|
||||
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()) {
|
||||
CacheSource s = it.next();
|
||||
if (s.match(sourceConf)) {
|
||||
type = s.getClass();
|
||||
CacheSourceLoader s = it.next();
|
||||
if (s != null && s.match(sourceConf)) {
|
||||
type = s.sourceClass();
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -333,6 +333,9 @@ public abstract class NodeServer {
|
||||
}
|
||||
|
||||
application.dataSources.add(source);
|
||||
if (source instanceof SearchSource) {
|
||||
appResFactory.register(resourceName, SearchSource.class, source);
|
||||
}
|
||||
appResFactory.register(resourceName, DataSource.class, source);
|
||||
|
||||
field.set(src, source);
|
||||
@@ -365,11 +368,11 @@ public abstract class NodeServer {
|
||||
if (sourceConf != null) {
|
||||
String classval = sourceConf.getValue("value");
|
||||
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()) {
|
||||
CacheSource s = it.next();
|
||||
if (s.match(sourceConf)) {
|
||||
sourceType0 = s.getClass();
|
||||
CacheSourceLoader s = it.next();
|
||||
if (s != null && s.match(sourceConf)) {
|
||||
sourceType0 = s.sourceClass();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
25
src/org/redkale/cluster/ClusterAgentLoader.java
Normal file
25
src/org/redkale/cluster/ClusterAgentLoader.java
Normal 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();
|
||||
}
|
||||
@@ -31,8 +31,14 @@ public final class AnyEncoder<T> implements Encodeable<Writer, T> {
|
||||
out.writeClassName(null);
|
||||
out.writeNull();
|
||||
} else {
|
||||
if (out.needWriteClassName()) out.writeClassName(factory.getEntityAlias(value.getClass()));
|
||||
factory.loadEncoder(value.getClass()).convertTo(out, value);
|
||||
Class clazz = value.getClass();
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -68,7 +68,8 @@ public class ArrayEncoder<T> implements Encodeable<Writer, T[]> {
|
||||
out.writeNull();
|
||||
return;
|
||||
}
|
||||
if (value.length == 0) {
|
||||
int iMax = value.length - 1;
|
||||
if (iMax == -1) {
|
||||
out.writeArrayB(0, this, componentEncoder, value);
|
||||
out.writeArrayE();
|
||||
return;
|
||||
@@ -87,28 +88,27 @@ public class ArrayEncoder<T> implements Encodeable<Writer, T[]> {
|
||||
Encodeable<Writer, Object> itemEncoder = this.componentEncoder;
|
||||
if (subtypefinal) {
|
||||
if (out.writeArrayB(value.length, this, itemEncoder, value) < 0) {
|
||||
boolean first = true;
|
||||
for (Object v : value) {
|
||||
if (!first) out.writeArrayMark();
|
||||
writeMemberValue(out, member, itemEncoder, v, first);
|
||||
if (first) first = false;
|
||||
for (int i = 0;; i++) {
|
||||
writeMemberValue(out, member, itemEncoder, value[i], i);
|
||||
if (i == iMax) break;
|
||||
out.writeArrayMark();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (out.writeArrayB(value.length, this, itemEncoder, value) < 0) {
|
||||
final Type comp = this.componentType;
|
||||
boolean first = true;
|
||||
for (Object v : value) {
|
||||
if (!first) out.writeArrayMark();
|
||||
writeMemberValue(out, member, ((v != null && (v.getClass() == comp || out.specify() == comp)) ? itemEncoder : anyEncoder), v, first);
|
||||
if (first) first = false;
|
||||
for (int i = 0;; i++) {
|
||||
Object v = value[i];
|
||||
writeMemberValue(out, member, ((v != null && (v.getClass() == comp || out.specify() == comp)) ? itemEncoder : anyEncoder), v, i);
|
||||
if (i == iMax) break;
|
||||
out.writeArrayMark();
|
||||
}
|
||||
}
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
@@ -71,6 +71,7 @@ public abstract class ConvertFactory<R extends Reader, W extends Writer> {
|
||||
this.parent = parent;
|
||||
if (parent == null) {
|
||||
//---------------------------------------------------------
|
||||
|
||||
this.register(boolean.class, BoolSimpledCoder.instance);
|
||||
this.register(Boolean.class, BoolSimpledCoder.instance);
|
||||
|
||||
|
||||
@@ -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));
|
||||
|
||||
} catch (Exception ex) {
|
||||
throw new ConvertException(ex);
|
||||
throw new ConvertException("ObjectEncoder init type=" + this.type + " error", ex);
|
||||
}
|
||||
} finally {
|
||||
inited = true;
|
||||
|
||||
@@ -30,6 +30,16 @@ public class JsonBytesWriter extends JsonWriter implements ByteTuple {
|
||||
|
||||
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 byte[] content;
|
||||
@@ -138,11 +148,17 @@ public class JsonBytesWriter extends JsonWriter implements ByteTuple {
|
||||
public void writeLatin1To(final boolean quote, final String value) {
|
||||
byte[] bs = Utility.byteArray(value);
|
||||
int len = bs.length;
|
||||
expand(len + (quote ? 2 : 0));
|
||||
if (quote) content[count++] = '"';
|
||||
System.arraycopy(bs, 0, content, count, bs.length);
|
||||
count += len;
|
||||
if (quote) content[count++] = '"';
|
||||
if (quote) {
|
||||
byte[] src = expand(len + 2);
|
||||
src[count++] = '"';
|
||||
System.arraycopy(bs, 0, src, count, bs.length);
|
||||
count += len;
|
||||
src[count++] = '"';
|
||||
} else {
|
||||
byte[] src = expand(len);
|
||||
System.arraycopy(bs, 0, src, count, bs.length);
|
||||
count += len;
|
||||
}
|
||||
}
|
||||
|
||||
public JsonBytesWriter clear() {
|
||||
@@ -281,8 +297,8 @@ public class JsonBytesWriter extends JsonWriter implements ByteTuple {
|
||||
public String toString() {
|
||||
return new String(content, 0, count, StandardCharsets.UTF_8);
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------
|
||||
|
||||
//----------------------------------------------------------------------------------------------
|
||||
@Override
|
||||
public void writeBoolean(boolean value) {
|
||||
byte[] bs = value ? BYTES_TUREVALUE : BYTES_FALSEVALUE;
|
||||
@@ -293,6 +309,13 @@ public class JsonBytesWriter extends JsonWriter implements ByteTuple {
|
||||
|
||||
@Override
|
||||
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 : '-';
|
||||
if (value < 0) value = -value;
|
||||
int size;
|
||||
@@ -333,6 +356,13 @@ public class JsonBytesWriter extends JsonWriter implements ByteTuple {
|
||||
|
||||
@Override
|
||||
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 : '-';
|
||||
if (value < 0) value = -value;
|
||||
int size = 19;
|
||||
|
||||
@@ -24,6 +24,16 @@ public class JsonCharsWriter extends JsonWriter {
|
||||
|
||||
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 char[] content;
|
||||
@@ -171,6 +181,10 @@ public class JsonCharsWriter extends JsonWriter {
|
||||
|
||||
@Override
|
||||
public void writeInt(int value) {
|
||||
if (value >= 0 && value < TENTHOUSAND_MAX) {
|
||||
writeTo(TENTHOUSAND_BYTES[value]);
|
||||
return;
|
||||
}
|
||||
final char sign = value >= 0 ? 0 : '-';
|
||||
if (value < 0) value = -value;
|
||||
int size;
|
||||
@@ -211,6 +225,10 @@ public class JsonCharsWriter extends JsonWriter {
|
||||
|
||||
@Override
|
||||
public void writeLong(long value) {
|
||||
if (value >= 0 && value < TENTHOUSAND_MAX) {
|
||||
writeTo(TENTHOUSAND_BYTES[(int) value]);
|
||||
return;
|
||||
}
|
||||
final char sign = value >= 0 ? 0 : '-';
|
||||
if (value < 0) value = -value;
|
||||
int size = 19;
|
||||
|
||||
@@ -74,7 +74,12 @@ public abstract class JsonDynEncoder<T> implements Encodeable<JsonWriter, T> {
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@@ -119,21 +119,21 @@ public class HttpMessageClient extends MessageClient {
|
||||
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) -> {
|
||||
if (httbs == null || httbs.getResult() == null) return null;
|
||||
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) -> {
|
||||
if (httbs == null || httbs.getResult() == null) return null;
|
||||
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) -> {
|
||||
if (httbs == null || httbs.getResult() == null) return null;
|
||||
return JsonConvert.root().convertFrom(type, httbs.getResult());
|
||||
|
||||
@@ -30,8 +30,7 @@ public class HttpMessageClusterClient extends HttpMessageClient {
|
||||
|
||||
//jdk.internal.net.http.common.Utils.DISALLOWED_HEADERS_SET
|
||||
private static final Set<String> DISALLOWED_HEADERS_SET = Utility.ofSet("connection", "content-length",
|
||||
"date", "expect", "from", "host", "origin",
|
||||
"referer", "upgrade", "via", "warning");
|
||||
"date", "expect", "from", "host", "origin", "referer", "upgrade", "via", "warning");
|
||||
|
||||
protected ClusterAgent clusterAgent;
|
||||
|
||||
@@ -39,6 +38,7 @@ public class HttpMessageClusterClient extends HttpMessageClient {
|
||||
protected HttpClient httpClient;
|
||||
|
||||
//protected java.net.http.HttpClient httpClient;
|
||||
|
||||
public HttpMessageClusterClient(ClusterAgent clusterAgent) {
|
||||
super(null);
|
||||
Objects.requireNonNull(clusterAgent);
|
||||
|
||||
216
src/org/redkale/mq/HttpMessageLocalClient.java
Normal file
216
src/org/redkale/mq/HttpMessageLocalClient.java
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -216,6 +216,10 @@ public abstract class MessageAgent {
|
||||
public final synchronized void putService(NodeHttpServer ns, Service service, HttpServlet servlet) {
|
||||
AutoLoad al = service.getClass().getAnnotation(AutoLoad.class);
|
||||
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 consumerid = generateHttpConsumerid(topics, service);
|
||||
if (messageNodes.containsKey(consumerid)) throw new RuntimeException("consumerid(" + consumerid + ") is repeat");
|
||||
|
||||
25
src/org/redkale/mq/MessageAgentLoader.java
Normal file
25
src/org/redkale/mq/MessageAgentLoader.java
Normal 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();
|
||||
}
|
||||
@@ -269,6 +269,11 @@ class AsyncAioTcpConnection extends AsyncConnection {
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream newInputStream() {
|
||||
return Channels.newInputStream(readableByteChannel());
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void close() throws IOException {
|
||||
super.close();
|
||||
|
||||
@@ -22,7 +22,7 @@ import org.redkale.util.*;
|
||||
*
|
||||
* @author zhangjx
|
||||
*/
|
||||
public abstract class AsyncConnection implements AutoCloseable {
|
||||
public abstract class AsyncConnection implements ChannelContext, Channel, AutoCloseable {
|
||||
|
||||
private SSLContext sslContext;
|
||||
|
||||
@@ -134,6 +134,8 @@ public abstract class AsyncConnection implements AutoCloseable {
|
||||
|
||||
public abstract WritableByteChannel writableByteChannel();
|
||||
|
||||
protected abstract InputStream newInputStream();
|
||||
|
||||
public abstract void read(CompletionHandler<Integer, ByteBuffer> handler);
|
||||
|
||||
//src会写完才会回调
|
||||
@@ -145,26 +147,29 @@ public abstract class AsyncConnection implements AutoCloseable {
|
||||
}
|
||||
|
||||
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) {
|
||||
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) {
|
||||
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) {
|
||||
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();
|
||||
if (buffer.remaining() >= headerLength + bodyLength) {
|
||||
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();
|
||||
CompletionHandler<Integer, Void> newhandler = new CompletionHandler<Integer, Void>() {
|
||||
@Override
|
||||
@@ -183,7 +188,10 @@ public abstract class AsyncConnection implements AutoCloseable {
|
||||
} else {
|
||||
ByteBufferWriter writer = ByteBufferWriter.create(bufferSupplier, buffer);
|
||||
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();
|
||||
CompletionHandler<Integer, Void> newhandler = new CompletionHandler<Integer, Void>() {
|
||||
@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 {
|
||||
|
||||
public int pipelineCount;
|
||||
@@ -320,6 +366,19 @@ public abstract class AsyncConnection implements AutoCloseable {
|
||||
}
|
||||
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> {
|
||||
@@ -335,6 +394,19 @@ public abstract class AsyncConnection implements AutoCloseable {
|
||||
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
|
||||
public int compareTo(PipelineDataItem o) {
|
||||
return this.index - o.index;
|
||||
@@ -409,6 +481,7 @@ public abstract class AsyncConnection implements AutoCloseable {
|
||||
for (Object obj : attributes.values()) {
|
||||
if (obj instanceof AutoCloseable) ((AutoCloseable) obj).close();
|
||||
}
|
||||
attributes.clear();
|
||||
} catch (Exception io) {
|
||||
}
|
||||
}
|
||||
@@ -422,24 +495,29 @@ public abstract class AsyncConnection implements AutoCloseable {
|
||||
this.subobject = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAttribute(String name, Object value) {
|
||||
if (this.attributes == null) this.attributes = new HashMap<>();
|
||||
this.attributes.put(name, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public final <T> T getAttribute(String name) {
|
||||
return (T) (this.attributes == null ? null : this.attributes.get(name));
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void removeAttribute(String name) {
|
||||
if (this.attributes != null) this.attributes.remove(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Map<String, Object> getAttributes() {
|
||||
return this.attributes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void clearAttribute() {
|
||||
if (this.attributes != null) this.attributes.clear();
|
||||
}
|
||||
|
||||
@@ -20,7 +20,9 @@ import java.util.concurrent.*;
|
||||
*/
|
||||
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;
|
||||
|
||||
@@ -28,20 +30,35 @@ class AsyncNioCompletionHandler<A> implements CompletionHandler<Integer, A>, Run
|
||||
|
||||
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.attachment = attachment;
|
||||
}
|
||||
|
||||
public void setConnBuffers(AsyncNioConnection conn, ByteBuffer... buffs) {
|
||||
this.conn = conn;
|
||||
public void attachment(A attachment) {
|
||||
this.attachment = attachment;
|
||||
}
|
||||
|
||||
public void buffers(ByteBuffer... buffs) {
|
||||
this.buffers = buffs;
|
||||
}
|
||||
|
||||
public void setAttachment(A attachment) {
|
||||
this.attachment = attachment;
|
||||
public void buffer(ByteBuffer buff) {
|
||||
this.buffer = buff;
|
||||
}
|
||||
|
||||
private void clear() {
|
||||
this.handler = null;
|
||||
this.attachment = null;
|
||||
this.timeoutFuture = null;
|
||||
this.buffers = null;
|
||||
this.buffer = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -51,10 +68,17 @@ class AsyncNioCompletionHandler<A> implements CompletionHandler<Integer, A>, Run
|
||||
this.timeoutFuture = null;
|
||||
future.cancel(true);
|
||||
}
|
||||
if (conn != null && buffers != null) {
|
||||
conn.offerBuffer(buffers);
|
||||
if (conn != null) {
|
||||
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
|
||||
@@ -64,18 +88,32 @@ class AsyncNioCompletionHandler<A> implements CompletionHandler<Integer, A>, Run
|
||||
this.timeoutFuture = null;
|
||||
future.cancel(true);
|
||||
}
|
||||
if (conn != null && buffers != null) {
|
||||
conn.offerBuffer(buffers);
|
||||
if (conn != null) {
|
||||
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
|
||||
public void run() {
|
||||
if (conn != null && buffers != null) {
|
||||
conn.offerBuffer(buffers);
|
||||
if (conn != null) {
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -47,6 +47,8 @@ abstract class AsyncNioConnection extends AsyncConnection {
|
||||
protected SelectionKey connectKey;
|
||||
|
||||
//-------------------------------- 读操作 --------------------------------------
|
||||
protected final AsyncNioCompletionHandler<ByteBuffer> readTimeoutCompletionHandler = new AsyncNioCompletionHandler<>(this);
|
||||
|
||||
protected int readTimeoutSeconds;
|
||||
|
||||
int currReadInvoker;
|
||||
@@ -60,6 +62,8 @@ abstract class AsyncNioConnection extends AsyncConnection {
|
||||
protected SelectionKey readKey;
|
||||
|
||||
//-------------------------------- 写操作 --------------------------------------
|
||||
protected final AsyncNioCompletionHandler<Object> writeTimeoutCompletionHandler = new AsyncNioCompletionHandler<>(this);
|
||||
|
||||
protected int writeTimeoutSeconds;
|
||||
|
||||
int currWriteInvoker;
|
||||
@@ -76,6 +80,10 @@ abstract class AsyncNioConnection extends AsyncConnection {
|
||||
|
||||
protected int writeByteTuple2Length;
|
||||
|
||||
protected Consumer writeByteTuple2Callback;
|
||||
|
||||
protected Object writeByteTuple2Attachment;
|
||||
|
||||
//写操作, 二选一,要么writeByteBuffer有值,要么writeByteBuffers、writeOffset、writeLength有值
|
||||
protected ByteBuffer writeByteBuffer;
|
||||
|
||||
@@ -163,17 +171,18 @@ abstract class AsyncNioConnection extends AsyncConnection {
|
||||
}
|
||||
this.readPending = true;
|
||||
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;
|
||||
newhandler.timeoutFuture = ioGroup.scheduleTimeout(newhandler, this.readTimeoutSeconds, TimeUnit.SECONDS);
|
||||
} else {
|
||||
this.readCompletionHandler = handler;
|
||||
}
|
||||
doRead(currReadInvoker < MAX_INVOKER_ONSTACK && this.ioThread.inCurrThread());
|
||||
doRead(currReadInvoker < MAX_INVOKER_ONSTACK || this.ioThread.inCurrThread()); //同一线程中Selector.wakeup无效
|
||||
}
|
||||
|
||||
@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(handler);
|
||||
if (!this.isConnected()) {
|
||||
@@ -191,15 +200,20 @@ abstract class AsyncNioConnection extends AsyncConnection {
|
||||
this.writeByteTuple2Array = bodyContent;
|
||||
this.writeByteTuple2Offset = bodyOffset;
|
||||
this.writeByteTuple2Length = bodyLength;
|
||||
this.writeByteTuple2Callback = bodyCallback;
|
||||
this.writeByteTuple2Attachment = bodyAttachment;
|
||||
this.writeAttachment = null;
|
||||
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;
|
||||
newhandler.timeoutFuture = ioGroup.scheduleTimeout(newhandler, this.writeTimeoutSeconds, TimeUnit.SECONDS);
|
||||
} 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
|
||||
@@ -218,7 +232,8 @@ abstract class AsyncNioConnection extends AsyncConnection {
|
||||
this.writeByteBuffer = src;
|
||||
this.writeAttachment = attachment;
|
||||
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;
|
||||
newhandler.timeoutFuture = ioGroup.scheduleTimeout(newhandler, this.writeTimeoutSeconds, TimeUnit.SECONDS);
|
||||
} else {
|
||||
@@ -245,7 +260,8 @@ abstract class AsyncNioConnection extends AsyncConnection {
|
||||
this.writeLength = length;
|
||||
this.writeAttachment = attachment;
|
||||
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;
|
||||
newhandler.timeoutFuture = ioGroup.scheduleTimeout(newhandler, this.writeTimeoutSeconds, TimeUnit.SECONDS);
|
||||
} else {
|
||||
@@ -262,8 +278,8 @@ abstract class AsyncNioConnection extends AsyncConnection {
|
||||
currReadInvoker++;
|
||||
if (this.readByteBuffer == null) {
|
||||
this.readByteBuffer = pollReadBuffer();
|
||||
if (this.readTimeoutSeconds > 0 && this.readCompletionHandler != null) {
|
||||
((AsyncNioCompletionHandler) this.readCompletionHandler).setAttachment(this.readByteBuffer);
|
||||
if (this.readTimeoutSeconds > 0) {
|
||||
this.readTimeoutCompletionHandler.attachment(this.readByteBuffer);
|
||||
}
|
||||
}
|
||||
readCount = implRead(readByteBuffer);
|
||||
@@ -295,12 +311,15 @@ abstract class AsyncNioConnection extends AsyncConnection {
|
||||
int totalCount = 0;
|
||||
boolean hasRemain = true;
|
||||
if (invokeDirect) currWriteInvoker++;
|
||||
while (invokeDirect && hasRemain) {
|
||||
while (invokeDirect && hasRemain) { //必须要将buffer写完为止
|
||||
if (writeByteTuple1Array != null) {
|
||||
final ByteBuffer buffer = pollWriteBuffer();
|
||||
if (buffer.remaining() >= writeByteTuple1Length + writeByteTuple2Length) {
|
||||
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();
|
||||
writeByteBuffer = buffer;
|
||||
writeByteTuple1Array = null;
|
||||
@@ -309,10 +328,15 @@ abstract class AsyncNioConnection extends AsyncConnection {
|
||||
writeByteTuple2Array = null;
|
||||
writeByteTuple2Offset = 0;
|
||||
writeByteTuple2Length = 0;
|
||||
writeByteTuple2Callback = null;
|
||||
writeByteTuple2Attachment = null;
|
||||
} else {
|
||||
ByteBufferWriter writer = ByteBufferWriter.create(getBufferSupplier(), buffer);
|
||||
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();
|
||||
writeByteBuffers = buffers;
|
||||
writeOffset = 0;
|
||||
@@ -323,11 +347,15 @@ abstract class AsyncNioConnection extends AsyncConnection {
|
||||
writeByteTuple2Array = null;
|
||||
writeByteTuple2Offset = 0;
|
||||
writeByteTuple2Length = 0;
|
||||
writeByteTuple2Callback = null;
|
||||
writeByteTuple2Attachment = null;
|
||||
}
|
||||
if (writeByteBuffer == null) {
|
||||
((AsyncNioCompletionHandler) writeCompletionHandler).setConnBuffers(this, writeByteBuffers);
|
||||
} else {
|
||||
((AsyncNioCompletionHandler) writeCompletionHandler).setConnBuffers(this, writeByteBuffer);
|
||||
if (this.writeCompletionHandler == this.writeTimeoutCompletionHandler) {
|
||||
if (writeByteBuffer == null) {
|
||||
this.writeTimeoutCompletionHandler.buffers(writeByteBuffers);
|
||||
} else {
|
||||
this.writeTimeoutCompletionHandler.buffer(writeByteBuffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
int writeCount;
|
||||
|
||||
@@ -198,4 +198,76 @@ class AsyncNioTcpConnection extends AsyncNioConnection {
|
||||
if (this.readKey != null) this.readKey.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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -168,4 +168,9 @@ class AsyncNioUdpConnection extends AsyncNioConnection {
|
||||
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.
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
31
src/org/redkale/net/ChannelContext.java
Normal file
31
src/org/redkale/net/ChannelContext.java
Normal 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();
|
||||
}
|
||||
@@ -5,6 +5,7 @@
|
||||
*/
|
||||
package org.redkale.net;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.*;
|
||||
import org.redkale.convert.bson.BsonConvert;
|
||||
@@ -107,6 +108,10 @@ public abstract class Request<C extends Context> {
|
||||
return properties;
|
||||
}
|
||||
|
||||
protected InputStream newInputStream() {
|
||||
return channel.newInputStream();
|
||||
}
|
||||
|
||||
public <T> T setAttribute(String name, T value) {
|
||||
attributes.put(name, value);
|
||||
return value;
|
||||
@@ -126,6 +131,10 @@ public abstract class Request<C extends Context> {
|
||||
return attributes;
|
||||
}
|
||||
|
||||
public ChannelContext getChannelContext() {
|
||||
return channel;
|
||||
}
|
||||
|
||||
public C getContext() {
|
||||
return this.context;
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
finish(false, buffer);
|
||||
}
|
||||
|
||||
@@ -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";
|
||||
|
||||
@Deprecated //@deprecated 2.3.0 使用RESNAME_APP_EXECUTOR
|
||||
public static final String RESNAME_SERVER_EXECUTOR2 = "SERVER_EXECUTOR";
|
||||
//@Deprecated //@deprecated 2.3.0 使用RESNAME_APP_EXECUTOR
|
||||
//public static final String RESNAME_SERVER_EXECUTOR2 = "SERVER_EXECUTOR";
|
||||
|
||||
public static final String RESNAME_SERVER_RESFACTORY = "SERVER_RESFACTORY";
|
||||
|
||||
|
||||
@@ -157,14 +157,18 @@ public abstract class Client<R extends ClientRequest, P> {
|
||||
}
|
||||
|
||||
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) {
|
||||
return conn.writeChannel(request);
|
||||
}
|
||||
|
||||
protected CompletableFuture<ClientConnection> connect() {
|
||||
protected CompletableFuture<ClientConnection> connect(ChannelContext context) {
|
||||
ClientConnection minRunningConn = null;
|
||||
for (int i = 0; i < this.connArray.length; i++) {
|
||||
final int index = i;
|
||||
|
||||
@@ -30,15 +30,30 @@ public class HttpContext extends Context {
|
||||
|
||||
protected final String remoteAddrHeader;
|
||||
|
||||
protected final boolean lazyHeaders;
|
||||
protected boolean lazyHeaders; //存在动态改值
|
||||
|
||||
// protected RequestURINode[] uriCacheNodes;
|
||||
public HttpContext(HttpContextConfig config) {
|
||||
super(config);
|
||||
this.remoteAddrHeader = config.remoteAddrHeader;
|
||||
this.lazyHeaders = config.lazyHeaders;
|
||||
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() {
|
||||
byte[] bytes = new byte[16];
|
||||
random.nextBytes(bytes);
|
||||
@@ -162,6 +177,23 @@ public class HttpContext extends Context {
|
||||
|
||||
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 + '}';
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,10 +46,6 @@ public @interface HttpMapping {
|
||||
*/
|
||||
int cacheseconds() default 0;
|
||||
|
||||
//json结果的长度, 临时功能, 仅供Rest的HttpResponse.finishJson(final int length, final Object obj) 方法使用
|
||||
@Deprecated
|
||||
int length() default 0;
|
||||
|
||||
/**
|
||||
* 是否只接受RPC请求, 默认为false
|
||||
*
|
||||
|
||||
@@ -41,12 +41,14 @@ public class HttpPrepareServlet extends PrepareServlet<String, HttpContext, Http
|
||||
|
||||
private final Object excludeLock = new Object();
|
||||
|
||||
protected HttpContext context;
|
||||
|
||||
protected boolean lazyHeaders = true;
|
||||
|
||||
private Map<String, BiPredicate<String, String>> forbidURIMaps; //禁用的URL的正则表达式, 必须与 forbidURIPredicates 保持一致
|
||||
|
||||
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) {
|
||||
List<HttpServlet> servlets = new ArrayList<>();
|
||||
synchronized (allMapStrings) {
|
||||
@@ -204,6 +206,8 @@ public class HttpPrepareServlet extends PrepareServlet<String, HttpContext, Http
|
||||
@SuppressWarnings("unchecked")
|
||||
public void init(HttpContext context, AnyValue config) {
|
||||
super.init(context, config); //必须要执行
|
||||
this.context = context;
|
||||
context.lazyHeaders = lazyHeaders;
|
||||
Collection<HttpServlet> servlets = getServlets();
|
||||
servlets.forEach(s -> {
|
||||
s.preInit(context, getServletConf(s));
|
||||
@@ -234,29 +238,16 @@ public class HttpPrepareServlet extends PrepareServlet<String, HttpContext, Http
|
||||
this.resourceHttpServlet = new HttpResourceServlet();
|
||||
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);
|
||||
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
|
||||
@@ -334,12 +325,16 @@ public class HttpPrepareServlet extends PrepareServlet<String, HttpContext, Http
|
||||
if (!ws.repair()) prefix = "";//被设置为不自动追加前缀则清空prefix
|
||||
}
|
||||
}
|
||||
if (lazyHeaders && !Rest.isSimpleRestDyn(servlet)) {
|
||||
lazyHeaders = false;
|
||||
if (context != null) context.lazyHeaders = false; //启动后运行过程中执行addServlet
|
||||
}
|
||||
synchronized (allMapStrings) { //需要整段锁住
|
||||
for (String mappingpath : mappingpaths) {
|
||||
if (mappingpath == null) continue;
|
||||
if (!prefix.toString().isEmpty()) mappingpath = prefix + mappingpath;
|
||||
|
||||
if (Utility.contains(mappingpath, '.', '*', '{', '[', '(', '|', '^', '$', '+', '?', '\\')) { //是否是正则表达式))
|
||||
if (Utility.contains(mappingpath, '*', '{', '[', '(', '|', '^', '$', '+', '?', '\\')) { //是否是正则表达式))
|
||||
if (mappingpath.charAt(0) != '^') mappingpath = '^' + mappingpath;
|
||||
if (mappingpath.endsWith("/*")) {
|
||||
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()) {
|
||||
if (servlet._actionmap != null && servlet._actionmap.containsKey(mappingpath)) {
|
||||
//context.addRequestURINode(mappingpath);
|
||||
putMapping(mappingpath, new HttpServlet.HttpActionServlet(servlet._actionmap.get(mappingpath), servlet));
|
||||
} else {
|
||||
putMapping(mappingpath, servlet);
|
||||
|
||||
@@ -15,9 +15,8 @@ import org.redkale.util.AnyValue;
|
||||
* HttpResponse.finish(Object obj)内置对如下数据类型进行了特殊处理:
|
||||
* CharSequence/String
|
||||
* byte[]
|
||||
* ByteBuffer
|
||||
* ByteBuffer[]
|
||||
* File
|
||||
* RetResult
|
||||
* HttpResult
|
||||
* </pre>
|
||||
* <p>
|
||||
@@ -26,13 +25,11 @@ import org.redkale.util.AnyValue;
|
||||
* 详情见: https://redkale.org
|
||||
*
|
||||
* @author zhangjx
|
||||
* @param <T> 泛型
|
||||
*/
|
||||
public interface HttpRender<T> {
|
||||
public interface HttpRender {
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
@@ -10,9 +10,9 @@ import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Array;
|
||||
import java.net.*;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.Channels;
|
||||
import java.nio.charset.*;
|
||||
import java.util.*;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.logging.Level;
|
||||
import org.redkale.convert.*;
|
||||
import org.redkale.convert.json.JsonConvert;
|
||||
@@ -34,6 +34,8 @@ import org.redkale.util.*;
|
||||
*/
|
||||
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() {
|
||||
};
|
||||
|
||||
@@ -90,7 +92,7 @@ public class HttpRequest extends Request<HttpContext> {
|
||||
// @since 2.1.0
|
||||
protected Serializable currentUserid = CURRUSERID_NIL;
|
||||
|
||||
protected Object currentUser;
|
||||
protected Supplier<Object> currentUserSupplier;
|
||||
|
||||
protected boolean frombody;
|
||||
|
||||
@@ -130,6 +132,10 @@ public class HttpRequest extends Request<HttpContext> {
|
||||
|
||||
protected String remoteAddr;
|
||||
|
||||
private String lastRequestURIString;
|
||||
|
||||
private byte[] lastRequestURIBytes;
|
||||
|
||||
private final ByteArray array;
|
||||
|
||||
private byte[] headerBytes;
|
||||
@@ -289,7 +295,7 @@ public class HttpRequest extends Request<HttpContext> {
|
||||
this.headerBytes = httplast.headerBytes;
|
||||
this.headerParsed = httplast.headerParsed;
|
||||
this.headers.putAll(httplast.headers);
|
||||
} else if (context.lazyHeaders && getmethod) {
|
||||
} else if (context.lazyHeaders && getmethod) { //非GET必须要读header,会有Content-Length
|
||||
int rs = loadHeaderBytes(buffer);
|
||||
if (rs != 0) return rs;
|
||||
this.headerParsed = false;
|
||||
@@ -444,13 +450,30 @@ public class HttpRequest extends Request<HttpContext> {
|
||||
if (qst > 0) {
|
||||
this.requestURI = decodeable ? bytes.toDecodeString(0, qst, charset) : bytes.toString(0, qst, charset);
|
||||
this.queryBytes = bytes.getBytes(qst + 1, size - qst - 1);
|
||||
this.lastRequestURIString = null;
|
||||
this.lastRequestURIBytes = null;
|
||||
try {
|
||||
addParameter(bytes, qst + 1, size - qst - 1);
|
||||
} catch (Exception e) {
|
||||
this.context.getLogger().log(Level.WARNING, "HttpRequest.addParameter error: " + bytes.toString(), e);
|
||||
}
|
||||
} 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;
|
||||
}
|
||||
bytes.clear();
|
||||
@@ -700,7 +723,7 @@ public class HttpRequest extends Request<HttpContext> {
|
||||
|
||||
@Override
|
||||
protected HttpRequest copyHeader() {
|
||||
if (!context.lazyHeaders) return null;
|
||||
if (!pipelineSameHeaders || !context.lazyHeaders) return null;
|
||||
HttpRequest req = new HttpRequest(context, this.array);
|
||||
req.headerLength = this.headerLength;
|
||||
req.headerBytes = this.headerBytes;
|
||||
@@ -715,7 +738,7 @@ public class HttpRequest extends Request<HttpContext> {
|
||||
req.rpc = this.rpc;
|
||||
req.hashid = this.hashid;
|
||||
req.currentUserid = this.currentUserid;
|
||||
req.currentUser = this.currentUser;
|
||||
req.currentUserSupplier = this.currentUserSupplier;
|
||||
req.frombody = this.frombody;
|
||||
req.reqConvertType = this.reqConvertType;
|
||||
req.reqConvert = this.reqConvert;
|
||||
@@ -746,7 +769,7 @@ public class HttpRequest extends Request<HttpContext> {
|
||||
this.rpc = false;
|
||||
this.readState = READ_STATE_ROUTE;
|
||||
this.currentUserid = CURRUSERID_NIL;
|
||||
this.currentUser = null;
|
||||
this.currentUserSupplier = null;
|
||||
this.frombody = false;
|
||||
this.reqConvertType = null;
|
||||
this.reqConvert = null;
|
||||
@@ -837,6 +860,20 @@ public class HttpRequest extends Request<HttpContext> {
|
||||
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>
|
||||
*
|
||||
@@ -872,14 +909,14 @@ public class HttpRequest extends Request<HttpContext> {
|
||||
* 设置当前用户信息, 通常在HttpServlet.preExecute方法里设置currentUser <br>
|
||||
* 数据类型由@HttpUserType指定
|
||||
*
|
||||
* @param <T> 泛型
|
||||
* @param user 用户信息
|
||||
* @param supplier currentUser对象方法
|
||||
*
|
||||
* @since 2.4.0
|
||||
*
|
||||
* @return HttpRequest
|
||||
*/
|
||||
@Deprecated
|
||||
public <T> HttpRequest setCurrentUser(T user) {
|
||||
this.currentUser = user;
|
||||
public HttpRequest setCurrentUserSupplier(Supplier supplier) {
|
||||
this.currentUserSupplier = supplier;
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -892,10 +929,10 @@ public class HttpRequest extends Request<HttpContext> {
|
||||
*
|
||||
* @return 用户信息
|
||||
*/
|
||||
@Deprecated
|
||||
@SuppressWarnings("unchecked")
|
||||
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
|
||||
public final MultiContext getMultiContext() {
|
||||
final InputStream in = newInputStream();
|
||||
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);
|
||||
this.count = array.length();
|
||||
@@ -1119,6 +1157,15 @@ public class HttpRequest extends Request<HttpContext> {
|
||||
}, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否上传文件请求
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public final boolean isMultipart() {
|
||||
return boundary;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文件上传信息列表
|
||||
*
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
package org.redkale.net.http;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.ByteBuffer;
|
||||
import static java.nio.file.StandardWatchEventKinds.*;
|
||||
import java.nio.file.*;
|
||||
import java.util.AbstractMap.SimpleEntry;
|
||||
@@ -15,7 +14,7 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.atomic.LongAdder;
|
||||
import java.util.logging.*;
|
||||
import java.util.regex.*;
|
||||
import org.redkale.util.AnyValue;
|
||||
import org.redkale.util.*;
|
||||
|
||||
/**
|
||||
* 静态资源HttpServlet
|
||||
@@ -107,6 +106,8 @@ public class HttpResourceServlet extends HttpServlet {
|
||||
|
||||
protected WatchThread watchThread;
|
||||
|
||||
protected String[] renderSuffixs;
|
||||
|
||||
@Override
|
||||
public void init(HttpContext context, AnyValue config) {
|
||||
if (config != null) {
|
||||
@@ -211,6 +212,16 @@ public class HttpResourceServlet extends HttpServlet {
|
||||
if (uri.length() == 0 || uri.equals("/")) {
|
||||
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);
|
||||
FileEntry entry;
|
||||
if (watchThread == null && files.isEmpty()) {
|
||||
@@ -219,7 +230,7 @@ public class HttpResourceServlet extends HttpServlet {
|
||||
entry = files.computeIfAbsent(uri, x -> createFileEntry(x));
|
||||
}
|
||||
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();
|
||||
} else {
|
||||
//file = null 表示资源内容在内存而不是在File中
|
||||
@@ -253,8 +264,9 @@ public class HttpResourceServlet extends HttpServlet {
|
||||
|
||||
protected final HttpResourceServlet servlet;
|
||||
|
||||
protected ByteBuffer content;
|
||||
protected ByteArray content;
|
||||
|
||||
@SuppressWarnings("OverridableMethodCallInConstructor")
|
||||
public FileEntry(final HttpResourceServlet servlet, File file) {
|
||||
this.servlet = servlet;
|
||||
this.file = file;
|
||||
@@ -262,37 +274,32 @@ public class HttpResourceServlet extends HttpServlet {
|
||||
update();
|
||||
}
|
||||
|
||||
public FileEntry(final HttpResourceServlet servlet, String filename, ByteBuffer content) {
|
||||
public FileEntry(final HttpResourceServlet servlet, String filename, ByteArray content) {
|
||||
this.servlet = servlet;
|
||||
this.file = null;
|
||||
this.filename = filename;
|
||||
this.content = content.asReadOnlyBuffer();
|
||||
this.servlet.cachedLength.add(this.content.remaining());
|
||||
this.content = content;
|
||||
this.servlet.cachedLength.add(this.content.length());
|
||||
}
|
||||
|
||||
public FileEntry(final HttpResourceServlet servlet, String filename, InputStream in) throws IOException {
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
ByteArray out = new ByteArray();
|
||||
byte[] bytes = new byte[10240];
|
||||
int pos;
|
||||
while ((pos = in.read(bytes)) != -1) {
|
||||
out.write(bytes, 0, pos);
|
||||
}
|
||||
byte[] bs = out.toByteArray();
|
||||
ByteBuffer buf = ByteBuffer.allocateDirect(bs.length);
|
||||
buf.put(bs);
|
||||
buf.flip();
|
||||
|
||||
out.put(bytes, 0, pos);
|
||||
}
|
||||
this.servlet = servlet;
|
||||
this.file = null;
|
||||
this.filename = filename;
|
||||
this.content = buf.asReadOnlyBuffer();
|
||||
this.servlet.cachedLength.add(this.content.remaining());
|
||||
this.content = out;
|
||||
this.servlet.cachedLength.add(this.content.length());
|
||||
}
|
||||
|
||||
public void update() {
|
||||
if (this.file == null) return;
|
||||
if (this.content != null) {
|
||||
this.servlet.cachedLength.add(0L - this.content.remaining());
|
||||
this.servlet.cachedLength.add(0L - this.content.length());
|
||||
this.content = null;
|
||||
}
|
||||
long length = this.file.length();
|
||||
@@ -300,30 +307,26 @@ public class HttpResourceServlet extends HttpServlet {
|
||||
if (this.servlet.cachedLength.longValue() + length > this.servlet.cachelimit) return; //超过缓存总容量
|
||||
try {
|
||||
FileInputStream in = new FileInputStream(file);
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream((int) file.length());
|
||||
ByteArray out = new ByteArray((int) file.length());
|
||||
byte[] bytes = new byte[10240];
|
||||
int pos;
|
||||
while ((pos = in.read(bytes)) != -1) {
|
||||
out.write(bytes, 0, pos);
|
||||
out.put(bytes, 0, pos);
|
||||
}
|
||||
in.close();
|
||||
byte[] bs = out.toByteArray();
|
||||
ByteBuffer buf = ByteBuffer.allocateDirect(bs.length);
|
||||
buf.put(bs);
|
||||
buf.flip();
|
||||
this.content = buf.asReadOnlyBuffer();
|
||||
this.servlet.cachedLength.add(this.content.remaining());
|
||||
this.content = out;
|
||||
this.servlet.cachedLength.add(this.content.length());
|
||||
} catch (Exception e) {
|
||||
this.servlet.logger.log(Level.INFO, HttpResourceServlet.class.getSimpleName() + " update FileEntry(" + file + ") erroneous", e);
|
||||
}
|
||||
}
|
||||
|
||||
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() {
|
||||
return this.content == null ? 0L : this.content.remaining();
|
||||
return this.content == null ? 0L : this.content.length();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ import static java.time.format.DateTimeFormatter.RFC_1123_DATE_TIME;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.function.*;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.*;
|
||||
import org.redkale.convert.*;
|
||||
import org.redkale.convert.json.JsonConvert;
|
||||
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();
|
||||
|
||||
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);
|
||||
|
||||
@@ -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, byte[]> contentLengthMap = new HashMap<>();
|
||||
private static final byte[][] contentLengthArray = new byte[cacheMaxContentLength][];
|
||||
|
||||
static {
|
||||
|
||||
@@ -111,8 +113,8 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
|
||||
httpCodes.put(504, "Gateway Timeout");
|
||||
httpCodes.put(505, "HTTP Version Not Supported");
|
||||
|
||||
for (int i = 0; i <= cacheMaxContentLength; i++) {
|
||||
contentLengthMap.put(i, ("Content-Length: " + i + "\r\n").getBytes());
|
||||
for (int i = 0; i < cacheMaxContentLength; i++) {
|
||||
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 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;
|
||||
|
||||
@@ -160,21 +155,17 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
|
||||
|
||||
private final HttpCookie defaultCookie;
|
||||
|
||||
private final List<HttpRender> renders;
|
||||
|
||||
private final boolean hasRender;
|
||||
|
||||
private final HttpRender onlyoneHttpRender;
|
||||
private final HttpRender httpRender;
|
||||
|
||||
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>() {
|
||||
|
||||
@@ -204,18 +195,16 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
|
||||
this.defaultCookie = config == null ? null : config.defaultCookie;
|
||||
this.autoOptions = config == null ? false : config.autoOptions;
|
||||
this.dateSupplier = config == null ? null : config.dateSupplier;
|
||||
this.renders = config == null ? null : config.renders;
|
||||
this.hasRender = renders != null && !renders.isEmpty();
|
||||
this.onlyoneHttpRender = renders != null && renders.size() == 1 ? renders.get(0) : null;
|
||||
this.httpRender = config == null ? null : config.httpRender;
|
||||
|
||||
this.plainContentType = config == null ? "text/plain; charset=utf-8" : config.plainContentType;
|
||||
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.jsonContentTypeBytes = config == null ? ("Content-Type: " + this.jsonContentType + "\r\n").getBytes() : config.jsonContentTypeBytes;
|
||||
this.plainLiveContentLengthMap = config == null ? new HashMap<>() : config.plainLiveContentLengthMap;
|
||||
this.plainCloseContentLengthMap = config == null ? new HashMap<>() : config.plainCloseContentLengthMap;
|
||||
this.jsonLiveContentLengthMap = config == null ? new HashMap<>() : config.jsonLiveContentLengthMap;
|
||||
this.jsonCloseContentLengthMap = config == null ? new HashMap<>() : config.jsonCloseContentLengthMap;
|
||||
this.plainLiveContentLengthArray = config == null ? null : config.plainLiveContentLengthArray;
|
||||
this.plainCloseContentLengthArray = config == null ? null : config.plainCloseContentLengthArray;
|
||||
this.jsonLiveContentLengthArray = config == null ? null : config.jsonLiveContentLengthArray;
|
||||
this.jsonCloseContentLengthArray = config == null ? null : config.jsonCloseContentLengthArray;
|
||||
this.contentType = this.plainContentType;
|
||||
}
|
||||
|
||||
@@ -352,42 +341,6 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
|
||||
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格式输出
|
||||
*
|
||||
@@ -430,13 +383,12 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
|
||||
*
|
||||
* @param objs 输出对象
|
||||
*/
|
||||
@Deprecated //@since 2.3.0
|
||||
void finishJson(final Object... objs) {
|
||||
this.contentType = this.jsonContentType;
|
||||
if (this.recycleListener != null) this.output = objs;
|
||||
request.getRespConvert().convertToBytes(objs, convertHandler);
|
||||
}
|
||||
|
||||
// @Deprecated //@since 2.3.0
|
||||
// void finishJson(final Object... objs) {
|
||||
// this.contentType = this.jsonContentType;
|
||||
// if (this.recycleListener != null) this.output = objs;
|
||||
// request.getRespConvert().convertToBytes(objs, convertHandler);
|
||||
// }
|
||||
/**
|
||||
* 将RetResult对象以JSON格式输出
|
||||
*
|
||||
@@ -476,6 +428,15 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
|
||||
convert.convertToBytes(ret, convertHandler);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将HttpResult对象输出
|
||||
*
|
||||
* @param result HttpResult输出对象
|
||||
*/
|
||||
public void finish(HttpResult result) {
|
||||
finish(request.getRespConvert(), result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将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格式输出
|
||||
*
|
||||
@@ -585,23 +576,9 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
|
||||
finishJson((org.redkale.service.RetResult) obj);
|
||||
} else if (obj instanceof HttpResult) {
|
||||
finish(convert, (HttpResult) obj);
|
||||
} else if (obj instanceof HttpScope) {
|
||||
finish(convert, (HttpScope) obj);
|
||||
} 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) {
|
||||
this.contentType = this.jsonContentType;
|
||||
} else if (convert instanceof TextConvert) {
|
||||
@@ -646,7 +623,7 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
|
||||
if (isClosed()) return;
|
||||
this.status = status;
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
if (isClosed()) return; //避免重复关闭
|
||||
ByteArray data = headerArray;
|
||||
if (this.headWritedSize < 0) {
|
||||
if (contentType != null) this.contentType = contentType;
|
||||
this.contentLength = length;
|
||||
createHeader();
|
||||
}
|
||||
ByteArray data = headerArray;
|
||||
data.put(bs, offset, length);
|
||||
if (callback != null) callback.accept(attachment);
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
// 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() {
|
||||
if (this.status == 200 && !this.respHeadContainsConnection && !this.request.isWebSocket()
|
||||
&& (this.contentType == null || this.contentType == this.jsonContentType || this.contentType == this.plainContentType)
|
||||
&& (this.contentLength >= 0 && this.contentLength < jsonLiveContentLengthMap.size())) {
|
||||
Map<Integer, byte[]> lengthMap = this.plainLiveContentLengthMap;
|
||||
&& (this.contentLength >= 0 && this.contentLength < jsonLiveContentLengthArray.length)) {
|
||||
byte[][] lengthArray = this.plainLiveContentLengthArray;
|
||||
if (this.request.isKeepAlive()) {
|
||||
if (this.contentType == this.jsonContentType) {
|
||||
lengthMap = this.jsonLiveContentLengthMap;
|
||||
lengthArray = this.jsonLiveContentLengthArray;
|
||||
}
|
||||
} else {
|
||||
if (this.contentType == this.jsonContentType) {
|
||||
lengthMap = this.jsonCloseContentLengthMap;
|
||||
lengthArray = this.jsonCloseContentLengthArray;
|
||||
} else {
|
||||
lengthMap = this.plainCloseContentLengthMap;
|
||||
lengthArray = this.plainCloseContentLengthArray;
|
||||
}
|
||||
}
|
||||
if (context.lazyHeaders) {
|
||||
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));
|
||||
}
|
||||
headerArray.put(lengthArray[(int) this.contentLength]);
|
||||
} else {
|
||||
if (this.status == 200 && !this.respHeadContainsConnection && !this.request.isWebSocket()) {
|
||||
if (this.request.isKeepAlive()) {
|
||||
@@ -801,8 +792,8 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
|
||||
}
|
||||
}
|
||||
if (this.contentLength >= 0) {
|
||||
if (this.contentLength < contentLengthMap.size()) {
|
||||
headerArray.put(contentLengthMap.get((int) this.contentLength));
|
||||
if (this.contentLength < contentLengthArray.length) {
|
||||
headerArray.put(contentLengthArray[(int) this.contentLength]);
|
||||
} else {
|
||||
headerArray.put(("Content-Length: " + this.contentLength + "\r\n").getBytes());
|
||||
}
|
||||
@@ -938,7 +929,7 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
|
||||
*
|
||||
* @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);
|
||||
}
|
||||
|
||||
@@ -953,13 +944,12 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
|
||||
*
|
||||
* @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) {
|
||||
finish404();
|
||||
return;
|
||||
}
|
||||
if (fileBody != null) fileBody = fileBody.duplicate().asReadOnlyBuffer();
|
||||
final long length = file == null ? fileBody.remaining() : file.length();
|
||||
final long length = file == null ? fileBody.length() : file.length();
|
||||
final String match = request.getHeader("If-None-Match");
|
||||
final String etag = (file == null ? 0L : file.lastModified()) + "-" + length;
|
||||
if (match != null && etag.equals(match)) {
|
||||
@@ -992,24 +982,54 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
|
||||
}
|
||||
this.addHeader("ETag", etag);
|
||||
createHeader();
|
||||
ByteBuffer hbuffer = channel.pollWriteBuffer();
|
||||
hbuffer.put(headerArray.content(), 0, headerArray.length());
|
||||
hbuffer.flip();
|
||||
ByteArray data = headerArray;
|
||||
if (fileBody == null) {
|
||||
if (this.recycleListener != null) this.output = file;
|
||||
finishFile(hbuffer, file, start, len);
|
||||
} else {
|
||||
if (start >= 0) {
|
||||
fileBody.position((int) start);
|
||||
if (len > 0) fileBody.limit((int) (fileBody.position() + len));
|
||||
}
|
||||
if (this.recycleListener != null) this.output = fileBody;
|
||||
super.finish(hbuffer, fileBody);
|
||||
finishFile(data, file, start, len);
|
||||
} else { //一般HttpResourceServlet缓存file内容时fileBody不为空
|
||||
if (start >= 0) data.put(fileBody, (int) start, (int) ((len > 0) ? len : fileBody.length() - start));
|
||||
super.finish(false, data.content(), 0, data.length());
|
||||
}
|
||||
}
|
||||
|
||||
private void finishFile(ByteBuffer hbuffer, File file, long offset, long length) throws IOException {
|
||||
this.channel.write(hbuffer, hbuffer, new TransferFileHandler(file, offset, length));
|
||||
//offset、length 为 -1 表示输出整个文件
|
||||
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;
|
||||
}
|
||||
|
||||
protected final class TransferFileHandler implements CompletionHandler<Integer, ByteBuffer> {
|
||||
|
||||
private final File file;
|
||||
|
||||
private final AsynchronousFileChannel filechannel;
|
||||
|
||||
private final long max; //需要读取的字节数, -1表示读到文件结尾
|
||||
|
||||
private long count;//读取文件的字节数
|
||||
|
||||
private long readpos = 0;
|
||||
|
||||
private boolean hdwrite = true; //写入Header
|
||||
|
||||
private boolean read = false;
|
||||
|
||||
public TransferFileHandler(File file) throws IOException {
|
||||
this.file = file;
|
||||
this.filechannel = AsynchronousFileChannel.open(file.toPath(), options);
|
||||
this.readpos = 0;
|
||||
this.max = file.length();
|
||||
}
|
||||
|
||||
public TransferFileHandler(File file, long offset, long len) throws IOException {
|
||||
this.file = file;
|
||||
this.filechannel = AsynchronousFileChannel.open(file.toPath(), options);
|
||||
this.readpos = offset <= 0 ? 0 : offset;
|
||||
this.max = len <= 0 ? file.length() : len;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void completed(Integer result, ByteBuffer attachment) {
|
||||
//(Utility.now() + "---" + Thread.currentThread().getName() + "-----------" + file + "-------------------result: " + result + ", max = " + max + ", readpos = " + readpos + ", count = " + count + ", " + (hdwrite ? "正在写Header" : (read ? "准备读" : "准备写")));
|
||||
if (result < 0 || count >= max) {
|
||||
failed(null, attachment);
|
||||
return;
|
||||
}
|
||||
if (hdwrite && attachment.hasRemaining()) { //Header还没写完
|
||||
channel.write(attachment, attachment, this);
|
||||
return;
|
||||
}
|
||||
if (hdwrite) {
|
||||
//(Utility.now() + "---" + Thread.currentThread().getName() + "-----------" + file + "-------------------Header写入完毕, 准备读取文件.");
|
||||
hdwrite = false;
|
||||
read = true;
|
||||
result = 0;
|
||||
}
|
||||
if (read) {
|
||||
count += result;
|
||||
} else {
|
||||
readpos += result;
|
||||
}
|
||||
if (read && attachment.hasRemaining()) { //Buffer还没写完
|
||||
channel.write(attachment, attachment, this);
|
||||
return;
|
||||
}
|
||||
|
||||
if (read) {
|
||||
read = false;
|
||||
attachment.clear();
|
||||
filechannel.read(attachment, readpos, attachment, this);
|
||||
} else {
|
||||
read = true;
|
||||
if (count > max) {
|
||||
attachment.limit((int) (attachment.position() + max - count));
|
||||
}
|
||||
attachment.flip();
|
||||
if (attachment.hasRemaining()) {
|
||||
channel.write(attachment, attachment, this);
|
||||
} else {
|
||||
failed(null, attachment);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void failed(Throwable exc, ByteBuffer attachment) {
|
||||
channel.offerBuffer(attachment);
|
||||
finish(true);
|
||||
try {
|
||||
filechannel.close();
|
||||
} catch (IOException e) {
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// protected final class TransferFileHandler implements CompletionHandler<Integer, Void> {
|
||||
//
|
||||
// private final File file;
|
||||
//
|
||||
// private final AsynchronousFileChannel filechannel;
|
||||
//
|
||||
// private final long max; //需要读取的字节数, -1表示读到文件结尾
|
||||
//
|
||||
// private long count;//读取文件的字节数
|
||||
//
|
||||
// private long readpos = 0;
|
||||
//
|
||||
// private boolean hdwrite = true; //写入Header
|
||||
//
|
||||
// private boolean read = false;
|
||||
//
|
||||
// public TransferFileHandler(File file) throws IOException {
|
||||
// this.file = file;
|
||||
// this.filechannel = AsynchronousFileChannel.open(file.toPath(), options);
|
||||
// this.readpos = 0;
|
||||
// this.max = file.length();
|
||||
// }
|
||||
//
|
||||
// public TransferFileHandler(File file, long offset, long len) throws IOException {
|
||||
// this.file = file;
|
||||
// this.filechannel = AsynchronousFileChannel.open(file.toPath(), options);
|
||||
// this.readpos = offset <= 0 ? 0 : offset;
|
||||
// this.max = len <= 0 ? file.length() : len;
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public void completed(Integer result, Void attachment) {
|
||||
// //(Utility.now() + "---" + Thread.currentThread().getName() + "-----------" + file + "-------------------result: " + result + ", max = " + max + ", readpos = " + readpos + ", count = " + count + ", " + (hdwrite ? "正在写Header" : (read ? "准备读" : "准备写")));
|
||||
// if (result < 0 || count >= max) {
|
||||
// failed(null, attachment);
|
||||
// return;
|
||||
// }
|
||||
// if (hdwrite && attachment.hasRemaining()) { //Header还没写完
|
||||
// channel.write(attachment, attachment, this);
|
||||
// return;
|
||||
// }
|
||||
// if (hdwrite) {
|
||||
// //(Utility.now() + "---" + Thread.currentThread().getName() + "-----------" + file + "-------------------Header写入完毕, 准备读取文件.");
|
||||
// hdwrite = false;
|
||||
// read = true;
|
||||
// result = 0;
|
||||
// }
|
||||
// if (read) {
|
||||
// count += result;
|
||||
// } else {
|
||||
// readpos += result;
|
||||
// }
|
||||
// if (read && attachment.hasRemaining()) { //Buffer还没写完
|
||||
// channel.write(attachment, attachment, this);
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// if (read) {
|
||||
// read = false;
|
||||
// attachment.clear();
|
||||
// filechannel.read(attachment, readpos, attachment, this);
|
||||
// } else {
|
||||
// read = true;
|
||||
// if (count > max) {
|
||||
// attachment.limit((int) (attachment.position() + max - count));
|
||||
// }
|
||||
// attachment.flip();
|
||||
// if (attachment.hasRemaining()) {
|
||||
// channel.write(attachment, attachment, this);
|
||||
// } else {
|
||||
// failed(null, attachment);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public void failed(Throwable exc, Void attachment) {
|
||||
// finish(true);
|
||||
// try {
|
||||
// filechannel.close();
|
||||
// } catch (IOException e) {
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// }
|
||||
public static class HttpResponseConfig {
|
||||
|
||||
public String plainContentType;
|
||||
@@ -1279,15 +1297,15 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
|
||||
|
||||
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) {
|
||||
if (this.plainContentTypeBytes == null) {
|
||||
@@ -1297,12 +1315,12 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
|
||||
this.jsonContentType = jsonct;
|
||||
this.plainContentTypeBytes = ("Content-Type: " + plainct + "\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();
|
||||
plainLiveContentLengthMap.put(i, append(append(status200_server_live_Bytes, plainContentTypeBytes), lenbytes));
|
||||
plainCloseContentLengthMap.put(i, append(append(status200_server_close_Bytes, plainContentTypeBytes), lenbytes));
|
||||
jsonLiveContentLengthMap.put(i, append(append(status200_server_live_Bytes, jsonContentTypeBytes), lenbytes));
|
||||
jsonCloseContentLengthMap.put(i, append(append(status200_server_close_Bytes, jsonContentTypeBytes), lenbytes));
|
||||
plainLiveContentLengthArray[i] = append(append(status200_server_live_Bytes, plainContentTypeBytes), lenbytes);
|
||||
plainCloseContentLengthArray[i] = append(append(status200_server_close_Bytes, plainContentTypeBytes), lenbytes);
|
||||
jsonLiveContentLengthArray[i] = append(append(status200_server_live_Bytes, jsonContentTypeBytes), lenbytes);
|
||||
jsonCloseContentLengthArray[i] = append(append(status200_server_close_Bytes, jsonContentTypeBytes), lenbytes);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
|
||||
@@ -26,7 +26,7 @@ public class HttpResult<T> {
|
||||
public static final String SESSIONID_COOKIENAME = HttpRequest.SESSIONID_NAME;
|
||||
|
||||
@ConvertColumn(index = 1)
|
||||
protected int status = 0; //不设置则为 200
|
||||
protected int status = 200; //不设置则为 200
|
||||
|
||||
@ConvertColumn(index = 2)
|
||||
protected String contentType;
|
||||
@@ -110,7 +110,7 @@ public class HttpResult<T> {
|
||||
}
|
||||
|
||||
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() {
|
||||
|
||||
@@ -5,8 +5,11 @@
|
||||
*/
|
||||
package org.redkale.net.http;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.net.HttpCookie;
|
||||
import java.util.*;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.*;
|
||||
import javax.persistence.Transient;
|
||||
import org.redkale.convert.*;
|
||||
import org.redkale.convert.json.JsonConvert;
|
||||
|
||||
@@ -36,16 +39,56 @@ import org.redkale.convert.json.JsonConvert;
|
||||
*/
|
||||
public class HttpScope {
|
||||
|
||||
public static final Object NIL = new Object();
|
||||
|
||||
@ConvertColumn(index = 1)
|
||||
protected String referid;
|
||||
|
||||
@ConvertColumn(index = 2)
|
||||
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) {
|
||||
HttpScope rs = new HttpScope();
|
||||
rs.setReferid(template);
|
||||
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) {
|
||||
if (map == null) return this;
|
||||
if (this.attributes == null) this.attributes = new LinkedHashMap<>();
|
||||
@@ -54,6 +97,7 @@ public class HttpScope {
|
||||
}
|
||||
|
||||
public HttpScope attr(String name, Object value) {
|
||||
if (name == null || value == null) return this;
|
||||
if (this.attributes == null) this.attributes = new LinkedHashMap<>();
|
||||
this.attributes.put(name, value);
|
||||
return this;
|
||||
@@ -76,6 +120,52 @@ public class HttpScope {
|
||||
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() {
|
||||
return referid;
|
||||
}
|
||||
@@ -85,7 +175,24 @@ public class HttpScope {
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
@@ -80,15 +80,6 @@ public class HttpServer extends Server<String, HttpContext, HttpRequest, HttpRes
|
||||
return this.prepare.getFilters();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取HttpRender列表
|
||||
*
|
||||
* @return HttpRender列表
|
||||
*/
|
||||
public List<HttpRender> getHttpRenders() {
|
||||
return ((HttpPrepareServlet) this.prepare).renders;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取静态资源HttpServlet
|
||||
*
|
||||
@@ -324,9 +315,8 @@ public class HttpServer extends Server<String, HttpContext, HttpRequest, HttpRes
|
||||
String jsonContentType = null;
|
||||
HttpCookie defaultCookie = null;
|
||||
String remoteAddrHeader = null;
|
||||
boolean lazyHeaders = false;
|
||||
|
||||
if (config != null) {
|
||||
lazyHeaders = config.getBoolValue("lazy", false);
|
||||
AnyValue reqs = config.getAnyValue("request");
|
||||
if (reqs != null) {
|
||||
AnyValue raddr = reqs.getAnyValue("remoteaddr");
|
||||
@@ -424,6 +414,21 @@ public class HttpServer extends Server<String, HttpContext, HttpRequest, HttpRes
|
||||
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;
|
||||
|
||||
@@ -435,7 +440,7 @@ public class HttpServer extends Server<String, HttpContext, HttpRequest, HttpRes
|
||||
respConfig.defaultCookie = defaultCookie;
|
||||
respConfig.autoOptions = autoOptions;
|
||||
respConfig.dateSupplier = dateSupplier;
|
||||
respConfig.renders = ((HttpPrepareServlet) prepare).renders;
|
||||
respConfig.httpRender = httpRender;
|
||||
respConfig.init(config);
|
||||
|
||||
final HttpContextConfig contextConfig = new HttpContextConfig();
|
||||
@@ -454,9 +459,10 @@ public class HttpServer extends Server<String, HttpContext, HttpRequest, HttpRes
|
||||
contextConfig.readTimeoutSeconds = this.readTimeoutSeconds;
|
||||
contextConfig.writeTimeoutSeconds = this.writeTimeoutSeconds;
|
||||
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
|
||||
|
||||
@@ -248,7 +248,7 @@ public class HttpServlet extends Servlet<HttpContext, HttpRequest, HttpResponse>
|
||||
this.rpconly = rpconly;
|
||||
this.auth = auth;
|
||||
this.cacheseconds = cacheseconds;
|
||||
if (Utility.contains(name, '.', '*', '{', '[', '(', '|', '^', '$', '+', '?', '\\') || name.endsWith("/")) { //是否是正则表达式
|
||||
if (Utility.contains(name, '*', '{', '[', '(', '|', '^', '$', '+', '?', '\\') || name.endsWith("/")) { //是否是正则表达式
|
||||
this.modeOneCache = false;
|
||||
this.cache = cacheseconds > 0 ? new ConcurrentHashMap<>() : null;
|
||||
this.cacheHandler = cacheseconds > 0 ? (HttpResponse response, byte[] content) -> {
|
||||
|
||||
@@ -19,7 +19,6 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||
*
|
||||
* @author zhangjx
|
||||
*/
|
||||
@Deprecated
|
||||
@Inherited
|
||||
@Documented
|
||||
@Target({TYPE})
|
||||
|
||||
@@ -273,7 +273,11 @@ public final class MultiContext {
|
||||
buf[s++] = buf[i];
|
||||
}
|
||||
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;
|
||||
if (Arrays.equals(boundarray, buf)) {
|
||||
this.end = true;
|
||||
|
||||
@@ -23,7 +23,7 @@ import org.redkale.asm.Type;
|
||||
import org.redkale.convert.*;
|
||||
import org.redkale.convert.json.*;
|
||||
import org.redkale.mq.*;
|
||||
import org.redkale.net.Cryptor;
|
||||
import org.redkale.net.*;
|
||||
import org.redkale.net.sncp.Sncp;
|
||||
import org.redkale.service.*;
|
||||
import org.redkale.util.*;
|
||||
@@ -80,6 +80,8 @@ public final class Rest {
|
||||
@Retention(RUNTIME)
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断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
|
||||
*
|
||||
@@ -767,7 +781,13 @@ public final class Rest {
|
||||
if (!HttpServlet.class.isAssignableFrom(baseServletType)) throw new RuntimeException(baseServletType + " is not HttpServlet Class on createRestServlet");
|
||||
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.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 serviceDesc = Type.getDescriptor(serviceType);
|
||||
@@ -779,8 +799,11 @@ public final class Rest {
|
||||
final String typeDesc = Type.getDescriptor(java.lang.reflect.Type.class);
|
||||
final String jsonConvertDesc = Type.getDescriptor(JsonConvert.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 flipperDesc = Type.getDescriptor(Flipper.class);
|
||||
final String channelDesc = Type.getDescriptor(ChannelContext.class);
|
||||
final String httpServletName = HttpServlet.class.getName().replace('.', '/');
|
||||
final String actionEntryName = HttpServlet.ActionEntry.class.getName().replace('.', '/');
|
||||
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<>();
|
||||
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
|
||||
av0 = cw.visitAnnotation(Type.getDescriptor(RestDynSourceType.class), true);
|
||||
av0.visit("value", Type.getType(Type.getDescriptor(serviceType)));
|
||||
av0.visitEnd();
|
||||
}
|
||||
|
||||
boolean dynsimple = true;
|
||||
final List<MappingEntry> entrys = new ArrayList<>();
|
||||
final Map<String, org.redkale.util.Attribute> restAttributes = new LinkedHashMap<>();
|
||||
//获取所有可以转换成HttpMapping的方法
|
||||
@@ -918,6 +937,11 @@ public final class Rest {
|
||||
if (defmodulename.isEmpty() || (!pound && entrys.size() <= 2)) {
|
||||
for (MappingEntry entry : entrys) {
|
||||
String suburl = (catalog.isEmpty() ? "/" : ("/" + catalog + "/")) + (defmodulename.isEmpty() ? "" : (defmodulename + "/")) + entry.name;
|
||||
if ("//".equals(suburl)) {
|
||||
suburl = "/";
|
||||
} else if (suburl.length() > 2 && suburl.endsWith("/")) {
|
||||
suburl += "*";
|
||||
}
|
||||
urlpath += "," + 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);
|
||||
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);
|
||||
if (annpara != null) radix = annpara.radix();
|
||||
@@ -1181,7 +1218,7 @@ public final class Rest {
|
||||
} while ((loop = loop.getSuperclass()) != Object.class);
|
||||
}
|
||||
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<>();
|
||||
@@ -1196,6 +1233,7 @@ public final class Rest {
|
||||
}
|
||||
av0 = mv.visitAnnotation(mappingDesc, true);
|
||||
String url = (catalog.isEmpty() ? "/" : ("/" + catalog + "/")) + (defmodulename.isEmpty() ? "" : (defmodulename + "/")) + entry.name + (reqpath ? "/" : "");
|
||||
if ("//".equals(url)) url = "/";
|
||||
av0.visit("url", url);
|
||||
av0.visit("rpconly", entry.rpconly);
|
||||
av0.visit("auth", entry.auth);
|
||||
@@ -1303,8 +1341,9 @@ public final class Rest {
|
||||
RestURI annuri = (RestURI) ps[13];
|
||||
RestUserid userid = (RestUserid) ps[14];
|
||||
RestHeaders annheaders = (RestHeaders) ps[15];
|
||||
|
||||
java.lang.reflect.Type pgentype = (java.lang.reflect.Type) ps[16];
|
||||
RestParams annparams = (RestParams) 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 iscookie = anncookie != null; //是否取getCookie
|
||||
@@ -1343,6 +1382,11 @@ public final class Rest {
|
||||
mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getHeaders", "()Ljava/util/Map;", false);
|
||||
mv.visitVarInsn(ASTORE, 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
|
||||
if (ptype == String.class) {
|
||||
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.visitVarInsn(ASTORE, 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) {
|
||||
mv.visitVarInsn(ALOAD, 1);
|
||||
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)
|
||||
&& !HttpResult.class.isAssignableFrom(returnType) && !HttpScope.class.isAssignableFrom(returnType)) {
|
||||
mv.visitVarInsn(ASTORE, maxLocals);
|
||||
if (entry.contentLength > 0) {
|
||||
mv.visitVarInsn(ALOAD, 2); //response
|
||||
pushInt(mv, entry.contentLength);
|
||||
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.visitVarInsn(ALOAD, 2); //response
|
||||
mv.visitVarInsn(ALOAD, maxLocals);
|
||||
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finishJson", "(Ljava/lang/Object;)V", false);
|
||||
mv.visitInsn(RETURN);
|
||||
maxLocals++;
|
||||
} else {
|
||||
mv.visitVarInsn(ASTORE, maxLocals);
|
||||
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) {
|
||||
mv.visitVarInsn(ALOAD, 0);
|
||||
mv.visitFieldInsn(GETFIELD, newDynName, REST_JSONCONVERT_FIELD_PREFIX + restConverts.size(), jsonConvertDesc);
|
||||
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 {
|
||||
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);
|
||||
maxLocals++;
|
||||
@@ -2075,6 +2118,12 @@ public final class Rest {
|
||||
mv.visitEnd();
|
||||
}
|
||||
|
||||
{ //RestDyn
|
||||
av0 = cw.visitAnnotation(Type.getDescriptor(RestDyn.class), true);
|
||||
av0.visit("simple", (Boolean) dynsimple);
|
||||
av0.visitEnd();
|
||||
}
|
||||
|
||||
cw.visitEnd();
|
||||
newLoader.addClass(newDynName.replace('/', '.'), cw.toByteArray());
|
||||
try {
|
||||
@@ -2213,7 +2262,6 @@ public final class Rest {
|
||||
this.rpconly = serrpconly || mapping.rpconly();
|
||||
this.actionid = mapping.actionid();
|
||||
this.cacheseconds = mapping.cacheseconds();
|
||||
this.contentLength = mapping.length();
|
||||
this.comment = mapping.comment();
|
||||
boolean pound = false;
|
||||
Parameter[] params = method.getParameters();
|
||||
@@ -2229,8 +2277,6 @@ public final class Rest {
|
||||
this.newActionClassName = "_Dyn_" + this.newMethodName + "_ActionHttpServlet";
|
||||
}
|
||||
|
||||
public final int contentLength;
|
||||
|
||||
public final int methodidx; // _paramtypes 的下标,从0开始
|
||||
|
||||
public final Method mappingMethod;
|
||||
|
||||
@@ -74,10 +74,6 @@ public @interface RestMapping {
|
||||
*/
|
||||
int cacheseconds() default 0;
|
||||
|
||||
//json结果的长度, 临时功能, 对应@HttpMapping.length
|
||||
@Deprecated
|
||||
int length() default 0;
|
||||
|
||||
/**
|
||||
* 允许方法(不区分大小写),如:GET/POST/PUT,为空表示允许所有方法, 对应@HttpMapping.methods
|
||||
*
|
||||
|
||||
28
src/org/redkale/net/http/RestParams.java
Normal file
28
src/org/redkale/net/http/RestParams.java
Normal 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<String, String>字段
|
||||
*
|
||||
* <p>
|
||||
* 详情见: https://redkale.org
|
||||
*
|
||||
* @author zhangjx
|
||||
*
|
||||
* @since 2.4.0
|
||||
*/
|
||||
@Inherited
|
||||
@Documented
|
||||
@Target({PARAMETER, FIELD})
|
||||
@Retention(RUNTIME)
|
||||
public @interface RestParams {
|
||||
|
||||
}
|
||||
@@ -22,6 +22,7 @@ import org.redkale.source.*;
|
||||
import org.redkale.util.*;
|
||||
|
||||
/**
|
||||
* 注: 部署了WebSocketNodeService就必然要配置SNCP协议的Server,不然无法做到WebSocketNode.sendMessage方法的有效性
|
||||
*
|
||||
* <p>
|
||||
* 详情见: https://redkale.org
|
||||
@@ -284,7 +285,7 @@ public abstract class WebSocketNode {
|
||||
CompletableFuture<Boolean> localFuture = null;
|
||||
if (this.localEngine != null) localFuture = CompletableFuture.completedFuture(localEngine.existsLocalWebSocket(userid));
|
||||
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就不会有分布式节点
|
||||
return localFuture == null ? CompletableFuture.completedFuture(false) : localFuture;
|
||||
}
|
||||
@@ -317,7 +318,7 @@ public abstract class WebSocketNode {
|
||||
public CompletableFuture<Boolean> existsWebSocket(final WebSocketUserAddress userAddress) {
|
||||
if (this.localEngine != null && localEngine.existsLocalWebSocket(userAddress.userid())) return CompletableFuture.completedFuture(true);
|
||||
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就不会有分布式节点
|
||||
return CompletableFuture.completedFuture(false);
|
||||
}
|
||||
@@ -365,7 +366,7 @@ public abstract class WebSocketNode {
|
||||
CompletableFuture<Integer> localFuture = null;
|
||||
if (this.localEngine != null) localFuture = CompletableFuture.completedFuture(localEngine.forceCloseLocalWebSocket(userAddress == null ? userid : userAddress.userid()));
|
||||
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就不会有分布式节点
|
||||
return localFuture == null ? CompletableFuture.completedFuture(0) : localFuture;
|
||||
}
|
||||
@@ -629,7 +630,7 @@ public abstract class WebSocketNode {
|
||||
CompletableFuture<Integer> localFuture = null;
|
||||
if (this.localEngine != null) localFuture = localEngine.sendLocalMessage(message, last, userid);
|
||||
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就不会有分布式节点
|
||||
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);
|
||||
}
|
||||
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就不会有分布式节点
|
||||
return CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY);
|
||||
}
|
||||
@@ -954,7 +955,7 @@ public abstract class WebSocketNode {
|
||||
CompletableFuture<Integer> localFuture = null;
|
||||
if (this.localEngine != null) localFuture = localEngine.sendLocalAction(action, userid);
|
||||
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就不会有分布式节点
|
||||
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);
|
||||
}
|
||||
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就不会有分布式节点
|
||||
return CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY);
|
||||
}
|
||||
|
||||
@@ -93,11 +93,11 @@ public class WebSocketReadHandler implements CompletionHandler<Integer, ByteBuff
|
||||
* | Payload Data continued |
|
||||
* +-----------------------------------------------------------------------+
|
||||
*
|
||||
* @param realbuf
|
||||
* @param realbuf ByteBuffer
|
||||
*
|
||||
*/
|
||||
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;
|
||||
ByteBuffer buffer = realbuf;
|
||||
byte frameOpcode;
|
||||
|
||||
@@ -59,7 +59,8 @@ public abstract class AbstractService implements Service {
|
||||
if (workExecutor != null) return workExecutor;
|
||||
Thread thread = Thread.currentThread();
|
||||
if (thread instanceof WorkThread) {
|
||||
return ((WorkThread) thread).getWorkExecutor();
|
||||
ExecutorService e = ((WorkThread) thread).getWorkExecutor();
|
||||
if (e != null) return e;
|
||||
}
|
||||
return ForkJoinPool.commonPool();
|
||||
}
|
||||
|
||||
@@ -5,12 +5,14 @@
|
||||
*/
|
||||
package org.redkale.service;
|
||||
|
||||
import java.io.*;
|
||||
import java.lang.annotation.*;
|
||||
import static java.lang.annotation.ElementType.*;
|
||||
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||
import java.lang.reflect.*;
|
||||
import java.util.*;
|
||||
import java.util.function.BiFunction;
|
||||
import static org.redkale.boot.Application.*;
|
||||
|
||||
/**
|
||||
* 用于定义错误码的注解 <br>
|
||||
@@ -50,7 +52,7 @@ public @interface RetLabel {
|
||||
public static abstract class RetLoader {
|
||||
|
||||
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);
|
||||
Iterator<RetInfoTransfer> it = loader.iterator();
|
||||
RetInfoTransfer func = it.hasNext() ? it.next() : null;
|
||||
@@ -67,15 +69,40 @@ public @interface RetLabel {
|
||||
continue;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public static Map<Integer, String> load(Class clazz) {
|
||||
return loadMap(clazz).computeIfAbsent("", (k) -> new HashMap<>());
|
||||
}
|
||||
// @Deprecated
|
||||
// public static Map<Integer, String> load(Class clazz) {
|
||||
// return loadMap(clazz).computeIfAbsent("", (k) -> new LinkedHashMap<>());
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,6 @@ import org.redkale.util.*;
|
||||
/**
|
||||
* CacheSource的默认实现--内存缓存
|
||||
*
|
||||
* @param <V> value类型
|
||||
* <p>
|
||||
* 详情见: https://redkale.org
|
||||
*
|
||||
@@ -32,7 +31,7 @@ import org.redkale.util.*;
|
||||
@AutoLoad(false)
|
||||
@SuppressWarnings("unchecked")
|
||||
@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>>() {
|
||||
}.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);
|
||||
};
|
||||
|
||||
@RpcRemote
|
||||
protected CacheSource<V> remoteSource;
|
||||
|
||||
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
|
||||
public final String getType() {
|
||||
return "memory";
|
||||
@@ -110,17 +95,6 @@ public final class CacheMemorySource<V extends Object> extends AbstractService i
|
||||
if (this.convert == null) this.convert = JsonConvert.root();
|
||||
final CacheMemorySource self = this;
|
||||
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");
|
||||
if (expireHandlerClass != null) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
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 {
|
||||
@@ -222,44 +174,43 @@ public final class CacheMemorySource<V extends Object> extends AbstractService i
|
||||
|
||||
CacheMemorySource source = new CacheMemorySource();
|
||||
source.defaultConvert = JsonFactory.root().getConvert();
|
||||
source.initValueType(String.class); //value用String类型
|
||||
source.initTransient(false);
|
||||
|
||||
source.init(conf);
|
||||
|
||||
System.out.println("------------------------------------");
|
||||
source.remove("key1");
|
||||
source.remove("key2");
|
||||
source.remove("300");
|
||||
source.set("key1", "value1");
|
||||
source.setString("key1", "value1");
|
||||
source.setString("keystr1", "strvalue1");
|
||||
source.setLong("keylong1", 333L);
|
||||
source.set("300", "4000");
|
||||
source.getAndRefresh("key1", 3500);
|
||||
System.out.println("[有值] 300 GET : " + source.get("300"));
|
||||
System.out.println("[有值] key1 GET : " + source.get("key1"));
|
||||
System.out.println("[无值] key2 GET : " + source.get("key2"));
|
||||
source.setString("300", "4000");
|
||||
source.getStringAndRefresh("key1", 3500);
|
||||
System.out.println("[有值] 300 GET : " + source.get("300", String.class));
|
||||
System.out.println("[有值] key1 GET : " + source.get("key1", String.class));
|
||||
System.out.println("[无值] key2 GET : " + source.get("key2", String.class));
|
||||
System.out.println("[有值] keylong1 GET : " + source.getLong("keylong1", 0L));
|
||||
System.out.println("[有值] key1 EXISTS : " + source.exists("key1"));
|
||||
System.out.println("[无值] key2 EXISTS : " + source.exists("key2"));
|
||||
|
||||
source.remove("keys3");
|
||||
source.appendListItem("keys3", "vals1");
|
||||
source.appendListItem("keys3", "vals2");
|
||||
source.appendStringListItem("keys3", "vals1");
|
||||
source.appendStringListItem("keys3", "vals2");
|
||||
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"));
|
||||
source.removeListItem("keys3", "vals1");
|
||||
System.out.println("[一值] keys3 VALUES : " + source.getCollection("keys3"));
|
||||
source.getCollectionAndRefresh("keys3", 3000);
|
||||
source.removeStringListItem("keys3", "vals1");
|
||||
System.out.println("[一值] keys3 VALUES : " + source.getStringCollection("keys3"));
|
||||
source.getStringCollectionAndRefresh("keys3", 3000);
|
||||
|
||||
source.remove("sets3");
|
||||
source.appendSetItem("sets3", "setvals1");
|
||||
source.appendSetItem("sets3", "setvals2");
|
||||
source.appendSetItem("sets3", "setvals1");
|
||||
System.out.println("[两值] sets3 VALUES : " + source.getCollection("sets3"));
|
||||
source.appendStringSetItem("sets3", "setvals1");
|
||||
source.appendStringSetItem("sets3", "setvals2");
|
||||
source.appendStringSetItem("sets3", "setvals1");
|
||||
System.out.println("[两值] sets3 VALUES : " + source.getStringCollection("sets3"));
|
||||
System.out.println("[有值] sets3 EXISTS : " + source.exists("sets3"));
|
||||
source.removeSetItem("sets3", "setvals1");
|
||||
System.out.println("[一值] sets3 VALUES : " + source.getCollection("sets3"));
|
||||
source.removeStringSetItem("sets3", "setvals1");
|
||||
System.out.println("[一值] sets3 VALUES : " + source.getStringCollection("sets3"));
|
||||
System.out.println("sets3 大小 : " + source.getCollectionSize("sets3"));
|
||||
System.out.println("all keys: " + source.queryKeys());
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
//----------- hxxx --------------
|
||||
|
||||
@Override
|
||||
@@ -523,19 +475,13 @@ public final class CacheMemorySource<V extends Object> extends AbstractService i
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public V get(String key) {
|
||||
public <T> T get(final String key, final Type type) {
|
||||
if (key == null) return null;
|
||||
CacheEntry entry = container.get(key);
|
||||
if (entry == null || entry.isExpired()) return null;
|
||||
if (entry.isListCacheType()) return (V) (entry.listValue == null ? null : new ArrayList(entry.listValue));
|
||||
if (entry.isSetCacheType()) return (V) (entry.csetValue == null ? null : new HashSet(entry.csetValue));
|
||||
return (V) entry.objectValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T get(final String key, final Type type) {
|
||||
return (T) get(key);
|
||||
if (entry.isListCacheType()) return (T) (entry.listValue == null ? null : new ArrayList(entry.listValue));
|
||||
if (entry.isSetCacheType()) return (T) (entry.csetValue == null ? null : new HashSet(entry.csetValue));
|
||||
return (T) entry.objectValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -656,14 +602,9 @@ public final class CacheMemorySource<V extends Object> extends AbstractService i
|
||||
}
|
||||
|
||||
//----------- hxxx --------------
|
||||
@Override
|
||||
public CompletableFuture<V> getAsync(final String key) {
|
||||
return CompletableFuture.supplyAsync(() -> get(key), getExecutor());
|
||||
}
|
||||
|
||||
@Override
|
||||
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
|
||||
@@ -677,21 +618,15 @@ public final class CacheMemorySource<V extends Object> extends AbstractService i
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public V getAndRefresh(String key, final int expireSeconds) {
|
||||
public <T> T getAndRefresh(final String key, final int expireSeconds, final Type type) {
|
||||
if (key == null) return null;
|
||||
CacheEntry entry = container.get(key);
|
||||
if (entry == null || entry.isExpired()) return null;
|
||||
entry.lastAccessed = (int) (System.currentTimeMillis() / 1000);
|
||||
entry.expireSeconds = expireSeconds;
|
||||
if (entry.isListCacheType()) return (V) (entry.listValue == null ? null : new ArrayList(entry.listValue));
|
||||
if (entry.isSetCacheType()) return (V) (entry.csetValue == null ? null : new HashSet(entry.csetValue));
|
||||
return (V) entry.objectValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T getAndRefresh(final String key, final int expireSeconds, final Type type) {
|
||||
return (T) getAndRefresh(key, expireSeconds);
|
||||
if (entry.isListCacheType()) return (T) (entry.listValue == null ? null : new ArrayList(entry.listValue));
|
||||
if (entry.isSetCacheType()) return (T) (entry.csetValue == null ? null : new HashSet(entry.csetValue));
|
||||
return (T) entry.objectValue;
|
||||
}
|
||||
|
||||
@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
|
||||
public <T> CompletableFuture<T> getAndRefreshAsync(final String key, final int expireSeconds, final Type type) {
|
||||
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
|
||||
public <T> void set(String key, Convert convert, T 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);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> setAsync(String key, V value) {
|
||||
return CompletableFuture.runAsync(() -> set(key, value), getExecutor()).whenComplete(futureCompleteConsumer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> CompletableFuture<Void> setAsync(String key, Convert convert, T value) {
|
||||
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
|
||||
public <T> void set(final int expireSeconds, String key, Convert convert, T 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);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> setAsync(int expireSeconds, String key, V value) {
|
||||
return CompletableFuture.runAsync(() -> set(expireSeconds, key, value), getExecutor()).whenComplete(futureCompleteConsumer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> CompletableFuture<Void> setAsync(int expireSeconds, String key, Convert convert, T value) {
|
||||
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);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<V> getCollection(final String key) {
|
||||
return (Collection<V>) get(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Collection<T> getCollection(final String key, final Type componentType) {
|
||||
return (Collection<T>) get(key);
|
||||
return (Collection<T>) get(key, componentType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Map<String, Collection<T>> getCollectionMap(final boolean set, final Type componentType, final String... keys) {
|
||||
Map<String, Collection<T>> map = new HashMap<>();
|
||||
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);
|
||||
}
|
||||
return map;
|
||||
@@ -1006,14 +911,14 @@ public final class CacheMemorySource<V extends Object> extends AbstractService i
|
||||
|
||||
@Override
|
||||
public Collection<String> getStringCollection(final String key) {
|
||||
return (Collection<String>) get(key);
|
||||
return (Collection<String>) get(key, String.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Collection<String>> getStringCollectionMap(final boolean set, final String... keys) {
|
||||
Map<String, Collection<String>> map = new HashMap<>();
|
||||
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);
|
||||
}
|
||||
return map;
|
||||
@@ -1023,7 +928,7 @@ public final class CacheMemorySource<V extends Object> extends AbstractService i
|
||||
public Map<String, Long> getLongMap(final String... keys) {
|
||||
Map<String, Long> map = new LinkedHashMap<>();
|
||||
for (String key : keys) {
|
||||
Number n = (Number) get(key);
|
||||
Number n = (Number) get(key, long.class);
|
||||
map.put(key, n == null ? null : n.longValue());
|
||||
}
|
||||
return map;
|
||||
@@ -1034,7 +939,7 @@ public final class CacheMemorySource<V extends Object> extends AbstractService i
|
||||
Long[] rs = new Long[keys.length];
|
||||
int index = -1;
|
||||
for (String key : keys) {
|
||||
Number n = (Number) get(key);
|
||||
Number n = (Number) get(key, long.class);
|
||||
rs[++index] = n == null ? null : n.longValue();
|
||||
}
|
||||
return rs;
|
||||
@@ -1054,7 +959,7 @@ public final class CacheMemorySource<V extends Object> extends AbstractService i
|
||||
public Map<String, String> getStringMap(final String... keys) {
|
||||
Map<String, String> map = new LinkedHashMap<>();
|
||||
for (String key : keys) {
|
||||
Object n = get(key);
|
||||
Object n = get(key, String.class);
|
||||
map.put(key, n == null ? null : n.toString());
|
||||
}
|
||||
return map;
|
||||
@@ -1065,7 +970,7 @@ public final class CacheMemorySource<V extends Object> extends AbstractService i
|
||||
String[] rs = new String[keys.length];
|
||||
int index = -1;
|
||||
for (String key : keys) {
|
||||
Object n = get(key);
|
||||
Object n = get(key, String.class);
|
||||
rs[++index] = n == null ? null : n.toString();
|
||||
}
|
||||
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) {
|
||||
Map<String, T> map = new LinkedHashMap<>();
|
||||
for (String key : keys) {
|
||||
map.put(key, (T) get(key));
|
||||
map.put(key, (T) get(key, componentType));
|
||||
}
|
||||
return map;
|
||||
}
|
||||
@@ -1097,31 +1002,21 @@ public final class CacheMemorySource<V extends Object> extends AbstractService i
|
||||
|
||||
@Override
|
||||
public Collection<Long> getLongCollection(final String key) {
|
||||
return (Collection<Long>) get(key);
|
||||
return (Collection<Long>) get(key, long.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Collection<Long>> getLongCollectionMap(final boolean set, final String... keys) {
|
||||
Map<String, Collection<Long>> map = new HashMap<>();
|
||||
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);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Collection<V>> getCollectionAsync(final String key) {
|
||||
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) {
|
||||
public <T> CompletableFuture<Map<String, Collection<T>>> getCollectionMapAsync(boolean set, Type componentType, String... keys) {
|
||||
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());
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> CompletableFuture<Collection<T>> getCollectionAsync(String key, Type componentType) {
|
||||
return CompletableFuture.supplyAsync(() -> getCollection(key, componentType), getExecutor());
|
||||
}
|
||||
|
||||
@Override
|
||||
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();
|
||||
}
|
||||
|
||||
@@ -1156,11 +1056,6 @@ public final class CacheMemorySource<V extends Object> extends AbstractService i
|
||||
return CompletableFuture.supplyAsync(() -> getCollectionSize(key), getExecutor());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<V> getCollectionAndRefresh(final String key, final int expireSeconds) {
|
||||
return (Collection<V>) getAndRefresh(key, expireSeconds);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Collection<T> getCollectionAndRefresh(final String key, final int expireSeconds, final Type componentType) {
|
||||
return (Collection<T>) getAndRefresh(key, expireSeconds, componentType);
|
||||
@@ -1168,26 +1063,15 @@ public final class CacheMemorySource<V extends Object> extends AbstractService i
|
||||
|
||||
@Override
|
||||
public Collection<String> getStringCollectionAndRefresh(final String key, final int expireSeconds) {
|
||||
return (Collection<String>) getAndRefresh(key, expireSeconds);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean existsSetItem(final String key, final V value) {
|
||||
Collection<V> list = getCollection(key);
|
||||
return list != null && list.contains(value);
|
||||
return (Collection<String>) getAndRefresh(key, expireSeconds, String.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
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);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Boolean> existsSetItemAsync(final String key, final V value) {
|
||||
return CompletableFuture.supplyAsync(() -> existsSetItem(key, value), getExecutor());
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> CompletableFuture<Boolean> existsSetItemAsync(final String key, final Type type, final T value) {
|
||||
return CompletableFuture.supplyAsync(() -> existsSetItem(key, type, value), getExecutor());
|
||||
@@ -1216,13 +1100,8 @@ public final class CacheMemorySource<V extends Object> extends AbstractService i
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Long> getLongCollectionAndRefresh(final String key, final int expireSeconds) {
|
||||
return (Collection<Long>) getAndRefresh(key, expireSeconds);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Collection<V>> getCollectionAndRefreshAsync(final String key, final int expireSeconds) {
|
||||
return CompletableFuture.supplyAsync(() -> getCollectionAndRefresh(key, expireSeconds), getExecutor());
|
||||
public Collection<Long> getLongCollectionAndRefresh(String key, int expireSeconds) {
|
||||
return (Collection<Long>) getAndRefresh(key, expireSeconds, long.class);
|
||||
}
|
||||
|
||||
@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
|
||||
public <T> void appendListItem(String key, Type componentType, T 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);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> appendListItemAsync(final String key, final V value) {
|
||||
return CompletableFuture.runAsync(() -> appendListItem(key, value), getExecutor()).whenComplete(futureCompleteConsumer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> CompletableFuture<Void> appendListItemAsync(final String key, final Type componentType, final T value) {
|
||||
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);
|
||||
}
|
||||
|
||||
@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
|
||||
public <T> int removeListItem(String key, final Type componentType, T value) {
|
||||
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;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Integer> removeListItemAsync(final String key, final V value) {
|
||||
return CompletableFuture.supplyAsync(() -> removeListItem(key, value), getExecutor()).whenComplete(futureCompleteConsumer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> CompletableFuture<Integer> removeListItemAsync(final String key, final Type componentType, T value) {
|
||||
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
|
||||
public <T> void appendSetItem(String key, final Type componentType, T 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);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> appendSetItemAsync(final String key, final V value) {
|
||||
return CompletableFuture.runAsync(() -> appendSetItem(key, value), getExecutor()).whenComplete(futureCompleteConsumer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> CompletableFuture<Void> appendSetItemAsync(final String key, final Type componentType, T value) {
|
||||
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);
|
||||
}
|
||||
|
||||
@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
|
||||
public <T> int removeSetItem(String key, Type type, T value) {
|
||||
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;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Integer> removeSetItemAsync(final String key, final V value) {
|
||||
return CompletableFuture.supplyAsync(() -> removeSetItem(key, value), getExecutor()).whenComplete(futureCompleteConsumer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> CompletableFuture<Integer> removeSetItemAsync(final String key, final Type componentType, final T value) {
|
||||
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) {
|
||||
return CompletableFuture.supplyAsync(() -> spopLongSetItem(key, count), getExecutor()).whenComplete(futureCompleteConsumer);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -9,7 +9,6 @@ import java.io.Serializable;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.function.Function;
|
||||
import org.redkale.convert.*;
|
||||
import org.redkale.convert.json.JsonFactory;
|
||||
import org.redkale.util.*;
|
||||
@@ -21,22 +20,16 @@ import org.redkale.util.*;
|
||||
* Long统一用setLong、getLong、incr等系列方法。<br>
|
||||
* 其他则供自定义数据类型使用。
|
||||
*
|
||||
* @param <V> value的类型
|
||||
* param V value的类型 移除 @2.4.0
|
||||
* <p>
|
||||
* 详情见: https://redkale.org
|
||||
*
|
||||
* @author zhangjx
|
||||
*/
|
||||
public interface CacheSource<V extends Object> {
|
||||
public interface CacheSource {
|
||||
|
||||
public String getType();
|
||||
|
||||
@Deprecated
|
||||
public void initValueType(Type valueType);
|
||||
|
||||
@Deprecated
|
||||
public void initTransient(boolean flag);
|
||||
|
||||
//ServiceLoader时判断配置是否符合当前实现类
|
||||
public boolean match(AnyValue config);
|
||||
|
||||
@@ -46,36 +39,10 @@ public interface CacheSource<V extends Object> {
|
||||
|
||||
public boolean exists(final String key);
|
||||
|
||||
@Deprecated
|
||||
public V get(final String key);
|
||||
|
||||
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);
|
||||
|
||||
@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 --------------
|
||||
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);
|
||||
|
||||
@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 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 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);
|
||||
|
||||
@Deprecated
|
||||
public Collection<V> getCollection(final String key);
|
||||
|
||||
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 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);
|
||||
|
||||
@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> 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);
|
||||
|
||||
@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);
|
||||
|
||||
@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 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 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);
|
||||
|
||||
@Deprecated
|
||||
public CompletableFuture<Collection<V>> getCollectionAsync(final String key);
|
||||
|
||||
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 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);
|
||||
|
||||
@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<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<Integer> removeListItemAsync(final String key, final Type componentType, final T value);
|
||||
|
||||
25
src/org/redkale/source/CacheSourceLoader.java
Normal file
25
src/org/redkale/source/CacheSourceLoader.java
Normal 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();
|
||||
}
|
||||
@@ -74,6 +74,19 @@ public class ColumnValue {
|
||||
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} 操作
|
||||
*
|
||||
@@ -85,7 +98,20 @@ public class ColumnValue {
|
||||
public static ColumnValue dec(String column, Serializable 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} 操作
|
||||
*
|
||||
@@ -109,7 +135,7 @@ public class ColumnValue {
|
||||
public static ColumnValue div(String column, Serializable value) {
|
||||
return new ColumnValue(column, DIV, value);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 返回 {column} = {column} % {value} 操作
|
||||
*
|
||||
@@ -118,11 +144,10 @@ public class ColumnValue {
|
||||
*
|
||||
* @return ColumnValue
|
||||
*/
|
||||
//不常用,防止开发者容易在mov时误输入mod
|
||||
//不常用,防止开发者容易在mov时误输入mod
|
||||
// public static ColumnValue mod(String column, Serializable value) {
|
||||
// return new ColumnValue(column, MOD, value);
|
||||
// }
|
||||
|
||||
/**
|
||||
* 返回 {column} = {column} & {value} 操作
|
||||
*
|
||||
|
||||
@@ -13,7 +13,7 @@ import java.util.concurrent.*;
|
||||
import java.util.concurrent.atomic.*;
|
||||
import java.util.function.*;
|
||||
import java.util.logging.Level;
|
||||
import org.redkale.net.AsyncGroup;
|
||||
import org.redkale.net.*;
|
||||
import org.redkale.service.Local;
|
||||
import org.redkale.util.*;
|
||||
|
||||
@@ -29,7 +29,7 @@ import org.redkale.util.*;
|
||||
@AutoLoad(false)
|
||||
@SuppressWarnings("unchecked")
|
||||
@ResourceType(DataSource.class)
|
||||
public class DataJdbcSource extends DataSqlSource<Connection> {
|
||||
public class DataJdbcSource extends DataSqlSource {
|
||||
|
||||
public DataJdbcSource(String unitName, URL persistxml, Properties readprop, Properties writeprop) {
|
||||
super(unitName, persistxml, readprop, writeprop);
|
||||
@@ -46,7 +46,7 @@ public class DataJdbcSource extends DataSqlSource<Connection> {
|
||||
}
|
||||
|
||||
@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);
|
||||
}
|
||||
|
||||
@@ -256,7 +256,7 @@ public class DataJdbcSource extends DataSqlSource<Connection> {
|
||||
}
|
||||
|
||||
@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;
|
||||
try {
|
||||
conn = writePool.poll();
|
||||
@@ -471,7 +471,7 @@ public class DataJdbcSource extends DataSqlSource<Connection> {
|
||||
}
|
||||
|
||||
@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;
|
||||
try {
|
||||
conn = readPool.poll();
|
||||
|
||||
@@ -11,7 +11,7 @@ import java.sql.ResultSet;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.function.*;
|
||||
import org.redkale.net.AsyncGroup;
|
||||
import org.redkale.net.*;
|
||||
import org.redkale.service.Local;
|
||||
import org.redkale.util.*;
|
||||
|
||||
@@ -33,7 +33,7 @@ import org.redkale.util.*;
|
||||
@AutoLoad(false)
|
||||
@SuppressWarnings("unchecked")
|
||||
@ResourceType(DataSource.class)
|
||||
public class DataMemorySource extends DataSqlSource<Void> {
|
||||
public class DataMemorySource extends DataSqlSource {
|
||||
|
||||
public DataMemorySource(String unitName, URL persistxml, Properties readprop, Properties writeprop) {
|
||||
super(unitName, persistxml, readprop, writeprop);
|
||||
@@ -80,7 +80,7 @@ public class DataMemorySource extends DataSqlSource<Void> {
|
||||
}
|
||||
|
||||
@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;
|
||||
}
|
||||
|
||||
@@ -105,7 +105,7 @@ public class DataMemorySource extends DataSqlSource<Void> {
|
||||
}
|
||||
|
||||
@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);
|
||||
}
|
||||
|
||||
@@ -135,7 +135,7 @@ public class DataMemorySource extends DataSqlSource<Void> {
|
||||
}
|
||||
|
||||
@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);
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -5,6 +5,8 @@
|
||||
*/
|
||||
package org.redkale.source;
|
||||
|
||||
import org.redkale.util.AnyValue;
|
||||
|
||||
/**
|
||||
* 自定义的DataSource加载器
|
||||
*
|
||||
@@ -13,10 +15,11 @@ package org.redkale.source;
|
||||
* 详情见: https://redkale.org
|
||||
*
|
||||
* @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();
|
||||
}
|
||||
@@ -54,6 +54,9 @@ public final class DataSources {
|
||||
|
||||
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() {
|
||||
}
|
||||
|
||||
@@ -88,26 +91,33 @@ public final class DataSources {
|
||||
final String url = readprop.getProperty(JDBC_URL);
|
||||
String dbtype = null;
|
||||
{
|
||||
/* jdbc:mysql:// jdbc:microsoft:sqlserver:// 取://之前的到最后一个:之间的字符串 */
|
||||
int pos = url.indexOf("://");
|
||||
if (pos > 0) {
|
||||
String url0 = url.substring(0, pos);
|
||||
pos = url0.lastIndexOf(':');
|
||||
if (pos > 0) dbtype = url0.substring(pos + 1);
|
||||
} 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 (url.startsWith("http://") || url.startsWith("https://")) { //elasticsearch or opensearch
|
||||
dbtype = "search";
|
||||
} else {
|
||||
/* jdbc:mysql:// jdbc:microsoft:sqlserver:// 取://之前的到最后一个:之间的字符串 */
|
||||
int pos = url.indexOf("://");
|
||||
if (pos > 0) {
|
||||
String url0 = url.substring(0, pos);
|
||||
pos = url0.lastIndexOf(':');
|
||||
if (pos > 0) dbtype = url0.substring(pos + 1);
|
||||
} 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);
|
||||
|
||||
Iterator<SourceLoader> it = ServiceLoader.load(SourceLoader.class).iterator();
|
||||
Iterator<DataSourceLoader> it = ServiceLoader.load(DataSourceLoader.class).iterator();
|
||||
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()) {
|
||||
SourceLoader loader = it.next();
|
||||
if (dbtype.equalsIgnoreCase(loader.dbtype())) {
|
||||
dsClass = loader.dataSourceClass();
|
||||
DataSourceLoader loader = it.next();
|
||||
if (loader != null && loader.match(lc)) {
|
||||
dsClass = loader.sourceClass();
|
||||
if (dsClass != null) break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ import org.redkale.service.*;
|
||||
import static org.redkale.source.DataSources.*;
|
||||
import org.redkale.util.*;
|
||||
import static org.redkale.boot.Application.RESNAME_APP_GROUP;
|
||||
import org.redkale.net.*;
|
||||
|
||||
/**
|
||||
* DataSource的SQL抽象实现类 <br>
|
||||
@@ -28,13 +29,12 @@ import static org.redkale.boot.Application.RESNAME_APP_GROUP;
|
||||
* 详情见: https://redkale.org
|
||||
*
|
||||
* @author zhangjx
|
||||
* @param <DBChannel> 数据库连接
|
||||
*/
|
||||
@Local
|
||||
@AutoLoad(false)
|
||||
@SuppressWarnings("unchecked")
|
||||
@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);
|
||||
|
||||
@@ -50,9 +50,9 @@ public abstract class DataSqlSource<DBChannel> extends AbstractService implement
|
||||
|
||||
protected boolean cacheForbidden;
|
||||
|
||||
protected PoolSource<DBChannel> readPool;
|
||||
protected PoolSource readPool;
|
||||
|
||||
protected PoolSource<DBChannel> writePool;
|
||||
protected PoolSource writePool;
|
||||
|
||||
@Resource(name = RESNAME_APP_GROUP)
|
||||
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)));
|
||||
if (readprop != writeprop) maxconns = 0;
|
||||
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;
|
||||
this.readPool = createPoolSource(this, this.asyncGroup, "read", queue, semaphore, readprop);
|
||||
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 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);
|
||||
@@ -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> 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);
|
||||
@@ -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> 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);
|
||||
@@ -222,12 +222,12 @@ public abstract class DataSqlSource<DBChannel> extends AbstractService implement
|
||||
}
|
||||
|
||||
@Local
|
||||
public PoolSource<DBChannel> getReadPoolSource() {
|
||||
public PoolSource getReadPoolSource() {
|
||||
return readPool;
|
||||
}
|
||||
|
||||
@Local
|
||||
public PoolSource<DBChannel> getWritePoolSource() {
|
||||
public PoolSource getWritePoolSource() {
|
||||
return writePool;
|
||||
}
|
||||
|
||||
@@ -701,7 +701,7 @@ public abstract class DataSqlSource<DBChannel> extends AbstractService implement
|
||||
final Class<T> clazz = (Class<T>) entitys[0].getClass();
|
||||
final EntityInfo<T> info = loadEntityInfo(clazz);
|
||||
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) {
|
||||
futureCompleteConsumer.accept(rs, t);
|
||||
} else {
|
||||
@@ -712,6 +712,11 @@ public abstract class DataSqlSource<DBChannel> extends AbstractService implement
|
||||
|
||||
@Override
|
||||
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);
|
||||
CompletableFuture future = checkEntity("update", true, entitys);
|
||||
if (future != null) return future;
|
||||
@@ -720,14 +725,14 @@ public abstract class DataSqlSource<DBChannel> extends AbstractService implement
|
||||
if (isOnlyCache(info)) {
|
||||
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) {
|
||||
futureCompleteConsumer.accept(rs, t);
|
||||
} else {
|
||||
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) {
|
||||
futureCompleteConsumer.accept(rs, t);
|
||||
} else {
|
||||
@@ -1616,6 +1621,11 @@ public abstract class DataSqlSource<DBChannel> extends AbstractService implement
|
||||
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
|
||||
public <T> T find(Class<T> clazz, final SelectColumn selects, Serializable pk) {
|
||||
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);
|
||||
if (cache.isFullLoaded() || rs != null) return rs;
|
||||
}
|
||||
return findCompose(info, selects, pk).join();
|
||||
return findCompose(info, null, selects, pk).join();
|
||||
}
|
||||
|
||||
@Override
|
||||
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 EntityCache<T> cache = info.getCache();
|
||||
if (cache != null) {
|
||||
T rs = selects == null ? cache.find(pk) : cache.find(selects, pk);
|
||||
if (cache.isFullLoaded() || rs != null) return CompletableFuture.completedFuture(rs);
|
||||
}
|
||||
if (isAsync()) return findCompose(info, selects, pk);
|
||||
return CompletableFuture.supplyAsync(() -> findCompose(info, selects, pk).join(), getExecutor());
|
||||
if (isAsync()) return findCompose(info, context, selects, pk);
|
||||
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();
|
||||
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);
|
||||
return findDB(info, sql, true, selects);
|
||||
return findDB(info, context, sql, true, selects);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1711,7 +1725,7 @@ public abstract class DataSqlSource<DBChannel> extends AbstractService implement
|
||||
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));
|
||||
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
|
||||
|
||||
@@ -32,8 +32,6 @@ public final class EntityCache<T> {
|
||||
//日志
|
||||
private static final Logger logger = Logger.getLogger(EntityCache.class.getName());
|
||||
|
||||
private Object[] array;
|
||||
|
||||
//主键与对象的键值对
|
||||
private ConcurrentHashMap<Serializable, T> map = new ConcurrentHashMap();
|
||||
|
||||
@@ -75,7 +73,7 @@ public final class EntityCache<T> {
|
||||
//@Cacheable的定时器
|
||||
private ScheduledThreadPoolExecutor scheduler;
|
||||
|
||||
private CompletableFuture<List> loadFuture;
|
||||
private CompletableFuture<List<T>> loadFuture;
|
||||
|
||||
public EntityCache(final EntityInfo<T> info, final Cacheable c) {
|
||||
this.info = info;
|
||||
@@ -106,24 +104,25 @@ public final class EntityCache<T> {
|
||||
});
|
||||
}
|
||||
|
||||
public void fullLoadAsync() {
|
||||
if (loading.getAndSet(true)) return;
|
||||
public CompletableFuture<List<T>> fullLoadAsync() {
|
||||
if (this.fullloaded) return this.loadFuture;
|
||||
if (loading.getAndSet(true)) return this.loadFuture;
|
||||
if (info.fullloader == null) {
|
||||
this.list = new ConcurrentLinkedQueue();
|
||||
this.map = new ConcurrentHashMap();
|
||||
this.fullloaded = true;
|
||||
loading.set(false);
|
||||
return;
|
||||
return this.loadFuture;
|
||||
}
|
||||
this.fullloaded = false;
|
||||
CompletableFuture<List> allFuture = info.fullloader.apply(info.source, info);
|
||||
this.loadFuture = allFuture;
|
||||
this.loadFuture = (CompletableFuture) allFuture;
|
||||
if (allFuture == null) {
|
||||
this.list = new ConcurrentLinkedQueue();
|
||||
this.map = new ConcurrentHashMap();
|
||||
this.fullloaded = true;
|
||||
loading.set(false);
|
||||
return;
|
||||
return this.loadFuture;
|
||||
}
|
||||
if (this.interval > 0 && this.scheduler == null && info.fullloader != null) {
|
||||
this.scheduler = new ScheduledThreadPoolExecutor(1, (Runnable r) -> {
|
||||
@@ -164,6 +163,7 @@ public final class EntityCache<T> {
|
||||
this.fullloaded = true;
|
||||
loading.set(false);
|
||||
});
|
||||
return this.loadFuture;
|
||||
}
|
||||
|
||||
public Class<T> getType() {
|
||||
@@ -185,20 +185,6 @@ public final class EntityCache<T> {
|
||||
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) {
|
||||
if (pk == null) return null;
|
||||
T rs = map.get(pk);
|
||||
|
||||
@@ -8,7 +8,7 @@ package org.redkale.source;
|
||||
/**
|
||||
* FilterBean用于过滤条件, 所有的FilterBean都必须可以转换成FilterNode <br>
|
||||
*
|
||||
* 不被标记为@javax.persistence.Transient 的字段均视为过滤条件 <br>
|
||||
* 标记为@FilterColumn.ignore=true 的字段会被忽略, 不参与生成过滤条件 <br>
|
||||
*
|
||||
* <p>
|
||||
* 详情见: https://redkale.org
|
||||
|
||||
@@ -37,6 +37,14 @@ public @interface FilterColumn {
|
||||
*/
|
||||
long least() default 1;
|
||||
|
||||
/**
|
||||
* 生成过滤条件时是否屏蔽该字段
|
||||
*
|
||||
* @return 是否屏蔽该字段
|
||||
* @since 2.4.0
|
||||
*/
|
||||
boolean ignore() default false;
|
||||
|
||||
/**
|
||||
* express的默认值根据字段类型的不同而不同: <br>
|
||||
* 数组 --> IN <br>
|
||||
|
||||
@@ -173,6 +173,7 @@ public final class FilterNodeBean<T extends FilterBean> implements Comparable<Fi
|
||||
if (Modifier.isStatic(field.getModifiers())) continue;
|
||||
if (fields.contains(field.getName())) 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());
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ import static org.redkale.source.DataSources.*;
|
||||
*
|
||||
* @author zhangjx
|
||||
*/
|
||||
public class PoolJdbcSource extends PoolSource<Connection> {
|
||||
public class PoolJdbcSource extends PoolSource {
|
||||
|
||||
protected final ConnectionPoolDataSource source;
|
||||
|
||||
@@ -187,7 +187,8 @@ public class PoolJdbcSource extends PoolSource<Connection> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void offerConnection(final Connection conn) {
|
||||
public <C> void offerConnection(final C connection) {
|
||||
Connection conn = (Connection) connection;
|
||||
if (conn == null) return;
|
||||
try {
|
||||
conn.close();
|
||||
|
||||
@@ -19,9 +19,8 @@ import static org.redkale.source.DataSources.*;
|
||||
* 详情见: https://redkale.org
|
||||
*
|
||||
* @author zhangjx
|
||||
* @param <DBChannel> 连接泛型
|
||||
*/
|
||||
public abstract class PoolSource<DBChannel> {
|
||||
public abstract class PoolSource {
|
||||
|
||||
protected final AtomicLong closeCounter = new AtomicLong();
|
||||
|
||||
@@ -152,11 +151,11 @@ public abstract class PoolSource<DBChannel> {
|
||||
|
||||
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();
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ import org.redkale.util.*;
|
||||
*
|
||||
* @author zhangjx
|
||||
*/
|
||||
public abstract class PoolTcpSource extends PoolSource<AsyncConnection> {
|
||||
public abstract class PoolTcpSource extends PoolSource {
|
||||
|
||||
protected AsyncGroup asyncGroup;
|
||||
|
||||
@@ -84,7 +84,8 @@ public abstract class PoolTcpSource extends PoolSource<AsyncConnection> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void offerConnection(final AsyncConnection conn) {
|
||||
public <C> void offerConnection(final C connection) {
|
||||
AsyncConnection conn = (AsyncConnection) connection;
|
||||
if (conn == null) return;
|
||||
if (conn.isOpen()) {
|
||||
CompletableFuture<AsyncConnection> future = pollQueue.poll();
|
||||
|
||||
398
src/org/redkale/source/SearchBean.java
Normal file
398
src/org/redkale/source/SearchBean.java
Normal 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>
|
||||
*
|
||||
* 不被标记为@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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
87
src/org/redkale/source/SearchColumn.java
Normal file
87
src/org/redkale/source/SearchColumn.java
Normal 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 "";
|
||||
|
||||
}
|
||||
26
src/org/redkale/source/SearchSource.java
Normal file
26
src/org/redkale/source/SearchSource.java
Normal 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);
|
||||
}
|
||||
@@ -279,8 +279,8 @@ public abstract class AnyValue {
|
||||
}
|
||||
|
||||
public DefaultAnyValue clear() {
|
||||
this.stringEntrys = new Entry[0];
|
||||
this.anyEntrys = new Entry[0];
|
||||
if (this.stringEntrys != null && this.stringEntrys.length > 0) this.stringEntrys = new Entry[0];
|
||||
if (this.anyEntrys != null && this.anyEntrys.length > 0) this.anyEntrys = new Entry[0];
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
*/
|
||||
package org.redkale.util;
|
||||
|
||||
import java.lang.reflect.TypeVariable;
|
||||
import java.util.*;
|
||||
import java.util.function.*;
|
||||
import org.redkale.asm.*;
|
||||
@@ -857,7 +858,11 @@ public interface Attribute<T, F> {
|
||||
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 {
|
||||
mv.visitVarInsn(ALOAD, 1);
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
*/
|
||||
package org.redkale.util;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.charset.*;
|
||||
import java.util.Arrays;
|
||||
@@ -46,6 +47,20 @@ public final class ByteArray implements ByteTuple {
|
||||
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 是否相同
|
||||
*/
|
||||
public boolean equal(final byte[] bytes) {
|
||||
if (bytes == null || count != bytes.length) return false;
|
||||
for (int i = 0; i < count; i++) {
|
||||
if (content[i] != bytes[i]) return false;
|
||||
if (bytes == null) return false;
|
||||
int len = count;
|
||||
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;
|
||||
}
|
||||
@@ -577,6 +595,23 @@ public final class ByteArray implements ByteTuple {
|
||||
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指定长度的数据
|
||||
*
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
package org.redkale.util;
|
||||
|
||||
/**
|
||||
* 版本
|
||||
* <p>
|
||||
* 详情见: https://redkale.org
|
||||
*
|
||||
@@ -17,7 +18,7 @@ public final class Redkale {
|
||||
}
|
||||
|
||||
public static String getDotedVersion() {
|
||||
return "2.3.0";
|
||||
return "2.4.0";
|
||||
}
|
||||
|
||||
public static int getMajorVersion() {
|
||||
@@ -25,6 +26,6 @@ public final class Redkale {
|
||||
}
|
||||
|
||||
public static int getMinorVersion() {
|
||||
return 3;
|
||||
return 4;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,17 +70,16 @@ public class SelectColumn implements Predicate<String> {
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
* class中的字段名
|
||||
*
|
||||
* @param columns 包含的字段名集合
|
||||
*
|
||||
* @return SelectColumn
|
||||
*/
|
||||
@Deprecated
|
||||
public static SelectColumn createIncludes(String... columns) {
|
||||
return new SelectColumn(columns, false);
|
||||
}
|
||||
// @Deprecated
|
||||
// public static SelectColumn createIncludes(String... columns) {
|
||||
// return new SelectColumn(columns, false);
|
||||
// }
|
||||
|
||||
/**
|
||||
* class中的字段名
|
||||
@@ -94,7 +93,6 @@ public class SelectColumn implements Predicate<String> {
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
* class中的字段名
|
||||
*
|
||||
* @param cols 包含的字段名集合
|
||||
@@ -102,10 +100,10 @@ public class SelectColumn implements Predicate<String> {
|
||||
*
|
||||
* @return SelectColumn
|
||||
*/
|
||||
@Deprecated
|
||||
public static SelectColumn createIncludes(String[] cols, String... columns) {
|
||||
return new SelectColumn(Utility.append(cols, columns), false);
|
||||
}
|
||||
// @Deprecated
|
||||
// public static SelectColumn createIncludes(String[] cols, String... columns) {
|
||||
// return new SelectColumn(Utility.append(cols, columns), false);
|
||||
// }
|
||||
|
||||
/**
|
||||
* class中的字段名
|
||||
@@ -120,7 +118,6 @@ public class SelectColumn implements Predicate<String> {
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*
|
||||
* class中的字段名
|
||||
*
|
||||
@@ -128,10 +125,10 @@ public class SelectColumn implements Predicate<String> {
|
||||
*
|
||||
* @return SelectColumn
|
||||
*/
|
||||
@Deprecated
|
||||
public static SelectColumn createExcludes(String... columns) {
|
||||
return new SelectColumn(columns, true);
|
||||
}
|
||||
// @Deprecated
|
||||
// public static SelectColumn createExcludes(String... columns) {
|
||||
// return new SelectColumn(columns, true);
|
||||
// }
|
||||
|
||||
/**
|
||||
* class中的字段名
|
||||
@@ -145,7 +142,6 @@ public class SelectColumn implements Predicate<String> {
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
* class中的字段名
|
||||
*
|
||||
* @param cols 排除的字段名集合
|
||||
@@ -153,10 +149,10 @@ public class SelectColumn implements Predicate<String> {
|
||||
*
|
||||
* @return SelectColumn
|
||||
*/
|
||||
@Deprecated
|
||||
public static SelectColumn createExcludes(String[] cols, String... columns) {
|
||||
return new SelectColumn(Utility.append(cols, columns), true);
|
||||
}
|
||||
// @Deprecated
|
||||
// public static SelectColumn createExcludes(String[] cols, String... columns) {
|
||||
// return new SelectColumn(Utility.append(cols, columns), true);
|
||||
// }
|
||||
|
||||
/**
|
||||
*
|
||||
|
||||
@@ -60,16 +60,25 @@ public final class Utility {
|
||||
|
||||
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.or = or;
|
||||
this.u = u;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
if (f != null && !f.isDone())
|
||||
f.completeExceptionally(new TimeoutException());
|
||||
if (or) {
|
||||
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;
|
||||
}
|
||||
|
||||
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);
|
||||
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 {
|
||||
byte[] bs = readBytes(new FileInputStream("D:\\Java-Projects\\JavaApplication20\\dist\\AnonymousUnsafeByteBooleanFunction.class"));
|
||||
System.out.println(binToHex(bs));
|
||||
public static <T> CompletableFuture<T> completeOnTimeout(CompletableFuture future, T value, long timeout, TimeUnit unit) {
|
||||
//if (greatejdk8) return future.completeOnTimeout(value, timeout, unit);
|
||||
return future.whenComplete(new FutureCanceller(futureDelayer.schedule(new FutureTimeout(future, true, value), timeout, unit)));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -422,6 +431,19 @@ public final class Utility {
|
||||
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值
|
||||
*
|
||||
@@ -677,6 +699,68 @@ public final class Utility {
|
||||
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数组结尾
|
||||
*
|
||||
@@ -2703,6 +2787,10 @@ public final class Utility {
|
||||
return out;
|
||||
}
|
||||
|
||||
public static byte[] readBytes(File file) throws IOException {
|
||||
return readBytesThenClose(new FileInputStream(file));
|
||||
}
|
||||
|
||||
public static byte[] readBytes(InputStream in) throws IOException {
|
||||
return readStream(in).toByteArray();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user