visit | visitEnum |
- * visitAnnotation | visitArray )* visitEnd.
+ * 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
- * null if this visitor is not interested in visiting this
+ * <tt>null</tt> if this visitor is not interested in visiting this
* nested annotation. The nested annotation value must be fully
* visited before calling other methods on this annotation
* visitor.
@@ -172,7 +175,7 @@ public abstract class AnnotationVisitor {
* @param name
* the value name.
* @return a visitor to visit the actual array value elements, or
- * null 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. All the array values must be visited
* before calling other methods on this annotation visitor.
diff --git a/src/org/redkale/asm/AnnotationWriter.java b/src/org/redkale/asm/AnnotationWriter.java
index 437aeb59e..b9a6d03e5 100644
--- a/src/org/redkale/asm/AnnotationWriter.java
+++ b/src/org/redkale/asm/AnnotationWriter.java
@@ -77,7 +77,7 @@ final class AnnotationWriter extends AnnotationVisitor {
private int size;
/**
- * true if values are named, false 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
- * true if values are named, false 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 parent 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
- * null 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.
*/
diff --git a/src/org/redkale/asm/Attribute.java b/src/org/redkale/asm/Attribute.java
index ac959f499..e2ddb7451 100644
--- a/src/org/redkale/asm/Attribute.java
+++ b/src/org/redkale/asm/Attribute.java
@@ -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 null.
+ * The next attribute in this attribute list. May be <tt>null</tt>.
*/
Attribute next;
@@ -92,19 +94,19 @@ class Attribute {
}
/**
- * Returns true if this type of attribute is unknown. The default
- * implementation of this method always returns true.
+ * Returns <tt>true</tt> if this type of attribute is unknown. The default
+ * implementation of this method always returns <tt>true</tt>.
*
- * @return true if this type of attribute is unknown.
+ * @return <tt>true</tt> if this type of attribute is unknown.
*/
public boolean isUnknown() {
return true;
}
/**
- * Returns true if this type of attribute is a code attribute.
+ * Returns <tt>true</tt> if this type of attribute is a code attribute.
*
- * @return true 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 null 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
* new {@link Attribute} object, of type {@link #type type},
- * corresponding to the len 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 null 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 new {@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 null 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 null 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 null 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 null 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 null 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 null 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()
+ };
}
diff --git a/src/org/redkale/asm/ByteVector.java b/src/org/redkale/asm/ByteVector.java
index 779707f54..ff215c9d4 100644
--- a/src/org/redkale/asm/ByteVector.java
+++ b/src/org/redkale/asm/ByteVector.java
@@ -332,7 +332,7 @@ public class ByteVector {
* automatically enlarged if necessary.
*
* @param b
- * an array of bytes. May be null to put len
+ * 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.
diff --git a/src/org/redkale/asm/ClassReader.java b/src/org/redkale/asm/ClassReader.java
index 0300a466f..5f28fb50e 100644
--- a/src/org/redkale/asm/ClassReader.java
+++ b/src/org/redkale/asm/ClassReader.java
@@ -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 null 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
- * null.
+ * <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
- * true 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 null 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 null 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,
diff --git a/src/org/redkale/asm/ClassVisitor.java b/src/org/redkale/asm/ClassVisitor.java
index 929415e7f..57b96a95d 100644
--- a/src/org/redkale/asm/ClassVisitor.java
+++ b/src/org/redkale/asm/ClassVisitor.java
@@ -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: visit [ visitSource ] [
- * visitModule ][ visitOuterClass ] ( visitAnnotation |
- * visitTypeAnnotation | visitAttribute )* (
- * visitInnerClass | visitField | visitMethod )*
- * visitEnd.
+ * 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 null 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
- * null, 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
- * null.
+ * <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 null.
+ * May be <tt>null</tt>.
* @param debug
* additional debug information to compute the correspondance
* between source and compiled elements of the class. May be
- * null.
+ * <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 null 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
- * null 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
- * null 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
- * true if the annotation is visible at runtime.
- * @return a visitor to visit the annotation values, or null 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
- * null 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
- * true if the annotation is visible at runtime.
- * @return a visitor to visit the annotation values, or null 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 null 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 null 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 null 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
- * null 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 int,
- * float, long or String fields
+ * {@link Double} or a {@link String} (for <tt>int</tt>,
+ * <tt>float</tt>, <tt>long</tt> or <tt>String</tt> fields
* respectively). This parameter is only used for static
* fields. 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
- * null 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 must return a new
- * {@link MethodVisitor} instance (or null) 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 null 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
- * null.
- * @return an object to visit the byte code of the method, or null
+ * <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.
*/
diff --git a/src/org/redkale/asm/ClassWriter.java b/src/org/redkale/asm/ClassWriter.java
index 8455aa270..25da6f65a 100644
--- a/src/org/redkale/asm/ClassWriter.java
+++ b/src/org/redkale/asm/ClassWriter.java
@@ -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 i
- * the Item whose index is i. 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;
/**
- * true 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. This method is
+ * intended for {@link Attribute} sub classes, and is normally not needed by
+ * class generators or adapters.
+ *
+ * @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. This method is
@@ -1486,7 +1518,7 @@ public class ClassWriter extends ClassVisitor {
* @param desc
* the method's descriptor.
* @param itf
- * true if owner 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
- * true if owner 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 null 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];
diff --git a/src/org/redkale/asm/FieldVisitor.java b/src/org/redkale/asm/FieldVisitor.java
index 7b6029d7a..51a954dfa 100644
--- a/src/org/redkale/asm/FieldVisitor.java
+++ b/src/org/redkale/asm/FieldVisitor.java
@@ -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: ( visitAnnotation |
- * visitTypeAnnotation | visitAttribute )* visitEnd.
+ * 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
- * true if the annotation is visible at runtime.
- * @return a visitor to visit the annotation values, or null 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
- * null 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
- * true if the annotation is visible at runtime.
- * @return a visitor to visit the annotation values, or null 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);
}
diff --git a/src/org/redkale/asm/FieldWriter.java b/src/org/redkale/asm/FieldWriter.java
index 17be5542a..85c68574d 100644
--- a/src/org/redkale/asm/FieldWriter.java
+++ b/src/org/redkale/asm/FieldWriter.java
@@ -100,28 +100,28 @@ final class FieldWriter extends FieldVisitor {
private int value;
/**
- * The runtime visible annotations of this field. May be null.
+ * The runtime visible annotations of this field. May be <tt>null</tt>.
*/
private AnnotationWriter anns;
/**
- * The runtime invisible annotations of this field. May be null.
+ * 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 null.
+ * 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
- * null.
+ * <tt>null</tt>.
*/
private AnnotationWriter itanns;
/**
- * The non standard attributes of this field. May be null.
+ * 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 null.
+ * the field's signature. May be <tt>null</tt>.
* @param value
- * the field's constant value. May be null.
+ * 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) {
diff --git a/src/org/redkale/asm/Frame.java b/src/org/redkale/asm/Frame.java
index d843dca72..e5419fc8b 100644
--- a/src/org/redkale/asm/Frame.java
+++ b/src/org/redkale/asm/Frame.java
@@ -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 true 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 true 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 true 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 true 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,
diff --git a/src/org/redkale/asm/Handle.java b/src/org/redkale/asm/Handle.java
index 259feb217..6a35cf7d9 100644
--- a/src/org/redkale/asm/Handle.java
+++ b/src/org/redkale/asm/Handle.java
@@ -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.
diff --git a/src/org/redkale/asm/Handler.java b/src/org/redkale/asm/Handler.java
index 3448caded..e3dc44b98 100644
--- a/src/org/redkale/asm/Handler.java
+++ b/src/org/redkale/asm/Handler.java
@@ -82,7 +82,7 @@ class Handler {
/**
* Internal name of the type of exceptions handled by this handler, or
- * null to catch any exceptions.
+ * <tt>null</tt> to catch any exceptions.
*/
String desc;
diff --git a/src/org/redkale/asm/Item.java b/src/org/redkale/asm/Item.java
index 5cca74c3a..6c6120ccd 100644
--- a/src/org/redkale/asm/Item.java
+++ b/src/org/redkale/asm/Item.java
@@ -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 true if the given item if equal to this one,
- * false 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) {
diff --git a/src/org/redkale/asm/Label.java b/src/org/redkale/asm/Label.java
index bbd4b98b0..4c33dd2f6 100644
--- a/src/org/redkale/asm/Label.java
+++ b/src/org/redkale/asm/Label.java
@@ -325,8 +325,8 @@ public class Label {
* the position of first byte of the bytecode instruction that
* contains this label.
* @param wideOffset
- * true if the reference must be stored in 4 bytes, or
- * false 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 true 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
diff --git a/src/org/redkale/asm/MethodVisitor.java b/src/org/redkale/asm/MethodVisitor.java
index b35595a54..99f8535dd 100644
--- a/src/org/redkale/asm/MethodVisitor.java
+++ b/src/org/redkale/asm/MethodVisitor.java
@@ -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: ( visitParameter )* [
- * visitAnnotationDefault ] ( visitAnnotation |
- * visitParameterAnnotation visitTypeAnnotation |
- * visitAttribute )* [ visitCode ( visitFrame |
- * visitXInsn | visitLabel |
- * visitInsnAnnotation | visitTryCatchBlock |
- * visitTryCatchAnnotation | visitLocalVariable |
- * visitLocalVariableAnnotation | visitLineNumber )*
- * visitMaxs ] visitEnd. In addition, the
- * visitXInsn and visitLabel 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>visitXInsn</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>visitXInsn</tt> and <tt>visitLabel</tt> methods must be called in
* the sequential order of the bytecode instructions of the visited code,
- * visitInsnAnnotation must be called after the annotated
- * instruction, visitTryCatchBlock must be called before the
+ * <tt>visitInsnAnnotation</tt> must be called after the annotated
+ * instruction, <tt>visitTryCatchBlock</tt> must be called before the
* labels passed as arguments have been visited,
- * visitTryCatchBlockAnnotation must be called after the
+ * <tt>visitTryCatchBlockAnnotation</tt> must be called after the
* corresponding try catch block has been visited, and the
- * visitLocalVariable, visitLocalVariableAnnotation and
- * visitLineNumber methods must be called after the labels
+ * <tt>visitLocalVariable</tt>, <tt>visitLocalVariableAnnotation</tt> and
+ * <tt>visitLineNumber</tt> methods must be called after 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 ACC_FINAL,
- * ACC_SYNTHETIC or/and ACC_MANDATED 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 null 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
- * true if the annotation is visible at runtime.
- * @return a visitor to visit the annotation values, or null 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
- * null 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
- * true if the annotation is visible at runtime.
- * @return a visitor to visit the annotation values, or null 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
- * true if the annotation is visible at runtime.
- * @return a visitor to visit the annotation values, or null 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 .class 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. labels[i] is the
- * beginning of the handler block for the min + i 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. labels[i] is the
- * beginning of the handler block for the keys[i] 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
- * null 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
- * true if the annotation is visible at runtime.
- * @return a visitor to visit the annotation values, or null 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 null 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
- * null 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
- * true if the annotation is visible at runtime.
- * @return a visitor to visit the annotation values, or null 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
- * null 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
- * null 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
- * true if the annotation is visible at runtime.
- * @return a visitor to visit the annotation values, or null 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 start 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) {
diff --git a/src/org/redkale/asm/MethodWriter.java b/src/org/redkale/asm/MethodWriter.java
index 7d3fa5eda..e8fd34a19 100644
--- a/src/org/redkale/asm/MethodWriter.java
+++ b/src/org/redkale/asm/MethodWriter.java
@@ -218,41 +218,41 @@ class MethodWriter extends MethodVisitor {
int[] exceptions;
/**
- * The annotation default attribute of this method. May be null.
+ * The annotation default attribute of this method. May be <tt>null</tt>.
*/
private ByteVector annd;
/**
- * The runtime visible annotations of this method. May be null.
+ * The runtime visible annotations of this method. May be <tt>null</tt>.
*/
private AnnotationWriter anns;
/**
- * The runtime invisible annotations of this method. May be null.
+ * 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 null
+ * 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
- * null.
+ * <tt>null</tt>.
*/
private AnnotationWriter itanns;
/**
* The runtime visible parameter annotations of this method. May be
- * null.
+ * <tt>null</tt>.
*/
private AnnotationWriter[] panns;
/**
* The runtime invisible parameter annotations of this method. May be
- * null.
+ * <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 null.
+ * 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 null.
+ * 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 stackSize.
+ * 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 stackSize.
+ * 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 null.
+ * the method's signature. May be <tt>null</tt>.
* @param exceptions
* the internal names of the method's exceptions. May be
- * null.
+ * <tt>null</tt>.
* @param compute
* Indicates what must be automatically computed (see #compute).
*/
diff --git a/src/org/redkale/asm/ModuleVisitor.java b/src/org/redkale/asm/ModuleVisitor.java
index 455d50123..da743696a 100644
--- a/src/org/redkale/asm/ModuleVisitor.java
+++ b/src/org/redkale/asm/ModuleVisitor.java
@@ -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: visitMainClass | ( visitPackage |
- * visitRequire | visitExport | visitOpen |
- * visitUse | visitProvide )* visitEnd.
+ * 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
- * null.
+ * <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
- * null.
+ * <tt>null</tt>.
*/
public void visitOpen(String packaze, int access, String... modules) {
if (mv != null) {
diff --git a/src/org/redkale/asm/Opcodes.java b/src/org/redkale/asm/Opcodes.java
index 89459ddd1..30f9efd17 100644
--- a/src/org/redkale/asm/Opcodes.java
+++ b/src/org/redkale/asm/Opcodes.java
@@ -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
diff --git a/src/org/redkale/asm/Type.java b/src/org/redkale/asm/Type.java
index 73d8f45c4..8772c64c7 100644
--- a/src/org/redkale/asm/Type.java
+++ b/src/org/redkale/asm/Type.java
@@ -71,47 +71,47 @@ import java.lang.reflect.Method;
public class Type {
/**
- * The sort of the void 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 boolean 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 char 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 byte 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 short 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 int 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 float 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 long 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 double 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 void 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 boolean 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 char 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 byte 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 short 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 int 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 float 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 long 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 double 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 =
- * (argSize << 2) | retSize (argSize is therefore equal to
- * i >> 2, and retSize to i & 0x03).
+ * <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 = (argSize << 2) | retSize
- * (argSize is therefore equal to i >> 2,
- * and retSize to i & 0x03).
+ * 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 long and
- * double, 0 for void 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 float and
- * opcode 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 true 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) {
diff --git a/src/org/redkale/boot/Application.java b/src/org/redkale/boot/Application.java
index ed87a89e1..477183b41 100644
--- a/src/org/redkale/boot/Application.java
+++ b/src/org/redkale/boot/Application.java
@@ -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
*/
@@ -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 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() + " -------------------------");
//------------------配置 节点 ------------------
- ObjectPool 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 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);
}
diff --git a/src/org/redkale/boot/NodeHttpServer.java b/src/org/redkale/boot/NodeHttpServer.java
index 39159545b..27f591a44 100644
--- a/src/org/redkale/boot/NodeHttpServer.java
+++ b/src/org/redkale/boot/NodeHttpServer.java
@@ -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() {
diff --git a/src/org/redkale/boot/NodeServer.java b/src/org/redkale/boot/NodeServer.java
index 05ac10005..3e92576e1 100644
--- a/src/org/redkale/boot/NodeServer.java
+++ b/src/org/redkale/boot/NodeServer.java
@@ -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 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);
}
diff --git a/src/org/redkale/boot/NodeSncpServer.java b/src/org/redkale/boot/NodeSncpServer.java
index e45278e82..c3caa4254 100644
--- a/src/org/redkale/boot/NodeSncpServer.java
+++ b/src/org/redkale/boot/NodeSncpServer.java
@@ -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
diff --git a/src/org/redkale/boot/watch/ServerWatchService.java b/src/org/redkale/boot/watch/ServerWatchService.java
index f536afd94..80e7476c9 100644
--- a/src/org/redkale/boot/watch/ServerWatchService.java
+++ b/src/org/redkale/boot/watch/ServerWatchService.java
@@ -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 formatToMap(NodeServer node) {
Server server = node.getServer();
Map 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());
diff --git a/src/org/redkale/cluster/CacheClusterAgent.java b/src/org/redkale/cluster/CacheClusterAgent.java
index a22aae381..9dd1b8146 100644
--- a/src/org/redkale/cluster/CacheClusterAgent.java
+++ b/src/org/redkale/cluster/CacheClusterAgent.java
@@ -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;
});
diff --git a/src/org/redkale/cluster/ClusterAgent.java b/src/org/redkale/cluster/ClusterAgent.java
index 35df53612..8e2da7d67 100644
--- a/src/org/redkale/cluster/ClusterAgent.java
+++ b/src/org/redkale/cluster/ClusterAgent.java
@@ -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
diff --git a/src/org/redkale/convert/ArrayEncoder.java b/src/org/redkale/convert/ArrayEncoder.java
index 4e8091281..a156779c3 100644
--- a/src/org/redkale/convert/ArrayEncoder.java
+++ b/src/org/redkale/convert/ArrayEncoder.java
@@ -29,6 +29,8 @@ public class ArrayEncoder implements Encodeable {
protected final Encodeable componentEncoder;
+ protected final boolean subtypefinal;
+
protected volatile boolean inited = false;
protected final Object lock = new Object();
@@ -47,6 +49,7 @@ public class ArrayEncoder implements Encodeable {
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 implements Encodeable {
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 implements Encodeable {
}
}
}
- 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 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();
diff --git a/src/org/redkale/convert/BinaryConvert.java b/src/org/redkale/convert/BinaryConvert.java
index 5019545a2..6db363567 100644
--- a/src/org/redkale/convert/BinaryConvert.java
+++ b/src/org/redkale/convert/BinaryConvert.java
@@ -32,5 +32,4 @@ public abstract class BinaryConvert extends
public abstract byte[] convertTo(final Type type, final Object value);
- public abstract byte[] convertMapTo(final Object... values);
}
diff --git a/src/org/redkale/convert/Convert.java b/src/org/redkale/convert/Convert.java
index 3a10c333f..846e1f999 100644
--- a/src/org/redkale/convert/Convert.java
+++ b/src/org/redkale/convert/Convert.java
@@ -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 {
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 supplier, final Object value);
public abstract ByteBuffer[] convertTo(final Supplier supplier, final Type type, final Object value);
- public abstract byte[] convertMapToBytes(final Object... values);
-
- public abstract ByteBuffer[] convertMapTo(final Supplier supplier, final Object... values);
-
}
diff --git a/src/org/redkale/convert/ConvertBytesHandler.java b/src/org/redkale/convert/ConvertBytesHandler.java
new file mode 100644
index 000000000..320ca0dcf
--- /dev/null
+++ b/src/org/redkale/convert/ConvertBytesHandler.java
@@ -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系列的方法的回调
+ *
+ * 详情见: https://redkale.org
+ *
+ * @author zhangjx
+ *
+ * @since 2.3.0
+ */
+public interface ConvertBytesHandler {
+
+ void completed(byte[] bs, int offset, int length, Consumer callback, A attachment);
+}
diff --git a/src/org/redkale/convert/ConvertFactory.java b/src/org/redkale/convert/ConvertFactory.java
index 2b30a9047..a980c913f 100644
--- a/src/org/redkale/convert/ConvertFactory.java
+++ b/src/org/redkale/convert/ConvertFactory.java
@@ -42,7 +42,7 @@ public abstract class ConvertFactory {
protected Convert convert;
- protected boolean tiny;
+ protected boolean tiny; //String类型值为"",Boolean类型值为false时是否需要输出, 默认为true
private final Encodeable anyEncoder = new AnyEncoder(this);
@@ -112,6 +112,7 @@ public abstract class ConvertFactory {
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 {
return new EnumSimpledCoder(enumClass);
}
+ protected Encodeable createDyncEncoder(Type type) {
+ return null;
+ }
+
protected ObjectDecoder createObjectDecoder(Type type) {
return new ObjectDecoder(type);
}
- protected ObjectEncoder createObjectEncoder(Type type) {
+ protected ObjectEncoder createObjectEncoder(Type type) {
return new ObjectEncoder(type);
}
@@ -810,8 +815,11 @@ public abstract class ConvertFactory {
}
}
if (simpleCoder == null) {
- oe = createObjectEncoder(type);
- encoder = oe;
+ encoder = createDyncEncoder(type);
+ if (encoder == null) {
+ oe = createObjectEncoder(type);
+ encoder = oe;
+ }
} else {
encoder = simpleCoder;
}
diff --git a/src/org/redkale/convert/EnMember.java b/src/org/redkale/convert/EnMember.java
index cbba0251f..12edd905f 100644
--- a/src/org/redkale/convert/EnMember.java
+++ b/src/org/redkale/convert/EnMember.java
@@ -31,6 +31,10 @@ public final class EnMember {
//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 {
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 {
return attribute;
}
+ public char[] getJsonFieldNameChars() {
+ return jsonFieldNameChars;
+ }
+
+ public byte[] getJsonFieldNameBytes() {
+ return jsonFieldNameBytes;
+ }
+
public Encodeable getEncoder() {
return encoder;
}
diff --git a/src/org/redkale/convert/Encodeable.java b/src/org/redkale/convert/Encodeable.java
index a6b591949..136e1458c 100644
--- a/src/org/redkale/convert/Encodeable.java
+++ b/src/org/redkale/convert/Encodeable.java
@@ -28,4 +28,9 @@ public interface Encodeable {
*/
public Type getType();
+ //是否需要检查Writer.specify
+ default boolean specifyable() {
+ return true;
+ }
+
}
diff --git a/src/org/redkale/convert/ObjectEncoder.java b/src/org/redkale/convert/ObjectEncoder.java
index 00f2b6320..5800495ab 100644
--- a/src/org/redkale/convert/ObjectEncoder.java
+++ b/src/org/redkale/convert/ObjectEncoder.java
@@ -103,7 +103,8 @@ public class ObjectEncoder implements Encodeable {
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 implements Encodeable {
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 fieldCoder;
if (small != null && method.getReturnType() == String.class) {
fieldCoder = StringSimpledCoder.SmallStringSimpledCoder.instance;
diff --git a/src/org/redkale/convert/TextConvert.java b/src/org/redkale/convert/TextConvert.java
index 7d9d0b086..c6f3b095a 100644
--- a/src/org/redkale/convert/TextConvert.java
+++ b/src/org/redkale/convert/TextConvert.java
@@ -32,5 +32,4 @@ public abstract class TextConvert extends Co
public abstract String convertTo(final Type type, final Object value);
- public abstract String convertMapTo(final Object... values);
}
diff --git a/src/org/redkale/convert/Writer.java b/src/org/redkale/convert/Writer.java
index 7999962fe..052321df8 100644
--- a/src/org/redkale/convert/Writer.java
+++ b/src/org/redkale/convert/Writer.java
@@ -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值
diff --git a/src/org/redkale/convert/bson/BsonByteBufferReader.java b/src/org/redkale/convert/bson/BsonByteBufferReader.java
index dea6b38c8..77bdfe3b9 100644
--- a/src/org/redkale/convert/bson/BsonByteBufferReader.java
+++ b/src/org/redkale/convert/bson/BsonByteBufferReader.java
@@ -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);
}
}
diff --git a/src/org/redkale/convert/bson/BsonByteBufferWriter.java b/src/org/redkale/convert/bson/BsonByteBufferWriter.java
index e0905d884..2058e92ed 100644
--- a/src/org/redkale/convert/bson/BsonByteBufferWriter.java
+++ b/src/org/redkale/convert/bson/BsonByteBufferWriter.java
@@ -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."); //无需实现
+ }
}
diff --git a/src/org/redkale/convert/bson/BsonConvert.java b/src/org/redkale/convert/bson/BsonConvert.java
index 1f19d3745..662ba81af 100644
--- a/src/org/redkale/convert/bson/BsonConvert.java
+++ b/src/org/redkale/convert/bson/BsonConvert.java
@@ -39,9 +39,9 @@ import org.redkale.util.*;
*/
public class BsonConvert extends BinaryConvert {
- private static final ObjectPool readerPool = BsonReader.createPool(Integer.getInteger("convert.bson.pool.size", Integer.getInteger("convert.pool.size", 16)));
+ private final ThreadLocal writerPool = ThreadLocal.withInitial(BsonWriter::new);
- private static final ObjectPool writerPool = BsonWriter.createPool(Integer.getInteger("convert.bson.pool.size", Integer.getInteger("convert.pool.size", 16)));
+ private final Consumer offerConsumer = w -> offerBsonWriter(w);
private final boolean tiny;
@@ -84,11 +84,11 @@ public class BsonConvert extends BinaryConvert {
}
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 {
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 {
@SuppressWarnings("unchecked")
public 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 {
@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 {
@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 {
}
@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 {
}
}
- 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 supplier, final Object value) {
if (supplier == null) return null;
@@ -238,36 +259,13 @@ public class BsonConvert extends BinaryConvert {
@Override
public ByteBuffer[] convertTo(final Supplier 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 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 {
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 {
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;
}
}
diff --git a/src/org/redkale/convert/bson/BsonReader.java b/src/org/redkale/convert/bson/BsonReader.java
index ceeb9b640..51c09abfd 100644
--- a/src/org/redkale/convert/bson/BsonReader.java
+++ b/src/org/redkale/convert/bson/BsonReader.java
@@ -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.");
}
}
diff --git a/src/org/redkale/convert/bson/BsonWriter.java b/src/org/redkale/convert/bson/BsonWriter.java
index 63ba622f1..0e7a5a20d 100644
--- a/src/org/redkale/convert/bson/BsonWriter.java
+++ b/src/org/redkale/convert/bson/BsonWriter.java
@@ -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 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));
diff --git a/src/org/redkale/convert/ext/ThrowableSimpledCoder.java b/src/org/redkale/convert/ext/ThrowableSimpledCoder.java
new file mode 100644
index 000000000..1c872bcfb
--- /dev/null
+++ b/src/org/redkale/convert/ext/ThrowableSimpledCoder.java
@@ -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实现
+ *
+ *
+ * 详情见: https://redkale.org
+ *
+ * @author zhangjx
+ * @param Reader输入的子类型
+ * @param Writer输出的子类型
+ */
+public class ThrowableSimpledCoder extends SimpledCoder {
+
+ 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);
+ }
+
+}
diff --git a/src/org/redkale/convert/json/JsonByteBufferWriter.java b/src/org/redkale/convert/json/JsonByteBufferWriter.java
index a8b4260a3..52b674512 100644
--- a/src/org/redkale/convert/json/JsonByteBufferWriter.java
+++ b/src/org/redkale/convert/json/JsonByteBufferWriter.java
@@ -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 supplier;
@@ -36,7 +40,7 @@ public class JsonByteBufferWriter extends JsonWriter {
protected JsonByteBufferWriter(boolean tiny, Charset charset, Supplier 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));
diff --git a/src/org/redkale/convert/json/JsonBytesWriter.java b/src/org/redkale/convert/json/JsonBytesWriter.java
new file mode 100644
index 000000000..5a51ad0e8
--- /dev/null
+++ b/src/org/redkale/convert/json/JsonBytesWriter.java
@@ -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系列的方法输出的字符不能含特殊字符
+ *
+ * 详情见: 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;
+ }
+
+ /**
+ * 注意: 该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 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;
+ }
+
+}
diff --git a/src/org/redkale/convert/json/JsonCharsWriter.java b/src/org/redkale/convert/json/JsonCharsWriter.java
new file mode 100644
index 000000000..6cdb2102d
--- /dev/null
+++ b/src/org/redkale/convert/json/JsonCharsWriter.java
@@ -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系列的方法输出的字符不能含特殊字符
+ *
+ * 详情见: 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;
+ }
+
+ /**
+ * 注意: 该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;
+ }
+
+}
diff --git a/src/org/redkale/convert/json/JsonConvert.java b/src/org/redkale/convert/json/JsonConvert.java
index 9f13ab628..23c829007 100644
--- a/src/org/redkale/convert/json/JsonConvert.java
+++ b/src/org/redkale/convert/json/JsonConvert.java
@@ -30,13 +30,18 @@ public class JsonConvert extends TextConvert {
public static final Type TYPE_RETRESULT_STRING = new TypeToken>() {
}.getType();
- //private static final ObjectPool readerPool = JsonReader.createPool(Integer.getInteger("convert.json.pool.size", Integer.getInteger("convert.pool.size", 16)));
- //private static final ObjectPool writerPool = JsonWriter.createPool(Integer.getInteger("convert.json.pool.size", Integer.getInteger("convert.pool.size", 16)));
- //
- private static final ThreadLocal writerPool = ThreadLocal.withInitial(JsonWriter::new);
+ private final ThreadLocal charsWriterPool = ThreadLocal.withInitial(JsonCharsWriter::new);
+
+ private final ThreadLocal bytesWriterPool = ThreadLocal.withInitial(JsonBytesWriter::new);
+
+ private final Consumer 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 {
};
}
- //------------------------------ 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 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 convertFrom(final Type type, final byte[] bytes) {
if (bytes == null) return null;
@@ -129,34 +131,56 @@ public class JsonConvert extends TextConvert {
public 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 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 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 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 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 {
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 {
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 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 {
@Override
public ByteBuffer[] convertTo(final Supplier 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 {
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 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 {
}
}
- 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;
-// }
}
diff --git a/src/org/redkale/convert/json/JsonDynEncoder.java b/src/org/redkale/convert/json/JsonDynEncoder.java
new file mode 100644
index 000000000..71d71db9f
--- /dev/null
+++ b/src/org/redkale/convert/json/JsonDynEncoder.java
@@ -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序列化操作类
+ *
+ *
+ * 详情见: https://redkale.org
+ *
+ * @author zhangjx
+ * @since 2.3.0
+ *
+ * @param 序列化的数据类型
+ */
+@SuppressWarnings("unchecked")
+public abstract class JsonDynEncoder implements Encodeable {
+
+ protected final Class typeClass;
+
+ protected final ObjectEncoder 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 members = null;
+ Set 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 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 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, "", "(" + jsonfactoryDesc + typeDesc + ")V", null, null));
+ //mv.setDebug(true);
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitVarInsn(ALOAD, 1);
+ mv.visitVarInsn(ALOAD, 2);
+ mv.visitMethodInsn(INVOKESPECIAL, supDynName, "", "(" + 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 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;
+ }
+}
diff --git a/src/org/redkale/convert/json/JsonFactory.java b/src/org/redkale/convert/json/JsonFactory.java
index b8f67832e..9ebecb630 100644
--- a/src/org/redkale/convert/json/JsonFactory.java
+++ b/src/org/redkale/convert/json/JsonFactory.java
@@ -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 {
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 {
return new JsonFactory(null, getSystemPropertyBoolean("convert.json.tiny", "convert.tiny", true));
}
+ @Override
+ protected Encodeable createDyncEncoder(Type type) {
+ return JsonDynEncoder.createDyncEncoder(this, type);
+ }
+
+ @Override
+ protected ObjectEncoder 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);
diff --git a/src/org/redkale/convert/json/JsonReader.java b/src/org/redkale/convert/json/JsonReader.java
index e34beaf5e..17c4683b7 100644
--- a/src/org/redkale/convert/json/JsonReader.java
+++ b/src/org/redkale/convert/json/JsonReader.java
@@ -28,7 +28,6 @@ public class JsonReader extends Reader {
// public static ObjectPool 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;
diff --git a/src/org/redkale/convert/json/JsonStreamWriter.java b/src/org/redkale/convert/json/JsonStreamWriter.java
index 563a37ed2..ad989ebc5 100644
--- a/src/org/redkale/convert/json/JsonStreamWriter.java
+++ b/src/org/redkale/convert/json/JsonStreamWriter.java
@@ -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)));
diff --git a/src/org/redkale/convert/json/JsonWriter.java b/src/org/redkale/convert/json/JsonWriter.java
index 5850caf37..86bd8313b 100644
--- a/src/org/redkale/convert/json/JsonWriter.java
+++ b/src/org/redkale/convert/json/JsonWriter.java
@@ -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 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 的字符
/**
* 注意: 该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));
diff --git a/src/org/redkale/mq/HttpMessageClusterClient.java b/src/org/redkale/mq/HttpMessageClusterClient.java
index 01f01628c..c4f1a3032 100644
--- a/src/org/redkale/mq/HttpMessageClusterClient.java
+++ b/src/org/redkale/mq/HttpMessageClusterClient.java
@@ -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 DISALLOWED_HEADERS_SET = Set.of("connection", "content-length",
+ private static final Set 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 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 futures = new ArrayList<>();
for (Map.Entry> 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 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> forEachCollectionFuture(boolean finest, int userid, HttpSimpleRequest req, String requesturi, java.net.http.HttpRequest.Builder builder, Iterator it) {
+ private CompletableFuture> forEachCollectionFuture(boolean finest, int userid, HttpSimpleRequest req, String requesturi, final Map clientHeaders, byte[] clientBody, Iterator 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> hm = hs.map();
- if (hm != null) {
- for (Map.Entry> en : hm.entrySet()) {
- if ("date".equals(en.getKey()) || "content-type".equals(en.getKey())
- || "server".equals(en.getKey()) || "connection".equals(en.getKey())) continue;
- List 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 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> 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 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 futures = new ArrayList<>();
+// for (Map.Entry> en : addrmap.entrySet()) {
+// String realmodule = en.getKey();
+// Collection 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> 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 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> forEachCollectionFuture(boolean finest, int userid, HttpSimpleRequest req, String requesturi, java.net.http.HttpRequest.Builder builder, Iterator 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> hm = hs.map();
+// if (hm != null) {
+// for (Map.Entry> en : hm.entrySet()) {
+// if ("date".equals(en.getKey()) || "content-type".equals(en.getKey())
+// || "server".equals(en.getKey()) || "connection".equals(en.getKey())) continue;
+// List 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 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);
+// });
+// }
}
diff --git a/src/org/redkale/mq/HttpMessageProcessor.java b/src/org/redkale/mq/HttpMessageProcessor.java
index 4b634d86f..6165c702d 100644
--- a/src/org/redkale/mq/HttpMessageProcessor.java
+++ b/src/org/redkale/mq/HttpMessageProcessor.java
@@ -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
*
*
* 详情见: 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> respPoolThreadLocal;
+
+ protected final Supplier respSupplier;
+
+ protected final Consumer 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() {
diff --git a/src/org/redkale/mq/HttpMessageRequest.java b/src/org/redkale/mq/HttpMessageRequest.java
index dbdcb0966..6f7d0fcab 100644
--- a/src/org/redkale/mq/HttpMessageRequest.java
+++ b/src/org/redkale/mq/HttpMessageRequest.java
@@ -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;
+ }
}
diff --git a/src/org/redkale/mq/HttpMessageResponse.java b/src/org/redkale/mq/HttpMessageResponse.java
index 7566aa090..b9b41cbb7 100644
--- a/src/org/redkale/mq/HttpMessageResponse.java
+++ b/src/org/redkale/mq/HttpMessageResponse.java
@@ -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 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 respSupplier, final Consumer 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 respSupplier = this.responseSupplier;
+ Consumer 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
diff --git a/src/org/redkale/mq/HttpSimpleRequestCoder.java b/src/org/redkale/mq/HttpSimpleRequestCoder.java
index f86d5a56d..75bb1461a 100644
--- a/src/org/redkale/mq/HttpSimpleRequestCoder.java
+++ b/src/org/redkale/mq/HttpSimpleRequestCoder.java
@@ -40,6 +40,7 @@ public class HttpSimpleRequestCoder implements MessageCoder {
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 {
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 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));
diff --git a/src/org/redkale/mq/MessageAgent.java b/src/org/redkale/mq/MessageAgent.java
index 6b5a24304..08cb25cc7 100644
--- a/src/org/redkale/mq/MessageAgent.java
+++ b/src/org/redkale/mq/MessageAgent.java
@@ -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