Redkale 2.3.0 结束
This commit is contained in:
@@ -51,4 +51,12 @@ public @interface Cacheable {
|
||||
* @return int
|
||||
*/
|
||||
int interval() default 0;
|
||||
|
||||
/**
|
||||
* DataSource是否直接返回对象的真实引用, 而不是copy一份
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
boolean direct() default false;
|
||||
|
||||
}
|
||||
|
||||
@@ -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: ( <tt>visit</tt> | <tt>visitEnum</tt> |
|
||||
* <tt>visitAnnotation</tt> | <tt>visitArray</tt> )* <tt>visitEnd</tt>.
|
||||
*
|
||||
* @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
|
||||
* <tt>null</tt> 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
|
||||
* <tt>null</tt> 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>.
|
||||
|
||||
@@ -77,7 +77,7 @@ final class AnnotationWriter extends AnnotationVisitor {
|
||||
private int size;
|
||||
|
||||
/**
|
||||
* <code>true<code> if values are named, <code>false</code> otherwise. Annotation
|
||||
* <tt>true<tt> if values are named, <tt>false</tt> 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.
|
||||
* <tt>true<tt> if values are named, <tt>false</tt> 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 <tt>parent</tt> 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.
|
||||
* <tt>null</tt> if the annotation targets 'typeRef' as a whole.
|
||||
* @param out
|
||||
* where the type reference and type path must be put.
|
||||
*/
|
||||
|
||||
@@ -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 <tt>null</tt>.
|
||||
*/
|
||||
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 <tt>true</tt> if this type of attribute is unknown. The default
|
||||
* implementation of this method always returns <tt>true</tt>.
|
||||
*
|
||||
* @return <code>true</code> if this type of attribute is unknown.
|
||||
* @return <tt>true</tt> 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 <tt>true</tt> if this type of attribute is a code attribute.
|
||||
*
|
||||
* @return <code>true</code> if this type of attribute is a code attribute.
|
||||
* @return <tt>true</tt> 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 <tt>null</tt> 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 <tt>len</tt> 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 <tt>null</tt> 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 <tt>null</tt> 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 <tt>null</tt> 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 <tt>null</tt> 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 <tt>null</tt> 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 <tt>null</tt> 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 <tt>null</tt> 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()
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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 <tt>null</tt> to put <tt>len</tt>
|
||||
* null bytes into this byte vector.
|
||||
* @param off
|
||||
* index of the fist byte of b that must be copied.
|
||||
|
||||
@@ -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 <tt>null</tt> 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>.
|
||||
* <tt>null</tt>.
|
||||
*
|
||||
* @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
|
||||
* <tt>true</tt> 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 <tt>null</tt> 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 <tt>null</tt> to skip this
|
||||
* attribute.
|
||||
*/
|
||||
private Attribute readAttribute(final Attribute[] attrs, final String type,
|
||||
|
||||
@@ -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: <tt>visit</tt> [ <tt>visitSource</tt> ] [
|
||||
* <tt>visitModule</tt> ][ <tt>visitOuterClass</tt> ] ( <tt>visitAnnotation</tt> |
|
||||
* <tt>visitTypeAnnotation</tt> | <tt>visitAttribute</tt> )* (
|
||||
* <tt>visitInnerClass</tt> | <tt>visitField</tt> | <tt>visitMethod</tt> )*
|
||||
* <tt>visitEnd</tt>.
|
||||
*
|
||||
* @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 <tt>null</tt> 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.
|
||||
* <tt>null</tt>, 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>.
|
||||
* <tt>null</tt>.
|
||||
*/
|
||||
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 <tt>null</tt>.
|
||||
* @param debug
|
||||
* additional debug information to compute the correspondance
|
||||
* between source and compiled elements of the class. May be
|
||||
* <code>null</code>.
|
||||
* <tt>null</tt>.
|
||||
*/
|
||||
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 <tt>null</tt> 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
|
||||
* <tt>null</tt> 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
|
||||
* <tt>null</tt> 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
|
||||
* <tt>true</tt> if the annotation is visible at runtime.
|
||||
* @return a visitor to visit the annotation values, or <tt>null</tt> 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.
|
||||
* <tt>null</tt> 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
|
||||
* <tt>true</tt> if the annotation is visible at runtime.
|
||||
* @return a visitor to visit the annotation values, or <tt>null</tt> 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 <tt>null</tt> 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 <tt>null</tt> 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 <tt>null</tt> 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,
|
||||
* <tt>null</tt> 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 <tt>int</tt>,
|
||||
* <tt>float</tt>, <tt>long</tt> or <tt>String</tt> 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
|
||||
* <tt>null</tt> 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 <tt>null</tt>) 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 <tt>null</tt> 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>
|
||||
* <tt>null</tt>.
|
||||
* @return an object to visit the byte code of the method, or <tt>null</tt>
|
||||
* if this class visitor is not interested in visiting the code of
|
||||
* this method.
|
||||
*/
|
||||
|
||||
@@ -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 <tt>i</tt>
|
||||
* the Item whose index is <tt>i</tt>. 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
|
||||
* <tt>true</tt> 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.
|
||||
* <tt>true</tt> if <tt>owner</tt> 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.
|
||||
* <tt>true</tt> if <tt>owner</tt> 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 <tt>null</tt> if there is no such item.
|
||||
*/
|
||||
private Item get(final Item key) {
|
||||
Item i = items[key.hashCode % items.length];
|
||||
|
||||
@@ -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: ( <tt>visitAnnotation</tt> |
|
||||
* <tt>visitTypeAnnotation</tt> | <tt>visitAttribute</tt> )* <tt>visitEnd</tt>.
|
||||
*
|
||||
* @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
|
||||
* <tt>true</tt> if the annotation is visible at runtime.
|
||||
* @return a visitor to visit the annotation values, or <tt>null</tt> 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.
|
||||
* <tt>null</tt> 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
|
||||
* <tt>true</tt> if the annotation is visible at runtime.
|
||||
* @return a visitor to visit the annotation values, or <tt>null</tt> 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);
|
||||
}
|
||||
|
||||
@@ -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 <tt>null</tt>.
|
||||
*/
|
||||
private AnnotationWriter anns;
|
||||
|
||||
/**
|
||||
* The runtime invisible annotations of this field. May be <code>null</code>.
|
||||
* The runtime invisible annotations of this field. May be <tt>null</tt>.
|
||||
*/
|
||||
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 <tt>null</tt>.
|
||||
*/
|
||||
private AnnotationWriter tanns;
|
||||
|
||||
/**
|
||||
* The runtime invisible type annotations of this field. May be
|
||||
* <code>null</code>.
|
||||
* <tt>null</tt>.
|
||||
*/
|
||||
private AnnotationWriter itanns;
|
||||
|
||||
/**
|
||||
* The non standard attributes of this field. May be <code>null</code>.
|
||||
* The non standard attributes of this field. May be <tt>null</tt>.
|
||||
*/
|
||||
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 <tt>null</tt>.
|
||||
* @param value
|
||||
* the field's constant value. May be <code>null</code>.
|
||||
* the field's constant value. May be <tt>null</tt>.
|
||||
*/
|
||||
FieldWriter(final ClassWriter cw, final int access, final String name,
|
||||
final String desc, final String signature, final Object value) {
|
||||
|
||||
@@ -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 <tt>true</tt> 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 <tt>true</tt> 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 <tt>true</tt> 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 <tt>true</tt> if the type array has been modified by this
|
||||
* operation.
|
||||
*/
|
||||
private static boolean merge(final ClassWriter cw, int t,
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
* <tt>null</tt> to catch any exceptions.
|
||||
*/
|
||||
String desc;
|
||||
|
||||
|
||||
@@ -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 <tt>true</tt> if the given item if equal to this one,
|
||||
* <tt>false</tt> otherwise.
|
||||
*/
|
||||
boolean isEqualTo(final Item i) {
|
||||
switch (type) {
|
||||
|
||||
@@ -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.
|
||||
* <tt>true</tt> if the reference must be stored in 4 bytes, or
|
||||
* <tt>false</tt> 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 <tt>true</tt> 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
|
||||
|
||||
@@ -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: ( <tt>visitParameter</tt> )* [
|
||||
* <tt>visitAnnotationDefault</tt> ] ( <tt>visitAnnotation</tt> |
|
||||
* <tt>visitParameterAnnotation</tt> <tt>visitTypeAnnotation</tt> |
|
||||
* <tt>visitAttribute</tt> )* [ <tt>visitCode</tt> ( <tt>visitFrame</tt> |
|
||||
* <tt>visit<i>X</i>Insn</tt> | <tt>visitLabel</tt> |
|
||||
* <tt>visitInsnAnnotation</tt> | <tt>visitTryCatchBlock</tt> |
|
||||
* <tt>visitTryCatchAnnotation</tt> | <tt>visitLocalVariable</tt> |
|
||||
* <tt>visitLocalVariableAnnotation</tt> | <tt>visitLineNumber</tt> )*
|
||||
* <tt>visitMaxs</tt> ] <tt>visitEnd</tt>. In addition, the
|
||||
* <tt>visit<i>X</i>Insn</tt> and <tt>visitLabel</tt> 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
|
||||
* <tt>visitInsnAnnotation</tt> must be called <i>after</i> the annotated
|
||||
* instruction, <tt>visitTryCatchBlock</tt> must be called <i>before</i> the
|
||||
* labels passed as arguments have been visited,
|
||||
* <code>visitTryCatchBlockAnnotation</code> must be called <i>after</i> the
|
||||
* <tt>visitTryCatchBlockAnnotation</tt> 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
|
||||
* <tt>visitLocalVariable</tt>, <tt>visitLocalVariableAnnotation</tt> and
|
||||
* <tt>visitLineNumber</tt> 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 <tt>ACC_FINAL</tt>,
|
||||
* <tt>ACC_SYNTHETIC</tt> or/and <tt>ACC_MANDATED</tt> 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 <tt>null</tt> 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
|
||||
* <tt>true</tt> if the annotation is visible at runtime.
|
||||
* @return a visitor to visit the annotation values, or <tt>null</tt> 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.
|
||||
* <tt>null</tt> 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
|
||||
* <tt>true</tt> if the annotation is visible at runtime.
|
||||
* @return a visitor to visit the annotation values, or <tt>null</tt> 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
|
||||
* <tt>true</tt> if the annotation is visible at runtime.
|
||||
* @return a visitor to visit the annotation values, or <tt>null</tt> 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 <tt>.class</tt> 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. <tt>labels[i]</tt> is the
|
||||
* beginning of the handler block for the <tt>min + i</tt> 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. <tt>labels[i]</tt> is the
|
||||
* beginning of the handler block for the <tt>keys[i]</tt> 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.
|
||||
* <tt>null</tt> 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
|
||||
* <tt>true</tt> if the annotation is visible at runtime.
|
||||
* @return a visitor to visit the annotation values, or <tt>null</tt> 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 <tt>null</tt> 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.
|
||||
* <tt>null</tt> 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
|
||||
* <tt>true</tt> if the annotation is visible at runtime.
|
||||
* @return a visitor to visit the annotation values, or <tt>null</tt> 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
|
||||
* <tt>null</tt> 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.
|
||||
* <tt>null</tt> 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
|
||||
* <tt>true</tt> if the annotation is visible at runtime.
|
||||
* @return a visitor to visit the annotation values, or <tt>null</tt> 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 <tt>start</tt> has not already been visited by this
|
||||
* visitor (by the {@link #visitLabel visitLabel} method).
|
||||
*/
|
||||
public void visitLineNumber(int line, Label start) {
|
||||
|
||||
@@ -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 <tt>null</tt>.
|
||||
*/
|
||||
private ByteVector annd;
|
||||
|
||||
/**
|
||||
* The runtime visible annotations of this method. May be <code>null</code>.
|
||||
* The runtime visible annotations of this method. May be <tt>null</tt>.
|
||||
*/
|
||||
private AnnotationWriter anns;
|
||||
|
||||
/**
|
||||
* The runtime invisible annotations of this method. May be <code>null</code>.
|
||||
* The runtime invisible annotations of this method. May be <tt>null</tt>.
|
||||
*/
|
||||
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 <tt>null</tt>
|
||||
* .
|
||||
*/
|
||||
private AnnotationWriter tanns;
|
||||
|
||||
/**
|
||||
* The runtime invisible type annotations of this method. May be
|
||||
* <code>null</code>.
|
||||
* <tt>null</tt>.
|
||||
*/
|
||||
private AnnotationWriter itanns;
|
||||
|
||||
/**
|
||||
* The runtime visible parameter annotations of this method. May be
|
||||
* <code>null</code>.
|
||||
* <tt>null</tt>.
|
||||
*/
|
||||
private AnnotationWriter[] panns;
|
||||
|
||||
/**
|
||||
* The runtime invisible parameter annotations of this method. May be
|
||||
* <code>null</code>.
|
||||
* <tt>null</tt>.
|
||||
*/
|
||||
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 <tt>null</tt>.
|
||||
*/
|
||||
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 <tt>null</tt>.
|
||||
*/
|
||||
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 <tt>stackSize</tt>.
|
||||
*/
|
||||
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 <tt>stackSize</tt>.
|
||||
*/
|
||||
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 <tt>null</tt>.
|
||||
* @param exceptions
|
||||
* the internal names of the method's exceptions. May be
|
||||
* <code>null</code>.
|
||||
* <tt>null</tt>.
|
||||
* @param compute
|
||||
* Indicates what must be automatically computed (see #compute).
|
||||
*/
|
||||
|
||||
@@ -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: <tt>visitMainClass</tt> | ( <tt>visitPackage</tt> |
|
||||
* <tt>visitRequire</tt> | <tt>visitExport</tt> | <tt>visitOpen</tt> |
|
||||
* <tt>visitUse</tt> | <tt>visitProvide</tt> )* <tt>visitEnd</tt>.
|
||||
*
|
||||
* 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>.
|
||||
* <tt>null</tt>.
|
||||
*/
|
||||
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>.
|
||||
* <tt>null</tt>.
|
||||
*/
|
||||
public void visitOpen(String packaze, int access, String... modules) {
|
||||
if (mv != null) {
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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 <tt>void</tt> 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 <tt>boolean</tt> 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 <tt>char</tt> 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 <tt>byte</tt> 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 <tt>short</tt> 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 <tt>int</tt> 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 <tt>float</tt> 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 <tt>long</tt> 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 <tt>double</tt> 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 <tt>void</tt> 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 <tt>boolean</tt> 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 <tt>char</tt> 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 <tt>byte</tt> 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 <tt>short</tt> 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 <tt>int</tt> 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 <tt>float</tt> 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 <tt>long</tt> 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 <tt>double</tt> 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 << 2) | retSize</code> (argSize is therefore equal to
|
||||
* <code>i >> 2</code>, and retSize to <code>i & 0x03</code>).
|
||||
* <tt>(argSize << 2) | retSize</tt> (argSize is therefore equal to
|
||||
* <tt>i >> 2</tt>, and retSize to <tt>i & 0x03</tt>).
|
||||
*/
|
||||
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 << 2) | retSize</code>
|
||||
* (argSize is therefore equal to <code>i >> 2</code>,
|
||||
* and retSize to <code>i & 0x03</code>).
|
||||
* int i = <tt>(argSize << 2) | retSize</tt>
|
||||
* (argSize is therefore equal to <tt>i >> 2</tt>,
|
||||
* and retSize to <tt>i & 0x03</tt>).
|
||||
*/
|
||||
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 <tt>long</tt> and
|
||||
* <tt>double</tt>, 0 for <tt>void</tt> 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 <tt>float</tt> and
|
||||
* <tt>opcode</tt> 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 <tt>true</tt> if the given object is equal to this type.
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(final Object o) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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;
|
||||
});
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
|
||||
23
src/org/redkale/convert/ConvertBytesHandler.java
Normal file
23
src/org/redkale/convert/ConvertBytesHandler.java
Normal 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);
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -28,4 +28,9 @@ public interface Encodeable<W extends Writer, T> {
|
||||
*/
|
||||
public Type getType();
|
||||
|
||||
//是否需要检查Writer.specify
|
||||
default boolean specifyable() {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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值
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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."); //无需实现
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
|
||||
40
src/org/redkale/convert/ext/ThrowableSimpledCoder.java
Normal file
40
src/org/redkale/convert/ext/ThrowableSimpledCoder.java
Normal 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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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));
|
||||
|
||||
389
src/org/redkale/convert/json/JsonBytesWriter.java
Normal file
389
src/org/redkale/convert/json/JsonBytesWriter.java
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
267
src/org/redkale/convert/json/JsonCharsWriter.java
Normal file
267
src/org/redkale/convert/json/JsonCharsWriter.java
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
// }
|
||||
}
|
||||
|
||||
559
src/org/redkale/convert/json/JsonDynEncoder.java
Normal file
559
src/org/redkale/convert/json/JsonDynEncoder.java
Normal 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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)));
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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);
|
||||
// });
|
||||
// }
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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)));
|
||||
}
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
package org.redkale.mq;
|
||||
|
||||
/**
|
||||
* 一个Service对应一个MessageProcessor
|
||||
*
|
||||
* <p>
|
||||
* 详情见: https://redkale.org
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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调用
|
||||
|
||||
@@ -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()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
@@ -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();
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
44
src/org/redkale/net/AsyncGroup.java
Normal file
44
src/org/redkale/net/AsyncGroup.java
Normal 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);
|
||||
}
|
||||
217
src/org/redkale/net/AsyncIOGroup.java
Normal file
217
src/org/redkale/net/AsyncIOGroup.java
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
133
src/org/redkale/net/AsyncIOThread.java
Normal file
133
src/org/redkale/net/AsyncIOThread.java
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
450
src/org/redkale/net/AsyncNioConnection.java
Normal file
450
src/org/redkale/net/AsyncNioConnection.java
Normal 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();
|
||||
}
|
||||
201
src/org/redkale/net/AsyncNioTcpConnection.java
Normal file
201
src/org/redkale/net/AsyncNioTcpConnection.java
Normal 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();
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
171
src/org/redkale/net/AsyncNioUdpConnection.java
Normal file
171
src/org/redkale/net/AsyncNioUdpConnection.java
Normal 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();
|
||||
}
|
||||
|
||||
}
|
||||
169
src/org/redkale/net/AsyncNioUdpProtocolServer.java
Normal file
169
src/org/redkale/net/AsyncNioUdpProtocolServer.java
Normal 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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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) {
|
||||
|
||||
171
src/org/redkale/net/ProtocolCodec.java
Normal file
171
src/org/redkale/net/ProtocolCodec.java
Normal 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);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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:解析完毕; >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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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() {
|
||||
|
||||
219
src/org/redkale/net/client/Client.java
Normal file
219
src/org/redkale/net/client/Client.java
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
57
src/org/redkale/net/client/ClientCodec.java
Normal file
57
src/org/redkale/net/client/ClientCodec.java
Normal 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);
|
||||
}
|
||||
|
||||
}
|
||||
273
src/org/redkale/net/client/ClientConnection.java
Normal file
273
src/org/redkale/net/client/ClientConnection.java
Normal 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();
|
||||
}
|
||||
|
||||
}
|
||||
58
src/org/redkale/net/client/ClientFuture.java
Normal file
58
src/org/redkale/net/client/ClientFuture.java
Normal 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());
|
||||
}
|
||||
}
|
||||
35
src/org/redkale/net/client/ClientRequest.java
Normal file
35
src/org/redkale/net/client/ClientRequest.java
Normal 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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
47
src/org/redkale/net/client/ClientResult.java
Normal file
47
src/org/redkale/net/client/ClientResult.java
Normal 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
Reference in New Issue
Block a user