68 Commits

Author SHA1 Message Date
Redkale
b7acce0814 Redkale 2.0.0.beta4 结束 2019-09-20 08:58:51 +08:00
Redkale
8744e76cad 2019-09-19 21:52:52 +08:00
Redkale
446b3c13dc 2019-09-19 21:38:53 +08:00
Redkale
3951e28148 2019-09-19 21:38:22 +08:00
Redkale
b74d679608 修复ByteBuffer Utility.encodeUTF8的bug 2019-09-19 21:35:05 +08:00
Redkale
edbc878b73 修复Utility.encodeUTF8Length的bug 2019-09-19 21:22:47 +08:00
Redkale
f706209ec1 RestResult合并进HttpResult 2019-09-19 21:08:36 +08:00
Redkale
bf000b188f 增加RestResult功能 2019-09-19 20:30:30 +08:00
Redkale
def1736a9b 2019-09-19 13:22:56 +08:00
Redkale
0242f4c0c3 2019-09-19 13:22:20 +08:00
Redkale
5cd399b2df 2019-09-19 13:12:33 +08:00
Redkale
9ddb662016 Convert增加BiFunction<Attribute, Object, Object> fieldFunc参数 2019-09-19 12:05:03 +08:00
Redkale
2947275d54 2019-09-18 11:27:54 +08:00
Redkale
e43f814872 Utility增加containsMatch、removeMatch方法 2019-09-18 11:22:28 +08:00
Redkale
6e16f52e28 2019-09-18 11:02:13 +08:00
Redkale
fca13557df 【不兼容】删掉javax.persistence.GeneratedValue功能 2019-09-16 16:35:41 +08:00
Redkale
824a6df55a 2019-09-16 16:29:12 +08:00
Redkale
b98b526c50 2019-09-09 16:35:08 +08:00
Redkale
8f6aa4f4a5 2019-09-09 16:29:48 +08:00
Redkale
00a07a79b2 2019-09-09 16:25:10 +08:00
Redkale
264dfbef2e HttpServlet增加postStart方法 2019-09-04 10:00:29 +08:00
Redkale
2e27814809 2019-09-04 09:22:42 +08:00
Redkale
f767f40e56 StringConvertWrapper 改成 StringWrapper 2019-08-30 15:18:03 +08:00
Redkale
543ecc071a 2019-08-30 10:50:24 +08:00
Redkale
2a14f39495 2019-08-30 09:30:41 +08:00
Redkale
95c8ae2334 2019-08-30 08:05:28 +08:00
Redkale
338ea13828 2019-08-29 17:44:13 +08:00
Redkale
4a8b9e5fec 2019-08-29 16:43:45 +08:00
Redkale
e281cac3d3 2019-08-28 16:15:28 +08:00
Redkale
a495829a3c RestHeader支持InetSocketAddress类型 2019-08-28 14:31:33 +08:00
Redkale
b0deed2a89 WebSocket增加getSncpAddress方法,获取分布式下的sncp地址 2019-08-28 12:56:50 +08:00
Redkale
70a75abf74 Redkale 2.0.0.beta3 结束 2019-08-15 13:11:17 +08:00
Redkale
1aa97f8e79 2019-08-06 10:08:24 +08:00
Redkale
b4000235ac 2019-08-05 17:51:26 +08:00
Redkale
4b93f29a1c 2019-08-05 17:14:14 +08:00
Redkale
4892a50670 2019-08-05 16:26:52 +08:00
Redkale
2fe0ac0ef9 修复ColumnValue.mov时字段类型是long,而参数值是int会报错的bug 2019-07-31 17:19:35 +08:00
Redkale
333ae72148 2019-07-30 11:05:11 +08:00
Redkale
5fd5b7f303 2019-07-30 10:58:37 +08:00
Redkale
528cf45f3f ColumnExpress增加除法、取模表达式 2019-07-30 10:31:48 +08:00
Redkale
c7308e7320 修复ConvertFacotry.register方法中column参数没有对应field时会导致method的ignore失效的bug 2019-07-29 13:20:23 +08:00
Redkale
e2a49eaab7 2019-07-29 11:40:33 +08:00
Redkale
44bd6f235c 2019-07-25 16:58:20 +08:00
Redkale
601d15b513 2019-07-17 19:00:18 +08:00
Redkale
9e93485a97 新增HttpRequest.getQueryBytes方法 2019-07-17 18:42:45 +08:00
Redkale
ad87b2115d 修改多个@RetLabel不能正确根据locale获取对应值的bug 2019-07-17 15:14:48 +08:00
Redkale
27a587d31f 修复mysql下update操作值带转义字符导致失败的bug 2019-07-12 16:52:46 +08:00
Redkale
fc8fa27602 RetResult增加map静态方法 2019-07-11 14:53:51 +08:00
Redkale
f9aebc8ee3 修复同一个Entity类被多个source源分表分库操作时判断表是否已建有误的bug 2019-07-09 14:18:07 +08:00
Redkale
1167da8f4c Redkale 2.0.0.beta2 结束 2019-07-08 09:15:26 +08:00
Redkale
7a5fbcdccd 2019-07-03 11:35:37 +08:00
Redkale
345e929712 [不兼容修改]CacheSource的getCollectionMap序列方法增加一个set参数 2019-06-27 18:21:19 +08:00
Redkale
358862fe59 修复Entity类带boolean字段调DataSource.insert出现异常的bug 2019-06-26 16:51:48 +08:00
Redkale
3dde9bb293 2019-06-21 17:00:40 +08:00
Redkale
99ae4ccadd 从Context中移除BufferPool和ResponsePool 2019-06-20 15:26:20 +08:00
Redkale
98e9ffe0ef 2019-06-20 10:02:24 +08:00
Redkale
6927bfe8ac 2019-06-19 16:52:00 +08:00
Redkale
340a3a8fa3 2019-06-19 16:46:12 +08:00
Redkale
4724763991 2019-06-19 16:45:45 +08:00
Redkale
03353ad08c 2019-06-19 16:21:05 +08:00
Redkale
95c3354fcd WebSocketParam增加getAnnotations系列方法 2019-06-19 15:47:46 +08:00
Redkale
1bda2f92b9 HttpRequest增加getAnnotation系列方法 2019-06-18 22:59:07 +08:00
Redkale
bd3c706934 修复DataSource中json字段不为Serializable时会异常的bug 2019-06-13 22:34:53 +08:00
Redkale
ef3663aa36 修复DataSource中json字段不为Serializable时会异常的bug 2019-06-13 22:23:30 +08:00
Redkale
427ff717d4 UDP协议下bufferCapacity默认值为1350字节 2019-05-30 12:07:09 +08:00
Redkale
b409300412 UDP协议下bufferCapacity默认值为1480字节 2019-05-30 12:04:13 +08:00
Redkale
ca1f974dbe 2019-05-28 17:43:48 +08:00
Redkale
6a8c86096b DataSource的clearTable、dropTable在表不存在的情况下由抛异常改为结果值返回-1 2019-05-28 15:51:59 +08:00
74 changed files with 1906 additions and 742 deletions

View File

@@ -119,7 +119,7 @@
threads 线程数, 默认: CPU核数*32
maxconns最大连接数, 小于1表示无限制 默认: 0
maxbody: request.body最大值 默认: 64K
bufferCapacity: ByteBuffer的初始化大小 默认: 32K; (HTTP 2.0、WebSocket必须要16k以上)
bufferCapacity: ByteBuffer的初始化大小 TCP默认: 32K; (HTTP 2.0、WebSocket必须要16k以上); UDP默认: 1350B
bufferPoolSize ByteBuffer池的大小默认: 线程数*4
responsePoolSize Response池的大小默认: 线程数*2
aliveTimeoutSeconds: KeepAlive读操作超时秒数 默认30 0表示永久不超时; -1表示禁止KeepAlive

View File

@@ -1,63 +0,0 @@
/** *****************************************************************************
* Copyright (c) 2008 - 2013 Oracle Corporation. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0
* which accompanies this distribution.
* The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
* Linda DeMichiel - Java Persistence 2.1
* Linda DeMichiel - Java Persistence 2.0
*
***************************************************************************** */
package javax.persistence;
import java.lang.annotation.Target;
import java.lang.annotation.Retention;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
/**
* Provides for the specification of generation strategies for the
* values of primary keys.
*
* <p>
* The <code>GeneratedValue</code> annotation
* may be applied to a primary key property or field of an entity or
* mapped superclass in conjunction with the {@link Id} annotation.
* The use of the <code>GeneratedValue</code> annotation is only
* required to be supported for simple primary keys. Use of the
* <code>GeneratedValue</code> annotation is not supported for derived
* primary keys.
*
* <pre>
*
* Example 1:
*
* &#064;Id
* &#064;GeneratedValue(strategy=SEQUENCE, generator="CUST_SEQ")
* &#064;Column(name="CUST_ID")
* public Long getId() { return id; }
*
* Example 2:
*
* &#064;Id
* &#064;GeneratedValue(strategy=TABLE, generator="CUST_GEN")
* &#064;Column(name="CUST_ID")
* Long id;
* </pre>
*
* @see Id
*
* @since Java Persistence 1.0
*/
@Target({METHOD, FIELD})
@Retention(RUNTIME)
public @interface GeneratedValue {
}

View File

@@ -45,7 +45,7 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME;
* </pre>
*
* @see Column
* @see GeneratedValue
* see GeneratedValue
*
* @since Java Persistence 1.0
*/

View File

@@ -437,7 +437,7 @@ public abstract class NodeServer {
final ResourceFactory.ResourceLoader resourceLoader = (ResourceFactory rf, final Object src, final String resourceName, Field field, final Object attachment) -> {
try {
if (SncpClient.parseMethod(serviceImplClass).isEmpty() && serviceImplClass.getAnnotation(Priority.class) == null) { //class没有可用的方法且没有标记启动优先级的 通常为BaseService
logger.log(Level.FINE, serviceImplClass + " cannot load because not found less one public non-final method");
if (!serviceImplClass.getName().startsWith("org.redkale.")) logger.log(Level.FINE, serviceImplClass + " cannot load because not found less one public non-final method");
return;
}

View File

@@ -7,7 +7,8 @@ package org.redkale.convert;
import java.lang.reflect.Type;
import java.nio.ByteBuffer;
import java.util.function.Supplier;
import java.util.function.*;
import org.redkale.util.Attribute;
/**
* 序列化/反序列化操作类
@@ -31,6 +32,17 @@ public abstract class Convert<R extends Reader, W extends Writer> {
return this.factory;
}
protected <S extends W> S configWrite(S writer) {
return writer;
}
protected <S extends W> S fieldFunc(S writer, BiFunction<Attribute, Object, Object> fieldFunc) {
writer.fieldFunc = fieldFunc;
return writer;
}
public abstract Convert<R, W> newConvert(final BiFunction<Attribute, Object, Object> fieldFunc);
public abstract boolean isBinary();
public abstract <T> T convertFrom(final Type type, final byte[] bytes);

View File

@@ -91,8 +91,9 @@ public abstract class ConvertFactory<R extends Reader, W extends Writer> {
this.register(Number.class, NumberSimpledCoder.instance);
this.register(String.class, StringSimpledCoder.instance);
this.register(StringConvertWrapper.class, StringConvertWrapperSimpledCoder.instance);
this.register(StringWrapper.class, StringWrapperSimpledCoder.instance);
this.register(CharSequence.class, CharSequenceSimpledCoder.instance);
this.register(StringBuilder.class, CharSequenceSimpledCoder.StringBuilderSimpledCoder.instance);
this.register(java.util.Date.class, DateSimpledCoder.instance);
this.register(java.time.Duration.class, DurationSimpledCoder.instance);
this.register(AtomicInteger.class, AtomicIntegerSimpledCoder.instance);
@@ -433,25 +434,32 @@ public abstract class ConvertFactory<R extends Reader, W extends Writer> {
public final boolean register(final Class type, String column, ConvertColumnEntry entry) {
if (type == null || column == null || entry == null) return false;
Field field = null;
try {
final Field field = type.getDeclaredField(column);
String get = "get";
if (field.getType() == boolean.class || field.getType() == Boolean.class) get = "is";
char[] cols = column.toCharArray();
cols[0] = Character.toUpperCase(cols[0]);
String col2 = new String(cols);
try {
register(type.getMethod(get + col2), entry);
} catch (Exception ex) {
}
try {
register(type.getMethod("set" + col2, field.getType()), entry);
} catch (Exception ex) {
}
return register(field, entry);
field = type.getDeclaredField(column);
} catch (Exception e) {
return false;
}
String get = "get";
if (field != null && (field.getType() == boolean.class || field.getType() == Boolean.class)) get = "is";
char[] cols = column.toCharArray();
cols[0] = Character.toUpperCase(cols[0]);
final String bigColumn = new String(cols);
try {
register(type.getMethod(get + bigColumn), entry);
} catch (NoSuchMethodException mex) {
if (get.length() >= 3) { //get
try {
register(type.getMethod("is" + bigColumn), entry);
} catch (Exception ex) {
}
}
} catch (Exception ex) {
}
try {
register(type.getMethod("set" + bigColumn, field.getType()), entry);
} catch (Exception ex) {
}
return field == null ? true : register(field, entry);
}
public final <E> boolean register(final AccessibleObject field, final ConvertColumnEntry entry) {

View File

@@ -1,63 +0,0 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.convert;
import org.redkale.convert.json.JsonConvert;
/**
* 序列化去掉引号的String对象。
* <blockquote><pre>
* 场景: JavaBean bean = ... ;
* Map map = new HashMap();
* map.put("bean", a);
* records.add(map);
* records需要在后期序列化写入库。 但是在这期间bean的内部字段值可能就变化了会导致入库时并不是records.add的快照信息。
* 所以需要使用StringConvertWrapper
* Map map = new HashMap();
* map.put("bean", new StringConvertWrapper(bean.toString()));
* records.add(map);
* 这样既可以保持快照又不会在bean的值上面多一层引号。
* </pre></blockquote>
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*/
public class StringConvertWrapper {
protected String value;
public StringConvertWrapper() {
}
public StringConvertWrapper(String value) {
this.value = value;
}
public static StringConvertWrapper create(Object value) {
return create(JsonConvert.root(), value);
}
public static StringConvertWrapper create(TextConvert convert, Object value) {
if (value == null) return new StringConvertWrapper(null);
if (value instanceof String) return new StringConvertWrapper((String) value);
return new StringConvertWrapper(convert.convertTo(value));
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
@Override
public String toString() {
return value;
}
}

View File

@@ -6,6 +6,8 @@
package org.redkale.convert;
import java.lang.reflect.*;
import java.util.function.BiFunction;
import org.redkale.util.*;
/**
* 序列化的数据输出流
@@ -23,6 +25,9 @@ public abstract class Writer {
//convertTo时是否以指定Type的ObjectEncoder进行处理
protected Type specify;
//对某个字段值进行动态处理
protected BiFunction<Attribute, Object, Object> fieldFunc;
/**
* 设置specify
*
@@ -38,6 +43,11 @@ public abstract class Writer {
}
}
protected boolean recycle() {
this.fieldFunc = null;
return true;
}
/**
* 返回specify
*
@@ -105,7 +115,12 @@ public abstract class Writer {
*/
@SuppressWarnings("unchecked")
public void writeObjectField(final EnMember member, Object obj) {
Object value = member.attribute.get(obj);
Object value;
if (fieldFunc == null) {
value = member.attribute.get(obj);
} else {
value = fieldFunc.apply(member.attribute, obj);
}
if (value == null) return;
if (tiny()) {
if (member.istring) {
@@ -262,5 +277,5 @@ public abstract class Writer {
*
* @param value StringConvertWrapper值
*/
public abstract void writeWrapper(StringConvertWrapper value);
public abstract void writeWrapper(StringWrapper value);
}

View File

@@ -131,6 +131,7 @@ public class BsonByteBufferWriter extends BsonWriter {
@Override
protected boolean recycle() {
super.recycle();
this.index = 0;
this.specify = null;
this.buffers = null;

View File

@@ -37,7 +37,7 @@ import org.redkale.util.*;
*
* @author zhangjx
*/
public final class BsonConvert extends BinaryConvert<BsonReader, BsonWriter> {
public class BsonConvert extends BinaryConvert<BsonReader, BsonWriter> {
private static final ObjectPool<BsonReader> readerPool = BsonReader.createPool(Integer.getInteger("convert.bson.pool.size", 16));
@@ -59,6 +59,16 @@ public final class BsonConvert extends BinaryConvert<BsonReader, BsonWriter> {
return BsonFactory.root().getConvert();
}
@Override
public BsonConvert newConvert(final BiFunction<Attribute, Object, Object> fieldFunc) {
return new BsonConvert(getFactory(), tiny) {
@Override
protected <S extends BsonWriter> S configWrite(S writer) {
return fieldFunc(writer, fieldFunc);
}
};
}
//------------------------------ reader -----------------------------------------------------------
public BsonReader pollBsonReader(final ByteBuffer... buffers) {
return new BsonByteBufferReader((ConvertMask) null, buffers);
@@ -78,11 +88,11 @@ public final class BsonConvert extends BinaryConvert<BsonReader, BsonWriter> {
//------------------------------ writer -----------------------------------------------------------
public BsonByteBufferWriter pollBsonWriter(final Supplier<ByteBuffer> supplier) {
return new BsonByteBufferWriter(tiny, supplier);
return configWrite(new BsonByteBufferWriter(tiny, supplier));
}
public BsonWriter pollBsonWriter(final OutputStream out) {
return new BsonStreamWriter(tiny, out);
return configWrite(new BsonStreamWriter(tiny, out));
}
public BsonWriter pollBsonWriter() {
@@ -94,6 +104,7 @@ public final class BsonConvert extends BinaryConvert<BsonReader, BsonWriter> {
}
//------------------------------ convertFrom -----------------------------------------------------------
@Override
public <T> T convertFrom(final Type type, final byte[] bytes) {
if (bytes == null) return null;
return convertFrom(type, bytes, 0, bytes.length);
@@ -173,33 +184,33 @@ public final class BsonConvert extends BinaryConvert<BsonReader, BsonWriter> {
public void convertTo(final OutputStream out, final Object value) {
if (value == null) {
new BsonStreamWriter(tiny, out).writeNull();
pollBsonWriter(out).writeNull();
} else {
factory.loadEncoder(value.getClass()).convertTo(new BsonStreamWriter(tiny, out), value);
factory.loadEncoder(value.getClass()).convertTo(pollBsonWriter(out), value);
}
}
public void convertTo(final OutputStream out, final Type type, final Object value) {
if (type == null) return;
if (value == null) {
new BsonStreamWriter(tiny, out).writeNull();
pollBsonWriter(out).writeNull();
} else {
factory.loadEncoder(type).convertTo(new BsonStreamWriter(tiny, out), value);
factory.loadEncoder(type).convertTo(pollBsonWriter(out), value);
}
}
public void convertMapTo(final OutputStream out, final Object... values) {
if (values == null) {
new BsonStreamWriter(tiny, out).writeNull();
pollBsonWriter(out).writeNull();
} else {
((AnyEncoder) factory.getAnyEncoder()).convertMapTo(new BsonStreamWriter(tiny, out), values);
((AnyEncoder) factory.getAnyEncoder()).convertMapTo(pollBsonWriter(out), values);
}
}
@Override
public ByteBuffer[] convertTo(final Supplier<ByteBuffer> supplier, final Object value) {
if (supplier == null) return null;
BsonByteBufferWriter out = new BsonByteBufferWriter(tiny, supplier);
BsonByteBufferWriter out = pollBsonWriter(supplier);
if (value == null) {
out.writeNull();
} else {
@@ -211,7 +222,7 @@ public final class BsonConvert extends BinaryConvert<BsonReader, BsonWriter> {
@Override
public ByteBuffer[] convertTo(final Supplier<ByteBuffer> supplier, final Type type, final Object value) {
if (supplier == null || type == null) return null;
BsonByteBufferWriter out = new BsonByteBufferWriter(tiny, supplier);
BsonByteBufferWriter out = pollBsonWriter(supplier);
if (value == null) {
out.writeNull();
} else {
@@ -223,7 +234,7 @@ public final class BsonConvert extends BinaryConvert<BsonReader, BsonWriter> {
@Override
public ByteBuffer[] convertMapTo(final Supplier<ByteBuffer> supplier, final Object... values) {
if (supplier == null) return null;
BsonByteBufferWriter out = new BsonByteBufferWriter(tiny, supplier);
BsonByteBufferWriter out = pollBsonWriter(supplier);
if (values == null) {
out.writeNull();
} else {

View File

@@ -97,7 +97,9 @@ public class BsonWriter extends Writer {
count += len;
}
@Override
protected boolean recycle() {
super.recycle();
this.count = 0;
this.specify = null;
if (this.content.length > defaultSize) {
@@ -242,7 +244,7 @@ public class BsonWriter extends Writer {
}
@Override
public final void writeWrapper(StringConvertWrapper value) {
public final void writeWrapper(StringWrapper value) {
this.writeString(value == null ? null : value.getValue());
}

View File

@@ -8,9 +8,11 @@ package org.redkale.convert.ext;
import org.redkale.convert.*;
/**
* CharSequence 的SimpledCoder实现
* CharSequence 的SimpledCoder实现
*
* <p>
* 详情见: https://redkale.org
*
* <p> 详情见: https://redkale.org
* @author zhangjx
* @param <R> Reader输入的子类型
* @param <W> Writer输出的子类型
@@ -28,4 +30,20 @@ public class CharSequenceSimpledCoder<R extends Reader, W extends Writer> extend
public CharSequence convertFrom(R in) {
return in.readString();
}
public static class StringBuilderSimpledCoder<R extends Reader, W extends Writer> extends SimpledCoder<R, W, StringBuilder> {
public static final StringBuilderSimpledCoder instance = new StringBuilderSimpledCoder();
@Override
public void convertTo(W out, StringBuilder value) {
out.writeString(value == null ? null : value.toString());
}
@Override
public StringBuilder convertFrom(R in) {
String rs = in.readString();
return rs == null ? null : new StringBuilder(rs);
}
}
}

View File

@@ -6,6 +6,7 @@
package org.redkale.convert.ext;
import org.redkale.convert.*;
import org.redkale.util.StringWrapper;
/**
* String 的SimpledCoder实现
@@ -17,18 +18,18 @@ import org.redkale.convert.*;
* @param <R> Reader输入的子类型
* @param <W> Writer输出的子类型
*/
public final class StringConvertWrapperSimpledCoder<R extends Reader, W extends Writer> extends SimpledCoder<R, W, StringConvertWrapper> {
public final class StringWrapperSimpledCoder<R extends Reader, W extends Writer> extends SimpledCoder<R, W, StringWrapper> {
public static final StringConvertWrapperSimpledCoder instance = new StringConvertWrapperSimpledCoder();
public static final StringWrapperSimpledCoder instance = new StringWrapperSimpledCoder();
@Override
public void convertTo(W out, StringConvertWrapper value) {
public void convertTo(W out, StringWrapper value) {
out.writeWrapper(value);
}
@Override
public StringConvertWrapper convertFrom(R in) {
return new StringConvertWrapper(in.readString());
public StringWrapper convertFrom(R in) {
return new StringWrapper(in.readString());
}
}

View File

@@ -48,6 +48,7 @@ public class JsonByteBufferWriter extends JsonWriter {
@Override
protected boolean recycle() {
super.recycle();
this.index = 0;
this.specify = null;
this.charset = null;

View File

@@ -21,7 +21,7 @@ import org.redkale.util.*;
* @author zhangjx
*/
@SuppressWarnings("unchecked")
public final class JsonConvert extends TextConvert<JsonReader, JsonWriter> {
public class JsonConvert extends TextConvert<JsonReader, JsonWriter> {
public static final Type TYPE_MAP_STRING_STRING = new TypeToken<java.util.HashMap<String, String>>() {
}.getType();
@@ -46,6 +46,16 @@ public final class JsonConvert extends TextConvert<JsonReader, JsonWriter> {
return JsonFactory.root().getConvert();
}
@Override
public JsonConvert newConvert(final BiFunction<Attribute, Object, Object> fieldFunc) {
return new JsonConvert(getFactory(), tiny) {
@Override
protected <S extends JsonWriter> S configWrite(S writer) {
return fieldFunc(writer, fieldFunc);
}
};
}
//------------------------------ reader -----------------------------------------------------------
public JsonReader pollJsonReader(final ByteBuffer... buffers) {
return new JsonByteBufferReader((ConvertMask) null, buffers);
@@ -65,19 +75,19 @@ public final class JsonConvert extends TextConvert<JsonReader, JsonWriter> {
//------------------------------ writer -----------------------------------------------------------
public JsonByteBufferWriter pollJsonWriter(final Supplier<ByteBuffer> supplier) {
return new JsonByteBufferWriter(tiny, supplier);
return configWrite(new JsonByteBufferWriter(tiny, supplier));
}
public JsonWriter pollJsonWriter(final OutputStream out) {
return new JsonStreamWriter(tiny, out);
return configWrite(new JsonStreamWriter(tiny, out));
}
public JsonWriter pollJsonWriter(final Charset charset, final OutputStream out) {
return new JsonStreamWriter(tiny, charset, out);
return configWrite(new JsonStreamWriter(tiny, charset, out));
}
public JsonWriter pollJsonWriter() {
return writerPool.get().tiny(tiny);
return configWrite(writerPool.get().tiny(tiny));
}
public void offerJsonWriter(final JsonWriter writer) {
@@ -85,6 +95,7 @@ public final class JsonConvert extends TextConvert<JsonReader, JsonWriter> {
}
//------------------------------ convertFrom -----------------------------------------------------------
@Override
public <T> T convertFrom(final Type type, final byte[] bytes) {
if (bytes == null) return null;
return convertFrom(type, new String(bytes, StandardCharsets.UTF_8));
@@ -190,7 +201,7 @@ public final class JsonConvert extends TextConvert<JsonReader, JsonWriter> {
public String convertTo(final Type type, final Object value) {
if (type == null) return null;
if (value == null) return "null";
final JsonWriter writer = writerPool.get().tiny(tiny);
final JsonWriter writer = pollJsonWriter();
writer.specify(type);
factory.loadEncoder(type).convertTo(writer, value);
String result = writer.toString();
@@ -201,7 +212,7 @@ public final class JsonConvert extends TextConvert<JsonReader, JsonWriter> {
@Override
public String convertMapTo(final Object... values) {
if (values == null) return "null";
final JsonWriter writer = writerPool.get().tiny(tiny);
final JsonWriter writer = pollJsonWriter();
((AnyEncoder) factory.getAnyEncoder()).convertMapTo(writer, values);
String result = writer.toString();
writerPool.accept(writer);
@@ -210,7 +221,7 @@ public final class JsonConvert extends TextConvert<JsonReader, JsonWriter> {
public void convertTo(final OutputStream out, final Object value) {
if (value == null) {
new JsonStreamWriter(tiny, out).writeNull();
pollJsonWriter(out).writeNull();
} else {
convertTo(out, value.getClass(), value);
}
@@ -219,9 +230,9 @@ public final class JsonConvert extends TextConvert<JsonReader, JsonWriter> {
public void convertTo(final OutputStream out, final Type type, final Object value) {
if (type == null) return;
if (value == null) {
new JsonStreamWriter(tiny, out).writeNull();
pollJsonWriter(out).writeNull();
} else {
final JsonWriter writer = writerPool.get().tiny(tiny);
final JsonWriter writer = pollJsonWriter();
writer.specify(type);
factory.loadEncoder(type).convertTo(writer, value);
byte[] bs = writer.toBytes();
@@ -236,9 +247,9 @@ public final class JsonConvert extends TextConvert<JsonReader, JsonWriter> {
public void convertMapTo(final OutputStream out, final Object... values) {
if (values == null) {
new JsonStreamWriter(tiny, out).writeNull();
pollJsonWriter(out).writeNull();
} else {
final JsonWriter writer = writerPool.get().tiny(tiny);
final JsonWriter writer = pollJsonWriter();
((AnyEncoder) factory.getAnyEncoder()).convertMapTo(writer, values);
byte[] bs = writer.toBytes();
writerPool.accept(writer);
@@ -253,7 +264,7 @@ public final class JsonConvert extends TextConvert<JsonReader, JsonWriter> {
@Override
public ByteBuffer[] convertTo(final Supplier<ByteBuffer> supplier, final Object value) {
if (supplier == null) return null;
JsonByteBufferWriter out = new JsonByteBufferWriter(tiny, null, supplier);
JsonByteBufferWriter out = pollJsonWriter(supplier);
if (value == null) {
out.writeNull();
} else {
@@ -265,7 +276,7 @@ public final class JsonConvert extends TextConvert<JsonReader, JsonWriter> {
@Override
public ByteBuffer[] convertTo(final Supplier<ByteBuffer> supplier, final Type type, final Object value) {
if (supplier == null || type == null) return null;
JsonByteBufferWriter out = new JsonByteBufferWriter(tiny, null, supplier);
JsonByteBufferWriter out = pollJsonWriter(supplier);
if (value == null) {
out.writeNull();
} else {
@@ -278,7 +289,7 @@ public final class JsonConvert extends TextConvert<JsonReader, JsonWriter> {
@Override
public ByteBuffer[] convertMapTo(final Supplier<ByteBuffer> supplier, final Object... values) {
if (supplier == null) return null;
JsonByteBufferWriter out = new JsonByteBufferWriter(tiny, null, supplier);
JsonByteBufferWriter out = pollJsonWriter(supplier);
if (values == null) {
out.writeNull();
} else {
@@ -320,14 +331,14 @@ public final class JsonConvert extends TextConvert<JsonReader, JsonWriter> {
public JsonWriter convertToWriter(final Type type, final Object value) {
if (type == null) return null;
final JsonWriter writer = writerPool.get().tiny(tiny);
final JsonWriter writer = pollJsonWriter();
writer.specify(type);
factory.loadEncoder(type).convertTo(writer, value);
return writer;
}
public JsonWriter convertMapToWriter(final Object... values) {
final JsonWriter writer = writerPool.get().tiny(tiny);
final JsonWriter writer = pollJsonWriter();
((AnyEncoder) factory.getAnyEncoder()).convertMapTo(writer, values);
return writer;
}

View File

@@ -97,7 +97,9 @@ public class JsonWriter extends Writer {
if (quote) content[count++] = '"';
}
@Override
protected boolean recycle() {
super.recycle();
this.count = 0;
this.specify = null;
if (this.content.length > defaultSize) {
@@ -320,7 +322,7 @@ public class JsonWriter extends Writer {
}
@Override
public final void writeWrapper(StringConvertWrapper value) {
public final void writeWrapper(StringWrapper value) {
writeTo(false, String.valueOf(value));
}

View File

@@ -52,10 +52,6 @@ public abstract class AsyncConnection implements ReadableByteChannel, WritableBy
//关联的事件数, 小于1表示没有事件
protected final AtomicInteger eventing = new AtomicInteger();
protected AsyncConnection(Context context) {
this(context.getBufferSupplier(), context.getBufferConsumer(), context.getSSLContext());
}
protected AsyncConnection(ObjectPool<ByteBuffer> bufferPool, SSLContext sslContext) {
this(bufferPool, bufferPool, sslContext);
}
@@ -68,6 +64,14 @@ public abstract class AsyncConnection implements ReadableByteChannel, WritableBy
this.sslContext = sslContext;
}
public Supplier<ByteBuffer> getBufferSupplier() {
return this.bufferSupplier;
}
public Consumer<ByteBuffer> getBufferConsumer() {
return this.bufferConsumer;
}
public final long getLastReadTime() {
return readtime;
}
@@ -114,7 +118,6 @@ public abstract class AsyncConnection implements ReadableByteChannel, WritableBy
public abstract void read(CompletionHandler<Integer, ByteBuffer> handler);
public abstract void read(long timeout, TimeUnit unit, CompletionHandler<Integer, ByteBuffer> handler);
@Override
public abstract int write(ByteBuffer src) throws IOException;
@@ -245,22 +248,6 @@ public abstract class AsyncConnection implements ReadableByteChannel, WritableBy
return createTCP(bufferPool, group, null, address, readTimeoutSeconds, writeTimeoutSeconds);
}
/**
* 创建TCP协议客户端连接
*
* @param context Context
* @param address 连接点子
* @param group 连接AsynchronousChannelGroup
* @param readTimeoutSeconds 读取超时秒数
* @param writeTimeoutSeconds 写入超时秒数
*
* @return 连接CompletableFuture
*/
public static CompletableFuture<AsyncConnection> createTCP(final Context context, final AsynchronousChannelGroup group,
final SocketAddress address, final int readTimeoutSeconds, final int writeTimeoutSeconds) {
return createTCP(context.getBufferSupplier(), context.getBufferConsumer(), group, context.getSSLContext(), address, readTimeoutSeconds, writeTimeoutSeconds);
}
/**
* 创建TCP协议客户端连接
*
@@ -371,35 +358,6 @@ public abstract class AsyncConnection implements ReadableByteChannel, WritableBy
return new UdpBioAsyncConnection(bufferPool, bufferPool, ch, sslContext, addr, client0, readTimeoutSeconds0, writeTimeoutSeconds0, livingCounter, closedCounter);
}
public static AsyncConnection create(final Context context, final AsynchronousSocketChannel ch) {
return create(context, ch, (SocketAddress) null, 0, 0);
}
public static AsyncConnection create(final Context context, final AsynchronousSocketChannel ch,
final SocketAddress addr0, final AtomicLong livingCounter, final AtomicLong closedCounter) {
return new TcpAioAsyncConnection(context.getBufferSupplier(), context.getBufferConsumer(), ch, context.sslContext, addr0, context.readTimeoutSeconds, context.writeTimeoutSeconds, livingCounter, closedCounter);
}
public static AsyncConnection create(final Context context, final AsynchronousSocketChannel ch,
final SocketAddress addr0, final int readTimeoutSeconds, final int writeTimeoutSeconds) {
return new TcpAioAsyncConnection(context.getBufferSupplier(), context.getBufferConsumer(), ch, null, addr0, readTimeoutSeconds, writeTimeoutSeconds, null, null);
}
public static AsyncConnection create(final Context context, final AsynchronousSocketChannel ch, SSLContext sslContext,
final SocketAddress addr0, final int readTimeoutSeconds, final int writeTimeoutSeconds) {
return new TcpAioAsyncConnection(context.getBufferSupplier(), context.getBufferConsumer(), ch, sslContext, addr0, readTimeoutSeconds, writeTimeoutSeconds, null, null);
}
public static AsyncConnection create(final Context context, final AsynchronousSocketChannel ch,
final SocketAddress addr0, final int readTimeoutSeconds, final int writeTimeoutSeconds, final AtomicLong livingCounter, final AtomicLong closedCounter) {
return new TcpAioAsyncConnection(context.getBufferSupplier(), context.getBufferConsumer(), ch, null, addr0, readTimeoutSeconds, writeTimeoutSeconds, livingCounter, closedCounter);
}
public static AsyncConnection create(final Context context, final AsynchronousSocketChannel ch, SSLContext sslContext,
final SocketAddress addr0, final int readTimeoutSeconds, final int writeTimeoutSeconds, final AtomicLong livingCounter, final AtomicLong closedCounter) {
return new TcpAioAsyncConnection(context.getBufferSupplier(), context.getBufferConsumer(), ch, sslContext, addr0, readTimeoutSeconds, writeTimeoutSeconds, livingCounter, closedCounter);
}
public static AsyncConnection create(final ObjectPool<ByteBuffer> bufferPool, final AsynchronousSocketChannel ch) {
return create(bufferPool, ch, null, 0, 0);
}

View File

@@ -6,11 +6,8 @@
package org.redkale.net;
import java.net.*;
import java.nio.*;
import java.nio.charset.*;
import java.util.Collection;
import java.util.concurrent.*;
import java.util.function.*;
import java.util.logging.*;
import javax.net.ssl.SSLContext;
import org.redkale.convert.bson.*;
@@ -39,12 +36,6 @@ public class Context {
//ByteBuffer的容量默认8K
protected final int bufferCapacity;
//ByteBuffer对象池
protected final ObjectPool<ByteBuffer> bufferPool;
//Response对象池
protected final ObjectPool<Response> responsePool;
//服务的根Servlet
protected final PrepareServlet prepare;
@@ -83,22 +74,18 @@ public class Context {
public Context(ContextConfig config) {
this(config.serverStartTime, config.logger, config.executor, config.sslContext,
config.bufferCapacity, config.bufferPool, config.responsePool, config.maxconns, config.maxbody,
config.charset, config.address, config.resourceFactory, config.prepare,
config.aliveTimeoutSeconds, config.readTimeoutSeconds, config.writeTimeoutSeconds);
config.bufferCapacity, config.maxconns, config.maxbody, config.charset, config.address, config.resourceFactory,
config.prepare, config.aliveTimeoutSeconds, config.readTimeoutSeconds, config.writeTimeoutSeconds);
}
public Context(long serverStartTime, Logger logger, ThreadPoolExecutor executor, SSLContext sslContext,
int bufferCapacity, ObjectPool<ByteBuffer> bufferPool, ObjectPool<Response> responsePool, final int maxconns,
final int maxbody, Charset charset, InetSocketAddress address, ResourceFactory resourceFactory,
final PrepareServlet prepare, final int aliveTimeoutSeconds, final int readTimeoutSeconds, final int writeTimeoutSeconds) {
int bufferCapacity, final int maxconns, final int maxbody, Charset charset, InetSocketAddress address,
ResourceFactory resourceFactory, PrepareServlet prepare, int aliveTimeoutSeconds, int readTimeoutSeconds, int writeTimeoutSeconds) {
this.serverStartTime = serverStartTime;
this.logger = logger;
this.executor = executor;
this.sslContext = sslContext;
this.bufferCapacity = bufferCapacity;
this.bufferPool = bufferPool;
this.responsePool = responsePool;
this.maxconns = maxconns;
this.maxbody = maxbody;
this.charset = StandardCharsets.UTF_8.equals(charset) ? null : charset;
@@ -160,36 +147,6 @@ public class Context {
return bufferCapacity;
}
public Supplier<ByteBuffer> getBufferSupplier() {
return bufferPool;
}
public Consumer<ByteBuffer> getBufferConsumer() {
return bufferPool;
}
public ByteBuffer pollBuffer() {
return bufferPool.get();
}
public void offerBuffer(ByteBuffer buffer) {
bufferPool.accept(buffer);
}
public void offerBuffer(ByteBuffer... buffers) {
if (buffers == null) return;
for (ByteBuffer buffer : buffers) {
bufferPool.accept(buffer);
}
}
public void offerBuffer(Collection<ByteBuffer> buffers) {
if (buffers == null) return;
for (ByteBuffer buffer : buffers) {
bufferPool.accept(buffer);
}
}
public Logger getLogger() {
return logger;
}
@@ -228,12 +185,6 @@ public class Context {
//ByteBuffer的容量默认8K
public int bufferCapacity;
//ByteBuffer对象池
public ObjectPool<ByteBuffer> bufferPool;
//Response对象池
public ObjectPool<Response> responsePool;
//服务的根Servlet
public PrepareServlet prepare;

View File

@@ -8,7 +8,6 @@ package org.redkale.net;
import java.io.IOException;
import java.nio.*;
import java.nio.channels.*;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.*;
import org.redkale.util.*;
@@ -28,12 +27,15 @@ public class PrepareRunner implements Runnable {
private final Context context;
private final ObjectPool<Response> responsePool;
private ByteBuffer data;
private Response response;
public PrepareRunner(Context context, AsyncConnection channel, ByteBuffer data, Response response) {
public PrepareRunner(Context context, ObjectPool<Response> responsePool, AsyncConnection channel, ByteBuffer data, Response response) {
this.context = context;
this.responsePool = responsePool;
this.channel = channel;
this.data = data;
this.response = response;
@@ -41,8 +43,6 @@ public class PrepareRunner implements Runnable {
@Override
public void run() {
final boolean keepalive = response != null;
final ObjectPool<? extends Response> responsePool = context.responsePool;
if (data != null) { //BIO模式的UDP连接创建AsyncConnection时已经获取到ByteBuffer数据了
if (response == null) response = responsePool.get();
try {
@@ -56,8 +56,7 @@ public class PrepareRunner implements Runnable {
}
if (response == null) response = responsePool.get();
try {
channel.read(keepalive ? context.getAliveTimeoutSeconds() : context.getReadTimeoutSeconds(), TimeUnit.SECONDS,
new CompletionHandler<Integer, ByteBuffer>() {
channel.read(new CompletionHandler<Integer, ByteBuffer>() {
@Override
public void completed(Integer count, ByteBuffer buffer) {
if (count < 1) {
@@ -165,7 +164,7 @@ public class PrepareRunner implements Runnable {
}
protected Response pollResponse() {
return context.responsePool.get();
return responsePool.get();
}
protected Request pollRequest(Response response) {

View File

@@ -43,7 +43,7 @@ public abstract class ProtocolServer {
public abstract <T> void setOption(SocketOption<T> name, T value) throws IOException;
public abstract void accept() throws IOException;
public abstract void accept(Server server) throws IOException;
public abstract void close() throws IOException;

View File

@@ -9,6 +9,7 @@ import java.nio.ByteBuffer;
import java.util.*;
import org.redkale.convert.bson.BsonConvert;
import org.redkale.convert.json.JsonConvert;
import org.redkale.util.ObjectPool;
/**
* 协议请求对象
@@ -23,6 +24,8 @@ public abstract class Request<C extends Context> {
protected final C context;
protected final ObjectPool<ByteBuffer> bufferPool;
protected final BsonConvert bsonConvert;
protected final JsonConvert jsonConvert;
@@ -47,9 +50,9 @@ public abstract class Request<C extends Context> {
protected final Map<String, Object> attributes = new HashMap<>();
protected Request(C context) {
protected Request(C context, ObjectPool<ByteBuffer> bufferPool) {
this.context = context;
this.readBuffer = context.pollBuffer();
this.bufferPool = bufferPool;
this.bsonConvert = context.getBsonConvert();
this.jsonConvert = context.getJsonConvert();
}
@@ -67,7 +70,7 @@ public abstract class Request<C extends Context> {
protected ByteBuffer pollReadBuffer() {
ByteBuffer buffer = this.readBuffer;
this.readBuffer = null;
if (buffer == null) buffer = context.pollBuffer();
if (buffer == null) buffer = bufferPool.get();
return buffer;
}
@@ -77,7 +80,7 @@ public abstract class Request<C extends Context> {
buffer.clear();
this.readBuffer = buffer;
} else {
context.offerBuffer(buffer);
bufferPool.accept(buffer);
}
}

View File

@@ -10,6 +10,7 @@ import java.nio.ByteBuffer;
import java.nio.channels.CompletionHandler;
import java.util.function.*;
import java.util.logging.Level;
import org.redkale.util.ObjectPool;
/**
* 协议响应对象
@@ -26,6 +27,10 @@ public abstract class Response<C extends Context, R extends Request<C>> {
protected final C context;
protected final ObjectPool<ByteBuffer> bufferPool;
protected final ObjectPool<Response> responsePool;
protected final R request;
protected AsyncConnection channel;
@@ -66,15 +71,15 @@ public abstract class Response<C extends Context, R extends Request<C>> {
private void offerResponseBuffer(ByteBuffer attachment) {
if (writeHeadBuffer == null) {
if (context.bufferPool.getRecyclerPredicate().test(attachment)) {
if (bufferPool.getRecyclerPredicate().test(attachment)) {
writeHeadBuffer = attachment;
}
} else if (writeBodyBuffer == null) {
if (context.bufferPool.getRecyclerPredicate().test(attachment)) {
if (bufferPool.getRecyclerPredicate().test(attachment)) {
writeBodyBuffer = attachment;
}
} else {
context.offerBuffer(attachment);
bufferPool.accept(attachment);
}
}
@@ -108,31 +113,33 @@ public abstract class Response<C extends Context, R extends Request<C>> {
private void offerResponseBuffer(ByteBuffer[] attachments) {
int start = 0;
if (writeHeadBuffer == null && attachments.length > start) {
if (context.bufferPool.getRecyclerPredicate().test(attachments[start])) {
if (bufferPool.getRecyclerPredicate().test(attachments[start])) {
writeHeadBuffer = attachments[start];
start++;
}
}
if (writeBodyBuffer == null && attachments.length > start) {
if (context.bufferPool.getRecyclerPredicate().test(attachments[start])) {
if (bufferPool.getRecyclerPredicate().test(attachments[start])) {
writeBodyBuffer = attachments[start];
start++;
}
}
for (int i = start; i < attachments.length; i++) {
context.offerBuffer(attachments[i]);
bufferPool.accept(attachments[i]);
}
}
};
protected Response(C context, final R request) {
protected Response(C context, final R request, ObjectPool<Response> responsePool) {
this.context = context;
this.request = request;
this.writeHeadBuffer = context.pollBuffer();
this.writeBodyBuffer = context.pollBuffer();
this.bufferPool = request.bufferPool;
this.responsePool = responsePool;
this.writeHeadBuffer = bufferPool.get();
this.writeBodyBuffer = bufferPool.get();
this.bodyBufferSupplier = () -> {
ByteBuffer buffer = writeBodyBuffer;
if (buffer == null) return context.pollBuffer();
if (buffer == null) return bufferPool.get();
writeBodyBuffer = null;
return buffer;
};
@@ -141,14 +148,14 @@ public abstract class Response<C extends Context, R extends Request<C>> {
protected ByteBuffer pollWriteReadBuffer() {
ByteBuffer buffer = this.writeHeadBuffer;
this.writeHeadBuffer = null;
if (buffer == null) buffer = context.pollBuffer();
if (buffer == null) buffer = bufferPool.get();
return buffer;
}
protected ByteBuffer pollWriteBodyBuffer() {
ByteBuffer buffer = this.writeBodyBuffer;
this.writeBodyBuffer = null;
if (buffer == null) buffer = context.pollBuffer();
if (buffer == null) buffer = bufferPool.get();
return buffer;
}
@@ -157,7 +164,9 @@ public abstract class Response<C extends Context, R extends Request<C>> {
}
protected void offerBuffer(ByteBuffer... buffers) {
context.offerBuffer(buffers);
for (ByteBuffer buffer : buffers) {
bufferPool.accept(buffer);
}
}
protected AsyncConnection removeChannel() {
@@ -257,19 +266,19 @@ public abstract class Response<C extends Context, R extends Request<C>> {
AsyncConnection conn = removeChannel();
this.recycle();
this.prepare();
new PrepareRunner(context, conn, null, this).run();
new PrepareRunner(context, this.responsePool, conn, null, this).run();
} else {
channel.dispose();
}
} else {
this.context.responsePool.accept(this);
this.responsePool.accept(this);
}
}
public void finish(final byte[] bs) {
if (!this.inited) return; //避免重复关闭
if (this.context.bufferCapacity == bs.length) {
ByteBuffer buffer = this.context.pollBuffer();
ByteBuffer buffer = this.bufferPool.get();
buffer.put(bs);
buffer.flip();
this.finish(buffer);
@@ -285,7 +294,7 @@ public abstract class Response<C extends Context, R extends Request<C>> {
final boolean more = data != null && this.request.keepAlive;
this.request.more = more;
conn.write(buffer, buffer, finishHandler);
if (more) new PrepareRunner(this.context, conn, data, null).run();
if (more) new PrepareRunner(this.context, this.responsePool, conn, data, null).run();
}
public void finish(boolean kill, ByteBuffer buffer) {
@@ -296,7 +305,7 @@ public abstract class Response<C extends Context, R extends Request<C>> {
final boolean more = data != null && this.request.keepAlive;
this.request.more = more;
conn.write(buffer, buffer, finishHandler);
if (more) new PrepareRunner(this.context, conn, data, null).run();
if (more) new PrepareRunner(this.context, this.responsePool, conn, data, null).run();
}
public void finish(ByteBuffer... buffers) {
@@ -306,7 +315,7 @@ public abstract class Response<C extends Context, R extends Request<C>> {
final boolean more = data != null && this.request.keepAlive;
this.request.more = more;
conn.write(buffers, buffers, finishHandler2);
if (more) new PrepareRunner(this.context, conn, data, null).run();
if (more) new PrepareRunner(this.context, this.responsePool, conn, data, null).run();
}
public void finish(boolean kill, ByteBuffer... buffers) {
@@ -317,7 +326,7 @@ public abstract class Response<C extends Context, R extends Request<C>> {
final boolean more = data != null && this.request.keepAlive;
this.request.more = more;
conn.write(buffers, buffers, finishHandler2);
if (more) new PrepareRunner(this.context, conn, data, null).run();
if (more) new PrepareRunner(this.context, this.responsePool, conn, data, null).run();
}
protected <A> void send(final ByteBuffer buffer, final A attachment, final CompletionHandler<Integer, A> handler) {
@@ -328,14 +337,14 @@ public abstract class Response<C extends Context, R extends Request<C>> {
if (buffer.hasRemaining()) {
channel.write(buffer, attachment, this);
} else {
context.offerBuffer(buffer);
bufferPool.accept(buffer);
if (handler != null) handler.completed(result, attachment);
}
}
@Override
public void failed(Throwable exc, A attachment) {
context.offerBuffer(buffer);
bufferPool.accept(buffer);
if (handler != null) handler.failed(exc, attachment);
}
@@ -353,7 +362,7 @@ public abstract class Response<C extends Context, R extends Request<C>> {
index = i;
break;
}
context.offerBuffer(buffers[i]);
bufferPool.accept(buffers[i]);
}
if (index == 0) {
channel.write(buffers, attachment, this);
@@ -367,7 +376,7 @@ public abstract class Response<C extends Context, R extends Request<C>> {
@Override
public void failed(Throwable exc, A attachment) {
for (ByteBuffer buffer : buffers) {
context.offerBuffer(buffer);
bufferPool.accept(buffer);
}
if (handler != null) handler.failed(exc, attachment);
}

View File

@@ -7,14 +7,14 @@ package org.redkale.net;
import java.io.*;
import java.net.*;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.text.*;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.*;
import java.util.logging.*;
import javax.net.ssl.SSLContext;
import org.redkale.net.Filter;
import org.redkale.util.*;
/**
@@ -127,8 +127,8 @@ public abstract class Server<K extends Serializable, C extends Context, R extend
this.writeTimeoutSeconds = config.getIntValue("writeTimeoutSeconds", 0);
this.backlog = parseLenth(config.getValue("backlog"), 8 * 1024);
this.maxbody = parseLenth(config.getValue("maxbody"), 64 * 1024);
int bufCapacity = parseLenth(config.getValue("bufferCapacity"), 32 * 1024);
this.bufferCapacity = bufCapacity < 8 * 1024 ? 8 * 1024 : bufCapacity;
int bufCapacity = parseLenth(config.getValue("bufferCapacity"), "UDP".equalsIgnoreCase(protocol) ? 1350 : 32 * 1024);
this.bufferCapacity = "UDP".equalsIgnoreCase(protocol) ? bufCapacity : (bufCapacity < 8 * 1024 ? 8 * 1024 : bufCapacity);
this.threads = config.getIntValue("threads", Runtime.getRuntime().availableProcessors() * 32);
this.bufferPoolSize = config.getIntValue("bufferPoolSize", this.threads * 4);
this.responsePoolSize = config.getIntValue("responsePoolSize", this.threads * 2);
@@ -281,13 +281,17 @@ public abstract class Server<K extends Serializable, C extends Context, R extend
this.serverChannel = ProtocolServer.create(this.protocol, context, this.serverClassLoader, config == null ? null : config.getValue("netimpl"));
this.serverChannel.open(config);
serverChannel.bind(address, backlog);
serverChannel.accept();
serverChannel.accept(this);
final String threadName = "[" + Thread.currentThread().getName() + "] ";
postStart();
logger.info(threadName + this.getClass().getSimpleName() + ("TCP".equalsIgnoreCase(protocol) ? "" : ("." + protocol)) + " listen: " + address
+ ", threads: " + threads + ", maxbody: " + formatLenth(context.maxbody) + ", bufferCapacity: " + formatLenth(bufferCapacity) + ", bufferPoolSize: " + bufferPoolSize + ", responsePoolSize: " + responsePoolSize
+ ", started in " + (System.currentTimeMillis() - context.getServerStartTime()) + " ms");
}
protected void postStart() {
}
public void changeAddress(final InetSocketAddress addr) throws IOException {
long s = System.currentTimeMillis();
Objects.requireNonNull(addr);
@@ -299,7 +303,7 @@ public abstract class Server<K extends Serializable, C extends Context, R extend
newServerChannel = ProtocolServer.create(this.protocol, context, this.serverClassLoader, config == null ? null : config.getValue("netimpl"));
newServerChannel.open(config);
newServerChannel.bind(addr, backlog);
newServerChannel.accept();
newServerChannel.accept(this);
} catch (IOException e) {
context.address = oldAddress;
throw e;
@@ -358,6 +362,15 @@ public abstract class Server<K extends Serializable, C extends Context, R extend
protected abstract C createContext();
//必须在 createContext()之后调用
protected abstract ObjectPool<ByteBuffer> createBufferPool(AtomicLong createCounter, AtomicLong cycleCounter, int bufferPoolSize);
//必须在 createContext()之后调用
protected abstract ObjectPool<Response> createResponsePool(AtomicLong createCounter, AtomicLong cycleCounter, int responsePoolSize);
//必须在 createResponsePool()之后调用
protected abstract Creator<Response> createResponseCreator(ObjectPool<ByteBuffer> bufferPool, ObjectPool<Response> responsePool);
public void shutdown() throws IOException {
long s = System.currentTimeMillis();
logger.info(this.getClass().getSimpleName() + "-" + this.protocol + " shutdowning");

View File

@@ -23,11 +23,13 @@ public abstract class Servlet<C extends Context, R extends Request<C>, P extends
AnyValue _conf; //当前Servlet的配置
//Server执行start时运行此方法
public void init(C context, AnyValue config) {
}
public abstract void execute(R request, P response) throws IOException;
//Server执行shutdown后运行此方法
public void destroy(C context, AnyValue config) {
}

View File

@@ -103,13 +103,6 @@ public class TcpAioAsyncConnection extends AsyncConnection {
}
}
@Override
public void read(long timeout, TimeUnit unit, CompletionHandler<Integer, ByteBuffer> handler) {
this.readtime = System.currentTimeMillis();
ByteBuffer dst = pollReadBuffer();
channel.read(dst, timeout < 0 ? 0 : timeout, unit, dst, handler);
}
private <A> void nextWrite(Throwable exc, A attachment) {
BlockingQueue<WriteEntry> queue = this.writeQueue;
if (queue != null && exc != null && !isOpen()) {

View File

@@ -7,10 +7,12 @@ package org.redkale.net;
import java.io.IOException;
import java.net.*;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Set;
import java.util.concurrent.atomic.AtomicLong;
import java.util.logging.Level;
import org.redkale.util.AnyValue;
import org.redkale.util.*;
/**
* 协议底层Server
@@ -70,7 +72,14 @@ public class TcpAioProtocolServer extends ProtocolServer {
}
@Override
public void accept() throws IOException {
public void accept(Server server) throws IOException {
AtomicLong createBufferCounter = new AtomicLong();
AtomicLong cycleBufferCounter = new AtomicLong();
ObjectPool<ByteBuffer> bufferPool = server.createBufferPool(createBufferCounter, cycleBufferCounter, server.bufferPoolSize);
AtomicLong createResponseCounter = new AtomicLong();
AtomicLong cycleResponseCounter = new AtomicLong();
ObjectPool<Response> responsePool = server.createResponsePool(createResponseCounter, cycleResponseCounter, server.responsePoolSize);
responsePool.setCreator(server.createResponseCreator(bufferPool, responsePool));
final AsynchronousServerSocketChannel serchannel = this.serverChannel;
serchannel.accept(null, new CompletionHandler<AsynchronousSocketChannel, Void>() {
@@ -93,9 +102,9 @@ public class TcpAioProtocolServer extends ProtocolServer {
channel.setOption(StandardSocketOptions.SO_RCVBUF, 16 * 1024);
channel.setOption(StandardSocketOptions.SO_SNDBUF, 16 * 1024);
AsyncConnection conn = new TcpAioAsyncConnection(context.getBufferSupplier(), context.getBufferConsumer(), channel,
AsyncConnection conn = new TcpAioAsyncConnection(bufferPool, bufferPool, channel,
context.getSSLContext(), null, context.readTimeoutSeconds, context.writeTimeoutSeconds, livingCounter, closedCounter);
context.runAsync(new PrepareRunner(context, conn, null, null));
context.runAsync(new PrepareRunner(context, responsePool, conn, null, null));
} catch (Throwable e) {
context.logger.log(Level.INFO, channel + " accept error", e);
}

View File

@@ -10,7 +10,6 @@ import java.net.*;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Set;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.*;
import javax.net.ssl.SSLContext;
@@ -142,11 +141,6 @@ public class UdpBioAsyncConnection extends AsyncConnection {
}
}
@Override
public void read(long timeout, TimeUnit unit, CompletionHandler<Integer, ByteBuffer> handler) {
read(handler);
}
@Override
public int read(ByteBuffer dst) throws IOException {
int rs = channel.read(dst);

View File

@@ -11,7 +11,8 @@ import java.nio.ByteBuffer;
import java.nio.channels.DatagramChannel;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import org.redkale.util.AnyValue;
import java.util.concurrent.atomic.AtomicLong;
import org.redkale.util.*;
/**
* 协议底层Server
@@ -70,7 +71,14 @@ public class UdpBioProtocolServer extends ProtocolServer {
}
@Override
public void accept() throws IOException {
public void accept(Server server) throws IOException {
AtomicLong createBufferCounter = new AtomicLong();
AtomicLong cycleBufferCounter = new AtomicLong();
ObjectPool<ByteBuffer> bufferPool = server.createBufferPool(createBufferCounter, cycleBufferCounter, server.bufferPoolSize);
AtomicLong createResponseCounter = new AtomicLong();
AtomicLong cycleResponseCounter = new AtomicLong();
ObjectPool<Response> responsePool = server.createResponsePool(createResponseCounter, cycleResponseCounter, server.responsePoolSize);
responsePool.setCreator(server.createResponseCreator(bufferPool, responsePool));
final DatagramChannel serchannel = this.serverChannel;
final int readTimeoutSeconds = this.context.readTimeoutSeconds;
final int writeTimeoutSeconds = this.context.writeTimeoutSeconds;
@@ -81,15 +89,15 @@ public class UdpBioProtocolServer extends ProtocolServer {
public void run() {
cdl.countDown();
while (running) {
final ByteBuffer buffer = context.pollBuffer();
final ByteBuffer buffer = bufferPool.get();
try {
SocketAddress address = serchannel.receive(buffer);
buffer.flip();
AsyncConnection conn = new UdpBioAsyncConnection(context.getBufferSupplier(), context.getBufferConsumer(), serchannel,
AsyncConnection conn = new UdpBioAsyncConnection(bufferPool, bufferPool, serchannel,
context.getSSLContext(), address, false, readTimeoutSeconds, writeTimeoutSeconds, null, null);
context.runAsync(new PrepareRunner(context, conn, buffer, null));
context.runAsync(new PrepareRunner(context, responsePool, conn, buffer, null));
} catch (Exception e) {
context.offerBuffer(buffer);
bufferPool.accept(buffer);
}
}
}

View File

@@ -28,6 +28,8 @@ public class HttpContext extends Context {
protected final ConcurrentHashMap<Class, Creator> asyncHandlerCreators = new ConcurrentHashMap<>();
protected String remoteAddrHeader;
public HttpContext(HttpContextConfig config) {
super(config);
random.setSeed(Math.abs(System.nanoTime()));
@@ -43,10 +45,6 @@ public class HttpContext extends Context {
return executor;
}
protected ObjectPool<Response> getResponsePool() {
return responsePool;
}
@SuppressWarnings("unchecked")
protected <H extends CompletionHandler> Creator<H> loadAsyncHandlerCreator(Class<H> handlerClass) {
Creator<H> creator = asyncHandlerCreators.get(handlerClass);
@@ -162,5 +160,6 @@ public class HttpContext extends Context {
public static class HttpContextConfig extends ContextConfig {
public String remoteAddrHeader;
}
}

View File

@@ -6,6 +6,7 @@
package org.redkale.net.http;
import org.redkale.net.Filter;
import org.redkale.util.AnyValue;
/**
* HTTP 过滤器 <br>
@@ -17,4 +18,7 @@ import org.redkale.net.Filter;
*/
public abstract class HttpFilter extends Filter<HttpContext, HttpRequest, HttpResponse> {
//Server执行start后运行此方法
public void postStart(HttpContext context, AnyValue config) {
}
}

View File

@@ -402,6 +402,21 @@ public class HttpPrepareServlet extends PrepareServlet<String, HttpContext, Http
return this.resourceHttpServlet;
}
public void postStart(HttpContext context, AnyValue config) {
List filters = getFilters();
synchronized (filters) {
if (!filters.isEmpty()) {
for (Object filter : filters) {
((HttpFilter) filter).postStart(context, config);
}
}
}
this.resourceHttpServlet.postStart(context, config);
getServlets().forEach(s -> {
s.postStart(context, getServletConf(s));
});
}
@Override
public void destroy(HttpContext context, AnyValue config) {
super.destroy(context, config); //必须要执行

View File

@@ -6,11 +6,14 @@
package org.redkale.net.http;
import java.io.*;
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.logging.Level;
import org.redkale.convert.json.JsonConvert;
import org.redkale.net.*;
import org.redkale.util.*;
@@ -40,6 +43,8 @@ public class HttpRequest extends Request<HttpContext> {
protected String requestURI;
private byte[] queryBytes;
private long contentLength = -1;
private String contentType;
@@ -69,15 +74,17 @@ public class HttpRequest extends Request<HttpContext> {
protected int actionid;
protected Annotation[] annotations;
protected Object currentUser;
private final String remoteAddrHeader;
Object attachment; //仅供HttpServlet传递Entry使用
public HttpRequest(HttpContext context, String remoteAddrHeader) {
super(context);
this.remoteAddrHeader = remoteAddrHeader;
public HttpRequest(HttpContext context, ObjectPool<ByteBuffer> bufferPool) {
super(context, bufferPool);
this.remoteAddrHeader = context.remoteAddrHeader;
}
protected boolean isWebSocket() {
@@ -116,9 +123,15 @@ public class HttpRequest extends Request<HttpContext> {
int qst = array.find(index, offset, (byte) '?');
if (qst > 0) {
this.requestURI = array.toDecodeString(index, qst - index, charset).trim();
addParameter(array, qst + 1, offset - qst - 1);
this.queryBytes = array.getBytes(qst + 1, offset - qst - 1);
try {
addParameter(array, qst + 1, offset - qst - 1);
} catch (Exception e) {
this.context.getLogger().log(Level.WARNING, "HttpRequest.addParameter error: " + array.toString(), e);
}
} else {
this.requestURI = array.toDecodeString(index, offset - index, charset).trim();
this.queryBytes = new byte[0];
}
index = ++offset;
this.protocol = array.toString(index, array.size() - index, charset).trim();
@@ -197,7 +210,9 @@ public class HttpRequest extends Request<HttpContext> {
private void parseBody() {
if (this.boundary || bodyparsed) return;
addParameter(array, 0, array.size());
if (this.contentType != null && this.contentType.toLowerCase().contains("x-www-form-urlencoded")) {
addParameter(array, 0, array.size());
}
bodyparsed = true;
}
@@ -298,6 +313,55 @@ public class HttpRequest extends Request<HttpContext> {
return this.actionid;
}
/**
* 获取当前操作Method上的注解集合
*
* @return Annotation[]
*/
public Annotation[] getAnnotations() {
if (this.annotations == null) return new Annotation[0];
Annotation[] newanns = new Annotation[this.annotations.length];
System.arraycopy(this.annotations, 0, newanns, 0, newanns.length);
return newanns;
}
/**
* 获取当前操作Method上的注解
*
* @param <T> 注解泛型
* @param annotationClass 注解类型
*
* @return Annotation
*/
public <T extends Annotation> T getAnnotation(Class<T> annotationClass) {
if (this.annotations == null) return null;
for (Annotation ann : this.annotations) {
if (ann.getClass() == annotationClass) return (T) ann;
}
return null;
}
/**
* 获取当前操作Method上的注解集合
*
* @param <T> 注解泛型
* @param annotationClass 注解类型
*
* @return Annotation[]
*/
public <T extends Annotation> T[] getAnnotationsByType(Class<T> annotationClass) {
if (this.annotations == null) return (T[]) Array.newInstance(annotationClass, 0);
T[] news = (T[]) Array.newInstance(annotationClass, this.annotations.length);
int index = 0;
for (Annotation ann : this.annotations) {
if (ann.getClass() == annotationClass) {
news[index++] = (T) ann;
}
}
if (index < 1) return (T[]) Array.newInstance(annotationClass, 0);
return Arrays.copyOf(news, index);
}
/**
* 获取客户端地址IP
*
@@ -435,6 +499,7 @@ public class HttpRequest extends Request<HttpContext> {
this.method = null;
this.protocol = null;
this.requestURI = null;
this.queryBytes = null;
this.contentType = null;
this.host = null;
this.connection = null;
@@ -443,6 +508,7 @@ public class HttpRequest extends Request<HttpContext> {
this.bodyparsed = false;
this.moduleid = 0;
this.actionid = 0;
this.annotations = null;
this.currentUser = null;
this.attachment = null;
@@ -613,6 +679,15 @@ public class HttpRequest extends Request<HttpContext> {
return requestURI;
}
/**
* 获取请求参数的byte[]
*
* @return byte[]
*/
public byte[] getQueryBytes() {
return queryBytes;
}
/**
* 截取getRequestURI最后的一个/后面的部分
*
@@ -1227,16 +1302,11 @@ public class HttpRequest extends Request<HttpContext> {
* @return String
*/
public String getParametersToString(String prefix) {
final StringBuilder sb = new StringBuilder();
getParameters().forEach((k, v) -> {
if (sb.length() > 0) sb.append('&');
try {
sb.append(k).append('=').append(URLEncoder.encode(v, "UTF-8"));
} catch (IOException ex) {
throw new RuntimeException(ex);
}
});
return (sb.length() > 0 && prefix != null) ? (prefix + sb) : sb.toString();
byte[] rbs = queryBytes;
if (rbs == null || rbs.length < 1) return "";
Charset charset = this.context.getCharset();
String str = charset == null ? new String(rbs, StandardCharsets.UTF_8) : new String(rbs, charset);
return (prefix == null) ? str : (prefix + str);
}
/**

View File

@@ -148,8 +148,8 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
return new ObjectPool<>(creatCounter, cycleCounter, max, creator, (x) -> ((HttpResponse) x).prepare(), (x) -> ((HttpResponse) x).recycle());
}
public HttpResponse(HttpContext context, HttpRequest request, HttpResponseConfig config) {
super(context, request);
public HttpResponse(HttpContext context, HttpRequest request, ObjectPool<Response> responsePool, HttpResponseConfig config) {
super(context, request, responsePool);
this.plainContentType = config.plainContentType == null || config.plainContentType.isEmpty() ? "text/plain; charset=utf-8" : config.plainContentType;
this.jsonContentType = config.jsonContentType == null || config.jsonContentType.isEmpty() ? "application/json; charset=utf-8" : config.jsonContentType;
this.plainContentTypeBytes = ("Content-Type: " + this.plainContentType + "\r\n").getBytes();
@@ -174,6 +174,11 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
return channel;
}
@Override
protected void prepare() {
super.prepare();
}
@Override
protected boolean recycle() {
this.status = 200;
@@ -501,7 +506,7 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
} else if (result.getResult() instanceof CharSequence) {
finish(result.getResult().toString());
} else {
finish(convert, result.getResult());
finish(result.getConvert() == null ? convert : result.getConvert(), result.getResult());
}
} else {
if (hasRender) {
@@ -1197,7 +1202,7 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
@Override
public void failed(Throwable exc, ByteBuffer attachment) {
context.offerBuffer(attachment);
bufferPool.accept(attachment);
finish(true);
try {
filechannel.close();

View File

@@ -8,6 +8,7 @@ package org.redkale.net.http;
import java.io.Serializable;
import java.net.HttpCookie;
import java.util.*;
import org.redkale.convert.*;
import org.redkale.convert.json.JsonConvert;
/**
@@ -22,21 +23,28 @@ public class HttpResult<T> {
public static final String SESSIONID_COOKIENAME = HttpRequest.SESSIONID_NAME;
private Map<String, String> headers;
protected Map<String, String> headers;
private List<HttpCookie> cookies;
protected List<HttpCookie> cookies;
private String contentType;
protected String contentType;
private T result;
protected T result;
private int status = 0; //不设置则为 200
protected int status = 0; //不设置则为 200
private String message;
protected String message;
protected Convert convert;
public HttpResult() {
}
public HttpResult(Convert convert, T result) {
this.convert = convert;
this.result = result;
}
public HttpResult(T result) {
this.result = result;
}
@@ -82,6 +90,15 @@ public class HttpResult<T> {
return this;
}
@ConvertDisabled
public Convert getConvert() {
return convert;
}
public void setConvert(Convert convert) {
this.convert = convert;
}
public Map<String, String> getHeaders() {
return headers;
}

View File

@@ -37,6 +37,8 @@ public class HttpServer extends Server<String, HttpContext, HttpRequest, HttpRes
private byte[] currDateBytes;
private HttpResponseConfig respConfig;
public HttpServer() {
this(System.currentTimeMillis(), ResourceFactory.root());
}
@@ -54,6 +56,11 @@ public class HttpServer extends Server<String, HttpContext, HttpRequest, HttpRes
super.init(config);
}
@Override
protected void postStart() {
((HttpPrepareServlet) this.prepare).postStart(this.context, config);
}
@Override
public void destroy(final AnyValue config) throws Exception {
super.destroy(config);
@@ -304,16 +311,7 @@ public class HttpServer extends Server<String, HttpContext, HttpRequest, HttpRes
@SuppressWarnings("unchecked")
protected HttpContext createContext() {
final int port = this.address.getPort();
AtomicLong createBufferCounter = new AtomicLong();
AtomicLong cycleBufferCounter = new AtomicLong();
this.bufferCapacity = Math.max(this.bufferCapacity, 16 * 1024 + 16); //兼容 HTTP 2.0;
final int rcapacity = this.bufferCapacity;
ObjectPool<ByteBuffer> bufferPool = new ObjectPool<>(createBufferCounter, cycleBufferCounter, this.bufferPoolSize,
(Object... params) -> ByteBuffer.allocateDirect(rcapacity), null, (e) -> {
if (e == null || e.isReadOnly() || e.capacity() != rcapacity) return false;
e.clear();
return true;
});
final List<String[]> defaultAddHeaders = new ArrayList<>();
final List<String[]> defaultSetHeaders = new ArrayList<>();
boolean autoOptions = false;
@@ -423,7 +421,7 @@ public class HttpServer extends Server<String, HttpContext, HttpRequest, HttpRes
final String addrHeader = remoteAddrHeader;
final HttpResponseConfig respConfig = new HttpResponseConfig();
this.respConfig = new HttpResponseConfig();
respConfig.plainContentType = plainContentType;
respConfig.jsonContentType = jsonContentType;
respConfig.defaultAddHeaders = defaultAddHeaders.isEmpty() ? null : defaultAddHeaders.toArray(new String[defaultAddHeaders.size()][]);
@@ -433,18 +431,12 @@ public class HttpServer extends Server<String, HttpContext, HttpRequest, HttpRes
respConfig.dateSupplier = dateSupplier;
respConfig.renders = ((HttpPrepareServlet) prepare).renders;
AtomicLong createResponseCounter = new AtomicLong();
AtomicLong cycleResponseCounter = new AtomicLong();
ObjectPool<Response> responsePool = HttpResponse.createPool(createResponseCounter, cycleResponseCounter, this.responsePoolSize, null);
final HttpContextConfig contextConfig = new HttpContextConfig();
contextConfig.serverStartTime = this.serverStartTime;
contextConfig.logger = this.logger;
contextConfig.executor = this.executor;
contextConfig.sslContext = this.sslContext;
contextConfig.bufferCapacity = rcapacity;
contextConfig.bufferPool = bufferPool;
contextConfig.responsePool = responsePool;
contextConfig.bufferCapacity = this.bufferCapacity;
contextConfig.maxconns = this.maxconns;
contextConfig.maxbody = this.maxbody;
contextConfig.charset = this.charset;
@@ -454,9 +446,32 @@ public class HttpServer extends Server<String, HttpContext, HttpRequest, HttpRes
contextConfig.aliveTimeoutSeconds = this.aliveTimeoutSeconds;
contextConfig.readTimeoutSeconds = this.readTimeoutSeconds;
contextConfig.writeTimeoutSeconds = this.writeTimeoutSeconds;
contextConfig.remoteAddrHeader = addrHeader;
HttpContext httpcontext = new HttpContext(contextConfig);
responsePool.setCreator((Object... params) -> new HttpResponse(httpcontext, new HttpRequest(httpcontext, addrHeader), respConfig));
return httpcontext;
return new HttpContext(contextConfig);
}
@Override
protected ObjectPool<ByteBuffer> createBufferPool(AtomicLong createCounter, AtomicLong cycleCounter, int bufferPoolSize) {
AtomicLong createBufferCounter = new AtomicLong();
AtomicLong cycleBufferCounter = new AtomicLong();
final int rcapacity = this.bufferCapacity;
ObjectPool<ByteBuffer> bufferPool = new ObjectPool<>(createBufferCounter, cycleBufferCounter, bufferPoolSize,
(Object... params) -> ByteBuffer.allocateDirect(rcapacity), null, (e) -> {
if (e == null || e.isReadOnly() || e.capacity() != rcapacity) return false;
e.clear();
return true;
});
return bufferPool;
}
@Override
protected ObjectPool<Response> createResponsePool(AtomicLong createCounter, AtomicLong cycleCounter, int responsePoolSize) {
return HttpResponse.createPool(createCounter, cycleCounter, responsePoolSize, null);
}
@Override
protected Creator<Response> createResponseCreator(ObjectPool<ByteBuffer> bufferPool, ObjectPool<Response> responsePool) {
return (Object... params) -> new HttpResponse(this.context, new HttpRequest(this.context, bufferPool), responsePool, this.respConfig);
}
}

View File

@@ -6,6 +6,7 @@
package org.redkale.net.http;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.nio.ByteBuffer;
import java.util.*;
@@ -71,6 +72,7 @@ public class HttpServlet extends Servlet<HttpContext, HttpRequest, HttpResponse>
request.attachment = entry;
request.moduleid = entry.moduleid;
request.actionid = entry.actionid;
request.annotations = entry.annotations;
if (entry.auth) {
response.thenEvent(authSuccessServlet);
authenticate(request, response);
@@ -103,6 +105,10 @@ public class HttpServlet extends Servlet<HttpContext, HttpRequest, HttpResponse>
void postDestroy(HttpContext context, AnyValue config) {
}
//Server执行start后运行此方法
public void postStart(HttpContext context, AnyValue config) {
}
/**
* <p>
* 预执行方法在execute方法之前运行设置当前用户信息或者加入常规统计和基础检测例如 : <br>
@@ -210,6 +216,7 @@ public class HttpServlet extends Servlet<HttpContext, HttpRequest, HttpResponse>
InnerActionEntry(int moduleid, int actionid, String name, String[] methods, Method method, HttpServlet servlet) {
this(moduleid, actionid, name, methods, method, auth(method), cacheseconds(method), servlet);
this.annotations = annotations(method);
}
//供Rest类使用参数不能随便更改
@@ -232,16 +239,21 @@ public class HttpServlet extends Servlet<HttpContext, HttpRequest, HttpResponse>
} : null;
}
private static boolean auth(Method method) {
protected static boolean auth(Method method) {
HttpMapping mapping = method.getAnnotation(HttpMapping.class);
return mapping == null || mapping.auth();
}
private static int cacheseconds(Method method) {
protected static int cacheseconds(Method method) {
HttpMapping mapping = method.getAnnotation(HttpMapping.class);
return mapping == null ? 0 : mapping.cacheseconds();
}
//Rest.class会用到此方法
protected static Annotation[] annotations(Method method) {
return method.getAnnotations();
}
boolean isNeedCheck() {
return this.moduleid != 0 || this.actionid != 0;
}
@@ -270,9 +282,11 @@ public class HttpServlet extends Servlet<HttpContext, HttpRequest, HttpResponse>
final String[] methods;
final Method method;
final HttpServlet servlet;
Method method;
Annotation[] annotations;
}
private HttpServlet createActionServlet(final Method method) {

View File

@@ -11,6 +11,7 @@ import java.lang.annotation.*;
import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.reflect.*;
import java.net.InetSocketAddress;
import java.nio.channels.CompletionHandler;
import java.util.*;
import java.util.concurrent.CompletableFuture;
@@ -355,6 +356,10 @@ public final class Rest {
fv.visitEnd();
}
}
{ //_redkale_annotations
fv = cw.visitField(ACC_PUBLIC + ACC_STATIC, "_redkale_annotations", "Ljava/util/Map;", "Ljava/util/Map<Ljava/lang/String;[Ljava/lang/annotation/Annotation;>;", null);
fv.visitEnd();
}
{ //_DynWebSocketServlet构造函数
mv = new MethodDebugVisitor(cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null));
mv.visitVarInsn(ALOAD, 0);
@@ -422,7 +427,7 @@ public final class Rest {
}
RestClassLoader newLoader = new RestClassLoader(loader);
Map<String, Annotation[]> msgclassToAnnotations = new HashMap<>();
for (int i = 0; i < messageMethods.size(); i++) { // _DyncXXXWebSocketMessage 子消息List
Method method = messageMethods.get(i);
String endfix = "_" + method.getName() + "_" + (i > 9 ? i : ("0" + i));
@@ -504,6 +509,30 @@ public final class Rest {
mv.visitMaxs(2, 2);
mv.visitEnd();
}
{ //getAnnotations
mv = new MethodDebugVisitor(cw2.visitMethod(ACC_PUBLIC, "getAnnotations", "()[Ljava/lang/annotation/Annotation;", null, null));
mv.visitFieldInsn(GETSTATIC, newDynName, "_redkale_annotations", "Ljava/util/Map;");
mv.visitLdcInsn(newDynMessageFullName + endfix);
mv.visitMethodInsn(INVOKEINTERFACE, "java/util/Map", "get", "(Ljava/lang/Object;)Ljava/lang/Object;", true);
mv.visitTypeInsn(CHECKCAST, "[Ljava/lang/annotation/Annotation;");
mv.visitVarInsn(ASTORE, 1);
mv.visitVarInsn(ALOAD, 1);
Label l2 = new Label();
mv.visitJumpInsn(IFNONNULL, l2);
mv.visitInsn(ICONST_0);
mv.visitTypeInsn(ANEWARRAY, "java/lang/annotation/Annotation");
mv.visitInsn(ARETURN);
mv.visitLabel(l2);
mv.visitFrame(Opcodes.F_APPEND, 1, new Object[]{"[Ljava/lang/annotation/Annotation;"}, 0, null);
mv.visitVarInsn(ALOAD, 1);
mv.visitVarInsn(ALOAD, 1);
mv.visitInsn(ARRAYLENGTH);
mv.visitMethodInsn(INVOKESTATIC, "java/util/Arrays", "copyOf", "([Ljava/lang/Object;I)[Ljava/lang/Object;", false);
mv.visitTypeInsn(CHECKCAST, "[Ljava/lang/annotation/Annotation;");
mv.visitInsn(ARETURN);
mv.visitMaxs(2, 2);
mv.visitEnd();
}
{ //execute
mv = new MethodDebugVisitor(cw2.visitMethod(ACC_PUBLIC, "execute", "(L" + newDynWebSokcetFullName + ";)V", null, null));
mv.visitVarInsn(ALOAD, 0);
@@ -544,6 +573,7 @@ public final class Rest {
}
cw2.visitEnd();
newLoader.loadClass((newDynMessageFullName + endfix).replace('/', '.'), cw2.toByteArray());
msgclassToAnnotations.put(newDynMessageFullName + endfix, method.getAnnotations());
}
{ //_DynXXXWebSocketMessage class
@@ -676,6 +706,7 @@ public final class Rest {
Class<?> newClazz = newLoader.loadClass(newDynName.replace('/', '.'), cw.toByteArray());
try {
T servlet = (T) newClazz.getDeclaredConstructor().newInstance();
newClazz.getField("_redkale_annotations").set(null, msgclassToAnnotations);
if (rws.cryptor() != Cryptor.class) {
Cryptor cryptor = rws.cryptor().getDeclaredConstructor().newInstance();
Field cryptorField = newClazz.getSuperclass().getDeclaredField("cryptor"); //WebSocketServlet
@@ -972,7 +1003,7 @@ public final class Rest {
RestHeader annhead = param.getAnnotation(RestHeader.class);
if (annhead != null) {
if (ptype != String.class) throw new RuntimeException("@RestHeader must on String Parameter in " + method);
if (ptype != String.class && ptype != InetSocketAddress.class) throw new RuntimeException("@RestHeader must on String or InetSocketAddress Parameter in " + method);
n = annhead.name();
radix = annhead.radix();
comment = annhead.comment();
@@ -1810,9 +1841,10 @@ public final class Rest {
// HashMap<String, InnerActionEntry> _createRestInnerActionEntry() {
// HashMap<String, InnerActionEntry> map = new HashMap<>();
// map.put("asyncfind3", new InnerActionEntry(100000,200000,"asyncfind3", new String[]{},null,false,0, new _Dync_asyncfind3_HttpServlet()));
// map.put("asyncfind3", new InnerActionEntry(1,2,"asyncfind2", new String[]{"GET", "POST"},null,true,0, new _Dync_asyncfind2_HttpServlet()));
// map.put("asyncfind2", new InnerActionEntry(1,2,"asyncfind2", new String[]{"GET", "POST"},null,true,0, new _Dync_asyncfind2_HttpServlet()));
// return map;
// }
Map<String, Method> mappingurlToMethod = new HashMap<>();
{ //_createRestInnerActionEntry 方法
mv = new MethodDebugVisitor(cw.visitMethod(0, "_createRestInnerActionEntry", "()Ljava/util/HashMap;", "()Ljava/util/HashMap<Ljava/lang/String;L" + innerEntryName + ";>;", null));
//mv.setDebug(true);
@@ -1822,6 +1854,7 @@ public final class Rest {
mv.visitVarInsn(ASTORE, 1);
for (final MappingEntry entry : entrys) {
mappingurlToMethod.put(entry.mappingurl, entry.mappingMethod);
mv.visitVarInsn(ALOAD, 1);
mv.visitLdcInsn(entry.mappingurl); //name
mv.visitTypeInsn(NEW, innerEntryName); //new InnerActionEntry
@@ -1837,9 +1870,9 @@ public final class Rest {
mv.visitLdcInsn(entry.methods[i]);
mv.visitInsn(AASTORE);
}
mv.visitInsn(ACONST_NULL); //method
mv.visitInsn(ACONST_NULL); //method
mv.visitInsn(entry.auth ? ICONST_1 : ICONST_0); //auth
pushInt(mv, entry.cacheseconds); //cacheseconds
pushInt(mv, entry.cacheseconds); //cacheseconds
mv.visitTypeInsn(NEW, newDynName + "$" + entry.newActionClassName);
mv.visitInsn(DUP);
mv.visitVarInsn(ALOAD, 0);
@@ -1920,7 +1953,12 @@ public final class Rest {
restactMethod.setAccessible(true);
Field tmpentrysfield = HttpServlet.class.getDeclaredField("_tmpentrys");
tmpentrysfield.setAccessible(true);
tmpentrysfield.set(obj, restactMethod.invoke(obj));
HashMap<String, HttpServlet.InnerActionEntry> innerEntryMap = (HashMap) restactMethod.invoke(obj);
for (Map.Entry<String, HttpServlet.InnerActionEntry> en : innerEntryMap.entrySet()) {
Method m = mappingurlToMethod.get(en.getKey());
if (m != null) en.getValue().annotations = HttpServlet.InnerActionEntry.annotations(m);
}
tmpentrysfield.set(obj, innerEntryMap);
return obj;
} catch (Throwable e) {
throw new RuntimeException(e);

View File

@@ -10,7 +10,7 @@ import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
/**
* 只能注解于RestService类的方法的参数或参数内的String字段
* 只能注解于RestService类的方法的参数或参数内的String、java.net.InetSocketAddress字段
* <p>
* 详情见: https://redkale.org
*

View File

@@ -11,10 +11,11 @@ import java.net.*;
import java.nio.ByteBuffer;
import java.util.*;
import java.util.concurrent.*;
import java.util.function.Supplier;
import java.util.function.*;
import java.util.logging.*;
import java.util.stream.Stream;
import org.redkale.convert.Convert;
import org.redkale.net.AsyncConnection;
import org.redkale.util.Comment;
/**
@@ -82,6 +83,10 @@ public abstract class WebSocket<G extends Serializable, T> {
WebSocketEngine _engine; //不可能为空
InetSocketAddress _sncpAddress; //分布式下不可为空
AsyncConnection _channel;//不可能为空
String _sessionid; //不可能为空
G _userid; //不可能为空
@@ -674,12 +679,21 @@ public abstract class WebSocket<G extends Serializable, T> {
}
/**
* 获取ByteBuffer资源池
* 获取ByteBuffer生成器
*
* @return Supplier
*/
protected Supplier<ByteBuffer> getByteBufferSupplier() {
return this._runner.context.getBufferSupplier();
protected Supplier<ByteBuffer> getBufferSupplier() {
return this._channel.getBufferSupplier();
}
/**
* 获取ByteBuffer回收器
*
* @return Consumer
*/
protected Consumer<ByteBuffer> getBufferConsumer() {
return this._channel.getBufferConsumer();
}
//-------------------------------------------------------------------
@@ -814,6 +828,15 @@ public abstract class WebSocket<G extends Serializable, T> {
return forceCloseWebSocket(getUserid()).thenApply((r) -> true);
}
/**
* 获取分布式情况下的SNCP地址, 非分布式下为null
*
* @return InetSocketAddress sncpAddress
*/
public InetSocketAddress getSncpAddress() {
return _sncpAddress;
}
/**
* 获取Logger
*

View File

@@ -7,6 +7,7 @@ package org.redkale.net.http;
import static org.redkale.net.http.WebSocketServlet.DEFAILT_LIVEINTERVAL;
import java.io.*;
import java.nio.ByteBuffer;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.*;
@@ -229,26 +230,45 @@ public class WebSocketEngine {
}
final boolean more = (!(message instanceof WebSocketPacket) || ((WebSocketPacket) message).sendBuffers == null);
if (more) {
Supplier<ByteBuffer> bufferSupplier = null;
Consumer<ByteBuffer> bufferConsumer = null;
//此处的WebSocketPacket只能是包含payload或bytes内容的不能包含sendConvert、sendJson、sendBuffers
final WebSocketPacket packet = (message instanceof WebSocketPacket) ? (WebSocketPacket) message
: ((message == null || message instanceof CharSequence || message instanceof byte[])
? new WebSocketPacket((Serializable) message, last) : new WebSocketPacket(this.sendConvert, false, message, last));
packet.setSendBuffers(packet.encode(context.getBufferSupplier(), context.getBufferConsumer(), cryptor));
//packet.setSendBuffers(packet.encode(context.getBufferSupplier(), context.getBufferConsumer(), cryptor));
CompletableFuture<Integer> future = null;
if (single) {
for (WebSocket websocket : websockets.values()) {
if (predicate != null && !predicate.test(websocket)) continue;
if (bufferSupplier == null) {
bufferSupplier = websocket.getBufferSupplier();
bufferConsumer = websocket.getBufferConsumer();
packet.setSendBuffers(packet.encode(bufferSupplier, bufferConsumer, cryptor));
}
future = future == null ? websocket.sendPacket(packet) : future.thenCombine(websocket.sendPacket(packet), (a, b) -> a | (Integer) b);
}
} else {
for (List<WebSocket> list : websockets2.values()) {
for (WebSocket websocket : list) {
if (predicate != null && !predicate.test(websocket)) continue;
if (bufferSupplier == null) {
bufferSupplier = websocket.getBufferSupplier();
bufferConsumer = websocket.getBufferConsumer();
packet.setSendBuffers(packet.encode(bufferSupplier, bufferConsumer, cryptor));
}
future = future == null ? websocket.sendPacket(packet) : future.thenCombine(websocket.sendPacket(packet), (a, b) -> a | (Integer) b);
}
}
}
if (future != null) future.whenComplete((rs, ex) -> context.offerBuffer(packet.sendBuffers));
final Consumer<ByteBuffer> bufferConsumer0 = bufferConsumer;
if (future != null) future.whenComplete((rs, ex) -> {
if (packet.sendBuffers != null && bufferConsumer0 != null) {
for (ByteBuffer buffer : packet.sendBuffers) {
bufferConsumer0.accept(buffer);
}
}
});
return future == null ? CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY) : future;
} else {
CompletableFuture<Integer> future = null;
@@ -286,16 +306,23 @@ public class WebSocketEngine {
}
final boolean more = (!(message instanceof WebSocketPacket) || ((WebSocketPacket) message).sendBuffers == null) && userids.length > 1;
if (more) {
Supplier<ByteBuffer> bufferSupplier = null;
Consumer<ByteBuffer> bufferConsumer = null;
//此处的WebSocketPacket只能是包含payload或bytes内容的不能包含sendConvert、sendJson、sendBuffers
final WebSocketPacket packet = (message instanceof WebSocketPacket) ? (WebSocketPacket) message
: ((message == null || message instanceof CharSequence || message instanceof byte[])
? new WebSocketPacket((Serializable) message, last) : new WebSocketPacket(this.sendConvert, false, message, last));
packet.setSendBuffers(packet.encode(context.getBufferSupplier(), context.getBufferConsumer(), cryptor));
//packet.setSendBuffers(packet.encode(context.getBufferSupplier(), context.getBufferConsumer(), cryptor));
CompletableFuture<Integer> future = null;
if (single) {
for (Serializable userid : userids) {
WebSocket websocket = websockets.get(userid);
if (websocket == null) continue;
if (bufferSupplier == null) {
bufferSupplier = websocket.getBufferSupplier();
bufferConsumer = websocket.getBufferConsumer();
packet.setSendBuffers(packet.encode(bufferSupplier, bufferConsumer, cryptor));
}
future = future == null ? websocket.sendPacket(packet) : future.thenCombine(websocket.sendPacket(packet), (a, b) -> a | (Integer) b);
}
} else {
@@ -303,11 +330,23 @@ public class WebSocketEngine {
List<WebSocket> list = websockets2.get(userid);
if (list == null) continue;
for (WebSocket websocket : list) {
if (bufferSupplier == null) {
bufferSupplier = websocket.getBufferSupplier();
bufferConsumer = websocket.getBufferConsumer();
packet.setSendBuffers(packet.encode(bufferSupplier, bufferConsumer, cryptor));
}
future = future == null ? websocket.sendPacket(packet) : future.thenCombine(websocket.sendPacket(packet), (a, b) -> a | (Integer) b);
}
}
}
if (future != null) future.whenComplete((rs, ex) -> context.offerBuffer(packet.sendBuffers));
final Consumer<ByteBuffer> bufferConsumer0 = bufferConsumer;
if (future != null) future.whenComplete((rs, ex) -> {
if (packet.sendBuffers != null && bufferConsumer0 != null) {
for (ByteBuffer buffer : packet.sendBuffers) {
bufferConsumer0.accept(buffer);
}
}
});
return future == null ? CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY) : future;
} else {
CompletableFuture<Integer> future = null;

View File

@@ -217,6 +217,7 @@ public abstract class WebSocketNode {
*/
@Local
public CompletableFuture<Boolean> existsWebSocket(final Serializable userid) {
if (userid instanceof WebSocketUserAddress) return existsWebSocket((WebSocketUserAddress) userid);
CompletableFuture<Boolean> localFuture = null;
if (this.localEngine != null) localFuture = CompletableFuture.completedFuture(localEngine.existsLocalWebSocket(userid));
if (this.sncpNodeAddresses == null || this.remoteNode == null) {
@@ -229,7 +230,7 @@ public abstract class WebSocketNode {
CompletableFuture<Collection<InetSocketAddress>> addrsFuture = sncpNodeAddresses.getCollectionAsync(SOURCE_SNCP_USERID_PREFIX + userid, InetSocketAddress.class);
if (semaphore != null) addrsFuture.whenComplete((r, e) -> releaseSemaphore());
CompletableFuture<Boolean> remoteFuture = addrsFuture.thenCompose((Collection<InetSocketAddress> addrs) -> {
if (logger.isLoggable(Level.FINEST)) logger.finest("websocket found userid:" + userid + " on " + addrs);
//if (logger.isLoggable(Level.FINEST)) logger.finest("websocket found userid:" + userid + " on " + addrs);
if (addrs == null || addrs.isEmpty()) return CompletableFuture.completedFuture(false);
CompletableFuture<Boolean> future = null;
for (InetSocketAddress addr : addrs) {
@@ -242,6 +243,38 @@ public abstract class WebSocketNode {
return localFuture == null ? remoteFuture : localFuture.thenCombine(remoteFuture, (a, b) -> a | b);
}
/**
* 判断指定用户是否WebSocket在线
*
* @param userAddress WebSocketUserAddress
*
* @return boolean
*/
@Local
public CompletableFuture<Boolean> existsWebSocket(final WebSocketUserAddress userAddress) {
CompletableFuture<Boolean> localFuture = null;
if (this.localEngine != null) localFuture = CompletableFuture.completedFuture(localEngine.existsLocalWebSocket(userAddress.userid()));
if (this.sncpNodeAddresses == null || this.remoteNode == null) {
if (logger.isLoggable(Level.FINEST)) logger.finest("websocket remote node is null");
//没有CacheSource就不会有分布式节点
return localFuture;
}
Collection<InetSocketAddress> addrs = userAddress.sncpAddresses();
if (addrs != null) addrs = new ArrayList<>(addrs); //不能修改参数内部值
if (userAddress.sncpAddress() != null) {
if (addrs == null) addrs = new ArrayList<>();
addrs.add(userAddress.sncpAddress());
}
if (addrs == null || addrs.isEmpty()) return CompletableFuture.completedFuture(false);
CompletableFuture<Boolean> future = null;
for (InetSocketAddress addr : addrs) {
if (addr == null || addr.equals(localSncpAddress)) continue;
future = future == null ? remoteNode.existsWebSocket(userAddress.userid(), addr)
: future.thenCombine(remoteNode.existsWebSocket(userAddress.userid(), addr), (a, b) -> a | b);
}
return future == null ? CompletableFuture.completedFuture(false) : future;
}
/**
* 强制关闭用户WebSocket
*
@@ -251,17 +284,45 @@ public abstract class WebSocketNode {
*/
@Local
public CompletableFuture<Integer> forceCloseWebSocket(final Serializable userid) {
return forceCloseWebSocket(userid, (WebSocketUserAddress) null);
}
/**
* 强制关闭用户WebSocket
*
* @param userAddress WebSocketUserAddress
*
* @return int
*/
@Local
public CompletableFuture<Integer> forceCloseWebSocket(final WebSocketUserAddress userAddress) {
return forceCloseWebSocket(null, userAddress);
}
private CompletableFuture<Integer> forceCloseWebSocket(final Serializable userid, final WebSocketUserAddress userAddress) {
CompletableFuture<Integer> localFuture = null;
if (this.localEngine != null) localFuture = CompletableFuture.completedFuture(localEngine.forceCloseLocalWebSocket(userid));
if (this.localEngine != null) localFuture = CompletableFuture.completedFuture(localEngine.forceCloseLocalWebSocket(userAddress == null ? userid : userAddress.userid()));
if (this.sncpNodeAddresses == null || this.remoteNode == null) {
if (logger.isLoggable(Level.FINEST)) logger.finest("websocket remote node is null");
//没有CacheSource就不会有分布式节点
return localFuture;
}
//远程节点关闭
tryAcquireSemaphore();
CompletableFuture<Collection<InetSocketAddress>> addrsFuture = sncpNodeAddresses.getCollectionAsync(SOURCE_SNCP_USERID_PREFIX + userid, InetSocketAddress.class);
if (semaphore != null) addrsFuture.whenComplete((r, e) -> releaseSemaphore());
CompletableFuture<Collection<InetSocketAddress>> addrsFuture;
if (userAddress == null) {
tryAcquireSemaphore();
addrsFuture = sncpNodeAddresses.getCollectionAsync(SOURCE_SNCP_USERID_PREFIX + userid, InetSocketAddress.class);
if (semaphore != null) addrsFuture.whenComplete((r, e) -> releaseSemaphore());
} else {
Collection<InetSocketAddress> addrs = userAddress.sncpAddresses();
if (addrs != null) addrs = new ArrayList<>(addrs); //不能修改参数内部值
if (userAddress.sncpAddress() != null) {
if (addrs == null) addrs = new ArrayList<>();
addrs.add(userAddress.sncpAddress());
}
if (addrs == null || addrs.isEmpty()) return CompletableFuture.completedFuture(0);
addrsFuture = CompletableFuture.completedFuture(addrs);
}
CompletableFuture<Integer> remoteFuture = addrsFuture.thenCompose((Collection<InetSocketAddress> addrs) -> {
if (logger.isLoggable(Level.FINEST)) logger.finest("websocket found userid:" + userid + " on " + addrs);
if (addrs == null || addrs.isEmpty()) return CompletableFuture.completedFuture(0);
@@ -292,88 +353,88 @@ public abstract class WebSocketNode {
* 向指定用户发送消息,先发送本地连接,再发送远程连接 <br>
* 如果当前WebSocketNode是远程模式此方法只发送远程连接
*
* @param message 消息内容
* @param userids Stream
* @param message 消息内容
* @param useridOrAddrs Stream
*
* @return 为0表示成功 其他值表示部分发送异常
*/
@Local
public final CompletableFuture<Integer> sendMessage(Object message, final Stream<? extends Serializable> userids) {
return sendMessage((Convert) null, message, true, userids);
public final CompletableFuture<Integer> sendMessage(Object message, final Stream<? extends Serializable> useridOrAddrs) {
return sendMessage((Convert) null, message, true, useridOrAddrs);
}
/**
* 向指定用户发送消息,先发送本地连接,再发送远程连接 <br>
* 如果当前WebSocketNode是远程模式此方法只发送远程连接
*
* @param message 消息内容
* @param userids Serializable[]
* @param message 消息内容
* @param useridOrAddrs Serializable[]
*
* @return 为0表示成功 其他值表示部分发送异常
*/
@Local
public final CompletableFuture<Integer> sendMessage(Object message, final Serializable... userids) {
return sendMessage((Convert) null, message, true, userids);
public final CompletableFuture<Integer> sendMessage(Object message, final Serializable... useridOrAddrs) {
return sendMessage((Convert) null, message, true, useridOrAddrs);
}
/**
* 向指定用户发送消息,先发送本地连接,再发送远程连接 <br>
* 如果当前WebSocketNode是远程模式此方法只发送远程连接
*
* @param convert Convert
* @param message 消息内容
* @param userids Stream
* @param convert Convert
* @param message 消息内容
* @param useridOrAddrs Stream
*
* @return 为0表示成功 其他值表示部分发送异常
*/
@Local
public final CompletableFuture<Integer> sendMessage(final Convert convert, Object message, final Stream<? extends Serializable> userids) {
return sendMessage(convert, message, true, userids);
public final CompletableFuture<Integer> sendMessage(final Convert convert, Object message, final Stream<? extends Serializable> useridOrAddrs) {
return sendMessage(convert, message, true, useridOrAddrs);
}
/**
* 向指定用户发送消息,先发送本地连接,再发送远程连接 <br>
* 如果当前WebSocketNode是远程模式此方法只发送远程连接
*
* @param convert Convert
* @param message 消息内容
* @param userids Serializable[]
* @param convert Convert
* @param message 消息内容
* @param useridOrAddrs Serializable[]
*
* @return 为0表示成功 其他值表示部分发送异常
*/
@Local
public final CompletableFuture<Integer> sendMessage(final Convert convert, Object message, final Serializable... userids) {
return sendMessage(convert, message, true, userids);
public final CompletableFuture<Integer> sendMessage(final Convert convert, Object message, final Serializable... useridOrAddrs) {
return sendMessage(convert, message, true, useridOrAddrs);
}
/**
* 向指定用户发送消息,先发送本地连接,再发送远程连接 <br>
* 如果当前WebSocketNode是远程模式此方法只发送远程连接
*
* @param message 消息内容
* @param last 是否最后一条
* @param userids Stream
* @param message 消息内容
* @param last 是否最后一条
* @param useridOrAddrs Stream
*
* @return 为0表示成功 其他值表示部分发送异常
*/
@Local
public final CompletableFuture<Integer> sendMessage(final Object message, final boolean last, final Stream<? extends Serializable> userids) {
return sendMessage((Convert) null, message, last, userids);
public final CompletableFuture<Integer> sendMessage(final Object message, final boolean last, final Stream<? extends Serializable> useridOrAddrs) {
return sendMessage((Convert) null, message, last, useridOrAddrs);
}
/**
* 向指定用户发送消息,先发送本地连接,再发送远程连接 <br>
* 如果当前WebSocketNode是远程模式此方法只发送远程连接
*
* @param message 消息内容
* @param last 是否最后一条
* @param userids Serializable[]
* @param message 消息内容
* @param last 是否最后一条
* @param useridOrAddrs Serializable[]
*
* @return 为0表示成功 其他值表示部分发送异常
*/
@Local
public final CompletableFuture<Integer> sendMessage(final Object message, final boolean last, final Serializable... userids) {
return sendMessage((Convert) null, message, last, userids);
public final CompletableFuture<Integer> sendMessage(final Object message, final boolean last, final Serializable... useridOrAddrs) {
return sendMessage((Convert) null, message, last, useridOrAddrs);
}
/**
@@ -411,6 +472,13 @@ public abstract class WebSocketNode {
@Local
public CompletableFuture<Integer> sendMessage(final Convert convert, final Object message0, final boolean last, final Serializable... userids) {
if (userids == null || userids.length < 1) return CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY);
if (userids[0] instanceof WebSocketUserAddress) {
WebSocketUserAddress[] useraddrs = new WebSocketUserAddress[userids.length];
for (int i = 0; i < useraddrs.length; i++) {
useraddrs[i] = (WebSocketUserAddress) userids[i];
}
return sendMessage(convert, message0, last, useraddrs);
}
if (message0 instanceof CompletableFuture) return ((CompletableFuture) message0).thenApply(msg -> sendMessage(convert, msg, last, userids));
final Object message = (convert == null || message0 instanceof WebSocketPacket) ? message0 : ((convert instanceof TextConvert) ? new WebSocketPacket(((TextConvert) convert).convertTo(message0), last) : new WebSocketPacket(((BinaryConvert) convert).convertTo(message0), last));
if (this.localEngine != null && this.sncpNodeAddresses == null) { //本地模式且没有分布式
@@ -428,7 +496,7 @@ public abstract class WebSocketNode {
keyuser.put(keys[i], userids[i]);
}
tryAcquireSemaphore();
CompletableFuture<Map<String, Collection<InetSocketAddress>>> addrsFuture = sncpNodeAddresses.getCollectionMapAsync(InetSocketAddress.class, keys);
CompletableFuture<Map<String, Collection<InetSocketAddress>>> addrsFuture = sncpNodeAddresses.getCollectionMapAsync(true, InetSocketAddress.class, keys);
if (semaphore != null) addrsFuture.whenComplete((r, e) -> releaseSemaphore());
rsfuture = addrsFuture.thenCompose((Map<String, Collection<InetSocketAddress>> addrs) -> {
if (addrs == null || addrs.isEmpty()) {
@@ -446,9 +514,9 @@ public abstract class WebSocketNode {
}
CompletableFuture<Integer> future = null;
for (Map.Entry<InetSocketAddress, List<Serializable>> en : addrUsers.entrySet()) {
Serializable[] us = en.getValue().toArray(new Serializable[en.getValue().size()]);
future = future == null ? sendOneAddrMessage(en.getKey(), remoteMessage, last, us)
: future.thenCombine(sendOneAddrMessage(en.getKey(), remoteMessage, last, us), (a, b) -> a | b);
Serializable[] oneaddrUserids = en.getValue().toArray(new Serializable[en.getValue().size()]);
future = future == null ? sendOneAddrMessage(en.getKey(), remoteMessage, last, oneaddrUserids)
: future.thenCombine(sendOneAddrMessage(en.getKey(), remoteMessage, last, oneaddrUserids), (a, b) -> a | b);
}
return future == null ? CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY) : future;
});
@@ -456,6 +524,41 @@ public abstract class WebSocketNode {
return rsfuture == null ? CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY) : rsfuture;
}
/**
* 向指定用户发送消息,先发送本地连接,再发送远程连接 <br>
* 如果当前WebSocketNode是远程模式此方法只发送远程连接
*
* @param convert Convert
* @param message0 消息内容
* @param last 是否最后一条
* @param useraddrs WebSocketUserAddress[]
*
* @return 为0表示成功 其他值表示部分发送异常
*/
@Local
public CompletableFuture<Integer> sendMessage(final Convert convert, final Object message0, final boolean last, final WebSocketUserAddress... useraddrs) {
if (useraddrs == null || useraddrs.length < 1) return CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY);
if (message0 instanceof CompletableFuture) return ((CompletableFuture) message0).thenApply(msg -> sendMessage(convert, msg, last, useraddrs));
final Object message = (convert == null || message0 instanceof WebSocketPacket) ? message0 : ((convert instanceof TextConvert) ? new WebSocketPacket(((TextConvert) convert).convertTo(message0), last) : new WebSocketPacket(((BinaryConvert) convert).convertTo(message0), last));
if (this.localEngine != null && this.sncpNodeAddresses == null) { //本地模式且没有分布式
return this.localEngine.sendLocalMessage(message, last, userAddressToUserids(useraddrs));
}
final Object remoteMessage = formatRemoteMessage(message);
final Map<InetSocketAddress, List<Serializable>> addrUsers = userAddressToAddrMap(useraddrs);
if (logger.isLoggable(Level.FINEST)) {
logger.finest("websocket(localaddr=" + localSncpAddress + ", useraddrs=" + JsonConvert.root().convertTo(useraddrs) + ") found message-addr-userids: " + addrUsers);
}
CompletableFuture<Integer> future = null;
for (Map.Entry<InetSocketAddress, List<Serializable>> en : addrUsers.entrySet()) {
Serializable[] oneaddrUserids = en.getValue().toArray(new Serializable[en.getValue().size()]);
future = future == null ? sendOneAddrMessage(en.getKey(), remoteMessage, last, oneaddrUserids)
: future.thenCombine(sendOneAddrMessage(en.getKey(), remoteMessage, last, oneaddrUserids), (a, b) -> a | b);
}
return future == null ? CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY) : future;
}
protected CompletableFuture<Integer> sendOneUserMessage(final Object message, final boolean last, final Serializable userid) {
if (message instanceof CompletableFuture) return ((CompletableFuture) message).thenApply(msg -> sendOneUserMessage(msg, last, userid));
if (logger.isLoggable(Level.FINEST)) {
@@ -507,6 +610,32 @@ public abstract class WebSocketNode {
return remoteNode.sendMessage(sncpAddr, remoteMessage, last, userids);
}
protected Serializable[] userAddressToUserids(WebSocketUserAddress... useraddrs) {
if (useraddrs == null || useraddrs.length == 1) return new Serializable[0];
Set<Serializable> set = new HashSet<>();
for (WebSocketUserAddress userAddress : useraddrs) {
set.add(userAddress.userid());
}
return set.toArray(new Serializable[set.size()]);
}
protected Map<InetSocketAddress, List<Serializable>> userAddressToAddrMap(WebSocketUserAddress... useraddrs) {
final Map<InetSocketAddress, List<Serializable>> addrUsers = new HashMap<>();
for (WebSocketUserAddress userAddress : useraddrs) {
if (userAddress.sncpAddress() != null) {
addrUsers.computeIfAbsent(userAddress.sncpAddress(), k -> new ArrayList<>()).add(userAddress.userid());
}
if (userAddress.sncpAddresses() != null) {
for (InetSocketAddress addr : userAddress.sncpAddresses()) {
if (addr != null) {
addrUsers.computeIfAbsent(addr, k -> new ArrayList<>()).add(userAddress.userid());
}
}
}
}
return addrUsers;
}
/**
* 广播消息, 给所有人发消息
*
@@ -678,6 +807,13 @@ public abstract class WebSocketNode {
@Local
public CompletableFuture<Integer> sendAction(final WebSocketAction action, final Serializable... userids) {
if (userids == null || userids.length < 1) return CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY);
if (userids[0] instanceof WebSocketUserAddress) {
WebSocketUserAddress[] useraddrs = new WebSocketUserAddress[userids.length];
for (int i = 0; i < useraddrs.length; i++) {
useraddrs[i] = (WebSocketUserAddress) userids[i];
}
return sendAction(action, useraddrs);
}
if (this.localEngine != null && this.sncpNodeAddresses == null) { //本地模式且没有分布式
return this.localEngine.sendLocalAction(action, userids);
}
@@ -692,7 +828,7 @@ public abstract class WebSocketNode {
keyuser.put(keys[i], userids[i]);
}
tryAcquireSemaphore();
CompletableFuture<Map<String, Collection<InetSocketAddress>>> addrsFuture = sncpNodeAddresses.getCollectionMapAsync(InetSocketAddress.class, keys);
CompletableFuture<Map<String, Collection<InetSocketAddress>>> addrsFuture = sncpNodeAddresses.getCollectionMapAsync(true, InetSocketAddress.class, keys);
if (semaphore != null) addrsFuture.whenComplete((r, e) -> releaseSemaphore());
rsfuture = addrsFuture.thenCompose((Map<String, Collection<InetSocketAddress>> addrs) -> {
if (addrs == null || addrs.isEmpty()) {
@@ -710,9 +846,9 @@ public abstract class WebSocketNode {
}
CompletableFuture<Integer> future = null;
for (Map.Entry<InetSocketAddress, List<Serializable>> en : addrUsers.entrySet()) {
Serializable[] us = en.getValue().toArray(new Serializable[en.getValue().size()]);
future = future == null ? sendOneAddrAction(en.getKey(), action, us)
: future.thenCombine(sendOneAddrAction(en.getKey(), action, us), (a, b) -> a | b);
Serializable[] oneaddrUserids = en.getValue().toArray(new Serializable[en.getValue().size()]);
future = future == null ? sendOneAddrAction(en.getKey(), action, oneaddrUserids)
: future.thenCombine(sendOneAddrAction(en.getKey(), action, oneaddrUserids), (a, b) -> a | b);
}
return future == null ? CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY) : future;
});
@@ -720,6 +856,35 @@ public abstract class WebSocketNode {
return rsfuture == null ? CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY) : rsfuture;
}
/**
* 向指定用户发送操作,先发送本地连接,再发送远程连接 <br>
* 如果当前WebSocketNode是远程模式此方法只发送远程连接
*
* @param action 操作参数
* @param useraddrs WebSocketUserAddress[]
*
* @return 为0表示成功 其他值表示部分发送异常
*/
@Local
public CompletableFuture<Integer> sendAction(final WebSocketAction action, final WebSocketUserAddress... useraddrs) {
if (useraddrs == null || useraddrs.length < 1) return CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY);
if (this.localEngine != null && this.sncpNodeAddresses == null) { //本地模式且没有分布式
return this.localEngine.sendLocalAction(action, userAddressToUserids(useraddrs));
}
final Map<InetSocketAddress, List<Serializable>> addrUsers = userAddressToAddrMap(useraddrs);
if (logger.isLoggable(Level.FINEST)) {
logger.finest("websocket(localaddr=" + localSncpAddress + ", useraddrs=" + JsonConvert.root().convertTo(useraddrs) + ") found action-userid-addrs: " + addrUsers);
}
CompletableFuture<Integer> future = null;
for (Map.Entry<InetSocketAddress, List<Serializable>> en : addrUsers.entrySet()) {
Serializable[] oneaddrUserids = en.getValue().toArray(new Serializable[en.getValue().size()]);
future = future == null ? sendOneAddrAction(en.getKey(), action, oneaddrUserids)
: future.thenCombine(sendOneAddrAction(en.getKey(), action, oneaddrUserids), (a, b) -> a | b);
}
return future == null ? CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY) : future;
}
protected CompletableFuture<Integer> sendOneUserAction(final WebSocketAction action, final Serializable userid) {
if (logger.isLoggable(Level.FINEST)) {
logger.finest("websocket want send action {userid:" + userid + ", action:" + action + "} from locale node to " + ((this.localEngine != null) ? "locale" : "remote") + " engine");

View File

@@ -492,7 +492,7 @@ public final class WebSocketPacket {
void parseReceiveMessage(final Logger logger, WebSocketRunner runner, WebSocket webSocket, ByteBuffer... buffers) {
if (webSocket._engine.cryptor != null) {
HttpContext context = webSocket._engine.context;
buffers = webSocket._engine.cryptor.decrypt(buffers, context.getBufferSupplier(), context.getBufferConsumer());
buffers = webSocket._engine.cryptor.decrypt(buffers, webSocket._channel.getBufferSupplier(), webSocket._channel.getBufferConsumer());
}
FrameType selfType = this.type;
final boolean series = selfType == FrameType.SERIES;

View File

@@ -5,6 +5,10 @@
*/
package org.redkale.net.http;
import java.lang.annotation.Annotation;
import java.lang.reflect.Array;
import java.util.Arrays;
/**
*
* 供WebSocket.preOnMessage 方法获取RestWebSocket里OnMessage方法的参数 <br>
@@ -16,6 +20,29 @@ package org.redkale.net.http;
public interface WebSocketParam {
public <T> T getValue(String name);
public String[] getNames();
public Annotation[] getAnnotations();
default <T extends Annotation> T getAnnotation(Class<T> annotationClass) {
for (Annotation ann : getAnnotations()) {
if (ann.getClass() == annotationClass) return (T) ann;
}
return null;
}
default <T extends Annotation> T[] getAnnotationsByType(Class<T> annotationClass) {
Annotation[] annotations = getAnnotations();
if (annotations == null) return (T[]) Array.newInstance(annotationClass, 0);
T[] news = (T[]) Array.newInstance(annotationClass, annotations.length);
int index = 0;
for (Annotation ann : annotations) {
if (ann.getClass() == annotationClass) {
news[index++] = (T) ann;
}
}
if (index < 1) return (T[]) Array.newInstance(annotationClass, 0);
return Arrays.copyOf(news, index);
}
}

View File

@@ -5,7 +5,6 @@
*/
package org.redkale.net.http;
import org.redkale.net.AsyncConnection;
import static org.redkale.net.http.WebSocket.*;
import org.redkale.net.http.WebSocketPacket.FrameType;
import java.nio.ByteBuffer;
@@ -29,8 +28,6 @@ class WebSocketRunner implements Runnable {
private final WebSocketEngine engine;
private final AsyncConnection channel;
private final WebSocket webSocket;
protected final HttpContext context;
@@ -49,13 +46,12 @@ class WebSocketRunner implements Runnable {
protected long lastReadTime;
WebSocketRunner(HttpContext context, WebSocket webSocket, BiConsumer<WebSocket, Object> messageConsumer, AsyncConnection channel) {
WebSocketRunner(HttpContext context, WebSocket webSocket, BiConsumer<WebSocket, Object> messageConsumer) {
this.context = context;
this.engine = webSocket._engine;
this.webSocket = webSocket;
this.mergemsg = webSocket._engine.mergemsg;
this.restMessageConsumer = messageConsumer;
this.channel = channel;
}
@Override
@@ -64,10 +60,10 @@ class WebSocketRunner implements Runnable {
final WebSocketRunner self = this;
try {
webSocket.onConnected();
channel.setReadTimeoutSeconds(300); //读取超时5分钟
if (channel.isOpen()) {
webSocket._channel.setReadTimeoutSeconds(300); //读取超时5分钟
if (webSocket._channel.isOpen()) {
final int wsmaxbody = webSocket._engine.wsmaxbody;
channel.read(new CompletionHandler<Integer, ByteBuffer>() {
webSocket._channel.read(new CompletionHandler<Integer, ByteBuffer>() {
//尚未解析完的数据包
private WebSocketPacket unfinishPacket;
@@ -94,11 +90,11 @@ class WebSocketRunner implements Runnable {
onePacket = unfinishPacket;
unfinishPacket = null;
for (ByteBuffer b : exBuffers) {
context.offerBuffer(b);
webSocket._channel.offerBuffer(b);
}
exBuffers.clear();
} else { //需要继续接收, 此处不能回收readBuffer
channel.read(this);
webSocket._channel.read(this);
return;
}
}
@@ -125,7 +121,7 @@ class WebSocketRunner implements Runnable {
}
//继续监听消息
if (readBuffer.hasRemaining()) { //exBuffers缓存了
readBuffer = context.pollBuffer();
readBuffer = webSocket._channel.pollReadBuffer();
} else {
readBuffer.clear();
}
@@ -133,8 +129,8 @@ class WebSocketRunner implements Runnable {
readBuffer.put(halfBytes.getValue());
halfBytes.setValue(null);
}
channel.setReadBuffer(readBuffer);
channel.read(this);
webSocket._channel.setReadBuffer(readBuffer);
webSocket._channel.read(this);
//消息处理
for (final WebSocketPacket packet : packets) {
@@ -229,11 +225,11 @@ class WebSocketRunner implements Runnable {
//System.out.println("推送消息");
final CompletableFuture<Integer> futureResult = new CompletableFuture<>();
try {
ByteBuffer[] buffers = packet.sendBuffers != null ? packet.duplicateSendBuffers() : packet.encode(this.context.getBufferSupplier(), this.context.getBufferConsumer(), webSocket._engine.cryptor);
ByteBuffer[] buffers = packet.sendBuffers != null ? packet.duplicateSendBuffers() : packet.encode(webSocket._channel.getBufferSupplier(), webSocket._channel.getBufferConsumer(), webSocket._engine.cryptor);
//if (debug) context.getLogger().log(Level.FINEST, "wsrunner.sending websocket message: " + packet);
this.lastSendTime = System.currentTimeMillis();
channel.write(buffers, buffers, new CompletionHandler<Integer, ByteBuffer[]>() {
webSocket._channel.write(buffers, buffers, new CompletionHandler<Integer, ByteBuffer[]>() {
private CompletableFuture<Integer> future = futureResult;
@@ -245,7 +241,7 @@ class WebSocketRunner implements Runnable {
future = null;
if (attachments != null) {
for (ByteBuffer buf : attachments) {
context.offerBuffer(buf);
webSocket._channel.offerBuffer(buf);
}
}
}
@@ -260,7 +256,7 @@ class WebSocketRunner implements Runnable {
}
}
if (index >= 0) { //ByteBuffer[]统一回收的可以采用此写法
channel.write(attachments, index, attachments.length - index, attachments, this);
webSocket._channel.write(attachments, index, attachments.length - index, attachments, this);
return;
}
if (future != null) {
@@ -268,7 +264,7 @@ class WebSocketRunner implements Runnable {
future = null;
if (attachments != null) {
for (ByteBuffer buf : attachments) {
context.offerBuffer(buf);
webSocket._channel.offerBuffer(buf);
}
}
}
@@ -310,7 +306,7 @@ class WebSocketRunner implements Runnable {
if (closed) return null;
closed = true;
CompletableFuture<Void> future = engine.removeLocalThenClose(webSocket);
channel.dispose();
webSocket._channel.dispose();
webSocket.onClose(code, reason);
return future;
}

View File

@@ -202,12 +202,14 @@ public abstract class WebSocketServlet extends HttpServlet implements Resourcabl
}
final WebSocket webSocket = this.createWebSocket();
webSocket._engine = this.node.localEngine;
webSocket._channel = response.getChannel();
webSocket._messageTextType = this.messageTextType;
webSocket._textConvert = textConvert;
webSocket._binaryConvert = binaryConvert;
webSocket._sendConvert = sendConvert;
webSocket._remoteAddress = request.getRemoteAddress();
webSocket._remoteAddr = request.getRemoteAddr();
webSocket._sncpAddress = this.node.localSncpAddress;
initRestWebSocket(webSocket);
CompletableFuture<String> sessionFuture = webSocket.onOpen(request);
if (sessionFuture == null) {
@@ -262,7 +264,8 @@ public abstract class WebSocketServlet extends HttpServlet implements Resourcabl
Consumer<Boolean> task = (oldkilled) -> {
if (oldkilled) {
WebSocketServlet.this.node.localEngine.addLocal(webSocket);
WebSocketRunner runner = new WebSocketRunner(context, webSocket, restMessageConsumer, response.removeChannel());
response.removeChannel();
WebSocketRunner runner = new WebSocketRunner(context, webSocket, restMessageConsumer);
webSocket._runner = runner;
context.runAsync(runner);
response.finish(true);
@@ -283,7 +286,8 @@ public abstract class WebSocketServlet extends HttpServlet implements Resourcabl
}
} else {
WebSocketServlet.this.node.localEngine.addLocal(webSocket);
WebSocketRunner runner = new WebSocketRunner(context, webSocket, restMessageConsumer, response.removeChannel());
response.removeChannel();
WebSocketRunner runner = new WebSocketRunner(context, webSocket, restMessageConsumer);
webSocket._runner = runner;
context.runAsync(runner);
response.finish(true);
@@ -291,14 +295,15 @@ public abstract class WebSocketServlet extends HttpServlet implements Resourcabl
});
} else {
WebSocketServlet.this.node.localEngine.addLocal(webSocket);
WebSocketRunner runner = new WebSocketRunner(context, webSocket, restMessageConsumer, response.removeChannel());
response.removeChannel();
WebSocketRunner runner = new WebSocketRunner(context, webSocket, restMessageConsumer);
webSocket._runner = runner;
context.runAsync(runner);
response.finish(true);
}
};
if (webSocket.delayPackets != null) { //存在待发送的消息
if (temprunner == null) temprunner = new WebSocketRunner(context, webSocket, restMessageConsumer, response.getChannel());
if (temprunner == null) temprunner = new WebSocketRunner(context, webSocket, restMessageConsumer);
List<WebSocketPacket> delayPackets = webSocket.delayPackets;
webSocket.delayPackets = null;
CompletableFuture<Integer> cf = null;
@@ -323,7 +328,7 @@ public abstract class WebSocketServlet extends HttpServlet implements Resourcabl
});
};
if (webSocket.delayPackets != null) { //存在待发送的消息
if (temprunner == null) temprunner = new WebSocketRunner(context, webSocket, restMessageConsumer, response.getChannel());
if (temprunner == null) temprunner = new WebSocketRunner(context, webSocket, restMessageConsumer);
List<WebSocketPacket> delayPackets = webSocket.delayPackets;
webSocket.delayPackets = null;
CompletableFuture<Integer> cf = null;

View File

@@ -0,0 +1,107 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.net.http;
import java.io.Serializable;
import java.net.InetSocketAddress;
import java.util.Collection;
import org.redkale.convert.json.JsonConvert;
/**
* userid 与 sncpaddress组合对象
*
*
* @author zhangjx
*/
public interface WebSocketUserAddress extends Serializable {
Serializable userid();
InetSocketAddress sncpAddress();
Collection<InetSocketAddress> sncpAddresses();
public static WebSocketUserAddress create(WebSocketUserAddress userAddress) {
return new SimpleWebSocketUserAddress(userAddress);
}
public static WebSocketUserAddress create(Serializable userid, InetSocketAddress sncpAddress) {
return new SimpleWebSocketUserAddress(userid, sncpAddress, null);
}
public static WebSocketUserAddress create(Serializable userid, Collection<InetSocketAddress> sncpAddresses) {
return new SimpleWebSocketUserAddress(userid, null, sncpAddresses);
}
public static class SimpleWebSocketUserAddress implements WebSocketUserAddress {
private Serializable userid;
private InetSocketAddress sncpAddress;
private Collection<InetSocketAddress> sncpAddresses;
public SimpleWebSocketUserAddress() {
}
public SimpleWebSocketUserAddress(Serializable userid, InetSocketAddress sncpAddress, Collection<InetSocketAddress> sncpAddresses) {
this.userid = userid;
this.sncpAddress = sncpAddress;
this.sncpAddresses = sncpAddresses;
}
public SimpleWebSocketUserAddress(WebSocketUserAddress userAddress) {
if (userAddress == null) return;
this.userid = userAddress.userid();
this.sncpAddress = userAddress.sncpAddress();
this.sncpAddresses = userAddress.sncpAddresses();
}
@Override
public Serializable userid() {
return userid;
}
@Override
public InetSocketAddress sncpAddress() {
return sncpAddress;
}
@Override
public Collection<InetSocketAddress> sncpAddresses() {
return sncpAddresses;
}
public Serializable getUserid() {
return userid;
}
public void setUserid(Serializable userid) {
this.userid = userid;
}
public InetSocketAddress getSncpAddress() {
return sncpAddress;
}
public void setSncpAddress(InetSocketAddress sncpAddress) {
this.sncpAddress = sncpAddress;
}
public Collection<InetSocketAddress> getSncpAddresses() {
return sncpAddresses;
}
public void setSncpAddresses(Collection<InetSocketAddress> sncpAddresses) {
this.sncpAddresses = sncpAddresses;
}
@Override
public String toString() {
return JsonConvert.root().convertTo(this);
}
}
}

View File

@@ -332,9 +332,10 @@ public final class SncpClient {
return bsonConvert.convertFrom(action.handlerFuncParamIndex >= 0 ? Object.class : action.resultTypes, reader);
} catch (RpcRemoteException re) {
throw re;
} catch (InterruptedException | ExecutionException | TimeoutException e) {
//logger.log(Level.SEVERE, actions[index].method + " sncp (params: " + jsonConvert.convertTo(params) + ") remote error", e);
throw new RpcRemoteException(actions[index].method + " sncp remote error", e);
} catch (TimeoutException e) {
throw new RpcRemoteException(actions[index].method + " sncp remote timeout, params=" + JsonConvert.root().convertTo(params));
} catch (InterruptedException | ExecutionException e) {
throw new RpcRemoteException(actions[index].method + " sncp remote error, params=" + JsonConvert.root().convertTo(params), e);
} finally {
bsonConvert.offerBsonReader(reader);
}
@@ -358,12 +359,12 @@ public final class SncpClient {
return connFuture.thenCompose(conn0 -> {
final CompletableFuture<byte[]> future = new CompletableFuture();
if (conn0 == null) {
future.completeExceptionally(new RuntimeException("sncp " + (conn0 == null ? addr : conn0.getRemoteAddress()) + " cannot connect"));
future.completeExceptionally(new RpcRemoteException("sncp " + (conn0 == null ? addr : conn0.getRemoteAddress()) + " cannot connect, params=" + JsonConvert.root().convertTo(params)));
return future;
}
if (!conn0.isOpen()) {
conn0.dispose();
future.completeExceptionally(new RuntimeException("sncp " + (conn0 == null ? addr : conn0.getRemoteAddress()) + " cannot connect"));
future.completeExceptionally(new RpcRemoteException("sncp " + (conn0 == null ? addr : conn0.getRemoteAddress()) + " cannot connect, params=" + JsonConvert.root().convertTo(params)));
return future;
}
final AsyncConnection conn = conn0;
@@ -403,7 +404,7 @@ public final class SncpClient {
public void completed(Integer count, ByteBuffer buffer) {
try {
if (count < 1 && buffer.remaining() == buffer.limit()) { //没有数据可读
future.completeExceptionally(new RpcRemoteException(action.method + " sncp[" + conn.getRemoteAddress() + "] remote no response data"));
future.completeExceptionally(new RpcRemoteException(action.method + " sncp[" + conn.getRemoteAddress() + "] remote no response data, params=" + JsonConvert.root().convertTo(params)));
conn.offerBuffer(buffer);
transport.offerConnection(true, conn);
return;
@@ -433,7 +434,7 @@ public final class SncpClient {
final int respBodyLength = buffer.getInt();
final int retcode = buffer.getInt();
if (retcode != 0) {
logger.log(Level.SEVERE, action.method + " sncp (params: " + convert.convertTo(params) + ") deal error (retcode=" + retcode + ", retinfo=" + SncpResponse.getRetCodeInfo(retcode) + ")");
logger.log(Level.SEVERE, action.method + " sncp (params: " + convert.convertTo(params) + ") deal error (retcode=" + retcode + ", retinfo=" + SncpResponse.getRetCodeInfo(retcode) + "), params=" + JsonConvert.root().convertTo(params));
throw new RuntimeException("remote service(" + action.method + ") deal error (retcode=" + retcode + ", retinfo=" + SncpResponse.getRetCodeInfo(retcode) + ")");
}
@@ -451,7 +452,7 @@ public final class SncpClient {
success();
}
} catch (Throwable e) {
future.completeExceptionally(new RuntimeException(action.method + " sncp[" + conn.getRemoteAddress() + "] remote response error"));
future.completeExceptionally(new RpcRemoteException(action.method + " sncp[" + conn.getRemoteAddress() + "] remote response error, params=" + JsonConvert.root().convertTo(params)));
transport.offerConnection(true, conn);
if (handler != null) {
final Object handlerAttach = action.handlerAttachParamIndex >= 0 ? params[action.handlerAttachParamIndex] : null;
@@ -487,27 +488,27 @@ public final class SncpClient {
@Override
public void failed(Throwable exc, ByteBuffer attachment2) {
future.completeExceptionally(new RuntimeException(action.method + " sncp remote exec failed"));
future.completeExceptionally(new RpcRemoteException(action.method + " sncp remote exec failed, params=" + JsonConvert.root().convertTo(params)));
conn.offerBuffer(attachment2);
transport.offerConnection(true, conn);
if (handler != null) {
final Object handlerAttach = action.handlerAttachParamIndex >= 0 ? params[action.handlerAttachParamIndex] : null;
handler.failed(exc, handlerAttach);
}
logger.log(Level.SEVERE, action.method + " sncp (params: " + convert.convertTo(params) + ") remote read exec failed", exc);
logger.log(Level.SEVERE, action.method + " sncp (params: " + convert.convertTo(params) + ") remote read exec failed, params=" + JsonConvert.root().convertTo(params), exc);
}
});
}
@Override
public void failed(Throwable exc, ByteBuffer[] attachment) {
future.completeExceptionally(new RuntimeException(action.method + " sncp remote exec failed"));
future.completeExceptionally(new RpcRemoteException(action.method + " sncp remote exec failed, params=" + JsonConvert.root().convertTo(params)));
transport.offerConnection(true, conn);
if (handler != null) {
final Object handlerAttach = action.handlerAttachParamIndex >= 0 ? params[action.handlerAttachParamIndex] : null;
handler.failed(exc, handlerAttach);
}
logger.log(Level.SEVERE, action.method + " sncp (params: " + convert.convertTo(params) + ") remote write exec failed", exc);
logger.log(Level.SEVERE, action.method + " sncp (params: " + convert.convertTo(params) + ") remote write exec failed, params=" + JsonConvert.root().convertTo(params), exc);
}
});
return future;

View File

@@ -112,7 +112,7 @@ public final class SncpDynServlet extends SncpServlet {
@SuppressWarnings("unchecked")
public void execute(SncpRequest request, SncpResponse response) throws IOException {
if (bufferSupplier == null) {
bufferSupplier = request.getContext().getBufferSupplier();
bufferSupplier = request.getBufferPool();
}
final SncpServletAction action = actions.get(request.getActionid());
//logger.log(Level.FINEST, "sncpdyn.execute: " + request + ", " + (action == null ? "null" : action.method));

View File

@@ -45,11 +45,15 @@ public final class SncpRequest extends Request<SncpContext> {
private byte[] bufferbytes = new byte[6];
protected SncpRequest(SncpContext context) {
super(context);
protected SncpRequest(SncpContext context, ObjectPool<ByteBuffer> bufferPool) {
super(context, bufferPool);
this.convert = context.getBsonConvert();
}
protected ObjectPool<ByteBuffer> getBufferPool() {
return this.bufferPool;
}
@Override
protected int readHeader(ByteBuffer buffer) {
if (buffer.remaining() < HEADER_SIZE) {

View File

@@ -45,8 +45,8 @@ public final class SncpResponse extends Response<SncpContext, SncpRequest> {
return null;
}
protected SncpResponse(SncpContext context, SncpRequest request) {
super(context, request);
protected SncpResponse(SncpContext context, SncpRequest request, ObjectPool<Response> responsePool) {
super(context, request, responsePool);
this.addrBytes = context.getServerAddress().getAddress().getAddress();
this.addrPort = context.getServerAddress().getPort();
if (this.addrBytes.length != 4) throw new RuntimeException("SNCP serverAddress only support IPv4");
@@ -56,7 +56,7 @@ public final class SncpResponse extends Response<SncpContext, SncpRequest> {
protected void offerBuffer(ByteBuffer... buffers) {
super.offerBuffer(buffers);
}
public void finish(final int retcode, final BsonWriter out) {
if (out == null) {
final ByteBuffer buffer = pollWriteReadBuffer();

View File

@@ -99,28 +99,14 @@ public class SncpServer extends Server<DLong, SncpContext, SncpRequest, SncpResp
@Override
@SuppressWarnings("unchecked")
protected SncpContext createContext() {
final int port = this.address.getPort();
AtomicLong createBufferCounter = new AtomicLong();
AtomicLong cycleBufferCounter = new AtomicLong();
final int rcapacity = Math.max(this.bufferCapacity, 8 * 1024);
ObjectPool<ByteBuffer> bufferPool = new ObjectPool<>(createBufferCounter, cycleBufferCounter, this.bufferPoolSize,
(Object... params) -> ByteBuffer.allocateDirect(rcapacity), null, (e) -> {
if (e == null || e.isReadOnly() || e.capacity() != rcapacity) return false;
e.clear();
return true;
});
AtomicLong createResponseCounter = new AtomicLong();
AtomicLong cycleResponseCounter = new AtomicLong();
ObjectPool<Response> responsePool = SncpResponse.createPool(createResponseCounter, cycleResponseCounter, this.responsePoolSize, null);
this.bufferCapacity = Math.max(this.bufferCapacity, 8 * 1024);
final SncpContextConfig contextConfig = new SncpContextConfig();
contextConfig.serverStartTime = this.serverStartTime;
contextConfig.logger = this.logger;
contextConfig.executor = this.executor;
contextConfig.sslContext = this.sslContext;
contextConfig.bufferCapacity = rcapacity;
contextConfig.bufferPool = bufferPool;
contextConfig.responsePool = responsePool;
contextConfig.bufferCapacity = this.bufferCapacity;
contextConfig.maxconns = this.maxconns;
contextConfig.maxbody = this.maxbody;
contextConfig.charset = this.charset;
@@ -131,9 +117,31 @@ public class SncpServer extends Server<DLong, SncpContext, SncpRequest, SncpResp
contextConfig.readTimeoutSeconds = this.readTimeoutSeconds;
contextConfig.writeTimeoutSeconds = this.writeTimeoutSeconds;
SncpContext sncpcontext = new SncpContext(contextConfig);
responsePool.setCreator((Object... params) -> new SncpResponse(sncpcontext, new SncpRequest(sncpcontext)));
return sncpcontext;
return new SncpContext(contextConfig);
}
@Override
protected ObjectPool<ByteBuffer> createBufferPool(AtomicLong createCounter, AtomicLong cycleCounter, int bufferPoolSize) {
AtomicLong createBufferCounter = new AtomicLong();
AtomicLong cycleBufferCounter = new AtomicLong();
final int rcapacity = this.bufferCapacity;
ObjectPool<ByteBuffer> bufferPool = new ObjectPool<>(createBufferCounter, cycleBufferCounter, bufferPoolSize,
(Object... params) -> ByteBuffer.allocateDirect(rcapacity), null, (e) -> {
if (e == null || e.isReadOnly() || e.capacity() != rcapacity) return false;
e.clear();
return true;
});
return bufferPool;
}
@Override
protected ObjectPool<Response> createResponsePool(AtomicLong createCounter, AtomicLong cycleCounter, int responsePoolSize) {
return SncpResponse.createPool(createCounter, cycleCounter, responsePoolSize, null);
}
@Override
protected Creator<Response> createResponseCreator(ObjectPool<ByteBuffer> bufferPool, ObjectPool<Response> responsePool) {
return (Object... params) -> new SncpResponse(this.context, new SncpRequest(this.context, bufferPool), responsePool);
}
}

View File

@@ -49,8 +49,8 @@ public @interface RetLabel {
for (Field field : clazz.getFields()) {
if (!Modifier.isStatic(field.getModifiers())) continue;
if (field.getType() != int.class) continue;
RetLabel info = field.getAnnotation(RetLabel.class);
if (info == null) continue;
RetLabel[] infos = field.getAnnotationsByType(RetLabel.class);
if (infos == null || infos.length == 0) continue;
int value;
try {
value = field.getInt(null);
@@ -58,7 +58,9 @@ public @interface RetLabel {
ex.printStackTrace();
continue;
}
rets.computeIfAbsent(info.locale(), (k) -> new HashMap<>()).put(value, info.value());
for (RetLabel info : infos) {
rets.computeIfAbsent(info.locale(), (k) -> new HashMap<>()).put(value, info.value());
}
}
return rets;
}

View File

@@ -8,6 +8,7 @@ package org.redkale.service;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import org.redkale.convert.json.*;
import org.redkale.util.Utility;
/**
* 通用的结果对象在常见的HTTP+JSON接口中返回的结果需要含结果码错误信息和实体对象。 <br>
@@ -66,6 +67,14 @@ public class RetResult<T> {
return CompletableFuture.completedFuture(new RetResult());
}
public static RetResult<Map<String, String>> map(String... items) {
return new RetResult(Utility.ofMap(items));
}
public static <K, V> RetResult<Map<K, V>> map(Object... items) {
return new RetResult(Utility.ofMap(items));
}
/**
* 判断结果是否成功返回, retcode = 0 视为成功, 否则视为错误码
*

View File

@@ -677,7 +677,7 @@ public class CacheMemorySource<V extends Object> extends AbstractService impleme
}
@Override
public <T> Map<String, Collection<T>> getCollectionMap(final Type componentType, final String... keys) {
public <T> Map<String, Collection<T>> getCollectionMap(final boolean set, final Type componentType, final String... keys) {
Map<String, Collection<T>> map = new HashMap<>();
for (String key : keys) {
Collection<T> s = (Collection<T>) get(key);
@@ -692,7 +692,7 @@ public class CacheMemorySource<V extends Object> extends AbstractService impleme
}
@Override
public Map<String, Collection<String>> getStringCollectionMap(final String... keys) {
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);
@@ -707,7 +707,7 @@ public class CacheMemorySource<V extends Object> extends AbstractService impleme
}
@Override
public Map<String, Collection<Long>> getLongCollectionMap(final String... keys) {
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);
@@ -727,8 +727,8 @@ public class CacheMemorySource<V extends Object> extends AbstractService impleme
}
@Override
public CompletableFuture<Map<String, Collection<V>>> getCollectionMapAsync(final Type componentType, final String... keys) {
return CompletableFuture.supplyAsync(() -> getCollectionMap(componentType, keys), getExecutor());
public CompletableFuture<Map<String, Collection<V>>> getCollectionMapAsync(final boolean set, final Type componentType, final String... keys) {
return CompletableFuture.supplyAsync(() -> getCollectionMap(set, componentType, keys), getExecutor());
}
@Override
@@ -737,8 +737,8 @@ public class CacheMemorySource<V extends Object> extends AbstractService impleme
}
@Override
public CompletableFuture<Map<String, Collection<String>>> getStringCollectionMapAsync(final String... keys) {
return CompletableFuture.supplyAsync(() -> getStringCollectionMap(keys), getExecutor());
public CompletableFuture<Map<String, Collection<String>>> getStringCollectionMapAsync(final boolean set, final String... keys) {
return CompletableFuture.supplyAsync(() -> getStringCollectionMap(set, keys), getExecutor());
}
@Override
@@ -747,8 +747,8 @@ public class CacheMemorySource<V extends Object> extends AbstractService impleme
}
@Override
public CompletableFuture<Map<String, Collection<Long>>> getLongCollectionMapAsync(final String... keys) {
return CompletableFuture.supplyAsync(() -> getLongCollectionMap(keys), getExecutor());
public CompletableFuture<Map<String, Collection<Long>>> getLongCollectionMapAsync(final boolean set, final String... keys) {
return CompletableFuture.supplyAsync(() -> getLongCollectionMap(set, keys), getExecutor());
}
@Override

View File

@@ -92,7 +92,7 @@ public interface CacheSource<V extends Object> {
public <T> Collection<T> getCollection(final String key, final Type componentType);
public <T> Map<String, Collection<T>> getCollectionMap(final Type componentType, final String... keys);
public <T> Map<String, Collection<T>> getCollectionMap(final boolean set, final Type componentType, final String... keys);
public int getCollectionSize(final String key);
@@ -140,7 +140,7 @@ public interface CacheSource<V extends Object> {
public Collection<String> getStringCollection(final String key);
public Map<String, Collection<String>> getStringCollectionMap(final String... keys);
public Map<String, Collection<String>> getStringCollectionMap(final boolean set, final String... keys);
public Collection<String> getStringCollectionAndRefresh(final String key, final int expireSeconds);
@@ -164,7 +164,7 @@ public interface CacheSource<V extends Object> {
public Collection<Long> getLongCollection(final String key);
public Map<String, Collection<Long>> getLongCollectionMap(final String... keys);
public Map<String, Collection<Long>> getLongCollectionMap(final boolean set, final String... keys);
public Collection<Long> getLongCollectionAndRefresh(final String key, final int expireSeconds);
@@ -241,7 +241,7 @@ public interface CacheSource<V extends Object> {
public <T> CompletableFuture<Collection<T>> getCollectionAsync(final String key, final Type componentType);
public <T> CompletableFuture<Map<String, Collection<T>>> getCollectionMapAsync(final Type componentType, final String... keys);
public <T> CompletableFuture<Map<String, Collection<T>>> getCollectionMapAsync(final boolean set, final Type componentType, final String... keys);
public CompletableFuture<Integer> getCollectionSizeAsync(final String key);
@@ -289,7 +289,7 @@ public interface CacheSource<V extends Object> {
public CompletableFuture<Collection<String>> getStringCollectionAsync(final String key);
public CompletableFuture<Map<String, Collection<String>>> getStringCollectionMapAsync(final String... keys);
public CompletableFuture<Map<String, Collection<String>>> getStringCollectionMapAsync(final boolean set, final String... keys);
public CompletableFuture<Collection<String>> getStringCollectionAndRefreshAsync(final String key, final int expireSeconds);
@@ -313,7 +313,7 @@ public interface CacheSource<V extends Object> {
public CompletableFuture<Collection<Long>> getLongCollectionAsync(final String key);
public CompletableFuture<Map<String, Collection<Long>>> getLongCollectionMapAsync(final String... keys);
public CompletableFuture<Map<String, Collection<Long>>> getLongCollectionMapAsync(final boolean set, final String... keys);
public CompletableFuture<Collection<Long>> getLongCollectionAndRefreshAsync(final String key, final int expireSeconds);

View File

@@ -15,17 +15,25 @@ package org.redkale.source;
*/
public enum ColumnExpress {
/**
* 直接赋值 col = val
* 赋值 col = val
*/
MOV,
/**
* 加值 col = col + val
* 加值 col = col + val
*/
INC,
/**
* 乘值 col = col * val
*/
MUL,
/**
* 除值 col = col / val
*/
DIV,
/**
* 取模 col = col % val
*/
MOD,
/**
* 与值 col = col &#38; val
*/

View File

@@ -0,0 +1,124 @@
/*
* 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.io.Serializable;
import static org.redkale.source.ColumnExpress.*;
/**
* 作为ColumnValue的value字段值用于复杂的字段表达式 。
* String 视为 字段名
* Number 视为 数值
*
* @author zhangjx
*/
public class ColumnNodeValue implements Serializable {
private Serializable left;//类型只能是String、Number、ColumnNode
private ColumnExpress express; //不能是MOV
private Serializable right;//类型只能是String、Number、ColumnNode
public ColumnNodeValue() {
}
public ColumnNodeValue(Serializable left, ColumnExpress express, Serializable right) {
if (express == null || express == ColumnExpress.MOV) throw new IllegalArgumentException("express cannot be null or MOV");
this.left = left;
this.express = express;
this.right = right;
}
public static ColumnNodeValue create(Serializable left, ColumnExpress express, Serializable right) {
return new ColumnNodeValue(left, express, right);
}
public static ColumnNodeValue inc(Serializable left, Serializable right) {
return new ColumnNodeValue(left, INC, right);
}
public static ColumnNodeValue mul(Serializable left, Serializable right) {
return new ColumnNodeValue(left, MUL, right);
}
public static ColumnNodeValue div(Serializable left, Serializable right) {
return new ColumnNodeValue(left, DIV, right);
}
public static ColumnNodeValue mod(Serializable left, Serializable right) {
return new ColumnNodeValue(left, MOD, right);
}
public static ColumnNodeValue and(Serializable left, Serializable right) {
return new ColumnNodeValue(left, AND, right);
}
public static ColumnNodeValue orr(Serializable left, Serializable right) {
return new ColumnNodeValue(left, ORR, right);
}
public ColumnNodeValue inc(Serializable right) {
return any(INC, right);
}
public ColumnNodeValue mul(Serializable right) {
return any(MUL, right);
}
public ColumnNodeValue div(Serializable right) {
return any(DIV, right);
}
public ColumnNodeValue mod(Serializable right) {
return any(MOD, right);
}
public ColumnNodeValue and(Serializable right) {
return any(AND, right);
}
public ColumnNodeValue orr(Serializable right) {
return any(ORR, right);
}
protected ColumnNodeValue any(ColumnExpress express, Serializable right) {
ColumnNodeValue one = new ColumnNodeValue(this.left, this.express, this.right);
this.left = one;
this.express = express;
this.right = right;
return this;
}
public Serializable getLeft() {
return left;
}
public void setLeft(Serializable left) {
this.left = left;
}
public ColumnExpress getExpress() {
return express;
}
public void setExpress(ColumnExpress express) {
this.express = express;
}
public Serializable getRight() {
return right;
}
public void setRight(Serializable right) {
this.right = right;
}
@Override
public String toString() {
return "{\"column\":" + ((left instanceof CharSequence) ? ("\"" + left + "\"") : left) + ", \"express\":" + express + ", \"value\":" + ((right instanceof CharSequence) ? ("\"" + right + "\"") : right) + "}";
}
}

View File

@@ -86,6 +86,31 @@ public class ColumnValue {
return new ColumnValue(column, MUL, value);
}
/**
* 返回 {column} = {column} / {value} 操作
*
* @param column 字段名
* @param value 字段值
*
* @return ColumnValue
*/
public static ColumnValue div(String column, Serializable value) {
return new ColumnValue(column, DIV, value);
}
/**
* 返回 {column} = {column} % {value} 操作
*
* @param column 字段名
* @param value 字段值
*
* @return ColumnValue
*/
//不常用防止开发者容易在mov时误输入mod
// public static ColumnValue mod(String column, Serializable value) {
// return new ColumnValue(column, MOD, value);
// }
/**
* 返回 {column} = {column} &#38; {value} 操作
*

View File

@@ -30,7 +30,6 @@ public class DataCallAttribute implements Attribute<Object, Serializable> {
Class cltmp = clazz;
do {
for (Field field : cltmp.getDeclaredFields()) {
if (field.getAnnotation(javax.persistence.GeneratedValue.class) == null) continue;
try {
rs = Attribute.create(cltmp, field);
attributes.put(clazz, rs);

View File

@@ -73,13 +73,15 @@ public class DataJdbcSource extends DataSqlSource<Connection> {
if (info.tableStrategy == null || !info.isTableNotExist(se)) throw se;
synchronized (info.tables) {
final String oldTable = info.table;
final String catalog = conn.getCatalog();
final String newTable = info.getTable(entitys[0]);
if (!info.tables.contains(newTable)) {
final String tablekey = newTable.indexOf('.') > 0 ? newTable : (catalog + '.' + newTable);
if (!info.tables.contains(tablekey)) {
try {
Statement st = conn.createStatement();
st.execute(info.tablecopySQL.replace("${newtable}", newTable).replace("${oldtable}", oldTable));
st.close();
info.tables.add(newTable);
info.tables.add(tablekey);
} catch (SQLException sqle) { //多进程并发时可能会出现重复建表
if (newTable.indexOf('.') > 0 && info.isTableNotExist(se)) {
Statement st;
@@ -94,7 +96,7 @@ public class DataJdbcSource extends DataSqlSource<Connection> {
st = conn.createStatement();
st.execute(info.tablecopySQL.replace("${newtable}", newTable).replace("${oldtable}", oldTable));
st.close();
info.tables.add(newTable);
info.tables.add(tablekey);
} catch (SQLException sqle2) {
logger.log(Level.SEVERE, "create table2(" + info.tablecopySQL.replace("${newtable}", newTable).replace("${oldtable}", oldTable) + ") error", sqle2);
}
@@ -112,21 +114,7 @@ public class DataJdbcSource extends DataSqlSource<Connection> {
c1 += cc;
}
c = c1;
}
if (info.autoGenerated) { //由数据库自动生成主键值
ResultSet set = prestmt.getGeneratedKeys();
int i = -1;
while (set.next()) {
if (primaryType == int.class) {
primary.set(entitys[++i], set.getInt(1));
} else if (primaryType == long.class) {
primary.set(entitys[++i], set.getLong(1));
} else {
primary.set(entitys[++i], set.getObject(1));
}
}
set.close();
}
}
prestmt.close();
//------------------------------------------------------------
if (info.isLoggable(logger, Level.FINEST)) { //打印调试信息
@@ -141,7 +129,7 @@ public class DataJdbcSource extends DataSqlSource<Connection> {
if (obj != null && obj.getClass().isArray()) {
sb.append("'[length=").append(java.lang.reflect.Array.getLength(obj)).append("]'");
} else {
sb.append(FilterNode.formatToString(obj));
sb.append(info.formatSQLValue(obj, sqlFormatter));
}
} else {
sb.append(ch);
@@ -164,10 +152,9 @@ public class DataJdbcSource extends DataSqlSource<Connection> {
protected <T> PreparedStatement createInsertPreparedStatement(final Connection conn, final String sql,
final EntityInfo<T> info, T... entitys) throws SQLException {
Attribute<T, Serializable>[] attrs = info.insertAttributes;
final PreparedStatement prestmt = info.autoGenerated ? conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS) : conn.prepareStatement(sql);
final PreparedStatement prestmt = conn.prepareStatement(sql);
for (final T value : entitys) {
if (info.autouuid) info.createPrimaryValue(value);
batchStatementParameters(conn, prestmt, info, attrs, value);
prestmt.addBatch();
}
@@ -177,11 +164,13 @@ public class DataJdbcSource extends DataSqlSource<Connection> {
protected <T> int batchStatementParameters(Connection conn, PreparedStatement prestmt, EntityInfo<T> info, Attribute<T, Serializable>[] attrs, T entity) throws SQLException {
int i = 0;
for (Attribute<T, Serializable> attr : attrs) {
Serializable val = info.getSQLValue(attr, entity);
Object val = info.getSQLValue(attr, entity);
if (val instanceof byte[]) {
Blob blob = conn.createBlob();
blob.setBytes(1, (byte[]) val);
prestmt.setObject(++i, blob);
} else if (val instanceof Boolean) {
prestmt.setObject(++i, ((Boolean) val) ? (byte) 1 : (byte) 0);
} else if (val instanceof AtomicInteger) {
prestmt.setObject(++i, ((AtomicInteger) val).get());
} else if (val instanceof AtomicLong) {
@@ -230,6 +219,7 @@ public class DataJdbcSource extends DataSqlSource<Connection> {
stmt.close();
return CompletableFuture.completedFuture(c);
} catch (SQLException e) {
if (info.isTableNotExist(e)) return CompletableFuture.completedFuture(-1);
CompletableFuture future = new CompletableFuture();
future.completeExceptionally(e);
return future;
@@ -250,6 +240,7 @@ public class DataJdbcSource extends DataSqlSource<Connection> {
stmt.close();
return CompletableFuture.completedFuture(c);
} catch (SQLException e) {
if (info.isTableNotExist(e)) return CompletableFuture.completedFuture(-1);
CompletableFuture future = new CompletableFuture();
future.completeExceptionally(e);
return future;
@@ -285,7 +276,7 @@ public class DataJdbcSource extends DataSqlSource<Connection> {
if (obj != null && obj.getClass().isArray()) {
sb.append("'[length=").append(java.lang.reflect.Array.getLength(obj)).append("]'");
} else {
sb.append(FilterNode.formatToString(obj));
sb.append(info.formatSQLValue(obj, sqlFormatter));
}
} else {
sb.append(ch);

View File

@@ -216,7 +216,7 @@ public interface DataSource {
* @param <T> Entity泛型
* @param clazz Entity类
*
* @return 影响的记录条数
* @return 影响的记录条数 -1表示表不存在
*/
public <T> int clearTable(final Class<T> clazz);
@@ -227,7 +227,7 @@ public interface DataSource {
* @param <T> Entity泛型
* @param clazz Entity类
*
* @return 影响的记录条数CompletableFuture
* @return 影响的记录条数CompletableFuture -1表示表不存在
*/
public <T> CompletableFuture<Integer> clearTableAsync(final Class<T> clazz);
@@ -239,7 +239,7 @@ public interface DataSource {
* @param clazz Entity类
* @param node 过滤条件
*
* @return 影响的记录条数
* @return 影响的记录条数 -1表示表不存在
*/
public <T> int clearTable(final Class<T> clazz, final FilterNode node);
@@ -251,7 +251,7 @@ public interface DataSource {
* @param clazz Entity类
* @param node 过滤条件
*
* @return 影响的记录条数CompletableFuture
* @return 影响的记录条数CompletableFuture -1表示表不存在
*/
public <T> CompletableFuture<Integer> clearTableAsync(final Class<T> clazz, final FilterNode node);
@@ -263,7 +263,7 @@ public interface DataSource {
* @param <T> Entity泛型
* @param clazz Entity类
*
* @return 影响的记录条数
* @return 影响的记录条数 -1表示表不存在
*/
public <T> int dropTable(final Class<T> clazz);
@@ -274,7 +274,7 @@ public interface DataSource {
* @param <T> Entity泛型
* @param clazz Entity类
*
* @return 影响的记录条数CompletableFuture
* @return 影响的记录条数CompletableFuture -1表示表不存在
*/
public <T> CompletableFuture<Integer> dropTableAsync(final Class<T> clazz);
@@ -286,7 +286,7 @@ public interface DataSource {
* @param clazz Entity类
* @param node 过滤条件
*
* @return 影响的记录条数
* @return 影响的记录条数 -1表示表不存在
*/
public <T> int dropTable(final Class<T> clazz, final FilterNode node);
@@ -298,7 +298,7 @@ public interface DataSource {
* @param clazz Entity类
* @param node 过滤条件
*
* @return 影响的记录条数CompletableFuture
* @return 影响的记录条数CompletableFuture -1表示表不存在
*/
public <T> CompletableFuture<Integer> dropTableAsync(final Class<T> clazz, final FilterNode node);

View File

@@ -59,6 +59,8 @@ public abstract class DataSqlSource<DBChannel> extends AbstractService implement
@Resource(name = "$")
protected DataCacheListener cacheListener;
protected final BiFunction<EntityInfo, Object, CharSequence> sqlFormatter;
protected final BiConsumer futureCompleteConsumer = (r, t) -> {
if (t != null) logger.log(Level.SEVERE, "CompletableFuture complete error", (Throwable) t);
};
@@ -107,6 +109,7 @@ public abstract class DataSqlSource<DBChannel> extends AbstractService implement
Semaphore semaphore = maxconns > 0 ? new Semaphore(maxconns) : null;
this.readPool = createPoolSource(this, "read", queue, semaphore, readprop);
this.writePool = createPoolSource(this, "write", queue, semaphore, writeprop);
this.sqlFormatter = (info, val) -> formatValueToString(info, val);
}
@Local
@@ -284,7 +287,7 @@ public abstract class DataSqlSource<DBChannel> extends AbstractService implement
return null;
}
protected <T> String formatValueToString(final EntityInfo<T> info, Object value) {
protected <T> CharSequence formatValueToString(final EntityInfo<T> info, Object value) {
final String dbtype = this.readPool.getDbtype();
if ("mysql".equals(dbtype)) {
if (value == null) return null;
@@ -296,7 +299,7 @@ public abstract class DataSqlSource<DBChannel> extends AbstractService implement
}
return String.valueOf(value);
}
return info.formatToString(value);
return info.formatSQLValue(value, null);
}
//----------------------------- insert -----------------------------
@@ -313,11 +316,6 @@ public abstract class DataSqlSource<DBChannel> extends AbstractService implement
if (entitys.length == 0) return 0;
checkEntity("insert", false, entitys);
final EntityInfo<T> info = loadEntityInfo((Class<T>) entitys[0].getClass());
if (info.autouuid) {
for (T value : entitys) {
info.createPrimaryValue(value);
}
}
if (isOnlyCache(info)) return insertCache(info, entitys);
return insertDB(info, entitys).whenComplete((rs, t) -> {
if (t != null) {
@@ -334,11 +332,6 @@ public abstract class DataSqlSource<DBChannel> extends AbstractService implement
CompletableFuture future = checkEntity("insert", true, entitys);
if (future != null) return future;
final EntityInfo<T> info = loadEntityInfo((Class<T>) entitys[0].getClass());
if (info.autouuid) {
for (T value : entitys) {
info.createPrimaryValue(value);
}
}
if (isOnlyCache(info)) {
return CompletableFuture.supplyAsync(() -> insertCache(info, entitys), getExecutor());
}
@@ -506,13 +499,13 @@ public abstract class DataSqlSource<DBChannel> extends AbstractService implement
protected <T> CompletableFuture<Integer> deleteCompose(final EntityInfo<T> info, final Serializable... pks) {
if (pks.length == 1) {
String sql = "DELETE FROM " + info.getTable(pks[0]) + " WHERE " + info.getPrimarySQLColumn() + " = " + FilterNode.formatToString(info.getSQLValue(info.getPrimarySQLColumn(), pks[0]));
String sql = "DELETE FROM " + info.getTable(pks[0]) + " WHERE " + info.getPrimarySQLColumn() + " = " + info.formatSQLValue(info.getPrimarySQLColumn(), pks[0], sqlFormatter);
return deleteDB(info, null, sql);
}
String sql = "DELETE FROM " + info.getTable(pks[0]) + " WHERE " + info.getPrimarySQLColumn() + " IN (";
for (int i = 0; i < pks.length; i++) {
if (i > 0) sql += ',';
sql += FilterNode.formatToString(info.getSQLValue(info.getPrimarySQLColumn(), pks[i]));
sql += info.formatSQLValue(info.getPrimarySQLColumn(), pks[i], sqlFormatter);
}
sql += ")";
if (info.isLoggable(logger, Level.FINEST, sql)) logger.finest(info.getType().getSimpleName() + " delete sql=" + sql);
@@ -795,11 +788,11 @@ public abstract class DataSqlSource<DBChannel> extends AbstractService implement
protected <T> CompletableFuture<Integer> updateColumnCompose(final EntityInfo<T> info, Serializable pk, String column, final Serializable colval) {
if (colval instanceof byte[]) {
String sql = "UPDATE " + info.getTable(pk) + " SET " + info.getSQLColumn(null, column) + " = " + prepareParamSign(1) + " WHERE " + info.getPrimarySQLColumn() + " = " + FilterNode.formatToString(info.getSQLValue(info.getPrimarySQLColumn(), pk));
String sql = "UPDATE " + info.getTable(pk) + " SET " + info.getSQLColumn(null, column) + " = " + prepareParamSign(1) + " WHERE " + info.getPrimarySQLColumn() + " = " + info.formatSQLValue(info.getPrimarySQLColumn(), pk, sqlFormatter);
return updateDB(info, null, sql, true, colval);
} else {
String sql = "UPDATE " + info.getTable(pk) + " SET " + info.getSQLColumn(null, column) + " = "
+ formatValueToString(info, info.getSQLValue(column, colval)) + " WHERE " + info.getPrimarySQLColumn() + " = " + FilterNode.formatToString(info.getSQLValue(info.getPrimarySQLColumn(), pk));
+ info.formatSQLValue(column, colval, sqlFormatter) + " WHERE " + info.getPrimarySQLColumn() + " = " + info.formatSQLValue(info.getPrimarySQLColumn(), pk, sqlFormatter);
return updateDB(info, null, sql, false);
}
}
@@ -871,7 +864,7 @@ public abstract class DataSqlSource<DBChannel> extends AbstractService implement
return updateDB(info, null, sql, true, colval);
} else {
String sql = "UPDATE " + info.getTable(node) + " a " + (join1 == null ? "" : (", " + join1))
+ " SET " + info.getSQLColumn(alias, column) + " = " + formatValueToString(info, colval)
+ " SET " + info.getSQLColumn(alias, column) + " = " + info.formatSQLValue(colval, sqlFormatter)
+ ((where == null || where.length() == 0) ? (join2 == null ? "" : (" WHERE " + join2))
: (" WHERE " + where + (join2 == null ? "" : (" AND " + join2))));
return updateDB(info, null, sql, false);
@@ -934,17 +927,17 @@ public abstract class DataSqlSource<DBChannel> extends AbstractService implement
Attribute<T, Serializable> attr = info.getUpdateAttribute(col.getColumn());
if (attr == null) throw new RuntimeException(info.getType() + " cannot found column " + col.getColumn());
if (setsql.length() > 0) setsql.append(", ");
String c = info.getSQLColumn(null, col.getColumn());
String sqlColumn = info.getSQLColumn(null, col.getColumn());
if (col.getValue() instanceof byte[]) {
if (blobs == null) blobs = new ArrayList<>();
blobs.add((byte[]) col.getValue());
setsql.append(c).append(" = ").append(prepareParamSign(++index));
setsql.append(sqlColumn).append(" = ").append(prepareParamSign(++index));
} else {
setsql.append(c).append(" = ").append(info.formatSQLValue(c, attr, col));
setsql.append(sqlColumn).append(" = ").append(info.formatSQLValue(sqlColumn, attr, col, sqlFormatter));
}
}
if (setsql.length() < 1) return CompletableFuture.completedFuture(0);
String sql = "UPDATE " + info.getTable(pk) + " SET " + setsql + " WHERE " + info.getPrimarySQLColumn() + " = " + FilterNode.formatToString(info.getSQLValue(info.getPrimarySQLColumn(), pk));
String sql = "UPDATE " + info.getTable(pk) + " SET " + setsql + " WHERE " + info.getPrimarySQLColumn() + " = " + info.formatSQLValue(info.getPrimarySQLColumn(), pk, sqlFormatter);
if (blobs == null) return updateDB(info, null, sql, false);
return updateDB(info, null, sql, true, blobs.toArray());
}
@@ -1016,13 +1009,13 @@ public abstract class DataSqlSource<DBChannel> extends AbstractService implement
Attribute<T, Serializable> attr = info.getUpdateAttribute(col.getColumn());
if (attr == null) continue;
if (setsql.length() > 0) setsql.append(", ");
String c = info.getSQLColumn(alias, col.getColumn());
String sqlColumn = info.getSQLColumn(alias, col.getColumn());
if (col.getValue() instanceof byte[]) {
if (blobs == null) blobs = new ArrayList<>();
blobs.add((byte[]) col.getValue());
setsql.append(c).append(" = ").append(prepareParamSign(++index));
setsql.append(sqlColumn).append(" = ").append(prepareParamSign(++index));
} else {
setsql.append(c).append(" = ").append(info.formatSQLValue(c, attr, col));
setsql.append(sqlColumn).append(" = ").append(info.formatSQLValue(sqlColumn, attr, col, sqlFormatter));
}
}
if (setsql.length() < 1) return CompletableFuture.completedFuture(0);
@@ -1157,7 +1150,7 @@ public abstract class DataSqlSource<DBChannel> extends AbstractService implement
blobs.add((byte[]) val);
setsql.append(" = ").append(prepareParamSign(++index));
} else {
setsql.append(" = ").append(formatValueToString(info, val));
setsql.append(" = ").append(info.formatSQLValue(val, sqlFormatter));
}
}
if (neednode) {
@@ -1177,8 +1170,8 @@ public abstract class DataSqlSource<DBChannel> extends AbstractService implement
if (blobs == null) return updateDB(info, null, sql, false);
return updateDB(info, null, sql, true, blobs.toArray());
} else {
final Serializable id = info.getSQLValue(info.getPrimary(), entity);
String sql = "UPDATE " + info.getTable(id) + " a SET " + setsql + " WHERE " + info.getPrimarySQLColumn() + " = " + FilterNode.formatToString(id);
final Serializable id = (Serializable) info.getSQLValue(info.getPrimary(), entity);
String sql = "UPDATE " + info.getTable(id) + " a SET " + setsql + " WHERE " + info.getPrimarySQLColumn() + " = " + info.formatSQLValue(id, sqlFormatter);
if (blobs == null) return updateDB(info, null, sql, false);
return updateDB(info, null, sql, true, blobs.toArray());
}
@@ -1501,13 +1494,14 @@ public abstract class DataSqlSource<DBChannel> extends AbstractService implement
}
protected <T, K extends Serializable, N extends Number> CompletableFuture<Map<K, N>> queryColumnMapCompose(final EntityInfo<T> info, final String keyColumn, final FilterFunc func, final String funcColumn, FilterNode node) {
final String sqlkey = info.getSQLColumn(null, keyColumn);
final String keySqlColumn = info.getSQLColumn(null, keyColumn);
final Map<Class, String> joinTabalis = node == null ? null : node.getJoinTabalis();
final Set<String> haset = new HashSet<>();
final CharSequence join = node == null ? null : node.createSQLJoin(this, false, joinTabalis, haset, info);
final CharSequence where = node == null ? null : node.createSQLExpress(info, joinTabalis);
final String sql = "SELECT a." + sqlkey + ", " + func.getColumn((funcColumn == null || funcColumn.isEmpty() ? "*" : info.getSQLColumn("a", funcColumn)))
+ " FROM " + info.getTable(node) + " a" + (join == null ? "" : join) + ((where == null || where.length() == 0) ? "" : (" WHERE " + where)) + " GROUP BY a." + sqlkey;
final String funcSqlColumn = func == null ? info.getSQLColumn("a", funcColumn) : func.getColumn((funcColumn == null || funcColumn.isEmpty() ? "*" : info.getSQLColumn("a", funcColumn)));
final String sql = "SELECT a." + keySqlColumn + ", " + funcSqlColumn
+ " FROM " + info.getTable(node) + " a" + (join == null ? "" : join) + ((where == null || where.length() == 0) ? "" : (" WHERE " + where)) + " GROUP BY a." + keySqlColumn;
if (info.isLoggable(logger, Level.FINEST, sql)) logger.finest(info.getType().getSimpleName() + " querycolumnmap sql=" + sql);
return queryColumnMapDB(info, sql, keyColumn);
}
@@ -1557,7 +1551,7 @@ public abstract class DataSqlSource<DBChannel> extends AbstractService implement
protected <T> CompletableFuture<T> findCompose(final EntityInfo<T> info, final SelectColumn selects, Serializable pk) {
String column = info.getPrimarySQLColumn();
final String sql = "SELECT " + info.getQueryColumns(null, selects) + " FROM " + info.getTable(pk) + " WHERE " + column + " = " + FilterNode.formatToString(info.getSQLValue(column, pk));
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);
}
@@ -1694,7 +1688,7 @@ public abstract class DataSqlSource<DBChannel> extends AbstractService implement
}
protected <T> CompletableFuture<Serializable> findColumnCompose(final EntityInfo<T> info, String column, final Serializable defValue, final Serializable pk) {
final String sql = "SELECT " + info.getSQLColumn(null, column) + " FROM " + info.getTable(pk) + " WHERE " + info.getPrimarySQLColumn() + " = " + FilterNode.formatToString(info.getSQLValue(info.getPrimarySQLColumn(), pk));
final String sql = "SELECT " + info.getSQLColumn(null, column) + " FROM " + info.getTable(pk) + " WHERE " + info.getPrimarySQLColumn() + " = " + info.formatSQLValue(info.getPrimarySQLColumn(), pk, sqlFormatter);
if (info.isLoggable(logger, Level.FINEST, sql)) logger.finest(info.getType().getSimpleName() + " find sql=" + sql);
return findColumnDB(info, sql, true, column, defValue);
}
@@ -1756,7 +1750,7 @@ public abstract class DataSqlSource<DBChannel> extends AbstractService implement
}
protected <T> CompletableFuture<Boolean> existsCompose(final EntityInfo<T> info, Serializable pk) {
final String sql = "SELECT COUNT(*) FROM " + info.getTable(pk) + " WHERE " + info.getPrimarySQLColumn() + " = " + FilterNode.formatToString(info.getSQLValue(info.getPrimarySQLColumn(), pk));
final String sql = "SELECT COUNT(*) FROM " + info.getTable(pk) + " WHERE " + info.getPrimarySQLColumn() + " = " + info.formatSQLValue(info.getPrimarySQLColumn(), pk, sqlFormatter);
if (info.isLoggable(logger, Level.FINEST, sql)) logger.finest(info.getType().getSimpleName() + " exists sql=" + sql);
return existsDB(info, sql, true);
}

View File

@@ -265,34 +265,37 @@ public final class EntityCache<T> {
if (filter != null) stream = stream.filter(filter);
Collector<T, Map, ?> collector = null;
final Class valtype = funcAttr == null ? null : funcAttr.type();
switch (func) {
case AVG:
if (valtype == float.class || valtype == Float.class || valtype == double.class || valtype == Double.class) {
collector = (Collector<T, Map, ?>) Collectors.averagingDouble((T t) -> ((Number) funcAttr.get(t)).doubleValue());
} else {
collector = (Collector<T, Map, ?>) Collectors.averagingLong((T t) -> ((Number) funcAttr.get(t)).longValue());
}
break;
case COUNT:
collector = (Collector<T, Map, ?>) Collectors.counting();
break;
case DISTINCTCOUNT:
collector = (Collector<T, Map, ?>) Collectors.mapping((t) -> funcAttr.get(t), Collectors.toSet());
break;
case MAX:
case MIN:
Comparator<T> comp = (o1, o2) -> o1 == null ? (o2 == null ? 0 : -1) : ((Comparable) funcAttr.get(o1)).compareTo(funcAttr.get(o2));
collector = (Collector<T, Map, ?>) ((func == MAX) ? Collectors.maxBy(comp) : Collectors.minBy(comp));
break;
case SUM:
if (valtype == float.class || valtype == Float.class || valtype == double.class || valtype == Double.class) {
collector = (Collector<T, Map, ?>) Collectors.summingDouble((T t) -> ((Number) funcAttr.get(t)).doubleValue());
} else {
collector = (Collector<T, Map, ?>) Collectors.summingLong((T t) -> ((Number) funcAttr.get(t)).longValue());
}
break;
if (func != null) {
switch (func) {
case AVG:
if (valtype == float.class || valtype == Float.class || valtype == double.class || valtype == Double.class) {
collector = (Collector<T, Map, ?>) Collectors.averagingDouble((T t) -> ((Number) funcAttr.get(t)).doubleValue());
} else {
collector = (Collector<T, Map, ?>) Collectors.averagingLong((T t) -> ((Number) funcAttr.get(t)).longValue());
}
break;
case COUNT:
collector = (Collector<T, Map, ?>) Collectors.counting();
break;
case DISTINCTCOUNT:
collector = (Collector<T, Map, ?>) Collectors.mapping((t) -> funcAttr.get(t), Collectors.toSet());
break;
case MAX:
case MIN:
Comparator<T> comp = (o1, o2) -> o1 == null ? (o2 == null ? 0 : -1) : ((Comparable) funcAttr.get(o1)).compareTo(funcAttr.get(o2));
collector = (Collector<T, Map, ?>) ((func == MAX) ? Collectors.maxBy(comp) : Collectors.minBy(comp));
break;
case SUM:
if (valtype == float.class || valtype == Float.class || valtype == double.class || valtype == Double.class) {
collector = (Collector<T, Map, ?>) Collectors.summingDouble((T t) -> ((Number) funcAttr.get(t)).doubleValue());
} else {
collector = (Collector<T, Map, ?>) Collectors.summingLong((T t) -> ((Number) funcAttr.get(t)).longValue());
}
break;
}
}
Map rs = stream.collect(Collectors.groupingBy(t -> keyAttr.get(t), LinkedHashMap::new, collector));
Map rs = collector == null ? stream.collect(Collectors.toMap(t -> keyAttr.get(t), t -> funcAttr.get(t), (key1, key2) -> key2))
: stream.collect(Collectors.groupingBy(t -> keyAttr.get(t), LinkedHashMap::new, collector));
if (func == MAX || func == MIN) {
Map rs2 = new LinkedHashMap();
rs.forEach((x, y) -> {
@@ -599,39 +602,17 @@ public final class EntityCache<T> {
Serializable newval = null;
switch (express) {
case INC:
numb = (Number) attr.get(entity);
if (numb == null) {
numb = (Number) val;
} else {
numb = numb.longValue() + ((Number) val).longValue();
}
break;
case MUL:
numb = (Number) attr.get(entity);
if (numb == null) {
numb = 0;
} else {
numb = numb.longValue() * ((Number) val).floatValue();
}
break;
case DIV:
case MOD:
case AND:
numb = (Number) attr.get(entity);
if (numb == null) {
numb = 0;
} else {
numb = numb.longValue() & ((Number) val).longValue();
}
break;
case ORR:
numb = (Number) attr.get(entity);
if (numb == null) {
numb = 0;
} else {
numb = numb.longValue() | ((Number) val).longValue();
}
numb = getValue((Number) attr.get(entity), express, val);
break;
case MOV:
if (val instanceof ColumnNodeValue) val = updateColumnNodeValue(attr, entity, (ColumnNodeValue) val);
newval = val;
if (val instanceof Number) numb = (Number) val;
break;
}
if (numb != null) {
@@ -663,6 +644,70 @@ public final class EntityCache<T> {
return entity;
}
private <V> Serializable updateColumnNodeValue(Attribute<T, V> attr, final T entity, ColumnNodeValue node) {
Serializable left = node.getLeft();
if (left instanceof CharSequence) {
left = info.getUpdateAttribute(left.toString()).get(entity);
} else if (left instanceof ColumnNodeValue) {
left = updateColumnNodeValue(attr, entity, (ColumnNodeValue) left);
}
Serializable right = node.getRight();
if (left instanceof CharSequence) {
right = info.getUpdateAttribute(right.toString()).get(entity);
} else if (left instanceof ColumnNodeValue) {
right = updateColumnNodeValue(attr, entity, (ColumnNodeValue) right);
}
return getValue((Number) left, node.getExpress(), right);
}
private <V> Number getValue(Number numb, final ColumnExpress express, Serializable val) {
switch (express) {
case INC:
if (numb == null) {
numb = (Number) val;
} else {
numb = numb.longValue() + ((Number) val).longValue();
}
break;
case MUL:
if (numb == null) {
numb = 0;
} else {
numb = numb.longValue() * ((Number) val).floatValue();
}
break;
case DIV:
if (numb == null) {
numb = 0;
} else {
numb = numb.longValue() / ((Number) val).floatValue();
}
break;
case MOD:
if (numb == null) {
numb = 0;
} else {
numb = numb.longValue() % ((Number) val).intValue();
}
break;
case AND:
if (numb == null) {
numb = 0;
} else {
numb = numb.longValue() & ((Number) val).longValue();
}
break;
case ORR:
if (numb == null) {
numb = 0;
} else {
numb = numb.longValue() | ((Number) val).longValue();
}
break;
}
return numb;
}
public Attribute<T, Serializable> getAttribute(String fieldname) {
return info.getAttribute(fieldname);
}

View File

@@ -94,7 +94,7 @@ public final class EntityInfo<T> {
//用于复制表结构使用
final String tablecopySQL;
//用于存在table_20160202类似这种分布式表
//用于存在database.table_20160202类似这种分布式表
final Set<String> tables = new HashSet<>();
//分表 策略
@@ -154,12 +154,6 @@ public final class EntityInfo<T> {
//Flipper.sort转换成以ORDER BY开头SQL的缓存
private final Map<String, String> sortOrderbySqls = new ConcurrentHashMap<>();
//是否由数据库生成主键值
final boolean autoGenerated;
//是否UUID主键
final boolean autouuid;
//所属的DataSource
final DataSource source;
@@ -287,8 +281,6 @@ public final class EntityInfo<T> {
List<Attribute<T, Serializable>> insertattrs = new ArrayList<>();
List<String> updatecols = new ArrayList<>();
List<Attribute<T, Serializable>> updateattrs = new ArrayList<>();
boolean auto = false;
boolean uuid = false;
Map<Class, Creator<CryptHandler>> cryptCreatorMap = new HashMap<>();
do {
for (Field field : cltmp.getDeclaredFields()) {
@@ -318,19 +310,8 @@ public final class EntityInfo<T> {
}
if (field.getAnnotation(javax.persistence.Id.class) != null && idAttr0 == null) {
idAttr0 = attr;
GeneratedValue gv = field.getAnnotation(GeneratedValue.class);
auto = gv != null;
// if (gv != null && gv.strategy() != GenerationType.IDENTITY) {
// throw new RuntimeException(cltmp.getName() + "'s @ID primary not a GenerationType.IDENTITY");
// }
if (gv != null && field.getType() == String.class) { //UUID
uuid = true;
auto = false;
}
if (!auto) {
insertcols.add(sqlfield);
insertattrs.add(attr);
}
insertcols.add(sqlfield);
insertattrs.add(attr);
} else {
if (col == null || col.insertable()) {
insertcols.add(sqlfield);
@@ -456,8 +437,6 @@ public final class EntityInfo<T> {
this.deleteNamesPrepareSQL = null;
this.queryNamesPrepareSQL = null;
}
this.autoGenerated = auto;
this.autouuid = uuid;
//----------------cache--------------
Cacheable c = type.getAnnotation(Cacheable.class);
if (this.table == null || (!cacheForbidden && c != null && c.value())) {
@@ -482,15 +461,6 @@ public final class EntityInfo<T> {
return jsonConvert;
}
/**
* 创建主键值目前只支持UUID赋值
*
* @param src Entity对象
*/
public void createPrimaryValue(T src) {
if (autouuid) getPrimary().set(src, Utility.uuid());
}
/**
* 获取Entity缓存器
*
@@ -536,14 +506,6 @@ public final class EntityInfo<T> {
return table == null;
}
public boolean isAutoGenerated() {
return autoGenerated;
}
public boolean isAutouuid() {
return autouuid;
}
public DistributeTableStrategy<T> getTableStrategy() {
return tableStrategy;
}
@@ -894,21 +856,63 @@ public final class EntityInfo<T> {
return handler.encrypt(fieldvalue);
}
/**
* 字段值转换成带转义的数据库的值
*
* @param fieldname 字段名
* @param fieldvalue 字段值
* @param sqlFormatter 转义器
*
* @return CharSequence
*/
public CharSequence formatSQLValue(String fieldname, Serializable fieldvalue, BiFunction<EntityInfo, Object, CharSequence> sqlFormatter) {
Object val = getSQLValue(fieldname, fieldvalue);
return sqlFormatter == null ? formatToString(val) : sqlFormatter.apply(this, val);
}
/**
* 字段值转换成带转义的数据库的值
*
* @param value 字段值
* @param sqlFormatter 转义器
*
* @return CharSequence
*/
public CharSequence formatSQLValue(Object value, BiFunction<EntityInfo, Object, CharSequence> sqlFormatter) {
return sqlFormatter == null ? formatToString(value) : sqlFormatter.apply(this, value);
}
/**
* 字段值转换成数据库的值
*
* @param <F> 泛型
* @param attr Attribute
* @param entity 记录对象
*
* @return Object
*/
public Serializable getSQLValue(Attribute<T, Serializable> attr, T entity) {
Serializable val = attr.get(entity);
public <F> Object getSQLValue(Attribute<T, F> attr, T entity) {
Object val = attr.get(entity);
CryptHandler cryptHandler = attr.attach();
if (cryptHandler != null) val = (Serializable) cryptHandler.encrypt(val);
if (cryptHandler != null) val = cryptHandler.encrypt(val);
return val;
}
/**
* 字段值转换成带转义的数据库的值
*
* @param <F> 泛型
* @param attr Attribute
* @param entity 记录对象
* @param sqlFormatter 转义器
*
* @return CharSequence
*/
public <F> CharSequence formatSQLValue(Attribute<T, F> attr, T entity, BiFunction<EntityInfo, Object, CharSequence> sqlFormatter) {
Object val = getSQLValue(attr, entity);
return sqlFormatter == null ? formatToString(val) : sqlFormatter.apply(this, val);
}
/**
* 数据库的值转换成数字段值
*
@@ -947,30 +951,68 @@ public final class EntityInfo<T> {
/**
* 拼接UPDATE给字段赋值的SQL片段
*
* @param col 表字段名
* @param attr Attribute
* @param cv ColumnValue
* @param sqlColumn 表字段名
* @param attr Attribute
* @param cv ColumnValue
* @param formatter 转义器
*
* @return CharSequence
*/
protected CharSequence formatSQLValue(String col, Attribute<T, Serializable> attr, final ColumnValue cv) {
protected CharSequence formatSQLValue(String sqlColumn, Attribute<T, Serializable> attr, final ColumnValue cv, BiFunction<EntityInfo, Object, CharSequence> formatter) {
if (cv == null) return null;
Object val = cv.getValue();
CryptHandler handler = attr.attach();
if (handler != null) val = handler.encrypt(val);
if (val instanceof ColumnNodeValue && cv.getExpress() == ColumnExpress.MOV) return formatSQLValue(attr, (ColumnNodeValue) val, formatter);
switch (cv.getExpress()) {
case INC:
return new StringBuilder().append(col).append(" + ").append(val);
return new StringBuilder().append(sqlColumn).append(" + ").append(val);
case MUL:
return new StringBuilder().append(col).append(" * ").append(val);
return new StringBuilder().append(sqlColumn).append(" * ").append(val);
case DIV:
return new StringBuilder().append(sqlColumn).append(" / ").append(val);
case MOD:
return new StringBuilder().append(sqlColumn).append(" % ").append(val);
case AND:
return new StringBuilder().append(col).append(" & ").append(val);
return new StringBuilder().append(sqlColumn).append(" & ").append(val);
case ORR:
return new StringBuilder().append(col).append(" | ").append(val);
return new StringBuilder().append(sqlColumn).append(" | ").append(val);
case MOV:
return formatToString(val);
CryptHandler handler = attr.attach();
if (handler != null) val = handler.encrypt(val);
return formatter == null ? formatToString(val) : formatter.apply(this, val);
}
return formatToString(val);
CryptHandler handler = attr.attach();
if (handler != null) val = handler.encrypt(val);
return formatter == null ? formatToString(val) : formatter.apply(this, val);
}
protected CharSequence formatSQLValue(Attribute<T, Serializable> attr, final ColumnNodeValue node, BiFunction<EntityInfo, Object, CharSequence> formatter) {
Serializable left = node.getLeft();
if (left instanceof CharSequence) {
left = this.getSQLColumn(null, left.toString());
} else if (left instanceof ColumnNodeValue) {
left = "(" + formatSQLValue(attr, (ColumnNodeValue) left, formatter) + ")";
}
Serializable right = node.getRight();
if (right instanceof CharSequence) {
right = this.getSQLColumn(null, right.toString());
} else if (left instanceof ColumnNodeValue) {
right = "(" + formatSQLValue(attr, (ColumnNodeValue) right, formatter) + ")";
}
switch (node.getExpress()) {
case INC:
return new StringBuilder().append(left).append(" + ").append(right);
case MUL:
return new StringBuilder().append(left).append(" * ").append(right);
case DIV:
return new StringBuilder().append(left).append(" / ").append(right);
case MOD:
return new StringBuilder().append(left).append(" % ").append(right);
case AND:
return new StringBuilder().append(left).append(" & ").append(right);
case ORR:
return new StringBuilder().append(left).append(" | ").append(right);
}
throw new IllegalArgumentException(node + " express cannot be null or MOV");
}
/**
@@ -1021,7 +1063,7 @@ public final class EntityInfo<T> {
*
* @return String
*/
protected String formatToString(Object value) {
private String formatToString(Object value) {
if (value == null) return null;
if (value instanceof CharSequence) {
return new StringBuilder().append('\'').append(value.toString().replace("'", "\\'")).append('\'').toString();

View File

@@ -1811,7 +1811,7 @@ public class FilterNode { //FilterNode 不能实现Serializable接口 否则
return sb;
}
protected static CharSequence formatToString(Object value) {
private static CharSequence formatToString(Object value) {
CharSequence sb = formatToString(null, value);
return sb == null ? null : sb.toString();
}

View File

@@ -86,7 +86,7 @@ public interface Range<E extends Comparable> extends java.io.Serializable, Predi
@Override
public boolean test(Byte t) {
if (max < min) return t >= min;
if (max < min && max <= 0) return t >= min;
return t >= min && t <= max;
}
@@ -131,7 +131,7 @@ public interface Range<E extends Comparable> extends java.io.Serializable, Predi
@Override
public boolean test(Short t) {
if (max < min) return t >= min;
if (max < min && max <= 0) return t >= min;
return t >= min && t <= max;
}
@@ -175,7 +175,7 @@ public interface Range<E extends Comparable> extends java.io.Serializable, Predi
@Override
public boolean test(Integer t) {
if (max < min) return t >= min;
if (max < min && max <= 0) return t >= min;
return t >= min && t <= max;
}
@@ -229,7 +229,7 @@ public interface Range<E extends Comparable> extends java.io.Serializable, Predi
@Override
public boolean test(Long t) {
if (max < min) return t >= min;
if (max < min && max <= 0) return t >= min;
return t >= min && t <= max;
}
@@ -273,7 +273,7 @@ public interface Range<E extends Comparable> extends java.io.Serializable, Predi
@Override
public boolean test(Float t) {
if (max < min) return t >= min;
if (max < min && max <= 0) return t >= min;
return t >= min && t <= max;
}
@@ -317,7 +317,7 @@ public interface Range<E extends Comparable> extends java.io.Serializable, Predi
@Override
public boolean test(Double t) {
if (max < min) return t >= min;
if (max < min && max <= 0) return t >= min;
return t >= min && t <= max;
}

View File

@@ -142,6 +142,21 @@ public final class ByteArray {
return Arrays.copyOf(content, count);
}
/**
* 获取byte[]
*
* @param offset 偏移位
* @param length 长度
*
* @return byte[]
*/
public byte[] getBytes(int offset, int length) {
if (length < 1) return new byte[0];
byte[] bs = new byte[length];
System.arraycopy(this.content, offset, bs, 0, length);
return bs;
}
/**
* 获取byte[]并清空
*

View File

@@ -17,14 +17,14 @@ public final class Redkale {
}
public static String getDotedVersion() {
return "2.0.0";
return "2.0.0-beta4";
}
public static int getMajorVersion() {
return 2;
}
public static int getMinorVersion() {
public static int getMinorVersion() {
return 0;
}
}

View File

@@ -0,0 +1,42 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.util;
import java.io.Serializable;
/**
* 主要供 JsonConvert.writeWrapper 使用
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*/
public class StringWrapper implements Serializable {
protected String value;
public StringWrapper() {
}
public StringWrapper(String value) {
this.value = value;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
@Override
public String toString() {
return value;
}
}

View File

@@ -647,6 +647,180 @@ public final class Utility {
return rs;
}
/**
* 将指定的long元素从数组中删除, 相同的元素会根据items里重复次数来执行删除 <br>
* 例如: <br>
* remove(new short[]{1, 1, 1, 2, 2, 3, 3, 3}, false, 1, 1, 2, 3, 3) = [1,2,3]<br>
*
* @param array 原数组
* @param items short[]
*
* @return 新数组
*/
public static short[] removeMatch(final short[] array, final short... items) {
return remove(array, false, items);
}
/**
* 将指定的int元素从数组中删除, repeat=true时相同的元素会根据items里重复次数来执行删除 <br>
* 例如: <br>
* remove(new short[]{1, 1, 1, 2, 2, 3, 3, 3}, true, 1, 1, 2, 3, 3) = [] <br>
* remove(new short[]{1, 1, 1, 2, 2, 3, 3, 3}, false, 1, 1, 2, 3, 3) = [1,2,3]
*
* @param array 原数组
* @param repeat 是否重复删除相同的元素
* @param items short[]
*
* @return 新数组
*/
public static short[] remove(final short[] array, boolean repeat, final short... items) {
if (array == null || array.length == 0 || items == null || items.length == 0) return array;
final short[] news = new short[array.length];
short[] subs = items;
int index = 0;
for (int i = 0; i < news.length; i++) {
if (subs.length > 0 && contains(subs, array[i])) {
if (!repeat) {
short[] newsubs = new short[subs.length - 1];
int k = 0;
boolean done = false;
for (short v : subs) {
if (done) {
newsubs[k++] = v;
} else if (v == array[i]) {
done = true;
} else {
newsubs[k++] = v;
}
}
subs = newsubs;
}
} else {
news[index++] = array[i];
}
}
if (index == array.length) return array;
final short[] rs = new short[index];
System.arraycopy(news, 0, rs, 0, index);
return rs;
}
/**
* 将指定的long元素从数组中删除, 相同的元素会根据items里重复次数来执行删除 <br>
* 例如: <br>
* remove(new int[]{1, 1, 1, 2, 2, 3, 3, 3}, false, 1, 1, 2, 3, 3) = [1,2,3]<br>
*
* @param array 原数组
* @param items int[]
*
* @return 新数组
*/
public static int[] removeMatch(final int[] array, final int... items) {
return remove(array, false, items);
}
/**
* 将指定的int元素从数组中删除, repeat=false时相同的元素会根据items里重复次数来执行删除 <br>
* 例如: <br>
* remove(new int[]{1, 1, 1, 2, 2, 3, 3, 3}, true, 1, 1, 2, 3, 3) = [] <br>
* remove(new int[]{1, 1, 1, 2, 2, 3, 3, 3}, false, 1, 1, 2, 3, 3) = [1,2,3]
*
* @param array 原数组
* @param repeat 是否重复删除相同的元素
* @param items int[]
*
* @return 新数组
*/
public static int[] remove(final int[] array, boolean repeat, final int... items) {
if (array == null || array.length == 0 || items == null || items.length == 0) return array;
final int[] news = new int[array.length];
int[] subs = items;
int index = 0;
for (int i = 0; i < news.length; i++) {
if (subs.length > 0 && contains(subs, array[i])) {
if (!repeat) {
int[] newsubs = new int[subs.length - 1];
int k = 0;
boolean done = false;
for (int v : subs) {
if (done) {
newsubs[k++] = v;
} else if (v == array[i]) {
done = true;
} else {
newsubs[k++] = v;
}
}
subs = newsubs;
}
} else {
news[index++] = array[i];
}
}
if (index == array.length) return array;
final int[] rs = new int[index];
System.arraycopy(news, 0, rs, 0, index);
return rs;
}
/**
* 将指定的long元素从数组中删除, 相同的元素会根据items里重复次数来执行删除 <br>
* 例如: <br>
* remove(new long[]{1, 1, 1, 2, 2, 3, 3, 3}, false, 1, 1, 2, 3, 3) = [1,2,3]<br>
*
* @param array 原数组
* @param items long[]
*
* @return 新数组
*/
public static long[] removeMatch(final long[] array, final long... items) {
return remove(array, false, items);
}
/**
* 将指定的long元素从数组中删除, repeat=false时相同的元素会根据items里重复次数来执行删除 <br>
* 例如: <br>
* remove(new long[]{1, 1, 1, 2, 2, 3, 3, 3}, true, 1, 1, 2, 3, 3) = [] <br>
* remove(new long[]{1, 1, 1, 2, 2, 3, 3, 3}, false, 1, 1, 2, 3, 3) = [1,2,3]<br>
*
* @param array 原数组
* @param repeat 是否重复删除相同的元素
* @param items long[]
*
* @return 新数组
*/
public static long[] remove(final long[] array, boolean repeat, final long... items) {
if (array == null || array.length == 0 || items == null || items.length == 0) return array;
final long[] news = new long[array.length];
long[] subs = items;
int index = 0;
for (int i = 0; i < news.length; i++) {
if (subs.length > 0 && contains(subs, array[i])) {
if (!repeat) {
long[] newsubs = new long[subs.length - 1];
int k = 0;
boolean done = false;
for (long v : subs) {
if (done) {
newsubs[k++] = v;
} else if (v == array[i]) {
done = true;
} else {
newsubs[k++] = v;
}
}
subs = newsubs;
}
} else {
news[index++] = array[i];
}
}
if (index == array.length) return array;
final long[] rs = new long[index];
System.arraycopy(news, 0, rs, 0, index);
return rs;
}
/**
* 判断字符串是否包含指定的字符包含返回true
*
@@ -697,6 +871,22 @@ public final class Utility {
return false;
}
/**
* 判断指定值(不要包含相同的元素)是否包含指定的数组中包含返回true
*
* @param values 集合
* @param items 多值
*
* @return boolean
*/
public static boolean contains(short[] values, short... items) {
if (values == null) return false;
for (short item : items) {
if (!contains(values, item)) return false;
}
return true;
}
/**
* 判断指定值是否包含指定的数组中包含返回true
*
@@ -713,6 +903,22 @@ public final class Utility {
return false;
}
/**
* 判断指定值(不要包含相同的元素)是否包含指定的数组中包含返回true
*
* @param values 集合
* @param items 多值
*
* @return boolean
*/
public static boolean contains(int[] values, int... items) {
if (values == null) return false;
for (int item : items) {
if (!contains(values, item)) return false;
}
return true;
}
/**
* 判断指定值是否包含指定的数组中包含返回true
*
@@ -729,6 +935,22 @@ public final class Utility {
return false;
}
/**
* 判断指定值(不要包含相同的元素)是否包含指定的数组中包含返回true
*
* @param values 集合
* @param items 多值
*
* @return boolean
*/
public static boolean contains(long[] values, long... items) {
if (values == null) return false;
for (long item : items) {
if (!contains(values, item)) return false;
}
return true;
}
/**
* 判断指定值是否包含指定的数组中包含返回true
*
@@ -746,6 +968,117 @@ public final class Utility {
return false;
}
/**
* 将指定的short元素是否数组中完全包含重复元素的次数也要相同 <br>
* 例如: <br>
* containsMatch(new short[]{1, 2, 2, 3, 3, 3}, 1, 2, 3, 3) = true <br>
* containsMatch(new short[]{1, 2, 2, 3, 3, 3}, 1, 1, 2, 3, 3) = false <br>
*
* @param array 原数组
* @param items short[]
*
* @return 是否完全包含
*/
public static boolean containsMatch(final short[] array, final short... items) {
if (array == null) return false;
if (items == null || items.length == 0) return true;
if (array.length == 0 && items.length == 0) return true;
if (array.length < items.length) return false;
short[] subs = array;
for (short item : items) {
if (!contains(subs, item)) return false;
short[] newsubs = new short[subs.length - 1];
int k = 0;
boolean done = false;
for (short v : subs) {
if (done) {
newsubs[k++] = v;
} else if (v == item) {
done = true;
} else {
newsubs[k++] = v;
}
}
subs = newsubs;
}
return true;
}
/**
* 将指定的int元素是否数组中完全包含重复元素的次数也要相同 <br>
* 例如: <br>
* containsMatch(new int[]{1, 2, 2, 3, 3, 3}, 1, 2, 3, 3) = true <br>
* containsMatch(new int[]{1, 2, 2, 3, 3, 3}, 1, 1, 2, 3, 3) = false <br>
*
* @param array 原数组
* @param items int[]
*
* @return 是否完全包含
*/
public static boolean containsMatch(final int[] array, final int... items) {
if (array == null) return false;
if (items == null || items.length == 0) return true;
if (array.length == 0 && items.length == 0) return true;
if (array.length < items.length) return false;
int[] subs = array;
for (int item : items) {
if (!contains(subs, item)) return false;
int[] newsubs = new int[subs.length - 1];
int k = 0;
boolean done = false;
for (int v : subs) {
if (done) {
newsubs[k++] = v;
} else if (v == item) {
done = true;
} else {
newsubs[k++] = v;
}
}
subs = newsubs;
}
return true;
}
/**
* 将指定的long元素是否数组中完全包含重复元素的次数也要相同 <br>
* 例如: <br>
* containsMatch(new long[]{1, 2, 2, 3, 3, 3}, 1, 2, 3, 3) = true <br>
* containsMatch(new long[]{1, 2, 2, 3, 3, 3}, 1, 1, 2, 3, 3) = false <br>
*
* @param array 原数组
* @param items long[]
*
* @return 是否完全包含
*/
public static boolean containsMatch(final long[] array, final long... items) {
if (array == null) return false;
if (items == null || items.length == 0) return true;
if (array.length == 0 && items.length == 0) return true;
if (array.length < items.length) return false;
long[] subs = array;
for (long item : items) {
if (!contains(subs, item)) return false;
long[] newsubs = new long[subs.length - 1];
int k = 0;
boolean done = false;
for (long v : subs) {
if (done) {
newsubs[k++] = v;
} else if (v == item) {
done = true;
} else {
newsubs[k++] = v;
}
}
subs = newsubs;
}
return true;
}
/**
* 删除掉字符串数组中包含指定的字符串
*
@@ -1591,7 +1924,15 @@ public final class Utility {
final int limit = start + len;
for (int i = start; i < limit; i++) {
c = chars[i];
size += (c < 0x80 ? 1 : (c < 0x800 ? 2 : 3));
if (c < 0x80) {
size++;
} else if (c < 0x800) {
size += 2;
} else if (Character.isSurrogate(c)) {
size += 2;
} else {
size += 3;
}
}
return size;
}
@@ -1612,12 +1953,13 @@ public final class Utility {
return encodeUTF8(buffer, encodeUTF8Length(text, start, len), text, start, len);
}
//返回的ByteBuffer为扩展buffer为null表示参数中的buffer足够存储数据
public static ByteBuffer encodeUTF8(final ByteBuffer buffer, int bytesLength, final char[] text, final int start, final int len) {
char c;
char[] chars = text;
final int limit = start + len;
int remain = buffer.remaining();
final ByteBuffer buffer2 = remain >= bytesLength ? null : ByteBuffer.allocate(bytesLength - remain + 3); //最差情况buffer最后两byte没有填充
final ByteBuffer buffer2 = remain >= bytesLength ? null : ByteBuffer.allocate(bytesLength - remain + 4); //最差情况buffer最后两byte没有填充
ByteBuffer buf = buffer;
for (int i = start; i < limit; i++) {
c = chars[i];
@@ -1628,6 +1970,14 @@ public final class Utility {
if (buf.remaining() < 2) buf = buffer2;
buf.put((byte) (0xc0 | (c >> 6)));
buf.put((byte) (0x80 | (c & 0x3f)));
} else if (Character.isSurrogate(c)) { //连取两个
if (buf.remaining() < 4) buf = buffer2;
int uc = Character.toCodePoint(c, chars[i + 1]);
buf.put((byte) (0xf0 | ((uc >> 18))));
buf.put((byte) (0x80 | ((uc >> 12) & 0x3f)));
buf.put((byte) (0x80 | ((uc >> 6) & 0x3f)));
buf.put((byte) (0x80 | (uc & 0x3f)));
i++;
} else {
if (buf.remaining() < 3) buf = buffer2;
buf.put((byte) (0xe0 | ((c >> 12))));
@@ -1636,7 +1986,7 @@ public final class Utility {
}
}
if (buffer2 != null) buffer2.flip();
return buffer2;
return buffer2; //返回扩展buffer
}
public static String getTypeDescriptor(java.lang.reflect.Type type) {
@@ -1980,14 +2330,14 @@ public final class Utility {
}
conn.setRequestMethod(method);
if (headers != null) {
for (Map.Entry<String, String> en : headers.entrySet()) { //不用forEach是为了兼容JDK 6
for (Map.Entry<String, String> en : headers.entrySet()) {
conn.setRequestProperty(en.getKey(), en.getValue());
}
}
if (body != null && !body.isEmpty()) { //conn.getOutputStream()会将GET强制变成POST
conn.setDoInput(true);
conn.setDoOutput(true);
conn.getOutputStream().write(body == null ? new byte[0] : body.getBytes(UTF_8));
conn.getOutputStream().write(body.getBytes(UTF_8));
}
conn.connect();
int rs = conn.getResponseCode();

View File

@@ -0,0 +1,58 @@
/*
* 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.test.convert;
import org.redkale.convert.json.JsonConvert;
import org.redkale.util.Attribute;
/**
*
* @author zhangjx
*/
public class BiFunctionConvertMain {
public static class GamePlayer {
public int userid;
public String username;
public int[] cards;
}
public static class GameTable {
public int tableid;
public GamePlayer[] players;
}
public static void main(String[] args) throws Throwable {
GamePlayer player1 = new GamePlayer();
player1.userid= 1;
player1.username="玩家1";
player1.cards = new int[]{11,12,13,14,15};
GamePlayer player2 = new GamePlayer();
player2.userid= 2;
player2.username="玩家2";
player2.cards = new int[]{21,22,23,24,25};
GamePlayer player3 = new GamePlayer();
player3.userid= 3;
player3.username="玩家3";
player3.cards = new int[]{31,32,33,34,35};
GameTable table = new GameTable();
table.tableid=100;
table.players = new GamePlayer[]{player1,player2,player3};
JsonConvert convert1 = JsonConvert.root();
System.out.println(convert1.convertTo(table));
JsonConvert convert2 = convert1.newConvert((Attribute t, Object u) -> {
if(t.field().equals("cards") && u instanceof GamePlayer){
int userid = ((GamePlayer)u).userid;
if(userid == 3) return null; //玩家3的cards不输出
return t.get(u);
}
return t.get(u);
});
System.out.println(convert2.convertTo(table));
//{"players":[{"cards":[11,12,13,14,15],"userid":1,"username":"玩家1"},{"cards":[21,22,23,24,25],"userid":2,"username":"玩家2"},{"cards":[31,32,33,34,35],"userid":3,"username":"玩家3"}],"tableid":100}
//{"players":[{"cards":[11,12,13,14,15],"userid":1,"username":"玩家1"},{"cards":[21,22,23,24,25],"userid":2,"username":"玩家2"},{"userid":3,"username":"玩家3"}],"tableid":100}
}
}

View File

@@ -18,7 +18,6 @@ import org.redkale.convert.json.*;
public class SncpTestBean implements FilterBean {
@Id
@GeneratedValue
private long id;
private String content;

View File

@@ -21,12 +21,9 @@ import org.redkale.convert.json.*;
*
* @author zhangjx
*/
@Entity
public class LoginTestRecord {
@Id
@GeneratedValue
//@SequenceGenerator(name = "SEQ", initialValue = 100001, allocationSize = 1000)
private String sessionid;
private int userid;

View File

@@ -6,7 +6,8 @@
package org.redkale.test.wsdync;
import java.io.Serializable;
import java.util.Map;
import java.lang.annotation.Annotation;
import java.util.*;
import java.util.function.BiConsumer;
import javax.annotation.Resource;
import org.redkale.convert.ConvertDisabled;
@@ -26,6 +27,8 @@ public final class _DyncChatWebSocketServlet extends WebSocketServlet {
@Resource
private ChatService _redkale_resource_0;
public static Map<String, Annotation[]> _redkale_annotations;
public _DyncChatWebSocketServlet() {
super();
this.messageTextType = _DyncChatWebSocketMessage.class;
@@ -82,6 +85,13 @@ public final class _DyncChatWebSocketServlet extends WebSocketServlet {
return null;
}
@Override
public Annotation[] getAnnotations() {
Annotation[] annotations = _redkale_annotations.get("org/redkale/test/wsdync/_DyncChatWebSocketServlet$_DyncChatWebSocketMessage_sendmessagee_00");
if (annotations == null) return new Annotation[0];
return Arrays.copyOf(annotations, annotations.length);
}
public void execute(_DyncChatWebSocket websocket) {
this._redkale_websocket = websocket;
websocket.preOnMessage("sendmessage", this, this);
@@ -116,6 +126,13 @@ public final class _DyncChatWebSocketServlet extends WebSocketServlet {
return null;
}
@Override
public Annotation[] getAnnotations() {
Annotation[] annotations = _redkale_annotations.get("org/redkale/test/wsdync/_DyncChatWebSocketServlet$_DyncChatWebSocketMessage_joinroom_01");
if (annotations == null) return new Annotation[0];
return Arrays.copyOf(annotations, annotations.length);
}
public void execute(_DyncChatWebSocket websocket) {
this._redkale_websocket = websocket;
websocket.preOnMessage("joinroom", this, this);