Redkale 2.3.0 结束

This commit is contained in:
Redkale
2021-04-09 00:28:57 +08:00
parent 4f9c14da97
commit 1917ccf35c
167 changed files with 9725 additions and 5413 deletions

View File

@@ -51,4 +51,12 @@ public @interface Cacheable {
* @return int
*/
int interval() default 0;
/**
* DataSource是否直接返回对象的真实引用 而不是copy一份
*
* @return boolean
*/
boolean direct() default false;
}

View File

@@ -60,8 +60,8 @@ package org.redkale.asm;
/**
* A visitor to visit a Java annotation. The methods of this class must be
* called in the following order: ( <code>visit</code> | <code>visitEnum</code> |
* <code>visitAnnotation</code> | <code>visitArray</code> )* <code>visitEnd</code>.
* called in the following order: ( &#60;tt&#62;visit&#60;/tt&#62; | &#60;tt&#62;visitEnum&#60;/tt&#62; |
* &#60;tt&#62;visitAnnotation&#60;/tt&#62; | &#60;tt&#62;visitArray&#60;/tt&#62; )* &#60;tt&#62;visitEnd&#60;/tt&#62;.
*
* @author Eric Bruneton
* @author Eugene Kuleshov
@@ -102,6 +102,9 @@ public abstract class AnnotationVisitor {
* method calls. May be null.
*/
public AnnotationVisitor(final int api, final AnnotationVisitor av) {
if (api < Opcodes.ASM4 || api > Opcodes.ASM6) {
throw new IllegalArgumentException();
}
this.api = api;
this.av = av;
}
@@ -151,7 +154,7 @@ public abstract class AnnotationVisitor {
* @param desc
* the class descriptor of the nested annotation class.
* @return a visitor to visit the actual nested annotation value, or
* <code>null</code> if this visitor is not interested in visiting this
* &#60;tt&#62;null&#60;/tt&#62; if this visitor is not interested in visiting this
* nested annotation. <i>The nested annotation value must be fully
* visited before calling other methods on this annotation
* visitor</i>.
@@ -172,7 +175,7 @@ public abstract class AnnotationVisitor {
* @param name
* the value name.
* @return a visitor to visit the actual array value elements, or
* <code>null</code> if this visitor is not interested in visiting these
* &#60;tt&#62;null&#60;/tt&#62; if this visitor is not interested in visiting these
* values. The 'name' parameters passed to the methods of this
* visitor are ignored. <i>All the array values must be visited
* before calling other methods on this annotation visitor</i>.

View File

@@ -77,7 +77,7 @@ final class AnnotationWriter extends AnnotationVisitor {
private int size;
/**
* <code>true<code> if values are named, <code>false</code> otherwise. Annotation
* &#60;tt&#62;true&#60;tt&#62; if values are named, &#60;tt&#62;false&#60;/tt&#62; otherwise. Annotation
* writers used for annotation default and annotation arrays use unnamed
* values.
*/
@@ -122,13 +122,13 @@ final class AnnotationWriter extends AnnotationVisitor {
* @param cw
* the class writer to which this annotation must be added.
* @param named
* <code>true<code> if values are named, <code>false</code> otherwise.
* &#60;tt&#62;true&#60;tt&#62; if values are named, &#60;tt&#62;false&#60;/tt&#62; otherwise.
* @param bv
* where the annotation values must be stored.
* @param parent
* where the number of annotation values must be stored.
* @param offset
* where in <code>parent</code> the number of annotation values must
* where in &#60;tt&#62;parent&#60;/tt&#62; the number of annotation values must
* be stored.
*/
AnnotationWriter(final ClassWriter cw, final boolean named,
@@ -354,7 +354,7 @@ final class AnnotationWriter extends AnnotationVisitor {
* @param typePath
* the path to the annotated type argument, wildcard bound, array
* element type, or static inner type within 'typeRef'. May be
* <code>null</code> if the annotation targets 'typeRef' as a whole.
* &#60;tt&#62;null&#60;/tt&#62; if the annotation targets 'typeRef' as a whole.
* @param out
* where the type reference and type path must be put.
*/

View File

@@ -58,13 +58,15 @@
*/
package org.redkale.asm;
import java.util.Arrays;
/**
* A non standard class, field, method or code attribute.
*
* @author Eric Bruneton
* @author Eugene Kuleshov
*/
class Attribute {
public class Attribute {
/**
* The type of this attribute.
@@ -77,7 +79,7 @@ class Attribute {
byte[] value;
/**
* The next attribute in this attribute list. May be <code>null</code>.
* The next attribute in this attribute list. May be &#60;tt&#62;null&#60;/tt&#62;.
*/
Attribute next;
@@ -92,19 +94,19 @@ class Attribute {
}
/**
* Returns <code>true</code> if this type of attribute is unknown. The default
* implementation of this method always returns <code>true</code>.
* Returns &#60;tt&#62;true&#60;/tt&#62; if this type of attribute is unknown. The default
* implementation of this method always returns &#60;tt&#62;true&#60;/tt&#62;.
*
* @return <code>true</code> if this type of attribute is unknown.
* @return &#60;tt&#62;true&#60;/tt&#62; if this type of attribute is unknown.
*/
public boolean isUnknown() {
return true;
}
/**
* Returns <code>true</code> if this type of attribute is a code attribute.
* Returns &#60;tt&#62;true&#60;/tt&#62; if this type of attribute is a code attribute.
*
* @return <code>true</code> if this type of attribute is a code attribute.
* @return &#60;tt&#62;true&#60;/tt&#62; if this type of attribute is a code attribute.
*/
public boolean isCodeAttribute() {
return false;
@@ -113,7 +115,7 @@ class Attribute {
/**
* Returns the labels corresponding to this attribute.
*
* @return the labels corresponding to this attribute, or <code>null</code> if
* @return the labels corresponding to this attribute, or &#60;tt&#62;null&#60;/tt&#62; if
* this attribute is not a code attribute that contains labels.
*/
protected Label[] getLabels() {
@@ -123,7 +125,7 @@ class Attribute {
/**
* Reads a {@link #type type} attribute. This method must return a
* <i>new</i> {@link Attribute} object, of type {@link #type type},
* corresponding to the <code>len</code> bytes starting at the given offset, in
* corresponding to the &#60;tt&#62;len&#60;/tt&#62; bytes starting at the given offset, in
* the given class reader.
*
* @param cr
@@ -146,7 +148,7 @@ class Attribute {
* containing the type and the length of the attribute, are not
* taken into account here.
* @param labels
* the labels of the method's code, or <code>null</code> if the
* the labels of the method's code, or &#60;tt&#62;null&#60;/tt&#62; if the
* attribute to be read is not a code attribute.
* @return a <i>new</i> {@link Attribute} object corresponding to the given
* bytes.
@@ -169,11 +171,11 @@ class Attribute {
* class the items that corresponds to this attribute.
* @param code
* the bytecode of the method corresponding to this code
* attribute, or <code>null</code> if this attribute is not a code
* attribute, or &#60;tt&#62;null&#60;/tt&#62; if this attribute is not a code
* attributes.
* @param len
* the length of the bytecode of the method corresponding to this
* code attribute, or <code>null</code> if this attribute is not a
* code attribute, or &#60;tt&#62;null&#60;/tt&#62; if this attribute is not a
* code attribute.
* @param maxStack
* the maximum stack size of the method corresponding to this
@@ -216,11 +218,11 @@ class Attribute {
* byte arrays, with the {@link #write write} method.
* @param code
* the bytecode of the method corresponding to these code
* attributes, or <code>null</code> if these attributes are not code
* attributes, or &#60;tt&#62;null&#60;/tt&#62; if these attributes are not code
* attributes.
* @param len
* the length of the bytecode of the method corresponding to
* these code attributes, or <code>null</code> if these attributes
* these code attributes, or &#60;tt&#62;null&#60;/tt&#62; if these attributes
* are not code attributes.
* @param maxStack
* the maximum stack size of the method corresponding to these
@@ -254,11 +256,11 @@ class Attribute {
* byte arrays, with the {@link #write write} method.
* @param code
* the bytecode of the method corresponding to these code
* attributes, or <code>null</code> if these attributes are not code
* attributes, or &#60;tt&#62;null&#60;/tt&#62; if these attributes are not code
* attributes.
* @param len
* the length of the bytecode of the method corresponding to
* these code attributes, or <code>null</code> if these attributes
* these code attributes, or &#60;tt&#62;null&#60;/tt&#62; if these attributes
* are not code attributes.
* @param maxStack
* the maximum stack size of the method corresponding to these
@@ -281,4 +283,72 @@ class Attribute {
attr = attr.next;
}
}
//The stuff below is temporary - once proper support for nestmate attribute has been added, it can be safely removed.
//see also changes in ClassReader.accept.
public static class NestMembers extends Attribute {
public NestMembers() {
super("NestMembers");
}
byte[] bytes;
String[] classes;
@Override
protected Attribute read(ClassReader cr, int off, int len, char[] buf, int codeOff, Label[] labels) {
int offset = off;
NestMembers a = new NestMembers();
int size = cr.readShort(off);
a.classes = new String[size];
off += 2;
for (int i = 0; i < size ; i++) {
a.classes[i] = cr.readClass(off, buf);
off += 2;
}
a.bytes = Arrays.copyOfRange(cr.b, offset, offset + len);
return a;
}
@Override
protected ByteVector write(ClassWriter cw, byte[] code, int len, int maxStack, int maxLocals) {
ByteVector v = new ByteVector(bytes.length);
v.putShort(classes.length);
for (String s : classes) {
v.putShort(cw.newClass(s));
}
return v;
}
}
public static class NestHost extends Attribute {
byte[] bytes;
String clazz;
public NestHost() {
super("NestHost");
}
@Override
protected Attribute read(ClassReader cr, int off, int len, char[] buf, int codeOff, Label[] labels) {
int offset = off;
NestHost a = new NestHost();
a.clazz = cr.readClass(off, buf);
a.bytes = Arrays.copyOfRange(cr.b, offset, offset + len);
return a;
}
@Override
protected ByteVector write(ClassWriter cw, byte[] code, int len, int maxStack, int maxLocals) {
ByteVector v = new ByteVector(bytes.length);
v.putShort(cw.newClass(clazz));
return v;
}
}
static final Attribute[] DEFAULT_ATTRIBUTE_PROTOS = new Attribute[] {
new NestMembers(),
new NestHost()
};
}

View File

@@ -332,7 +332,7 @@ public class ByteVector {
* automatically enlarged if necessary.
*
* @param b
* an array of bytes. May be <code>null</code> to put <code>len</code>
* an array of bytes. May be &#60;tt&#62;null&#60;/tt&#62; to put &#60;tt&#62;len&#60;/tt&#62;
* null bytes into this byte vector.
* @param off
* index of the fist byte of b that must be copied.

View File

@@ -185,9 +185,9 @@ public class ClassReader {
public ClassReader(final byte[] b, final int off, final int len) {
this.b = b;
// checks the class version
if (readShort(off + 6) > Opcodes.V10) {
//throw new IllegalArgumentException();
}
//if (readShort(off + 6) > Opcodes.V11) {
// throw new IllegalArgumentException();
//}
// parses the constant pool
items = new int[readUnsignedShort(off + 8)];
int n = items.length;
@@ -205,6 +205,10 @@ public class ClassReader {
case ClassWriter.FLOAT:
case ClassWriter.NAME_TYPE:
case ClassWriter.INDY:
// @@@ ClassWriter.CONDY
// Enables MethodHandles.lookup().defineClass to function correctly
// when it reads the class name
case 17:
size = 5;
break;
case ClassWriter.LONG:
@@ -267,7 +271,7 @@ public class ClassReader {
* {@link Type#getInternalName() getInternalName}). For interfaces, the
* super class is {@link Object}.
*
* @return the internal name of super class, or <code>null</code> for
* @return the internal name of super class, or &#60;tt&#62;null&#60;/tt&#62; for
* {@link Object} class.
*
* @see ClassVisitor#visit(int, int, String, String, String, String[])
@@ -281,7 +285,7 @@ public class ClassReader {
* {@link Type#getInternalName() getInternalName}).
*
* @return the array of internal names for all implemented interfaces or
* <code>null</code>.
* &#60;tt&#62;null&#60;/tt&#62;.
*
* @see ClassVisitor#visit(int, int, String, String, String, String[])
*/
@@ -526,7 +530,7 @@ public class ClassReader {
* , {@link #SKIP_FRAMES}, {@link #SKIP_CODE}.
*/
public void accept(final ClassVisitor classVisitor, final int flags) {
accept(classVisitor, new Attribute[0], flags);
accept(classVisitor, Attribute.DEFAULT_ATTRIBUTE_PROTOS, flags);
}
/**
@@ -1932,7 +1936,7 @@ public class ClassReader {
* @param v
* start offset in {@link #b b} of the annotations to be read.
* @param visible
* <code>true</code> if the annotations to be read are visible at
* &#60;tt&#62;true&#60;/tt&#62; if the annotations to be read are visible at
* runtime.
*/
private void readParameterAnnotations(final MethodVisitor mv,
@@ -2474,9 +2478,9 @@ public class ClassReader {
* and the length of the attribute, are not taken into account
* here.
* @param labels
* the labels of the method's code, or <code>null</code> if the
* the labels of the method's code, or &#60;tt&#62;null&#60;/tt&#62; if the
* attribute to be read is not a code attribute.
* @return the attribute that has been read, or <code>null</code> to skip this
* @return the attribute that has been read, or &#60;tt&#62;null&#60;/tt&#62; to skip this
* attribute.
*/
private Attribute readAttribute(final Attribute[] attrs, final String type,

View File

@@ -60,11 +60,11 @@ package org.redkale.asm;
/**
* A visitor to visit a Java class. The methods of this class must be called in
* the following order: <code>visit</code> [ <code>visitSource</code> ] [
* <code>visitModule</code> ][ <code>visitOuterClass</code> ] ( <code>visitAnnotation</code> |
* <code>visitTypeAnnotation</code> | <code>visitAttribute</code> )* (
* <code>visitInnerClass</code> | <code>visitField</code> | <code>visitMethod</code> )*
* <code>visitEnd</code>.
* the following order: &#60;tt&#62;visit&#60;/tt&#62; [ &#60;tt&#62;visitSource&#60;/tt&#62; ] [
* &#60;tt&#62;visitModule&#60;/tt&#62; ][ &#60;tt&#62;visitOuterClass&#60;/tt&#62; ] ( &#60;tt&#62;visitAnnotation&#60;/tt&#62; |
* &#60;tt&#62;visitTypeAnnotation&#60;/tt&#62; | &#60;tt&#62;visitAttribute&#60;/tt&#62; )* (
* &#60;tt&#62;visitInnerClass&#60;/tt&#62; | &#60;tt&#62;visitField&#60;/tt&#62; | &#60;tt&#62;visitMethod&#60;/tt&#62; )*
* &#60;tt&#62;visitEnd&#60;/tt&#62;.
*
* @author Eric Bruneton
*/
@@ -104,6 +104,9 @@ public abstract class ClassVisitor {
* calls. May be null.
*/
public ClassVisitor(final int api, final ClassVisitor cv) {
if (api < Opcodes.ASM4 || api > Opcodes.ASM6) {
throw new IllegalArgumentException();
}
this.api = api;
this.cv = cv;
}
@@ -120,18 +123,18 @@ public abstract class ClassVisitor {
* the internal name of the class (see
* {@link Type#getInternalName() getInternalName}).
* @param signature
* the signature of this class. May be <code>null</code> if the class
* the signature of this class. May be &#60;tt&#62;null&#60;/tt&#62; if the class
* is not a generic one, and does not extend or implement generic
* classes or interfaces.
* @param superName
* the internal of name of the super class (see
* {@link Type#getInternalName() getInternalName}). For
* interfaces, the super class is {@link Object}. May be
* <code>null</code>, but only for the {@link Object} class.
* &#60;tt&#62;null&#60;/tt&#62;, but only for the {@link Object} class.
* @param interfaces
* the internal names of the class's interfaces (see
* {@link Type#getInternalName() getInternalName}). May be
* <code>null</code>.
* &#60;tt&#62;null&#60;/tt&#62;.
*/
public void visit(int version, int access, String name, String signature,
String superName, String[] interfaces) {
@@ -145,11 +148,11 @@ public abstract class ClassVisitor {
*
* @param source
* the name of the source file from which the class was compiled.
* May be <code>null</code>.
* May be &#60;tt&#62;null&#60;/tt&#62;.
* @param debug
* additional debug information to compute the correspondance
* between source and compiled elements of the class. May be
* <code>null</code>.
* &#60;tt&#62;null&#60;/tt&#62;.
*/
public void visitSource(String source, String debug) {
if (cv != null) {
@@ -166,7 +169,7 @@ public abstract class ClassVisitor {
* and {@code ACC_MANDATED}.
* @param version
* module version or null.
* @return a visitor to visit the module values, or <code>null</code> if
* @return a visitor to visit the module values, or &#60;tt&#62;null&#60;/tt&#62; if
* this visitor is not interested in visiting this module.
*/
public ModuleVisitor visitModule(String name, int access, String version) {
@@ -187,11 +190,11 @@ public abstract class ClassVisitor {
* internal name of the enclosing class of the class.
* @param name
* the name of the method that contains the class, or
* <code>null</code> if the class is not enclosed in a method of its
* &#60;tt&#62;null&#60;/tt&#62; if the class is not enclosed in a method of its
* enclosing class.
* @param desc
* the descriptor of the method that contains the class, or
* <code>null</code> if the class is not enclosed in a method of its
* &#60;tt&#62;null&#60;/tt&#62; if the class is not enclosed in a method of its
* enclosing class.
*/
public void visitOuterClass(String owner, String name, String desc) {
@@ -206,8 +209,8 @@ public abstract class ClassVisitor {
* @param desc
* the class descriptor of the annotation class.
* @param visible
* <code>true</code> if the annotation is visible at runtime.
* @return a visitor to visit the annotation values, or <code>null</code> if
* &#60;tt&#62;true&#60;/tt&#62; if the annotation is visible at runtime.
* @return a visitor to visit the annotation values, or &#60;tt&#62;null&#60;/tt&#62; if
* this visitor is not interested in visiting this annotation.
*/
public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
@@ -231,16 +234,19 @@ public abstract class ClassVisitor {
* @param typePath
* the path to the annotated type argument, wildcard bound, array
* element type, or static inner type within 'typeRef'. May be
* <code>null</code> if the annotation targets 'typeRef' as a whole.
* &#60;tt&#62;null&#60;/tt&#62; if the annotation targets 'typeRef' as a whole.
* @param desc
* the class descriptor of the annotation class.
* @param visible
* <code>true</code> if the annotation is visible at runtime.
* @return a visitor to visit the annotation values, or <code>null</code> if
* &#60;tt&#62;true&#60;/tt&#62; if the annotation is visible at runtime.
* @return a visitor to visit the annotation values, or &#60;tt&#62;null&#60;/tt&#62; if
* this visitor is not interested in visiting this annotation.
*/
public AnnotationVisitor visitTypeAnnotation(int typeRef,
TypePath typePath, String desc, boolean visible) {
if (api < Opcodes.ASM5) {
throw new RuntimeException();
}
if (cv != null) {
return cv.visitTypeAnnotation(typeRef, typePath, desc, visible);
}
@@ -269,10 +275,10 @@ public abstract class ClassVisitor {
* @param outerName
* the internal name of the class to which the inner class
* belongs (see {@link Type#getInternalName() getInternalName}).
* May be <code>null</code> for not member classes.
* May be &#60;tt&#62;null&#60;/tt&#62; for not member classes.
* @param innerName
* the (simple) name of the inner class inside its enclosing
* class. May be <code>null</code> for anonymous inner classes.
* class. May be &#60;tt&#62;null&#60;/tt&#62; for anonymous inner classes.
* @param access
* the access flags of the inner class as originally declared in
* the enclosing class.
@@ -295,20 +301,20 @@ public abstract class ClassVisitor {
* @param desc
* the field's descriptor (see {@link Type Type}).
* @param signature
* the field's signature. May be <code>null</code> if the field's
* the field's signature. May be &#60;tt&#62;null&#60;/tt&#62; if the field's
* type does not use generic types.
* @param value
* the field's initial value. This parameter, which may be
* <code>null</code> if the field does not have an initial value,
* &#60;tt&#62;null&#60;/tt&#62; if the field does not have an initial value,
* must be an {@link Integer}, a {@link Float}, a {@link Long}, a
* {@link Double} or a {@link String} (for <code>int</code>,
* <code>float</code>, <code>long</code> or <code>String</code> fields
* {@link Double} or a {@link String} (for &#60;tt&#62;int&#60;/tt&#62;,
* &#60;tt&#62;float&#60;/tt&#62;, &#60;tt&#62;long&#60;/tt&#62; or &#60;tt&#62;String&#60;/tt&#62; fields
* respectively). <i>This parameter is only used for static
* fields</i>. Its value is ignored for non static fields, which
* must be initialized through bytecode instructions in
* constructors or methods.
* @return a visitor to visit field annotations and attributes, or
* <code>null</code> if this class visitor is not interested in visiting
* &#60;tt&#62;null&#60;/tt&#62; if this class visitor is not interested in visiting
* these annotations and attributes.
*/
public FieldVisitor visitField(int access, String name, String desc,
@@ -321,7 +327,7 @@ public abstract class ClassVisitor {
/**
* Visits a method of the class. This method <i>must</i> return a new
* {@link MethodVisitor} instance (or <code>null</code>) each time it is called,
* {@link MethodVisitor} instance (or &#60;tt&#62;null&#60;/tt&#62;) each time it is called,
* i.e., it should not return a previously returned visitor.
*
* @param access
@@ -333,14 +339,14 @@ public abstract class ClassVisitor {
* @param desc
* the method's descriptor (see {@link Type Type}).
* @param signature
* the method's signature. May be <code>null</code> if the method
* the method's signature. May be &#60;tt&#62;null&#60;/tt&#62; if the method
* parameters, return type and exceptions do not use generic
* types.
* @param exceptions
* the internal names of the method's exception classes (see
* {@link Type#getInternalName() getInternalName}). May be
* <code>null</code>.
* @return an object to visit the byte code of the method, or <code>null</code>
* &#60;tt&#62;null&#60;/tt&#62;.
* @return an object to visit the byte code of the method, or &#60;tt&#62;null&#60;/tt&#62;
* if this class visitor is not interested in visiting the code of
* this method.
*/

View File

@@ -391,8 +391,8 @@ public class ClassWriter extends ClassVisitor {
* A type table used to temporarily store internal names that will not
* necessarily be stored in the constant pool. This type table is used by
* the control flow and data flow analysis algorithm used to compute stack
* map frames from scratch. This array associates to each index <code>i</code>
* the Item whose index is <code>i</code>. All Item objects stored in this array
* map frames from scratch. This array associates to each index &#60;tt&#62;i&#60;/tt&#62;
* the Item whose index is &#60;tt&#62;i&#60;/tt&#62;. All Item objects stored in this array
* are also stored in the {@link #items} hash table. These two arrays allow
* to retrieve an Item from its index or, conversely, to get the index of an
* Item from its value. Each Item stores an internal name in its
@@ -556,7 +556,7 @@ public class ClassWriter extends ClassVisitor {
private int compute;
/**
* <code>true</code> if some methods have wide forward jumps using ASM pseudo
* &#60;tt&#62;true&#60;/tt&#62; if some methods have wide forward jumps using ASM pseudo
* instructions, which need to be expanded into sequences of standard
* bytecode instructions. In this case the class is re-read and re-written
* with a ClassReader -> ClassWriter chain to perform this transformation.
@@ -1297,6 +1297,38 @@ public class ClassWriter extends ClassVisitor {
return result;
}
/**
* Adds a handle to the constant pool of the class being build. Does nothing
* if the constant pool already contains a similar item. <i>This method is
* intended for {@link Attribute} sub classes, and is normally not needed by
* class generators or adapters.</i>
*
* @param tag
* the kind of this handle. Must be {@link Opcodes#H_GETFIELD},
* {@link Opcodes#H_GETSTATIC}, {@link Opcodes#H_PUTFIELD},
* {@link Opcodes#H_PUTSTATIC}, {@link Opcodes#H_INVOKEVIRTUAL},
* {@link Opcodes#H_INVOKESTATIC},
* {@link Opcodes#H_INVOKESPECIAL},
* {@link Opcodes#H_NEWINVOKESPECIAL} or
* {@link Opcodes#H_INVOKEINTERFACE}.
* @param owner
* the internal name of the field or method owner class.
* @param name
* the name of the field or method.
* @param desc
* the descriptor of the field or method.
* @return the index of a new or already existing method type reference
* item.
*
* @deprecated this method is superseded by
* {@link #newHandle(int, String, String, String, boolean)}.
*/
@Deprecated
public int newHandle(final int tag, final String owner, final String name,
final String desc) {
return newHandle(tag, owner, name, desc, tag == Opcodes.H_INVOKEINTERFACE);
}
/**
* Adds a handle to the constant pool of the class being build. Does nothing
* if the constant pool already contains a similar item. <i>This method is
@@ -1486,7 +1518,7 @@ public class ClassWriter extends ClassVisitor {
* @param desc
* the method's descriptor.
* @param itf
* <code>true</code> if <code>owner</code> is an interface.
* &#60;tt&#62;true&#60;/tt&#62; if &#60;tt&#62;owner&#60;/tt&#62; is an interface.
* @return a new or already existing method reference item.
*/
Item newMethodItem(final String owner, final String name,
@@ -1515,7 +1547,7 @@ public class ClassWriter extends ClassVisitor {
* @param desc
* the method's descriptor.
* @param itf
* <code>true</code> if <code>owner</code> is an interface.
* &#60;tt&#62;true&#60;/tt&#62; if &#60;tt&#62;owner&#60;/tt&#62; is an interface.
* @return the index of a new or already existing method reference item.
*/
public int newMethod(final String owner, final String name,
@@ -1778,7 +1810,7 @@ public class ClassWriter extends ClassVisitor {
* @param key
* a constant pool item.
* @return the constant pool's hash table item which is equal to the given
* item, or <code>null</code> if there is no such item.
* item, or &#60;tt&#62;null&#60;/tt&#62; if there is no such item.
*/
private Item get(final Item key) {
Item i = items[key.hashCode % items.length];

View File

@@ -60,8 +60,8 @@ package org.redkale.asm;
/**
* A visitor to visit a Java field. The methods of this class must be called in
* the following order: ( <code>visitAnnotation</code> |
* <code>visitTypeAnnotation</code> | <code>visitAttribute</code> )* <code>visitEnd</code>.
* the following order: ( &#60;tt&#62;visitAnnotation&#60;/tt&#62; |
* &#60;tt&#62;visitTypeAnnotation&#60;/tt&#62; | &#60;tt&#62;visitAttribute&#60;/tt&#62; )* &#60;tt&#62;visitEnd&#60;/tt&#62;.
*
* @author Eric Bruneton
*/
@@ -101,6 +101,9 @@ public abstract class FieldVisitor {
* calls. May be null.
*/
public FieldVisitor(final int api, final FieldVisitor fv) {
if (api < Opcodes.ASM4 || api > Opcodes.ASM6) {
throw new IllegalArgumentException();
}
this.api = api;
this.fv = fv;
}
@@ -111,8 +114,8 @@ public abstract class FieldVisitor {
* @param desc
* the class descriptor of the annotation class.
* @param visible
* <code>true</code> if the annotation is visible at runtime.
* @return a visitor to visit the annotation values, or <code>null</code> if
* &#60;tt&#62;true&#60;/tt&#62; if the annotation is visible at runtime.
* @return a visitor to visit the annotation values, or &#60;tt&#62;null&#60;/tt&#62; if
* this visitor is not interested in visiting this annotation.
*/
public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
@@ -132,16 +135,19 @@ public abstract class FieldVisitor {
* @param typePath
* the path to the annotated type argument, wildcard bound, array
* element type, or static inner type within 'typeRef'. May be
* <code>null</code> if the annotation targets 'typeRef' as a whole.
* &#60;tt&#62;null&#60;/tt&#62; if the annotation targets 'typeRef' as a whole.
* @param desc
* the class descriptor of the annotation class.
* @param visible
* <code>true</code> if the annotation is visible at runtime.
* @return a visitor to visit the annotation values, or <code>null</code> if
* &#60;tt&#62;true&#60;/tt&#62; if the annotation is visible at runtime.
* @return a visitor to visit the annotation values, or &#60;tt&#62;null&#60;/tt&#62; if
* this visitor is not interested in visiting this annotation.
*/
public AnnotationVisitor visitTypeAnnotation(int typeRef,
TypePath typePath, String desc, boolean visible) {
if (api < Opcodes.ASM5) {
throw new RuntimeException();
}
if (fv != null) {
return fv.visitTypeAnnotation(typeRef, typePath, desc, visible);
}

View File

@@ -100,28 +100,28 @@ final class FieldWriter extends FieldVisitor {
private int value;
/**
* The runtime visible annotations of this field. May be <code>null</code>.
* The runtime visible annotations of this field. May be &#60;tt&#62;null&#60;/tt&#62;.
*/
private AnnotationWriter anns;
/**
* The runtime invisible annotations of this field. May be <code>null</code>.
* The runtime invisible annotations of this field. May be &#60;tt&#62;null&#60;/tt&#62;.
*/
private AnnotationWriter ianns;
/**
* The runtime visible type annotations of this field. May be <code>null</code>.
* The runtime visible type annotations of this field. May be &#60;tt&#62;null&#60;/tt&#62;.
*/
private AnnotationWriter tanns;
/**
* The runtime invisible type annotations of this field. May be
* <code>null</code>.
* &#60;tt&#62;null&#60;/tt&#62;.
*/
private AnnotationWriter itanns;
/**
* The non standard attributes of this field. May be <code>null</code>.
* The non standard attributes of this field. May be &#60;tt&#62;null&#60;/tt&#62;.
*/
private Attribute attrs;
@@ -141,9 +141,9 @@ final class FieldWriter extends FieldVisitor {
* @param desc
* the field's descriptor (see {@link Type}).
* @param signature
* the field's signature. May be <code>null</code>.
* the field's signature. May be &#60;tt&#62;null&#60;/tt&#62;.
* @param value
* the field's constant value. May be <code>null</code>.
* the field's constant value. May be &#60;tt&#62;null&#60;/tt&#62;.
*/
FieldWriter(final ClassWriter cw, final int access, final String name,
final String desc, final String signature, final Object value) {

View File

@@ -1403,7 +1403,7 @@ class Frame {
/**
* Merges the input frame of the given basic block with the input and output
* frames of this basic block. Returns <code>true</code> if the input frame of
* frames of this basic block. Returns &#60;tt&#62;true&#60;/tt&#62; if the input frame of
* the given label has been changed by this operation.
*
* @param cw
@@ -1413,7 +1413,7 @@ class Frame {
* @param edge
* the kind of the {@link Edge} between this label and 'label'.
* See {@link Edge#info}.
* @return <code>true</code> if the input frame of the given label has been
* @return &#60;tt&#62;true&#60;/tt&#62; if the input frame of the given label has been
* changed by this operation.
*/
final boolean merge(final ClassWriter cw, final Frame frame, final int edge) {
@@ -1511,7 +1511,7 @@ class Frame {
/**
* Merges the type at the given index in the given type array with the given
* type. Returns <code>true</code> if the type array has been modified by this
* type. Returns &#60;tt&#62;true&#60;/tt&#62; if the type array has been modified by this
* operation.
*
* @param cw
@@ -1522,7 +1522,7 @@ class Frame {
* an array of types.
* @param index
* the index of the type that must be merged in 'types'.
* @return <code>true</code> if the type array has been modified by this
* @return &#60;tt&#62;true&#60;/tt&#62; if the type array has been modified by this
* operation.
*/
private static boolean merge(final ClassWriter cw, int t,

View File

@@ -99,6 +99,34 @@ public final class Handle {
*/
final boolean itf;
/**
* Constructs a new field or method handle.
*
* @param tag
* the kind of field or method designated by this Handle. Must be
* {@link Opcodes#H_GETFIELD}, {@link Opcodes#H_GETSTATIC},
* {@link Opcodes#H_PUTFIELD}, {@link Opcodes#H_PUTSTATIC},
* {@link Opcodes#H_INVOKEVIRTUAL},
* {@link Opcodes#H_INVOKESTATIC},
* {@link Opcodes#H_INVOKESPECIAL},
* {@link Opcodes#H_NEWINVOKESPECIAL} or
* {@link Opcodes#H_INVOKEINTERFACE}.
* @param owner
* the internal name of the class that owns the field or method
* designated by this handle.
* @param name
* the name of the field or method designated by this handle.
* @param desc
* the descriptor of the field or method designated by this
* handle.
*
* @deprecated this constructor has been superseded
* by {@link #Handle(int, String, String, String, boolean)}.
*/
@Deprecated
public Handle(int tag, String owner, String name, String desc) {
this(tag, owner, name, desc, tag == Opcodes.H_INVOKEINTERFACE);
}
/**
* Constructs a new field or method handle.

View File

@@ -82,7 +82,7 @@ class Handler {
/**
* Internal name of the type of exceptions handled by this handler, or
* <code>null</code> to catch any exceptions.
* &#60;tt&#62;null&#60;/tt&#62; to catch any exceptions.
*/
String desc;

View File

@@ -306,8 +306,8 @@ final class Item {
* @param i
* the item to be compared to this one. Both items must have the
* same {@link #type}.
* @return <code>true</code> if the given item if equal to this one,
* <code>false</code> otherwise.
* @return &#60;tt&#62;true&#60;/tt&#62; if the given item if equal to this one,
* &#60;tt&#62;false&#60;/tt&#62; otherwise.
*/
boolean isEqualTo(final Item i) {
switch (type) {

View File

@@ -325,8 +325,8 @@ public class Label {
* the position of first byte of the bytecode instruction that
* contains this label.
* @param wideOffset
* <code>true</code> if the reference must be stored in 4 bytes, or
* <code>false</code> if it must be stored with 2 bytes.
* &#60;tt&#62;true&#60;/tt&#62; if the reference must be stored in 4 bytes, or
* &#60;tt&#62;false&#60;/tt&#62; if it must be stored with 2 bytes.
* @throws IllegalArgumentException
* if this label has not been created by the given code writer.
*/
@@ -389,7 +389,7 @@ public class Label {
* the position of this label in the bytecode.
* @param data
* the bytecode of the method.
* @return <code>true</code> if a blank that was left for this label was too
* @return &#60;tt&#62;true&#60;/tt&#62; if a blank that was left for this label was too
* small to store the offset. In such a case the corresponding jump
* instruction is replaced with a pseudo instruction (using unused
* opcodes) using an unsigned two bytes offset. These pseudo

View File

@@ -60,24 +60,24 @@ package org.redkale.asm;
/**
* A visitor to visit a Java method. The methods of this class must be called in
* the following order: ( <code>visitParameter</code> )* [
* <code>visitAnnotationDefault</code> ] ( <code>visitAnnotation</code> |
* <code>visitParameterAnnotation</code> <code>visitTypeAnnotation</code> |
* <code>visitAttribute</code> )* [ <code>visitCode</code> ( <code>visitFrame</code> |
* <code>visit<i>X</i>Insn</code> | <code>visitLabel</code> |
* <code>visitInsnAnnotation</code> | <code>visitTryCatchBlock</code> |
* <code>visitTryCatchAnnotation</code> | <code>visitLocalVariable</code> |
* <code>visitLocalVariableAnnotation</code> | <code>visitLineNumber</code> )*
* <code>visitMaxs</code> ] <code>visitEnd</code>. In addition, the
* <code>visit<i>X</i>Insn</code> and <code>visitLabel</code> methods must be called in
* the following order: ( &#60;tt&#62;visitParameter&#60;/tt&#62; )* [
* &#60;tt&#62;visitAnnotationDefault&#60;/tt&#62; ] ( &#60;tt&#62;visitAnnotation&#60;/tt&#62; |
* &#60;tt&#62;visitParameterAnnotation&#60;/tt&#62; &#60;tt&#62;visitTypeAnnotation&#60;/tt&#62; |
* &#60;tt&#62;visitAttribute&#60;/tt&#62; )* [ &#60;tt&#62;visitCode&#60;/tt&#62; ( &#60;tt&#62;visitFrame&#60;/tt&#62; |
* &#60;tt&#62;visit<i>X</i>Insn&#60;/tt&#62; | &#60;tt&#62;visitLabel&#60;/tt&#62; |
* &#60;tt&#62;visitInsnAnnotation&#60;/tt&#62; | &#60;tt&#62;visitTryCatchBlock&#60;/tt&#62; |
* &#60;tt&#62;visitTryCatchAnnotation&#60;/tt&#62; | &#60;tt&#62;visitLocalVariable&#60;/tt&#62; |
* &#60;tt&#62;visitLocalVariableAnnotation&#60;/tt&#62; | &#60;tt&#62;visitLineNumber&#60;/tt&#62; )*
* &#60;tt&#62;visitMaxs&#60;/tt&#62; ] &#60;tt&#62;visitEnd&#60;/tt&#62;. In addition, the
* &#60;tt&#62;visit<i>X</i>Insn&#60;/tt&#62; and &#60;tt&#62;visitLabel&#60;/tt&#62; methods must be called in
* the sequential order of the bytecode instructions of the visited code,
* <code>visitInsnAnnotation</code> must be called <i>after</i> the annotated
* instruction, <code>visitTryCatchBlock</code> must be called <i>before</i> the
* &#60;tt&#62;visitInsnAnnotation&#60;/tt&#62; must be called <i>after</i> the annotated
* instruction, &#60;tt&#62;visitTryCatchBlock&#60;/tt&#62; must be called <i>before</i> the
* labels passed as arguments have been visited,
* <code>visitTryCatchBlockAnnotation</code> must be called <i>after</i> the
* &#60;tt&#62;visitTryCatchBlockAnnotation&#60;/tt&#62; must be called <i>after</i> the
* corresponding try catch block has been visited, and the
* <code>visitLocalVariable</code>, <code>visitLocalVariableAnnotation</code> and
* <code>visitLineNumber</code> methods must be called <i>after</i> the labels
* &#60;tt&#62;visitLocalVariable&#60;/tt&#62;, &#60;tt&#62;visitLocalVariableAnnotation&#60;/tt&#62; and
* &#60;tt&#62;visitLineNumber&#60;/tt&#62; methods must be called <i>after</i> the labels
* passed as arguments have been visited.
*
* @author Eric Bruneton
@@ -118,6 +118,9 @@ public abstract class MethodVisitor {
* calls. May be null.
*/
public MethodVisitor(final int api, final MethodVisitor mv) {
if (api < Opcodes.ASM4 || api > Opcodes.ASM6) {
throw new IllegalArgumentException();
}
this.api = api;
this.mv = mv;
}
@@ -132,11 +135,14 @@ public abstract class MethodVisitor {
* @param name
* parameter name or null if none is provided.
* @param access
* the parameter's access flags, only <code>ACC_FINAL</code>,
* <code>ACC_SYNTHETIC</code> or/and <code>ACC_MANDATED</code> are
* the parameter's access flags, only &#60;tt&#62;ACC_FINAL&#60;/tt&#62;,
* &#60;tt&#62;ACC_SYNTHETIC&#60;/tt&#62; or/and &#60;tt&#62;ACC_MANDATED&#60;/tt&#62; are
* allowed (see {@link Opcodes}).
*/
public void visitParameter(String name, int access) {
if (api < Opcodes.ASM5) {
throw new RuntimeException();
}
if (mv != null) {
mv.visitParameter(name, access);
}
@@ -146,7 +152,7 @@ public abstract class MethodVisitor {
* Visits the default value of this annotation interface method.
*
* @return a visitor to the visit the actual default value of this
* annotation interface method, or <code>null</code> if this visitor is
* annotation interface method, or &#60;tt&#62;null&#60;/tt&#62; if this visitor is
* not interested in visiting this default value. The 'name'
* parameters passed to the methods of this annotation visitor are
* ignored. Moreover, exacly one visit method must be called on this
@@ -165,8 +171,8 @@ public abstract class MethodVisitor {
* @param desc
* the class descriptor of the annotation class.
* @param visible
* <code>true</code> if the annotation is visible at runtime.
* @return a visitor to visit the annotation values, or <code>null</code> if
* &#60;tt&#62;true&#60;/tt&#62; if the annotation is visible at runtime.
* @return a visitor to visit the annotation values, or &#60;tt&#62;null&#60;/tt&#62; if
* this visitor is not interested in visiting this annotation.
*/
public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
@@ -193,16 +199,19 @@ public abstract class MethodVisitor {
* @param typePath
* the path to the annotated type argument, wildcard bound, array
* element type, or static inner type within 'typeRef'. May be
* <code>null</code> if the annotation targets 'typeRef' as a whole.
* &#60;tt&#62;null&#60;/tt&#62; if the annotation targets 'typeRef' as a whole.
* @param desc
* the class descriptor of the annotation class.
* @param visible
* <code>true</code> if the annotation is visible at runtime.
* @return a visitor to visit the annotation values, or <code>null</code> if
* &#60;tt&#62;true&#60;/tt&#62; if the annotation is visible at runtime.
* @return a visitor to visit the annotation values, or &#60;tt&#62;null&#60;/tt&#62; if
* this visitor is not interested in visiting this annotation.
*/
public AnnotationVisitor visitTypeAnnotation(int typeRef,
TypePath typePath, String desc, boolean visible) {
if (api < Opcodes.ASM5) {
throw new RuntimeException();
}
if (mv != null) {
return mv.visitTypeAnnotation(typeRef, typePath, desc, visible);
}
@@ -217,8 +226,8 @@ public abstract class MethodVisitor {
* @param desc
* the class descriptor of the annotation class.
* @param visible
* <code>true</code> if the annotation is visible at runtime.
* @return a visitor to visit the annotation values, or <code>null</code> if
* &#60;tt&#62;true&#60;/tt&#62; if the annotation is visible at runtime.
* @return a visitor to visit the annotation values, or &#60;tt&#62;null&#60;/tt&#62; if
* this visitor is not interested in visiting this annotation.
*/
public AnnotationVisitor visitParameterAnnotation(int parameter,
@@ -444,6 +453,34 @@ public abstract class MethodVisitor {
}
}
/**
* Visits a method instruction. A method instruction is an instruction that
* invokes a method.
*
* @param opcode
* the opcode of the type instruction to be visited. This opcode
* is either INVOKEVIRTUAL, INVOKESPECIAL, INVOKESTATIC or
* INVOKEINTERFACE.
* @param owner
* the internal name of the method's owner class (see
* {@link Type#getInternalName() getInternalName}).
* @param name
* the method's name.
* @param desc
* the method's descriptor (see {@link Type Type}).
*/
@Deprecated
public void visitMethodInsn(int opcode, String owner, String name,
String desc) {
if (api >= Opcodes.ASM5) {
boolean itf = opcode == Opcodes.INVOKEINTERFACE;
visitMethodInsn(opcode, owner, name, desc, itf);
return;
}
if (mv != null) {
mv.visitMethodInsn(opcode, owner, name, desc);
}
}
/**
* Visits a method instruction. A method instruction is an instruction that
@@ -465,6 +502,14 @@ public abstract class MethodVisitor {
*/
public void visitMethodInsn(int opcode, String owner, String name,
String desc, boolean itf) {
if (api < Opcodes.ASM5) {
if (itf != (opcode == Opcodes.INVOKEINTERFACE)) {
throw new IllegalArgumentException(
"INVOKESPECIAL/STATIC on interfaces require ASM 5");
}
visitMethodInsn(opcode, owner, name, desc);
return;
}
if (mv != null) {
mv.visitMethodInsn(opcode, owner, name, desc, itf);
}
@@ -569,7 +614,7 @@ public abstract class MethodVisitor {
* the constant to be loaded on the stack. This parameter must be
* a non null {@link Integer}, a {@link Float}, a {@link Long}, a
* {@link Double}, a {@link String}, a {@link Type} of OBJECT or
* ARRAY sort for <code>.class</code> constants, for classes whose
* ARRAY sort for &#60;tt&#62;.class&#60;/tt&#62; constants, for classes whose
* version is 49.0, a {@link Type} of METHOD sort or a
* {@link Handle} for MethodType and MethodHandle constants, for
* classes whose version is 51.0.
@@ -604,8 +649,8 @@ public abstract class MethodVisitor {
* @param dflt
* beginning of the default handler block.
* @param labels
* beginnings of the handler blocks. <code>labels[i]</code> is the
* beginning of the handler block for the <code>min + i</code> key.
* beginnings of the handler blocks. &#60;tt&#62;labels[i]&#60;/tt&#62; is the
* beginning of the handler block for the &#60;tt&#62;min + i&#60;/tt&#62; key.
*/
public void visitTableSwitchInsn(int min, int max, Label dflt,
Label... labels) {
@@ -622,8 +667,8 @@ public abstract class MethodVisitor {
* @param keys
* the values of the keys.
* @param labels
* beginnings of the handler blocks. <code>labels[i]</code> is the
* beginning of the handler block for the <code>keys[i]</code> key.
* beginnings of the handler blocks. &#60;tt&#62;labels[i]&#60;/tt&#62; is the
* beginning of the handler block for the &#60;tt&#62;keys[i]&#60;/tt&#62; key.
*/
public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) {
if (mv != null) {
@@ -668,16 +713,19 @@ public abstract class MethodVisitor {
* @param typePath
* the path to the annotated type argument, wildcard bound, array
* element type, or static inner type within 'typeRef'. May be
* <code>null</code> if the annotation targets 'typeRef' as a whole.
* &#60;tt&#62;null&#60;/tt&#62; if the annotation targets 'typeRef' as a whole.
* @param desc
* the class descriptor of the annotation class.
* @param visible
* <code>true</code> if the annotation is visible at runtime.
* @return a visitor to visit the annotation values, or <code>null</code> if
* &#60;tt&#62;true&#60;/tt&#62; if the annotation is visible at runtime.
* @return a visitor to visit the annotation values, or &#60;tt&#62;null&#60;/tt&#62; if
* this visitor is not interested in visiting this annotation.
*/
public AnnotationVisitor visitInsnAnnotation(int typeRef,
TypePath typePath, String desc, boolean visible) {
if (api < Opcodes.ASM5) {
throw new RuntimeException();
}
if (mv != null) {
return mv.visitInsnAnnotation(typeRef, typePath, desc, visible);
}
@@ -699,7 +747,7 @@ public abstract class MethodVisitor {
* beginning of the exception handler's code.
* @param type
* internal name of the type of exceptions handled by the
* handler, or <code>null</code> to catch any exceptions (for
* handler, or &#60;tt&#62;null&#60;/tt&#62; to catch any exceptions (for
* "finally" blocks).
* @throws IllegalArgumentException
* if one of the labels has already been visited by this visitor
@@ -725,16 +773,19 @@ public abstract class MethodVisitor {
* @param typePath
* the path to the annotated type argument, wildcard bound, array
* element type, or static inner type within 'typeRef'. May be
* <code>null</code> if the annotation targets 'typeRef' as a whole.
* &#60;tt&#62;null&#60;/tt&#62; if the annotation targets 'typeRef' as a whole.
* @param desc
* the class descriptor of the annotation class.
* @param visible
* <code>true</code> if the annotation is visible at runtime.
* @return a visitor to visit the annotation values, or <code>null</code> if
* &#60;tt&#62;true&#60;/tt&#62; if the annotation is visible at runtime.
* @return a visitor to visit the annotation values, or &#60;tt&#62;null&#60;/tt&#62; if
* this visitor is not interested in visiting this annotation.
*/
public AnnotationVisitor visitTryCatchAnnotation(int typeRef,
TypePath typePath, String desc, boolean visible) {
if (api < Opcodes.ASM5) {
throw new RuntimeException();
}
if (mv != null) {
return mv.visitTryCatchAnnotation(typeRef, typePath, desc, visible);
}
@@ -750,7 +801,7 @@ public abstract class MethodVisitor {
* the type descriptor of this local variable.
* @param signature
* the type signature of this local variable. May be
* <code>null</code> if the local variable type does not use generic
* &#60;tt&#62;null&#60;/tt&#62; if the local variable type does not use generic
* types.
* @param start
* the first instruction corresponding to the scope of this local
@@ -782,7 +833,7 @@ public abstract class MethodVisitor {
* @param typePath
* the path to the annotated type argument, wildcard bound, array
* element type, or static inner type within 'typeRef'. May be
* <code>null</code> if the annotation targets 'typeRef' as a whole.
* &#60;tt&#62;null&#60;/tt&#62; if the annotation targets 'typeRef' as a whole.
* @param start
* the fist instructions corresponding to the continuous ranges
* that make the scope of this local variable (inclusive).
@@ -796,13 +847,16 @@ public abstract class MethodVisitor {
* @param desc
* the class descriptor of the annotation class.
* @param visible
* <code>true</code> if the annotation is visible at runtime.
* @return a visitor to visit the annotation values, or <code>null</code> if
* &#60;tt&#62;true&#60;/tt&#62; if the annotation is visible at runtime.
* @return a visitor to visit the annotation values, or &#60;tt&#62;null&#60;/tt&#62; if
* this visitor is not interested in visiting this annotation.
*/
public AnnotationVisitor visitLocalVariableAnnotation(int typeRef,
TypePath typePath, Label[] start, Label[] end, int[] index,
String desc, boolean visible) {
if (api < Opcodes.ASM5) {
throw new RuntimeException();
}
if (mv != null) {
return mv.visitLocalVariableAnnotation(typeRef, typePath, start,
end, index, desc, visible);
@@ -819,7 +873,7 @@ public abstract class MethodVisitor {
* @param start
* the first instruction corresponding to this line number.
* @throws IllegalArgumentException
* if <code>start</code> has not already been visited by this
* if &#60;tt&#62;start&#60;/tt&#62; has not already been visited by this
* visitor (by the {@link #visitLabel visitLabel} method).
*/
public void visitLineNumber(int line, Label start) {

View File

@@ -218,41 +218,41 @@ class MethodWriter extends MethodVisitor {
int[] exceptions;
/**
* The annotation default attribute of this method. May be <code>null</code>.
* The annotation default attribute of this method. May be &#60;tt&#62;null&#60;/tt&#62;.
*/
private ByteVector annd;
/**
* The runtime visible annotations of this method. May be <code>null</code>.
* The runtime visible annotations of this method. May be &#60;tt&#62;null&#60;/tt&#62;.
*/
private AnnotationWriter anns;
/**
* The runtime invisible annotations of this method. May be <code>null</code>.
* The runtime invisible annotations of this method. May be &#60;tt&#62;null&#60;/tt&#62;.
*/
private AnnotationWriter ianns;
/**
* The runtime visible type annotations of this method. May be <code>null</code>
* The runtime visible type annotations of this method. May be &#60;tt&#62;null&#60;/tt&#62;
* .
*/
private AnnotationWriter tanns;
/**
* The runtime invisible type annotations of this method. May be
* <code>null</code>.
* &#60;tt&#62;null&#60;/tt&#62;.
*/
private AnnotationWriter itanns;
/**
* The runtime visible parameter annotations of this method. May be
* <code>null</code>.
* &#60;tt&#62;null&#60;/tt&#62;.
*/
private AnnotationWriter[] panns;
/**
* The runtime invisible parameter annotations of this method. May be
* <code>null</code>.
* &#60;tt&#62;null&#60;/tt&#62;.
*/
private AnnotationWriter[] ipanns;
@@ -381,12 +381,12 @@ class MethodWriter extends MethodVisitor {
private int lastCodeOffset;
/**
* The runtime visible type annotations of the code. May be <code>null</code>.
* The runtime visible type annotations of the code. May be &#60;tt&#62;null&#60;/tt&#62;.
*/
private AnnotationWriter ctanns;
/**
* The runtime invisible type annotations of the code. May be <code>null</code>.
* The runtime invisible type annotations of the code. May be &#60;tt&#62;null&#60;/tt&#62;.
*/
private AnnotationWriter ictanns;
@@ -446,7 +446,7 @@ class MethodWriter extends MethodVisitor {
* is relative to the beginning of the current basic block, i.e., the true
* stack size after the last visited instruction is equal to the
* {@link Label#inputStackTop beginStackSize} of the current basic block
* plus <code>stackSize</code>.
* plus &#60;tt&#62;stackSize&#60;/tt&#62;.
*/
private int stackSize;
@@ -455,7 +455,7 @@ class MethodWriter extends MethodVisitor {
* This size is relative to the beginning of the current basic block, i.e.,
* the true maximum stack size after the last visited instruction is equal
* to the {@link Label#inputStackTop beginStackSize} of the current basic
* block plus <code>stackSize</code>.
* block plus &#60;tt&#62;stackSize&#60;/tt&#62;.
*/
private int maxStackSize;
@@ -475,10 +475,10 @@ class MethodWriter extends MethodVisitor {
* @param desc
* the method's descriptor (see {@link Type}).
* @param signature
* the method's signature. May be <code>null</code>.
* the method's signature. May be &#60;tt&#62;null&#60;/tt&#62;.
* @param exceptions
* the internal names of the method's exceptions. May be
* <code>null</code>.
* &#60;tt&#62;null&#60;/tt&#62;.
* @param compute
* Indicates what must be automatically computed (see #compute).
*/

View File

@@ -60,9 +60,9 @@ package org.redkale.asm;
/**
* A visitor to visit a Java module. The methods of this class must be called in
* the following order: <code>visitMainClass</code> | ( <code>visitPackage</code> |
* <code>visitRequire</code> | <code>visitExport</code> | <code>visitOpen</code> |
* <code>visitUse</code> | <code>visitProvide</code> )* <code>visitEnd</code>.
* the following order: &#60;tt&#62;visitMainClass&#60;/tt&#62; | ( &#60;tt&#62;visitPackage&#60;/tt&#62; |
* &#60;tt&#62;visitRequire&#60;/tt&#62; | &#60;tt&#62;visitExport&#60;/tt&#62; | &#60;tt&#62;visitOpen&#60;/tt&#62; |
* &#60;tt&#62;visitUse&#60;/tt&#62; | &#60;tt&#62;visitProvide&#60;/tt&#62; )* &#60;tt&#62;visitEnd&#60;/tt&#62;.
*
* The methods {@link #visitRequire(String, int, String)}, {@link #visitExport(String, int, String...)},
* {@link #visitOpen(String, int, String...)} and {@link #visitPackage(String)}
@@ -157,7 +157,7 @@ public abstract class ModuleVisitor {
* {@code ACC_MANDATED}.
* @param modules the qualified names of the modules that can access to
* the public classes of the exported package or
* <code>null</code>.
* &#60;tt&#62;null&#60;/tt&#62;.
*/
public void visitExport(String packaze, int access, String... modules) {
if (mv != null) {
@@ -174,7 +174,7 @@ public abstract class ModuleVisitor {
* {@code ACC_MANDATED}.
* @param modules the qualified names of the modules that can use deep
* reflection to the classes of the open package or
* <code>null</code>.
* &#60;tt&#62;null&#60;/tt&#62;.
*/
public void visitOpen(String packaze, int access, String... modules) {
if (mv != null) {

View File

@@ -80,12 +80,17 @@ public interface Opcodes {
// versions
int V1_1 = 3 << 16 | 45;
int V1_2 = 0 << 16 | 46;
int V1_3 = 0 << 16 | 47;
int V1_4 = 0 << 16 | 48;
int V1_5 = 0 << 16 | 49;
int V1_6 = 0 << 16 | 50;
int V1_7 = 0 << 16 | 51;
int V1_8 = 0 << 16 | 52;
int V9 = 0 << 16 | 53;
int V10 = 0 << 16 | 54;
int V11 = 0 << 16 | 55;
// access flags

View File

@@ -71,47 +71,47 @@ import java.lang.reflect.Method;
public class Type {
/**
* The sort of the <code>void</code> type. See {@link #getSort getSort}.
* The sort of the &#60;tt&#62;void&#60;/tt&#62; type. See {@link #getSort getSort}.
*/
public static final int VOID = 0;
/**
* The sort of the <code>boolean</code> type. See {@link #getSort getSort}.
* The sort of the &#60;tt&#62;boolean&#60;/tt&#62; type. See {@link #getSort getSort}.
*/
public static final int BOOLEAN = 1;
/**
* The sort of the <code>char</code> type. See {@link #getSort getSort}.
* The sort of the &#60;tt&#62;char&#60;/tt&#62; type. See {@link #getSort getSort}.
*/
public static final int CHAR = 2;
/**
* The sort of the <code>byte</code> type. See {@link #getSort getSort}.
* The sort of the &#60;tt&#62;byte&#60;/tt&#62; type. See {@link #getSort getSort}.
*/
public static final int BYTE = 3;
/**
* The sort of the <code>short</code> type. See {@link #getSort getSort}.
* The sort of the &#60;tt&#62;short&#60;/tt&#62; type. See {@link #getSort getSort}.
*/
public static final int SHORT = 4;
/**
* The sort of the <code>int</code> type. See {@link #getSort getSort}.
* The sort of the &#60;tt&#62;int&#60;/tt&#62; type. See {@link #getSort getSort}.
*/
public static final int INT = 5;
/**
* The sort of the <code>float</code> type. See {@link #getSort getSort}.
* The sort of the &#60;tt&#62;float&#60;/tt&#62; type. See {@link #getSort getSort}.
*/
public static final int FLOAT = 6;
/**
* The sort of the <code>long</code> type. See {@link #getSort getSort}.
* The sort of the &#60;tt&#62;long&#60;/tt&#62; type. See {@link #getSort getSort}.
*/
public static final int LONG = 7;
/**
* The sort of the <code>double</code> type. See {@link #getSort getSort}.
* The sort of the &#60;tt&#62;double&#60;/tt&#62; type. See {@link #getSort getSort}.
*/
public static final int DOUBLE = 8;
@@ -131,55 +131,55 @@ public class Type {
public static final int METHOD = 11;
/**
* The <code>void</code> type.
* The &#60;tt&#62;void&#60;/tt&#62; type.
*/
public static final Type VOID_TYPE = new Type(VOID, null, ('V' << 24)
| (5 << 16) | (0 << 8) | 0, 1);
/**
* The <code>boolean</code> type.
* The &#60;tt&#62;boolean&#60;/tt&#62; type.
*/
public static final Type BOOLEAN_TYPE = new Type(BOOLEAN, null, ('Z' << 24)
| (0 << 16) | (5 << 8) | 1, 1);
/**
* The <code>char</code> type.
* The &#60;tt&#62;char&#60;/tt&#62; type.
*/
public static final Type CHAR_TYPE = new Type(CHAR, null, ('C' << 24)
| (0 << 16) | (6 << 8) | 1, 1);
/**
* The <code>byte</code> type.
* The &#60;tt&#62;byte&#60;/tt&#62; type.
*/
public static final Type BYTE_TYPE = new Type(BYTE, null, ('B' << 24)
| (0 << 16) | (5 << 8) | 1, 1);
/**
* The <code>short</code> type.
* The &#60;tt&#62;short&#60;/tt&#62; type.
*/
public static final Type SHORT_TYPE = new Type(SHORT, null, ('S' << 24)
| (0 << 16) | (7 << 8) | 1, 1);
/**
* The <code>int</code> type.
* The &#60;tt&#62;int&#60;/tt&#62; type.
*/
public static final Type INT_TYPE = new Type(INT, null, ('I' << 24)
| (0 << 16) | (0 << 8) | 1, 1);
/**
* The <code>float</code> type.
* The &#60;tt&#62;float&#60;/tt&#62; type.
*/
public static final Type FLOAT_TYPE = new Type(FLOAT, null, ('F' << 24)
| (2 << 16) | (2 << 8) | 1, 1);
/**
* The <code>long</code> type.
* The &#60;tt&#62;long&#60;/tt&#62; type.
*/
public static final Type LONG_TYPE = new Type(LONG, null, ('J' << 24)
| (1 << 16) | (1 << 8) | 2, 1);
/**
* The <code>double</code> type.
* The &#60;tt&#62;double&#60;/tt&#62; type.
*/
public static final Type DOUBLE_TYPE = new Type(DOUBLE, null, ('D' << 24)
| (3 << 16) | (3 << 8) | 2, 1);
@@ -439,8 +439,8 @@ public class Type {
* @return the size of the arguments of the method (plus one for the
* implicit this argument), argSize, and the size of its return
* value, retSize, packed into a single int i =
* <code>(argSize &lt;&lt; 2) | retSize</code> (argSize is therefore equal to
* <code>i &gt;&gt; 2</code>, and retSize to <code>i &amp; 0x03</code>).
* &#60;tt&#62;(argSize &lt;&lt; 2) | retSize&#60;/tt&#62; (argSize is therefore equal to
* &#60;tt&#62;i &gt;&gt; 2&#60;/tt&#62;, and retSize to &#60;tt&#62;i &amp; 0x03&#60;/tt&#62;).
*/
public static int getArgumentsAndReturnSizes(final String desc) {
int n = 1;
@@ -645,9 +645,9 @@ public class Type {
* @return the size of the arguments (plus one for the implicit this
* argument), argSize, and the size of the return value, retSize,
* packed into a single
* int i = <code>(argSize &lt;&lt; 2) | retSize</code>
* (argSize is therefore equal to <code>i &gt;&gt; 2</code>,
* and retSize to <code>i &amp; 0x03</code>).
* int i = &#60;tt&#62;(argSize &lt;&lt; 2) | retSize&#60;/tt&#62;
* (argSize is therefore equal to &#60;tt&#62;i &gt;&gt; 2&#60;/tt&#62;,
* and retSize to &#60;tt&#62;i &amp; 0x03&#60;/tt&#62;).
*/
public int getArgumentsAndReturnSizes() {
return getArgumentsAndReturnSizes(getDescriptor());
@@ -838,8 +838,8 @@ public class Type {
* Returns the size of values of this type. This method must not be used for
* method types.
*
* @return the size of values of this type, i.e., 2 for <code>long</code> and
* <code>double</code>, 0 for <code>void</code> and 1 otherwise.
* @return the size of values of this type, i.e., 2 for &#60;tt&#62;long&#60;/tt&#62; and
* &#60;tt&#62;double&#60;/tt&#62;, 0 for &#60;tt&#62;void&#60;/tt&#62; and 1 otherwise.
*/
public int getSize() {
// the size is in byte 0 of 'off' for primitive types (buf == null)
@@ -855,8 +855,8 @@ public class Type {
* ISTORE, IALOAD, IASTORE, IADD, ISUB, IMUL, IDIV, IREM, INEG,
* ISHL, ISHR, IUSHR, IAND, IOR, IXOR and IRETURN.
* @return an opcode that is similar to the given opcode, but adapted to
* this Java type. For example, if this type is <code>float</code> and
* <code>opcode</code> is IRETURN, this method returns FRETURN.
* this Java type. For example, if this type is &#60;tt&#62;float&#60;/tt&#62; and
* &#60;tt&#62;opcode&#60;/tt&#62; is IRETURN, this method returns FRETURN.
*/
public int getOpcode(final int opcode) {
if (opcode == Opcodes.IALOAD || opcode == Opcodes.IASTORE) {
@@ -879,7 +879,7 @@ public class Type {
*
* @param o
* the object to be compared to this type.
* @return <code>true</code> if the given object is equal to this type.
* @return &#60;tt&#62;true&#60;/tt&#62; if the given object is equal to this type.
*/
@Override
public boolean equals(final Object o) {

View File

@@ -89,6 +89,20 @@ public final class Application {
*/
public static final String RESNAME_APP_ADDR = "APP_ADDR";
/**
* 当前进程的work线程池 类型Executor、ExecutorService
*
* @since 2.3.0
*/
public static final String RESNAME_APP_EXECUTOR = "APP_EXECUTOR";
/**
* 当前进程的客户端组, 类型AsyncGroup
*
* @since 2.3.0
*/
public static final String RESNAME_APP_GROUP = "APP_GROUP";
/**
* 当前Service所属的SNCP Server的地址 类型: SocketAddress、InetSocketAddress、String <br>
*/
@@ -106,8 +120,11 @@ public final class Application {
/**
* 当前Server的线程池
*
* @deprecated 2.3.0 使用RESNAME_APP_EXECUTOR
*/
public static final String RESNAME_SERVER_EXECUTOR = Server.RESNAME_SERVER_EXECUTOR;
@Deprecated
public static final String RESNAME_SERVER_EXECUTOR2 = Server.RESNAME_SERVER_EXECUTOR2;
/**
* 当前Server的ResourceFactory
@@ -123,6 +140,10 @@ public final class Application {
//本地IP地址
final InetSocketAddress localAddress;
//业务逻辑线程池
//@since 2.3.0
final ExecutorService workExecutor;
//CacheSource 资源
final List<CacheSource> cacheSources = new CopyOnWriteArrayList<>();
@@ -135,6 +156,9 @@ public final class Application {
//SNCP传输端的TransportFactory, 注意: 只给SNCP使用
final TransportFactory sncpTransportFactory;
//给客户端使用包含SNCP客户端、自定义数据库客户端连接池
final AsyncGroup asyncGroup;
//第三方服务发现管理接口
//@since 2.1.0
final ClusterAgent clusterAgent;
@@ -289,9 +313,6 @@ public final class Application {
this.classLoader = new RedkaleClassLoader(Thread.currentThread().getContextClassLoader());
logger.log(Level.INFO, "------------------------- Redkale " + Redkale.getDotedVersion() + " -------------------------");
//------------------配置 <transport> 节点 ------------------
ObjectPool<ByteBuffer> transportPool = null;
ExecutorService transportExec = null;
AsynchronousChannelGroup transportGroup = null;
final AnyValue resources = config.getAnyValue("resources");
TransportStrategy strategy = null;
String excludelib0 = null;
@@ -301,9 +322,9 @@ public final class Application {
int bufferPoolSize = Runtime.getRuntime().availableProcessors() * 8;
int readTimeoutSeconds = TransportFactory.DEFAULT_READTIMEOUTSECONDS;
int writeTimeoutSeconds = TransportFactory.DEFAULT_WRITETIMEOUTSECONDS;
AtomicLong createBufferCounter = new AtomicLong();
AtomicLong cycleBufferCounter = new AtomicLong();
AnyValue executorConf = null;
if (resources != null) {
executorConf = resources.getAnyValue("executor");
AnyValue excludelibConf = resources.getAnyValue("excludelibs");
if (excludelibConf != null) excludelib0 = excludelibConf.getValue("value");
AnyValue transportConf = resources.getAnyValue("transport");
@@ -316,31 +337,6 @@ public final class Application {
writeTimeoutSeconds = transportConf.getIntValue("writeTimeoutSeconds", writeTimeoutSeconds);
final int threads = parseLenth(transportConf.getValue("threads"), groupsize * Runtime.getRuntime().availableProcessors() * 2);
bufferPoolSize = parseLenth(transportConf.getValue("bufferPoolSize"), threads * 4);
final int capacity = bufferCapacity;
transportPool = ObjectPool.createSafePool(createBufferCounter, cycleBufferCounter, bufferPoolSize,
(Object... params) -> ByteBuffer.allocateDirect(capacity), null, (e) -> {
if (e == null || e.isReadOnly() || e.capacity() != capacity) return false;
e.clear();
return true;
});
//-----------transportChannelGroup--------------
try {
final String strategyClass = transportConf.getValue("strategy");
if (strategyClass != null && !strategyClass.isEmpty()) {
strategy = (TransportStrategy) classLoader.loadClass(strategyClass).getDeclaredConstructor().newInstance();
}
final AtomicInteger counter = new AtomicInteger();
transportExec = Executors.newFixedThreadPool(threads, (Runnable r) -> {
Thread t = new Thread(r);
t.setDaemon(true);
t.setName("Redkale-Transport-Thread-" + counter.incrementAndGet());
return t;
});
transportGroup = AsynchronousChannelGroup.withCachedThreadPool(transportExec, 1);
} catch (Exception e) {
throw new RuntimeException(e);
}
logger.log(Level.INFO, Transport.class.getSimpleName() + " configure bufferCapacity = " + bufferCapacity / 1024 + "K; bufferPoolSize = " + bufferPoolSize + "; threads = " + threads + ";");
}
AnyValue clusterConf = resources.getAnyValue("cluster");
@@ -424,31 +420,41 @@ public final class Application {
}
}
}
if (transportGroup == null) {
final AtomicInteger counter = new AtomicInteger();
transportExec = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() * 8, (Runnable r) -> {
Thread t = new Thread(r);
t.setDaemon(true);
t.setName("Redkale-Transport-Thread-" + counter.incrementAndGet());
return t;
});
try {
transportGroup = AsynchronousChannelGroup.withCachedThreadPool(transportExec, 1);
} catch (Exception e) {
throw new RuntimeException(e);
ExecutorService workExecutor0 = null;
if (executorConf != null) {
final AtomicReference<ExecutorService> workref = new AtomicReference<>();
int executorThreads = executorConf.getIntValue("threads", Math.max(2, Runtime.getRuntime().availableProcessors()));
boolean executorHash = executorConf.getBoolValue("hash");
if (executorThreads > 0) {
final AtomicInteger workcounter = new AtomicInteger();
if (executorHash) {
workExecutor0 = new ThreadHashExecutor(executorThreads, (Runnable r) -> {
int c = workcounter.incrementAndGet();
String threadname = "Redkale-HashWorkThread-" + (c > 9 ? c : ("0" + c));
Thread t = new WorkThread(threadname, workref.get(), r);
return t;
});
} else {
workExecutor0 = Executors.newFixedThreadPool(executorThreads, (Runnable r) -> {
int c = workcounter.incrementAndGet();
String threadname = "Redkale-WorkThread-" + (c > 9 ? c : ("0" + c));
Thread t = new WorkThread(threadname, workref.get(), r);
return t;
});
}
workref.set(workExecutor0);
}
}
if (transportPool == null) {
final int capacity = bufferCapacity;
transportPool = ObjectPool.createSafePool(createBufferCounter, cycleBufferCounter, bufferPoolSize,
(Object... params) -> ByteBuffer.allocateDirect(capacity), null, (e) -> {
if (e == null || e.isReadOnly() || e.capacity() != capacity) return false;
e.clear();
return true;
});
}
this.workExecutor = workExecutor0;
this.resourceFactory.register(RESNAME_APP_EXECUTOR, Executor.class, this.workExecutor);
this.resourceFactory.register(RESNAME_APP_EXECUTOR, ExecutorService.class, this.workExecutor);
this.asyncGroup = new AsyncIOGroup(this.workExecutor, Runtime.getRuntime().availableProcessors(), bufferCapacity, bufferPoolSize);
this.resourceFactory.register(RESNAME_APP_GROUP, AsyncGroup.class, this.asyncGroup);
this.excludelibs = excludelib0;
this.sncpTransportFactory = TransportFactory.create(transportExec, transportPool, transportGroup, (SSLContext) null, readTimeoutSeconds, writeTimeoutSeconds, strategy);
this.sncpTransportFactory = TransportFactory.create(this.asyncGroup, (SSLContext) null, Transport.DEFAULT_NETPROTOCOL, readTimeoutSeconds, writeTimeoutSeconds, strategy);
DefaultAnyValue tarnsportConf = DefaultAnyValue.create(TransportFactory.NAME_POOLMAXCONNS, System.getProperty("net.transport.pool.maxconns", "100"))
.addValue(TransportFactory.NAME_PINGINTERVAL, System.getProperty("net.transport.ping.interval", "30"))
.addValue(TransportFactory.NAME_CHECKINTERVAL, System.getProperty("net.transport.check.interval", "30"));
@@ -470,6 +476,14 @@ public final class Application {
return name;
}
public ExecutorService getWorkExecutor() {
return workExecutor;
}
public AsyncGroup getAsyncGroup() {
return asyncGroup;
}
public ResourceFactory getResourceFactory() {
return resourceFactory;
}
@@ -677,7 +691,23 @@ public final class Application {
}
}, Application.class, ResourceFactory.class, TransportFactory.class, NodeSncpServer.class, NodeHttpServer.class, NodeWatchServer.class);
//------------------------------------- 注册 HttpClient --------------------------------------------------------
resourceFactory.register((ResourceFactory rf, final Object src, String resourceName, Field field, final Object attachment) -> {
try {
if (field.getAnnotation(Resource.class) == null) return;
HttpClient httpClient = HttpClient.create(asyncGroup);
field.set(src, httpClient);
rf.inject(httpClient, null); // 给其可能包含@Resource的字段赋值;
rf.register(resourceName, HttpClient.class, httpClient);
} catch (Exception e) {
logger.log(Level.SEVERE, "[" + Thread.currentThread().getName() + "] HttpClient inject error", e);
}
}, HttpClient.class);
//--------------------------------------------------------------------------
if (this.asyncGroup != null) {
((AsyncIOGroup) this.asyncGroup).start();
}
if (this.clusterAgent != null) {
if (logger.isLoggable(Level.FINER)) logger.log(Level.FINER, "ClusterAgent initing");
long s = System.currentTimeMillis();
@@ -759,7 +789,7 @@ public final class Application {
//------------------------------------------------------------------------
for (AnyValue conf : resources.getAnyValues("group")) {
final String group = conf.getValue("name", "");
final String protocol = conf.getValue("protocol", Transport.DEFAULT_PROTOCOL).toUpperCase();
final String protocol = conf.getValue("protocol", Transport.DEFAULT_NETPROTOCOL).toUpperCase();
if (!"TCP".equalsIgnoreCase(protocol) && !"UDP".equalsIgnoreCase(protocol)) {
throw new RuntimeException("Not supported Transport Protocol " + conf.getValue("protocol"));
}
@@ -1004,8 +1034,7 @@ public final class Application {
for (final AnyValue serconf : serconfs) {
Thread thread = new Thread() {
{
String host = serconf.getValue("host", "0.0.0.0").replace("0.0.0.0", "*");
setName("Redkale-" + serconf.getValue("protocol", "Server").toUpperCase() + "-" + host + ":" + serconf.getIntValue("port") + "-Thread");
setName("Redkale-" + serconf.getValue("protocol", "Server").toUpperCase().replaceFirst("\\..+", "") + ":" + serconf.getIntValue("port") + "-Thread");
this.setDaemon(true);
}

View File

@@ -46,7 +46,7 @@ public class NodeHttpServer extends NodeServer {
}
private static Server createServer(Application application, AnyValue serconf) {
return new HttpServer(application.getStartTime(), application.getResourceFactory().createChild());
return new HttpServer(application, application.getStartTime(), application.getResourceFactory().createChild());
}
public HttpServer getHttpServer() {

View File

@@ -19,7 +19,6 @@ import java.util.concurrent.*;
import java.util.function.*;
import java.util.logging.*;
import javax.annotation.*;
import javax.persistence.Transient;
import static org.redkale.boot.Application.*;
import org.redkale.boot.ClassFilter.FilterEntry;
import org.redkale.net.Filter;
@@ -154,9 +153,10 @@ public abstract class NodeServer {
//必须要进行初始化, 构建Service时需要使用Context中的ExecutorService
server.init(this.serverConf);
//init之后才有Executor
resourceFactory.register(Server.RESNAME_SERVER_EXECUTOR, Executor.class, server.getWorkExecutor());
resourceFactory.register(Server.RESNAME_SERVER_EXECUTOR, ExecutorService.class, server.getWorkExecutor());
resourceFactory.register(Server.RESNAME_SERVER_EXECUTOR, ThreadPoolExecutor.class, server.getWorkExecutor());
//废弃 @since 2.3.0
// resourceFactory.register(Server.RESNAME_SERVER_EXECUTOR, Executor.class, server.getWorkExecutor());
// resourceFactory.register(Server.RESNAME_SERVER_EXECUTOR, ExecutorService.class, server.getWorkExecutor());
// resourceFactory.register(Server.RESNAME_SERVER_EXECUTOR, ThreadPoolExecutor.class, server.getWorkExecutor());
initResource(); //给 DataSource、CacheSource 注册依赖注入时的监听回调事件。
String interceptorClass = this.serverConf.getValue("interceptor", "");
@@ -307,7 +307,6 @@ public abstract class NodeServer {
SimpleEntry<Class, AnyValue> resEntry = dataResources.get(resourceName);
AnyValue sourceConf = resEntry == null ? null : resEntry.getValue();
DataSource source = null;
boolean needinit = true;
if (sourceConf != null) {
final Class sourceType = resEntry.getKey();
if (sourceType == DataJdbcSource.class) {
@@ -331,15 +330,15 @@ public abstract class NodeServer {
}
if (source == null) {
source = DataSources.createDataSource(resourceName); //从persistence.xml配置中创建
needinit = false;
}
application.dataSources.add(source);
appResFactory.register(resourceName, DataSource.class, source);
field.set(src, source);
rf.inject(source, self); // 给其可能包含@Resource的字段赋值;
rf.inject(source, self); // 给AsyncGroup和其他@Resource的字段赋值;
//NodeServer.this.watchFactory.inject(src);
if (source instanceof Service && needinit) ((Service) source).init(sourceConf);
if (source instanceof Service) ((Service) source).init(sourceConf);
} catch (Exception e) {
logger.log(Level.SEVERE, "[" + Thread.currentThread().getName() + "] DataSource inject to " + src + " error", e);
}
@@ -383,16 +382,11 @@ public abstract class NodeServer {
if (CacheSource.class.isAssignableFrom(sourceType)) { // CacheSource
source = Modifier.isFinal(sourceType.getModifiers()) ? sourceType.getConstructor().newInstance() : (CacheSource) Sncp.createLocalService(serverClassLoader, resourceName, sourceType, client == null ? null : client.getMessageAgent(), appResFactory, appSncpTranFactory, sncpAddr, null, Sncp.getConf(srcService));
Type genericType = field.getGenericType();
ParameterizedType pt = (genericType instanceof ParameterizedType) ? (ParameterizedType) genericType : null;
Type valType = pt == null ? null : pt.getActualTypeArguments()[0];
if (CacheSource.class.isAssignableFrom(sourceType)) {
CacheSource cacheSource = (CacheSource) source;
cacheSource.initValueType(valType instanceof Class ? (Class) valType : Object.class);
cacheSource.initTransient(field.getAnnotation(Transient.class) != null); //必须在initValueType之后
}
application.cacheSources.add((CacheSource) source);
appResFactory.register(resourceName, genericType, source);
appResFactory.register(resourceName, CacheSource.class, source);
if (genericType != CacheSource.class) {
appResFactory.register(resourceName, genericType, source);
}
}
field.set(src, source);
rf.inject(source, self); //
@@ -778,7 +772,7 @@ public abstract class NodeServer {
public void start() throws IOException {
if (interceptor != null) interceptor.preStart(this);
server.start();
server.start(application);
postStartServer(localServices, remoteServices);
}

View File

@@ -46,7 +46,7 @@ public class NodeSncpServer extends NodeServer {
}
private static Server createServer(Application application, AnyValue serconf) {
return new SncpServer(application.getStartTime(), application.getResourceFactory().createChild());
return new SncpServer(application, application.getStartTime(), serconf, application.getResourceFactory().createChild());
}
@Override

View File

@@ -61,7 +61,7 @@ public class ServerWatchService extends AbstractWatchService {
final Server server = node.getServer();
InetSocketAddress newAddr = new InetSocketAddress(newhost == null || newhost.isEmpty() ? server.getSocketAddress().getHostString() : newhost, newport);
try {
server.changeAddress(newAddr);
server.changeAddress(application, newAddr);
} catch (IOException e) {
e.printStackTrace();
return new RetResult(RET_SERVER_CHANGEPORT_ERROR, "changeaddress error");
@@ -72,7 +72,7 @@ public class ServerWatchService extends AbstractWatchService {
private Map<String, Object> formatToMap(NodeServer node) {
Server server = node.getServer();
Map<String, Object> rs = new LinkedHashMap<>();
String protocol = server.getProtocol();
String protocol = server.getNetprotocol();
if (node instanceof NodeSncpServer) {
protocol += "/SNCP";
} else if (node instanceof NodeWatchServer) {
@@ -86,7 +86,6 @@ public class ServerWatchService extends AbstractWatchService {
rs.put("name", server.getName());
rs.put("protocol", protocol);
rs.put("address", server.getSocketAddress());
rs.put("threads", server.getThreads());
rs.put("backlog", server.getBacklog());
rs.put("bufferCapacity", server.getBufferCapacity());
rs.put("bufferPoolSize", server.getBufferPoolSize());

View File

@@ -95,7 +95,7 @@ public class CacheClusterAgent extends ClusterAgent implements Resourcable {
public void start() {
if (this.scheduler == null) {
this.scheduler = new ScheduledThreadPoolExecutor(4, (Runnable r) -> {
final Thread t = new Thread(r, CacheClusterAgent.class.getSimpleName() + "-Task-Thread");
final Thread t = new Thread(r, "Redkale-" + CacheClusterAgent.class.getSimpleName() + "-Task-Thread");
t.setDaemon(true);
return t;
});

View File

@@ -140,6 +140,8 @@ public abstract class ClusterAgent {
protected boolean canRegister(String protocol, Service service) {
if ("SNCP".equalsIgnoreCase(protocol) && service.getClass().getAnnotation(Local.class) != null) return false;
AutoLoad al = service.getClass().getAnnotation(AutoLoad.class);
if (al != null && !al.value() && service.getClass().getAnnotation(Local.class) != null) return false;
if (service instanceof WebSocketNode) {
if (((WebSocketNode) service).getLocalWebSocketEngine() == null) return false;
}
@@ -329,7 +331,7 @@ public abstract class ClusterAgent {
this.address = addr;
this.serviceref = new WeakReference(service);
Server server = ns.getServer();
this.netprotocol = server instanceof SncpServer ? ((SncpServer) server).getNetprotocol() : Transport.DEFAULT_PROTOCOL;
this.netprotocol = server instanceof SncpServer ? ((SncpServer) server).getNetprotocol() : Transport.DEFAULT_NETPROTOCOL;
}
@Override

View File

@@ -29,6 +29,8 @@ public class ArrayEncoder<T> implements Encodeable<Writer, T[]> {
protected final Encodeable<Writer, Object> componentEncoder;
protected final boolean subtypefinal;
protected volatile boolean inited = false;
protected final Object lock = new Object();
@@ -47,6 +49,7 @@ public class ArrayEncoder<T> implements Encodeable<Writer, T[]> {
factory.register(type, this);
this.componentEncoder = factory.loadEncoder(this.componentType);
this.anyEncoder = factory.getAnyEncoder();
this.subtypefinal = (this.componentType instanceof Class) && Modifier.isFinal(((Class) this.componentType).getModifiers());
} finally {
inited = true;
synchronized (lock) {
@@ -66,7 +69,7 @@ public class ArrayEncoder<T> implements Encodeable<Writer, T[]> {
return;
}
if (value.length == 0) {
out.writeArrayB(0, this, componentEncoder, value);
out.writeArrayB(0, this, componentEncoder, value);
out.writeArrayE();
return;
}
@@ -81,13 +84,25 @@ public class ArrayEncoder<T> implements Encodeable<Writer, T[]> {
}
}
}
if (out.writeArrayB(value.length, this, componentEncoder, value) < 0) {
final Type comp = this.componentType;
boolean first = true;
for (Object v : value) {
if (!first) out.writeArrayMark();
writeMemberValue(out, member, ((v != null && (v.getClass() == comp || out.specify() == comp)) ? componentEncoder : anyEncoder), v, first);
if (first) first = false;
Encodeable<Writer, Object> itemEncoder = this.componentEncoder;
if (subtypefinal) {
if (out.writeArrayB(value.length, this, itemEncoder, value) < 0) {
boolean first = true;
for (Object v : value) {
if (!first) out.writeArrayMark();
writeMemberValue(out, member, itemEncoder, v, first);
if (first) first = false;
}
}
} else {
if (out.writeArrayB(value.length, this, itemEncoder, value) < 0) {
final Type comp = this.componentType;
boolean first = true;
for (Object v : value) {
if (!first) out.writeArrayMark();
writeMemberValue(out, member, ((v != null && (v.getClass() == comp || out.specify() == comp)) ? itemEncoder : anyEncoder), v, first);
if (first) first = false;
}
}
}
out.writeArrayE();

View File

@@ -32,5 +32,4 @@ public abstract class BinaryConvert<R extends Reader, W extends Writer> extends
public abstract byte[] convertTo(final Type type, final Object value);
public abstract byte[] convertMapTo(final Object... values);
}

View File

@@ -8,7 +8,7 @@ package org.redkale.convert;
import java.lang.reflect.Type;
import java.nio.ByteBuffer;
import java.util.function.*;
import org.redkale.util.Attribute;
import org.redkale.util.*;
/**
* 序列化/反序列化操作类
@@ -61,12 +61,16 @@ public abstract class Convert<R extends Reader, W extends Writer> {
public abstract byte[] convertToBytes(final Type type, final Object value);
public abstract void convertToBytes(final Object value, final ConvertBytesHandler handler);
public abstract void convertToBytes(final Type type, final Object value, final ConvertBytesHandler handler);
public abstract void convertToBytes(final ByteArray array, final Object value);
public abstract void convertToBytes(final ByteArray array, final Type type, final Object value);
public abstract ByteBuffer[] convertTo(final Supplier<ByteBuffer> supplier, final Object value);
public abstract ByteBuffer[] convertTo(final Supplier<ByteBuffer> supplier, final Type type, final Object value);
public abstract byte[] convertMapToBytes(final Object... values);
public abstract ByteBuffer[] convertMapTo(final Supplier<ByteBuffer> supplier, final Object... values);
}

View File

@@ -0,0 +1,23 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.convert;
import java.util.function.Consumer;
/**
*
* convertToBytes系列的方法的回调
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*
* @since 2.3.0
*/
public interface ConvertBytesHandler {
<A> void completed(byte[] bs, int offset, int length, Consumer<A> callback, A attachment);
}

View File

@@ -42,7 +42,7 @@ public abstract class ConvertFactory<R extends Reader, W extends Writer> {
protected Convert<R, W> convert;
protected boolean tiny;
protected boolean tiny; //String类型值为""Boolean类型值为false时是否需要输出 默认为true
private final Encodeable<W, ?> anyEncoder = new AnyEncoder(this);
@@ -112,6 +112,7 @@ public abstract class ConvertFactory<R extends Reader, W extends Writer> {
this.register(InetSocketAddress.class, InetAddressSimpledCoder.InetSocketAddressSimpledCoder.instance);
this.register(Pattern.class, PatternSimpledCoder.instance);
this.register(File.class, FileSimpledCoder.instance);
this.register(Throwable.class, ThrowableSimpledCoder.instance);
this.register(CompletionHandler.class, CompletionHandlerSimpledCoder.instance);
this.register(URL.class, URLSimpledCoder.instance);
this.register(URI.class, URISimpledCoder.instance);
@@ -232,11 +233,15 @@ public abstract class ConvertFactory<R extends Reader, W extends Writer> {
return new EnumSimpledCoder(enumClass);
}
protected <E> Encodeable<W, E> createDyncEncoder(Type type) {
return null;
}
protected ObjectDecoder createObjectDecoder(Type type) {
return new ObjectDecoder(type);
}
protected ObjectEncoder createObjectEncoder(Type type) {
protected <E> ObjectEncoder<W, E> createObjectEncoder(Type type) {
return new ObjectEncoder(type);
}
@@ -810,8 +815,11 @@ public abstract class ConvertFactory<R extends Reader, W extends Writer> {
}
}
if (simpleCoder == null) {
oe = createObjectEncoder(type);
encoder = oe;
encoder = createDyncEncoder(type);
if (encoder == null) {
oe = createObjectEncoder(type);
encoder = oe;
}
} else {
encoder = simpleCoder;
}

View File

@@ -31,6 +31,10 @@ public final class EnMember<W extends Writer, T, F> {
//final boolean isnumber;
final boolean bool;
final char[] jsonFieldNameChars;
final byte[] jsonFieldNameBytes;
protected int index;
protected int position; //从1开始
@@ -43,6 +47,8 @@ public final class EnMember<W extends Writer, T, F> {
Class t = attribute.type();
this.string = CharSequence.class.isAssignableFrom(t);
this.bool = t == Boolean.class || t == boolean.class;
this.jsonFieldNameChars = ('"' + attribute.field() + "\":").toCharArray();
this.jsonFieldNameBytes = ('"' + attribute.field() + "\":").getBytes();
//this.isnumber = Number.class.isAssignableFrom(t) || (!this.isbool && t.isPrimitive());
}
@@ -71,6 +77,14 @@ public final class EnMember<W extends Writer, T, F> {
return attribute;
}
public char[] getJsonFieldNameChars() {
return jsonFieldNameChars;
}
public byte[] getJsonFieldNameBytes() {
return jsonFieldNameBytes;
}
public Encodeable<W, F> getEncoder() {
return encoder;
}

View File

@@ -28,4 +28,9 @@ public interface Encodeable<W extends Writer, T> {
*/
public Type getType();
//是否需要检查Writer.specify
default boolean specifyable() {
return true;
}
}

View File

@@ -103,7 +103,8 @@ public class ObjectEncoder<W extends Writer, T> implements Encodeable<W, T> {
if (factory.isConvertDisabled(method)) continue;
if (method.getParameterTypes().length != 0) continue;
if (method.getReturnType() == void.class) continue;
if (reversible && (cps == null || !contains(cps, ConvertFactory.readGetSetFieldName(method)))) {
String convertname = ConvertFactory.readGetSetFieldName(method);
if (reversible && (cps == null || !contains(cps, convertname))) {
boolean is = method.getName().startsWith("is");
try {
clazz.getMethod(method.getName().replaceFirst(is ? "is" : "get", "set"), method.getReturnType());
@@ -114,6 +115,13 @@ public class ObjectEncoder<W extends Writer, T> implements Encodeable<W, T> {
ref = factory.findRef(clazz, method);
if (ref != null && ref.ignore()) continue;
ConvertSmallString small = method.getAnnotation(ConvertSmallString.class);
if (small == null) {
try {
Field f = clazz.getDeclaredField(convertname);
if (f != null) small = f.getAnnotation(ConvertSmallString.class);
} catch (Exception e) {
}
}
Encodeable<W, ?> fieldCoder;
if (small != null && method.getReturnType() == String.class) {
fieldCoder = StringSimpledCoder.SmallStringSimpledCoder.instance;

View File

@@ -32,5 +32,4 @@ public abstract class TextConvert<R extends Reader, W extends Writer> extends Co
public abstract String convertTo(final Type type, final Object value);
public abstract String convertMapTo(final Object... values);
}

View File

@@ -133,7 +133,7 @@ public abstract class Writer {
}
}
Attribute attr = member.getAttribute();
this.writeFieldName(attr.field(), attr.genericType(), member.getPosition());
this.writeFieldName(member, attr.field(), attr.genericType(), member.getPosition());
member.encoder.convertTo(this, value);
this.comma = true;
}
@@ -160,7 +160,7 @@ public abstract class Writer {
if (!((Boolean) value)) return;
}
}
this.writeFieldName(fieldName, fieldType, fieldPos);
this.writeFieldName(null, fieldName, fieldType, fieldPos);
anyEncoder.convertTo(this, value);
this.comma = true;
}
@@ -172,7 +172,7 @@ public abstract class Writer {
*/
public final void writeFieldName(final EnMember member) {
Attribute attr = member.getAttribute();
this.writeFieldName(attr.field(), attr.genericType(), member.getPosition());
this.writeFieldName(member, attr.field(), attr.genericType(), member.getPosition());
}
/**
@@ -233,11 +233,12 @@ public abstract class Writer {
/**
* 输出一个字段名
*
* @param member EnMember
* @param fieldName 字段名称
* @param fieldType 字段类型
* @param fieldPos 字段顺序
*/
public abstract void writeFieldName(String fieldName, Type fieldType, int fieldPos);
public abstract void writeFieldName(EnMember member, String fieldName, Type fieldType, int fieldPos);
/**
* 写入一个boolean值

View File

@@ -6,10 +6,10 @@
package org.redkale.convert.bson;
import java.nio.*;
import java.nio.charset.StandardCharsets;
import org.redkale.convert.*;
import static org.redkale.convert.Reader.SIGN_NULL;
import org.redkale.convert.ext.ByteSimpledCoder;
import org.redkale.util.*;
/**
* 以ByteBuffer为数据载体的BsonReader
@@ -233,6 +233,6 @@ public class BsonByteBufferReader extends BsonReader {
int len = readInt();
if (len == SIGN_NULL) return null;
if (len == 0) return "";
return new String(Utility.decodeUTF8(read(len)));
return new String(read(len), StandardCharsets.UTF_8);
}
}

View File

@@ -137,4 +137,19 @@ public class BsonByteBufferWriter extends BsonWriter {
this.buffers = null;
return false;
}
@Override
public byte[] content() {
throw new UnsupportedOperationException("Not supported yet."); //无需实现
}
@Override
public int offset() {
throw new UnsupportedOperationException("Not supported yet.");//无需实现
}
@Override
public int length() {
throw new UnsupportedOperationException("Not supported yet."); //无需实现
}
}

View File

@@ -39,9 +39,9 @@ import org.redkale.util.*;
*/
public class BsonConvert extends BinaryConvert<BsonReader, BsonWriter> {
private static final ObjectPool<BsonReader> readerPool = BsonReader.createPool(Integer.getInteger("convert.bson.pool.size", Integer.getInteger("convert.pool.size", 16)));
private final ThreadLocal<BsonWriter> writerPool = ThreadLocal.withInitial(BsonWriter::new);
private static final ObjectPool<BsonWriter> writerPool = BsonWriter.createPool(Integer.getInteger("convert.bson.pool.size", Integer.getInteger("convert.pool.size", 16)));
private final Consumer<BsonWriter> offerConsumer = w -> offerBsonWriter(w);
private final boolean tiny;
@@ -84,11 +84,11 @@ public class BsonConvert extends BinaryConvert<BsonReader, BsonWriter> {
}
public BsonReader pollBsonReader() {
return readerPool.get();
return new BsonReader();
}
public void offerBsonReader(final BsonReader in) {
if (in != null) readerPool.accept(in);
//无需回收
}
//------------------------------ writer -----------------------------------------------------------
@@ -96,16 +96,25 @@ public class BsonConvert extends BinaryConvert<BsonReader, BsonWriter> {
return configWrite(new BsonByteBufferWriter(tiny, supplier));
}
public BsonWriter pollBsonWriter(final OutputStream out) {
protected BsonWriter pollBsonWriter(final OutputStream out) {
return configWrite(new BsonStreamWriter(tiny, out));
}
public BsonWriter pollBsonWriter() {
return writerPool.get().tiny(tiny);
BsonWriter writer = writerPool.get();
if (writer == null) {
writer = new BsonWriter();
} else {
writerPool.set(null);
}
return configWrite(writer.tiny(tiny));
}
public void offerBsonWriter(final BsonWriter out) {
if (out != null) writerPool.accept(out);
if (out != null) {
out.recycle();
writerPool.set(out);
}
}
//------------------------------ convertFrom -----------------------------------------------------------
@@ -119,11 +128,9 @@ public class BsonConvert extends BinaryConvert<BsonReader, BsonWriter> {
@SuppressWarnings("unchecked")
public <T> T convertFrom(final Type type, final byte[] bytes, final int offset, final int len) {
if (type == null) return null;
final BsonReader in = readerPool.get();
in.setBytes(bytes, offset, len);
final BsonReader in = new BsonReader(bytes, offset, len);
@SuppressWarnings("unchecked")
T rs = (T) factory.loadDecoder(type).convertFrom(in);
readerPool.accept(in);
return rs;
}
@@ -159,10 +166,10 @@ public class BsonConvert extends BinaryConvert<BsonReader, BsonWriter> {
@Override
public byte[] convertTo(final Object value) {
if (value == null) {
final BsonWriter out = writerPool.get().tiny(tiny);
final BsonWriter out = pollBsonWriter();
out.writeNull();
byte[] result = out.toArray();
writerPool.accept(out);
offerBsonWriter(out);
return result;
}
return convertTo(value.getClass(), value);
@@ -171,10 +178,10 @@ public class BsonConvert extends BinaryConvert<BsonReader, BsonWriter> {
@Override
public byte[] convertTo(final Type type, final Object value) {
if (type == null) return null;
final BsonWriter out = writerPool.get().tiny(tiny);
factory.loadEncoder(type).convertTo(out, value);
byte[] result = out.toArray();
writerPool.accept(out);
final BsonWriter writer = pollBsonWriter();
factory.loadEncoder(type).convertTo(writer, value);
byte[] result = writer.toArray();
offerBsonWriter(writer);
return result;
}
@@ -189,13 +196,35 @@ public class BsonConvert extends BinaryConvert<BsonReader, BsonWriter> {
}
@Override
public byte[] convertMapTo(final Object... values) {
if (values == null) return null;
final BsonWriter out = writerPool.get().tiny(tiny);
((AnyEncoder) factory.getAnyEncoder()).convertMapTo(out, values);
byte[] result = out.toArray();
writerPool.accept(out);
return result;
public void convertToBytes(final Object value, final ConvertBytesHandler handler) {
convertToBytes(value == null ? null : value.getClass(), value, handler);
}
@Override
public void convertToBytes(final Type type, final Object value, final ConvertBytesHandler handler) {
final BsonWriter writer = pollBsonWriter();
if (type == null) {
writer.writeNull();
} else {
factory.loadEncoder(type).convertTo(writer, value);
}
writer.completed(handler, offerConsumer);
}
@Override
public void convertToBytes(final ByteArray array, final Object value) {
convertToBytes(array, value == null ? null : value.getClass(), value);
}
@Override
public void convertToBytes(final ByteArray array, final Type type, final Object value) {
final BsonWriter writer = configWrite(new BsonWriter(array).tiny(tiny));
if (type == null) {
writer.writeNull();
} else {
factory.loadEncoder(type).convertTo(writer, value);
}
writer.directTo(array);
}
public void convertTo(final OutputStream out, final Object value) {
@@ -215,14 +244,6 @@ public class BsonConvert extends BinaryConvert<BsonReader, BsonWriter> {
}
}
public void convertMapTo(final OutputStream out, final Object... values) {
if (values == null) {
pollBsonWriter(out).writeNull();
} else {
((AnyEncoder) factory.getAnyEncoder()).convertMapTo(pollBsonWriter(out), values);
}
}
@Override
public ByteBuffer[] convertTo(final Supplier<ByteBuffer> supplier, final Object value) {
if (supplier == null) return null;
@@ -238,36 +259,13 @@ public 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 = pollBsonWriter(supplier);
BsonByteBufferWriter writer = pollBsonWriter(supplier);
if (value == null) {
out.writeNull();
writer.writeNull();
} else {
factory.loadEncoder(type).convertTo(out, value);
factory.loadEncoder(type).convertTo(writer, value);
}
return out.toBuffers();
}
@Override
public byte[] convertMapToBytes(final Object... values) {
BsonWriter out = pollBsonWriter();
if (values == null) {
out.writeNull();
} else {
((AnyEncoder) factory.getAnyEncoder()).convertMapTo(out, values);
}
return out.toArray();
}
@Override
public ByteBuffer[] convertMapTo(final Supplier<ByteBuffer> supplier, final Object... values) {
if (supplier == null) return null;
BsonByteBufferWriter out = pollBsonWriter(supplier);
if (values == null) {
out.writeNull();
} else {
((AnyEncoder) factory.getAnyEncoder()).convertMapTo(out, values);
}
return out.toBuffers();
return writer.toBuffers();
}
public void convertTo(final BsonWriter writer, final Object value) {
@@ -283,14 +281,6 @@ public class BsonConvert extends BinaryConvert<BsonReader, BsonWriter> {
factory.loadEncoder(type).convertTo(writer, value);
}
public void convertMapTo(final BsonWriter writer, final Object... values) {
if (values == null) {
writer.writeNull();
} else {
((AnyEncoder) factory.getAnyEncoder()).convertMapTo(writer, values);
}
}
public BsonWriter convertToWriter(final Object value) {
if (value == null) return null;
return convertToWriter(value.getClass(), value);
@@ -298,14 +288,8 @@ public class BsonConvert extends BinaryConvert<BsonReader, BsonWriter> {
public BsonWriter convertToWriter(final Type type, final Object value) {
if (type == null) return null;
final BsonWriter out = writerPool.get().tiny(tiny);
factory.loadEncoder(type).convertTo(out, value);
return out;
}
public BsonWriter convertMapToWriter(final Object... values) {
final BsonWriter out = writerPool.get().tiny(tiny);
((AnyEncoder) factory.getAnyEncoder()).convertMapTo(out, values);
return out;
final BsonWriter writer = writerPool.get().tiny(tiny);
factory.loadEncoder(type).convertTo(writer, value);
return writer;
}
}

View File

@@ -5,6 +5,7 @@
*/
package org.redkale.convert.bson;
import java.nio.charset.StandardCharsets;
import org.redkale.convert.*;
import static org.redkale.convert.Reader.SIGN_NULL;
import org.redkale.convert.ext.*;
@@ -341,14 +342,14 @@ public class BsonReader extends Reader {
int len = readInt();
if (len == SIGN_NULL) return null;
if (len == 0) return "";
String value = new String(Utility.decodeUTF8(content, ++this.position, len));
String value = new String(content, ++this.position, len, StandardCharsets.UTF_8);
this.position += len - 1;//上一行已经++this.position所以此处要-1
return value;
}
@Override
public ValueType readType() {
throw new UnsupportedOperationException("Not supported yet.");
throw new UnsupportedOperationException("Not supported yet.");
}
}

View File

@@ -7,6 +7,7 @@ package org.redkale.convert.bson;
import java.lang.reflect.Type;
import java.nio.ByteBuffer;
import java.util.function.Consumer;
import org.redkale.convert.*;
import org.redkale.convert.ext.ByteSimpledCoder;
import org.redkale.util.*;
@@ -18,7 +19,7 @@ import org.redkale.util.*;
*
* @author zhangjx
*/
public class BsonWriter extends Writer {
public class BsonWriter extends Writer implements ByteTuple {
private static final int defaultSize = Integer.getInteger("convert.bson.writer.buffer.defsize", Integer.getInteger("convert.writer.buffer.defsize", 1024));
@@ -32,6 +33,47 @@ public class BsonWriter extends Writer {
return ObjectPool.createSafePool(max, (Object... params) -> new BsonWriter(), null, (t) -> t.recycle());
}
@Override
public byte[] content() {
return content;
}
@Override
public int offset() {
return 0;
}
@Override
public int length() {
return count;
}
/**
* 直接获取全部数据, 实际数据需要根据count长度来截取
*
* @return byte[]
*/
public byte[] directBytes() {
return content;
}
/**
* 将本对象的内容引用复制给array
*
* @param array ByteArray
*/
public void directTo(ByteArray array) {
array.directFrom(content, count);
}
public void completed(ConvertBytesHandler handler, Consumer<BsonWriter> callback) {
handler.completed(content, 0, count, callback, this);
}
public ByteArray toByteArray() {
return new ByteArray(this);
}
public byte[] toArray() {
if (count == content.length) return content;
byte[] newdata = new byte[count];
@@ -55,6 +97,11 @@ public class BsonWriter extends Writer {
this.content = new byte[size > 128 ? size : 128];
}
public BsonWriter(ByteArray array) {
this.content = array.content();
this.count = array.length();
}
@Override
public final boolean tiny() {
return tiny;
@@ -201,7 +248,7 @@ public class BsonWriter extends Writer {
}
@Override
public final void writeFieldName(String fieldName, Type fieldType, int fieldPos) {
public final void writeFieldName(EnMember member, String fieldName, Type fieldType, int fieldPos) {
writeByte(BsonReader.SIGN_HASNEXT);
writeSmallString(fieldName);
writeByte(BsonFactory.typeEnum(fieldType));

View File

@@ -0,0 +1,40 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.convert.ext;
import org.redkale.convert.*;
/**
* 文件 的SimpledCoder实现
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
* @param <R> Reader输入的子类型
* @param <W> Writer输出的子类型
*/
public class ThrowableSimpledCoder<R extends Reader, W extends Writer> extends SimpledCoder<R, W, Throwable> {
public static final ThrowableSimpledCoder instance = new ThrowableSimpledCoder();
@Override
public void convertTo(W out, Throwable value) {
if (value == null) {
out.writeNull();
} else {
out.writeString(value.toString());
}
}
@Override
public Throwable convertFrom(R in) {
String value = in.readString();
if (value == null) return null;
return new Exception(value);
}
}

View File

@@ -22,6 +22,10 @@ import org.redkale.util.*;
*/
public class JsonByteBufferWriter extends JsonWriter {
private static final char[] CHARS_TUREVALUE = "true".toCharArray();
private static final char[] CHARS_FALSEVALUE = "false".toCharArray();
protected Charset charset;
private final Supplier<ByteBuffer> supplier;
@@ -36,7 +40,7 @@ public class JsonByteBufferWriter extends JsonWriter {
protected JsonByteBufferWriter(boolean tiny, Charset charset, Supplier<ByteBuffer> supplier) {
this.tiny = tiny;
this.charset = StandardCharsets.UTF_8.equals(charset) ? null : charset;
this.charset = charset;
this.supplier = supplier;
}
@@ -56,7 +60,6 @@ public class JsonByteBufferWriter extends JsonWriter {
return false;
}
@Override
public ByteBuffer[] toBuffers() {
if (buffers == null) return new ByteBuffer[0];
for (int i = index; i < this.buffers.length; i++) {
@@ -66,7 +69,6 @@ public class JsonByteBufferWriter extends JsonWriter {
return this.buffers;
}
@Override
public int count() {
if (this.buffers == null) return 0;
int len = 0;
@@ -111,6 +113,32 @@ public class JsonByteBufferWriter extends JsonWriter {
writeTo(-1, false, chs, start, len);
}
@Override
public void writeTo(final byte ch) { //只能是 0 - 127 的字符
expand(1);
this.buffers[index].put(ch);
}
@Override
public void writeTo(final byte[] chs, final int start, final int len) { //只能是 0 - 127 的字符
int expandsize = expand(len);
if (expandsize == 0) { // 只需要一个buffer
this.buffers[index].put(chs, start, len);
} else {
ByteBuffer buffer = this.buffers[index];
int remain = len;
int offset = start;
while (remain > 0) {
int bsize = Math.min(buffer.remaining(), remain);
buffer.put(chs, offset, bsize);
offset += bsize;
remain -= bsize;
if (remain < 1) break;
buffer = nextByteBuffer();
}
}
}
private void writeTo(int expandsize, final boolean quote, final char[] chs, final int start, final int len) {
int byteLength = quote ? 2 : 0;
ByteBuffer bb = null;
@@ -266,6 +294,11 @@ public class JsonByteBufferWriter extends JsonWriter {
}
}
@Override
public void writeBoolean(boolean value) {
writeTo(value ? CHARS_TUREVALUE : CHARS_FALSEVALUE);
}
@Override
public void writeInt(int value) {
writeLatin1To(false, String.valueOf(value));

View File

@@ -0,0 +1,389 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.convert.json;
import java.lang.reflect.Type;
import java.nio.charset.StandardCharsets;
import java.util.function.Consumer;
import org.redkale.convert.*;
import static org.redkale.convert.json.JsonWriter.*;
import org.redkale.util.*;
/**
*
* writeTo系列的方法输出的字符不能含特殊字符
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*
* @since 2.3.0
*/
public class JsonBytesWriter extends JsonWriter implements ByteTuple {
private static final boolean greatejdk8 = Utility.greaterJDK8();
private static final byte[] BYTES_TUREVALUE = "true".getBytes();
private static final byte[] BYTES_FALSEVALUE = "false".getBytes();
private int count;
private byte[] content;
public JsonBytesWriter() {
this(defaultSize);
}
public JsonBytesWriter(int size) {
this.content = new byte[size > 1024 ? size : 1024];
}
public JsonBytesWriter(ByteArray array) {
this.content = array.content();
this.count = array.length();
}
public JsonBytesWriter(boolean tiny, ByteArray array) {
this.tiny = tiny;
this.content = array.content();
this.count = array.length();
}
protected byte[] expand(int len) {
int newcount = count + len;
if (newcount <= content.length) return content;
byte[] newdata = new byte[Math.max(content.length * 3 / 2, newcount)];
System.arraycopy(content, 0, newdata, 0, count);
this.content = newdata;
return newdata;
}
@Override
public byte[] content() {
return content;
}
@Override
public int offset() {
return 0;
}
@Override
public int length() {
return count;
}
/**
* 将本对象的内容引用复制给array
*
* @param array ByteArray
*/
public void directTo(ByteArray array) {
array.directFrom(content, count);
}
@Override
public final void writeFieldName(EnMember member, String fieldName, Type fieldType, int fieldPos) {
if (this.comma) writeTo(',');
if (member != null) {
byte[] bs = member.getJsonFieldNameBytes();
expand(bs.length);
System.arraycopy(bs, 0, content, count, bs.length);
count += bs.length;
} else {
writeLatin1To(true, fieldName);
writeTo(':');
}
}
@Override
public void writeTo(final char ch) { //只能是 0 - 127 的字符
expand(1);
content[count++] = (byte) ch;
}
@Override
public void writeTo(final char[] chs, final int start, final int len) { //只能是 0 - 127 的字符
expand(len);
for (int i = 0; i < len; i++) {
content[count + i] = (byte) chs[start + i];
}
count += len;
}
@Override
public void writeTo(final byte ch) { //只能是 0 - 127 的字符
expand(1);
content[count++] = ch;
}
@Override
public void writeTo(final byte[] chs, final int start, final int len) { //只能是 0 - 127 的字符
expand(len);
System.arraycopy(chs, start, content, count, len);
count += len;
}
/**
* <b>注意:</b> 该String值不能为null且不会进行转义 只用于不含需要转义字符的字符串例如enum、double、BigInteger转换的String
*
* @param quote 是否加双引号
* @param value 非null且不含需要转义的字符的String值
*/
@Override
public void writeLatin1To(final boolean quote, final String value) {
byte[] bs = Utility.byteArray(value);
int len = bs.length;
expand(len + (quote ? 2 : 0));
if (quote) content[count++] = '"';
System.arraycopy(bs, 0, content, count, bs.length);
count += len;
if (quote) content[count++] = '"';
}
public JsonBytesWriter clear() {
this.count = 0;
return this;
}
@Override
public boolean recycle() {
super.recycle();
this.count = 0;
this.specify = null;
if (this.content != null && this.content.length > defaultSize * 100) {
this.content = new byte[defaultSize];
}
return true;
}
/**
* 直接获取全部数据, 实际数据需要根据count长度来截取
*
* @return byte[]
*/
public byte[] directBytes() {
return content;
}
public void completed(ConvertBytesHandler handler, Consumer<JsonBytesWriter> callback) {
handler.completed(content, 0, count, callback, this);
}
public byte[] toBytes() {
byte[] copy = new byte[count];
System.arraycopy(content, 0, copy, 0, count);
return copy;
}
public int count() {
return this.count;
}
private void writeEscapeLatinString(byte[] value) {
byte[] bytes = expand(value.length * 2 + 2);
int curr = count;
bytes[curr++] = '"';
for (byte b : value) {
if (b == '"') {
bytes[curr++] = '\\';
bytes[curr++] = '"';
} else if (b == '\\') {
bytes[curr++] = '\\';
bytes[curr++] = '\\';
} else if (b < 32) {
if (b == '\n') {
bytes[curr++] = '\\';
bytes[curr++] = 'n';
} else if (b == '\r') {
bytes[curr++] = '\\';
bytes[curr++] = 'r';
} else if (b == '\t') {
bytes[curr++] = '\\';
bytes[curr++] = 't';
} else {
bytes[curr++] = b;
}
} else {
bytes[curr++] = b;
}
}
bytes[curr++] = '"';
count = curr;
}
@Override
public void writeString(String value) {
if (value == null) {
writeNull();
return;
}
if (greatejdk8 && Utility.isLatin1(value)) {
writeEscapeLatinString(Utility.byteArray(value));
return;
}
byte[] bytes = expand(value.length() * 4 + 2);
int curr = count;
bytes[curr++] = '"';
int len = value.length();
for (int i = 0; i < len; i++) {
char ch = value.charAt(i);
switch (ch) {
case '\n':
bytes[curr++] = '\\';
bytes[curr++] = 'n';
break;
case '\r':
bytes[curr++] = '\\';
bytes[curr++] = 'r';
break;
case '\t':
bytes[curr++] = '\\';
bytes[curr++] = 't';
break;
case '\\':
bytes[curr++] = '\\';
bytes[curr++] = '\\';
break;
case '"':
bytes[curr++] = '\\';
bytes[curr++] = '"';
break;
default:
if (ch < 0x80) {
bytes[curr++] = (byte) ch;
} else if (ch < 0x800) {
bytes[curr++] = (byte) (0xc0 | (ch >> 6));
bytes[curr++] = (byte) (0x80 | (ch & 0x3f));
} else if (Character.isSurrogate(ch)) { //连取两个
int uc = Character.toCodePoint(ch, value.charAt(++i));
bytes[curr++] = (byte) (0xf0 | ((uc >> 18)));
bytes[curr++] = (byte) (0x80 | ((uc >> 12) & 0x3f));
bytes[curr++] = (byte) (0x80 | ((uc >> 6) & 0x3f));
bytes[curr++] = (byte) (0x80 | (uc & 0x3f));
} else {
bytes[curr++] = (byte) (0xe0 | ((ch >> 12)));
bytes[curr++] = (byte) (0x80 | ((ch >> 6) & 0x3f));
bytes[curr++] = (byte) (0x80 | (ch & 0x3f));
}
break;
}
}
bytes[curr++] = '"';
count = curr;
}
@Override
public String toString() {
return new String(content, 0, count, StandardCharsets.UTF_8);
}
//----------------------------------------------------------------------------------------------
@Override
public void writeBoolean(boolean value) {
byte[] bs = value ? BYTES_TUREVALUE : BYTES_FALSEVALUE;
expand(bs.length);
System.arraycopy(bs, 0, content, count, bs.length);
count += bs.length;
}
@Override
public void writeInt(int value) {
final char sign = value >= 0 ? 0 : '-';
if (value < 0) value = -value;
int size;
for (int i = 0;; i++) {
if (value <= sizeTable[i]) {
size = i + 1;
break;
}
}
if (sign != 0) size++; //负数
byte[] bytes = expand(size);
int q, r;
int charPos = count + size;
// Generate two digits per iteration
while (value >= 65536) {
q = value / 100;
// really: r = i - (q * 100);
r = value - ((q << 6) + (q << 5) + (q << 2));
value = q;
bytes[--charPos] = (byte) DigitOnes[r];
bytes[--charPos] = (byte) DigitTens[r];
}
// Fall thru to fast mode for smaller numbers
// assert(i <= 65536, i);
for (;;) {
q = (value * 52429) >>> (16 + 3);
r = value - ((q << 3) + (q << 1)); // r = i-(q*10) ...
bytes[--charPos] = (byte) digits[r];
value = q;
if (value == 0) break;
}
if (sign != 0) bytes[--charPos] = (byte) sign;
count += size;
}
@Override
public void writeLong(long value) {
final char sign = value >= 0 ? 0 : '-';
if (value < 0) value = -value;
int size = 19;
long p = 10;
for (int i = 1; i < 19; i++) {
if (value < p) {
size = i;
break;
}
p = 10 * p;
}
if (sign != 0) size++; //负数
byte[] bytes = expand(size);
long q;
int r;
int charPos = count + size;
// Get 2 digits/iteration using longs until quotient fits into an int
while (value > Integer.MAX_VALUE) {
q = value / 100;
// really: r = i - (q * 100);
r = (int) (value - ((q << 6) + (q << 5) + (q << 2)));
value = q;
content[--charPos] = (byte) DigitOnes[r];
content[--charPos] = (byte) DigitTens[r];
}
// Get 2 digits/iteration using ints
int q2;
int i2 = (int) value;
while (i2 >= 65536) {
q2 = i2 / 100;
// really: r = i2 - (q * 100);
r = i2 - ((q2 << 6) + (q2 << 5) + (q2 << 2));
i2 = q2;
bytes[--charPos] = (byte) DigitOnes[r];
bytes[--charPos] = (byte) DigitTens[r];
}
// Fall thru to fast mode for smaller numbers
// assert(i2 <= 65536, i2);
for (;;) {
q2 = (i2 * 52429) >>> (16 + 3);
r = i2 - ((q2 << 3) + (q2 << 1)); // r = i2-(q2*10) ...
bytes[--charPos] = (byte) digits[r];
i2 = q2;
if (i2 == 0) break;
}
if (sign != 0) bytes[--charPos] = (byte) sign;
count += size;
}
}

View File

@@ -0,0 +1,267 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.convert.json;
import java.nio.ByteBuffer;
import org.redkale.util.*;
/**
*
* writeTo系列的方法输出的字符不能含特殊字符
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*
* @since 2.3.0
*/
public class JsonCharsWriter extends JsonWriter {
private static final char[] CHARS_TUREVALUE = "true".toCharArray();
private static final char[] CHARS_FALSEVALUE = "false".toCharArray();
private int count;
private char[] content;
public JsonCharsWriter() {
this(defaultSize);
}
public JsonCharsWriter(int size) {
this.content = new char[size > 1024 ? size : 1024];
}
//-----------------------------------------------------------------------
/**
* 返回指定至少指定长度的缓冲区
*
* @param len
*
* @return
*/
private char[] expand(int len) {
int newcount = count + len;
if (newcount <= content.length) return content;
char[] newdata = new char[Math.max(content.length * 3 / 2, newcount)];
System.arraycopy(content, 0, newdata, 0, count);
this.content = newdata;
return newdata;
}
@Override
public void writeTo(final char ch) { //只能是 0 - 127 的字符
expand(1);
content[count++] = ch;
}
@Override
public void writeTo(final char[] chs, final int start, final int len) { //只能是 0 - 127 的字符
expand(len);
System.arraycopy(chs, start, content, count, len);
count += len;
}
@Override
public void writeTo(final byte ch) { //只能是 0 - 127 的字符
expand(1);
content[count++] = (char) ch;
}
@Override
public void writeTo(final byte[] chs, final int start, final int len) { //只能是 0 - 127 的字符
expand(len);
for (int i = 0; i < len; i++) {
content[count + i] = (char) chs[start + i];
}
count += len;
}
/**
* <b>注意:</b> 该String值不能为null且不会进行转义 只用于不含需要转义字符的字符串例如enum、double、BigInteger转换的String
*
* @param quote 是否加双引号
* @param value 非null且不含需要转义的字符的String值
*/
@Override
public void writeLatin1To(final boolean quote, final String value) {
int len = value.length();
expand(len + (quote ? 2 : 0));
if (quote) content[count++] = '"';
value.getChars(0, len, content, count);
count += len;
if (quote) content[count++] = '"';
}
@Override
protected boolean recycle() {
super.recycle();
this.count = 0;
this.specify = null;
if (this.content != null && this.content.length > defaultSize) {
this.content = new char[defaultSize];
}
return true;
}
public ByteBuffer[] toBuffers() {
return new ByteBuffer[]{ByteBuffer.wrap(Utility.encodeUTF8(content, 0, count))};
}
public byte[] toBytes() {
return Utility.encodeUTF8(content, 0, count);
}
public int count() {
return this.count;
}
@Override
public void writeString(String value) {
if (value == null) {
writeNull();
return;
}
expand(value.length() * 2 + 2);
content[count++] = '"';
for (char ch : Utility.charArray(value)) {
switch (ch) {
case '\n':
content[count++] = '\\';
content[count++] = 'n';
break;
case '\r':
content[count++] = '\\';
content[count++] = 'r';
break;
case '\t':
content[count++] = '\\';
content[count++] = 't';
break;
case '\\':
content[count++] = '\\';
content[count++] = ch;
break;
case '"':
content[count++] = '\\';
content[count++] = ch;
break;
default:
content[count++] = ch;
break;
}
}
content[count++] = '"';
}
@Override
public String toString() {
return new String(content, 0, count);
}
//----------------------------------------------------------------------------------------------
@Override
public void writeBoolean(boolean value) {
writeTo(value ? CHARS_TUREVALUE : CHARS_FALSEVALUE);
}
@Override
public void writeInt(int value) {
final char sign = value >= 0 ? 0 : '-';
if (value < 0) value = -value;
int size;
for (int i = 0;; i++) {
if (value <= sizeTable[i]) {
size = i + 1;
break;
}
}
if (sign != 0) size++; //负数
expand(size);
int q, r;
int charPos = count + size;
// Generate two digits per iteration
while (value >= 65536) {
q = value / 100;
// really: r = i - (q * 100);
r = value - ((q << 6) + (q << 5) + (q << 2));
value = q;
content[--charPos] = DigitOnes[r];
content[--charPos] = DigitTens[r];
}
// Fall thru to fast mode for smaller numbers
// assert(i <= 65536, i);
for (;;) {
q = (value * 52429) >>> (16 + 3);
r = value - ((q << 3) + (q << 1)); // r = i-(q*10) ...
content[--charPos] = digits[r];
value = q;
if (value == 0) break;
}
if (sign != 0) content[--charPos] = sign;
count += size;
}
@Override
public void writeLong(long value) {
final char sign = value >= 0 ? 0 : '-';
if (value < 0) value = -value;
int size = 19;
long p = 10;
for (int i = 1; i < 19; i++) {
if (value < p) {
size = i;
break;
}
p = 10 * p;
}
if (sign != 0) size++; //负数
expand(size);
long q;
int r;
int charPos = count + size;
// Get 2 digits/iteration using longs until quotient fits into an int
while (value > Integer.MAX_VALUE) {
q = value / 100;
// really: r = i - (q * 100);
r = (int) (value - ((q << 6) + (q << 5) + (q << 2)));
value = q;
content[--charPos] = DigitOnes[r];
content[--charPos] = DigitTens[r];
}
// Get 2 digits/iteration using ints
int q2;
int i2 = (int) value;
while (i2 >= 65536) {
q2 = i2 / 100;
// really: r = i2 - (q * 100);
r = i2 - ((q2 << 6) + (q2 << 5) + (q2 << 2));
i2 = q2;
content[--charPos] = DigitOnes[r];
content[--charPos] = DigitTens[r];
}
// Fall thru to fast mode for smaller numbers
// assert(i2 <= 65536, i2);
for (;;) {
q2 = (i2 * 52429) >>> (16 + 3);
r = i2 - ((q2 << 3) + (q2 << 1)); // r = i2-(q2*10) ...
content[--charPos] = digits[r];
i2 = q2;
if (i2 == 0) break;
}
if (sign != 0) content[--charPos] = sign;
count += size;
}
}

View File

@@ -30,13 +30,18 @@ public class JsonConvert extends TextConvert<JsonReader, JsonWriter> {
public static final Type TYPE_RETRESULT_STRING = new TypeToken<RetResult<String>>() {
}.getType();
//private static final ObjectPool<JsonReader> readerPool = JsonReader.createPool(Integer.getInteger("convert.json.pool.size", Integer.getInteger("convert.pool.size", 16)));
//private static final ObjectPool<JsonWriter> writerPool = JsonWriter.createPool(Integer.getInteger("convert.json.pool.size", Integer.getInteger("convert.pool.size", 16)));
//
private static final ThreadLocal<JsonWriter> writerPool = ThreadLocal.withInitial(JsonWriter::new);
private final ThreadLocal<JsonCharsWriter> charsWriterPool = ThreadLocal.withInitial(JsonCharsWriter::new);
private final ThreadLocal<JsonBytesWriter> bytesWriterPool = ThreadLocal.withInitial(JsonBytesWriter::new);
private final Consumer<JsonBytesWriter> offerBytesConsumer = w -> offerJsonBytesWriter(w);
private final boolean tiny;
private Encodeable lastConvertEncodeable;
private Decodeable lastConvertDecodeable;
protected JsonConvert(JsonFactory factory, boolean tiny) {
super(factory);
this.tiny = tiny;
@@ -66,45 +71,42 @@ public class JsonConvert extends TextConvert<JsonReader, JsonWriter> {
};
}
//------------------------------ reader -----------------------------------------------------------
// public JsonReader pollJsonReader(final ByteBuffer... buffers) {
// return new JsonByteBufferReader((ConvertMask) null, buffers);
// }
//
// public JsonReader pollJsonReader(final InputStream in) {
// return new JsonStreamReader(in);
// }
//
// public JsonReader pollJsonReader() {
// return readerPool.get();
// }
//
// public void offerJsonReader(final JsonReader in) {
// if (in != null) readerPool.accept(in);
// }
//------------------------------ writer -----------------------------------------------------------
private JsonByteBufferWriter pollJsonWriter(final Supplier<ByteBuffer> supplier) {
return configWrite(new JsonByteBufferWriter(tiny, supplier));
private JsonCharsWriter pollJsonCharsWriter() {
JsonCharsWriter writer = charsWriterPool.get();
if (writer == null) {
writer = new JsonCharsWriter();
} else {
charsWriterPool.set(null);
}
return configWrite((JsonCharsWriter) writer.tiny(tiny));
}
private JsonWriter pollJsonWriter(final OutputStream out) {
return configWrite(new JsonStreamWriter(tiny, out));
private JsonBytesWriter pollJsonBytesWriter() {
JsonBytesWriter writer = bytesWriterPool.get();
if (writer == null) {
writer = new JsonBytesWriter();
} else {
bytesWriterPool.set(null);
}
return configWrite((JsonBytesWriter) writer.tiny(tiny));
}
//
// public JsonWriter pollJsonWriter(final Charset charset, final OutputStream out) {
// return configWrite(new JsonStreamWriter(tiny, charset, out));
// }
//
private JsonWriter pollJsonWriter() {
return configWrite(writerPool.get().tiny(tiny));
private void offerJsonCharsWriter(final JsonCharsWriter writer) {
if (writer != null) {
writer.recycle();
charsWriterPool.set(writer);
}
}
//
// public void offerJsonWriter(final JsonWriter writer) {
// if (writer != null) writerPool.accept(writer);
// }
private void offerJsonBytesWriter(final JsonBytesWriter writer) {
if (writer != null) {
writer.recycle();
bytesWriterPool.set(writer);
}
}
//------------------------------ convertFrom -----------------------------------------------------------
@Override
public <T> T convertFrom(final Type type, final byte[] bytes) {
if (bytes == null) return null;
@@ -129,34 +131,56 @@ public class JsonConvert extends TextConvert<JsonReader, JsonWriter> {
public <T> T convertFrom(final Type type, final char[] text, final int offset, final int length) {
if (text == null || type == null) return null;
//final JsonReader in = readerPool.get();
//in.setText(text, offset, length);
T rs = (T) factory.loadDecoder(type).convertFrom(new JsonReader(text, offset, length));
//readerPool.accept(in);
Decodeable decoder = this.lastConvertDecodeable;
if (decoder == null || decoder.getType() != type) {
decoder = factory.loadDecoder(type);
this.lastConvertDecodeable = decoder;
}
T rs = (T) decoder.convertFrom(new JsonReader(text, offset, length));
return rs;
}
public <T> T convertFrom(final Type type, final InputStream in) {
if (type == null || in == null) return null;
return (T) factory.loadDecoder(type).convertFrom(new JsonStreamReader(in));
Decodeable decoder = this.lastConvertDecodeable;
if (decoder == null || decoder.getType() != type) {
decoder = factory.loadDecoder(type);
this.lastConvertDecodeable = decoder;
}
return (T) decoder.convertFrom(new JsonStreamReader(in));
}
@Override
public <T> T convertFrom(final Type type, final ByteBuffer... buffers) {
if (type == null || buffers == null || buffers.length == 0) return null;
return (T) factory.loadDecoder(type).convertFrom(new JsonByteBufferReader((ConvertMask) null, buffers));
Decodeable decoder = this.lastConvertDecodeable;
if (decoder == null || decoder.getType() != type) {
decoder = factory.loadDecoder(type);
this.lastConvertDecodeable = decoder;
}
return (T) decoder.convertFrom(new JsonByteBufferReader((ConvertMask) null, buffers));
}
@Override
public <T> T convertFrom(final Type type, final ConvertMask mask, final ByteBuffer... buffers) {
if (type == null || buffers == null || buffers.length == 0) return null;
return (T) factory.loadDecoder(type).convertFrom(new JsonByteBufferReader(mask, buffers));
Decodeable decoder = this.lastConvertDecodeable;
if (decoder == null || decoder.getType() != type) {
decoder = factory.loadDecoder(type);
this.lastConvertDecodeable = decoder;
}
return (T) decoder.convertFrom(new JsonByteBufferReader(mask, buffers));
}
public <T> T convertFrom(final Type type, final JsonReader reader) {
if (type == null) return null;
Decodeable decoder = this.lastConvertDecodeable;
if (decoder == null || decoder.getType() != type) {
decoder = factory.loadDecoder(type);
this.lastConvertDecodeable = decoder;
}
@SuppressWarnings("unchecked")
T rs = (T) factory.loadDecoder(type).convertFrom(reader);
T rs = (T) decoder.convertFrom(reader);
return rs;
}
@@ -217,73 +241,91 @@ public 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";
JsonWriter writer = pollJsonWriter();
if (writer == null) {
writer = new JsonWriter();
} else {
writerPool.set(null);
JsonCharsWriter writer = pollJsonCharsWriter();
Encodeable encoder = this.lastConvertEncodeable;
if (encoder == null || encoder.getType() != type) {
encoder = factory.loadEncoder(type);
this.lastConvertEncodeable = encoder;
}
writer.specify(type);
factory.loadEncoder(type).convertTo(writer, value);
if (encoder.specifyable()) writer.specify(type);
encoder.convertTo(writer, value);
String result = writer.toString();
//writerPool.accept(writer);
if (writerPool.get() == null) {
writer.recycle();
writerPool.set(writer);
}
offerJsonCharsWriter(writer);
return result;
}
@Override
public byte[] convertToBytes(final Object value) {
if (value == null) return null;
String result = convertTo(value.getClass(), value);
return result == null ? null : result.getBytes(StandardCharsets.UTF_8);
return convertToBytes(value.getClass(), value);
}
@Override
public byte[] convertToBytes(final Type type, final Object value) {
if (type == null) return null;
if (value == null) return null;
JsonWriter writer = pollJsonWriter();
if (writer == null) {
writer = new JsonWriter();
} else {
writerPool.set(null);
JsonBytesWriter writer = pollJsonBytesWriter();
Encodeable encoder = this.lastConvertEncodeable;
if (encoder == null || encoder.getType() != type) {
encoder = factory.loadEncoder(type);
this.lastConvertEncodeable = encoder;
}
writer.specify(type);
factory.loadEncoder(type).convertTo(writer, value);
String result = writer.toString();
//writerPool.accept(writer);
if (writerPool.get() == null) {
writer.recycle();
writerPool.set(writer);
}
return result == null ? null : result.getBytes(StandardCharsets.UTF_8);
if (encoder.specifyable()) writer.specify(type);
encoder.convertTo(writer, value);
byte[] result = writer.toBytes();
offerJsonBytesWriter(writer);
return result;
}
@Override
public String convertMapTo(final Object... values) {
if (values == null) return "null";
JsonWriter writer = pollJsonWriter();
if (writer == null) {
writer = new JsonWriter();
public void convertToBytes(final Object value, final ConvertBytesHandler handler) {
convertToBytes(value == null ? null : value.getClass(), value, handler);
}
@Override
public void convertToBytes(final Type type, final Object value, final ConvertBytesHandler handler) {
JsonBytesWriter writer = pollJsonBytesWriter();
if (type == null) {
writer.writeNull();
} else {
writerPool.set(null);
Encodeable encoder = this.lastConvertEncodeable;
if (encoder == null || encoder.getType() != type) {
encoder = factory.loadEncoder(type);
this.lastConvertEncodeable = encoder;
}
if (encoder.specifyable()) writer.specify(type);
encoder.convertTo(writer, value);
}
((AnyEncoder) factory.getAnyEncoder()).convertMapTo(writer, values);
String result = writer.toString();
//writerPool.accept(writer);
if (writerPool.get() == null) {
writer.recycle();
writerPool.set(writer);
writer.completed(handler, offerBytesConsumer);
}
@Override
public void convertToBytes(final ByteArray array, final Object value) {
convertToBytes(array, value == null ? null : value.getClass(), value);
}
@Override
public void convertToBytes(final ByteArray array, final Type type, final Object value) {
JsonBytesWriter writer = configWrite(new JsonBytesWriter(tiny, array));
if (type == null) {
writer.writeNull();
} else {
Encodeable encoder = this.lastConvertEncodeable;
if (encoder == null || encoder.getType() != type) {
encoder = factory.loadEncoder(type);
this.lastConvertEncodeable = encoder;
}
if (encoder.specifyable()) writer.specify(type);
encoder.convertTo(writer, value);
}
return result;
writer.directTo(array);
}
public void convertTo(final OutputStream out, final Object value) {
if (value == null) {
pollJsonWriter(out).writeNull();
configWrite(new JsonStreamWriter(tiny, out)).writeNull();
} else {
convertTo(out, value.getClass(), value);
}
@@ -292,59 +334,23 @@ public 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) {
pollJsonWriter(out).writeNull();
configWrite(new JsonStreamWriter(tiny, out)).writeNull();
} else {
JsonWriter writer = pollJsonWriter();
if (writer == null) {
writer = new JsonWriter();
} else {
writerPool.set(null);
}
writer.specify(type);
factory.loadEncoder(type).convertTo(writer, value);
byte[] bs = writer.toBytes();
//writerPool.accept(writer);
if (writerPool.get() == null) {
writer.recycle();
writerPool.set(writer);
}
try {
out.write(bs);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
public void convertMapTo(final OutputStream out, final Object... values) {
if (values == null) {
pollJsonWriter(out).writeNull();
} else {
JsonWriter writer = pollJsonWriter();
if (writer == null) {
writer = new JsonWriter();
} else {
writerPool.set(null);
}
((AnyEncoder) factory.getAnyEncoder()).convertMapTo(writer, values);
byte[] bs = writer.toBytes();
//writerPool.accept(writer);
if (writerPool.get() == null) {
writer.recycle();
writerPool.set(writer);
}
try {
out.write(bs);
} catch (IOException e) {
throw new RuntimeException(e);
JsonStreamWriter writer = configWrite(new JsonStreamWriter(tiny, out));
Encodeable encoder = this.lastConvertEncodeable;
if (encoder == null || encoder.getType() != type) {
encoder = factory.loadEncoder(type);
this.lastConvertEncodeable = encoder;
}
if (encoder.specifyable()) writer.specify(type);
encoder.convertTo(writer, value);
}
}
@Override
public ByteBuffer[] convertTo(final Supplier<ByteBuffer> supplier, final Object value) {
if (supplier == null) return null;
JsonByteBufferWriter out = pollJsonWriter(supplier);
JsonByteBufferWriter out = configWrite(new JsonByteBufferWriter(tiny, supplier));
if (value == null) {
out.writeNull();
} else {
@@ -356,7 +362,7 @@ public 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 = pollJsonWriter(supplier);
JsonByteBufferWriter out = configWrite(new JsonByteBufferWriter(tiny, supplier));
if (value == null) {
out.writeNull();
} else {
@@ -366,29 +372,6 @@ public class JsonConvert extends TextConvert<JsonReader, JsonWriter> {
return out.toBuffers();
}
@Override
public byte[] convertMapToBytes(final Object... values) {
JsonWriter out = pollJsonWriter();
if (values == null) {
out.writeNull();
} else {
((AnyEncoder) factory.getAnyEncoder()).convertMapTo(out, values);
}
return out.toBytes();
}
@Override
public ByteBuffer[] convertMapTo(final Supplier<ByteBuffer> supplier, final Object... values) {
if (supplier == null) return null;
JsonByteBufferWriter out = pollJsonWriter(supplier);
if (values == null) {
out.writeNull();
} else {
((AnyEncoder) factory.getAnyEncoder()).convertMapTo(out, values);
}
return out.toBuffers();
}
public void convertTo(final JsonWriter writer, final Object value) {
if (value == null) {
writer.writeNull();
@@ -407,40 +390,4 @@ public class JsonConvert extends TextConvert<JsonReader, JsonWriter> {
}
}
public void convertMapTo(final JsonWriter writer, final Object... values) {
if (values == null) {
writer.writeNull();
} else {
((AnyEncoder) factory.getAnyEncoder()).convertMapTo(writer, values);
}
}
// public JsonWriter convertToWriter(final Object value) {
// if (value == null) return null;
// return convertToWriter(value.getClass(), value);
// }
//
// public JsonWriter convertToWriter(final Type type, final Object value) {
// if (type == null) return null;
// JsonWriter writer = writerPool.get();// pollJsonWriter();
// if (writer == null) {
// writer = new JsonWriter();
// } else {
// writerPool.set(null);
// }
// writer.specify(type);
// factory.loadEncoder(type).convertTo(writer, value);
// return writer;
// }
//
// public JsonWriter convertMapToWriter(final Object... values) {
// JsonWriter writer = writerPool.get();// pollJsonWriter();
// if (writer == null) {
// writer = new JsonWriter();
// } else {
// writerPool.set(null);
// }
// ((AnyEncoder) factory.getAnyEncoder()).convertMapTo(writer, values);
// return writer;
// }
}

View File

@@ -0,0 +1,559 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.convert.json;
import java.lang.reflect.*;
import java.lang.reflect.Type;
import java.util.*;
import org.redkale.asm.*;
import static org.redkale.asm.ClassWriter.COMPUTE_FRAMES;
import static org.redkale.asm.Opcodes.*;
import org.redkale.convert.*;
import org.redkale.convert.ext.*;
/**
* 简单对象的JSON序列化操作类
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
* @since 2.3.0
*
* @param <T> 序列化的数据类型
*/
@SuppressWarnings("unchecked")
public abstract class JsonDynEncoder<T> implements Encodeable<JsonWriter, T> {
protected final Class typeClass;
protected final ObjectEncoder<JsonWriter, T> objectEncoder;
protected JsonDynEncoder(final JsonFactory factory, Type type) {
this.typeClass = (Class) type;
factory.register(type, this);
this.objectEncoder = factory.createObjectEncoder(type);
}
@Override
public boolean specifyable() {
return false;
}
private static boolean checkMemberType(final JsonFactory factory, Type type, Class clazz) {
if (type == String.class) return true;
if (clazz.isPrimitive()) return true;
if (clazz.isEnum()) return true;
if (type == boolean[].class) return true;
if (type == byte[].class) return true;
if (type == short[].class) return true;
if (type == char[].class) return true;
if (type == int[].class) return true;
if (type == float[].class) return true;
if (type == long[].class) return true;
if (type == double[].class) return true;
if (type == Boolean[].class) return true;
if (type == Byte[].class) return true;
if (type == Short[].class) return true;
if (type == Character[].class) return true;
if (type == Integer[].class) return true;
if (type == Float[].class) return true;
if (type == Long[].class) return true;
if (type == Double[].class) return true;
if (type == String[].class) return true;
if (Collection.class.isAssignableFrom(clazz) && type instanceof ParameterizedType) {
Type[] ts = ((ParameterizedType) type).getActualTypeArguments();
if (ts.length == 1) {
Type t = ts[0];
if (t == Boolean.class || t == Byte.class || t == Short.class || t == Character.class
|| t == Integer.class || t == Float.class || t == Long.class || t == Double.class
|| t == String.class || ((t instanceof Class) && ((Class) t).isEnum())) return true;
if (factory.loadEncoder(t) instanceof JsonDynEncoder) return true;
}
}
if (factory.loadEncoder(type) instanceof JsonDynEncoder) return true;
return false;
}
//字段全部是primitive或String类型且没有泛型的类才能动态生成JsonDynEncoder 不支持的返回null
public static JsonDynEncoder createDyncEncoder(final JsonFactory factory, final Type type) {
if (!(type instanceof Class)) return null;
//发现有自定义的基础数据类型Encoder就不动态生成JsonDynEncoder了
if (factory.loadEncoder(boolean.class) != BoolSimpledCoder.instance) return null;
if (factory.loadEncoder(byte.class) != ByteSimpledCoder.instance) return null;
if (factory.loadEncoder(short.class) != ShortSimpledCoder.instance) return null;
if (factory.loadEncoder(char.class) != CharSimpledCoder.instance) return null;
if (factory.loadEncoder(int.class) != IntSimpledCoder.instance) return null;
if (factory.loadEncoder(float.class) != FloatSimpledCoder.instance) return null;
if (factory.loadEncoder(long.class) != LongSimpledCoder.instance) return null;
if (factory.loadEncoder(double.class) != DoubleSimpledCoder.instance) return null;
if (factory.loadEncoder(String.class) != StringSimpledCoder.instance) return null;
//array
if (factory.loadEncoder(boolean[].class) != BoolArraySimpledCoder.instance) return null;
if (factory.loadEncoder(byte[].class) != ByteArraySimpledCoder.instance) return null;
if (factory.loadEncoder(short[].class) != ShortArraySimpledCoder.instance) return null;
if (factory.loadEncoder(char[].class) != CharArraySimpledCoder.instance) return null;
if (factory.loadEncoder(int[].class) != IntArraySimpledCoder.instance) return null;
if (factory.loadEncoder(float[].class) != FloatArraySimpledCoder.instance) return null;
if (factory.loadEncoder(long[].class) != LongArraySimpledCoder.instance) return null;
if (factory.loadEncoder(double[].class) != DoubleArraySimpledCoder.instance) return null;
if (factory.loadEncoder(String[].class) != StringArraySimpledCoder.instance) return null;
final Class clazz = (Class) type;
List<AccessibleObject> members = null;
Set<String> names = new HashSet<>();
try {
ConvertColumnEntry ref;
for (final Field field : clazz.getFields()) {
if (Modifier.isStatic(field.getModifiers())) continue;
if (factory.isConvertDisabled(field)) continue;
ref = factory.findRef(clazz, field);
if (ref != null && ref.ignore()) continue;
if (!(checkMemberType(factory, field.getGenericType(), field.getType()))) return null;
String name = convertFieldName(factory, clazz, field);
if (names.contains(name)) continue;
names.add(name);
if (members == null) members = new ArrayList<>();
members.add(field);
}
for (final Method method : clazz.getMethods()) {
if (Modifier.isStatic(method.getModifiers())) continue;
if (Modifier.isAbstract(method.getModifiers())) continue;
if (method.isSynthetic()) continue;
if (method.getName().length() < 3) continue;
if (method.getName().equals("getClass")) continue;
if (!method.getName().startsWith("is") && !method.getName().startsWith("get")) continue;
if (factory.isConvertDisabled(method)) continue;
if (method.getParameterTypes().length != 0) continue;
if (method.getReturnType() == void.class) continue;
ref = factory.findRef(clazz, method);
if (ref != null && ref.ignore()) continue;
if (!(checkMemberType(factory, method.getGenericReturnType(), method.getReturnType()))) return null;
String name = convertFieldName(factory, clazz, method);
if (names.contains(name)) continue;
names.add(name);
if (members == null) members = new ArrayList<>();
members.add(method);
}
if (members == null) return null;
Collections.sort(members, (o1, o2) -> {
ConvertColumnEntry ref1 = factory.findRef(clazz, o1);
ConvertColumnEntry ref2 = factory.findRef(clazz, o2);
if ((ref1 != null && ref1.getIndex() > 0) || (ref2 != null && ref2.getIndex() > 0)) {
int idx1 = ref1 == null ? Integer.MAX_VALUE / 2 : ref1.getIndex();
int idx2 = ref2 == null ? Integer.MAX_VALUE / 2 : ref2.getIndex();
if (idx1 != idx2) return idx1 - idx2;
}
String n1 = ref1 == null || ref1.name().isEmpty() ? readGetSetFieldName(o1) : ref1.name();
String n2 = ref2 == null || ref2.name().isEmpty() ? readGetSetFieldName(o2) : ref2.name();
return n1.compareTo(n2);
});
return generateDyncEncoder(factory, clazz, members);
} catch (Exception ex) {
ex.printStackTrace();
return null;
}
}
protected static String convertFieldName(final JsonFactory factory, Class clazz, AccessibleObject element) {
ConvertColumnEntry ref = factory.findRef(clazz, element);
String name = ref == null || ref.name().isEmpty() ? readGetSetFieldName(element) : ref.name();
return name;
}
protected static ConvertSmallString readConvertSmallString(AccessibleObject element) {
if (element instanceof Field) return ((Field) element).getAnnotation(ConvertSmallString.class);
Method method = (Method) element;
ConvertSmallString small = method.getAnnotation(ConvertSmallString.class);
if (small == null) {
try {
Field f = method.getDeclaringClass().getDeclaredField(readGetSetFieldName(method));
if (f != null) small = f.getAnnotation(ConvertSmallString.class);
} catch (Exception e) {
}
}
return small;
}
protected static Class readGetSetFieldType(AccessibleObject element) {
if (element instanceof Field) return ((Field) element).getType();
return element == null ? null : ((Method) element).getReturnType();
}
protected static String readGetSetFieldName(AccessibleObject element) {
if (element instanceof Field) return ((Field) element).getName();
Method method = (Method) element;
if (method == null) return null;
String fname = method.getName();
if (!fname.startsWith("is") && !fname.startsWith("get") && !fname.startsWith("set")) return fname;
fname = fname.substring(fname.startsWith("is") ? 2 : 3);
if (fname.length() > 1 && !(fname.charAt(1) >= 'A' && fname.charAt(1) <= 'Z')) {
fname = Character.toLowerCase(fname.charAt(0)) + fname.substring(1);
} else if (fname.length() == 1) {
fname = "" + Character.toLowerCase(fname.charAt(0));
}
return fname;
}
protected static JsonDynEncoder generateDyncEncoder(final JsonFactory factory, final Class clazz, final List<AccessibleObject> members) {
final String supDynName = JsonDynEncoder.class.getName().replace('.', '/');
final String valtypeName = clazz.getName().replace('.', '/');
final String writerName = JsonWriter.class.getName().replace('.', '/');
final String encodeableName = Encodeable.class.getName().replace('.', '/');
final String objEncoderName = ObjectEncoder.class.getName().replace('.', '/');
final String typeDesc = org.redkale.asm.Type.getDescriptor(Type.class);
final String jsonfactoryDesc = org.redkale.asm.Type.getDescriptor(JsonFactory.class);
final String jsonwriterDesc = org.redkale.asm.Type.getDescriptor(JsonWriter.class);
final String writerDesc = org.redkale.asm.Type.getDescriptor(Writer.class);
final String encodeableDesc = org.redkale.asm.Type.getDescriptor(Encodeable.class);
final String objEncoderDesc = org.redkale.asm.Type.getDescriptor(ObjectEncoder.class);
final String valtypeDesc = org.redkale.asm.Type.getDescriptor(clazz);
String newDynName = supDynName + "_Dyn" + clazz.getSimpleName();
ClassLoader loader = Thread.currentThread().getContextClassLoader();
if (String.class.getClassLoader() != clazz.getClassLoader()) {
loader = clazz.getClassLoader();
newDynName = valtypeName + "_" + JsonDynEncoder.class.getSimpleName();
}
try {
return (JsonDynEncoder) loader.loadClass(newDynName.replace('/', '.')).getDeclaredConstructor().newInstance();
} catch (Throwable ex) {
}
// ------------------------------------------------------------------------------
ClassWriter cw = new ClassWriter(COMPUTE_FRAMES);
FieldVisitor fv;
MethodVisitor mv;
AnnotationVisitor av0;
cw.visit(V1_8, ACC_PUBLIC + ACC_FINAL + ACC_SUPER, newDynName, "L" + supDynName + "<" + valtypeDesc + ">;", supDynName, null);
Map<String, AccessibleObject> mixedNames = null;
for (AccessibleObject element : members) {
ConvertColumnEntry ref1 = factory.findRef(clazz, element);
final String fieldname = ref1 == null || ref1.name().isEmpty() ? readGetSetFieldName(element) : ref1.name();
fv = cw.visitField(ACC_PROTECTED + ACC_FINAL, fieldname + "FieldBytes", "[B", null, null);
fv.visitEnd();
fv = cw.visitField(ACC_PROTECTED + ACC_FINAL, fieldname + "CommaFieldBytes", "[B", null, null);
fv.visitEnd();
final Class fieldtype = readGetSetFieldType(element);
if (fieldtype != String.class && !fieldtype.isPrimitive()) {
if (mixedNames == null) mixedNames = new HashMap<>();
mixedNames.put(fieldname, element);
fv = cw.visitField(ACC_PROTECTED, fieldname + "Encoder", encodeableDesc, null, null);
fv.visitEnd();
}
}
{ // 构造函数
mv = (cw.visitMethod(ACC_PUBLIC, "<init>", "(" + jsonfactoryDesc + typeDesc + ")V", null, null));
//mv.setDebug(true);
mv.visitVarInsn(ALOAD, 0);
mv.visitVarInsn(ALOAD, 1);
mv.visitVarInsn(ALOAD, 2);
mv.visitMethodInsn(INVOKESPECIAL, supDynName, "<init>", "(" + jsonfactoryDesc + typeDesc + ")V", false);
for (AccessibleObject element : members) {
ConvertColumnEntry ref1 = factory.findRef(clazz, element);
final String fieldname = ref1 == null || ref1.name().isEmpty() ? readGetSetFieldName(element) : ref1.name();
//xxxFieldBytes
mv.visitVarInsn(ALOAD, 0);
mv.visitLdcInsn("\"" + fieldname + "\":");
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/String", "getBytes", "()[B", false);
mv.visitFieldInsn(PUTFIELD, newDynName, fieldname + "FieldBytes", "[B");
//xxxCommaFieldBytes
mv.visitVarInsn(ALOAD, 0);
mv.visitLdcInsn(",\"" + fieldname + "\":");
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/String", "getBytes", "()[B", false);
mv.visitFieldInsn(PUTFIELD, newDynName, fieldname + "CommaFieldBytes", "[B");
}
mv.visitInsn(RETURN);
mv.visitMaxs(1 + members.size(), 1 + members.size());
mv.visitEnd();
}
{
mv = (cw.visitMethod(ACC_PUBLIC, "convertTo", "(" + jsonwriterDesc + valtypeDesc + ")V", null, null));
//mv.setDebug(true);
{ //if (value == null) { out.writeObjectNull(null); return; }
mv.visitVarInsn(ALOAD, 2);
Label valif = new Label();
mv.visitJumpInsn(IFNONNULL, valif);
mv.visitVarInsn(ALOAD, 1);
mv.visitInsn(ACONST_NULL);
mv.visitMethodInsn(INVOKEVIRTUAL, writerName, "writeObjectNull", "(Ljava/lang/Class;)V", false);
mv.visitInsn(RETURN);
mv.visitLabel(valif);
mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
}
{ //if (!out.isExtFuncEmpty()) { objectEncoder.convertTo(out, value); return; }
mv.visitVarInsn(ALOAD, 1);
mv.visitMethodInsn(INVOKEVIRTUAL, writerName, "isExtFuncEmpty", "()Z", false);
Label extif = new Label();
mv.visitJumpInsn(IFNE, extif);
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, newDynName, "objectEncoder", objEncoderDesc);
mv.visitVarInsn(ALOAD, 1);
mv.visitVarInsn(ALOAD, 2);
mv.visitMethodInsn(INVOKEVIRTUAL, objEncoderName, "convertTo", "(" + org.redkale.asm.Type.getDescriptor(Writer.class) + "Ljava/lang/Object;)V", false);
mv.visitInsn(RETURN);
mv.visitLabel(extif);
mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
}
{ //out.writeTo('{');
mv.visitVarInsn(ALOAD, 1);
mv.visitIntInsn(BIPUSH, '{');
mv.visitMethodInsn(INVOKEVIRTUAL, writerName, "writeTo", "(B)V", false);
}
int maxLocals = 4;
int elementIndex = -1;
final Class firstType = readGetSetFieldType(members.get(0));
final boolean mustHadComma = firstType.isPrimitive() && (firstType != boolean.class || !factory.tiny()); //byte/short/char/int/float/long/double
if (!mustHadComma) { //boolean comma = false;
mv.visitInsn(ICONST_0);
mv.visitVarInsn(ISTORE, 3);
}
for (AccessibleObject element : members) {
elementIndex++;
ConvertColumnEntry ref1 = factory.findRef(clazz, element);
final String fieldname = ref1 == null || ref1.name().isEmpty() ? readGetSetFieldName(element) : ref1.name();
final Class fieldtype = readGetSetFieldType(element);
int storeid = ASTORE;
int loadid = ALOAD;
{ //String message = value.getMessage();
mv.visitVarInsn(ALOAD, 2); //加载 value
if (element instanceof Field) {
mv.visitFieldInsn(GETFIELD, valtypeName, ((Field) element).getName(), org.redkale.asm.Type.getDescriptor(fieldtype));
} else {
mv.visitMethodInsn(INVOKEVIRTUAL, valtypeName, ((Method) element).getName(), "()" + org.redkale.asm.Type.getDescriptor(fieldtype), false);
}
if (fieldtype == boolean.class) {
storeid = ISTORE;
loadid = ILOAD;
mv.visitVarInsn(storeid, maxLocals);
} else if (fieldtype == byte.class) {
storeid = ISTORE;
loadid = ILOAD;
mv.visitVarInsn(storeid, maxLocals);
} else if (fieldtype == short.class) {
storeid = ISTORE;
loadid = ILOAD;
mv.visitVarInsn(storeid, maxLocals);
} else if (fieldtype == char.class) {
storeid = ISTORE;
loadid = ILOAD;
mv.visitVarInsn(storeid, maxLocals);
} else if (fieldtype == int.class) {
storeid = ISTORE;
loadid = ILOAD;
mv.visitVarInsn(storeid, maxLocals);
} else if (fieldtype == float.class) {
storeid = FSTORE;
loadid = FLOAD;
mv.visitVarInsn(storeid, maxLocals);
} else if (fieldtype == long.class) {
storeid = LSTORE;
loadid = LLOAD;
mv.visitVarInsn(storeid, maxLocals);
} else if (fieldtype == double.class) {
storeid = DSTORE;
loadid = DLOAD;
mv.visitVarInsn(storeid, maxLocals);
} else {
//storeid = ASTORE;
//loadid = ALOAD;
mv.visitVarInsn(storeid, maxLocals);
}
}
Label msgnotemptyif = null;
if (!fieldtype.isPrimitive()) { //if (message != null) { start
mv.visitVarInsn(loadid, maxLocals);
msgnotemptyif = new Label();
mv.visitJumpInsn(IFNULL, msgnotemptyif);
if (factory.tiny() && fieldtype == String.class) {
mv.visitVarInsn(loadid, maxLocals);
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/String", "isEmpty", "()Z", false);
mv.visitJumpInsn(IFNE, msgnotemptyif);
}
} else if (fieldtype == boolean.class && factory.tiny()) {
mv.visitVarInsn(loadid, maxLocals);
msgnotemptyif = new Label();
mv.visitJumpInsn(IFEQ, msgnotemptyif);
}
if (mustHadComma) { //第一个字段必然会写入
if (elementIndex == 0) { //第一个
//out.writeTo(messageFieldBytes);
mv.visitVarInsn(ALOAD, 1);
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, newDynName, fieldname + "FieldBytes", "[B");
mv.visitMethodInsn(INVOKEVIRTUAL, writerName, "writeTo", "([B)V", false);
} else {
//out.writeTo(messageCommaFieldBytes);
mv.visitVarInsn(ALOAD, 1);
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, newDynName, fieldname + "CommaFieldBytes", "[B");
mv.visitMethodInsn(INVOKEVIRTUAL, writerName, "writeTo", "([B)V", false);
}
} else { //if(comma) {} else {} 代码块
//if (comma) { start
mv.visitVarInsn(ILOAD, 3);
Label commaif = new Label();
mv.visitJumpInsn(IFEQ, commaif);
//out.writeTo(messageCommaFieldBytes);
mv.visitVarInsn(ALOAD, 1);
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, newDynName, fieldname + "CommaFieldBytes", "[B");
mv.visitMethodInsn(INVOKEVIRTUAL, writerName, "writeTo", "([B)V", false);
Label commaelse = new Label();
mv.visitJumpInsn(GOTO, commaelse);
mv.visitLabel(commaif);
if (fieldtype == boolean.class) {
mv.visitFrame(Opcodes.F_APPEND, 1, new Object[]{Opcodes.INTEGER}, 0, null);
} else if (fieldtype == byte.class) {
mv.visitFrame(Opcodes.F_APPEND, 1, new Object[]{Opcodes.INTEGER}, 0, null);
} else if (fieldtype == short.class) {
mv.visitFrame(Opcodes.F_APPEND, 1, new Object[]{Opcodes.INTEGER}, 0, null);
} else if (fieldtype == char.class) {
mv.visitFrame(Opcodes.F_APPEND, 1, new Object[]{Opcodes.INTEGER}, 0, null);
} else if (fieldtype == int.class) {
mv.visitFrame(Opcodes.F_APPEND, 1, new Object[]{Opcodes.INTEGER}, 0, null);
} else if (fieldtype == float.class) {
mv.visitFrame(Opcodes.F_APPEND, 1, new Object[]{Opcodes.FLOAT}, 0, null);
} else if (fieldtype == long.class) {
mv.visitFrame(Opcodes.F_APPEND, 1, new Object[]{Opcodes.LONG}, 0, null);
} else if (fieldtype == double.class) {
mv.visitFrame(Opcodes.F_APPEND, 1, new Object[]{Opcodes.DOUBLE}, 0, null);
} else {
mv.visitFrame(Opcodes.F_APPEND, 2, new Object[]{Opcodes.INTEGER, "java/lang/String"}, 0, null); // } else { comma
}
//out.writeTo(messageFieldBytes);
mv.visitVarInsn(ALOAD, 1);
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, newDynName, fieldname + "FieldBytes", "[B");
mv.visitMethodInsn(INVOKEVIRTUAL, writerName, "writeTo", "([B)V", false);
//comma = true;
mv.visitInsn(ICONST_1);
mv.visitVarInsn(ISTORE, 3);
mv.visitLabel(commaelse);
mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null); //if (comma) } end
}
//out.writeString(message);
if (fieldtype == boolean.class) {
mv.visitVarInsn(ALOAD, 1);
mv.visitVarInsn(loadid, maxLocals);
mv.visitMethodInsn(INVOKEVIRTUAL, writerName, "writeBoolean", "(Z)V", false);
} else if (fieldtype == byte.class) {
mv.visitVarInsn(ALOAD, 1);
mv.visitVarInsn(loadid, maxLocals);
mv.visitMethodInsn(INVOKEVIRTUAL, writerName, "writeByte", "(B)V", false);
} else if (fieldtype == short.class) {
mv.visitVarInsn(ALOAD, 1);
mv.visitVarInsn(loadid, maxLocals);
mv.visitMethodInsn(INVOKEVIRTUAL, writerName, "writeShort", "(S)V", false);
} else if (fieldtype == char.class) {
mv.visitVarInsn(ALOAD, 1);
mv.visitVarInsn(loadid, maxLocals);
mv.visitMethodInsn(INVOKEVIRTUAL, writerName, "writeChar", "(C)V", false);
} else if (fieldtype == int.class) {
mv.visitVarInsn(ALOAD, 1);
mv.visitVarInsn(loadid, maxLocals);
mv.visitMethodInsn(INVOKEVIRTUAL, writerName, "writeInt", "(I)V", false);
} else if (fieldtype == float.class) {
mv.visitVarInsn(ALOAD, 1);
mv.visitVarInsn(loadid, maxLocals);
mv.visitMethodInsn(INVOKEVIRTUAL, writerName, "writeFloat", "(F)V", false);
} else if (fieldtype == long.class) {
mv.visitVarInsn(ALOAD, 1);
mv.visitVarInsn(loadid, maxLocals);
mv.visitMethodInsn(INVOKEVIRTUAL, writerName, "writeLong", "(J)V", false);
} else if (fieldtype == double.class) {
mv.visitVarInsn(ALOAD, 1);
mv.visitVarInsn(loadid, maxLocals);
mv.visitMethodInsn(INVOKEVIRTUAL, writerName, "writeDouble", "(D)V", false);
} else if (fieldtype == String.class) {
if (readConvertSmallString(element) == null) {
mv.visitVarInsn(ALOAD, 1);
mv.visitVarInsn(loadid, maxLocals);
mv.visitMethodInsn(INVOKEVIRTUAL, writerName, "writeString", "(Ljava/lang/String;)V", false);
} else {
mv.visitVarInsn(ALOAD, 1);
mv.visitInsn(ICONST_1);
mv.visitVarInsn(loadid, maxLocals);
mv.visitMethodInsn(INVOKEVIRTUAL, writerName, "writeLatin1To", "(ZLjava/lang/String;)V", false);
}
} else { //int[],Boolean[],String[]
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, newDynName, fieldname + "Encoder", encodeableDesc);
mv.visitVarInsn(ALOAD, 1);
mv.visitVarInsn(loadid, maxLocals);
mv.visitMethodInsn(INVOKEINTERFACE, encodeableName, "convertTo", "(" + writerDesc + "Ljava/lang/Object;)V", true);
}
if (!fieldtype.isPrimitive()) { //if (message != null) } end
mv.visitLabel(msgnotemptyif);
mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
} else if (fieldtype == boolean.class && factory.tiny()) {
mv.visitLabel(msgnotemptyif);
mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
}
if (fieldtype == long.class || fieldtype == double.class) {
maxLocals += 2;
} else {
maxLocals++;
}
}
{ //out.writeTo('}');
mv.visitVarInsn(ALOAD, 1);
mv.visitIntInsn(BIPUSH, '}');
mv.visitMethodInsn(INVOKEVIRTUAL, writerName, "writeTo", "(B)V", false);
}
mv.visitInsn(RETURN);
mv.visitMaxs(maxLocals, maxLocals);
mv.visitEnd();
}
{
mv = (cw.visitMethod(ACC_PUBLIC + ACC_BRIDGE + ACC_SYNTHETIC, "convertTo", "(" + jsonwriterDesc + "Ljava/lang/Object;)V", null, null));
//mv.setDebug(true);
mv.visitVarInsn(ALOAD, 0);
mv.visitVarInsn(ALOAD, 1);
mv.visitVarInsn(ALOAD, 2);
mv.visitTypeInsn(CHECKCAST, valtypeName);
mv.visitMethodInsn(INVOKEVIRTUAL, newDynName, "convertTo", "(" + jsonwriterDesc + valtypeDesc + ")V", false);
mv.visitInsn(RETURN);
mv.visitMaxs(3, 3);
mv.visitEnd();
}
cw.visitEnd();
// ------------------------------------------------------------------------------
byte[] bytes = cw.toByteArray();
Class<?> creatorClazz = new ClassLoader(loader) {
public final Class<?> loadClass(String name, byte[] b) {
return defineClass(name, b, 0, b.length);
}
}.loadClass(newDynName.replace('/', '.'), bytes);
try {
JsonDynEncoder resultEncoder = (JsonDynEncoder) creatorClazz.getDeclaredConstructor(JsonFactory.class, Type.class).newInstance(factory, clazz);
if (mixedNames != null) {
for (Map.Entry<String, AccessibleObject> en : mixedNames.entrySet()) {
Field f = creatorClazz.getDeclaredField(en.getKey() + "Encoder");
f.setAccessible(true);
f.set(resultEncoder, factory.loadEncoder(en.getValue() instanceof Field ? ((Field) en.getValue()).getGenericType() : ((Method) en.getValue()).getGenericReturnType()));
}
}
return resultEncoder;
} catch (Exception ex) {
throw new RuntimeException(ex);
}
}
@Override
public abstract void convertTo(JsonWriter out, T value);
@Override
public Type getType() {
return typeClass;
}
}

View File

@@ -6,6 +6,7 @@
package org.redkale.convert.json;
import java.io.Serializable;
import java.lang.reflect.*;
import java.math.BigInteger;
import java.net.*;
import org.redkale.convert.*;
@@ -26,7 +27,6 @@ public final class JsonFactory extends ConvertFactory<JsonReader, JsonWriter> {
private static final JsonFactory instance = new JsonFactory(null, getSystemPropertyBoolean("convert.json.tiny", "convert.tiny", true));
static {
instance.register(Serializable.class, instance.loadEncoder(Object.class));
instance.register(AnyValue.class, instance.loadDecoder(AnyValue.DefaultAnyValue.class));
@@ -63,6 +63,20 @@ public final class JsonFactory extends ConvertFactory<JsonReader, JsonWriter> {
return new JsonFactory(null, getSystemPropertyBoolean("convert.json.tiny", "convert.tiny", true));
}
@Override
protected <E> Encodeable<JsonWriter, E> createDyncEncoder(Type type) {
return JsonDynEncoder.createDyncEncoder(this, type);
}
@Override
protected <E> ObjectEncoder<JsonWriter, E> createObjectEncoder(Type type) {
return super.createObjectEncoder(type);
}
protected boolean tiny() {
return this.tiny;
}
@Override
public final JsonConvert getConvert() {
if (convert == null) convert = new JsonConvert(this, tiny);

View File

@@ -28,7 +28,6 @@ public class JsonReader extends Reader {
// public static ObjectPool<JsonReader> createPool(int max) {
// return new ObjectPool<>(max, (Object... params) -> new JsonReader(), null, JsonReader::recycle);
// }
public JsonReader() {
}
@@ -395,13 +394,21 @@ public class JsonReader extends Reader {
if (firstchar < '0' || firstchar > '9') throw new ConvertException("illegal escape(" + firstchar + ") (position = " + currpos + ") in (" + new String(this.text) + ")");
value = firstchar - '0';
}
boolean dot = false;
for (;;) {
if (currpos == eof) break;
char ch = text0[++currpos];
int val = digits[ch];
if (quote && val == -3) continue;
if (val <= -3) break;
if (val == -1) throw new ConvertException("illegal escape(" + ch + ") (position = " + currpos + ") but '" + ch + "' in (" + new String(this.text) + ")");
if (dot) continue;
if (val == -1) {
if (ch == '.') {
dot = true;
continue;
}
throw new ConvertException("illegal escape(" + ch + ") (position = " + currpos + ") but '" + ch + "' in (" + new String(this.text) + ")");
}
if (val != -2) value = value * 10 + val;
}
this.position = currpos - 1;
@@ -446,13 +453,21 @@ public class JsonReader extends Reader {
if (firstchar < '0' || firstchar > '9') throw new ConvertException("illegal escape(" + firstchar + ") (position = " + currpos + ") in (" + new String(this.text) + ")");
value = firstchar - '0';
}
boolean dot = false;
for (;;) {
if (currpos == eof) break;
char ch = text0[++currpos];
int val = digits[ch];
if (quote && val == -3) continue;
if (val <= -3) break;
if (val == -1) throw new ConvertException("illegal escape(" + ch + ") (position = " + currpos + ") but '" + ch + "' in (" + new String(this.text) + ")");
if (dot) continue;
if (val == -1) {
if (ch == '.') {
dot = true;
continue;
}
throw new ConvertException("illegal escape(" + ch + ") (position = " + currpos + ") but '" + ch + "' in (" + new String(this.text) + ")");
}
if (val != -2) value = value * 10 + val;
}
this.position = currpos - 1;

View File

@@ -14,7 +14,7 @@ import org.redkale.util.*;
/**
*
* 详情见: https://redkale.org
*
*
* @author zhangjx
*/
class JsonStreamWriter extends JsonByteBufferWriter {
@@ -64,6 +64,13 @@ class JsonStreamWriter extends JsonByteBufferWriter {
} else if (c < 0x800) {
out.write((byte) (0xc0 | (c >> 6)));
out.write((byte) (0x80 | (c & 0x3f)));
} else if (Character.isSurrogate(c)) { //连取两个
int uc = Character.toCodePoint(c, chs[i + 1]);
out.write((byte) (0xf0 | ((uc >> 18))));
out.write((byte) (0x80 | ((uc >> 12) & 0x3f)));
out.write((byte) (0x80 | ((uc >> 6) & 0x3f)));
out.write((byte) (0x80 | (uc & 0x3f)));
i++;
} else {
out.write((byte) (0xe0 | ((c >> 12))));
out.write((byte) (0x80 | ((c >> 6) & 0x3f)));

View File

@@ -6,7 +6,6 @@
package org.redkale.convert.json;
import java.lang.reflect.Type;
import java.nio.ByteBuffer;
import org.redkale.convert.*;
import org.redkale.util.*;
@@ -18,32 +17,12 @@ import org.redkale.util.*;
*
* @author zhangjx
*/
public class JsonWriter extends Writer {
public abstract class JsonWriter extends Writer {
private static final char[] CHARS_TUREVALUE = "true".toCharArray();
private static final char[] CHARS_FALSEVALUE = "false".toCharArray();
private static final int defaultSize = Integer.getInteger("convert.json.writer.buffer.defsize", Integer.getInteger("convert.writer.buffer.defsize", 1024));
private int count;
private char[] content;
protected static final int defaultSize = Integer.getInteger("convert.json.writer.buffer.defsize", Integer.getInteger("convert.writer.buffer.defsize", 1024));
protected boolean tiny;
// public static ObjectPool<JsonWriter> createPool(int max) {
// return new ObjectPool<>(max, (Object... params) -> new JsonWriter(), null, JsonWriter::recycle);
// }
public JsonWriter() {
this(defaultSize);
}
public JsonWriter(int size) {
this.content = new char[size > 128 ? size : 128];
}
@Override
public boolean tiny() {
return tiny;
@@ -54,34 +33,18 @@ public class JsonWriter extends Writer {
return this;
}
//-----------------------------------------------------------------------
//-----------------------------------------------------------------------
/**
* 返回指定至少指定长度的缓冲区
*
* @param len
*
* @return
*/
private char[] expand(int len) {
int newcount = count + len;
if (newcount <= content.length) return content;
char[] newdata = new char[Math.max(content.length * 3 / 2, newcount)];
System.arraycopy(content, 0, newdata, 0, count);
this.content = newdata;
return newdata;
public boolean isExtFuncEmpty() {
return this.objExtFunc == null && this.objFieldFunc == null;
}
public void writeTo(final char ch) { //只能是 0 - 127 的字符
expand(1);
content[count++] = ch;
}
//-----------------------------------------------------------------------
public abstract void writeTo(final char ch); //只能是 0 - 127 的字符
public void writeTo(final char[] chs, final int start, final int len) { //只能是 0 - 127 的字符
expand(len);
System.arraycopy(chs, start, content, count, len);
count += len;
}
public abstract void writeTo(final char[] chs, final int start, final int len); //只能是 0 - 127 的字符
public abstract void writeTo(final byte ch); //只能是 0 - 127 的字符
public abstract void writeTo(final byte[] chs, final int start, final int len); //只能是 0 - 127 的字符
/**
* <b>注意:</b> 该String值不能为null且不会进行转义 只用于不含需要转义字符的字符串例如enum、double、BigInteger转换的String
@@ -89,81 +52,29 @@ public class JsonWriter extends Writer {
* @param quote 是否加双引号
* @param value 非null且不含需要转义的字符的String值
*/
public void writeLatin1To(final boolean quote, final String value) {
int len = value.length();
expand(len + (quote ? 2 : 0));
if (quote) content[count++] = '"';
value.getChars(0, len, content, count);
count += len;
if (quote) content[count++] = '"';
}
public abstract void writeLatin1To(final boolean quote, final String value);
@Override
protected boolean recycle() {
super.recycle();
this.count = 0;
this.specify = null;
if (this.content != null && this.content.length > defaultSize) {
this.content = new char[defaultSize];
}
return true;
}
public ByteBuffer[] toBuffers() {
return new ByteBuffer[]{ByteBuffer.wrap(Utility.encodeUTF8(content, 0, count))};
}
public byte[] toBytes() {
return Utility.encodeUTF8(content, 0, count);
}
public int count() {
return this.count;
}
public abstract void writeBoolean(boolean value);
@Override
public void writeString(String value) {
if (value == null) {
writeNull();
return;
}
expand(value.length() * 2 + 2);
content[count++] = '"';
for (char ch : Utility.charArray(value)) {
switch (ch) {
case '\n':
content[count++] = '\\';
content[count++] = 'n';
break;
case '\r':
content[count++] = '\\';
content[count++] = 'r';
break;
case '\t':
content[count++] = '\\';
content[count++] = 't';
break;
case '\\':
content[count++] = '\\';
content[count++] = ch;
break;
case '"':
content[count++] = '\\';
content[count++] = ch;
break;
default:
content[count++] = ch;
break;
}
}
content[count++] = '"';
}
public abstract void writeInt(int value);
@Override
public final void writeFieldName(String fieldName, Type fieldType, int fieldPos) {
public abstract void writeLong(long value);
@Override
public abstract void writeString(String value);
@Override //只容许JsonBytesWriter重写此方法
public void writeFieldName(EnMember member, String fieldName, Type fieldType, int fieldPos) {
if (this.comma) writeTo(',');
writeLatin1To(true, fieldName);
writeTo(':');
if (member != null) {
writeTo(member.getJsonFieldNameChars());
} else {
writeLatin1To(true, fieldName);
writeTo(':');
}
}
@Override
@@ -171,26 +82,20 @@ public class JsonWriter extends Writer {
writeLatin1To(true, value);
}
@Override
public String toString() {
return new String(content, 0, count);
}
//----------------------------------------------------------------------------------------------
public final void writeTo(final char... chs) { //只能是 0 - 127 的字符
writeTo(chs, 0, chs.length);
}
@Override
public final void writeBoolean(boolean value) {
writeTo(value ? CHARS_TUREVALUE : CHARS_FALSEVALUE);
}
@Override
public final void writeByte(byte value) {
writeInt(value);
}
public final void writeTo(final byte[] chs) { //只能是 0 - 127 的字符
writeTo(chs, 0, chs.length);
}
@Override
public final void writeByteArray(byte[] values) {
if (values == null) {
@@ -217,101 +122,6 @@ public class JsonWriter extends Writer {
writeInt(value);
}
@Override
public void writeInt(int value) {
final char sign = value >= 0 ? 0 : '-';
if (value < 0) value = -value;
int size;
for (int i = 0;; i++) {
if (value <= sizeTable[i]) {
size = i + 1;
break;
}
}
if (sign != 0) size++; //负数
expand(size);
int q, r;
int charPos = count + size;
// Generate two digits per iteration
while (value >= 65536) {
q = value / 100;
// really: r = i - (q * 100);
r = value - ((q << 6) + (q << 5) + (q << 2));
value = q;
content[--charPos] = DigitOnes[r];
content[--charPos] = DigitTens[r];
}
// Fall thru to fast mode for smaller numbers
// assert(i <= 65536, i);
for (;;) {
q = (value * 52429) >>> (16 + 3);
r = value - ((q << 3) + (q << 1)); // r = i-(q*10) ...
content[--charPos] = digits[r];
value = q;
if (value == 0) break;
}
if (sign != 0) content[--charPos] = sign;
count += size;
}
@Override
public void writeLong(long value) {
final char sign = value >= 0 ? 0 : '-';
if (value < 0) value = -value;
int size = 19;
long p = 10;
for (int i = 1; i < 19; i++) {
if (value < p) {
size = i;
break;
}
p = 10 * p;
}
if (sign != 0) size++; //负数
expand(size);
long q;
int r;
int charPos = count + size;
// Get 2 digits/iteration using longs until quotient fits into an int
while (value > Integer.MAX_VALUE) {
q = value / 100;
// really: r = i - (q * 100);
r = (int) (value - ((q << 6) + (q << 5) + (q << 2)));
value = q;
content[--charPos] = DigitOnes[r];
content[--charPos] = DigitTens[r];
}
// Get 2 digits/iteration using ints
int q2;
int i2 = (int) value;
while (i2 >= 65536) {
q2 = i2 / 100;
// really: r = i2 - (q * 100);
r = i2 - ((q2 << 6) + (q2 << 5) + (q2 << 2));
i2 = q2;
content[--charPos] = DigitOnes[r];
content[--charPos] = DigitTens[r];
}
// Fall thru to fast mode for smaller numbers
// assert(i2 <= 65536, i2);
for (;;) {
q2 = (i2 * 52429) >>> (16 + 3);
r = i2 - ((q2 << 3) + (q2 << 1)); // r = i2-(q2*10) ...
content[--charPos] = digits[r];
i2 = q2;
if (i2 == 0) break;
}
if (sign != 0) content[--charPos] = sign;
count += size;
}
@Override
public final void writeFloat(float value) {
writeLatin1To(false, String.valueOf(value));

View File

@@ -7,13 +7,14 @@ package org.redkale.mq;
import java.net.*;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.*;
import java.util.logging.Level;
import javax.annotation.Resource;
import org.redkale.cluster.ClusterAgent;
import org.redkale.net.http.*;
import org.redkale.util.Utility;
/**
* 没有配置MQ的情况下依赖ClusterAgent实现的默认HttpMessageClient实例
@@ -28,19 +29,21 @@ import org.redkale.net.http.*;
public class HttpMessageClusterClient extends HttpMessageClient {
//jdk.internal.net.http.common.Utils.DISALLOWED_HEADERS_SET
private static final Set<String> DISALLOWED_HEADERS_SET = Set.of("connection", "content-length",
private static final Set<String> DISALLOWED_HEADERS_SET = Utility.ofSet("connection", "content-length",
"date", "expect", "from", "host", "origin",
"referer", "upgrade", "via", "warning");
protected ClusterAgent clusterAgent;
protected java.net.http.HttpClient httpClient;
@Resource(name = "cluster.httpClient")
protected HttpClient httpClient;
//protected java.net.http.HttpClient httpClient;
public HttpMessageClusterClient(ClusterAgent clusterAgent) {
super(null);
Objects.requireNonNull(clusterAgent);
this.clusterAgent = clusterAgent;
this.httpClient = java.net.http.HttpClient.newHttpClient();
//this.httpClient = java.net.http.HttpClient.newHttpClient();
}
@Override
@@ -67,16 +70,17 @@ public class HttpMessageClusterClient extends HttpMessageClient {
String resname = headers == null ? "" : headers.getOrDefault(Rest.REST_HEADER_RESOURCE_NAME, "");
return clusterAgent.queryMqtpAddress("mqtp", module, resname).thenCompose(addrmap -> {
if (addrmap == null || addrmap.isEmpty()) return new HttpResult().status(404).toAnyFuture();
java.net.http.HttpRequest.Builder builder = java.net.http.HttpRequest.newBuilder().timeout(Duration.ofMillis(30000));
if (req.isRpc()) builder.header(Rest.REST_HEADER_RPC_NAME, "true");
if (req.isFrombody()) builder.header(Rest.REST_HEADER_PARAM_FROM_BODY, "true");
if (req.getReqConvertType() != null) builder.header(Rest.REST_HEADER_REQ_CONVERT_TYPE, req.getReqConvertType().toString());
if (req.getRespConvertType() != null) builder.header(Rest.REST_HEADER_RESP_CONVERT_TYPE, req.getRespConvertType().toString());
if (userid != 0) builder.header(Rest.REST_HEADER_CURRUSERID_NAME, "" + userid);
final Map<String, String> clientHeaders = new LinkedHashMap<>();
byte[] clientBody = null;
if (req.isRpc()) clientHeaders.put(Rest.REST_HEADER_RPC_NAME, "true");
if (req.isFrombody()) clientHeaders.put(Rest.REST_HEADER_PARAM_FROM_BODY, "true");
if (req.getReqConvertType() != null) clientHeaders.put(Rest.REST_HEADER_REQ_CONVERT_TYPE, req.getReqConvertType().toString());
if (req.getRespConvertType() != null) clientHeaders.put(Rest.REST_HEADER_RESP_CONVERT_TYPE, req.getRespConvertType().toString());
if (userid != 0) clientHeaders.put(Rest.REST_HEADER_CURRUSERID_NAME, "" + userid);
if (headers != null) headers.forEach((n, v) -> {
if (!DISALLOWED_HEADERS_SET.contains(n.toLowerCase())) builder.header(n, v);
if (!DISALLOWED_HEADERS_SET.contains(n.toLowerCase())) clientHeaders.put(n, v);
});
builder.header("Content-Type", "x-www-form-urlencoded");
clientHeaders.put("Content-Type", "x-www-form-urlencoded");
if (req.getBody() != null && req.getBody().length > 0) {
String paramstr = req.getParametersToString();
if (paramstr != null) {
@@ -86,10 +90,10 @@ public class HttpMessageClusterClient extends HttpMessageClient {
req.setRequestURI(req.getRequestURI() + "?" + paramstr);
}
}
builder.POST(java.net.http.HttpRequest.BodyPublishers.ofByteArray(req.getBody()));
clientBody = req.getBody();
} else {
String paramstr = req.getParametersToString();
if (paramstr != null) builder.POST(java.net.http.HttpRequest.BodyPublishers.ofString(paramstr));
if (paramstr != null) clientBody = paramstr.getBytes(StandardCharsets.UTF_8);
}
List<CompletableFuture> futures = new ArrayList<>();
for (Map.Entry<String, Collection<InetSocketAddress>> en : addrmap.entrySet()) {
@@ -99,7 +103,7 @@ public class HttpMessageClusterClient extends HttpMessageClient {
String suburi = req.getRequestURI();
suburi = suburi.substring(1); //跳过 /
suburi = "/" + realmodule + suburi.substring(suburi.indexOf('/'));
futures.add(forEachCollectionFuture(finest, userid, req, (req.getPath() != null && !req.getPath().isEmpty() ? req.getPath() : "") + suburi, builder, addrs.iterator()));
futures.add(forEachCollectionFuture(finest, userid, req, (req.getPath() != null && !req.getPath().isEmpty() ? req.getPath() : "") + suburi, clientHeaders, clientBody, addrs.iterator()));
}
if (futures.isEmpty()) return CompletableFuture.completedFuture(null);
return CompletableFuture.allOf(futures.toArray(new CompletableFuture[futures.size()])).thenApply(v -> null);
@@ -115,16 +119,17 @@ public class HttpMessageClusterClient extends HttpMessageClient {
String resname = headers == null ? "" : headers.getOrDefault(Rest.REST_HEADER_RESOURCE_NAME, "");
return clusterAgent.queryHttpAddress("http", module, resname).thenCompose(addrs -> {
if (addrs == null || addrs.isEmpty()) return new HttpResult().status(404).toAnyFuture();
java.net.http.HttpRequest.Builder builder = java.net.http.HttpRequest.newBuilder().timeout(Duration.ofMillis(30000));
if (req.isRpc()) builder.header(Rest.REST_HEADER_RPC_NAME, "true");
if (req.isFrombody()) builder.header(Rest.REST_HEADER_PARAM_FROM_BODY, "true");
if (req.getReqConvertType() != null) builder.header(Rest.REST_HEADER_REQ_CONVERT_TYPE, req.getReqConvertType().toString());
if (req.getRespConvertType() != null) builder.header(Rest.REST_HEADER_RESP_CONVERT_TYPE, req.getRespConvertType().toString());
if (userid != 0) builder.header(Rest.REST_HEADER_CURRUSERID_NAME, "" + userid);
final Map<String, String> clientHeaders = new LinkedHashMap<>();
byte[] clientBody = null;
if (req.isRpc()) clientHeaders.put(Rest.REST_HEADER_RPC_NAME, "true");
if (req.isFrombody()) clientHeaders.put(Rest.REST_HEADER_PARAM_FROM_BODY, "true");
if (req.getReqConvertType() != null) clientHeaders.put(Rest.REST_HEADER_REQ_CONVERT_TYPE, req.getReqConvertType().toString());
if (req.getRespConvertType() != null) clientHeaders.put(Rest.REST_HEADER_RESP_CONVERT_TYPE, req.getRespConvertType().toString());
if (userid != 0) clientHeaders.put(Rest.REST_HEADER_CURRUSERID_NAME, "" + userid);
if (headers != null) headers.forEach((n, v) -> {
if (!DISALLOWED_HEADERS_SET.contains(n.toLowerCase())) builder.header(n, v);
if (!DISALLOWED_HEADERS_SET.contains(n.toLowerCase())) clientHeaders.put(n, v);
});
builder.header("Content-Type", "x-www-form-urlencoded");
clientHeaders.put("Content-Type", "x-www-form-urlencoded");
if (req.getBody() != null && req.getBody().length > 0) {
String paramstr = req.getParametersToString();
if (paramstr != null) {
@@ -134,46 +139,138 @@ public class HttpMessageClusterClient extends HttpMessageClient {
req.setRequestURI(req.getRequestURI() + "?" + paramstr);
}
}
builder.POST(java.net.http.HttpRequest.BodyPublishers.ofByteArray(req.getBody()));
clientBody = req.getBody();
} else {
String paramstr = req.getParametersToString();
if (paramstr != null) builder.POST(java.net.http.HttpRequest.BodyPublishers.ofString(paramstr));
if (paramstr != null) clientBody = paramstr.getBytes(StandardCharsets.UTF_8);
}
return forEachCollectionFuture(finest, userid, req, (req.getPath() != null && !req.getPath().isEmpty() ? req.getPath() : "") + req.getRequestURI(), builder, addrs.iterator());
return forEachCollectionFuture(finest, userid, req, (req.getPath() != null && !req.getPath().isEmpty() ? req.getPath() : "") + req.getRequestURI(), clientHeaders, clientBody, addrs.iterator());
});
}
private CompletableFuture<HttpResult<byte[]>> forEachCollectionFuture(boolean finest, int userid, HttpSimpleRequest req, String requesturi, java.net.http.HttpRequest.Builder builder, Iterator<InetSocketAddress> it) {
private CompletableFuture<HttpResult<byte[]>> forEachCollectionFuture(boolean finest, int userid, HttpSimpleRequest req, String requesturi, final Map<String, String> clientHeaders, byte[] clientBody, Iterator<InetSocketAddress> it) {
if (!it.hasNext()) return CompletableFuture.completedFuture(null);
InetSocketAddress addr = it.next();
String url = "http://" + addr.getHostString() + ":" + addr.getPort() + requesturi;
return httpClient.sendAsync(builder.copy().uri(URI.create(url)).build(), java.net.http.HttpResponse.BodyHandlers.ofByteArray()).thenCompose(resp -> {
if (resp.statusCode() != 200) return forEachCollectionFuture(finest, userid, req, requesturi, builder, it);
HttpResult rs = new HttpResult();
java.net.http.HttpHeaders hs = resp.headers();
if (hs != null) {
Map<String, List<String>> hm = hs.map();
if (hm != null) {
for (Map.Entry<String, List<String>> en : hm.entrySet()) {
if ("date".equals(en.getKey()) || "content-type".equals(en.getKey())
|| "server".equals(en.getKey()) || "connection".equals(en.getKey())) continue;
List<String> val = en.getValue();
if (val != null && val.size() == 1) {
rs.header(en.getKey(), val.get(0));
}
}
}
}
rs.setResult(resp.body());
if (finest) {
StringBuilder sb = new StringBuilder();
Map<String, String> params = req.getParams();
if (params != null && !params.isEmpty()) {
params.forEach((n, v) -> sb.append('&').append(n).append('=').append(v));
}
logger.log(Level.FINEST, url + "?userid=" + userid + sb + ", result = " + new String(resp.body(), StandardCharsets.UTF_8));
}
return CompletableFuture.completedFuture(rs);
});
return httpClient.postAsync(url, clientHeaders, clientBody);
}
// private CompletableFuture<HttpResult<byte[]>> mqtpAsync(int userid, HttpSimpleRequest req) {
// final boolean finest = logger.isLoggable(Level.FINEST);
// String module = req.getRequestURI();
// module = module.substring(1); //去掉/
// module = module.substring(0, module.indexOf('/'));
// Map<String, String> headers = req.getHeaders();
// String resname = headers == null ? "" : headers.getOrDefault(Rest.REST_HEADER_RESOURCE_NAME, "");
// return clusterAgent.queryMqtpAddress("mqtp", module, resname).thenCompose(addrmap -> {
// if (addrmap == null || addrmap.isEmpty()) return new HttpResult().status(404).toAnyFuture();
// java.net.http.HttpRequest.Builder builder = java.net.http.HttpRequest.newBuilder().timeout(Duration.ofMillis(30000));
// if (req.isRpc()) builder.header(Rest.REST_HEADER_RPC_NAME, "true");
// if (req.isFrombody()) builder.header(Rest.REST_HEADER_PARAM_FROM_BODY, "true");
// if (req.getReqConvertType() != null) builder.header(Rest.REST_HEADER_REQ_CONVERT_TYPE, req.getReqConvertType().toString());
// if (req.getRespConvertType() != null) builder.header(Rest.REST_HEADER_RESP_CONVERT_TYPE, req.getRespConvertType().toString());
// if (userid != 0) builder.header(Rest.REST_HEADER_CURRUSERID_NAME, "" + userid);
// if (headers != null) headers.forEach((n, v) -> {
// if (!DISALLOWED_HEADERS_SET.contains(n.toLowerCase())) builder.header(n, v);
// });
// builder.header("Content-Type", "x-www-form-urlencoded");
// if (req.getBody() != null && req.getBody().length > 0) {
// String paramstr = req.getParametersToString();
// if (paramstr != null) {
// if (req.getRequestURI().indexOf('?') > 0) {
// req.setRequestURI(req.getRequestURI() + "&" + paramstr);
// } else {
// req.setRequestURI(req.getRequestURI() + "?" + paramstr);
// }
// }
// builder.POST(java.net.http.HttpRequest.BodyPublishers.ofByteArray(req.getBody()));
// } else {
// String paramstr = req.getParametersToString();
// if (paramstr != null) builder.POST(java.net.http.HttpRequest.BodyPublishers.ofString(paramstr));
// }
// List<CompletableFuture> futures = new ArrayList<>();
// for (Map.Entry<String, Collection<InetSocketAddress>> en : addrmap.entrySet()) {
// String realmodule = en.getKey();
// Collection<InetSocketAddress> addrs = en.getValue();
// if (addrs == null || addrs.isEmpty()) continue;
// String suburi = req.getRequestURI();
// suburi = suburi.substring(1); //跳过 /
// suburi = "/" + realmodule + suburi.substring(suburi.indexOf('/'));
// futures.add(forEachCollectionFuture(finest, userid, req, (req.getPath() != null && !req.getPath().isEmpty() ? req.getPath() : "") + suburi, builder, addrs.iterator()));
// }
// if (futures.isEmpty()) return CompletableFuture.completedFuture(null);
// return CompletableFuture.allOf(futures.toArray(new CompletableFuture[futures.size()])).thenApply(v -> null);
// });
// }
//
// private CompletableFuture<HttpResult<byte[]>> httpAsync(int userid, HttpSimpleRequest req) {
// final boolean finest = logger.isLoggable(Level.FINEST);
// String module = req.getRequestURI();
// module = module.substring(1); //去掉/
// module = module.substring(0, module.indexOf('/'));
// Map<String, String> headers = req.getHeaders();
// String resname = headers == null ? "" : headers.getOrDefault(Rest.REST_HEADER_RESOURCE_NAME, "");
// return clusterAgent.queryHttpAddress("http", module, resname).thenCompose(addrs -> {
// if (addrs == null || addrs.isEmpty()) return new HttpResult().status(404).toAnyFuture();
// java.net.http.HttpRequest.Builder builder = java.net.http.HttpRequest.newBuilder().timeout(Duration.ofMillis(30000));
// if (req.isRpc()) builder.header(Rest.REST_HEADER_RPC_NAME, "true");
// if (req.isFrombody()) builder.header(Rest.REST_HEADER_PARAM_FROM_BODY, "true");
// if (req.getReqConvertType() != null) builder.header(Rest.REST_HEADER_REQ_CONVERT_TYPE, req.getReqConvertType().toString());
// if (req.getRespConvertType() != null) builder.header(Rest.REST_HEADER_RESP_CONVERT_TYPE, req.getRespConvertType().toString());
// if (userid != 0) builder.header(Rest.REST_HEADER_CURRUSERID_NAME, "" + userid);
// if (headers != null) headers.forEach((n, v) -> {
// if (!DISALLOWED_HEADERS_SET.contains(n.toLowerCase())) builder.header(n, v);
// });
// builder.header("Content-Type", "x-www-form-urlencoded");
// if (req.getBody() != null && req.getBody().length > 0) {
// String paramstr = req.getParametersToString();
// if (paramstr != null) {
// if (req.getRequestURI().indexOf('?') > 0) {
// req.setRequestURI(req.getRequestURI() + "&" + paramstr);
// } else {
// req.setRequestURI(req.getRequestURI() + "?" + paramstr);
// }
// }
// builder.POST(java.net.http.HttpRequest.BodyPublishers.ofByteArray(req.getBody()));
// } else {
// String paramstr = req.getParametersToString();
// if (paramstr != null) builder.POST(java.net.http.HttpRequest.BodyPublishers.ofString(paramstr));
// }
// return forEachCollectionFuture(finest, userid, req, (req.getPath() != null && !req.getPath().isEmpty() ? req.getPath() : "") + req.getRequestURI(), builder, addrs.iterator());
// });
// }
//
// private CompletableFuture<HttpResult<byte[]>> forEachCollectionFuture(boolean finest, int userid, HttpSimpleRequest req, String requesturi, java.net.http.HttpRequest.Builder builder, Iterator<InetSocketAddress> it) {
// if (!it.hasNext()) return CompletableFuture.completedFuture(null);
// InetSocketAddress addr = it.next();
// String url = "http://" + addr.getHostString() + ":" + addr.getPort() + requesturi;
// return httpClient.sendAsync(builder.copy().uri(URI.create(url)).build(), java.net.http.HttpResponse.BodyHandlers.ofByteArray()).thenCompose(resp -> {
// if (resp.statusCode() != 200) return forEachCollectionFuture(finest, userid, req, requesturi, builder, it);
// HttpResult rs = new HttpResult();
// java.net.http.HttpHeaders hs = resp.headers();
// if (hs != null) {
// Map<String, List<String>> hm = hs.map();
// if (hm != null) {
// for (Map.Entry<String, List<String>> en : hm.entrySet()) {
// if ("date".equals(en.getKey()) || "content-type".equals(en.getKey())
// || "server".equals(en.getKey()) || "connection".equals(en.getKey())) continue;
// List<String> val = en.getValue();
// if (val != null && val.size() == 1) {
// rs.header(en.getKey(), val.get(0));
// }
// }
// }
// }
// rs.setResult(resp.body());
// if (finest) {
// StringBuilder sb = new StringBuilder();
// Map<String, String> params = req.getParams();
// if (params != null && !params.isEmpty()) {
// params.forEach((n, v) -> sb.append('&').append(n).append('=').append(v));
// }
// logger.log(Level.FINEST, url + "?userid=" + userid + sb + ", result = " + new String(resp.body(), StandardCharsets.UTF_8));
// }
// return CompletableFuture.completedFuture(rs);
// });
// }
}

View File

@@ -6,13 +6,15 @@
package org.redkale.mq;
import java.util.concurrent.*;
import java.util.function.*;
import java.util.logging.*;
import org.redkale.boot.NodeHttpServer;
import org.redkale.net.http.*;
import org.redkale.service.Service;
import org.redkale.util.ThreadHashExecutor;
import org.redkale.util.ObjectPool;
/**
* 一个Service对应一个MessageProcessor
*
* <p>
* 详情见: https://redkale.org
@@ -31,14 +33,12 @@ public class HttpMessageProcessor implements MessageProcessor {
protected final Logger logger;
protected MessageClient messageClient;
protected HttpMessageClient messageClient;
protected final MessageProducers producer;
protected final MessageProducers producers;
protected final NodeHttpServer server;
protected final ThreadHashExecutor workExecutor;
protected final Service service;
protected final HttpServlet servlet;
@@ -49,6 +49,12 @@ public class HttpMessageProcessor implements MessageProcessor {
protected final String multimodule; // 前后有/, 例如: /userstat/
protected ThreadLocal<ObjectPool<HttpMessageResponse>> respPoolThreadLocal;
protected final Supplier<HttpMessageResponse> respSupplier;
protected final Consumer<HttpMessageResponse> respConsumer;
protected CountDownLatch cdl;
protected long starttime;
@@ -57,13 +63,13 @@ public class HttpMessageProcessor implements MessageProcessor {
if (cdl != null) cdl.countDown();
};
public HttpMessageProcessor(Logger logger, ThreadHashExecutor workExecutor, MessageClient messageClient, MessageProducers producer, NodeHttpServer server, Service service, HttpServlet servlet) {
public HttpMessageProcessor(Logger logger, HttpMessageClient messageClient, MessageProducers producers, NodeHttpServer server, Service service, HttpServlet servlet) {
this.logger = logger;
this.finest = logger.isLoggable(Level.FINEST);
this.finer = logger.isLoggable(Level.FINER);
this.fine = logger.isLoggable(Level.FINE);
this.messageClient = messageClient;
this.producer = producer;
this.producers = producers;
this.server = server;
this.service = service;
this.servlet = servlet;
@@ -71,22 +77,21 @@ public class HttpMessageProcessor implements MessageProcessor {
this.multiconsumer = mmc != null;
this.restmodule = "/" + Rest.getRestModule(service) + "/";
this.multimodule = mmc != null ? ("/" + mmc.module() + "/") : null;
this.workExecutor = workExecutor;
this.respSupplier = () -> respPoolThreadLocal.get().get();
this.respConsumer = resp -> respPoolThreadLocal.get().accept(resp);
this.respPoolThreadLocal = ThreadLocal.withInitial(() -> ObjectPool.createUnsafePool(Runtime.getRuntime().availableProcessors(),
ps -> new HttpMessageResponse(server.getHttpServer().getContext(), messageClient, respSupplier, respConsumer), HttpMessageResponse::prepare, HttpMessageResponse::recycle));
}
@Override
public void begin(final int size, long starttime) {
this.starttime = starttime;
if (this.workExecutor != null) this.cdl = new CountDownLatch(size);
this.cdl = new CountDownLatch(size);
}
@Override
public void process(final MessageRecord message, final Runnable callback) {
if (this.workExecutor == null) {
execute(message, innerCallback);
} else {
this.workExecutor.execute(message.hash(), () -> execute(message, innerCallback));
}
execute(message, innerCallback);
}
private void execute(final MessageRecord message, final Runnable callback) {
@@ -96,13 +101,13 @@ public class HttpMessageProcessor implements MessageProcessor {
long cha = now - message.createtime;
long e = now - starttime;
if (multiconsumer) message.setResptopic(null); //不容许有响应
HttpContext context = server.getHttpServer().getContext();
request = new HttpMessageRequest(context, message);
if (multiconsumer) {
request.setRequestURI(request.getRequestURI().replaceFirst(this.multimodule, this.restmodule));
}
HttpMessageResponse response = new HttpMessageResponse(context, request, callback, null, null, messageClient, producer.getProducer(message));
servlet.execute(request, response);
HttpMessageResponse response = respSupplier.get();
request = response.request();
response.prepare(message, callback, producers.getProducer(message));
if (multiconsumer) request.setRequestURI(request.getRequestURI().replaceFirst(this.multimodule, this.restmodule));
server.getHttpServer().getContext().execute(servlet, request, response);
long o = System.currentTimeMillis() - now;
if ((cha > 1000 || e > 100 || o > 1000) && fine) {
logger.log(Level.FINE, "HttpMessageProcessor.process (mqs.delays = " + cha + " ms, mqs.blocks = " + e + " ms, mqs.executes = " + o + " ms) message: " + message);
@@ -114,7 +119,7 @@ public class HttpMessageProcessor implements MessageProcessor {
} catch (Throwable ex) {
if (message.getResptopic() != null && !message.getResptopic().isEmpty()) {
HttpMessageResponse.finishHttpResult(finest, request == null ? null : request.getRespConvert(),
message, callback, messageClient, producer.getProducer(message), message.getResptopic(), new HttpResult().status(500));
message, callback, messageClient, producers.getProducer(message), message.getResptopic(), new HttpResult().status(500));
}
logger.log(Level.SEVERE, HttpMessageProcessor.class.getSimpleName() + " process error, message=" + message, ex instanceof CompletionException ? ((CompletionException) ex).getCause() : ex);
}
@@ -133,7 +138,7 @@ public class HttpMessageProcessor implements MessageProcessor {
}
public MessageProducers getProducer() {
return producer;
return producers;
}
public NodeHttpServer getServer() {

View File

@@ -21,10 +21,17 @@ public class HttpMessageRequest extends HttpRequest {
protected MessageRecord message;
public HttpMessageRequest(HttpContext context, MessageRecord message) {
super(context, message.decodeContent(HttpSimpleRequestCoder.getInstance()));
public HttpMessageRequest(HttpContext context) {
super(context, (HttpSimpleRequest) null);
}
protected HttpMessageRequest prepare(MessageRecord message) {
super.initSimpleRequest(message.decodeContent(HttpSimpleRequestCoder.getInstance()));
this.message = message;
this.hashid = this.message.hash();
this.currentUserid = message.getUserid();
this.createtime = System.currentTimeMillis();
return this;
}
public void setRequestURI(String uri) {
@@ -35,4 +42,16 @@ public class HttpMessageRequest extends HttpRequest {
public Convert getRespConvert() {
return this.respConvert;
}
@Override
protected void prepare() {
super.prepare();
this.keepAlive = false;
}
@Override
protected void recycle() {
super.recycle();
this.message = null;
}
}

View File

@@ -7,13 +7,14 @@ package org.redkale.mq;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.function.*;
import java.util.logging.Level;
import org.redkale.convert.*;
import org.redkale.net.Response;
import org.redkale.net.http.*;
import org.redkale.service.RetResult;
import org.redkale.util.ObjectPool;
import static org.redkale.mq.MessageRecord.CTYPE_HTTP_RESULT;
import org.redkale.net.Response;
/**
*
@@ -27,7 +28,7 @@ import static org.redkale.mq.MessageRecord.CTYPE_HTTP_RESULT;
*/
public class HttpMessageResponse extends HttpResponse {
protected MessageClient messageClient;
protected final HttpMessageClient messageClient;
protected MessageRecord message;
@@ -37,22 +38,33 @@ public class HttpMessageResponse extends HttpResponse {
protected Runnable callback;
public HttpMessageResponse(HttpContext context, HttpMessageRequest request, Runnable callback,
ObjectPool<Response> responsePool, HttpResponseConfig config, MessageClient messageClient, MessageProducer producer) {
super(context, request, responsePool, config);
this.message = request.message;
this.callback = callback;
public HttpMessageResponse(HttpContext context, HttpMessageClient messageClient, final Supplier<HttpMessageResponse> respSupplier, final Consumer<HttpMessageResponse> respConsumer) {
super(context, new HttpMessageRequest(context), null);
this.responseSupplier = (Supplier) respSupplier;
this.responseConsumer = (Consumer) respConsumer;
this.messageClient = messageClient;
}
// public HttpMessageResponse(HttpContext context, HttpMessageRequest request, Runnable callback,
// HttpResponseConfig config, HttpMessageClient messageClient, MessageProducer producer) {
// super(context, request, config);
// this.message = request.message;
// this.callback = callback;
// this.messageClient = messageClient;
// this.producer = producer;
// this.finest = producer.logger.isLoggable(Level.FINEST);
// }
public void prepare(MessageRecord message, Runnable callback, MessageProducer producer) {
((HttpMessageRequest)request).prepare(message);
this.message = message;
this.callback = callback;
this.producer = producer;
this.finest = producer.logger.isLoggable(Level.FINEST);
}
public HttpMessageResponse(HttpContext context, MessageRecord message, Runnable callback, HttpResponseConfig config, MessageClient messageClient, MessageProducer producer) {
super(context, new HttpMessageRequest(context, message), null, config);
this.message = message;
this.callback = callback;
this.messageClient = messageClient;
this.producer = producer;
public HttpMessageRequest request() {
return (HttpMessageRequest) request;
}
public void finishHttpResult(HttpResult result) {
@@ -77,6 +89,25 @@ public class HttpMessageResponse extends HttpResponse {
producer.apply(messageClient.createMessageRecord(msg.getSeqid(), CTYPE_HTTP_RESULT, resptopic, null, content));
}
@Override
protected void prepare() {
super.prepare();
}
@Override
protected boolean recycle() {
Supplier<Response> respSupplier = this.responseSupplier;
Consumer<Response> respConsumer = this.responseConsumer;
boolean rs = super.recycle();
this.responseSupplier = respSupplier;
this.responseConsumer = respConsumer;
this.message = null;
this.producer = null;
this.callback = null;
this.finest = false;
return rs;
}
@Override
public void finishJson(org.redkale.service.RetResult ret) {
if (message.isEmptyResptopic()) {
@@ -103,7 +134,7 @@ public class HttpMessageResponse extends HttpResponse {
@Override
public void finish(int status, String msg) {
if (status > 400) {
producer.logger.log(Level.INFO, "HttpMessageResponse.finish status: " + status + ", message: " + this.message);
producer.logger.log(Level.WARNING, "HttpMessageResponse.finish status: " + status + ", message: " + this.message);
} else if (finest) {
producer.logger.log(Level.FINEST, "HttpMessageResponse.finish status: " + status);
}
@@ -125,21 +156,26 @@ public class HttpMessageResponse extends HttpResponse {
}
@Override
public void finish(final byte[] bs) {
public void finish(boolean kill, final byte[] bs, int offset, int length) {
if (message.isEmptyResptopic()) {
if (callback != null) callback.run();
return;
}
finishHttpResult(new HttpResult(bs));
if (offset == 0 && bs.length == length) {
finishHttpResult(new HttpResult(bs));
} else {
finishHttpResult(new HttpResult(Arrays.copyOfRange(bs, offset, offset + length)));
}
}
@Override
public void finish(final String contentType, final byte[] bs) {
public void finish(boolean kill, final String contentType, final byte[] bs, int offset, int length) {
if (message.isEmptyResptopic()) {
if (callback != null) callback.run();
return;
}
finishHttpResult(new HttpResult(bs).contentType(contentType));
byte[] rs = (offset == 0 && bs.length == length) ? bs : Arrays.copyOfRange(bs, offset, offset + length);
finishHttpResult(new HttpResult(rs).contentType(contentType));
}
@Override

View File

@@ -40,6 +40,7 @@ public class HttpSimpleRequestCoder implements MessageCoder<HttpSimpleRequest> {
byte[] body = MessageCoder.getBytes(data.getBody());
int count = 1 //rpc
+ 1 //frombody
+ 4 //hashid
+ 4 //reqConvertType
+ 4 //respConvertType
+ 4 + requestURI.length + 2 + path.length + 2 + remoteAddr.length + 2 + sessionid.length
@@ -48,6 +49,7 @@ public class HttpSimpleRequestCoder implements MessageCoder<HttpSimpleRequest> {
ByteBuffer buffer = ByteBuffer.wrap(bs);
buffer.put((byte) (data.isRpc() ? 'T' : 'F'));
buffer.put((byte) (data.isFrombody() ? 'T' : 'F'));
buffer.putInt(data.getHashid());
buffer.putInt(data.getReqConvertType() == null ? 0 : data.getReqConvertType().getValue());
buffer.putInt(data.getRespConvertType() == null ? 0 : data.getRespConvertType().getValue());
buffer.putInt(requestURI.length);
@@ -75,6 +77,7 @@ public class HttpSimpleRequestCoder implements MessageCoder<HttpSimpleRequest> {
HttpSimpleRequest req = new HttpSimpleRequest();
req.setRpc(buffer.get() == 'T');
req.setFrombody(buffer.get() == 'T');
req.setHashid(buffer.getInt());
int reqformat = buffer.getInt();
int respformat = buffer.getInt();
if (reqformat != 0) req.setReqConvertType(ConvertType.find(reqformat));

View File

@@ -16,7 +16,7 @@ import static org.redkale.boot.Application.RESNAME_APP_NODEID;
import org.redkale.net.Servlet;
import org.redkale.net.http.*;
import org.redkale.net.sncp.*;
import org.redkale.service.Service;
import org.redkale.service.*;
import org.redkale.util.*;
/**
@@ -56,8 +56,6 @@ public abstract class MessageAgent {
protected ScheduledThreadPoolExecutor timeoutExecutor;
protected ThreadHashExecutor workExecutor;
protected int producerCount = 1;
//本地Service消息接收处理器 key:consumer
@@ -68,23 +66,16 @@ public abstract class MessageAgent {
this.httpMessageClient = new HttpMessageClient(this);
this.sncpMessageClient = new SncpMessageClient(this);
this.producerCount = config.getIntValue("producers", Runtime.getRuntime().availableProcessors());
if ("hash".equalsIgnoreCase(config.getValue("pool", "hash"))) {
this.workExecutor = new ThreadHashExecutor(Math.max(4, config.getIntValue("threads", Runtime.getRuntime().availableProcessors())));
}
// application (it doesn't execute completion handlers).
this.timeoutExecutor = (ScheduledThreadPoolExecutor) Executors.newScheduledThreadPool(1, (Runnable r) -> {
Thread t = new Thread(r);
t.setName("MessageAgent-Timeout-Thread");
t.setName("Redkale-MessageAgent-Timeout-Thread");
t.setDaemon(true);
return t;
});
this.timeoutExecutor.setRemoveOnCancelPolicy(true);
}
public boolean isHashPool() {
return this.workExecutor != null;
}
public CompletableFuture<Map<String, Long>> start() {
final LinkedHashMap<String, Long> map = new LinkedHashMap<>();
final List<CompletableFuture> futures = new ArrayList<>();
@@ -108,7 +99,6 @@ public abstract class MessageAgent {
public void destroy(AnyValue config) {
this.httpMessageClient.close().join();
this.sncpMessageClient.close().join();
this.workExecutor.shutdown();
if (this.timeoutExecutor != null) this.timeoutExecutor.shutdown();
if (this.sncpProducer != null) this.sncpProducer.shutdown().join();
if (this.httpProducer != null) this.httpProducer.shutdown().join();
@@ -126,12 +116,12 @@ public abstract class MessageAgent {
protected List<MessageProducer> getAllMessageProducer() {
List<MessageProducer> producers = new ArrayList<>();
if (this.httpProducer != null) producers.addAll(List.of(this.httpProducer.producers));
if (this.sncpProducer != null) producers.addAll(List.of(this.sncpProducer.producers));
if (this.httpProducer != null) producers.addAll(Utility.ofList(this.httpProducer.producers));
if (this.sncpProducer != null) producers.addAll(Utility.ofList(this.sncpProducer.producers));
MessageProducers one = this.httpMessageClient == null ? null : this.httpMessageClient.getProducer();
if (one != null) producers.addAll(List.of(one.producers));
if (one != null) producers.addAll(Utility.ofList(one.producers));
one = this.sncpMessageClient == null ? null : this.sncpMessageClient.getProducer();
if (one != null) producers.addAll(List.of(one.producers));
if (one != null) producers.addAll(Utility.ofList(one.producers));
return producers;
}
@@ -224,18 +214,22 @@ public abstract class MessageAgent {
public abstract MessageConsumer createConsumer(String[] topics, String group, MessageProcessor processor);
public final synchronized void putService(NodeHttpServer ns, Service service, HttpServlet servlet) {
AutoLoad al = service.getClass().getAnnotation(AutoLoad.class);
if (al != null && !al.value() && service.getClass().getAnnotation(Local.class) != null) return;
String[] topics = generateHttpReqTopics(service);
String consumerid = generateHttpConsumerid(topics, service);
if (messageNodes.containsKey(consumerid)) throw new RuntimeException("consumerid(" + consumerid + ") is repeat");
HttpMessageProcessor processor = new HttpMessageProcessor(this.logger, this.workExecutor, httpMessageClient, getHttpProducer(), ns, service, servlet);
HttpMessageProcessor processor = new HttpMessageProcessor(this.logger, httpMessageClient, getHttpProducer(), ns, service, servlet);
this.messageNodes.put(consumerid, new MessageConsumerNode(ns, service, servlet, processor, createConsumer(topics, consumerid, processor)));
}
public final synchronized void putService(NodeSncpServer ns, Service service, SncpServlet servlet) {
AutoLoad al = service.getClass().getAnnotation(AutoLoad.class);
if (al != null && !al.value() && service.getClass().getAnnotation(Local.class) != null) return;
String topic = generateSncpReqTopic(service);
String consumerid = generateSncpConsumerid(topic, service);
if (messageNodes.containsKey(consumerid)) throw new RuntimeException("consumerid(" + consumerid + ") is repeat");
SncpMessageProcessor processor = new SncpMessageProcessor(this.logger, this.workExecutor, sncpMessageClient, getSncpProducer(), ns, service, servlet);
SncpMessageProcessor processor = new SncpMessageProcessor(this.logger, sncpMessageClient, getSncpProducer(), ns, service, servlet);
this.messageNodes.put(consumerid, new MessageConsumerNode(ns, service, servlet, processor, createConsumer(new String[]{topic}, consumerid, processor)));
}

View File

@@ -79,11 +79,11 @@ public abstract class MessageClient {
node.future.complete(msg);
long cha = now - msg.createtime;
if (cha > 1000 && fine) {
messageAgent.logger.log(Level.FINE, clazzName + ".MessageRespFutureNode.process (mqs.delays = " + cha + "ms, mqs.counters = " + ncer + ") mqresp.msg: " + msg);
messageAgent.logger.log(Level.FINE, clazzName + ".MessageRespFutureNode.process (mqs.delays = " + cha + "ms, mqs.counters = " + ncer + ") mqresp.msg: " + formatRespMessage(msg));
} else if (cha > 50 && finer) {
messageAgent.logger.log(Level.FINER, clazzName + ".MessageRespFutureNode.process (mq.delays = " + cha + "ms, mq.counters = " + ncer + ") mqresp.msg: " + msg);
messageAgent.logger.log(Level.FINER, clazzName + ".MessageRespFutureNode.process (mq.delays = " + cha + "ms, mq.counters = " + ncer + ") mqresp.msg: " + formatRespMessage(msg));
} else if (finest) {
messageAgent.logger.log(Level.FINEST, clazzName + ".MessageRespFutureNode.process (mq.delay = " + cha + "ms, mq.counter = " + ncer + ") mqresp.msg: " + msg);
messageAgent.logger.log(Level.FINEST, clazzName + ".MessageRespFutureNode.process (mq.delay = " + cha + "ms, mq.counter = " + ncer + ") mqresp.msg: " + formatRespMessage(msg));
}
};
MessageConsumer one = messageAgent.createConsumer(new String[]{respTopic}, respConsumerid, processor);
@@ -114,6 +114,10 @@ public abstract class MessageClient {
}
}
protected MessageRecord formatRespMessage(MessageRecord message) {
return message;
}
protected abstract MessageProducers getProducer();
public MessageRecord createMessageRecord(String resptopic, String content) {

View File

@@ -6,6 +6,7 @@
package org.redkale.mq;
/**
* 一个Service对应一个MessageProcessor
*
* <p>
* 详情见: https://redkale.org

View File

@@ -8,7 +8,10 @@ package org.redkale.mq;
import java.io.Serializable;
import java.nio.charset.StandardCharsets;
import org.redkale.convert.*;
import org.redkale.convert.bson.BsonConvert;
import org.redkale.convert.json.JsonConvert;
import org.redkale.net.http.HttpSimpleRequest;
import org.redkale.net.sncp.SncpRequest;
import org.redkale.util.Comment;
/**
@@ -33,6 +36,8 @@ public class MessageRecord implements Serializable {
protected static final byte CTYPE_HTTP_RESULT = 3;
protected static final byte CTYPE_BSON_RESULT = 4;
@ConvertColumn(index = 1)
@Comment("消息序列号")
protected long seqid;
@@ -274,8 +279,17 @@ public class MessageRecord implements Serializable {
if (this.topic != null) sb.append(",\"topic\":\"").append(this.topic).append("\"");
if (this.resptopic != null) sb.append(",\"resptopic\":\"").append(this.resptopic).append("\"");
if (this.content != null) {
if (this.ctype == CTYPE_HTTP_REQUEST) {
sb.append(",\"content\":").append(HttpSimpleRequestCoder.getInstance().decode(this.content));
if (this.ctype == CTYPE_BSON_RESULT && this.content.length > SncpRequest.HEADER_SIZE) {
int offset = SncpRequest.HEADER_SIZE + 1; //循环占位符
Object rs = BsonConvert.root().convertFrom(Object.class, this.content, offset, this.content.length - offset);
sb.append(",\"content\":").append(rs);
} else if (this.ctype == CTYPE_HTTP_REQUEST) {
HttpSimpleRequest req = HttpSimpleRequestCoder.getInstance().decode(this.content);
if (req != null) {
if (req.getCurrentUserid() == 0) req.setCurrentUserid(this.userid);
if (req.getHashid() == 0) req.setHashid(this.hash());
}
sb.append(",\"content\":").append(req);
} else if (this.ctype == CTYPE_HTTP_RESULT) {
sb.append(",\"content\":").append(HttpResultCoder.getInstance().decode(this.content));
} else if (localattach != null) {

View File

@@ -53,4 +53,9 @@ public class SncpMessageClient extends MessageClient {
return sendMessage(message, true, counter);
}
@Override
protected MessageRecord formatRespMessage(MessageRecord message) {
if (message != null) message.ctype = MessageRecord.CTYPE_BSON_RESULT;
return message;
}
}

View File

@@ -10,9 +10,9 @@ import java.util.logging.*;
import org.redkale.boot.NodeSncpServer;
import org.redkale.net.sncp.*;
import org.redkale.service.Service;
import org.redkale.util.ThreadHashExecutor;
/**
* 一个Service对应一个MessageProcessor
*
* <p>
* 详情见: https://redkale.org
@@ -37,8 +37,6 @@ public class SncpMessageProcessor implements MessageProcessor {
protected final NodeSncpServer server;
protected final ThreadHashExecutor workExecutor;
protected final Service service;
protected final SncpServlet servlet;
@@ -51,7 +49,7 @@ public class SncpMessageProcessor implements MessageProcessor {
if (cdl != null) cdl.countDown();
};
public SncpMessageProcessor(Logger logger, ThreadHashExecutor workExecutor, MessageClient messageClient, MessageProducers producer, NodeSncpServer server, Service service, SncpServlet servlet) {
public SncpMessageProcessor(Logger logger, SncpMessageClient messageClient, MessageProducers producer, NodeSncpServer server, Service service, SncpServlet servlet) {
this.logger = logger;
this.finest = logger.isLoggable(Level.FINEST);
this.finer = logger.isLoggable(Level.FINER);
@@ -61,22 +59,17 @@ public class SncpMessageProcessor implements MessageProcessor {
this.server = server;
this.service = service;
this.servlet = servlet;
this.workExecutor = workExecutor;
}
@Override
public void begin(final int size, long starttime) {
this.starttime = starttime;
if (this.workExecutor != null) this.cdl = new CountDownLatch(size);
this.cdl = new CountDownLatch(size);
}
@Override
public void process(final MessageRecord message, final Runnable callback) {
if (this.workExecutor == null) {
execute(message, innerCallback);
} else {
this.workExecutor.execute(message.hash(), () -> execute(message, innerCallback));
}
execute(message, innerCallback);
}
private void execute(final MessageRecord message, final Runnable callback) {
@@ -87,15 +80,16 @@ public class SncpMessageProcessor implements MessageProcessor {
long e = now - starttime;
SncpContext context = server.getSncpServer().getContext();
SncpMessageRequest request = new SncpMessageRequest(context, message);
response = new SncpMessageResponse(context, request, callback, null, messageClient, producer.getProducer(message));
servlet.execute(request, response);
response = new SncpMessageResponse(context, request, callback, messageClient, producer.getProducer(message));
context.execute(servlet, request, response);
long o = System.currentTimeMillis() - now;
if ((cha > 1000 || e > 100 || o > 1000) && fine) {
logger.log(Level.FINE, "SncpMessageProcessor.process (mqs.delays = " + cha + " ms, mqs.blocks = " + e + " ms, mqs.executes = " + o + " ms, works=" + (workExecutor != null ? workExecutor.size() : 0) + ") message: " + message);
logger.log(Level.FINE, "SncpMessageProcessor.process (mqs.delays = " + cha + " ms, mqs.blocks = " + e + " ms, mqs.executes = " + o + " ms) message: " + message);
} else if ((cha > 50 || e > 10 || o > 50) && finer) {
logger.log(Level.FINER, "SncpMessageProcessor.process (mq.delays = " + cha + " ms, mq.blocks = " + e + " ms, mq.executes = " + o + " ms, works=" + (workExecutor != null ? workExecutor.size() : 0) + ") message: " + message);
logger.log(Level.FINER, "SncpMessageProcessor.process (mq.delays = " + cha + " ms, mq.blocks = " + e + " ms, mq.executes = " + o + " ms) message: " + message);
} else if (finest) {
logger.log(Level.FINEST, "SncpMessageProcessor.process (mq.delay = " + cha + " ms, mq.block = " + e + " ms, mq.execute = " + o + " ms, works=" + (workExecutor != null ? workExecutor.size() : 0) + ") message: " + message);
logger.log(Level.FINEST, "SncpMessageProcessor.process (mq.delay = " + cha + " ms, mq.block = " + e + " ms, mq.execute = " + o + " ms) message: " + message);
}
} catch (Throwable ex) {
if (response != null) response.finish(SncpResponse.RETCODE_ILLSERVICEID, null);

View File

@@ -28,7 +28,9 @@ public class SncpMessageRequest extends SncpRequest {
public SncpMessageRequest(SncpContext context, MessageRecord message) {
super(context);
this.message = message;
readHeader(ByteBuffer.wrap(message.getContent()));
this.hashid = this.message.hash();
this.createtime = System.currentTimeMillis();
readHeader(ByteBuffer.wrap(message.getContent()), null);
}
@Override //被SncpAsyncHandler.sncp_setParams调用

View File

@@ -5,12 +5,10 @@
*/
package org.redkale.mq;
import java.nio.ByteBuffer;
import org.redkale.convert.bson.BsonWriter;
import org.redkale.net.Response;
import org.redkale.net.sncp.*;
import static org.redkale.net.sncp.SncpRequest.HEADER_SIZE;
import org.redkale.util.ObjectPool;
import org.redkale.util.ByteArray;
/**
*
@@ -31,16 +29,16 @@ public class SncpMessageResponse extends SncpResponse {
protected Runnable callback;
public SncpMessageResponse(SncpContext context, SncpMessageRequest request, Runnable callback, ObjectPool<Response> responsePool, MessageClient messageClient, MessageProducer producer) {
super(context, request, responsePool);
public SncpMessageResponse(SncpContext context, SncpMessageRequest request, Runnable callback, MessageClient messageClient, MessageProducer producer) {
super(context, request);
this.message = request.message;
this.callback = callback;
this.messageClient = messageClient;
this.producer = producer;
}
public SncpMessageResponse(SncpContext context, MessageRecord message, Runnable callback, ObjectPool<Response> responsePool, MessageClient messageClient, MessageProducer producer) {
super(context, new SncpMessageRequest(context, message), responsePool);
public SncpMessageResponse(SncpContext context, MessageRecord message, Runnable callback, MessageClient messageClient, MessageProducer producer) {
super(context, new SncpMessageRequest(context, message));
this.message = message;
this.callback = callback;
this.messageClient = messageClient;
@@ -51,14 +49,14 @@ public class SncpMessageResponse extends SncpResponse {
public void finish(final int retcode, final BsonWriter out) {
if (callback != null) callback.run();
if (out == null) {
final byte[] result = new byte[SncpRequest.HEADER_SIZE];
fillHeader(ByteBuffer.wrap(result), 0, retcode);
final ByteArray result = new ByteArray(SncpRequest.HEADER_SIZE);
fillHeader(result, 0, retcode);
producer.apply(messageClient.createMessageRecord(message.getSeqid(), message.getResptopic(), null, (byte[]) null));
return;
}
final int respBodyLength = out.count(); //body总长度
final byte[] result = out.toArray();
fillHeader(ByteBuffer.wrap(result), respBodyLength - HEADER_SIZE, retcode);
producer.apply(messageClient.createMessageRecord(message.getSeqid(), message.getResptopic(), null, result));
final ByteArray result = out.toByteArray();
fillHeader(result, respBodyLength - HEADER_SIZE, retcode);
producer.apply(messageClient.createMessageRecord(message.getSeqid(), message.getResptopic(), null, result.getBytes()));
}
}

View File

@@ -22,7 +22,8 @@ import javax.net.ssl.SSLContext;
*
* @author zhangjx
*/
class AioTcpAsyncConnection extends AsyncConnection {
@Deprecated //@since 2.3.0
class AsyncAioTcpConnection extends AsyncConnection {
//private final Semaphore semaphore = new Semaphore(1);
private int readTimeoutSeconds;
@@ -35,11 +36,11 @@ class AioTcpAsyncConnection extends AsyncConnection {
private BlockingQueue<WriteEntry> writeQueue;
public AioTcpAsyncConnection(Supplier<ByteBuffer> bufferSupplier, Consumer<ByteBuffer> bufferConsumer,
public AsyncAioTcpConnection(int bufferCapacity, Supplier<ByteBuffer> bufferSupplier, Consumer<ByteBuffer> bufferConsumer,
final AsynchronousSocketChannel ch, final SSLContext sslContext, final SocketAddress addr0,
final int readTimeoutSeconds, final int writeTimeoutSeconds,
final AtomicLong livingCounter, final AtomicLong closedCounter) {
super(bufferSupplier, bufferConsumer, sslContext, livingCounter, closedCounter);
super(false, bufferCapacity, bufferSupplier, bufferConsumer, sslContext, livingCounter, closedCounter);
this.channel = ch;
this.readTimeoutSeconds = readTimeoutSeconds;
this.writeTimeoutSeconds = writeTimeoutSeconds;
@@ -135,17 +136,6 @@ class AioTcpAsyncConnection extends AsyncConnection {
}
private <A> void write(boolean acquire, ByteBuffer src, A attachment, CompletionHandler<Integer, ? super A> handler) {
// if (acquire && !semaphore.tryAcquire()) {
// if (this.writeQueue == null) {
// synchronized (semaphore) {
// if (this.writeQueue == null) {
// this.writeQueue = new LinkedBlockingDeque<>();
// }
// }
// }
// this.writeQueue.add(new WriteEntry(src, attachment, handler));
// return;
// }
WriteOneCompletionHandler newHandler = new WriteOneCompletionHandler(src, handler);
if (!channel.isOpen()) {
newHandler.failed(new ClosedChannelException(), attachment);
@@ -282,6 +272,8 @@ class AioTcpAsyncConnection extends AsyncConnection {
@Override
public final void close() throws IOException {
super.close();
channel.shutdownInput();
channel.shutdownOutput();
channel.close();
BlockingQueue<WriteEntry> queue = this.writeQueue;
if (queue == null) return;
@@ -306,6 +298,11 @@ class AioTcpAsyncConnection extends AsyncConnection {
return true;
}
@Override
protected void continueRead() {
throw new UnsupportedOperationException("Not supported yet.");
}
private class WriteMoreCompletionHandler<A> implements CompletionHandler<Long, A> {
private final CompletionHandler<Integer, A> writeHandler;

View File

@@ -10,8 +10,9 @@ import java.net.*;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Set;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.*;
import java.util.logging.Level;
import org.redkale.boot.Application;
import org.redkale.util.*;
/**
@@ -22,20 +23,36 @@ import org.redkale.util.*;
*
* @author zhangjx
*/
public class AioTcpProtocolServer extends ProtocolServer {
@Deprecated //@since 2.3.0
class AsyncAioTcpProtocolServer extends ProtocolServer {
//创建数
private final AtomicLong createCounter = new AtomicLong();
//关闭数
private final AtomicLong closedCounter = new AtomicLong();
//在线数
private final AtomicLong livingCounter = new AtomicLong();
private AsynchronousChannelGroup group;
private AsynchronousServerSocketChannel serverChannel;
public AioTcpProtocolServer(Context context) {
public AsyncAioTcpProtocolServer(Context context) {
super(context);
}
@Override
public void open(AnyValue config) throws IOException {
//group = AsynchronousChannelGroup.withThreadPool(context.executor);
group = AsynchronousChannelGroup.withFixedThreadPool(context.executor.getCorePoolSize(), context.executor.getThreadFactory());
final AtomicInteger workcounter = new AtomicInteger();
group = AsynchronousChannelGroup.withFixedThreadPool(Runtime.getRuntime().availableProcessors(), (Runnable r) -> {
int c = workcounter.incrementAndGet();
String threadname = "Redkale-AioThread-" + (c > 9 ? c : ("0" + c));
Thread t = new WorkThread(threadname, application.getWorkExecutor(), r);
return t;
});
this.serverChannel = AsynchronousServerSocketChannel.open(group);
final Set<SocketOption<?>> options = this.serverChannel.supportedOptions();
@@ -72,7 +89,7 @@ public class AioTcpProtocolServer extends ProtocolServer {
}
@Override
public void accept(Server server) throws IOException {
public void accept(Application application, Server server) throws IOException {
AtomicLong createBufferCounter = new AtomicLong();
AtomicLong cycleBufferCounter = new AtomicLong();
ObjectPool<ByteBuffer> bufferPool = server.createBufferPool(createBufferCounter, cycleBufferCounter, server.bufferPoolSize);
@@ -101,10 +118,9 @@ public class AioTcpProtocolServer extends ProtocolServer {
channel.setOption(StandardSocketOptions.SO_RCVBUF, 16 * 1024);
channel.setOption(StandardSocketOptions.SO_SNDBUF, 16 * 1024);
AsyncConnection conn = new AioTcpAsyncConnection(bufferPool, bufferPool, channel,
AsyncConnection conn = new AsyncAioTcpConnection(server.bufferCapacity, bufferPool, bufferPool, channel,
context.getSSLContext(), null, context.readTimeoutSeconds, context.writeTimeoutSeconds, livingCounter, closedCounter);
//context.runAsync(new PrepareRunner(context, responsePool, conn, null, null));
new PrepareRunner(context, responsePool, conn, null, null).run();
new ProtocolCodec(context, responsePool, responsePool, conn).run(null);
} catch (Throwable e) {
context.logger.log(Level.INFO, channel + " accept error", e);
}
@@ -118,6 +134,21 @@ public class AioTcpProtocolServer extends ProtocolServer {
});
}
@Override
public long getCreateConnectionCount() {
return this.createCounter.get();
}
@Override
public long getClosedConnectionCount() {
return this.closedCounter.get();
}
@Override
public long getLivingConnectionCount() {
return this.livingCounter.get();
}
@Override
public void close() throws IOException {
this.serverChannel.close();

View File

@@ -10,7 +10,6 @@ import java.net.*;
import java.nio.*;
import java.nio.channels.*;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.*;
import java.util.function.*;
import javax.net.ssl.SSLContext;
@@ -35,10 +34,18 @@ public abstract class AsyncConnection implements AutoCloseable {
protected volatile long writetime;
protected final boolean client;
protected final int bufferCapacity;
private final Supplier<ByteBuffer> bufferSupplier;
private final Consumer<ByteBuffer> bufferConsumer;
private ByteBufferWriter pipelineWriter;
private PipelineDataNode pipelineDataNode;
private ByteBuffer readBuffer;
//在线数
@@ -52,15 +59,20 @@ public abstract class AsyncConnection implements AutoCloseable {
//关联的事件数, 小于1表示没有事件
private final AtomicInteger eventing = new AtomicInteger();
protected AsyncConnection(ObjectPool<ByteBuffer> bufferPool, SSLContext sslContext,
//用于服务端的Socket, 等同于一直存在的readCompletionHandler
ProtocolCodec protocolCodec;
protected AsyncConnection(boolean client, final int bufferCapacity, ObjectPool<ByteBuffer> bufferPool, SSLContext sslContext,
final AtomicLong livingCounter, final AtomicLong closedCounter) {
this(bufferPool, bufferPool, sslContext, livingCounter, closedCounter);
this(client, bufferCapacity, bufferPool, bufferPool, sslContext, livingCounter, closedCounter);
}
protected AsyncConnection(Supplier<ByteBuffer> bufferSupplier, Consumer<ByteBuffer> bufferConsumer,
protected AsyncConnection(boolean client, final int bufferCapacity, Supplier<ByteBuffer> bufferSupplier, Consumer<ByteBuffer> bufferConsumer,
SSLContext sslContext, final AtomicLong livingCounter, final AtomicLong closedCounter) {
Objects.requireNonNull(bufferSupplier);
Objects.requireNonNull(bufferConsumer);
this.client = client;
this.bufferCapacity = bufferCapacity;
this.bufferSupplier = bufferSupplier;
this.bufferConsumer = bufferConsumer;
this.sslContext = sslContext;
@@ -92,6 +104,8 @@ public abstract class AsyncConnection implements AutoCloseable {
return eventing.decrementAndGet();
}
protected abstract void continueRead();
public abstract boolean isOpen();
public abstract boolean isTCP();
@@ -118,16 +132,77 @@ public abstract class AsyncConnection implements AutoCloseable {
public abstract ReadableByteChannel readableByteChannel();
public abstract void read(CompletionHandler<Integer, ByteBuffer> handler);
public abstract WritableByteChannel writableByteChannel();
public abstract void read(CompletionHandler<Integer, ByteBuffer> handler);
//src会写完才会回调
public abstract <A> void write(ByteBuffer src, A attachment, CompletionHandler<Integer, ? super A> handler);
//srcs会写完才会回调
public final <A> void write(ByteBuffer[] srcs, A attachment, CompletionHandler<Integer, ? super A> handler) {
write(srcs, 0, srcs.length, attachment, handler);
}
public final void write(byte[] bytes, CompletionHandler<Integer, Void> handler) {
write(bytes, 0, bytes.length, null, 0, 0, handler);
}
public final void write(ByteTuple array, CompletionHandler<Integer, Void> handler) {
write(array.content(), array.offset(), array.length(), null, 0, 0, handler);
}
public final void write(byte[] bytes, int offset, int length, CompletionHandler<Integer, Void> handler) {
write(bytes, offset, length, null, 0, 0, handler);
}
public final void write(ByteTuple header, ByteTuple body, CompletionHandler<Integer, Void> handler) {
write(header.content(), header.offset(), header.length(), body == null ? null : body.content(), body == null ? 0 : body.offset(), body == null ? 0 : body.length(), handler);
}
public void write(byte[] headerContent, int headerOffset, int headerLength, byte[] bodyContent, int bodyOffset, int bodyLength, CompletionHandler<Integer, Void> handler) {
final ByteBuffer buffer = pollWriteBuffer();
if (buffer.remaining() >= headerLength + bodyLength) {
buffer.put(headerContent, headerOffset, headerLength);
if (bodyLength > 0) buffer.put(bodyContent, bodyOffset, bodyLength);
buffer.flip();
CompletionHandler<Integer, Void> newhandler = new CompletionHandler<Integer, Void>() {
@Override
public void completed(Integer result, Void attachment) {
offerBuffer(buffer);
handler.completed(result, attachment);
}
@Override
public void failed(Throwable exc, Void attachment) {
offerBuffer(buffer);
handler.failed(exc, attachment);
}
};
write(buffer, null, newhandler);
} else {
ByteBufferWriter writer = ByteBufferWriter.create(bufferSupplier, buffer);
writer.put(headerContent, headerOffset, headerLength);
if (bodyLength > 0) writer.put(bodyContent, bodyOffset, bodyLength);
final ByteBuffer[] buffers = writer.toBuffers();
CompletionHandler<Integer, Void> newhandler = new CompletionHandler<Integer, Void>() {
@Override
public void completed(Integer result, Void attachment) {
offerBuffer(buffers);
handler.completed(result, attachment);
}
@Override
public void failed(Throwable exc, Void attachment) {
offerBuffer(buffers);
handler.failed(exc, attachment);
}
};
write(buffers, null, newhandler);
}
}
//srcs会写完才会回调
public abstract <A> void write(ByteBuffer[] srcs, int offset, int length, A attachment, CompletionHandler<Integer, ? super A> handler);
public void setReadBuffer(ByteBuffer buffer) {
@@ -135,11 +210,139 @@ public abstract class AsyncConnection implements AutoCloseable {
this.readBuffer = buffer;
}
public static class Message {
public boolean hasPipelineData() {
ByteBufferWriter writer = this.pipelineWriter;
return writer != null && writer.position() > 0;
}
public String value;
public final void flushPipelineData(CompletionHandler<Integer, Void> handler) {
flushPipelineData(null, handler);
}
public Message() {
public <A> void flushPipelineData(A attachment, CompletionHandler<Integer, ? super A> handler) {
ByteBufferWriter writer = this.pipelineWriter;
this.pipelineWriter = null;
if (writer == null) {
handler.completed(0, attachment);
} else {
ByteBuffer[] srcs = writer.toBuffers();
CompletionHandler<Integer, ? super A> newhandler = new CompletionHandler<Integer, A>() {
@Override
public void completed(Integer result, A attachment) {
offerBuffer(srcs);
handler.completed(result, attachment);
}
@Override
public void failed(Throwable exc, A attachment) {
offerBuffer(srcs);
handler.failed(exc, attachment);
}
};
if (srcs.length == 1) {
write(srcs[0], attachment, newhandler);
} else {
write(srcs, attachment, newhandler);
}
}
}
//返回 是否over
public final boolean writePipelineData(int pipelineIndex, int pipelineCount, ByteTuple array) {
return writePipelineData(pipelineIndex, pipelineCount, array.content(), array.offset(), array.length());
}
//返回 是否over
public boolean writePipelineData(int pipelineIndex, int pipelineCount, byte[] bs, int offset, int length) {
synchronized (this) {
ByteBufferWriter writer = this.pipelineWriter;
if (writer == null) {
writer = ByteBufferWriter.create(getBufferSupplier());
this.pipelineWriter = writer;
}
if (this.pipelineDataNode == null && pipelineIndex == writer.getWriteBytesCounter() + 1) {
writer.put(bs, offset, length);
return (pipelineIndex == pipelineCount);
} else {
PipelineDataNode dataNode = this.pipelineDataNode;
if (dataNode == null) {
dataNode = new PipelineDataNode();
this.pipelineDataNode = dataNode;
}
if (pipelineIndex == pipelineCount) { //此时pipelineCount为最大值
dataNode.pipelineCount = pipelineCount;
}
dataNode.put(pipelineIndex, bs, offset, length);
if (writer.getWriteBytesCounter() + dataNode.itemsize == dataNode.pipelineCount) {
for (PipelineDataItem item : dataNode.arrayItems()) {
writer.put(item.data);
}
this.pipelineDataNode = null;
return true;
}
return false;
}
}
}
private static class PipelineDataNode {
public int pipelineCount;
public int itemsize;
private PipelineDataItem head;
private PipelineDataItem tail;
public PipelineDataItem[] arrayItems() {
PipelineDataItem[] items = new PipelineDataItem[itemsize];
PipelineDataItem item = head;
int i = 0;
while (item != null) {
items[i] = item;
item = item.next;
items[i].next = null;
i++;
}
Arrays.sort(items);
return items;
}
public void put(int pipelineIndex, byte[] bs, int offset, int length) {
if (tail == null) {
head = new PipelineDataItem(pipelineIndex, bs, offset, length);
tail = head;
} else {
PipelineDataItem item = new PipelineDataItem(pipelineIndex, bs, offset, length);
tail.next = item;
tail = item;
}
itemsize++;
}
}
private static class PipelineDataItem implements Comparable<PipelineDataItem> {
final byte[] data;
final int index;
public PipelineDataItem next;
public PipelineDataItem(int index, byte[] bs, int offset, int length) {
this.index = index;
this.data = Arrays.copyOfRange(bs, offset, offset + length);
}
@Override
public int compareTo(PipelineDataItem o) {
return this.index - o.index;
}
@Override
public String toString() {
return "{\"index\":" + index + "}";
}
}
@@ -199,11 +402,7 @@ public abstract class AsyncConnection implements AutoCloseable {
}
if (this.readBuffer != null) {
Consumer<ByteBuffer> consumer = this.bufferConsumer;
// Thread thread = Thread.currentThread();
// if (thread instanceof IOThread) {
// consumer = ((IOThread) thread).getBufferPool();
// }
consumer.accept(this.readBuffer);
if (consumer != null) consumer.accept(this.readBuffer);
}
if (attributes == null) return;
try {
@@ -245,155 +444,4 @@ public abstract class AsyncConnection implements AutoCloseable {
if (this.attributes != null) this.attributes.clear();
}
//------------------------------------------------------------------------------------------------------------------------------
/**
* 创建TCP协议客户端连接
*
* @param bufferPool ByteBuffer对象池
* @param address 连接点子
* @param group 连接AsynchronousChannelGroup
* @param readTimeoutSeconds 读取超时秒数
* @param writeTimeoutSeconds 写入超时秒数
*
* @return 连接CompletableFuture
*/
public static CompletableFuture<AsyncConnection> createTCP(final ObjectPool<ByteBuffer> bufferPool, final AsynchronousChannelGroup group,
final SocketAddress address, final int readTimeoutSeconds, final int writeTimeoutSeconds) {
return createTCP(bufferPool, group, null, address, readTimeoutSeconds, writeTimeoutSeconds);
}
/**
* 创建TCP协议客户端连接
*
* @param bufferPool ByteBuffer对象池
* @param address 连接点子
* @param sslContext SSLContext
* @param group 连接AsynchronousChannelGroup
* @param readTimeoutSeconds 读取超时秒数
* @param writeTimeoutSeconds 写入超时秒数
*
* @return 连接CompletableFuture
*/
public static CompletableFuture<AsyncConnection> createTCP(final ObjectPool<ByteBuffer> bufferPool, final AsynchronousChannelGroup group, final SSLContext sslContext,
final SocketAddress address, final int readTimeoutSeconds, final int writeTimeoutSeconds) {
return createTCP(bufferPool, bufferPool, group, sslContext, address, readTimeoutSeconds, writeTimeoutSeconds);
}
/**
* 创建TCP协议客户端连接
*
* @param bufferSupplier ByteBuffer生产器
* @param bufferConsumer ByteBuffer回收器
* @param address 连接点子
* @param sslContext SSLContext
* @param group 连接AsynchronousChannelGroup
* @param readTimeoutSeconds 读取超时秒数
* @param writeTimeoutSeconds 写入超时秒数
*
* @return 连接CompletableFuture
*/
public static CompletableFuture<AsyncConnection> createTCP(final Supplier<ByteBuffer> bufferSupplier, Consumer<ByteBuffer> bufferConsumer, final AsynchronousChannelGroup group, final SSLContext sslContext,
final SocketAddress address, final int readTimeoutSeconds, final int writeTimeoutSeconds) {
final CompletableFuture<AsyncConnection> future = new CompletableFuture<>();
try {
final AsynchronousSocketChannel channel = AsynchronousSocketChannel.open(group);
try {
channel.setOption(StandardSocketOptions.TCP_NODELAY, true);
channel.setOption(StandardSocketOptions.SO_KEEPALIVE, true);
channel.setOption(StandardSocketOptions.SO_REUSEADDR, true);
} catch (IOException e) {
}
channel.connect(address, null, new CompletionHandler<Void, Void>() {
@Override
public void completed(Void result, Void attachment) {
future.complete(new AioTcpAsyncConnection(bufferSupplier, bufferConsumer, channel, sslContext, address, readTimeoutSeconds, writeTimeoutSeconds, null, null));
}
@Override
public void failed(Throwable exc, Void attachment) {
future.completeExceptionally(exc);
}
});
} catch (IOException e) {
future.completeExceptionally(e);
}
return future;
}
// public static AsyncConnection create(final Socket socket) {
// return create(socket, null, 0, 0);
// }
// public static AsyncConnection create(final Socket socket, final SocketAddress addr0, final int readTimeoutSecond0, final int writeTimeoutSecond0) {
// return new TcpBioAsyncConnection(socket, addr0, readTimeoutSecond0, writeTimeoutSecond0, null, null);
// }
//
// public static AsyncConnection create(final Socket socket, final SocketAddress addr0, final int readTimeoutSecond0,
// final int writeTimeoutSecond0, final AtomicLong livingCounter, final AtomicLong closedCounter) {
// return new TcpBioAsyncConnection(socket, addr0, readTimeoutSecond0, writeTimeoutSecond0, livingCounter, closedCounter);
// }
//
// public static AsyncConnection create(final SocketChannel ch, SocketAddress addr, final Selector selector,
// final int readTimeoutSeconds0, final int writeTimeoutSeconds0) {
// return new TcpNioAsyncConnection(ch, addr, selector, readTimeoutSeconds0, writeTimeoutSeconds0, null, null);
// }
//
// public static AsyncConnection create(final SocketChannel ch, final SocketAddress addr0, final Selector selector, final Context context) {
// return new TcpNioAsyncConnection(ch, addr0, selector, context.readTimeoutSeconds, context.writeTimeoutSeconds, null, null);
// }
//
// public static AsyncConnection create(final SocketChannel ch, SocketAddress addr, final Selector selector,
// final int readTimeoutSeconds0, final int writeTimeoutSeconds0,
// final AtomicLong livingCounter, final AtomicLong closedCounter) {
// return new TcpNioAsyncConnection(ch, addr, selector, readTimeoutSeconds0, writeTimeoutSeconds0, livingCounter, closedCounter);
// }
public static AsyncConnection create(final ObjectPool<ByteBuffer> bufferPool, final DatagramChannel ch,
SocketAddress addr, final boolean client0,
final int readTimeoutSeconds0, final int writeTimeoutSeconds0) {
return new BioUdpAsyncConnection(bufferPool, bufferPool, ch, null, addr, client0, readTimeoutSeconds0, writeTimeoutSeconds0, null, null);
}
public static AsyncConnection create(final ObjectPool<ByteBuffer> bufferPool, final DatagramChannel ch,
SocketAddress addr, final boolean client0,
final int readTimeoutSeconds0, final int writeTimeoutSeconds0,
final AtomicLong livingCounter, final AtomicLong closedCounter) {
return new BioUdpAsyncConnection(bufferPool, bufferPool, ch, null, addr, client0, readTimeoutSeconds0, writeTimeoutSeconds0, livingCounter, closedCounter);
}
public static AsyncConnection create(final ObjectPool<ByteBuffer> bufferPool, final DatagramChannel ch, SSLContext sslContext,
SocketAddress addr, final boolean client0,
final int readTimeoutSeconds0, final int writeTimeoutSeconds0) {
return new BioUdpAsyncConnection(bufferPool, bufferPool, ch, sslContext, addr, client0, readTimeoutSeconds0, writeTimeoutSeconds0, null, null);
}
public static AsyncConnection create(final ObjectPool<ByteBuffer> bufferPool, final DatagramChannel ch, SSLContext sslContext,
SocketAddress addr, final boolean client0,
final int readTimeoutSeconds0, final int writeTimeoutSeconds0,
final AtomicLong livingCounter, final AtomicLong closedCounter) {
return new BioUdpAsyncConnection(bufferPool, bufferPool, ch, sslContext, addr, client0, readTimeoutSeconds0, writeTimeoutSeconds0, livingCounter, closedCounter);
}
public static AsyncConnection create(final ObjectPool<ByteBuffer> bufferPool, final AsynchronousSocketChannel ch) {
return create(bufferPool, ch, null, 0, 0);
}
public static AsyncConnection create(final ObjectPool<ByteBuffer> bufferPool, final AsynchronousSocketChannel ch,
final SocketAddress addr0, final int readTimeoutSeconds, final int writeTimeoutSeconds) {
return new AioTcpAsyncConnection(bufferPool, bufferPool, ch, null, addr0, readTimeoutSeconds, writeTimeoutSeconds, null, null);
}
public static AsyncConnection create(final ObjectPool<ByteBuffer> bufferPool, final AsynchronousSocketChannel ch, SSLContext sslContext,
final SocketAddress addr0, final int readTimeoutSeconds, final int writeTimeoutSeconds) {
return new AioTcpAsyncConnection(bufferPool, bufferPool, ch, sslContext, addr0, readTimeoutSeconds, writeTimeoutSeconds, null, null);
}
public static AsyncConnection create(final ObjectPool<ByteBuffer> bufferPool, final AsynchronousSocketChannel ch,
final SocketAddress addr0, final int readTimeoutSeconds, final int writeTimeoutSeconds, final AtomicLong livingCounter, final AtomicLong closedCounter) {
return new AioTcpAsyncConnection(bufferPool, bufferPool, ch, null, addr0, readTimeoutSeconds, writeTimeoutSeconds, livingCounter, closedCounter);
}
public static AsyncConnection create(final ObjectPool<ByteBuffer> bufferPool, final AsynchronousSocketChannel ch, SSLContext sslContext,
final SocketAddress addr0, final int readTimeoutSeconds, final int writeTimeoutSeconds, final AtomicLong livingCounter, final AtomicLong closedCounter) {
return new AioTcpAsyncConnection(bufferPool, bufferPool, ch, sslContext, addr0, readTimeoutSeconds, writeTimeoutSeconds, livingCounter, closedCounter);
}
}

View File

@@ -0,0 +1,44 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.net;
import java.net.SocketAddress;
import java.util.concurrent.*;
/**
* Client模式的AsyncConnection连接构造器
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*
* @since 2.3.0
*/
public abstract class AsyncGroup {
public CompletableFuture<AsyncConnection> createTCP(final SocketAddress address) {
return createTCP(address, 0, 0);
}
public abstract CompletableFuture<AsyncConnection> createTCP(final SocketAddress address, final int readTimeoutSeconds, final int writeTimeoutSeconds);
public CompletableFuture<AsyncConnection> createUDP(final SocketAddress address) {
return createUDP(address, 0, 0);
}
public abstract CompletableFuture<AsyncConnection> createUDP(final SocketAddress address, final int readTimeoutSeconds, final int writeTimeoutSeconds);
public CompletableFuture<AsyncConnection> create(final boolean tcp, final SocketAddress address) {
return tcp ? createTCP(address) : createUDP(address);
}
public CompletableFuture<AsyncConnection> create(final boolean tcp, final SocketAddress address, final int readTimeoutSeconds, final int writeTimeoutSeconds) {
return tcp ? createTCP(address, readTimeoutSeconds, writeTimeoutSeconds) : createUDP(address, readTimeoutSeconds, writeTimeoutSeconds);
}
public abstract ScheduledFuture scheduleTimeout(Runnable callable, long delay, TimeUnit unit);
}

View File

@@ -0,0 +1,217 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.net;
import java.io.IOException;
import java.net.*;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.*;
import org.redkale.util.*;
/**
* 协议处理的IO线程组
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*
* @since 2.1.0
*/
@ResourceType(AsyncGroup.class)
public class AsyncIOGroup extends AsyncGroup {
private boolean started;
private boolean closed;
AsyncIOThread[] ioThreads;
private AsyncIOThread connectThread;
final int bufferCapacity;
private final AtomicInteger readIndex = new AtomicInteger();
//创建数
final AtomicLong connCreateCounter = new AtomicLong();
//关闭数
final AtomicLong connLivingCounter = new AtomicLong();
//在线数
final AtomicLong connClosedCounter = new AtomicLong();
private ScheduledThreadPoolExecutor timeoutExecutor;
public AsyncIOGroup(final int bufferCapacity, final int bufferPoolSize) {
this(null, Runtime.getRuntime().availableProcessors(), bufferCapacity, bufferPoolSize);
}
public AsyncIOGroup(final ExecutorService workExecutor,
final int iothreads, final int bufferCapacity, final int bufferPoolSize) {
this(null, workExecutor, iothreads, bufferCapacity, ObjectPool.createSafePool(null, null, bufferPoolSize,
(Object... params) -> ByteBuffer.allocateDirect(bufferCapacity), null, (e) -> {
if (e == null || e.isReadOnly() || e.capacity() != bufferCapacity) return false;
e.clear();
return true;
}));
}
public AsyncIOGroup(String threadPrefixName0, ExecutorService workExecutor,
int iothreads, final int bufferCapacity, ObjectPool<ByteBuffer> safeBufferPool) {
this.bufferCapacity = bufferCapacity;
final String threadPrefixName = threadPrefixName0 == null ? "Redkale-Client-IOThread" : threadPrefixName0;
this.ioThreads = new AsyncIOThread[Math.max(iothreads, 1)];
try {
for (int i = 0; i < this.ioThreads.length; i++) {
ObjectPool<ByteBuffer> unsafeBufferPool = ObjectPool.createUnsafePool(safeBufferPool, safeBufferPool.getCreatCounter(),
safeBufferPool.getCycleCounter(), 512, safeBufferPool.getCreator(), safeBufferPool.getPrepare(), safeBufferPool.getRecycler());
String name = threadPrefixName + "-" + (i >= 9 ? (i + 1) : ("0" + (i + 1)));
this.ioThreads[i] = new AsyncIOThread(true, name, workExecutor, Selector.open(), unsafeBufferPool, safeBufferPool);
}
{
ObjectPool<ByteBuffer> unsafeBufferPool = ObjectPool.createUnsafePool(safeBufferPool, safeBufferPool.getCreatCounter(),
safeBufferPool.getCycleCounter(), 512, safeBufferPool.getCreator(), safeBufferPool.getPrepare(), safeBufferPool.getRecycler());
String name = threadPrefixName.replace("ServletThread", "ConnectThread").replace("Redkale-Client-IOThread", "Redkale-Client-ConnectThread");
this.connectThread = new AsyncIOThread(false, name, workExecutor, Selector.open(), unsafeBufferPool, safeBufferPool);
}
} catch (IOException e) {
throw new RuntimeException(e);
}
this.timeoutExecutor = (ScheduledThreadPoolExecutor) Executors.newScheduledThreadPool(1, (Runnable r) -> {
Thread t = new Thread(r);
t.setName(threadPrefixName + "-Timeout");
t.setDaemon(true);
return t;
});
}
public int size() {
return this.ioThreads.length;
}
public void start() {
if (started) return;
if (closed) throw new RuntimeException("group is closed");
for (AsyncIOThread thread : ioThreads) {
thread.start();
}
connectThread.start();
started = true;
}
public void close() {
if (closed) return;
for (AsyncIOThread thread : ioThreads) {
thread.close();
}
connectThread.close();
this.timeoutExecutor.shutdownNow();
closed = true;
}
public AtomicLong getCreateConnectionCount() {
return connCreateCounter;
}
public AtomicLong getClosedConnectionCount() {
return connLivingCounter;
}
public AtomicLong getLivingConnectionCount() {
return connClosedCounter;
}
public AsyncIOThread nextIOThread() {
return ioThreads[Math.abs(readIndex.getAndIncrement()) % ioThreads.length];
}
public AsyncIOThread connectThread() {
return connectThread;
}
@Override
public ScheduledFuture scheduleTimeout(Runnable callable, long delay, TimeUnit unit) {
return timeoutExecutor.schedule(callable, delay, unit);
}
public void interestOpsOr(AsyncIOThread thread, SelectionKey key, int opt) {
if (key == null) return;
if (key.selector() != thread.selector) throw new RuntimeException("NioThread.selector not the same to SelectionKey.selector");
if ((key.interestOps() & opt) != 0) return;
key.interestOps(key.interestOps() | opt);
if (thread.inCurrThread()) return;
//非IO线程中
key.selector().wakeup();
}
@Override
public CompletableFuture<AsyncConnection> createTCP(final SocketAddress address, final int readTimeoutSeconds, final int writeTimeoutSeconds) {
SocketChannel channel;
try {
channel = SocketChannel.open();
channel.configureBlocking(false);
channel.setOption(StandardSocketOptions.TCP_NODELAY, true);
channel.setOption(StandardSocketOptions.SO_KEEPALIVE, true);
channel.setOption(StandardSocketOptions.SO_REUSEADDR, true);
} catch (IOException e) {
CompletableFuture future = new CompletableFuture();
future.completeExceptionally(e);
return future;
}
AsyncIOThread readThread = nextIOThread();
final AsyncNioTcpConnection conn = new AsyncNioTcpConnection(true, this, readThread, connectThread, channel, null, address, connLivingCounter, connClosedCounter);
final CompletableFuture<AsyncConnection> future = new CompletableFuture<>();
conn.connect(address, null, new CompletionHandler<Void, Void>() {
@Override
public void completed(Void result, Void attachment) {
conn.setReadTimeoutSeconds(readTimeoutSeconds);
conn.setWriteTimeoutSeconds(writeTimeoutSeconds);
connCreateCounter.incrementAndGet();
connLivingCounter.incrementAndGet();
future.complete(conn);
}
@Override
public void failed(Throwable exc, Void attachment) {
future.completeExceptionally(exc);
}
});
return Utility.orTimeout(future, 30, TimeUnit.SECONDS);
}
@Override
public CompletableFuture<AsyncConnection> createUDP(final SocketAddress address, final int readTimeoutSeconds, final int writeTimeoutSeconds) {
DatagramChannel channel;
try {
channel = DatagramChannel.open();
} catch (IOException e) {
CompletableFuture future = new CompletableFuture();
future.completeExceptionally(e);
return future;
}
AsyncIOThread readThread = nextIOThread();
AsyncNioUdpConnection conn = new AsyncNioUdpConnection(true, this, readThread, connectThread, channel, null, address, connLivingCounter, connClosedCounter);
CompletableFuture future = new CompletableFuture();
conn.connect(address, null, new CompletionHandler<Void, Void>() {
@Override
public void completed(Void result, Void attachment) {
future.complete(conn);
}
@Override
public void failed(Throwable exc, Void attachment) {
future.completeExceptionally(exc);
}
});
return future;
}
}

View File

@@ -0,0 +1,133 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.net;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.*;
import java.util.logging.*;
import org.redkale.util.*;
/**
* 协议处理的IO线程类
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*
* @since 2.1.0
*/
public class AsyncIOThread extends WorkThread {
protected static final Logger logger = Logger.getLogger(AsyncIOThread.class.getSimpleName());
final Selector selector;
final AtomicInteger connCounter = new AtomicInteger();
private final Supplier<ByteBuffer> bufferSupplier;
private final Consumer<ByteBuffer> bufferConsumer;
private final ConcurrentLinkedQueue<Consumer<Selector>> registers = new ConcurrentLinkedQueue<>();
private boolean closed;
int invoker = 0;
public AsyncIOThread(final boolean readable, String name, ExecutorService workExecutor, Selector selector,
ObjectPool<ByteBuffer> unsafeBufferPool, ObjectPool<ByteBuffer> safeBufferPool) {
super(name, workExecutor, null);
this.selector = selector;
this.setDaemon(true);
this.bufferSupplier = () -> (inCurrThread() ? unsafeBufferPool : safeBufferPool).get();
this.bufferConsumer = (v) -> (inCurrThread() ? unsafeBufferPool : safeBufferPool).accept(v);
}
public void register(Consumer<Selector> consumer) {
registers.offer(consumer);
selector.wakeup();
}
public Supplier<ByteBuffer> getBufferSupplier() {
return bufferSupplier;
}
public Consumer<ByteBuffer> getBufferConsumer() {
return bufferConsumer;
}
public int currConnections() {
return connCounter.get();
}
@Override
public void run() {
this.localThread = Thread.currentThread();
while (!this.closed) {
try {
Consumer<Selector> register;
while ((register = registers.poll()) != null) {
register.accept(selector);
}
int count = selector.select();
if (count == 0) continue;
Set<SelectionKey> keys = selector.selectedKeys();
Iterator<SelectionKey> it = keys.iterator();
while (it.hasNext()) {
SelectionKey key = it.next();
it.remove();
invoker = 0;
if (!key.isValid()) continue;
AsyncNioConnection conn = (AsyncNioConnection) key.attachment();
if (conn.client) {
if (key.isConnectable()) {
key.interestOps(key.interestOps() & ~SelectionKey.OP_CONNECT);
conn.doConnect();
} else if (key.isReadable()) {
conn.currReadInvoker = 0;
key.interestOps(key.interestOps() & ~SelectionKey.OP_READ);
conn.doRead(true);
} else if (key.isWritable()) {
conn.currWriteInvoker = 0;
key.interestOps(key.interestOps() & ~SelectionKey.OP_WRITE);
conn.doWrite(true);
}
} else {
if (key.isReadable()) {
conn.currReadInvoker = 0;
//key.interestOps(key.interestOps() & ~SelectionKey.OP_READ);
conn.doRead(true);
} else if (conn.writeCompletionHandler != null && key.isWritable()) {
conn.currWriteInvoker = 0;
key.interestOps(key.interestOps() & ~SelectionKey.OP_WRITE);
conn.doWrite(true);
} else if (key.isConnectable()) {
key.interestOps(key.interestOps() & ~SelectionKey.OP_CONNECT);
conn.doConnect();
}
}
}
} catch (Exception ex) {
if (!this.closed) logger.log(Level.FINE, getName() + " selector run failed", ex);
}
}
}
public void close() {
this.closed = true;
this.interrupt();
try {
this.selector.close();
} catch (Exception e) {
logger.log(Level.FINE, getName() + " selector close failed", e);
}
}
}

View File

@@ -5,6 +5,7 @@
*/
package org.redkale.net;
import java.nio.ByteBuffer;
import java.nio.channels.CompletionHandler;
import java.util.concurrent.*;
@@ -17,7 +18,7 @@ import java.util.concurrent.*;
*
* @since 2.1.0
*/
public class NioCompletionHandler<A> implements CompletionHandler<Integer, A>, Runnable {
class AsyncNioCompletionHandler<A> implements CompletionHandler<Integer, A>, Runnable {
private final CompletionHandler<Integer, A> handler;
@@ -25,11 +26,20 @@ public class NioCompletionHandler<A> implements CompletionHandler<Integer, A>, R
public ScheduledFuture timeoutFuture;
public NioCompletionHandler(CompletionHandler<Integer, A> handler, A attachment) {
private ByteBuffer[] buffers;
private AsyncNioConnection conn;
public AsyncNioCompletionHandler(CompletionHandler<Integer, A> handler, A attachment) {
this.handler = handler;
this.attachment = attachment;
}
public void setConnBuffers(AsyncNioConnection conn, ByteBuffer... buffs) {
this.conn = conn;
this.buffers = buffs;
}
public void setAttachment(A attachment) {
this.attachment = attachment;
}
@@ -41,6 +51,9 @@ public class NioCompletionHandler<A> implements CompletionHandler<Integer, A>, R
this.timeoutFuture = null;
future.cancel(true);
}
if (conn != null && buffers != null) {
conn.offerBuffer(buffers);
}
handler.completed(result, attachment);
}
@@ -51,11 +64,17 @@ public class NioCompletionHandler<A> implements CompletionHandler<Integer, A>, R
this.timeoutFuture = null;
future.cancel(true);
}
if (conn != null && buffers != null) {
conn.offerBuffer(buffers);
}
handler.failed(exc, attachment);
}
@Override
public void run() {
if (conn != null && buffers != null) {
conn.offerBuffer(buffers);
}
handler.failed(new TimeoutException(), attachment);
}

View File

@@ -0,0 +1,450 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.net;
import java.io.IOException;
import java.net.SocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.*;
import javax.net.ssl.SSLContext;
import org.redkale.util.*;
/**
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*
* @since 2.3.0
*/
abstract class AsyncNioConnection extends AsyncConnection {
protected static final int MAX_INVOKER_ONSTACK = Integer.getInteger("net.invoker.max.onstack", 16);
final AsyncIOThread ioThread;
final AsyncIOThread connectThread;
final AsyncIOGroup ioGroup;
protected SocketAddress remoteAddress;
//-------------------------------- 连操作 --------------------------------------
protected Object connectAttachment;
protected CompletionHandler<Void, Object> connectCompletionHandler;
protected boolean connectPending;
protected SelectionKey connectKey;
//-------------------------------- 读操作 --------------------------------------
protected int readTimeoutSeconds;
int currReadInvoker;
protected ByteBuffer readByteBuffer;
protected CompletionHandler<Integer, ByteBuffer> readCompletionHandler;
protected boolean readPending;
protected SelectionKey readKey;
//-------------------------------- 写操作 --------------------------------------
protected int writeTimeoutSeconds;
int currWriteInvoker;
protected byte[] writeByteTuple1Array;
protected int writeByteTuple1Offset;
protected int writeByteTuple1Length;
protected byte[] writeByteTuple2Array;
protected int writeByteTuple2Offset;
protected int writeByteTuple2Length;
//写操作, 二选一要么writeByteBuffer有值要么writeByteBuffers、writeOffset、writeLength有值
protected ByteBuffer writeByteBuffer;
protected ByteBuffer[] writeByteBuffers;
protected int writeOffset;
protected int writeLength;
protected Object writeAttachment;
protected CompletionHandler<Integer, Object> writeCompletionHandler;
protected boolean writePending;
protected SelectionKey writeKey;
public AsyncNioConnection(boolean client, AsyncIOGroup ioGroup, AsyncIOThread ioThread, AsyncIOThread connectThread,
final int bufferCapacity, ObjectPool<ByteBuffer> bufferPool, SSLContext sslContext, AtomicLong livingCounter, AtomicLong closedCounter) {
super(client, bufferCapacity, bufferPool, sslContext, livingCounter, closedCounter);
this.ioGroup = ioGroup;
this.ioThread = ioThread;
this.connectThread = connectThread;
}
public AsyncNioConnection(boolean client, AsyncIOGroup ioGroup, AsyncIOThread ioThread, AsyncIOThread connectThread,
final int bufferCapacity, Supplier<ByteBuffer> bufferSupplier, Consumer<ByteBuffer> bufferConsumer, SSLContext sslContext, AtomicLong livingCounter, AtomicLong closedCounter) {
super(client, bufferCapacity, bufferSupplier, bufferConsumer, sslContext, livingCounter, closedCounter);
this.ioGroup = ioGroup;
this.ioThread = ioThread;
this.connectThread = connectThread;
}
@Override
public SocketAddress getRemoteAddress() {
return remoteAddress;
}
@Override
public void setReadTimeoutSeconds(int readTimeoutSeconds) {
this.readTimeoutSeconds = readTimeoutSeconds;
}
@Override
public void setWriteTimeoutSeconds(int writeTimeoutSeconds) {
this.writeTimeoutSeconds = writeTimeoutSeconds;
}
@Override
public int getReadTimeoutSeconds() {
return this.readTimeoutSeconds;
}
@Override
public int getWriteTimeoutSeconds() {
return this.writeTimeoutSeconds;
}
@Override
protected void continueRead() {
if (readKey == null) {
ioThread.register(selector -> {
try {
readKey = implRegister(selector, SelectionKey.OP_READ);
readKey.attach(this);
} catch (ClosedChannelException e) {
handleRead(0, e);
}
});
} else {
ioGroup.interestOpsOr(ioThread, readKey, SelectionKey.OP_READ);
}
}
@Override
public void read(CompletionHandler<Integer, ByteBuffer> handler) {
Objects.requireNonNull(handler);
if (!this.isConnected()) {
handler.failed(new NotYetConnectedException(), null);
return;
}
if (this.readPending) {
handler.failed(new ReadPendingException(), null);
return;
}
this.readPending = true;
if (this.readTimeoutSeconds > 0) {
AsyncNioCompletionHandler newhandler = new AsyncNioCompletionHandler(handler, this.readByteBuffer);
this.readCompletionHandler = newhandler;
newhandler.timeoutFuture = ioGroup.scheduleTimeout(newhandler, this.readTimeoutSeconds, TimeUnit.SECONDS);
} else {
this.readCompletionHandler = handler;
}
doRead(currReadInvoker < MAX_INVOKER_ONSTACK && this.ioThread.inCurrThread());
}
@Override
public void write(byte[] headerContent, int headerOffset, int headerLength, byte[] bodyContent, int bodyOffset, int bodyLength, CompletionHandler<Integer, Void> handler) {
Objects.requireNonNull(headerContent);
Objects.requireNonNull(handler);
if (!this.isConnected()) {
handler.failed(new NotYetConnectedException(), null);
return;
}
if (this.writePending) {
handler.failed(new WritePendingException(), null);
return;
}
this.writePending = true;
this.writeByteTuple1Array = headerContent;
this.writeByteTuple1Offset = headerOffset;
this.writeByteTuple1Length = headerLength;
this.writeByteTuple2Array = bodyContent;
this.writeByteTuple2Offset = bodyOffset;
this.writeByteTuple2Length = bodyLength;
this.writeAttachment = null;
if (this.writeTimeoutSeconds > 0) {
AsyncNioCompletionHandler newhandler = new AsyncNioCompletionHandler(handler, null);
this.writeCompletionHandler = newhandler;
newhandler.timeoutFuture = ioGroup.scheduleTimeout(newhandler, this.writeTimeoutSeconds, TimeUnit.SECONDS);
} else {
this.writeCompletionHandler = new AsyncNioCompletionHandler(handler, null);
}
doWrite(true || !client || currWriteInvoker < MAX_INVOKER_ONSTACK); // this.ioThread.inCurrThread() // !client && ioThread.workExecutor == null
}
@Override
public <A> void write(ByteBuffer src, A attachment, CompletionHandler<Integer, ? super A> handler) {
Objects.requireNonNull(src);
Objects.requireNonNull(handler);
if (!this.isConnected()) {
handler.failed(new NotYetConnectedException(), attachment);
return;
}
if (this.writePending) {
handler.failed(new WritePendingException(), attachment);
return;
}
this.writePending = true;
this.writeByteBuffer = src;
this.writeAttachment = attachment;
if (this.writeTimeoutSeconds > 0) {
AsyncNioCompletionHandler newhandler = new AsyncNioCompletionHandler(handler, attachment);
this.writeCompletionHandler = newhandler;
newhandler.timeoutFuture = ioGroup.scheduleTimeout(newhandler, this.writeTimeoutSeconds, TimeUnit.SECONDS);
} else {
this.writeCompletionHandler = (CompletionHandler) handler;
}
doWrite(true || !client || currWriteInvoker < MAX_INVOKER_ONSTACK); // this.ioThread.inCurrThread() // !client && ioThread.workExecutor == null
}
@Override
public <A> void write(ByteBuffer[] srcs, int offset, int length, A attachment, CompletionHandler<Integer, ? super A> handler) {
Objects.requireNonNull(srcs);
Objects.requireNonNull(handler);
if (!this.isConnected()) {
handler.failed(new NotYetConnectedException(), attachment);
return;
}
if (this.writePending) {
handler.failed(new WritePendingException(), attachment);
return;
}
this.writePending = true;
this.writeByteBuffers = srcs;
this.writeOffset = offset;
this.writeLength = length;
this.writeAttachment = attachment;
if (this.writeTimeoutSeconds > 0) {
AsyncNioCompletionHandler newhandler = new AsyncNioCompletionHandler(handler, attachment);
this.writeCompletionHandler = newhandler;
newhandler.timeoutFuture = ioGroup.scheduleTimeout(newhandler, this.writeTimeoutSeconds, TimeUnit.SECONDS);
} else {
this.writeCompletionHandler = (CompletionHandler) handler;
}
doWrite(true || !client || currWriteInvoker < MAX_INVOKER_ONSTACK); // this.ioThread.inCurrThread() // !client && ioThread.workExecutor == null
}
public void doRead(boolean direct) {
try {
this.readtime = System.currentTimeMillis();
int readCount = 0;
if (direct) {
currReadInvoker++;
if (this.readByteBuffer == null) {
this.readByteBuffer = pollReadBuffer();
if (this.readTimeoutSeconds > 0 && this.readCompletionHandler != null) {
((AsyncNioCompletionHandler) this.readCompletionHandler).setAttachment(this.readByteBuffer);
}
}
readCount = implRead(readByteBuffer);
}
if (readCount != 0) {
handleRead(readCount, null);
} else if (readKey == null) {
ioThread.register(selector -> {
try {
readKey = implRegister(selector, SelectionKey.OP_READ);
readKey.attach(this);
} catch (ClosedChannelException e) {
handleRead(0, e);
}
});
} else {
if (client || !direct) ioGroup.interestOpsOr(ioThread, readKey, SelectionKey.OP_READ);
}
} catch (Exception e) {
handleRead(0, e);
}
}
public void doWrite(boolean direct) {
try {
this.writetime = System.currentTimeMillis();
final boolean invokeDirect = direct;
int totalCount = 0;
boolean hasRemain = true;
if (invokeDirect) currWriteInvoker++;
while (invokeDirect && hasRemain) {
if (writeByteTuple1Array != null) {
final ByteBuffer buffer = pollWriteBuffer();
if (buffer.remaining() >= writeByteTuple1Length + writeByteTuple2Length) {
buffer.put(writeByteTuple1Array, writeByteTuple1Offset, writeByteTuple1Length);
if (writeByteTuple2Length > 0) buffer.put(writeByteTuple2Array, writeByteTuple2Offset, writeByteTuple2Length);
buffer.flip();
writeByteBuffer = buffer;
writeByteTuple1Array = null;
writeByteTuple1Offset = 0;
writeByteTuple1Length = 0;
writeByteTuple2Array = null;
writeByteTuple2Offset = 0;
writeByteTuple2Length = 0;
} else {
ByteBufferWriter writer = ByteBufferWriter.create(getBufferSupplier(), buffer);
writer.put(writeByteTuple1Array, writeByteTuple1Offset, writeByteTuple1Length);
if (writeByteTuple2Length > 0) writer.put(writeByteTuple2Array, writeByteTuple2Offset, writeByteTuple2Length);
final ByteBuffer[] buffers = writer.toBuffers();
writeByteBuffers = buffers;
writeOffset = 0;
writeLength = buffers.length;
writeByteTuple1Array = null;
writeByteTuple1Offset = 0;
writeByteTuple1Length = 0;
writeByteTuple2Array = null;
writeByteTuple2Offset = 0;
writeByteTuple2Length = 0;
}
if (writeByteBuffer == null) {
((AsyncNioCompletionHandler) writeCompletionHandler).setConnBuffers(this, writeByteBuffers);
} else {
((AsyncNioCompletionHandler) writeCompletionHandler).setConnBuffers(this, writeByteBuffer);
}
}
int writeCount;
if (writeByteBuffer != null) {
writeCount = implWrite(writeByteBuffer);
hasRemain = writeByteBuffer.hasRemaining();
} else {
writeCount = implWrite(writeByteBuffers, writeOffset, writeLength);
boolean remain = false;
for (int i = writeByteBuffers.length - 1; i >= writeOffset; i--) {
if (writeByteBuffers[i].hasRemaining()) {
remain = true;
break;
}
}
hasRemain = remain;
}
if (writeCount <= 0) {
if (totalCount == 0) totalCount = writeCount;
break;
} else {
totalCount += writeCount;
}
if (!hasRemain) break;
}
if (totalCount != 0 || !hasRemain) {
handleWrite(totalCount, null);
} else if (writeKey == null) {
ioThread.register(selector -> {
try {
writeKey = implRegister(selector, SelectionKey.OP_WRITE);
writeKey.attach(this);
} catch (ClosedChannelException e) {
handleWrite(0, e);
}
});
} else {
ioGroup.interestOpsOr(ioThread, writeKey, SelectionKey.OP_WRITE);
}
} catch (IOException e) {
handleWrite(0, e);
}
}
protected void handleConnect(Throwable t) {
if (connectKey != null) {
connectKey.cancel();
connectKey = null;
}
CompletionHandler handler = this.connectCompletionHandler;
Object attach = this.connectAttachment;
this.connectCompletionHandler = null;
this.connectAttachment = null;
this.connectPending = false;//必须放最后
if (handler != null) {
if (t == null) {
handler.completed(null, attach);
} else {
handler.failed(t, attach);
}
}
}
protected void handleRead(final int totalCount, Throwable t) {
CompletionHandler<Integer, ByteBuffer> handler = this.readCompletionHandler;
ByteBuffer attach = this.readByteBuffer;
//清空读参数
this.readCompletionHandler = null;
this.readByteBuffer = null;
this.readPending = false; //必须放最后
if (handler == null) {
if (t == null) {
protocolCodec.completed(totalCount, attach);
} else {
protocolCodec.failed(t, attach);
}
} else {
if (t == null) {
handler.completed(totalCount, attach);
} else {
handler.failed(t, attach);
}
}
}
protected void handleWrite(final int totalCount, Throwable t) {
CompletionHandler<Integer, Object> handler = this.writeCompletionHandler;
Object attach = this.writeAttachment;
//清空写参数
this.writeCompletionHandler = null;
this.writeAttachment = null;
this.writeByteBuffer = null;
this.writeByteBuffers = null;
this.writeOffset = 0;
this.writeLength = 0;
this.writePending = false; //必须放最后
if (t == null) {
handler.completed(totalCount, attach);
} else {
handler.failed(t, attach);
}
}
protected abstract SelectionKey implRegister(Selector sel, int ops) throws ClosedChannelException;
protected abstract int implRead(ByteBuffer dst) throws IOException;
protected abstract int implWrite(ByteBuffer src) throws IOException;
protected abstract int implWrite(ByteBuffer[] srcs, int offset, int length) throws IOException;
public abstract boolean isConnected();
public abstract void doConnect();
}

View File

@@ -0,0 +1,201 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.net;
import java.io.*;
import java.net.*;
import java.nio.*;
import java.nio.channels.*;
import java.util.*;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.*;
import javax.net.ssl.SSLContext;
/**
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*
* @since 2.1.0
*/
class AsyncNioTcpConnection extends AsyncNioConnection {
private final SocketChannel channel;
public AsyncNioTcpConnection(boolean client, AsyncIOGroup ioGroup, AsyncIOThread ioThread, AsyncIOThread connectThread,
SocketChannel ch, SSLContext sslContext, final SocketAddress addr0, AtomicLong livingCounter, AtomicLong closedCounter) {
super(client, ioGroup, ioThread, connectThread, ioGroup.bufferCapacity, ioThread.getBufferSupplier(), ioThread.getBufferConsumer(), sslContext, livingCounter, closedCounter);
this.channel = ch;
SocketAddress addr = addr0;
if (addr == null) {
try {
addr = ch.getRemoteAddress();
} catch (Exception e) {
//do nothing
}
}
this.remoteAddress = addr;
ioThread.connCounter.incrementAndGet();
}
public AsyncNioTcpConnection(boolean client, AsyncIOGroup ioGroup, AsyncIOThread ioThread, AsyncIOThread connectThread, Supplier<ByteBuffer> bufferSupplier, Consumer<ByteBuffer> bufferConsumer,
SocketChannel ch, SSLContext sslContext, final SocketAddress addr0, AtomicLong livingCounter, AtomicLong closedCounter) {
super(client, ioGroup, ioThread, connectThread, ioGroup.bufferCapacity, bufferSupplier, bufferConsumer, sslContext, livingCounter, closedCounter);
this.channel = ch;
SocketAddress addr = addr0;
if (addr == null) {
try {
addr = ch.getRemoteAddress();
} catch (Exception e) {
//do nothing
}
}
this.remoteAddress = addr;
}
@Override
public boolean isOpen() {
return this.channel.isOpen();
}
@Override
public boolean isTCP() {
return true;
}
@Override
public boolean shutdownInput() {
try {
this.channel.shutdownInput();
return true;
} catch (IOException e) {
return false;
}
}
@Override
public boolean shutdownOutput() {
try {
this.channel.shutdownOutput();
return true;
} catch (IOException e) {
return false;
}
}
@Override
public <T> boolean setOption(SocketOption<T> name, T value) {
try {
this.channel.setOption(name, value);
return true;
} catch (IOException e) {
return false;
}
}
@Override
public Set<SocketOption<?>> supportedOptions() {
return this.channel.supportedOptions();
}
@Override
public SocketAddress getLocalAddress() {
try {
return channel.getLocalAddress();
} catch (IOException e) {
return null;
}
}
@Override
public ReadableByteChannel readableByteChannel() {
return this.channel;
}
@Override
public WritableByteChannel writableByteChannel() {
return this.channel;
}
@Override
public boolean isConnected() {
return this.channel.isConnected();
}
@Override
protected SelectionKey implRegister(Selector sel, int ops) throws ClosedChannelException {
return this.channel.register(sel, ops);
}
@Override
protected int implRead(ByteBuffer dst) throws IOException {
return this.channel.read(dst);
}
@Override
protected int implWrite(ByteBuffer src) throws IOException {
return this.channel.write(src);
}
@Override
protected int implWrite(ByteBuffer[] srcs, int offset, int length) throws IOException {
return (int) this.channel.write(srcs, offset, length);
}
public <A> void connect(SocketAddress remote, A attachment, CompletionHandler<Void, ? super A> handler) {
if (channel.isConnected()) {
throw new AlreadyConnectedException();
}
if (connectPending) {
throw new ConnectionPendingException();
}
connectPending = true;
this.connectAttachment = attachment;
this.connectCompletionHandler = (CompletionHandler<Void, Object>) handler;
this.remoteAddress = remote;
doConnect();
}
@Override
public void doConnect() {
try {
boolean connected = channel.isConnectionPending();
if (connected || channel.connect(remoteAddress)) {
connected = channel.finishConnect();
}
if (connected) {
handleConnect(null);
} else if (connectKey == null) {
connectThread.register(selector -> {
try {
connectKey = channel.register(selector, SelectionKey.OP_CONNECT);
connectKey.attach(this);
} catch (ClosedChannelException e) {
handleConnect(e);
}
});
} else {
handleConnect(new IOException());
}
} catch (IOException e) {
handleConnect(e);
}
}
@Override
public final void close() throws IOException {
super.close();
ioThread.connCounter.decrementAndGet();
channel.shutdownInput();
channel.shutdownOutput();
channel.close();
if (this.connectKey != null) this.connectKey.cancel();
if (this.readKey != null) this.readKey.cancel();
if (this.writeKey != null) this.writeKey.cancel();
}
}

View File

@@ -11,6 +11,8 @@ import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.*;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.*;
import org.redkale.boot.Application;
import org.redkale.util.*;
/**
@@ -22,23 +24,23 @@ import org.redkale.util.*;
*
* @since 2.1.0
*/
public class NioTcpProtocolServer extends ProtocolServer {
private ObjectPool<ByteBuffer> bufferPool;
private ObjectPool<Response> responsePool;
class AsyncNioTcpProtocolServer extends ProtocolServer {
private ServerSocketChannel serverChannel;
private Selector selector;
private NioThreadGroup ioGroup;
private AsyncIOGroup ioGroup;
private Thread acceptThread;
private boolean closed;
public NioTcpProtocolServer(Context context) {
private Supplier<Response> responseSupplier;
private Consumer<Response> responseConsumer;
public AsyncNioTcpProtocolServer(Context context) {
super(context);
}
@@ -81,20 +83,38 @@ public class NioTcpProtocolServer extends ProtocolServer {
}
@Override
public void accept(Server server) throws IOException {
public void accept(Application application, Server server) throws IOException {
this.serverChannel.register(this.selector, SelectionKey.OP_ACCEPT);
AtomicLong createBufferCounter = new AtomicLong();
AtomicLong cycleBufferCounter = new AtomicLong();
this.bufferPool = server.createBufferPool(createBufferCounter, cycleBufferCounter, server.bufferPoolSize);
ObjectPool<ByteBuffer> bufferPool = server.createBufferPool(createBufferCounter, cycleBufferCounter, server.bufferPoolSize);
AtomicLong createResponseCounter = new AtomicLong();
AtomicLong cycleResponseCounter = new AtomicLong();
this.responsePool = server.createResponsePool(createResponseCounter, cycleResponseCounter, server.responsePoolSize);
this.ioGroup = new NioThreadGroup(server.name, null, Runtime.getRuntime().availableProcessors(), bufferPool, responsePool);
ObjectPool<Response> safeResponsePool = server.createResponsePool(createResponseCounter, cycleResponseCounter, server.responsePoolSize);
final int threads = Runtime.getRuntime().availableProcessors();
ThreadLocal<ObjectPool<Response>> localResponsePool = ThreadLocal.withInitial(() -> {
if (!(Thread.currentThread() instanceof WorkThread)) return null;
return ObjectPool.createUnsafePool(safeResponsePool, safeResponsePool.getCreatCounter(),
safeResponsePool.getCycleCounter(), 16, safeResponsePool.getCreator(), safeResponsePool.getPrepare(), safeResponsePool.getRecycler());
});
this.responseSupplier = () -> {
ObjectPool<Response> pool = localResponsePool.get();
return pool == null ? safeResponsePool.get() : pool.get();
};
this.responseConsumer = (v) -> {
ObjectPool<Response> pool = localResponsePool.get();
(pool == null ? safeResponsePool : pool).accept(v);
};
final String threadPrefixName = server.name == null || server.name.isEmpty() ? "Redkale-IOServletThread" : ("Redkale-" + server.name.replace("Server-", "") + "-IOServletThread");
this.ioGroup = new AsyncIOGroup(threadPrefixName, null, threads, server.bufferCapacity, bufferPool);
this.ioGroup.start();
this.acceptThread = new Thread() {
{
setName(threadPrefixName.replace("ServletThread", "AcceptThread"));
}
@Override
public void run() {
while (!closed) {
@@ -125,9 +145,13 @@ public class NioTcpProtocolServer extends ProtocolServer {
channel.setOption(StandardSocketOptions.SO_REUSEADDR, true);
channel.setOption(StandardSocketOptions.SO_RCVBUF, 16 * 1024);
channel.setOption(StandardSocketOptions.SO_SNDBUF, 16 * 1024);
NioThread ioThread = ioGroup.nextThread();
AsyncConnection conn = new NioTcpAsyncConnection(ioGroup, ioThread, channel, context.getSSLContext(), null, livingCounter, closedCounter);
new NioTcpPrepareRunner(context, responsePool, conn, null, null).run();
AsyncIOThread readThread = ioGroup.nextIOThread();
ioGroup.connCreateCounter.incrementAndGet();
ioGroup.connLivingCounter.incrementAndGet();
AsyncNioTcpConnection conn = new AsyncNioTcpConnection(false, ioGroup, readThread, ioGroup.connectThread(), channel, context.getSSLContext(), null, ioGroup.connLivingCounter, ioGroup.connClosedCounter);
ProtocolCodec codec = new ProtocolCodec(context, responseSupplier, responseConsumer, conn);
conn.protocolCodec = codec;
codec.run(null);
}
@Override
@@ -140,4 +164,18 @@ public class NioTcpProtocolServer extends ProtocolServer {
this.selector.close();
}
@Override
public long getCreateConnectionCount() {
return ioGroup.connCreateCounter.get();
}
@Override
public long getClosedConnectionCount() {
return ioGroup.connClosedCounter.get();
}
@Override
public long getLivingConnectionCount() {
return ioGroup.connLivingCounter.get();
}
}

View File

@@ -0,0 +1,171 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.net;
import java.io.*;
import java.net.*;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.*;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.*;
import javax.net.ssl.SSLContext;
/**
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*/
class AsyncNioUdpConnection extends AsyncNioConnection {
private final DatagramChannel channel;
public AsyncNioUdpConnection(boolean client, AsyncIOGroup ioGroup, AsyncIOThread ioThread, AsyncIOThread connectThread, DatagramChannel ch,
SSLContext sslContext, final SocketAddress addr0, AtomicLong livingCounter, AtomicLong closedCounter) {
super(client, ioGroup, ioThread, connectThread, ioGroup.bufferCapacity, ioThread.getBufferSupplier(), ioThread.getBufferConsumer(), sslContext, livingCounter, closedCounter);
this.channel = ch;
SocketAddress addr = addr0;
if (addr == null) {
try {
addr = ch.getRemoteAddress();
} catch (Exception e) {
//do nothing
}
}
this.remoteAddress = addr;
}
public AsyncNioUdpConnection(boolean client, AsyncIOGroup ioGroup, AsyncIOThread ioThread, AsyncIOThread connectThread,
Supplier<ByteBuffer> bufferSupplier, Consumer<ByteBuffer> bufferConsumer,
DatagramChannel ch, SSLContext sslContext, final SocketAddress addr0,
AtomicLong livingCounter, AtomicLong closedCounter) {
super(client, ioGroup, ioThread, connectThread, ioGroup.bufferCapacity, bufferSupplier, bufferConsumer, sslContext, livingCounter, closedCounter);
this.channel = ch;
SocketAddress addr = addr0;
if (addr == null) {
try {
addr = ch.getRemoteAddress();
} catch (Exception e) {
//do nothing
}
}
this.remoteAddress = addr;
}
@Override
public boolean isOpen() {
return this.channel.isOpen();
}
@Override
public boolean isTCP() {
return false;
}
@Override
public boolean shutdownInput() {
return true;
}
@Override
public boolean shutdownOutput() {
return true;
}
@Override
public <T> boolean setOption(SocketOption<T> name, T value) {
try {
this.channel.setOption(name, value);
return true;
} catch (IOException e) {
return false;
}
}
@Override
public Set<SocketOption<?>> supportedOptions() {
return this.channel.supportedOptions();
}
@Override
public SocketAddress getLocalAddress() {
try {
return channel.getLocalAddress();
} catch (IOException e) {
return null;
}
}
@Override
public ReadableByteChannel readableByteChannel() {
return this.channel;
}
@Override
public WritableByteChannel writableByteChannel() {
return this.channel;
}
@Override
public boolean isConnected() {
if (!client) return true;
return this.channel.isConnected();
}
@Override
protected SelectionKey implRegister(Selector sel, int ops) throws ClosedChannelException {
return this.channel.register(sel, ops);
}
@Override
protected int implRead(ByteBuffer dst) throws IOException {
return this.channel.read(dst);
}
@Override
protected int implWrite(ByteBuffer src) throws IOException {
return this.channel.send(src, remoteAddress);
}
@Override
protected int implWrite(ByteBuffer[] srcs, int offset, int length) throws IOException {
int end = offset + length;
for (int i = offset; i < end; i++) {
ByteBuffer buf = srcs[i];
if (buf.hasRemaining()) return this.channel.send(buf, remoteAddress);
}
return 0;
}
public <A> void connect(SocketAddress remote, A attachment, CompletionHandler<Void, ? super A> handler) {
this.connectAttachment = attachment;
this.connectCompletionHandler = (CompletionHandler<Void, Object>) handler;
this.remoteAddress = remote;
doConnect();
}
@Override
public void doConnect() {
try {
channel.connect(remoteAddress);
handleConnect(null);
} catch (IOException e) {
handleConnect(e);
}
}
@Override
public final void close() throws IOException {
super.close();
if (client) channel.close(); //不能关闭channel
if (this.connectKey != null) this.connectKey.cancel();
if (this.readKey != null) this.readKey.cancel();
if (this.writeKey != null) this.writeKey.cancel();
}
}

View File

@@ -0,0 +1,169 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.net;
import java.io.IOException;
import java.net.*;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.*;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.*;
import org.redkale.boot.Application;
import org.redkale.util.*;
/**
* 协议底层Server
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*/
class AsyncNioUdpProtocolServer extends ProtocolServer {
private DatagramChannel serverChannel;
private Selector selector;
private AsyncIOGroup ioGroup;
private Thread acceptThread;
private boolean closed;
private Supplier<Response> responseSupplier;
private Consumer<Response> responseConsumer;
public AsyncNioUdpProtocolServer(Context context) {
super(context);
}
@Override
public void open(AnyValue config) throws IOException {
this.serverChannel = DatagramChannel.open();
this.serverChannel.configureBlocking(false);
this.selector = Selector.open();
final Set<SocketOption<?>> options = this.serverChannel.supportedOptions();
if (options.contains(StandardSocketOptions.TCP_NODELAY)) {
this.serverChannel.setOption(StandardSocketOptions.TCP_NODELAY, true);
}
if (options.contains(StandardSocketOptions.SO_KEEPALIVE)) {
this.serverChannel.setOption(StandardSocketOptions.SO_KEEPALIVE, true);
}
if (options.contains(StandardSocketOptions.SO_REUSEADDR)) {
this.serverChannel.setOption(StandardSocketOptions.SO_REUSEADDR, true);
}
if (options.contains(StandardSocketOptions.SO_RCVBUF)) {
this.serverChannel.setOption(StandardSocketOptions.SO_RCVBUF, 16 * 1024);
}
if (options.contains(StandardSocketOptions.SO_SNDBUF)) {
this.serverChannel.setOption(StandardSocketOptions.SO_SNDBUF, 16 * 1024);
}
}
@Override
public void bind(SocketAddress local, int backlog) throws IOException {
this.serverChannel.bind(local);
}
@Override
public <T> void setOption(SocketOption<T> name, T value) throws IOException {
this.serverChannel.setOption(name, value);
}
@Override
public <T> Set<SocketOption<?>> supportedOptions() {
return this.serverChannel.supportedOptions();
}
@Override
public void accept(Application application, Server server) throws IOException {
AtomicLong createBufferCounter = new AtomicLong();
AtomicLong cycleBufferCounter = new AtomicLong();
ObjectPool<ByteBuffer> safeBufferPool = server.createBufferPool(createBufferCounter, cycleBufferCounter, server.bufferPoolSize);
AtomicLong createResponseCounter = new AtomicLong();
AtomicLong cycleResponseCounter = new AtomicLong();
ObjectPool<Response> safeResponsePool = server.createResponsePool(createResponseCounter, cycleResponseCounter, server.responsePoolSize);
final int threads = Runtime.getRuntime().availableProcessors();
ThreadLocal<ObjectPool<Response>> localResponsePool = ThreadLocal.withInitial(() -> {
if (!(Thread.currentThread() instanceof WorkThread)) return null;
return ObjectPool.createUnsafePool(safeResponsePool, safeResponsePool.getCreatCounter(),
safeResponsePool.getCycleCounter(), 16, safeResponsePool.getCreator(), safeResponsePool.getPrepare(), safeResponsePool.getRecycler());
});
this.responseSupplier = () -> {
ObjectPool<Response> pool = localResponsePool.get();
return pool == null ? safeResponsePool.get() : pool.get();
};
this.responseConsumer = (v) -> {
ObjectPool<Response> pool = localResponsePool.get();
(pool == null ? safeResponsePool : pool).accept(v);
};
final String threadPrefixName = server.name == null || server.name.isEmpty() ? "Redkale-IOServletThread" : ("Redkale-" + server.name.replace("Server-", "") + "-IOServletThread");
this.ioGroup = new AsyncIOGroup(threadPrefixName, null, threads, server.bufferCapacity, safeBufferPool);
this.ioGroup.start();
this.serverChannel.register(this.selector, SelectionKey.OP_READ);
this.acceptThread = new Thread() {
ObjectPool<ByteBuffer> unsafeBufferPool = ObjectPool.createUnsafePool(safeBufferPool, safeBufferPool.getCreatCounter(),
safeBufferPool.getCycleCounter(), 512, safeBufferPool.getCreator(), safeBufferPool.getPrepare(), safeBufferPool.getRecycler());
{
setName(threadPrefixName.replace("ServletThread", "AcceptThread"));
}
@Override
public void run() {
while (!closed) {
final ByteBuffer buffer = unsafeBufferPool.get();
try {
SocketAddress address = serverChannel.receive(buffer);
buffer.flip();
accept(address, buffer);
} catch (Throwable t) {
unsafeBufferPool.accept(buffer);
}
}
}
};
this.acceptThread.start();
}
private void accept(SocketAddress address, ByteBuffer buffer) throws IOException {
AsyncIOThread readThread = ioGroup.nextIOThread();
ioGroup.connCreateCounter.incrementAndGet();
ioGroup.connLivingCounter.incrementAndGet();
AsyncNioUdpConnection conn = new AsyncNioUdpConnection(false, ioGroup, readThread, ioGroup.connectThread(), this.serverChannel, context.getSSLContext(), address, ioGroup.connLivingCounter, ioGroup.connClosedCounter);
ProtocolCodec codec = new ProtocolCodec(context, responseSupplier, responseConsumer, conn);
conn.protocolCodec = codec;
codec.run(buffer);
}
@Override
public void close() throws IOException {
if (this.closed) return;
this.closed = true;
this.ioGroup.close();
this.serverChannel.close();
}
@Override
public long getCreateConnectionCount() {
return -1;
}
@Override
public long getClosedConnectionCount() {
return -1;
}
@Override
public long getLivingConnectionCount() {
return -1;
}
}

View File

@@ -1,178 +0,0 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.net;
import java.io.*;
import java.net.*;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Set;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.*;
import javax.net.ssl.SSLContext;
/**
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*/
class BioUdpAsyncConnection extends AsyncConnection {
private int readTimeoutSeconds;
private int writeTimeoutSeconds;
private final DatagramChannel channel;
private final SocketAddress remoteAddress;
private final boolean client;
public BioUdpAsyncConnection(Supplier<ByteBuffer> bufferSupplier, Consumer<ByteBuffer> bufferConsumer,
final DatagramChannel ch, final SSLContext sslContext, SocketAddress addr0, final boolean client0,
final int readTimeoutSeconds0, final int writeTimeoutSeconds0,
final AtomicLong livingCounter, final AtomicLong closedCounter) {
super(bufferSupplier, bufferConsumer, sslContext, livingCounter, closedCounter);
this.channel = ch;
this.client = client0;
this.readTimeoutSeconds = readTimeoutSeconds0;
this.writeTimeoutSeconds = writeTimeoutSeconds0;
SocketAddress addr = addr0;
if (addr == null) {
try {
addr = ch.getRemoteAddress();
} catch (Exception e) {
//do nothing
}
}
this.remoteAddress = addr;
}
@Override
public void setReadTimeoutSeconds(int readTimeoutSeconds) {
this.readTimeoutSeconds = readTimeoutSeconds;
}
@Override
public void setWriteTimeoutSeconds(int writeTimeoutSeconds) {
this.writeTimeoutSeconds = writeTimeoutSeconds;
}
@Override
public int getReadTimeoutSeconds() {
return this.readTimeoutSeconds;
}
@Override
public int getWriteTimeoutSeconds() {
return this.writeTimeoutSeconds;
}
@Override
public final SocketAddress getRemoteAddress() {
return remoteAddress;
}
@Override
public SocketAddress getLocalAddress() {
try {
return channel.getLocalAddress();
} catch (IOException e) {
return null;
}
}
@Override
public boolean shutdownInput() {
return false;
}
@Override
public boolean shutdownOutput() {
return false;
}
@Override
public <T> boolean setOption(SocketOption<T> name, T value) {
try {
this.channel.setOption(name, value);
return true;
} catch (IOException e) {
return false;
}
}
@Override
public Set<SocketOption<?>> supportedOptions() {
return this.channel.supportedOptions();
}
@Override
public <A> void write(ByteBuffer[] srcs, int offset, int length, A attachment, CompletionHandler<Integer, ? super A> handler) {
try {
int rs = 0;
for (int i = offset; i < offset + length; i++) {
rs += channel.send(srcs[i], remoteAddress);
if (i != offset) Thread.sleep(10);
}
this.writetime = System.currentTimeMillis();
if (handler != null) handler.completed(rs, attachment);
} catch (Exception e) {
if (handler != null) handler.failed(e, attachment);
}
}
@Override
public void read(CompletionHandler<Integer, ByteBuffer> handler) {
ByteBuffer dst = pollReadBuffer();
try {
int rs = channel.read(dst);
this.readtime = System.currentTimeMillis();
if (handler != null) handler.completed(rs, dst);
} catch (IOException e) {
if (handler != null) handler.failed(e, dst);
}
}
@Override
public final ReadableByteChannel readableByteChannel() {
return this.channel;
}
@Override
public final WritableByteChannel writableByteChannel() {
return this.channel;
}
@Override
public <A> void write(ByteBuffer src, A attachment, CompletionHandler<Integer, ? super A> handler) {
try {
int rs = channel.send(src, remoteAddress);
this.writetime = System.currentTimeMillis();
if (handler != null) handler.completed(rs, attachment);
} catch (IOException e) {
if (handler != null) handler.failed(e, attachment);
}
}
@Override
public final void close() throws IOException {
super.close();
if (client) channel.close();
}
@Override
public final boolean isOpen() {
return channel.isOpen();
}
@Override
public final boolean isTCP() {
return false;
}
}

View File

@@ -1,131 +0,0 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.net;
import java.io.IOException;
import java.net.*;
import java.nio.ByteBuffer;
import java.nio.channels.DatagramChannel;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicLong;
import org.redkale.util.*;
/**
* 协议底层Server
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*/
public class BioUdpProtocolServer extends ProtocolServer {
private boolean running;
private DatagramChannel serverChannel;
public BioUdpProtocolServer(Context context) {
super(context);
}
@Override
public void open(AnyValue config) throws IOException {
DatagramChannel ch = DatagramChannel.open();
ch.configureBlocking(true);
this.serverChannel = ch;
final Set<SocketOption<?>> options = this.serverChannel.supportedOptions();
if (options.contains(StandardSocketOptions.TCP_NODELAY)) {
this.serverChannel.setOption(StandardSocketOptions.TCP_NODELAY, true);
}
if (options.contains(StandardSocketOptions.SO_KEEPALIVE)) {
this.serverChannel.setOption(StandardSocketOptions.SO_KEEPALIVE, true);
}
if (options.contains(StandardSocketOptions.SO_REUSEADDR)) {
this.serverChannel.setOption(StandardSocketOptions.SO_REUSEADDR, true);
}
if (options.contains(StandardSocketOptions.SO_RCVBUF)) {
this.serverChannel.setOption(StandardSocketOptions.SO_RCVBUF, 16 * 1024);
}
if (options.contains(StandardSocketOptions.SO_SNDBUF)) {
this.serverChannel.setOption(StandardSocketOptions.SO_SNDBUF, 16 * 1024);
}
}
@Override
public void bind(SocketAddress local, int backlog) throws IOException {
this.serverChannel.bind(local);
}
@Override
public <T> void setOption(SocketOption<T> name, T value) throws IOException {
this.serverChannel.setOption(name, value);
}
@Override
public <T> Set<SocketOption<?>> supportedOptions() {
return this.serverChannel.supportedOptions();
}
@Override
public void accept(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);
final DatagramChannel serchannel = this.serverChannel;
final int readTimeoutSeconds = this.context.readTimeoutSeconds;
final int writeTimeoutSeconds = this.context.writeTimeoutSeconds;
final CountDownLatch cdl = new CountDownLatch(1);
this.running = true;
new Thread() {
@Override
public void run() {
cdl.countDown();
while (running) {
final ByteBuffer buffer = bufferPool.get();
try {
SocketAddress address = serchannel.receive(buffer);
buffer.flip();
AsyncConnection conn = new BioUdpAsyncConnection(bufferPool, bufferPool, serchannel,
context.getSSLContext(), address, false, readTimeoutSeconds, writeTimeoutSeconds, null, null);
context.runAsync(new PrepareRunner(context, responsePool, conn, buffer, null));
} catch (Exception e) {
bufferPool.accept(buffer);
}
}
}
}.start();
try {
cdl.await();
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void close() throws IOException {
this.running = false;
this.serverChannel.close();
}
@Override
public long getCreateCount() {
return -1;
}
@Override
public long getClosedCount() {
return -1;
}
@Override
public long getLivingCount() {
return -1;
}
}

View File

@@ -28,7 +28,9 @@ public class Context {
protected final long serverStartTime;
//Server的线程池
protected final ThreadPoolExecutor executor;
protected final ExecutorService workExecutor;
protected final ThreadHashExecutor workHashExecutor;
//SSL
protected final SSLContext sslContext;
@@ -73,17 +75,17 @@ public class Context {
protected Charset charset;
public Context(ContextConfig config) {
this(config.serverStartTime, config.logger, config.executor, config.sslContext,
this(config.serverStartTime, config.logger, config.workExecutor, config.sslContext,
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,
public Context(long serverStartTime, Logger logger, ExecutorService workExecutor, SSLContext sslContext,
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.workExecutor = workExecutor;
this.sslContext = sslContext;
this.bufferCapacity = bufferCapacity;
this.maxconns = maxconns;
@@ -97,6 +99,57 @@ public class Context {
this.writeTimeoutSeconds = writeTimeoutSeconds;
this.jsonFactory = JsonFactory.root();
this.bsonFactory = BsonFactory.root();
if (workExecutor instanceof ThreadHashExecutor) {
this.workHashExecutor = (ThreadHashExecutor) workExecutor;
} else {
this.workHashExecutor = null;
}
}
protected void executePrepareServlet(Request request, Response response) {
if (workHashExecutor != null) {
workHashExecutor.execute(request.getHashid(), () -> prepare.prepare(request, response));
} else if (workExecutor != null) {
workExecutor.execute(() -> prepare.prepare(request, response));
} else {
prepare.prepare(request, response);
}
}
public void execute(Servlet servlet, Request request, Response response) {
if (workHashExecutor != null) {
workHashExecutor.execute(request.getHashid(), () -> {
try {
long cha = System.currentTimeMillis() - request.getCreatetime();
servlet.execute(request, response);
if (cha > 1000 && response.context.logger.isLoggable(Level.WARNING)) {
response.context.logger.log(Level.WARNING, "hash execute servlet delays=" + cha + "ms, request=" + request);
} else if (cha > 100 && response.context.logger.isLoggable(Level.FINE)) {
response.context.logger.log(Level.FINE, "hash execute servlet delay=" + cha + "ms, request=" + request);
}
} catch (Throwable t) {
response.context.logger.log(Level.WARNING, "execute servlet abort, force to close channel ", t);
response.finish(true);
}
});
} else if (workExecutor != null) {
workExecutor.execute(() -> {
try {
servlet.execute(request, response);
} catch (Throwable t) {
response.context.logger.log(Level.WARNING, "execute servlet abort, force to close channel ", t);
response.finish(true);
}
});
} else {
try {
servlet.execute(request, response);
} catch (Throwable t) {
response.context.logger.log(Level.WARNING, "execute servlet abort, force to close channel ", t);
response.finish(true);
}
}
}
public ResourceFactory getResourceFactory() {
@@ -127,22 +180,6 @@ public class Context {
return charset;
}
public Future<?> submitAsync(Runnable r) {
return executor.submit(r);
}
public void runAsync(Runnable r) {
executor.execute(r);
}
public int getCorePoolSize() {
return executor.getCorePoolSize();
}
public ThreadFactory getThreadFactory() {
return executor.getThreadFactory();
}
public int getBufferCapacity() {
return bufferCapacity;
}
@@ -177,7 +214,7 @@ public class Context {
public long serverStartTime;
//Server的线程池
public ThreadPoolExecutor executor;
public ExecutorService workExecutor;
//SSL
public SSLContext sslContext;

View File

@@ -1,473 +0,0 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.net;
import java.io.IOException;
import java.net.*;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.*;
import javax.net.ssl.SSLContext;
/**
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*
* @since 2.1.0
*/
public class NioTcpAsyncConnection extends AsyncConnection {
private int readTimeoutSeconds;
private int writeTimeoutSeconds;
private SocketAddress remoteAddress;
final SocketChannel channel;
final NioThread ioThread;
final NioThreadGroup ioGroup;
//连
private Object connectAttachment;
private CompletionHandler<Void, Object> connectCompletionHandler;
private boolean connectPending;
private SelectionKey connectKey;
//读操作
private ByteBuffer readByteBuffer;
private CompletionHandler<Integer, ByteBuffer> readCompletionHandler;
private boolean readPending;
private SelectionKey readKey;
//写操作, 二选一要么writeByteBuffer有值要么writeByteBuffers、writeOffset、writeLength有值
private ByteBuffer writeByteBuffer;
private ByteBuffer[] writeByteBuffers;
private int writeOffset;
private int writeLength;
private Object writeAttachment;
private CompletionHandler<Integer, Object> writeCompletionHandler;
private boolean writePending;
private SelectionKey writeKey;
public NioTcpAsyncConnection(NioThreadGroup ioGroup, NioThread ioThread, SocketChannel ch,
SSLContext sslContext, final SocketAddress addr0, AtomicLong livingCounter, AtomicLong closedCounter) {
super(ioThread.getBufferSupplier(), ioThread.getBufferConsumer(), sslContext, livingCounter, closedCounter);
this.ioGroup = ioGroup;
this.ioThread = ioThread;
this.channel = ch;
SocketAddress addr = addr0;
if (addr == null) {
try {
addr = ch.getRemoteAddress();
} catch (Exception e) {
//do nothing
}
}
this.remoteAddress = addr;
}
public NioTcpAsyncConnection(NioThreadGroup ioGroup, NioThread ioThread,
Supplier<ByteBuffer> bufferSupplier, Consumer<ByteBuffer> bufferConsumer,
SocketChannel ch, SSLContext sslContext, final SocketAddress addr0,
AtomicLong livingCounter, AtomicLong closedCounter) {
super(bufferSupplier, bufferConsumer, sslContext, livingCounter, closedCounter);
this.ioGroup = ioGroup;
this.ioThread = ioThread;
this.channel = ch;
SocketAddress addr = addr0;
if (addr == null) {
try {
addr = ch.getRemoteAddress();
} catch (Exception e) {
//do nothing
}
}
this.remoteAddress = addr;
}
@Override
public boolean isOpen() {
return this.channel.isOpen();
}
@Override
public boolean isTCP() {
return true;
}
@Override
public boolean shutdownInput() {
try {
this.channel.shutdownInput();
return true;
} catch (IOException e) {
return false;
}
}
@Override
public boolean shutdownOutput() {
try {
this.channel.shutdownOutput();
return true;
} catch (IOException e) {
return false;
}
}
@Override
public <T> boolean setOption(SocketOption<T> name, T value) {
try {
this.channel.setOption(name, value);
return true;
} catch (IOException e) {
return false;
}
}
@Override
public Set<SocketOption<?>> supportedOptions() {
return this.channel.supportedOptions();
}
@Override
public SocketAddress getRemoteAddress() {
return remoteAddress;
}
@Override
public SocketAddress getLocalAddress() {
try {
return channel.getLocalAddress();
} catch (IOException e) {
return null;
}
}
@Override
public void setReadTimeoutSeconds(int readTimeoutSeconds) {
this.readTimeoutSeconds = readTimeoutSeconds;
}
@Override
public void setWriteTimeoutSeconds(int writeTimeoutSeconds) {
this.writeTimeoutSeconds = writeTimeoutSeconds;
}
@Override
public int getReadTimeoutSeconds() {
return this.readTimeoutSeconds;
}
@Override
public int getWriteTimeoutSeconds() {
return this.writeTimeoutSeconds;
}
@Override
public ReadableByteChannel readableByteChannel() {
return this.channel;
}
public <A> void connect(SocketAddress remote, A attachment, CompletionHandler<Void, ? super A> handler) {
if (channel.isConnected()) {
throw new AlreadyConnectedException();
}
if (connectPending) {
throw new ConnectionPendingException();
}
connectPending = true;
this.connectAttachment = attachment;
this.connectCompletionHandler = (CompletionHandler<Void, Object>) handler;
this.remoteAddress = remote;
doConnect();
}
@Override
public void read(CompletionHandler<Integer, ByteBuffer> handler) {
Objects.requireNonNull(handler);
if (!this.channel.isConnected()) {
handler.failed(new NotYetConnectedException(), pollReadBuffer());
return;
}
if (this.readPending) {
handler.failed(new ReadPendingException(), pollReadBuffer());
return;
}
this.readPending = true;
if (this.readTimeoutSeconds > 0) {
NioCompletionHandler newhandler = new NioCompletionHandler(handler, this.readByteBuffer);
this.readCompletionHandler = newhandler;
newhandler.timeoutFuture = ioGroup.scheduleTimeout(newhandler, this.readTimeoutSeconds, TimeUnit.SECONDS);
} else {
this.readCompletionHandler = handler;
}
doRead();
}
@Override
public WritableByteChannel writableByteChannel() {
return this.channel;
}
@Override
public <A> void write(ByteBuffer src, A attachment, CompletionHandler<Integer, ? super A> handler) {
Objects.requireNonNull(src);
Objects.requireNonNull(handler);
if (!this.channel.isConnected()) {
handler.failed(new NotYetConnectedException(), attachment);
return;
}
if (this.writePending) {
handler.failed(new WritePendingException(), attachment);
return;
}
this.writePending = true;
this.writeByteBuffer = src;
this.writeAttachment = attachment;
if (this.writeTimeoutSeconds > 0) {
NioCompletionHandler newhandler = new NioCompletionHandler(handler, attachment);
this.writeCompletionHandler = newhandler;
newhandler.timeoutFuture = ioGroup.scheduleTimeout(newhandler, this.writeTimeoutSeconds, TimeUnit.SECONDS);
} else {
this.writeCompletionHandler = (CompletionHandler) handler;
}
doWrite();
}
@Override
public <A> void write(ByteBuffer[] srcs, int offset, int length, A attachment, CompletionHandler<Integer, ? super A> handler) {
Objects.requireNonNull(srcs);
Objects.requireNonNull(handler);
if (!this.channel.isConnected()) {
handler.failed(new NotYetConnectedException(), attachment);
return;
}
if (this.writePending) {
handler.failed(new WritePendingException(), attachment);
return;
}
this.writePending = true;
this.writeByteBuffers = srcs;
this.writeOffset = offset;
this.writeLength = length;
this.writeAttachment = attachment;
if (this.writeTimeoutSeconds > 0) {
NioCompletionHandler newhandler = new NioCompletionHandler(handler, attachment);
this.writeCompletionHandler = newhandler;
newhandler.timeoutFuture = ioGroup.scheduleTimeout(newhandler, this.writeTimeoutSeconds, TimeUnit.SECONDS);
} else {
this.writeCompletionHandler = (CompletionHandler) handler;
}
doWrite();
}
public void doConnect() {
try {
boolean connected = channel.isConnectionPending();
if (connected || channel.connect(remoteAddress)) {
connected = channel.finishConnect();
}
if (connected) {
handleConnect(null);
} else if (connectKey == null) {
ioThread.register(selector -> {
try {
connectKey = channel.register(selector, SelectionKey.OP_CONNECT);
connectKey.attach(this);
} catch (ClosedChannelException e) {
handleConnect(e);
}
});
} else {
handleConnect(new IOException());
}
} catch (IOException e) {
handleConnect(e);
}
}
private void handleConnect(Throwable t) {
CompletionHandler handler = this.connectCompletionHandler;
Object attach = this.connectAttachment;
clearConnect();
if (handler != null) {
if (t == null) {
handler.completed(null, attach);
} else {
handler.failed(t, attach);
}
}
}
private void clearConnect() {
this.connectCompletionHandler = null;
this.connectAttachment = null;
this.connectPending = false;//必须放最后
}
public void doRead() {
try {
final boolean invokeDirect = this.ioThread.inCurrThread();
int totalCount = 0;
boolean hasRemain = true;
if (invokeDirect && this.readByteBuffer == null) {
this.readByteBuffer = pollReadBuffer();
if (this.readTimeoutSeconds > 0) {
((NioCompletionHandler) this.readCompletionHandler).setAttachment(this.readByteBuffer);
}
}
while (invokeDirect && hasRemain) {
int readCount = this.channel.read(readByteBuffer);
hasRemain = readByteBuffer.hasRemaining();
if (readCount <= 0) {
if (totalCount == 0) totalCount = readCount;
break;
}
totalCount += readCount;
}
if (totalCount != 0 || !hasRemain) {
if (!readPending && readKey != null) readKey.interestOps(readKey.interestOps() & ~SelectionKey.OP_READ);
handleRead(totalCount, null);
} else if (readKey == null) {
ioThread.register(selector -> {
try {
readKey = channel.register(selector, SelectionKey.OP_READ);
readKey.attach(this);
} catch (ClosedChannelException e) {
handleRead(0, e);
}
});
} else {
ioGroup.interestOpsOr(ioThread, readKey, SelectionKey.OP_READ);
}
} catch (Exception e) {
handleRead(0, e);
}
}
private void handleRead(final int totalCount, Throwable t) {
CompletionHandler<Integer, ByteBuffer> handler = this.readCompletionHandler;
ByteBuffer attach = this.readByteBuffer;
clearRead();
if (handler != null) {
if (t == null) {
handler.completed(totalCount, attach);
} else {
handler.failed(t, attach);
}
}
}
private void clearRead() {
this.readCompletionHandler = null;
this.readByteBuffer = null;
this.readPending = false; //必须放最后
}
public void doWrite() {
try {
final boolean invokeDirect = this.ioThread.inCurrThread();
int totalCount = 0;
boolean hasRemain = true;
while (invokeDirect && hasRemain) {
int writeCount;
if (writeByteBuffer != null) {
writeCount = channel.write(writeByteBuffer);
hasRemain = writeByteBuffer.hasRemaining();
} else {
writeCount = (int) channel.write(writeByteBuffers, writeOffset, writeLength);
boolean remain = false;
for (int i = writeByteBuffers.length - 1; i >= writeOffset; i--) {
if (writeByteBuffers[i].hasRemaining()) {
remain = true;
break;
}
}
hasRemain = remain;
}
if (writeCount <= 0) {
if (totalCount == 0) totalCount = writeCount;
break;
}
totalCount += writeCount;
}
if (totalCount > 0 || !hasRemain) {
if (writeKey != null) writeKey.interestOps(writeKey.interestOps() & ~SelectionKey.OP_WRITE);
CompletionHandler<Integer, Object> handler = this.writeCompletionHandler;
Object attach = this.writeAttachment;
clearWrite();
if (handler != null) {
handler.completed(totalCount, attach);
}
} else if (writeKey == null) {
ioThread.register(selector -> {
try {
writeKey = channel.register(selector, SelectionKey.OP_WRITE);
writeKey.attach(this);
} catch (ClosedChannelException e) {
CompletionHandler<Integer, Object> handler = this.writeCompletionHandler;
Object attach = this.writeAttachment;
clearWrite();
if (handler != null) {
handler.failed(e, attach);
}
}
});
} else {
ioGroup.interestOpsOr(ioThread, writeKey, SelectionKey.OP_WRITE);
}
} catch (IOException e) {
CompletionHandler<Integer, Object> handler = this.writeCompletionHandler;
Object attach = this.writeAttachment;
clearWrite();
if (handler != null) {
handler.failed(e, attach);
}
}
}
private void clearWrite() {
this.writeCompletionHandler = null;
this.writeAttachment = null;
this.writeByteBuffer = null;
this.writeByteBuffers = null;
this.writeOffset = 0;
this.writeLength = 0;
this.writePending = false; //必须放最后
}
@Override
public final void close() throws IOException {
super.close();
if (this.connectKey != null) this.connectKey.cancel();
if (this.readKey != null) this.readKey.cancel();
if (this.writeKey != null) this.writeKey.cancel();
channel.close();
}
}

View File

@@ -1,167 +0,0 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.net;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.CompletionHandler;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level;
import org.redkale.util.ObjectPool;
/**
*
* @author zhangjx
*/
public class NioTcpPrepareRunner implements Runnable {
private final AsyncConnection channel;
private final Context context;
private final ObjectPool<Response> responsePool;
private ByteBuffer data;
private Response response;
public NioTcpPrepareRunner(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;
}
@Override
public void run() {
try {
channel.read(new CompletionHandler<Integer, ByteBuffer>() {
@Override
public void completed(Integer count, ByteBuffer buffer) {
if (response == null) response = ((NioThread) Thread.currentThread()).getResponseSupplier().get();
if (count < 1) {
buffer.clear();
channel.setReadBuffer(buffer);
channel.dispose();// response.init(channel); 在调用之前异常
response.removeChannel();
response.finish(true);
return;
}
// { //测试
// buffer.flip();
// byte[] bs = new byte[buffer.remaining()];
// buffer.get(bs);
// System.println(new String(bs));
// }
buffer.flip();
try {
response.init(channel);
codec(buffer, response);
} catch (Throwable t) { //此处不可 context.offerBuffer(buffer); 以免prepare.prepare内部异常导致重复 offerBuffer
context.logger.log(Level.WARNING, "prepare servlet abort, force to close channel ", t);
response.finish(true);
}
}
@Override
public void failed(Throwable exc, ByteBuffer buffer) {
buffer.clear();
channel.setReadBuffer(buffer);
channel.dispose();// response.init(channel); 在调用之前异常
response.removeChannel();
response.finish(true);
if (exc != null && context.logger.isLoggable(Level.FINEST)) {
context.logger.log(Level.FINEST, "Servlet Handler read channel erroneous, force to close channel ", exc);
}
}
});
} catch (Exception te) {
channel.dispose();// response.init(channel); 在调用之前异常
response.removeChannel();
response.finish(true);
if (context.logger.isLoggable(Level.FINEST)) {
context.logger.log(Level.FINEST, "Servlet read channel erroneous, force to close channel ", te);
}
}
}
protected void codec(final ByteBuffer buffer, final Response response) throws IOException {
final Request request = response.request;
final PrepareServlet preparer = context.prepare;
preparer.executeCounter.incrementAndGet();
final int rs = request.readHeader(buffer);
if (rs < 0) { //表示数据格式不正确
channel.offerBuffer(buffer);
if (rs != Integer.MIN_VALUE) preparer.illRequestCounter.incrementAndGet();
response.finish(true);
} else if (rs == 0) {
if (buffer.hasRemaining()) {
request.setMoredata(buffer);
} else {
buffer.clear();
channel.setReadBuffer(buffer);
}
preparer.prepare(request, response);
} else {
buffer.clear();
channel.setReadBuffer(buffer);
final AtomicInteger ai = new AtomicInteger(rs);
channel.read(new CompletionHandler<Integer, ByteBuffer>() {
@Override
public void completed(Integer result, ByteBuffer attachment) {
attachment.flip();
ai.addAndGet(-request.readBody(attachment));
if (ai.get() > 0) {
attachment.clear();
channel.setReadBuffer(attachment);
channel.read(this);
} else {
if (attachment.hasRemaining()) {
request.setMoredata(attachment);
} else {
attachment.clear();
channel.setReadBuffer(attachment);
}
try {
preparer.prepare(request, response);
} catch (Throwable t) { //此处不可 context.offerBuffer(buffer); 以免preparer.prepare内部异常导致重复 offerBuffer
context.logger.log(Level.WARNING, "prepare servlet abort, force to close channel ", t);
response.finish(true);
}
}
}
@Override
public void failed(Throwable exc, ByteBuffer attachment) {
preparer.illRequestCounter.incrementAndGet();
attachment.clear();
channel.setReadBuffer(attachment);
response.finish(true);
if (exc != null) request.context.logger.log(Level.FINER, "Servlet read channel erroneous, force to close channel ", exc);
}
});
}
}
protected void initResponse(Response response, AsyncConnection channel) {
response.init(channel);
}
protected Response pollResponse() {
return responsePool.get();
}
protected Request pollRequest(Response response) {
return response.request;
}
protected AsyncConnection removeChannel(Response response) {
return response.removeChannel();
}
}

View File

@@ -1,123 +0,0 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.net;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.*;
import java.util.concurrent.*;
import java.util.function.*;
import org.redkale.util.*;
/**
* 协议处理的IO线程类
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*
* @since 2.1.0
*/
public class NioThread extends WorkThread {
final Selector selector;
private final Supplier<ByteBuffer> bufferSupplier;
private final Consumer<ByteBuffer> bufferConsumer;
private final Supplier<Response> responseSupplier;
private final Consumer<Response> responseConsumer;
private final ConcurrentLinkedQueue<Consumer<Selector>> registers = new ConcurrentLinkedQueue<>();
private boolean closed;
public NioThread(String name, ExecutorService workExecutor, Selector selector,
ObjectPool<ByteBuffer> unsafeBufferPool, ObjectPool<ByteBuffer> safeBufferPool,
ObjectPool<Response> unsafeResponsePool, ObjectPool<Response> safeResponsePool) {
super(name, workExecutor, null);
this.selector = selector;
this.setDaemon(true);
this.bufferSupplier = () -> inCurrThread() ? unsafeBufferPool.get() : safeBufferPool.get();
this.bufferConsumer = (v) -> {
if (inCurrThread()) {
unsafeBufferPool.accept(v);
} else {
safeBufferPool.accept(v);
}
};
this.responseSupplier = () -> inCurrThread() ? unsafeResponsePool.get() : safeResponsePool.get();
this.responseConsumer = (v) -> {
if (inCurrThread()) {
unsafeResponsePool.accept(v);
} else {
safeResponsePool.accept(v);
}
};
}
public void register(Consumer<Selector> consumer) {
registers.offer(consumer);
selector.wakeup();
}
public Supplier<ByteBuffer> getBufferSupplier() {
return bufferSupplier;
}
public Consumer<ByteBuffer> getBufferConsumer() {
return bufferConsumer;
}
public Supplier<Response> getResponseSupplier() {
return responseSupplier;
}
public Consumer<Response> getResponseConsumer() {
return responseConsumer;
}
@Override
public void run() {
this.localThread = Thread.currentThread();
while (!this.closed) {
try {
Consumer<Selector> register;
while ((register = registers.poll()) != null) {
register.accept(selector);
}
int count = selector.select();
if (count == 0) continue;
Set<SelectionKey> keys = selector.selectedKeys();
Iterator<SelectionKey> it = keys.iterator();
while (it.hasNext()) {
SelectionKey key = it.next();
it.remove();
if (!key.isValid()) continue;
NioTcpAsyncConnection conn = (NioTcpAsyncConnection) key.attachment();
if (key.isWritable()) {
//key.interestOps(key.interestOps() & ~SelectionKey.OP_WRITE);
conn.doWrite();
} else if (key.isReadable()) {
conn.doRead();
} else if (key.isConnectable()) {
conn.doConnect();
}
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
public void close() {
this.closed = true;
this.interrupt();
}
}

View File

@@ -1,87 +0,0 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.net;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
import org.redkale.net.Response;
import org.redkale.util.ObjectPool;
/**
* 协议处理的IO线程组
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*
* @since 2.1.0
*/
public class NioThreadGroup {
private NioThread[] threads;
private final AtomicInteger index = new AtomicInteger();
private ScheduledThreadPoolExecutor timeoutExecutor;
public NioThreadGroup(final String serverName, ExecutorService workExecutor, int iothreads,
ObjectPool<ByteBuffer> safeBufferPool, ObjectPool<Response> safeResponsePool) throws IOException {
this.threads = new NioThread[Math.max(iothreads, 1)];
for (int i = 0; i < this.threads.length; i++) {
ObjectPool<ByteBuffer> unsafeBufferPool = ObjectPool.createUnsafePool(safeBufferPool.getCreatCounter(),
safeBufferPool.getCycleCounter(), 8,
safeBufferPool.getCreator(), safeBufferPool.getPrepare(), safeBufferPool.getRecycler());
ObjectPool<Response> unsafeResponsePool = ObjectPool.createUnsafePool(safeResponsePool.getCreatCounter(),
safeResponsePool.getCycleCounter(), 8,
safeResponsePool.getCreator(), safeResponsePool.getPrepare(), safeResponsePool.getRecycler());
String name = "Redkale-" + serverName + "-ServletThread" + "-" + (i >= 9 ? (i + 1) : ("0" + (i + 1)));
this.threads[i] = new NioThread(name, workExecutor, Selector.open(), unsafeBufferPool, safeBufferPool, unsafeResponsePool, safeResponsePool);
}
this.timeoutExecutor = (ScheduledThreadPoolExecutor) Executors.newScheduledThreadPool(1, (Runnable r) -> {
Thread t = new Thread(r);
t.setName("Redkale-" + serverName + "-IOTimeoutThread");
t.setDaemon(true);
return t;
});
}
public void start() {
for (NioThread thread : threads) {
thread.start();
}
}
public void close() {
for (NioThread thread : threads) {
thread.close();
}
this.timeoutExecutor.shutdownNow();
}
public NioThread nextThread() {
return threads[Math.abs(index.getAndIncrement()) % threads.length];
}
public ScheduledFuture scheduleTimeout(Runnable callable, long delay, TimeUnit unit) {
return timeoutExecutor.schedule(callable, delay, unit);
}
public void interestOpsOr(NioThread thread, SelectionKey key, int opt) {
if (key == null) return;
if (key.selector() != thread.selector) throw new RuntimeException("NioThread.selector not the same to SelectionKey.selector");
if ((key.interestOps() & opt) != 0) return;
key.interestOps(key.interestOps() | opt);
if (thread.inCurrThread()) return;
//非IO线程中
key.selector().wakeup();
}
}

View File

@@ -1,183 +0,0 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.net;
import java.io.IOException;
import java.nio.*;
import java.nio.channels.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.*;
import org.redkale.util.*;
/**
* 根Servlet的处理逻辑类
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*/
@SuppressWarnings("unchecked")
public class PrepareRunner implements Runnable {
private final AsyncConnection channel;
private final Context context;
private final ObjectPool<Response> responsePool;
private ByteBuffer data;
private 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;
}
@Override
public void run() {
if (data != null) { //BIO模式的UDP连接创建AsyncConnection时已经获取到ByteBuffer数据了
if (response == null) response = responsePool.get();
try {
response.init(channel);
codec(data, response);
} catch (Throwable t) {
context.logger.log(Level.WARNING, "prepare servlet abort, force to close channel ", t);
response.finish(true);
}
return;
}
if (response == null) response = responsePool.get();
try {
channel.read(new CompletionHandler<Integer, ByteBuffer>() {
@Override
public void completed(Integer count, ByteBuffer buffer) {
if (count < 1) {
buffer.clear();
channel.setReadBuffer(buffer);
channel.dispose();// response.init(channel); 在调用之前异常
response.removeChannel();
response.finish(true);
return;
}
// { //测试
// buffer.flip();
// byte[] bs = new byte[buffer.remaining()];
// buffer.get(bs);
// System.println(new String(bs));
// }
buffer.flip();
try {
response.init(channel);
codec(buffer, response);
} catch (Throwable t) { //此处不可 context.offerBuffer(buffer); 以免prepare.prepare内部异常导致重复 offerBuffer
context.logger.log(Level.WARNING, "prepare servlet abort, force to close channel ", t);
response.finish(true);
}
}
@Override
public void failed(Throwable exc, ByteBuffer buffer) {
buffer.clear();
channel.setReadBuffer(buffer);
channel.dispose();// response.init(channel); 在调用之前异常
response.removeChannel();
response.finish(true);
if (exc != null && context.logger.isLoggable(Level.FINEST)) {
context.logger.log(Level.FINEST, "Servlet Handler read channel erroneous, force to close channel ", exc);
}
}
});
} catch (Exception te) {
channel.dispose();// response.init(channel); 在调用之前异常
response.removeChannel();
response.finish(true);
if (context.logger.isLoggable(Level.FINEST)) {
context.logger.log(Level.FINEST, "Servlet read channel erroneous, force to close channel ", te);
}
}
}
protected void codec(final ByteBuffer buffer, final Response response) throws IOException {
final Request request = response.request;
final PrepareServlet preparer = context.prepare;
preparer.executeCounter.incrementAndGet();
final int rs = request.readHeader(buffer);
if (rs < 0) { //表示数据格式不正确
channel.offerBuffer(buffer);
if (rs != Integer.MIN_VALUE) preparer.illRequestCounter.incrementAndGet();
response.finish(true);
} else if (rs == 0) {
if (buffer.hasRemaining()) {
request.setMoredata(buffer);
} else {
buffer.clear();
channel.setReadBuffer(buffer);
}
preparer.prepare(request, response);
} else {
buffer.clear();
channel.setReadBuffer(buffer);
final AtomicInteger ai = new AtomicInteger(rs);
channel.read(new CompletionHandler<Integer, ByteBuffer>() {
@Override
public void completed(Integer result, ByteBuffer attachment) {
attachment.flip();
ai.addAndGet(-request.readBody(attachment));
if (ai.get() > 0) {
attachment.clear();
channel.setReadBuffer(attachment);
channel.read(this);
} else {
if (attachment.hasRemaining()) {
request.setMoredata(attachment);
} else {
attachment.clear();
channel.setReadBuffer(attachment);
}
try {
preparer.prepare(request, response);
} catch (Throwable t) { //此处不可 context.offerBuffer(buffer); 以免preparer.prepare内部异常导致重复 offerBuffer
context.logger.log(Level.WARNING, "prepare servlet abort, force to close channel ", t);
response.finish(true);
}
}
}
@Override
public void failed(Throwable exc, ByteBuffer attachment) {
preparer.illRequestCounter.incrementAndGet();
attachment.clear();
channel.setReadBuffer(attachment);
response.finish(true);
if (exc != null) request.context.logger.log(Level.FINER, "Servlet read channel erroneous, force to close channel ", exc);
}
});
}
}
protected void initResponse(Response response, AsyncConnection channel) {
response.init(channel);
}
protected Response pollResponse() {
return responsePool.get();
}
protected Request pollRequest(Response response) {
return response.request;
}
protected AsyncConnection removeChannel(Response response) {
return response.removeChannel();
}
}

View File

@@ -9,6 +9,7 @@ import java.io.*;
import java.util.*;
import java.util.concurrent.atomic.*;
import java.util.function.Predicate;
import java.util.logging.Level;
import org.redkale.util.*;
/**
@@ -207,11 +208,16 @@ public abstract class PrepareServlet<K extends Serializable, C extends Context,
@SuppressWarnings("unchecked")
public abstract void addServlet(S servlet, Object attachment, AnyValue conf, K... mappings);
public final void prepare(final R request, final P response) throws IOException {
request.prepare();
response.filter = this.headFilter;
response.servlet = this;
response.nextEvent();
public final void prepare(final R request, final P response) {
try {
request.prepare();
response.filter = this.headFilter;
response.servlet = this;
response.nextEvent();
} catch (Throwable t) {
response.context.logger.log(Level.WARNING, "prepare servlet abort, force to close channel ", t);
response.finish(true);
}
}
protected AnyValue getServletConf(Servlet servlet) {

View File

@@ -0,0 +1,171 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.net;
import java.nio.ByteBuffer;
import java.nio.channels.CompletionHandler;
import java.util.function.*;
import java.util.logging.Level;
/**
*
* @author zhangjx
*/
class ProtocolCodec implements CompletionHandler<Integer, ByteBuffer> {
private final AsyncConnection channel;
private final Context context;
private Supplier<Response> responseSupplier;
private Consumer<Response> responseConsumer;
private Response resp;
public ProtocolCodec(Context context, Supplier<Response> responseSupplier,
Consumer<Response> responseConsumer, AsyncConnection channel) {
this.context = context;
this.channel = channel;
this.responseSupplier = responseSupplier;
this.responseConsumer = responseConsumer;
}
public ProtocolCodec response(Response resp) {
this.resp = resp;
return this;
}
private Response createResponse() {
Response response = resp;
if (response == null) {
response = responseSupplier.get();
} else {
response.prepare();
}
response.responseSupplier = responseSupplier;
response.responseConsumer = responseConsumer;
return response;
}
@Override
public void completed(Integer count, ByteBuffer buffer) {
if (count < 1) {
channel.offerBuffer(buffer);
channel.dispose(); // response.init(channel); 在调用之前异常
return;
}
// { //测试
// buffer.flip();
// byte[] bs = new byte[buffer.remaining()];
// buffer.get(bs);
// System.println(new String(bs));
// }
buffer.flip();
final Response response = createResponse();
try {
decode(buffer, response, 0, null);
} catch (Throwable t) { //此处不可 context.offerBuffer(buffer); 以免prepare.prepare内部异常导致重复 offerBuffer
context.logger.log(Level.WARNING, "prepare servlet abort, force to close channel ", t);
response.finish(true);
}
}
@Override
public void failed(Throwable exc, ByteBuffer buffer) {
channel.offerBuffer(buffer);
channel.dispose();// response.init(channel); 在调用之前异常
if (exc != null && context.logger.isLoggable(Level.FINEST)) {
context.logger.log(Level.FINEST, "Servlet Handler read channel erroneous, force to close channel ", exc);
}
}
public void run(final ByteBuffer data) {
if (data != null) { //pipeline模式或UDP连接创建AsyncConnection时已经获取到ByteBuffer数据了
final Response response = createResponse();
try {
decode(data, response, 0, null);
} catch (Throwable t) {
context.logger.log(Level.WARNING, "prepare servlet abort, force to close channel ", t);
response.finish(true);
}
return;
}
try {
channel.read(this);
} catch (Exception te) {
channel.dispose();// response.init(channel); 在调用之前异常
if (context.logger.isLoggable(Level.FINEST)) {
context.logger.log(Level.FINEST, "Servlet read channel erroneous, force to close channel ", te);
}
}
}
protected void decode(final ByteBuffer buffer, final Response response, final int pipelineIndex, final Request lastreq) {
response.init(channel);
final Request request = response.request;
final int rs = request.readHeader(buffer, lastreq);
if (rs < 0) { //表示数据格式不正确
final PrepareServlet preparer = context.prepare;
preparer.executeCounter.incrementAndGet();
channel.offerBuffer(buffer);
if (rs != Integer.MIN_VALUE) preparer.illRequestCounter.incrementAndGet();
response.finish(true);
if (context.logger.isLoggable(Level.FINEST)) {
context.logger.log(Level.FINEST, "request.readHeader erroneous (" + rs + "), force to close channel ");
}
} else if (rs == 0) {
final PrepareServlet preparer = context.prepare;
preparer.executeCounter.incrementAndGet();
int pindex = pipelineIndex;
boolean pipeline = false;
Request hreq = lastreq;
if (buffer.hasRemaining()) {
pipeline = true;
if (pindex == 0) pindex++;
request.pipeline(pindex, pindex + 1);
if (hreq == null) hreq = request.copyHeader();
} else {
request.pipeline(pindex, pindex);
channel.setReadBuffer((ByteBuffer) buffer.clear());
}
context.executePrepareServlet(request, response);
if (pipeline) {
final Response pipelineResponse = createResponse();
try {
decode(buffer, pipelineResponse, pindex + 1, hreq);
} catch (Throwable t) { //此处不可 offerBuffer(buffer); 以免prepare.prepare内部异常导致重复 offerBuffer
context.logger.log(Level.WARNING, "prepare pipeline servlet abort, force to close channel ", t);
pipelineResponse.finish(true);
}
}
} else {
channel.setReadBuffer(buffer);
channel.read(new CompletionHandler<Integer, ByteBuffer>() {
@Override
public void completed(Integer count, ByteBuffer attachment) {
if (count < 1) {
channel.offerBuffer(attachment);
channel.dispose();
return;
}
attachment.flip();
decode(attachment, response, pipelineIndex, lastreq);
}
@Override
public void failed(Throwable exc, ByteBuffer attachment) {
context.prepare.illRequestCounter.incrementAndGet();
channel.offerBuffer(attachment);
response.finish(true);
if (exc != null) request.context.logger.log(Level.FINER, "Servlet read channel erroneous, force to close channel ", exc);
}
});
}
}
}

View File

@@ -8,7 +8,8 @@ package org.redkale.net;
import java.io.IOException;
import java.net.*;
import java.util.*;
import java.util.concurrent.atomic.AtomicLong;
import javax.annotation.Resource;
import org.redkale.boot.Application;
import org.redkale.util.AnyValue;
/**
@@ -21,20 +22,14 @@ import org.redkale.util.AnyValue;
*/
public abstract class ProtocolServer {
//创建数
protected final AtomicLong createCounter = new AtomicLong();
//关闭数
protected final AtomicLong closedCounter = new AtomicLong();
//在线数
protected final AtomicLong livingCounter = new AtomicLong();
protected final Context context;
//最大连接数小于1表示无限制
protected int maxconns;
@Resource
protected Application application;
public abstract void open(AnyValue config) throws IOException;
public abstract void bind(SocketAddress local, int backlog) throws IOException;
@@ -43,7 +38,7 @@ public abstract class ProtocolServer {
public abstract <T> void setOption(SocketOption<T> name, T value) throws IOException;
public abstract void accept(Server server) throws IOException;
public abstract void accept(Application application, Server server) throws IOException;
public abstract void close() throws IOException;
@@ -52,45 +47,28 @@ public abstract class ProtocolServer {
this.maxconns = context.getMaxconns();
}
public long getCreateCount() {
return createCounter.longValue();
}
public long getClosedCount() {
return closedCounter.longValue();
}
public long getLivingCount() {
return livingCounter.longValue();
}
//---------------------------------------------------------------------
public static ProtocolServer create(String protocol, Context context, ClassLoader classLoader, String netimpl) {
if (netimpl != null) netimpl = netimpl.trim();
if ("TCP".equalsIgnoreCase(protocol)) {
if (netimpl == null || netimpl.isEmpty()) {
return new AioTcpProtocolServer(context);
} else if ("aio".equalsIgnoreCase(netimpl)) {
return new AioTcpProtocolServer(context);
} else if ("nio".equalsIgnoreCase(netimpl)) {
return new NioTcpProtocolServer(context);
}
return new AsyncNioTcpProtocolServer(context);
} else if ("UDP".equalsIgnoreCase(protocol)) {
if (netimpl == null || netimpl.isEmpty()) {
return null;// return new UdpBioProtocolServer(context);
} else if ("bio".equalsIgnoreCase(netimpl)) {
return null;// return new UdpBioProtocolServer(context);
}
return new AsyncNioUdpProtocolServer(context);
} else if (netimpl == null || netimpl.isEmpty()) {
throw new RuntimeException("ProtocolServer not support protocol " + protocol);
throw new RuntimeException(ProtocolServer.class.getSimpleName() + " not support protocol " + protocol);
}
try {
if (classLoader == null) classLoader = Thread.currentThread().getContextClassLoader();
Class clazz = classLoader.loadClass(netimpl);
return (ProtocolServer) clazz.getDeclaredConstructor(Context.class).newInstance(context);
} catch (Exception e) {
throw new RuntimeException("ProtocolServer(netimple=" + netimpl + ") newinstance error", e);
throw new RuntimeException(ProtocolServer.class.getSimpleName() + "(netimple=" + netimpl + ") newinstance error", e);
}
}
public abstract long getCreateConnectionCount();
public abstract long getClosedConnectionCount();
public abstract long getLivingConnectionCount();
}

View File

@@ -31,9 +31,13 @@ public abstract class Request<C extends Context> {
protected boolean keepAlive;
protected boolean more; //pipeline模式
protected int pipelineIndex;
protected ByteBuffer moredata; //pipeline模式
protected int pipelineCount;
protected boolean pipelineOver;
protected int hashid;
protected AsyncConnection channel;
@@ -51,41 +55,35 @@ public abstract class Request<C extends Context> {
this.jsonConvert = context.getJsonConvert();
}
protected void setMoredata(ByteBuffer buffer) {
this.moredata = buffer;
protected Request copyHeader() {
return null;
}
protected ByteBuffer removeMoredata() {
ByteBuffer rs = this.moredata;
this.moredata = null;
return rs;
protected Request pipeline(int pipelineIndex, int pipelineCount) {
this.pipelineIndex = pipelineIndex;
this.pipelineCount = pipelineCount;
return this;
}
/**
* 返回值Integer.MIN_VALUE: 帧数据; -1数据不合法 0解析完毕 &gt;0: 需再读取的字节数。
*
* @param buffer ByteBuffer对象
* @param last 同一Channel的上一个Request
*
* @return 缺少的字节数
*/
protected abstract int readHeader(ByteBuffer buffer);
/**
* 读取buffer并返回读取的有效数据长度
*
* @param buffer ByteBuffer对象
*
* @return 有效数据长度
*/
protected abstract int readBody(ByteBuffer buffer);
protected abstract int readHeader(ByteBuffer buffer, Request last);
protected abstract void prepare();
protected void recycle() {
hashid = 0;
createtime = 0;
pipelineIndex = 0;
pipelineCount = 0;
pipelineOver = false;
keepAlive = false;
more = false;
moredata = null;
attributes.clear();
channel = null; // close it by response
}
@@ -136,4 +134,13 @@ public abstract class Request<C extends Context> {
return createtime;
}
public int getHashid() {
return hashid;
}
public Request<C> hashid(int hashid) {
this.hashid = hashid;
return this;
}
}

View File

@@ -10,7 +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;
import org.redkale.util.ByteTuple;
/**
* 协议响应对象
@@ -25,11 +25,11 @@ import org.redkale.util.ObjectPool;
@SuppressWarnings("unchecked")
public abstract class Response<C extends Context, R extends Request<C>> {
protected static final boolean respConvertByBuffer = Boolean.getBoolean("resp.convert.bytebuffer");
protected final C context;
protected final ObjectPool<Response> responsePool; //虚拟构建的Response可能不存在responsePool
protected Supplier<Response> responseSupplier; //虚拟构建的Response可能不存在responseSupplier
protected Consumer<Response> responseConsumer; //虚拟构建的Response可能不存在responseConsumer
protected final R request;
@@ -45,20 +45,26 @@ public abstract class Response<C extends Context, R extends Request<C>> {
protected Servlet<C, R, ? extends Response<C, R>> servlet;
private final CompletionHandler finishHandler = new CompletionHandler<Integer, ByteBuffer>() {
private final CompletionHandler finishBytesHandler = new CompletionHandler<Integer, Void>() {
@Override
public void completed(Integer result, Void attachment) {
finish();
}
@Override
public void failed(Throwable exc, Void attachment) {
finish(true);
}
};
private final CompletionHandler finishBufferHandler = new CompletionHandler<Integer, ByteBuffer>() {
@Override
public void completed(Integer result, ByteBuffer attachment) {
if (attachment.hasRemaining()) {
channel.write(attachment, attachment, this);
} else {
channel.offerBuffer(attachment);
ByteBuffer data = request.removeMoredata();
final boolean more = data != null && request.keepAlive;
request.more = more;
finish();
if (more) new PrepareRunner(context, responsePool, request.channel, null, Response.this).run();
}
channel.offerBuffer(attachment);
finish();
}
@Override
@@ -69,51 +75,33 @@ public abstract class Response<C extends Context, R extends Request<C>> {
};
private final CompletionHandler finishHandler2 = new CompletionHandler<Integer, ByteBuffer[]>() {
private final CompletionHandler finishBuffersHandler = new CompletionHandler<Integer, ByteBuffer[]>() {
@Override
public void completed(final Integer result, final ByteBuffer[] attachments) {
int index = -1;
for (int i = 0; i < attachments.length; i++) {
if (attachments[i].hasRemaining()) {
index = i;
break;
}
}
if (index >= 0) {
channel.write(attachments, index, attachments.length - index, attachments, this);
} else {
if (attachments != null) {
for (ByteBuffer attachment : attachments) {
channel.offerBuffer(attachment);
}
ByteBuffer data = request.removeMoredata();
final boolean more = data != null && request.keepAlive;
request.more = more;
finish();
if (more) new PrepareRunner(context, responsePool, request.channel, null, Response.this).run();
}
finish();
}
@Override
public void failed(Throwable exc, final ByteBuffer[] attachments) {
for (ByteBuffer attachment : attachments) {
channel.offerBuffer(attachment);
if (attachments != null) {
for (ByteBuffer attachment : attachments) {
channel.offerBuffer(attachment);
}
}
finish(true);
}
};
protected Response(C context, final R request, ObjectPool<Response> responsePool) {
protected Response(C context, final R request) {
this.context = context;
this.request = request;
this.responsePool = responsePool;
}
protected void offerBuffer(ByteBuffer... buffers) {
for (ByteBuffer buffer : buffers) {
channel.offerBuffer(buffer);
}
}
protected AsyncConnection removeChannel() {
@@ -125,6 +113,7 @@ public abstract class Response<C extends Context, R extends Request<C>> {
protected void prepare() {
inited = true;
request.prepare();
}
protected boolean recycle() {
@@ -132,11 +121,14 @@ public abstract class Response<C extends Context, R extends Request<C>> {
this.output = null;
this.filter = null;
this.servlet = null;
boolean notpipeline = request.pipelineIndex == 0 || request.pipelineOver;
request.recycle();
if (channel != null) {
channel.dispose();
if (notpipeline) channel.dispose();
channel = null;
}
this.responseSupplier = null;
this.responseConsumer = null;
this.inited = false;
return true;
}
@@ -207,73 +199,127 @@ public abstract class Response<C extends Context, R extends Request<C>> {
}
this.recycleListener = null;
}
if (request.more) removeChannel();
if (request.keepAlive && !request.more && channel != null) {
if (channel.isOpen()) {
AsyncConnection conn = removeChannel();
this.recycle();
this.prepare();
new PrepareRunner(context, this.responsePool, conn, null, this).run();
if (request.keepAlive && (request.pipelineIndex == 0 || request.pipelineOver)) {
AsyncConnection conn = removeChannel();
if (conn != null && conn.protocolCodec != null) {
this.responseConsumer.accept(this);
conn.read(conn.protocolCodec);
} else {
channel.dispose();
Supplier<Response> poolSupplier = this.responseSupplier;
Consumer<Response> poolConsumer = this.responseConsumer;
this.recycle();
new ProtocolCodec(context, poolSupplier, poolConsumer, conn).response(this).run(null);
}
} else {
this.responsePool.accept(this);
this.responseConsumer.accept(this);
}
}
public void finish(final byte[] bs) {
public final void finish(final byte[] bs) {
finish(false, bs, 0, bs.length);
}
public final void finish(final byte[] bs, int offset, int length) {
finish(false, bs, offset, length);
}
public final void finish(final ByteTuple array) {
finish(false, array.content(), array.offset(), array.length());
}
public final void finish(boolean kill, final byte[] bs) {
finish(kill, bs, 0, bs.length);
}
public final void finish(boolean kill, final ByteTuple array) {
finish(kill, array.content(), array.offset(), array.length());
}
public void finish(boolean kill, final byte[] bs, int offset, int length) {
if (!this.inited) return; //避免重复关闭
if (this.context.bufferCapacity == bs.length) {
ByteBuffer buffer = channel.getBufferSupplier().get();
buffer.put(bs);
buffer.flip();
this.finish(buffer);
if (kill) refuseAlive();
if (this.channel.hasPipelineData()) {
this.channel.flushPipelineData(null, new CompletionHandler<Integer, Void>() {
@Override
public void completed(Integer result, Void attachment) {
channel.write(bs, offset, length, finishBytesHandler);
}
@Override
public void failed(Throwable exc, Void attachment) {
finishBytesHandler.failed(exc, attachment);
}
});
} else {
this.finish(ByteBuffer.wrap(bs));
this.channel.write(bs, offset, length, finishBytesHandler);
}
}
public void finish(ByteBuffer buffer) {
if (!this.inited) return; //避免重复关闭
final AsyncConnection conn = this.channel;
// ByteBuffer data = this.request.removeMoredata();
// final boolean more = data != null && this.request.keepAlive;
// this.request.more = more;
conn.write(buffer, buffer, finishHandler);
// if (more) new PrepareRunner(this.context, this.responsePool, conn, data, null).run();
protected final void finish(ByteBuffer buffer) {
finish(false, buffer);
}
public void finish(boolean kill, ByteBuffer buffer) {
protected final void finish(ByteBuffer... buffers) {
finish(false, buffers);
}
protected void finish(boolean kill, ByteBuffer buffer) {
if (!this.inited) return; //避免重复关闭
if (kill) refuseAlive();
final AsyncConnection conn = this.channel;
// ByteBuffer data = this.request.removeMoredata();
// final boolean more = data != null && this.request.keepAlive;
// this.request.more = more;
conn.write(buffer, buffer, finishHandler);
// if (more) new PrepareRunner(this.context, this.responsePool, conn, data, null).run();
if (this.channel.hasPipelineData()) {
this.channel.flushPipelineData(null, new CompletionHandler<Integer, Void>() {
@Override
public void completed(Integer result, Void attachment) {
channel.write(buffer, buffer, finishBufferHandler);
}
@Override
public void failed(Throwable exc, Void attachment) {
finishBufferHandler.failed(exc, buffer);
}
});
} else {
this.channel.write(buffer, buffer, finishBufferHandler);
}
}
public void finish(ByteBuffer... buffers) {
if (!this.inited) return; //避免重复关闭
final AsyncConnection conn = this.channel;
// ByteBuffer data = this.request.removeMoredata();
// final boolean more = data != null && this.request.keepAlive;
// this.request.more = more;
conn.write(buffers, buffers, finishHandler2);
// if (more) new PrepareRunner(this.context, this.responsePool, conn, data, null).run();
}
public void finish(boolean kill, ByteBuffer... buffers) {
protected void finish(boolean kill, ByteBuffer... buffers) {
if (!this.inited) return; //避免重复关闭
if (kill) refuseAlive();
final AsyncConnection conn = this.channel;
ByteBuffer data = this.request.removeMoredata();
final boolean more = data != null && this.request.keepAlive;
this.request.more = more;
conn.write(buffers, buffers, finishHandler2);
if (more) new PrepareRunner(this.context, this.responsePool, conn, data, null).run();
if (this.channel.hasPipelineData()) {
this.channel.flushPipelineData(null, new CompletionHandler<Integer, Void>() {
@Override
public void completed(Integer result, Void attachment) {
channel.write(buffers, buffers, finishBuffersHandler);
}
@Override
public void failed(Throwable exc, Void attachment) {
finishBuffersHandler.failed(exc, buffers);
}
});
} else {
this.channel.write(buffers, buffers, finishBuffersHandler);
}
}
protected <A> void send(final ByteTuple array, final CompletionHandler<Integer, Void> handler) {
this.channel.write(array, new CompletionHandler<Integer, Void>() {
@Override
public void completed(Integer result, Void attachment) {
if (handler != null) handler.completed(result, attachment);
}
@Override
public void failed(Throwable exc, Void attachment) {
if (handler != null) handler.failed(exc, attachment);
}
});
}
protected <A> void send(final ByteBuffer buffer, final A attachment, final CompletionHandler<Integer, A> handler) {
@@ -281,12 +327,8 @@ public abstract class Response<C extends Context, R extends Request<C>> {
@Override
public void completed(Integer result, A attachment) {
if (buffer.hasRemaining()) {
channel.write(buffer, attachment, this);
} else {
channel.offerBuffer(buffer);
if (handler != null) handler.completed(result, attachment);
}
channel.offerBuffer(buffer);
if (handler != null) handler.completed(result, attachment);
}
@Override
@@ -303,21 +345,8 @@ public abstract class Response<C extends Context, R extends Request<C>> {
@Override
public void completed(Integer result, A attachment) {
int index = -1;
for (int i = 0; i < buffers.length; i++) {
if (buffers[i].hasRemaining()) {
index = i;
break;
}
channel.offerBuffer(buffers[i]);
}
if (index == 0) {
channel.write(buffers, attachment, this);
} else if (index > 0) {
ByteBuffer[] newattachs = new ByteBuffer[buffers.length - index];
System.arraycopy(buffers, index, newattachs, 0, newattachs.length);
channel.write(newattachs, attachment, this);
} else if (handler != null) handler.completed(result, attachment);
channel.offerBuffer(buffers);
if (handler != null) handler.completed(result, attachment);
}
@Override

View File

@@ -9,12 +9,11 @@ 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.*;
import java.util.logging.*;
import javax.net.ssl.SSLContext;
import org.redkale.boot.Application;
import org.redkale.util.*;
/**
@@ -33,7 +32,8 @@ public abstract class Server<K extends Serializable, C extends Context, R extend
public static final String RESNAME_SERVER_ROOT = "SERVER_ROOT";
public static final String RESNAME_SERVER_EXECUTOR = "SERVER_EXECUTOR";
@Deprecated //@deprecated 2.3.0 使用RESNAME_APP_EXECUTOR
public static final String RESNAME_SERVER_EXECUTOR2 = "SERVER_EXECUTOR";
public static final String RESNAME_SERVER_RESFACTORY = "SERVER_RESFACTORY";
@@ -47,7 +47,7 @@ public abstract class Server<K extends Serializable, C extends Context, R extend
protected String name;
//应用层协议名
protected final String protocol;
protected final String netprotocol;
//依赖注入工厂类
protected final ResourceFactory resourceFactory;
@@ -82,12 +82,6 @@ public abstract class Server<K extends Serializable, C extends Context, R extend
//ByteBuffer的容量大小
protected int bufferCapacity;
//线程数
protected int threads;
//线程池
protected ThreadPoolExecutor workExecutor;
//ByteBuffer池大小
protected int bufferPoolSize;
@@ -109,9 +103,9 @@ public abstract class Server<K extends Serializable, C extends Context, R extend
//IO写入 的超时秒数小于1视为不设置
protected int writeTimeoutSeconds;
protected Server(long serverStartTime, String protocol, ResourceFactory resourceFactory, PrepareServlet<K, C, R, P, S> servlet) {
protected Server(Application application, long serverStartTime, String netprotocol, ResourceFactory resourceFactory, PrepareServlet<K, C, R, P, S> servlet) {
this.serverStartTime = serverStartTime;
this.protocol = protocol;
this.netprotocol = netprotocol;
this.resourceFactory = resourceFactory;
this.prepare = servlet;
}
@@ -127,12 +121,11 @@ public abstract class Server<K extends Serializable, C extends Context, R extend
this.writeTimeoutSeconds = config.getIntValue("writeTimeoutSeconds", 0);
this.backlog = parseLenth(config.getValue("backlog"), 1024);
this.maxbody = parseLenth(config.getValue("maxbody"), 64 * 1024);
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", Math.max(8, Runtime.getRuntime().availableProcessors() * 2));
this.bufferPoolSize = config.getIntValue("bufferPoolSize", this.threads * 4);
this.responsePoolSize = config.getIntValue("responsePoolSize", this.threads * 2);
this.name = config.getValue("name", "Server-" + protocol + "-" + this.address.getPort());
int bufCapacity = parseLenth(config.getValue("bufferCapacity"), "UDP".equalsIgnoreCase(netprotocol) ? 1350 : 32 * 1024);
this.bufferCapacity = "UDP".equalsIgnoreCase(netprotocol) ? bufCapacity : (bufCapacity < 1024 ? 1024 : bufCapacity);
this.bufferPoolSize = config.getIntValue("bufferPoolSize", Runtime.getRuntime().availableProcessors() * 4);
this.responsePoolSize = config.getIntValue("responsePoolSize", Runtime.getRuntime().availableProcessors() * 2);
this.name = config.getValue("name", "Server-" + (config == null ? netprotocol : config.getValue("protocol", netprotocol).replaceFirst("\\..+", "").toUpperCase()) + "-" + this.address.getPort());
if (!this.name.matches("^[a-zA-Z][\\w_-]{1,64}$")) throw new RuntimeException("server.name (" + this.name + ") is illegal");
AnyValue sslConf = config.getAnyValue("ssl");
if (sslConf != null) {
@@ -148,14 +141,6 @@ public abstract class Server<K extends Serializable, C extends Context, R extend
this.resourceFactory.inject(creator);
this.sslContext = creator.create(this, sslConf);
}
final AtomicInteger counter = new AtomicInteger();
final Format f = createFormat();
final String n = name;
this.workExecutor = (ThreadPoolExecutor) Executors.newFixedThreadPool(threads, (Runnable r) -> {
String threadname = "Redkale-" + n + "-WorkThread-" + f.format(counter.incrementAndGet());
Thread t = new WorkThread(threadname, workExecutor, r);
return t;
});
}
protected static int parseLenth(String value, int defValue) {
@@ -187,10 +172,6 @@ public abstract class Server<K extends Serializable, C extends Context, R extend
return resourceFactory;
}
public ThreadPoolExecutor getWorkExecutor() {
return workExecutor;
}
public InetSocketAddress getSocketAddress() {
return address;
}
@@ -199,8 +180,8 @@ public abstract class Server<K extends Serializable, C extends Context, R extend
return name;
}
public String getProtocol() {
return protocol;
public String getNetprotocol() {
return netprotocol;
}
public Logger getLogger() {
@@ -231,10 +212,6 @@ public abstract class Server<K extends Serializable, C extends Context, R extend
return bufferCapacity;
}
public int getThreads() {
return threads;
}
public int getBufferPoolSize() {
return bufferPoolSize;
}
@@ -263,36 +240,32 @@ public abstract class Server<K extends Serializable, C extends Context, R extend
return maxconns;
}
public void setThreads(int threads) {
int oldthreads = this.threads;
this.context.executor.setCorePoolSize(threads);
this.threads = threads;
logger.info("[" + Thread.currentThread().getName() + "] " + this.getClass().getSimpleName() + " change threads from " + oldthreads + " to " + threads);
}
@SuppressWarnings("unchecked")
public void addServlet(S servlet, final Object attachment, AnyValue conf, K... mappings) {
this.prepare.addServlet(servlet, attachment, conf, mappings);
}
public void start() throws IOException {
this.context = this.createContext();
public void start(Application application) throws IOException {
this.context = this.createContext(application);
this.prepare.init(this.context, config);
this.serverChannel = ProtocolServer.create(this.protocol, context, this.serverClassLoader, config == null ? null : config.getValue("netimpl"));
this.serverChannel = ProtocolServer.create(this.netprotocol, context, this.serverClassLoader, config == null ? null : config.getValue("netimpl"));
if (application != null) { //main函数调试时可能为null
application.getResourceFactory().inject(this.serverChannel);
}
this.serverChannel.open(config);
serverChannel.bind(address, backlog);
serverChannel.accept(this);
serverChannel.accept(application, this);
final String threadName = "[" + Thread.currentThread().getName() + "] ";
postStart();
logger.info(threadName + this.getClass().getSimpleName() + ("TCP".equalsIgnoreCase(protocol) ? "" : ("." + protocol)) + " listen: " + address
+ ", cpu: " + Runtime.getRuntime().availableProcessors() + ", threads: " + threads + ", maxbody: " + formatLenth(context.maxbody) + ", bufferCapacity: " + formatLenth(bufferCapacity) + ", bufferPoolSize: " + bufferPoolSize + ", responsePoolSize: " + responsePoolSize
logger.info(threadName + this.getClass().getSimpleName() + ("TCP".equalsIgnoreCase(netprotocol) ? "" : ("." + netprotocol)) + " listen: " + (address.getHostString() + ":" + address.getPort())
+ ", cpu: " + Runtime.getRuntime().availableProcessors() + ", 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 {
public void changeAddress(Application application, final InetSocketAddress addr) throws IOException {
long s = System.currentTimeMillis();
Objects.requireNonNull(addr);
final InetSocketAddress oldAddress = context.address;
@@ -300,10 +273,10 @@ public abstract class Server<K extends Serializable, C extends Context, R extend
context.address = addr;
ProtocolServer newServerChannel = null;
try {
newServerChannel = ProtocolServer.create(this.protocol, context, this.serverClassLoader, config == null ? null : config.getValue("netimpl"));
newServerChannel = ProtocolServer.create(this.netprotocol, context, this.serverClassLoader, config == null ? null : config.getValue("netimpl"));
newServerChannel.open(config);
newServerChannel.bind(addr, backlog);
newServerChannel.accept(this);
newServerChannel.accept(application, this);
} catch (IOException e) {
context.address = oldAddress;
throw e;
@@ -311,7 +284,7 @@ public abstract class Server<K extends Serializable, C extends Context, R extend
this.address = context.address;
this.serverChannel = newServerChannel;
final String threadName = "[" + Thread.currentThread().getName() + "] ";
logger.info(threadName + this.getClass().getSimpleName() + ("TCP".equalsIgnoreCase(protocol) ? "" : ("." + protocol))
logger.info(threadName + this.getClass().getSimpleName() + ("TCP".equalsIgnoreCase(netprotocol) ? "" : ("." + netprotocol))
+ " change address listen: " + address + ", started in " + (System.currentTimeMillis() - s) + " ms");
if (oldServerChannel != null) {
new Thread() {
@@ -360,7 +333,7 @@ public abstract class Server<K extends Serializable, C extends Context, R extend
if (this.context != null) this.context.aliveTimeoutSeconds = newAliveTimeoutSeconds;
}
protected abstract C createContext();
protected abstract C createContext(Application application);
//必须在 createContext()之后调用
protected abstract ObjectPool<ByteBuffer> createBufferPool(AtomicLong createCounter, AtomicLong cycleCounter, int bufferPoolSize);
@@ -370,12 +343,12 @@ public abstract class Server<K extends Serializable, C extends Context, R extend
public void shutdown() throws IOException {
long s = System.currentTimeMillis();
logger.info(this.getClass().getSimpleName() + "-" + this.protocol + " shutdowning");
logger.info(this.getClass().getSimpleName() + "-" + this.netprotocol + " shutdowning");
try {
this.serverChannel.close();
} catch (Exception e) {
}
logger.info(this.getClass().getSimpleName() + "-" + this.protocol + " shutdow prepare servlet");
logger.info(this.getClass().getSimpleName() + "-" + this.netprotocol + " shutdow prepare servlet");
this.prepare.destroy(this.context, config);
long e = System.currentTimeMillis() - s;
logger.info(this.getClass().getSimpleName() + " shutdown in " + e + " ms");
@@ -446,25 +419,17 @@ public abstract class Server<K extends Serializable, C extends Context, R extend
//创建数
public long getCreateConnectionCount() {
return serverChannel == null ? -1 : serverChannel.getCreateCount();
return serverChannel == null ? -1 : serverChannel.getCreateConnectionCount();
}
//关闭数
public long getClosedConnectionCount() {
return serverChannel == null ? -1 : serverChannel.getClosedCount();
return serverChannel == null ? -1 : serverChannel.getClosedConnectionCount();
}
//在线数
public long getLivingConnectionCount() {
return serverChannel == null ? -1 : serverChannel.getLivingCount();
}
protected Format createFormat() {
String sf = "0";
if (this.threads > 10) sf = "00";
if (this.threads > 100) sf = "000";
if (this.threads > 1000) sf = "0000";
return new DecimalFormat(sf);
return serverChannel == null ? -1 : serverChannel.getLivingConnectionCount();
}
public static URL[] loadLib(final RedkaleClassLoader classLoader, final Logger logger, final String lib) throws Exception {

View File

@@ -30,7 +30,7 @@ import org.redkale.util.*;
*/
public final class Transport {
public static final String DEFAULT_PROTOCOL = "TCP";
public static final String DEFAULT_NETPROTOCOL = "TCP";
protected final AtomicInteger seq = new AtomicInteger(-1);
@@ -40,17 +40,16 @@ public final class Transport {
protected final boolean tcp;
protected final String protocol;
protected final String netprotocol;
protected final AsynchronousChannelGroup group;
//传输端的AsyncGroup
protected final AsyncGroup asyncGroup;
protected final InetSocketAddress clientAddress;
//不可能为null
protected TransportNode[] transportNodes = new TransportNode[0];
protected final ObjectPool<ByteBuffer> bufferPool;
protected final SSLContext sslContext;
//负载均衡策略
@@ -59,23 +58,20 @@ public final class Transport {
//连接上限, 为null表示无限制
protected Semaphore semaphore;
protected Transport(String name, TransportFactory factory, final ObjectPool<ByteBuffer> transportBufferPool,
final AsynchronousChannelGroup transportChannelGroup, final SSLContext sslContext, final InetSocketAddress clientAddress,
protected Transport(String name, TransportFactory factory, final AsyncGroup asyncGroup, final SSLContext sslContext, final InetSocketAddress clientAddress,
final Collection<InetSocketAddress> addresses, final TransportStrategy strategy) {
this(name, DEFAULT_PROTOCOL, factory, transportBufferPool, transportChannelGroup, sslContext, clientAddress, addresses, strategy);
this(name, DEFAULT_NETPROTOCOL, factory, asyncGroup, sslContext, clientAddress, addresses, strategy);
}
protected Transport(String name, String protocol, final TransportFactory factory, final ObjectPool<ByteBuffer> transportBufferPool,
final AsynchronousChannelGroup transportChannelGroup, final SSLContext sslContext, final InetSocketAddress clientAddress,
protected Transport(String name, String netprotocol, final TransportFactory factory, final AsyncGroup asyncGroup, final SSLContext sslContext, final InetSocketAddress clientAddress,
final Collection<InetSocketAddress> addresses, final TransportStrategy strategy) {
this.name = name;
this.protocol = protocol;
this.netprotocol = netprotocol;
this.factory = factory;
factory.transportReferences.add(new WeakReference<>(this));
this.tcp = "TCP".equalsIgnoreCase(protocol);
this.group = transportChannelGroup;
this.tcp = "TCP".equalsIgnoreCase(netprotocol);
this.asyncGroup = asyncGroup;
this.sslContext = sslContext;
this.bufferPool = transportBufferPool;
this.clientAddress = clientAddress;
this.strategy = strategy;
updateRemoteAddresses(addresses);
@@ -190,44 +186,19 @@ public final class Transport {
@Override
public String toString() {
return Transport.class.getSimpleName() + "{name = " + name + ", protocol = " + protocol + ", clientAddress = " + clientAddress + ", remoteNodes = " + Arrays.toString(transportNodes) + "}";
return Transport.class.getSimpleName() + "{name = " + name + ", protocol = " + netprotocol + ", clientAddress = " + clientAddress + ", remoteNodes = " + Arrays.toString(transportNodes) + "}";
}
public ByteBuffer pollBuffer() {
return bufferPool.get();
}
public Supplier<ByteBuffer> getBufferSupplier() {
return bufferPool;
}
public void offerBuffer(ByteBuffer buffer) {
bufferPool.accept(buffer);
}
public void offerBuffer(ByteBuffer... buffers) {
for (ByteBuffer buffer : buffers) offerBuffer(buffer);
}
public AsynchronousChannelGroup getTransportChannelGroup() {
return group;
}
public String getProtocol() {
return protocol;
public String getNetprotocol() {
return netprotocol;
}
public boolean isTCP() {
return tcp;
}
protected CompletableFuture<AsyncConnection> pollAsync(TransportNode node, SocketAddress addr, Supplier<CompletableFuture<AsyncConnection>> func, final int count) {
if (count >= 5) {
CompletableFuture<AsyncConnection> future = new CompletableFuture<>();
future.completeExceptionally(new RuntimeException("create AsyncConnection error"));
return future;
}
final BlockingQueue<AsyncConnection> queue = node.conns;
protected CompletableFuture<AsyncConnection> pollAsync(TransportNode node, SocketAddress addr, Supplier<CompletableFuture<AsyncConnection>> func) {
final BlockingQueue<AsyncConnection> queue = node.connQueue;
if (!queue.isEmpty()) {
AsyncConnection conn;
while ((conn = queue.poll()) != null) {
@@ -239,24 +210,16 @@ public final class Transport {
}
}
if (semaphore != null && !semaphore.tryAcquire()) {
return CompletableFuture.supplyAsync(() -> {
try {
return queue.poll(1, TimeUnit.SECONDS);
} catch (Exception t) {
return null;
}
}, factory.executor).thenCompose((conn2) -> {
if (conn2 != null && conn2.isOpen()) {
return CompletableFuture.completedFuture(conn2);
}
return pollAsync(node, addr, func, count + 1);
});
final CompletableFuture<AsyncConnection> future = Utility.orTimeout(new CompletableFuture<>(), 10, TimeUnit.SECONDS);
future.whenComplete((r, t) -> node.pollQueue.remove(future));
if (node.pollQueue.offer(future)) return future;
future.completeExceptionally(new IOException("create transport connection error"));
return future;
}
return func.get().thenApply(conn -> {
if (conn != null && semaphore != null) conn.beforeCloseListener((c) -> semaphore.release());
return conn;
});
}
public CompletableFuture<AsyncConnection> pollConnection(SocketAddress addr0) {
@@ -269,17 +232,12 @@ public final class Transport {
try {
if (!tcp) { // UDP
SocketAddress udpaddr = rand ? nodes[0].address : addr;
DatagramChannel channel = DatagramChannel.open();
channel.configureBlocking(true);
channel.connect(udpaddr);
return CompletableFuture.completedFuture(AsyncConnection.create(bufferPool, channel, sslContext, udpaddr, true, factory.readTimeoutSeconds, factory.writeTimeoutSeconds));
return asyncGroup.createUDP(udpaddr, factory.readTimeoutSeconds, factory.writeTimeoutSeconds);
}
if (!rand) { //指定地址
TransportNode node = findTransportNode(addr);
if (node == null) {
return AsyncConnection.createTCP(bufferPool, group, sslContext, addr, factory.readTimeoutSeconds, factory.writeTimeoutSeconds);
}
return pollAsync(node, addr, () -> AsyncConnection.createTCP(bufferPool, group, sslContext, addr, factory.readTimeoutSeconds, factory.writeTimeoutSeconds), 1);
if (node == null) return asyncGroup.createTCP(addr, factory.readTimeoutSeconds, factory.writeTimeoutSeconds);
return pollAsync(node, addr, () -> asyncGroup.createTCP(addr, factory.readTimeoutSeconds, factory.writeTimeoutSeconds));
}
//---------------------随机取地址------------------------
@@ -292,7 +250,7 @@ public final class Transport {
final long now = System.currentTimeMillis();
if (enablecount > 0) { //存在可用的地址
final TransportNode one = newnodes[Math.abs(seq.incrementAndGet()) % enablecount];
final BlockingQueue<AsyncConnection> queue = one.conns;
final BlockingQueue<AsyncConnection> queue = one.connQueue;
if (!queue.isEmpty()) {
AsyncConnection conn;
while ((conn = queue.poll()) != null) {
@@ -304,52 +262,11 @@ public final class Transport {
}
}
return pollAsync(one, one.getAddress(), () -> {
CompletableFuture future = new CompletableFuture();
AsynchronousSocketChannel channel0 = null;
try {
channel0 = AsynchronousSocketChannel.open(group);
channel0.setOption(StandardSocketOptions.TCP_NODELAY, true);
channel0.setOption(StandardSocketOptions.SO_KEEPALIVE, true);
channel0.setOption(StandardSocketOptions.SO_REUSEADDR, true);
} catch (Exception ex) {
ex.printStackTrace();
}
final AsynchronousSocketChannel channel = channel0;
channel.connect(one.address, one, new CompletionHandler<Void, TransportNode>() {
@Override
public void completed(Void result, TransportNode attachment) {
attachment.disabletime = 0;
AsyncConnection asyncConn = AsyncConnection.create(bufferPool, channel, attachment.address, factory.readTimeoutSeconds, factory.writeTimeoutSeconds);
if (future.isDone()) {
if (!attachment.conns.offer(asyncConn)) asyncConn.dispose();
} else {
future.complete(asyncConn);
}
}
@Override
public void failed(Throwable exc, TransportNode attachment) {
attachment.disabletime = now;
try {
channel.close();
} catch (Exception e) {
}
try {
pollConnection0(nodes, one, now).whenComplete((r, t) -> {
if (t != null) {
future.completeExceptionally(t);
} else {
future.complete(r);
}
});
} catch (Exception e) {
future.completeExceptionally(e);
}
}
});
return future;
}, 1);
return asyncGroup.createTCP(one.address, factory.readTimeoutSeconds, factory.writeTimeoutSeconds)
.whenComplete((c, t) -> {
one.disabletime = t == null ? 0 : System.currentTimeMillis();
});
});
}
return pollConnection0(nodes, null, now);
} catch (Exception ex) {
@@ -359,43 +276,15 @@ public final class Transport {
private CompletableFuture<AsyncConnection> pollConnection0(TransportNode[] nodes, TransportNode exclude, long now) throws IOException {
//从可用/不可用的地址列表中创建连接
AtomicInteger count = new AtomicInteger(nodes.length);
CompletableFuture future = new CompletableFuture();
for (final TransportNode node : nodes) {
if (node == exclude) continue;
if (future.isDone()) return future;
final AsynchronousSocketChannel channel = AsynchronousSocketChannel.open(group);
channel.setOption(StandardSocketOptions.TCP_NODELAY, true);
channel.setOption(StandardSocketOptions.SO_KEEPALIVE, true);
channel.setOption(StandardSocketOptions.SO_REUSEADDR, true);
channel.connect(node.address, node, new CompletionHandler<Void, TransportNode>() {
@Override
public void completed(Void result, TransportNode attachment) {
try {
attachment.disabletime = 0;
AsyncConnection asyncConn = AsyncConnection.create(bufferPool, channel, attachment.address, factory.readTimeoutSeconds, factory.writeTimeoutSeconds);
if (future.isDone()) {
if (!attachment.conns.offer(asyncConn)) asyncConn.dispose();
} else {
future.complete(asyncConn);
}
} catch (Exception e) {
failed(e, attachment);
}
}
@Override
public void failed(Throwable exc, TransportNode attachment) {
attachment.disabletime = now;
if (count.decrementAndGet() < 1) {
future.completeExceptionally(exc);
}
try {
channel.close();
} catch (Exception e) {
}
}
});
asyncGroup.createTCP(node.address, factory.readTimeoutSeconds, factory.writeTimeoutSeconds)
.whenComplete((c, t) -> {
if (c != null && !future.complete(c)) node.connQueue.offer(c);
node.disabletime = t == null ? 0 : System.currentTimeMillis();
});
}
return future;
}
@@ -405,7 +294,7 @@ public final class Transport {
if (!forceClose && conn.isTCP()) {
if (conn.isOpen()) {
TransportNode node = findTransportNode(conn.getRemoteAddress());
if (node == null || !node.conns.offer(conn)) conn.dispose();
if (node == null || !node.connQueue.offer(conn)) conn.dispose();
} else {
conn.dispose();
}
@@ -459,25 +348,29 @@ public final class Transport {
protected volatile long disabletime; //不可用时的时间, 为0表示可用
protected final BlockingQueue<AsyncConnection> conns;
protected final BlockingQueue<AsyncConnection> connQueue;
protected final ArrayBlockingQueue<CompletableFuture<AsyncConnection>> pollQueue;
protected final ConcurrentHashMap<String, Object> attributes = new ConcurrentHashMap<>();
public TransportNode(int poolmaxconns, InetSocketAddress address) {
this.address = address;
this.disabletime = 0;
this.conns = new ArrayBlockingQueue<>(poolmaxconns);
this.connQueue = new ArrayBlockingQueue<>(poolmaxconns);
this.pollQueue = new ArrayBlockingQueue(this.connQueue.remainingCapacity() * 100);
}
@ConstructorParameters({"poolmaxconns", "address", "disabletime"})
public TransportNode(int poolmaxconns, InetSocketAddress address, long disabletime) {
this.address = address;
this.disabletime = disabletime;
this.conns = new LinkedBlockingQueue<>(poolmaxconns);
this.connQueue = new LinkedBlockingQueue<>(poolmaxconns);
this.pollQueue = new ArrayBlockingQueue(this.connQueue.remainingCapacity() * 100);
}
public int getPoolmaxconns() {
return this.conns.remainingCapacity() + this.conns.size();
return this.connQueue.remainingCapacity() + this.connQueue.size();
}
public <T> T setAttribute(String name, T value) {
@@ -518,13 +411,13 @@ public final class Transport {
}
@ConvertDisabled
public BlockingQueue<AsyncConnection> getConns() {
return conns;
public BlockingQueue<AsyncConnection> getConnQueue() {
return connQueue;
}
public void dispose() {
AsyncConnection conn;
while ((conn = conns.poll()) != null) {
while ((conn = connQueue.poll()) != null) {
conn.dispose();
}
}

View File

@@ -5,15 +5,12 @@
*/
package org.redkale.net;
import java.io.IOException;
import java.lang.ref.WeakReference;
import java.net.*;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.*;
import java.util.function.Supplier;
import java.util.logging.*;
import java.util.stream.Collectors;
import javax.net.ssl.SSLContext;
@@ -45,14 +42,8 @@ public class TransportFactory {
protected static final Logger logger = Logger.getLogger(TransportFactory.class.getSimpleName());
//传输端的线程池
protected final ExecutorService executor;
//传输端的ByteBuffer对象池
protected final ObjectPool<ByteBuffer> bufferPool;
//传输端的ChannelGroup
protected final AsynchronousChannelGroup channelGroup;
//传输端的AsyncGroup
protected final AsyncGroup asyncGroup;
//每个地址对应的Group名
protected final Map<InetSocketAddress, String> groupAddrs = new HashMap<>();
@@ -90,23 +81,25 @@ public class TransportFactory {
//pong的数据长度, 小于0表示不进行判断
protected int pongLength;
//是否TCP
protected String netprotocol = "TCP";
//负载均衡策略
protected final TransportStrategy strategy;
protected TransportFactory(ExecutorService executor, ObjectPool<ByteBuffer> bufferPool, AsynchronousChannelGroup channelGroup,
SSLContext sslContext, int readTimeoutSeconds, int writeTimeoutSeconds, final TransportStrategy strategy) {
this.executor = executor;
this.bufferPool = bufferPool;
this.channelGroup = channelGroup;
protected TransportFactory(AsyncGroup asyncGroup, SSLContext sslContext, String netprotocol,
int readTimeoutSeconds, int writeTimeoutSeconds, final TransportStrategy strategy) {
this.asyncGroup = asyncGroup;
this.sslContext = sslContext;
this.netprotocol = netprotocol;
this.readTimeoutSeconds = readTimeoutSeconds;
this.writeTimeoutSeconds = writeTimeoutSeconds;
this.strategy = strategy;
}
protected TransportFactory(ExecutorService executor, ObjectPool<ByteBuffer> bufferPool, AsynchronousChannelGroup channelGroup,
SSLContext sslContext, int readTimeoutSeconds, int writeTimeoutSeconds) {
this(executor, bufferPool, channelGroup, sslContext, readTimeoutSeconds, writeTimeoutSeconds, null);
protected TransportFactory(AsyncGroup asyncGroup, SSLContext sslContext, String netprotocol,
int readTimeoutSeconds, int writeTimeoutSeconds) {
this(asyncGroup, sslContext, netprotocol, readTimeoutSeconds, writeTimeoutSeconds, null);
}
public void init(AnyValue conf, ByteBuffer pingBuffer, int pongLength) {
@@ -143,71 +136,28 @@ public class TransportFactory {
}
}
public static TransportFactory create(int threads) {
return create(threads, threads * 2, 8 * 1024, DEFAULT_READTIMEOUTSECONDS, DEFAULT_WRITETIMEOUTSECONDS);
public static TransportFactory create(AsyncGroup asyncGroup, int readTimeoutSeconds, int writeTimeoutSeconds) {
return new TransportFactory(asyncGroup, null, "TCP", readTimeoutSeconds, writeTimeoutSeconds, null);
}
public static TransportFactory create(int threads, int bufferPoolSize, int bufferCapacity) {
return create(threads, bufferPoolSize, bufferCapacity, DEFAULT_READTIMEOUTSECONDS, DEFAULT_WRITETIMEOUTSECONDS);
public static TransportFactory create(AsyncGroup asyncGroup, SSLContext sslContext, int readTimeoutSeconds, int writeTimeoutSeconds, final TransportStrategy strategy) {
return new TransportFactory(asyncGroup, sslContext, "TCP", readTimeoutSeconds, writeTimeoutSeconds, strategy);
}
public static TransportFactory create(int threads, int bufferPoolSize, int bufferCapacity, int readTimeoutSeconds, int writeTimeoutSeconds) {
final ObjectPool<ByteBuffer> transportPool = ObjectPool.createSafePool(new AtomicLong(), new AtomicLong(), bufferPoolSize,
(Object... params) -> ByteBuffer.allocateDirect(bufferCapacity), null, (e) -> {
if (e == null || e.isReadOnly() || e.capacity() != bufferCapacity) return false;
e.clear();
return true;
});
final AtomicInteger counter = new AtomicInteger();
ExecutorService transportExec = Executors.newFixedThreadPool(threads, (Runnable r) -> {
Thread t = new Thread(r);
t.setDaemon(true);
t.setName("Redkale-Transport-Thread-" + counter.incrementAndGet());
return t;
});
AsynchronousChannelGroup transportGroup = null;
try {
transportGroup = AsynchronousChannelGroup.withCachedThreadPool(transportExec, 1);
} catch (IOException e) {
throw new RuntimeException(e);
}
return create(transportExec, transportPool, transportGroup, readTimeoutSeconds, writeTimeoutSeconds);
public static TransportFactory create(AsyncGroup asyncGroup, String netprotocol, int readTimeoutSeconds, int writeTimeoutSeconds) {
return new TransportFactory(asyncGroup, null, netprotocol, readTimeoutSeconds, writeTimeoutSeconds, null);
}
public static TransportFactory create(ExecutorService executor, ObjectPool<ByteBuffer> bufferPool, AsynchronousChannelGroup channelGroup) {
return new TransportFactory(executor, bufferPool, channelGroup, null, DEFAULT_READTIMEOUTSECONDS, DEFAULT_WRITETIMEOUTSECONDS, null);
}
public static TransportFactory create(ExecutorService executor, ObjectPool<ByteBuffer> bufferPool, AsynchronousChannelGroup channelGroup,
int readTimeoutSeconds, int writeTimeoutSeconds) {
return new TransportFactory(executor, bufferPool, channelGroup, null, readTimeoutSeconds, writeTimeoutSeconds, null);
}
public static TransportFactory create(ExecutorService executor, ObjectPool<ByteBuffer> bufferPool, AsynchronousChannelGroup channelGroup,
int readTimeoutSeconds, int writeTimeoutSeconds, final TransportStrategy strategy) {
return new TransportFactory(executor, bufferPool, channelGroup, null, readTimeoutSeconds, writeTimeoutSeconds, strategy);
}
public static TransportFactory create(ExecutorService executor, ObjectPool<ByteBuffer> bufferPool, AsynchronousChannelGroup channelGroup, SSLContext sslContext) {
return new TransportFactory(executor, bufferPool, channelGroup, sslContext, DEFAULT_READTIMEOUTSECONDS, DEFAULT_WRITETIMEOUTSECONDS, null);
}
public static TransportFactory create(ExecutorService executor, ObjectPool<ByteBuffer> bufferPool, AsynchronousChannelGroup channelGroup,
SSLContext sslContext, int readTimeoutSeconds, int writeTimeoutSeconds) {
return new TransportFactory(executor, bufferPool, channelGroup, sslContext, readTimeoutSeconds, writeTimeoutSeconds, null);
}
public static TransportFactory create(ExecutorService executor, ObjectPool<ByteBuffer> bufferPool, AsynchronousChannelGroup channelGroup,
SSLContext sslContext, int readTimeoutSeconds, int writeTimeoutSeconds, final TransportStrategy strategy) {
return new TransportFactory(executor, bufferPool, channelGroup, sslContext, readTimeoutSeconds, writeTimeoutSeconds, strategy);
public static TransportFactory create(AsyncGroup asyncGroup, SSLContext sslContext, String netprotocol, int readTimeoutSeconds, int writeTimeoutSeconds, final TransportStrategy strategy) {
return new TransportFactory(asyncGroup, sslContext, netprotocol, readTimeoutSeconds, writeTimeoutSeconds, strategy);
}
public Transport createTransportTCP(String name, final InetSocketAddress clientAddress, final Collection<InetSocketAddress> addresses) {
return new Transport(name, "TCP", this, this.bufferPool, this.channelGroup, this.sslContext, clientAddress, addresses, strategy);
return new Transport(name, "TCP", this, this.asyncGroup, this.sslContext, clientAddress, addresses, strategy);
}
public Transport createTransport(String name, String protocol, final InetSocketAddress clientAddress, final Collection<InetSocketAddress> addresses) {
return new Transport(name, protocol, this, this.bufferPool, this.channelGroup, this.sslContext, clientAddress, addresses, strategy);
public Transport createTransport(String name, String netprotocol, final InetSocketAddress clientAddress, final Collection<InetSocketAddress> addresses) {
return new Transport(name, netprotocol, this, this.asyncGroup, this.sslContext, clientAddress, addresses, strategy);
}
public String findGroupName(InetSocketAddress addr) {
@@ -269,17 +219,13 @@ public class TransportFactory {
if (info == null) continue;
addresses.addAll(info.addresses);
}
if (info == null) info = new TransportGroupInfo("TCP");
if (info == null) {
info = new TransportGroupInfo(netprotocol);
} else {
info.protocol = netprotocol;
}
if (sncpAddress != null) addresses.remove(sncpAddress);
return new Transport(groups.stream().sorted().collect(Collectors.joining(";")), info.protocol, this, this.bufferPool, this.channelGroup, this.sslContext, sncpAddress, addresses, this.strategy);
}
public ExecutorService getExecutor() {
return executor;
}
public Supplier<ByteBuffer> getBufferSupplier() {
return bufferPool;
return new Transport(groups.stream().sorted().collect(Collectors.joining(";")), info.protocol, this, this.asyncGroup, this.sslContext, sncpAddress, addresses, this.strategy);
}
public List<TransportGroupInfo> getGroupInfos() {
@@ -306,11 +252,6 @@ public class TransportFactory {
public void shutdownNow() {
if (this.scheduler != null) this.scheduler.shutdownNow();
try {
this.channelGroup.shutdownNow();
} catch (Exception e) {
logger.log(Level.FINER, "close transportChannelGroup erroneous", e);
}
}
private void checks() {
@@ -324,21 +265,11 @@ public class TransportFactory {
Transport.TransportNode[] nodes = transport.getTransportNodes();
for (final Transport.TransportNode node : nodes) {
if (node.disabletime < 1) continue; //可用
try {
final AsynchronousSocketChannel channel = AsynchronousSocketChannel.open(transport.group);
channel.connect(node.address, node, new CompletionHandler<Void, Transport.TransportNode>() {
@Override
public void completed(Void result, Transport.TransportNode attachment) {
attachment.disabletime = 0;
}
@Override
public void failed(Throwable exc, Transport.TransportNode attachment) {
attachment.disabletime = System.currentTimeMillis();
}
});
} catch (Exception e) {
}
CompletableFuture<AsyncConnection> future = Utility.orTimeout(asyncGroup.createTCP(node.address), 2, TimeUnit.SECONDS);
future.whenComplete((r, t) -> {
node.disabletime = t == null ? 0 : System.currentTimeMillis();
if (r != null) r.dispose();
});
}
}
for (WeakReference ref : nulllist) {
@@ -353,7 +284,7 @@ public class TransportFactory {
if (transport == null) continue;
Transport.TransportNode[] nodes = transport.getTransportNodes();
for (final Transport.TransportNode node : nodes) {
final BlockingQueue<AsyncConnection> queue = node.conns;
final BlockingQueue<AsyncConnection> queue = node.connQueue;
AsyncConnection conn;
while ((conn = queue.poll()) != null) {
if (conn.getLastWriteTime() > timex && false) { //最近几秒内已经进行过IO操作
@@ -365,10 +296,6 @@ public class TransportFactory {
localconn.write(sendBuffer, sendBuffer, new CompletionHandler<Integer, ByteBuffer>() {
@Override
public void completed(Integer result, ByteBuffer wbuffer) {
if (wbuffer.hasRemaining()) {
localconn.write(wbuffer, wbuffer, this);
return;
}
localconn.read(new CompletionHandler<Integer, ByteBuffer>() {
int counter = 0;

View File

@@ -6,6 +6,7 @@
package org.redkale.net;
import java.util.concurrent.*;
import org.redkale.util.ThreadHashExecutor;
/**
* 协议处理的自定义线程类
@@ -21,15 +22,42 @@ public class WorkThread extends Thread {
protected final ExecutorService workExecutor;
protected final ThreadHashExecutor hashExecutor;
public WorkThread(String name, ExecutorService workExecutor, Runnable target) {
super(target);
if (name != null) setName(name);
this.workExecutor = workExecutor;
this.hashExecutor = workExecutor instanceof ThreadHashExecutor ? (ThreadHashExecutor) workExecutor : null;
this.setDaemon(true);
}
public void runAsync(Runnable runner) {
workExecutor.execute(runner);
public void runWork(Runnable command) {
if (workExecutor == null) {
command.run();
} else {
workExecutor.execute(command);
}
}
public void runAsync(Runnable command) {
if (workExecutor == null) {
ForkJoinPool.commonPool().execute(command);
} else {
workExecutor.execute(command);
}
}
public void runAsync(int hash, Runnable command) {
if (hashExecutor == null) {
if (workExecutor == null) {
ForkJoinPool.commonPool().execute(command);
} else {
workExecutor.execute(command);
}
} else {
hashExecutor.execute(hash, command);
}
}
public ExecutorService getWorkExecutor() {

View File

@@ -0,0 +1,219 @@
/*
* 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.client;
import java.net.SocketAddress;
import java.util.concurrent.*;
import java.util.concurrent.atomic.*;
import java.util.function.*;
import org.redkale.net.*;
import org.redkale.util.*;
/**
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
* @since 2.3.0
*
* @param <R> 请求对象
* @param <P> 响应对象
*/
public abstract class Client<R extends ClientRequest, P> {
protected final AsyncGroup group; //连接构造器
protected final boolean tcp; //是否TCP协议
protected final SocketAddress address; //连接的地址
protected final ConcurrentLinkedDeque<CompletableFuture<ClientConnection>> connQueue = new ConcurrentLinkedDeque();
protected final Creator<ClientCodec<R, P>> codecCreator;
protected final ScheduledThreadPoolExecutor timeoutScheduler;
protected final AtomicLong writeReqCounter = new AtomicLong();
protected final AtomicLong pollRespCounter = new AtomicLong();
protected ScheduledFuture timeoutFuture;
protected ClientConnection<R, P>[] connArray; //连接池
protected AtomicInteger[] connResps; //连接当前处理数
protected AtomicBoolean[] connFlags; //conns的标记组当conn不存在或closed状态标记为false
protected int connLimit = Runtime.getRuntime().availableProcessors(); //最大连接数
protected int maxPipelines = 16; //单个连接最大并行处理数
protected int readTimeoutSeconds;
protected int writeTimeoutSeconds;
//------------------ 可选项 ------------------
//PING心跳的请求数据为null且pingInterval<1表示不需要定时ping
protected R pingRequest;
//关闭请求的数据, 为null表示直接关闭
protected R closeRequest;
//创建连接后进行的登录鉴权操作
protected Function<CompletableFuture<ClientConnection>, CompletableFuture<ClientConnection>> authenticate;
protected Client(AsyncGroup group, SocketAddress address, Creator<ClientCodec<R, P>> responseCreator) {
this(group, true, address, Runtime.getRuntime().availableProcessors(), 16, responseCreator, null, null, null);
}
protected Client(AsyncGroup group, boolean tcp, SocketAddress address, Creator<ClientCodec<R, P>> codecCreator) {
this(group, tcp, address, Runtime.getRuntime().availableProcessors(), 16, codecCreator, null, null, null);
}
protected Client(AsyncGroup group, boolean tcp, SocketAddress address, int maxconns, Creator<ClientCodec<R, P>> codecCreator) {
this(group, tcp, address, maxconns, 16, codecCreator, null, null, null);
}
protected Client(AsyncGroup group, boolean tcp, SocketAddress address, int maxconns, int maxPipelines, Creator<ClientCodec<R, P>> codecCreator) {
this(group, tcp, address, maxconns, maxPipelines, codecCreator, null, null, null);
}
protected Client(AsyncGroup group, boolean tcp, SocketAddress address, int maxconns, Creator<ClientCodec<R, P>> codecCreator,
Function<CompletableFuture<ClientConnection>, CompletableFuture<ClientConnection>> authenticate) {
this(group, tcp, address, maxconns, 16, codecCreator, null, null, authenticate);
}
protected Client(AsyncGroup group, boolean tcp, SocketAddress address, int maxconns, Creator<ClientCodec<R, P>> codecCreator,
R closeRequest, Function<CompletableFuture<ClientConnection>, CompletableFuture<ClientConnection>> authenticate) {
this(group, tcp, address, maxconns, 16, codecCreator, null, closeRequest, authenticate);
}
protected Client(AsyncGroup group, boolean tcp, SocketAddress address, int maxconns,
int maxPipelines, Creator<ClientCodec<R, P>> codecCreator, R pingRequest, R closeRequest,
Function<CompletableFuture<ClientConnection>, CompletableFuture<ClientConnection>> authenticate) {
if (maxPipelines < 1) throw new IllegalArgumentException("maxPipelines must bigger 0");
this.group = group;
this.tcp = tcp;
this.address = address;
this.connLimit = maxconns;
this.maxPipelines = maxPipelines;
this.pingRequest = pingRequest;
this.closeRequest = closeRequest;
this.codecCreator = codecCreator;
this.authenticate = authenticate;
this.connArray = new ClientConnection[connLimit];
this.connFlags = new AtomicBoolean[connLimit];
this.connResps = new AtomicInteger[connLimit];
for (int i = 0; i < connFlags.length; i++) {
this.connFlags[i] = new AtomicBoolean();
this.connResps[i] = new AtomicInteger();
}
//timeoutScheduler 不仅仅给超时用, 还给write用
this.timeoutScheduler = new ScheduledThreadPoolExecutor(1, (Runnable r) -> {
final Thread t = new Thread(r, "Redkale-" + Client.this.getClass().getSimpleName() + "-Interval-Thread");
t.setDaemon(true);
return t;
});
if (pingRequest != null) {
this.timeoutFuture = this.timeoutScheduler.scheduleAtFixedRate(() -> {
try {
ClientRequest req = pingRequest;
if (req == null) { //可能运行中进行重新赋值
timeoutFuture.cancel(true);
timeoutFuture = null;
return;
}
long now = System.currentTimeMillis();
for (ClientConnection conn : this.connArray) {
if (conn == null) continue;
if (now - conn.getLastWriteTime() > 10_000) conn.writeChannel(req);
}
} catch (Throwable t) {
}
}, 10, 10, TimeUnit.SECONDS);
}
}
public void close() {
this.timeoutScheduler.shutdownNow();
final R closereq = closeRequest;
for (ClientConnection conn : this.connArray) {
if (conn == null) continue;
if (closereq == null) {
conn.dispose(null);
} else {
try {
conn.writeChannel(closereq).get(100, TimeUnit.MILLISECONDS);
} catch (Exception e) {
}
conn.dispose(null);
}
}
}
public CompletableFuture<P> sendAsync(R request) {
return connect().thenCompose(conn -> conn.writeChannel(request));
}
protected CompletableFuture<P> writeChannel(ClientConnection conn, R request) {
return conn.writeChannel(request);
}
protected CompletableFuture<ClientConnection> connect() {
ClientConnection minRunningConn = null;
for (int i = 0; i < this.connArray.length; i++) {
final int index = i;
final ClientConnection conn = this.connArray[index];
if (conn == null || !conn.isOpen()) {
if (this.connFlags[index].compareAndSet(false, true)) {
CompletableFuture<ClientConnection> future = group.create(tcp, address, readTimeoutSeconds, writeTimeoutSeconds).thenApply(c -> createClientConnection(index, c));
return (authenticate == null ? future : authenticate.apply(future)).thenApply(c -> {
c.authenticated = true;
this.connArray[index] = c;
return c;
}).whenComplete((r, t) -> {
if (t != null) this.connFlags[index].set(false);
});
}
} else if (conn.runningCount() < 1) {
return CompletableFuture.completedFuture(conn);
} else if (minRunningConn == null || minRunningConn.runningCount() > conn.runningCount()) {
minRunningConn = conn;
}
}
if (minRunningConn != null && minRunningConn.runningCount() < maxPipelines) return CompletableFuture.completedFuture(minRunningConn);
return waitClientConnection();
}
protected CompletableFuture<ClientConnection> waitClientConnection() {
CompletableFuture rs = Utility.orTimeout(new CompletableFuture(), 6, TimeUnit.SECONDS);
connQueue.offer(rs);
return rs;
}
protected ClientConnection createClientConnection(final int index, AsyncConnection channel) {
return new ClientConnection(this, index, channel);
}
public int getReadTimeoutSeconds() {
return readTimeoutSeconds;
}
public void setReadTimeoutSeconds(int readTimeoutSeconds) {
this.readTimeoutSeconds = readTimeoutSeconds;
}
public int getWriteTimeoutSeconds() {
return writeTimeoutSeconds;
}
public void setWriteTimeoutSeconds(int writeTimeoutSeconds) {
this.writeTimeoutSeconds = writeTimeoutSeconds;
}
}

View File

@@ -0,0 +1,57 @@
/*
* 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.client;
import java.nio.ByteBuffer;
import java.util.*;
import org.redkale.convert.json.JsonConvert;
import org.redkale.util.ByteArray;
/**
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
* @since 2.3.0
* @param <R> ClientRequest
* @param <P> 响应对象
*/
public abstract class ClientCodec<R extends ClientRequest, P> {
private final List<ClientResult<P>> results = new ArrayList<>();
public ClientCodec() {
}
//返回true后array会clear
public abstract boolean codecResult(ClientConnection conn, List<R> requests, ByteBuffer buffer, ByteArray array);
public List<ClientResult<P>> removeResults() {
if (results.isEmpty()) return null;
List<ClientResult<P>> rs = new ArrayList<>(results);
this.results.clear();
return rs;
}
public void addResult(P result) {
this.results.add(new ClientResult<>(result));
}
public void addResult(Throwable exc) {
this.results.add(new ClientResult<>(exc));
}
public void reset() {
this.results.clear();
}
@Override
public String toString() {
return JsonConvert.root().convertTo(this);
}
}

View File

@@ -0,0 +1,273 @@
/*
* 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.client;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.List;
import java.util.concurrent.*;
import java.util.concurrent.atomic.*;
import java.util.function.*;
import java.util.stream.*;
import org.redkale.net.AsyncConnection;
import org.redkale.util.ByteArray;
/**
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
* @since 2.3.0
*
* @param <R> 请求对象
* @param <P> 响应对象
*/
public class ClientConnection<R extends ClientRequest, P> implements Consumer<AsyncConnection> {
protected final int index;
protected final Client<R, P> client;
protected final AtomicInteger respCounter;
protected final AsyncConnection channel;
protected final ByteArray writeArray = new ByteArray();
protected final ByteArray readArray = new ByteArray();
protected final AtomicBoolean readPending = new AtomicBoolean();
protected final AtomicBoolean writePending = new AtomicBoolean();
protected final ConcurrentLinkedDeque<R> requestQueue = new ConcurrentLinkedDeque();
protected final ConcurrentLinkedDeque<ClientFuture> responseQueue = new ConcurrentLinkedDeque();
protected final CompletionHandler<Integer, Void> writeHandler = new CompletionHandler<Integer, Void>() {
@Override
public void completed(Integer result, Void attachment) {
if (writeLastRequest != null && writeLastRequest == client.closeRequest) {
if (closeFuture != null) closeFuture.complete(null);
closeFuture = null;
return;
}
if (continueWrite()) return;
if (!writePending.compareAndSet(true, false)) {
if (continueWrite()) return;
}
readChannel();
}
@Override
public void failed(Throwable exc, Void attachment) {
dispose(exc);
}
};
private boolean continueWrite() {
writeArray.clear();
int pipelines = client.maxPipelines > 1 ? (client.maxPipelines - responseQueue.size()) : 1;
currPipelineIndex = 0;
for (int i = 0; i < pipelines; i++) {
R r = requestQueue.poll();
if (r == null) break;
writeLastRequest = r;
r.accept(ClientConnection.this, writeArray);
currPipelineIndex++;
}
if (writeArray.length() > 0) {
channel.write(writeArray, writeHandler);
return true;
}
return false;
}
protected final CompletionHandler<Integer, ByteBuffer> readHandler = new CompletionHandler<Integer, ByteBuffer>() {
ClientCodec<R, P> codec;
@Override
public void completed(Integer count, ByteBuffer attachment) {
if (count < 1) {
channel.setReadBuffer(attachment);
dispose(new NonReadableChannelException());
return;
}
try {
if (codec == null) codec = client.codecCreator.create();
attachment.flip();
codecResponse(attachment);
} catch (Exception e) {
channel.setReadBuffer(attachment);
dispose(e);
}
}
public void codecResponse(ByteBuffer buffer) {
Stream<R> reqstream = responseQueue.stream().map(r -> (R) r.request);
List<R> requests = reqstream.collect(Collectors.toList());
if (codec.codecResult(ClientConnection.this, requests, buffer, readArray)) { //成功了
readArray.clear();
List<ClientResult<P>> results = codec.removeResults();
if (results != null) {
for (ClientResult<P> rs : results) {
ClientFuture respFuture = responseQueue.poll();
if (respFuture != null) {
respCounter.decrementAndGet();
if (isAuthenticated()) client.pollRespCounter.incrementAndGet();
try {
if (respFuture.timeout != null) respFuture.timeout.cancel(true);
if (rs.exc != null) {
respFuture.completeExceptionally(rs.exc);
} else {
respFuture.complete(rs.result);
}
} catch (Throwable t) {
t.printStackTrace();
}
}
}
}
{
CompletableFuture<ClientConnection> connFuture = client.connQueue.poll();
if (connFuture != null) connFuture.complete(ClientConnection.this);
}
if (buffer.hasRemaining()) {
codecResponse(buffer);
} else if (responseQueue.isEmpty()) { //队列都已处理完了
buffer.clear();
channel.setReadBuffer(buffer);
if (readPending.compareAndSet(true, false)) {
CompletableFuture<ClientConnection> connFuture = client.connQueue.poll();
if (connFuture != null) connFuture.complete(ClientConnection.this);
} else {
channel.read(this);
}
} else {
buffer.clear();
channel.setReadBuffer(buffer);
channel.read(this);
}
} else { //数据不全, 继续读
buffer.clear();
channel.setReadBuffer(buffer);
channel.read(this);
}
}
@Override
public void failed(Throwable t, ByteBuffer attachment) {
dispose(t);
}
};
protected boolean authenticated;
protected int currPipelineIndex;
protected ClientFuture closeFuture;
private R writeLastRequest;
public ClientConnection(Client client, int index, AsyncConnection channel) {
this.client = client;
this.index = index;
this.respCounter = client.connResps[index];
this.channel = channel.beforeCloseListener(this);
}
protected CompletableFuture<P> writeChannel(R request) {
ClientFuture respFuture = createClientFuture(request);
if (request == client.closeRequest) {
respFuture.request = null;
closeFuture = respFuture;
} else {
int rts = this.channel.getReadTimeoutSeconds();
if (rts > 0 && respFuture.request != null) {
respFuture.responseQueue = responseQueue;
respFuture.timeout = client.timeoutScheduler.schedule(respFuture, rts, TimeUnit.SECONDS);
}
}
synchronized (requestQueue) { //保证顺序一致
responseQueue.offer(respFuture.request == null ? ClientFuture.EMPTY : respFuture);
requestQueue.offer(request);
respCounter.incrementAndGet();
if (isAuthenticated()) client.writeReqCounter.incrementAndGet();
}
if (writePending.compareAndSet(false, true)) {
writeArray.clear();
int pipelines = client.maxPipelines > 1 ? client.maxPipelines : 1; //pipelines必须大于0
currPipelineIndex = 0;
for (int i = 0; i < pipelines; i++) {
R r = requestQueue.poll();
if (r == null) break;
r.accept(ClientConnection.this, writeArray);
currPipelineIndex++;
}
channel.write(writeArray, writeHandler);
}
return respFuture;
}
protected ClientFuture createClientFuture(R request) {
return new ClientFuture(request);
}
protected void readChannel() {
if (readPending.compareAndSet(false, true)) {
readArray.clear();
channel.read(readHandler);
}
}
public boolean isAuthenticated() {
return authenticated;
}
public AsyncConnection getChannel() {
return channel;
}
public int currPipelineIndex() {
return currPipelineIndex;
}
@Override //AsyncConnection.beforeCloseListener
public void accept(AsyncConnection t) {
respCounter.set(0);
client.connFlags[index].set(false);
client.connArray[index] = null; //必须connflags之后
}
public void dispose(Throwable exc) {
channel.dispose();
Throwable e = exc;
CompletableFuture f;
respCounter.set(0);
while ((f = responseQueue.poll()) != null) {
if (e == null) e = new ClosedChannelException();
f.completeExceptionally(e);
}
}
public int runningCount() {
return respCounter.get();
}
public long getLastWriteTime() {
return channel.getLastWriteTime();
}
public boolean isOpen() {
return channel.isOpen();
}
}

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.net.client;
import java.util.concurrent.*;
/**
*
* @author zhangjx
* @param <T> 泛型
*/
public class ClientFuture<T> extends CompletableFuture<T> implements Runnable {
public static final ClientFuture EMPTY = new ClientFuture() {
@Override
public boolean complete(Object value) {
return true;
}
@Override
public boolean completeExceptionally(Throwable ex) {
return true;
}
};
protected ClientRequest request;
ScheduledFuture timeout;
ConcurrentLinkedDeque<ClientFuture> responseQueue;
public ClientFuture() {
super();
}
public ClientFuture(ClientRequest request) {
super();
this.request = request;
}
//@Override //JDK9+
public <U> ClientFuture<U> newIncompleteFuture() {
return new ClientFuture<>();
}
public <R extends ClientRequest> R getRequest() {
return (R) request;
}
@Override
public void run() {
if (responseQueue != null) responseQueue.remove(this);
this.completeExceptionally(new TimeoutException());
}
}

View File

@@ -0,0 +1,35 @@
/*
* 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.client;
import java.util.function.*;
import org.redkale.util.ByteArray;
/**
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
* @since 2.3.0
*/
public interface ClientRequest extends BiConsumer<ClientConnection, ByteArray> {
public static class ClientBytesRequest implements ClientRequest {
protected byte[] bytes;
public ClientBytesRequest(byte[] bytes) {
this.bytes = bytes;
}
@Override
public void accept(ClientConnection conn, ByteArray array) {
array.put(bytes);
}
}
}

View File

@@ -0,0 +1,47 @@
/*
* 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.client;
/**
*
* @author zhangjx
*/
public class ClientResult<P> {
protected P result;
protected Throwable exc;
public ClientResult(P result) {
this.result = result;
}
public ClientResult(Throwable exc) {
this.exc = exc;
}
public P getResult() {
return result;
}
public void setResult(P result) {
this.result = result;
}
public Throwable getExc() {
return exc;
}
public void setExc(Throwable exc) {
this.exc = exc;
}
@Override
public String toString() {
if (exc != null) return "{\"exc\":" + exc + "}";
return "{\"result\":" + result + "}";
}
}

Some files were not shown because too many files have changed in this diff Show More