Redkale 2.4.0 结束

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

View File

@@ -33,6 +33,14 @@
-->
<resources>
<!--
【节点全局唯一】 @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默认值为aioUDP情况下值可以是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节点

View File

@@ -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的子类。

View File

@@ -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;

View File

@@ -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;
}
}

View File

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

View File

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

View File

@@ -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;
}
}

View File

@@ -0,0 +1,25 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.cluster;
import org.redkale.util.AnyValue;
/**
* 自定义的ClusterAgent加载器
*
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
* @since 2.4.0
*/
public interface ClusterAgentLoader {
public boolean match(AnyValue config);
public Class<? extends ClusterAgent> agentClass();
}

View File

@@ -31,8 +31,14 @@ public final class AnyEncoder<T> implements Encodeable<Writer, T> {
out.writeClassName(null);
out.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);
}
}

View File

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

View File

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

View File

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

View File

@@ -30,6 +30,16 @@ public class JsonBytesWriter extends JsonWriter implements ByteTuple {
private static final byte[] BYTES_FALSEVALUE = "false".getBytes();
private static final 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;

View File

@@ -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;

View File

@@ -74,7 +74,12 @@ public abstract class JsonDynEncoder<T> implements Encodeable<JsonWriter, T> {
if (factory.loadEncoder(t) instanceof JsonDynEncoder) return true;
}
}
if (factory.loadEncoder(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;
}

View File

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

View File

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

View File

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

View File

@@ -216,6 +216,10 @@ public abstract class MessageAgent {
public final synchronized void putService(NodeHttpServer ns, Service service, HttpServlet servlet) {
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");

View File

@@ -0,0 +1,25 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.mq;
import org.redkale.util.AnyValue;
/**
* 自定义的MessageAgent加载器
*
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
* @since 2.4.0
*/
public interface MessageAgentLoader {
public boolean match(AnyValue config);
public Class<? extends MessageAgent> agentClass();
}

View File

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

View File

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

View File

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

View File

@@ -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;

View File

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

View File

@@ -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.
}
}

View File

@@ -0,0 +1,31 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.net;
import java.util.*;
/**
* 当前一个Request绑定的AsyncConnection 类似Session但概念上不同于sessionid对应的对象
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
* @since 2.4.0
*/
public interface ChannelContext {
public void setAttribute(String name, Object value);
@SuppressWarnings("unchecked")
public <T> T getAttribute(String name);
public void removeAttribute(String name);
public Map<String, Object> getAttributes();
public void clearAttribute();
}

View File

@@ -5,6 +5,7 @@
*/
package org.redkale.net;
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;
}

View File

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

View File

@@ -32,8 +32,8 @@ public abstract class Server<K extends Serializable, C extends Context, R extend
public static final String RESNAME_SERVER_ROOT = "SERVER_ROOT";
@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";

View File

@@ -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;

View File

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

View File

@@ -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
*

View File

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

View File

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

View File

@@ -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>
* 数据类型由&#64;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;
}
/**
* 获取文件上传信息列表
*

View File

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

View File

@@ -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;

View File

@@ -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() {

View File

@@ -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)

View File

@@ -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

View File

@@ -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) -> {

View File

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

View File

@@ -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;

View File

@@ -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;

View File

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

View File

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

View File

@@ -22,6 +22,7 @@ import org.redkale.source.*;
import org.redkale.util.*;
/**
* 注: 部署了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);
}

View File

@@ -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;

View File

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

View File

@@ -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<>());
// }
}
}

View File

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

View File

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

View File

@@ -0,0 +1,25 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.source;
import org.redkale.util.AnyValue;
/**
* 自定义的CacheSource加载器
*
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
* @since 2.4.0
*/
public interface CacheSourceLoader {
public boolean match(AnyValue config);
public Class<? extends CacheSource> sourceClass();
}

View File

@@ -74,6 +74,19 @@ public class ColumnValue {
return new ColumnValue(column, INC, value);
}
/**
* 返回 {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} &#38; {value} 操作
*

View File

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

View File

@@ -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

View File

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

View File

@@ -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;
}
}

View File

@@ -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

View File

@@ -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> {
//&#064;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);

View File

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

View File

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

View File

@@ -173,6 +173,7 @@ public final class FilterNodeBean<T extends FilterBean> implements Comparable<Fi
if (Modifier.isStatic(field.getModifiers())) continue;
if (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());

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -279,8 +279,8 @@ public abstract class AnyValue {
}
public DefaultAnyValue clear() {
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;
}

View File

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

View File

@@ -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指定长度的数据
*

View File

@@ -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;
}
}

View File

@@ -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);
// }
/**
*

View File

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