Files
redkale/src/org/redkale/net/http/Rest.java

1732 lines
104 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.net.http;
import java.io.*;
import java.lang.annotation.*;
import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.reflect.*;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import javax.annotation.Resource;
import jdk.internal.org.objectweb.asm.*;
import static jdk.internal.org.objectweb.asm.ClassWriter.COMPUTE_FRAMES;
import static jdk.internal.org.objectweb.asm.Opcodes.*;
import jdk.internal.org.objectweb.asm.Type;
import org.redkale.convert.json.*;
import org.redkale.service.*;
import org.redkale.util.*;
import org.redkale.source.Flipper;
/**
* 以find开头的方法且参数只有一个且参数类型为primitive class或String则RestParam值默认为#
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*/
public final class Rest {
public static final String REST_HEADER_RESOURCE_NAME = "rest-resource-name";
static final String REST_SERVICE_FIELD_NAME = "_redkale_service";
static final String REST_JSONCONVERT_FIELD_PREFIX = "_redkale_jsonconvert_";
static final String REST_SERVICEMAP_FIELD_NAME = "_redkale_servicemap"; //如果只有name=""的Service资源则实例中_servicemap必须为null
private static final String REST_PARAMTYPES_FIELD_NAME = "_redkale_paramtypes"; //存在泛型的参数数组 Type[][] 第1维度是方法的下标 第二维度是参数的下标
private static final Set<String> EXCLUDERMETHODS = new HashSet<>();
static {
for (Method m : Object.class.getMethods()) {
EXCLUDERMETHODS.add(m.getName());
}
}
/**
* 用于标记由Rest.createRestServlet 方法创建的RestServlet
*/
@Inherited
@Documented
@Target({TYPE})
@Retention(RUNTIME)
public static @interface RestDyn {
}
/**
* 用于标记由Rest.createRestServlet 方法创建的RestServlet
*/
@Inherited
@Documented
@Target({TYPE})
@Retention(RUNTIME)
public static @interface RestDynSourceType {
Class value();
}
private Rest() {
}
public static class MethodParamClassVisitor extends ClassVisitor {
private final Map<String, List<String>> fieldmap;
public MethodParamClassVisitor(int api, final Map<String, List<String>> fieldmap) {
super(api);
this.fieldmap = fieldmap;
}
@Override
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
if (java.lang.reflect.Modifier.isStatic(access)) return null;
List<String> fieldnames = new ArrayList<>();
fieldmap.put(name + ":" + desc, fieldnames);
return new MethodVisitor(Opcodes.ASM5) {
@Override
public void visitLocalVariable(String name, String description, String signature, Label start, Label end, int index) {
if (index > 0) fieldnames.add(name);
}
};
}
//返回的List中参数列表可能会比方法参数量多因为方法内的临时变量也会存入list中 所以需要list的元素集合比方法的参数多
public static Map<String, List<String>> getMethodParamNames(Class clazz) {
String n = clazz.getName();
InputStream in = clazz.getResourceAsStream(n.substring(n.lastIndexOf('.') + 1) + ".class");
Map<String, List<String>> map = new HashMap<>();
if (in == null) return map;
try {
new ClassReader(Utility.readBytesThenClose(in)).accept(new MethodParamClassVisitor(Opcodes.ASM5, map), 0);
} catch (Exception e) { //无需理会
}
return map;
}
}
static JsonConvert createJsonConvert(RestConvert[] converts) {
if (converts == null || converts.length < 1) return JsonConvert.root();
final JsonFactory childFactory = JsonFactory.root().createChild();
List<Class> types = new ArrayList<>();
for (RestConvert rc : converts) {
if (types.contains(rc.type())) throw new RuntimeException("@RestConvert type(" + rc.type() + ") repeat");
childFactory.register(rc.type(), false, rc.convertColumns());
childFactory.register(rc.type(), true, rc.ignoreColumns());
childFactory.reloadCoder(rc.type());
types.add(rc.type());
}
return childFactory.getConvert();
}
static String getWebModuleName(Class<? extends Service> serviceType) {
final RestService controller = serviceType.getAnnotation(RestService.class);
if (controller == null) return serviceType.getSimpleName().replaceAll("Service.*$", "").toLowerCase();
if (controller.ignore()) return null;
return (!controller.name().isEmpty()) ? controller.name() : serviceType.getSimpleName().replaceAll("Service.*$", "").toLowerCase();
}
static boolean isRestDyn(HttpServlet servlet) {
return servlet.getClass().getAnnotation(RestDyn.class) != null;
}
static Service getService(HttpServlet servlet) {
if (servlet == null) return null;
try {
Field ts = servlet.getClass().getDeclaredField(REST_SERVICE_FIELD_NAME);
ts.setAccessible(true);
return (Service) ts.get(servlet);
} catch (Exception e) {
return null;
}
}
static Map<String, Service> getServiceMap(HttpServlet servlet) {
if (servlet == null) return null;
try {
Field ts = servlet.getClass().getDeclaredField(REST_SERVICEMAP_FIELD_NAME);
ts.setAccessible(true);
return (Map) ts.get(servlet);
} catch (Exception e) {
return null;
}
}
static <T extends HttpServlet> T createRestWebSocketServlet(final ClassLoader classLoader, final Class<? extends WebSocket> webSocketType) {
if (webSocketType == null) throw new RuntimeException("Rest WebSocket Class is null on createRestWebSocketServlet");
if (Modifier.isAbstract(webSocketType.getModifiers())) throw new RuntimeException("Rest WebSocket Class(" + webSocketType + ") cannot abstract on createRestWebSocketServlet");
if (Modifier.isFinal(webSocketType.getModifiers())) throw new RuntimeException("Rest WebSocket Class(" + webSocketType + ") cannot final on createRestWebSocketServlet");
final RestWebSocket rws = webSocketType.getAnnotation(RestWebSocket.class);
if (rws == null || rws.ignore()) throw new RuntimeException("Rest WebSocket Class(" + webSocketType + ") have not @RestWebSocket or @RestWebSocket.ignore=true on createRestWebSocketServlet");
boolean valid = false;
for (Constructor c : webSocketType.getDeclaredConstructors()) {
if (c.getParameterCount() == 0 && (Modifier.isPublic(c.getModifiers()) || Modifier.isProtected(c.getModifiers()))) {
valid = true;
break;
}
}
if (!valid) throw new RuntimeException("Rest WebSocket Class(" + webSocketType + ") must have public or protected Constructor on createRestWebSocketServlet");
if (!checkName(rws.catalog())) throw new RuntimeException(webSocketType.getName() + " have illeal " + RestWebSocket.class.getSimpleName() + ".catalog, only 0-9 a-z A-Z _ cannot begin 0-9");
if (!checkName(rws.name())) throw new RuntimeException(webSocketType.getName() + " have illeal " + RestWebSocket.class.getSimpleName() + ".name, only 0-9 a-z A-Z _ cannot begin 0-9");
//----------------------------------------------------------------------------------------
final Set<Field> resourcesFieldSet = new LinkedHashSet<>();
final ClassLoader loader = classLoader == null ? Thread.currentThread().getContextClassLoader() : classLoader;
Class clzz = webSocketType;
do {
for (Field field : webSocketType.getDeclaredFields()) {
if (field.getAnnotation(Resource.class) == null) continue;
if (Modifier.isStatic(webSocketType.getModifiers())) throw new RuntimeException(field + " cannot static on createRestWebSocketServlet");
if (Modifier.isFinal(webSocketType.getModifiers())) throw new RuntimeException(field + " cannot final on createRestWebSocketServlet");
if (!Modifier.isPublic(webSocketType.getModifiers()) && !Modifier.isProtected(webSocketType.getModifiers())) {
throw new RuntimeException(field + " must be public or protected on createRestWebSocketServlet");
}
resourcesFieldSet.add(field);
}
} while ((clzz = clzz.getSuperclass()) != Object.class);
final List<Field> resourcesFields = new ArrayList<>(resourcesFieldSet);
StringBuilder sb1 = new StringBuilder();
StringBuilder sb2 = new StringBuilder();
for (int i = 0; i < resourcesFields.size(); i++) {
Field field = resourcesFields.get(i);
sb1.append(Type.getDescriptor(field.getType()));
sb2.append(Utility.getTypeDescriptor(field.getGenericType()));
}
final String resourceDescriptor = sb1.toString();
final String resourceGenericDescriptor = sb1.length() == sb2.length() ? null : sb2.toString();
//----------------------------------------------------------------------------------------
final Map<String, List<String>> asmParamMap = MethodParamClassVisitor.getMethodParamNames(webSocketType);
final Set<String> messageNames = new HashSet<>();
final List<Method> messageMethods = new ArrayList<>();
for (Method method : webSocketType.getMethods()) {
RestOnMessage rom = method.getAnnotation(RestOnMessage.class);
if (rom == null) continue;
String name = rom.name();
if (name.isEmpty()) throw new RuntimeException(method + " RestOnMessage.name is empty createRestWebSocketServlet");
if (messageNames.contains(name)) throw new RuntimeException(method + " repeat RestOnMessage.name(" + name + ") createRestWebSocketServlet");
messageNames.add(name);
messageMethods.add(method);
}
//----------------------------------------------------------------------------------------
final String supDynName = WebSocketServlet.class.getName().replace('.', '/');
final String webServletDesc = Type.getDescriptor(WebServlet.class);
final String webSocketInternalName = Type.getInternalName(webSocketType);
final String newDynName = webSocketInternalName.substring(0, webSocketInternalName.lastIndexOf('/') + 1) + "_Dyn" + webSocketType.getSimpleName() + "Servlet";
final String newDynWebSokcetSimpleName = "_Dyn" + webSocketType.getSimpleName();
final String newDynWebSokcetFullName = newDynName + "$" + newDynWebSokcetSimpleName;
final String newDynMessageSimpleName = "_Dyn" + webSocketType.getSimpleName() + "Message";
final String newDynMessageFullName = newDynName + "$" + newDynMessageSimpleName;
final String newDynConsumerSimpleName = "_DynRestOnMessageConsumer";
final String newDynConsumerFullName = newDynName + "$" + newDynConsumerSimpleName;
//----------------------------------------------------------------------------------------
ClassWriter cw = new ClassWriter(COMPUTE_FRAMES);
FieldVisitor fv;
AsmMethodVisitor mv;
AnnotationVisitor av0;
cw.visit(V1_8, ACC_PUBLIC + ACC_FINAL + ACC_SUPER, newDynName, null, supDynName, null);
{ //RestDynSourceType
av0 = cw.visitAnnotation(Type.getDescriptor(RestDynSourceType.class), true);
av0.visit("value", Type.getType(Type.getDescriptor(webSocketType)));
av0.visitEnd();
}
{ //注入 @WebServlet 注解
String urlpath = (rws.catalog().isEmpty() ? "/" : ("/" + rws.catalog() + "/")) + rws.name();
av0 = cw.visitAnnotation(webServletDesc, true);
{
AnnotationVisitor av1 = av0.visitArray("value");
av1.visit(null, urlpath);
av1.visitEnd();
}
av0.visit("moduleid", 0);
av0.visit("repair", rws.repair());
av0.visit("comment", rws.comment());
av0.visitEnd();
}
{ //内部类
cw.visitInnerClass(newDynConsumerFullName, newDynName, newDynConsumerSimpleName, ACC_PUBLIC + ACC_STATIC);
cw.visitInnerClass(newDynWebSokcetFullName, newDynName, newDynWebSokcetSimpleName, ACC_PUBLIC + ACC_STATIC);
cw.visitInnerClass(newDynMessageFullName, newDynName, newDynMessageSimpleName, ACC_PUBLIC + ACC_STATIC);
for (int i = 0; i < messageMethods.size(); i++) {
Method method = messageMethods.get(i);
String endfix = "_" + method.getName() + "_" + (i > 9 ? i : ("0" + i));
cw.visitInnerClass(newDynMessageFullName + endfix, newDynName, newDynMessageSimpleName + endfix, ACC_PUBLIC + ACC_STATIC);
}
}
{ //@Resource
for (int i = 0; i < resourcesFields.size(); i++) {
Field field = resourcesFields.get(i);
Resource res = field.getAnnotation(Resource.class);
java.lang.reflect.Type fieldType = field.getGenericType();
fv = cw.visitField(ACC_PRIVATE, "_redkale_resource_" + i, Type.getDescriptor(field.getType()), fieldType == field.getType() ? null : Utility.getTypeDescriptor(fieldType), null);
{
av0 = fv.visitAnnotation("Ljavax/annotation/Resource;", true);
av0.visit("name", res.name());
av0.visitEnd();
}
fv.visitEnd();
}
}
{ //构造函数
mv = new AsmMethodVisitor(cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null));
mv.visitVarInsn(ALOAD, 0);
mv.visitMethodInsn(INVOKESPECIAL, supDynName, "<init>", "()V", false);
mv.visitVarInsn(ALOAD, 0);
mv.visitLdcInsn(Type.getObjectType(newDynName + "$" + newDynWebSokcetSimpleName + "Message"));
mv.visitFieldInsn(PUTFIELD, newDynName, "messageTextType", "Ljava/lang/reflect/Type;");
mv.visitVarInsn(ALOAD, 0);
if (rws.liveinterval() < 6) {
mv.visitInsn(ICONST_0 + rws.liveinterval());
} else {
mv.visitIntInsn(BIPUSH, rws.liveinterval());
}
mv.visitFieldInsn(PUTFIELD, newDynName, "liveinterval", "I");
mv.visitVarInsn(ALOAD, 0);
mv.visitInsn(rws.single() ? ICONST_1 : ICONST_0);
mv.visitFieldInsn(PUTFIELD, newDynName, "single", "Z");
mv.visitInsn(RETURN);
mv.visitMaxs(2, 1);
mv.visitEnd();
}
{ //createWebSocket 方法
mv = new AsmMethodVisitor(cw.visitMethod(ACC_PROTECTED, "createWebSocket", "()Lorg/redkale/net/http/WebSocket;", "<G::Ljava/io/Serializable;T:Ljava/lang/Object;>()Lorg/redkale/net/http/WebSocket<TG;TT;>;", null));
mv.visitTypeInsn(NEW, newDynName + "$" + newDynWebSokcetSimpleName);
mv.visitInsn(DUP);
for (int i = 0; i < resourcesFields.size(); i++) {
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, newDynName, "_redkale_resource_" + i, Type.getDescriptor(resourcesFields.get(i).getType()));
}
mv.visitMethodInsn(INVOKESPECIAL, newDynWebSokcetFullName, "<init>", "(" + resourceDescriptor + ")V", false);
mv.visitInsn(ARETURN);
mv.visitMaxs(2 + resourcesFields.size(), 1);
mv.visitEnd();
}
{ //createRestOnMessageConsumer
mv = new AsmMethodVisitor(cw.visitMethod(ACC_PROTECTED, "createRestOnMessageConsumer", "()Ljava/util/function/BiConsumer;", "()Ljava/util/function/BiConsumer<Lorg/redkale/net/http/WebSocket;Ljava/lang/Object;>;", null));
mv.visitTypeInsn(NEW, newDynConsumerFullName);
mv.visitInsn(DUP);
mv.visitMethodInsn(INVOKESPECIAL, newDynConsumerFullName, "<init>", "()V", false);
mv.visitInsn(ARETURN);
mv.visitMaxs(2, 1);
mv.visitEnd();
}
{ //resourceName
mv = new AsmMethodVisitor(cw.visitMethod(ACC_PUBLIC, "resourceName", "()Ljava/lang/String;", null, null));
mv.visitLdcInsn(rws.name());
mv.visitInsn(ARETURN);
mv.visitMaxs(1, 1);
mv.visitEnd();
}
RestClassLoader newLoader = new RestClassLoader(loader);
for (int i = 0; i < messageMethods.size(); i++) { // _DyncXXXWebSocketMessage List
Method method = messageMethods.get(i);
String endfix = "_" + method.getName() + "_" + (i > 9 ? i : ("0" + i));
ClassWriter cw2 = new ClassWriter(COMPUTE_FRAMES);
cw2.visit(V1_8, ACC_PUBLIC + ACC_FINAL + ACC_SUPER, newDynMessageFullName + endfix, null, "java/lang/Object", null);
cw2.visitInnerClass(newDynMessageFullName + endfix, newDynName, newDynMessageSimpleName + endfix, ACC_PUBLIC + ACC_STATIC);
Set<String> paramnames = new HashSet<>();
String methodesc = method.getName() + ":" + Type.getMethodDescriptor(method);
List<String> names = asmParamMap.get(methodesc);
Parameter[] params = method.getParameters();
for (int j = 0; j < params.length; j++) {
Parameter param = params[j];
String paramname = param.getName();
RestParam rp = param.getAnnotation(RestParam.class);
if (rp != null && !rp.name().isEmpty()) {
paramname = rp.name();
} else if (names != null && names.size() > j) {
paramname = names.get(j);
}
if (paramnames.contains(paramname)) throw new RuntimeException(method + " has same @RestParam.name");
paramnames.add(paramname);
fv = cw2.visitField(ACC_PUBLIC, paramname, Type.getDescriptor(param.getType()),
param.getType() == param.getParameterizedType() ? null : Utility.getTypeDescriptor(param.getParameterizedType()), null);
fv.visitEnd();
}
{ //构造函数
mv = new AsmMethodVisitor(cw2.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null));
mv.visitVarInsn(ALOAD, 0);
mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
mv.visitInsn(RETURN);
mv.visitMaxs(1, 1);
mv.visitEnd();
}
{ //toString
mv = new AsmMethodVisitor(cw2.visitMethod(ACC_PUBLIC, "toString", "()Ljava/lang/String;", null, null));
mv.visitMethodInsn(INVOKESTATIC, "org/redkale/convert/json/JsonConvert", "root", "()Lorg/redkale/convert/json/JsonConvert;", false);
mv.visitVarInsn(ALOAD, 0);
mv.visitMethodInsn(INVOKEVIRTUAL, "org/redkale/convert/json/JsonConvert", "convertTo", "(Ljava/lang/Object;)Ljava/lang/String;", false);
mv.visitInsn(ARETURN);
mv.visitMaxs(2, 1);
mv.visitEnd();
}
cw2.visitEnd();
newLoader.loadClass((newDynMessageFullName + endfix).replace('/', '.'), cw2.toByteArray());
}
{ //_DynXXXWebSocketMessage class
ClassWriter cw2 = new ClassWriter(COMPUTE_FRAMES);
cw2.visit(V1_8, ACC_PUBLIC + ACC_FINAL + ACC_SUPER, newDynMessageFullName, null, "java/lang/Object", null);
cw2.visitInnerClass(newDynMessageFullName, newDynName, newDynMessageSimpleName, ACC_PUBLIC + ACC_STATIC);
for (int i = 0; i < messageMethods.size(); i++) {
Method method = messageMethods.get(i);
String endfix = "_" + method.getName() + "_" + (i > 9 ? i : ("0" + i));
cw2.visitInnerClass(newDynMessageFullName + endfix, newDynName, newDynMessageSimpleName + endfix, ACC_PUBLIC + ACC_STATIC);
fv = cw2.visitField(ACC_PUBLIC, method.getAnnotation(RestOnMessage.class).name(), "L" + newDynMessageFullName + endfix + ";", null, null);
fv.visitEnd();
}
{ //构造函数
mv = new AsmMethodVisitor(cw2.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null));
mv.visitVarInsn(ALOAD, 0);
mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
mv.visitInsn(RETURN);
mv.visitMaxs(1, 1);
mv.visitEnd();
}
{ //toString
mv = new AsmMethodVisitor(cw2.visitMethod(ACC_PUBLIC, "toString", "()Ljava/lang/String;", null, null));
mv.visitMethodInsn(INVOKESTATIC, "org/redkale/convert/json/JsonConvert", "root", "()Lorg/redkale/convert/json/JsonConvert;", false);
mv.visitVarInsn(ALOAD, 0);
mv.visitMethodInsn(INVOKEVIRTUAL, "org/redkale/convert/json/JsonConvert", "convertTo", "(Ljava/lang/Object;)Ljava/lang/String;", false);
mv.visitInsn(ARETURN);
mv.visitMaxs(2, 1);
mv.visitEnd();
}
cw2.visitEnd();
newLoader.loadClass(newDynMessageFullName.replace('/', '.'), cw2.toByteArray());
}
{ //_DynXXXWebSocket class
ClassWriter cw2 = new ClassWriter(COMPUTE_FRAMES);
cw2.visit(V1_8, ACC_PUBLIC + ACC_FINAL + ACC_SUPER, newDynWebSokcetFullName, null, webSocketInternalName, null);
cw2.visitInnerClass(newDynWebSokcetFullName, newDynName, newDynWebSokcetSimpleName, ACC_PUBLIC + ACC_STATIC);
{
mv = new AsmMethodVisitor(cw2.visitMethod(ACC_PUBLIC, "<init>", "(" + resourceDescriptor + ")V", resourceGenericDescriptor == null ? null : ("(" + resourceGenericDescriptor + ")V"), null));
mv.visitVarInsn(ALOAD, 0);
mv.visitMethodInsn(INVOKESPECIAL, webSocketInternalName, "<init>", "()V", false);
for (int i = 0; i < resourcesFields.size(); i++) {
Field field = resourcesFields.get(i);
mv.visitVarInsn(ALOAD, 0);
mv.visitVarInsn(ALOAD, i + 1);
mv.visitFieldInsn(PUTFIELD, newDynWebSokcetFullName, field.getName(), Type.getDescriptor(field.getType()));
}
mv.visitInsn(RETURN);
mv.visitMaxs(2, 1 + resourcesFields.size());
mv.visitEnd();
}
cw2.visitEnd();
newLoader.loadClass(newDynWebSokcetFullName.replace('/', '.'), cw2.toByteArray());
}
{ //_DynRestOnMessageConsumer class
ClassWriter cw2 = new ClassWriter(COMPUTE_FRAMES);
cw2.visit(V1_8, ACC_PUBLIC + ACC_FINAL + ACC_SUPER, newDynConsumerFullName, "Ljava/lang/Object;Ljava/util/function/BiConsumer<Lorg/redkale/net/http/WebSocket;Ljava/lang/Object;>;", "java/lang/Object", new String[]{"java/util/function/BiConsumer"});
cw2.visitInnerClass(newDynConsumerFullName, newDynName, newDynConsumerSimpleName, ACC_PUBLIC + ACC_STATIC);
cw2.visitInnerClass(newDynMessageFullName, newDynName, newDynMessageSimpleName, ACC_PUBLIC + ACC_STATIC);
for (int i = 0; i < messageMethods.size(); i++) {
Method method = messageMethods.get(i);
String endfix = "_" + method.getName() + "_" + (i > 9 ? i : ("0" + i));
cw2.visitInnerClass(newDynMessageFullName + endfix, newDynName, newDynMessageSimpleName + endfix, ACC_PUBLIC + ACC_STATIC);
}
{ //构造函数
mv = new AsmMethodVisitor(cw2.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null));
mv.visitVarInsn(ALOAD, 0);
mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
mv.visitInsn(RETURN);
mv.visitMaxs(1, 1);
mv.visitEnd();
}
{ //accept函数
mv = new AsmMethodVisitor(cw2.visitMethod(ACC_PUBLIC, "accept", "(Lorg/redkale/net/http/WebSocket;Ljava/lang/Object;)V", null, null));
mv.visitVarInsn(ALOAD, 1);
mv.visitTypeInsn(CHECKCAST, newDynWebSokcetFullName);
mv.visitVarInsn(ASTORE, 3);
mv.visitVarInsn(ALOAD, 2);
mv.visitTypeInsn(CHECKCAST, newDynMessageFullName);
mv.visitVarInsn(ASTORE, 4);
for (int i = 0; i < messageMethods.size(); i++) {
final Method method = messageMethods.get(i);
String endfix = "_" + method.getName() + "_" + (i > 9 ? i : ("0" + i));
final String messagename = method.getAnnotation(RestOnMessage.class).name();
String methodesc = method.getName() + ":" + Type.getMethodDescriptor(method);
List<String> names = asmParamMap.get(methodesc);
Parameter[] params = method.getParameters();
mv.visitVarInsn(ALOAD, 4);
mv.visitFieldInsn(GETFIELD, newDynMessageFullName, messagename, "L" + (newDynMessageFullName + endfix) + ";");
Label ifLabel = new Label();
mv.visitJumpInsn(IFNULL, ifLabel);
mv.visitVarInsn(ALOAD, 3);
for (int j = 0; j < params.length; j++) {
Parameter param = params[j];
String paramname = param.getName();
RestParam rp = param.getAnnotation(RestParam.class);
if (rp != null && !rp.name().isEmpty()) {
paramname = rp.name();
} else if (names != null && names.size() > j) {
paramname = names.get(j);
}
mv.visitVarInsn(ALOAD, 4);
mv.visitFieldInsn(GETFIELD, newDynMessageFullName, messagename, "L" + (newDynMessageFullName + endfix) + ";");
mv.visitFieldInsn(GETFIELD, (newDynMessageFullName + endfix), paramname, Type.getDescriptor(param.getType()));
}
mv.visitMethodInsn(INVOKEVIRTUAL, newDynWebSokcetFullName, method.getName(), Type.getMethodDescriptor(method), false);
mv.visitInsn(RETURN);
mv.visitLabel(ifLabel);
}
mv.visitInsn(RETURN);
mv.visitMaxs(3, 3 + messageMethods.size());
mv.visitEnd();
}
{//虚拟accept函数
mv = new AsmMethodVisitor(cw2.visitMethod(ACC_PUBLIC + ACC_BRIDGE + ACC_SYNTHETIC, "accept", "(Ljava/lang/Object;Ljava/lang/Object;)V", null, null));
mv.visitVarInsn(ALOAD, 0);
mv.visitVarInsn(ALOAD, 1);
mv.visitTypeInsn(CHECKCAST, "org/redkale/net/http/WebSocket");
mv.visitVarInsn(ALOAD, 2);
mv.visitTypeInsn(CHECKCAST, "java/lang/Object");
mv.visitMethodInsn(INVOKEVIRTUAL, newDynConsumerFullName, "accept", "(Lorg/redkale/net/http/WebSocket;Ljava/lang/Object;)V", false);
mv.visitInsn(RETURN);
mv.visitMaxs(3, 3);
mv.visitEnd();
}
cw2.visitEnd();
newLoader.loadClass(newDynConsumerFullName.replace('/', '.'), cw2.toByteArray());
}
cw.visitEnd();
Class<?> newClazz = newLoader.loadClass(newDynName.replace('/', '.'), cw.toByteArray());
try {
return (T) newClazz.newInstance();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
static <T extends HttpServlet> T createRestServlet(final ClassLoader classLoader, final Class userType0, final Class<T> baseServletType, final Class<? extends Service> serviceType) {
if (baseServletType == null || serviceType == null) throw new RuntimeException(" Servlet or Service is null Class on createRestServlet");
if (!HttpServlet.class.isAssignableFrom(baseServletType)) throw new RuntimeException(baseServletType + " is not HttpServlet Class on createRestServlet");
int mod = baseServletType.getModifiers();
if (!java.lang.reflect.Modifier.isPublic(mod)) throw new RuntimeException(baseServletType + " is not Public Class on createRestServlet");
if (java.lang.reflect.Modifier.isAbstract(mod)) throw new RuntimeException(baseServletType + " cannot a abstract Class on createRestServlet");
final String serviceDesc = Type.getDescriptor(serviceType);
final String webServletDesc = Type.getDescriptor(WebServlet.class);
final String reqDesc = Type.getDescriptor(HttpRequest.class);
final String respDesc = Type.getDescriptor(HttpResponse.class);
final String convertDesc = Type.getDescriptor(JsonConvert.class);
final String retDesc = Type.getDescriptor(RetResult.class);
final String futureDesc = Type.getDescriptor(CompletableFuture.class);
final String flipperDesc = Type.getDescriptor(Flipper.class);
final String httprsDesc = Type.getDescriptor(HttpResult.class);
final String attrDesc = Type.getDescriptor(org.redkale.util.Attribute.class);
final String mappingDesc = Type.getDescriptor(HttpMapping.class);
final String webparamDesc = Type.getDescriptor(HttpParam.class);
final String webparamsDesc = Type.getDescriptor(HttpParam.HttpParams.class);
final String sourcetypeDesc = Type.getDescriptor(HttpParam.HttpParamSourceType.class);
final String reqInternalName = Type.getInternalName(HttpRequest.class);
final String respInternalName = Type.getInternalName(HttpResponse.class);
final String attrInternalName = Type.getInternalName(org.redkale.util.Attribute.class);
final String retInternalName = Type.getInternalName(RetResult.class);
final String serviceTypeInternalName = Type.getInternalName(serviceType);
HttpUserType hut = baseServletType.getAnnotation(HttpUserType.class);
final Class userType = (userType0 == null || userType0 == Object.class) ? (hut == null ? null : hut.value()) : userType0;
final String supDynName = baseServletType.getName().replace('.', '/');
final RestService controller = serviceType.getAnnotation(RestService.class);
if (controller != null && controller.ignore()) throw new RuntimeException(serviceType + " is ignore Rest Service Class"); //标记为ignore=true不创建Servlet
ClassLoader loader = classLoader == null ? Thread.currentThread().getContextClassLoader() : classLoader;
String newDynName = serviceTypeInternalName.substring(0, serviceTypeInternalName.lastIndexOf('/') + 1) + "_Dyn" + serviceType.getSimpleName().replaceAll("Service.*$", "") + "RestServlet";
//------------------------------------------------------------------------------
final String defmodulename = getWebModuleName(serviceType);
final String catalog = controller == null ? "" : controller.catalog();
if (!checkName(catalog)) throw new RuntimeException(serviceType.getName() + " have illeal " + RestService.class.getSimpleName() + ".catalog, only 0-9 a-z A-Z _ cannot begin 0-9");
if (!checkName(defmodulename)) throw new RuntimeException(serviceType.getName() + " have illeal " + RestService.class.getSimpleName() + ".value, only 0-9 a-z A-Z _ cannot begin 0-9");
ClassWriter cw = new ClassWriter(COMPUTE_FRAMES);
FieldVisitor fv;
AsmMethodVisitor mv;
AnnotationVisitor av0;
Map<String, Object> classMap = new LinkedHashMap<>();
List<Map<String, Object>> mappingMaps = new ArrayList<>();
cw.visit(V1_8, ACC_PUBLIC + ACC_FINAL + ACC_SUPER, newDynName, null, supDynName, null);
{ //RestDyn
av0 = cw.visitAnnotation(Type.getDescriptor(RestDyn.class), true);
av0.visitEnd();
}
{ //RestDynSourceType
av0 = cw.visitAnnotation(Type.getDescriptor(RestDynSourceType.class), true);
av0.visit("value", Type.getType(Type.getDescriptor(serviceType)));
av0.visitEnd();
}
{ //注入 @WebServlet 注解
String urlpath = (catalog.isEmpty() ? "/" : ("/" + catalog + "/")) + defmodulename + "/*";
int moduleid = controller == null ? 0 : controller.moduleid();
boolean repair = controller == null ? true : controller.repair();
String comment = controller == null ? "" : controller.comment();
av0 = cw.visitAnnotation(webServletDesc, true);
{
AnnotationVisitor av1 = av0.visitArray("value");
av1.visit(null, urlpath);
av1.visitEnd();
}
av0.visit("moduleid", moduleid);
av0.visit("repair", repair);
av0.visit("comment", comment);
av0.visitEnd();
classMap.put("type", serviceType.getName());
classMap.put("url", urlpath);
classMap.put("moduleid", moduleid);
classMap.put("repair", repair);
//classMap.put("comment", comment); //不显示太多信息
}
{ //注入 @Resource private XXXService _service;
fv = cw.visitField(ACC_PRIVATE, REST_SERVICE_FIELD_NAME, serviceDesc, null, null);
av0 = fv.visitAnnotation("Ljavax/annotation/Resource;", true);
av0.visit("name", "");
av0.visitEnd();
fv.visitEnd();
}
{ //注入 @Resource(name = "APP_HOME") private File _redkale_home;
fv = cw.visitField(ACC_PRIVATE, "_redkale_home", Type.getDescriptor(File.class), null, null);
av0 = fv.visitAnnotation("Ljavax/annotation/Resource;", true);
av0.visit("name", "APP_HOME");
av0.visitEnd();
fv.visitEnd();
}
{ //_servicemap字段 Map<String, XXXService>
fv = cw.visitField(ACC_PRIVATE, REST_SERVICEMAP_FIELD_NAME, "Ljava/util/Map;", "Ljava/util/Map<Ljava/lang/String;" + serviceDesc + ">;", null);
fv.visitEnd();
}
{ //_paramtypes字段 java.lang.reflect.Type[][]
fv = cw.visitField(ACC_PRIVATE, REST_PARAMTYPES_FIELD_NAME, "[[Ljava/lang/reflect/Type;", null, null);
fv.visitEnd();
}
{ //构造函数
mv = new AsmMethodVisitor(cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null));
//mv.setDebug(true);
mv.visitVarInsn(ALOAD, 0);
mv.visitMethodInsn(INVOKESPECIAL, supDynName, "<init>", "()V", false);
mv.visitInsn(RETURN);
mv.visitMaxs(1, 1);
mv.visitEnd();
}
final List<MappingEntry> entrys = new ArrayList<>();
final Map<String, org.redkale.util.Attribute> restAttributes = new LinkedHashMap<>();
//获取所有可以转换成HttpMapping的方法
int methodidex = 0;
final List<java.lang.reflect.Type[]> paramtypes = new ArrayList<>();
for (final Method method : serviceType.getMethods()) {
if (Modifier.isStatic(method.getModifiers())) continue;
Class[] extypes = method.getExceptionTypes();
if (extypes.length > 1) continue;
if (extypes.length == 1 && extypes[0] != IOException.class) continue;
if (EXCLUDERMETHODS.contains(method.getName())) continue;
if ("init".equals(method.getName())) continue;
if ("destroy".equals(method.getName())) continue;
if ("version".equals(method.getName())) continue;
RestMapping[] mappings = method.getAnnotationsByType(RestMapping.class);
if (controller == null) continue;
if (!controller.automapping() && mappings.length < 1) continue;
boolean ignore = false;
for (RestMapping mapping : mappings) {
if (mapping.ignore()) {
ignore = true;
break;
}
}
if (ignore) continue;
paramtypes.add(method.getGenericParameterTypes());
if (mappings.length == 0) { //没有Mapping设置一个默认值
MappingEntry entry = new MappingEntry(methodidex, null, defmodulename, method);
if (entrys.contains(entry)) throw new RuntimeException(serviceType.getName() + " on " + method.getName() + " 's mapping(" + entry.name + ") is repeat");
entrys.add(entry);
} else {
for (RestMapping mapping : mappings) {
MappingEntry entry = new MappingEntry(methodidex, mapping, defmodulename, method);
if (entrys.contains(entry)) throw new RuntimeException(serviceType.getName() + " on " + method.getName() + " 's mapping(" + entry.name + ") is repeat");
entrys.add(entry);
}
}
methodidex++;
}
if (entrys.isEmpty()) return null; //没有可HttpMapping的方法
//将每个Service可转换的方法生成HttpServlet对应的HttpMapping方法
final Map<String, List<String>> asmParamMap = MethodParamClassVisitor.getMethodParamNames(serviceType);
final Map<String, java.lang.reflect.Type> bodyTypes = new HashMap<>();
final List<RestConvert[]> restConverts = new ArrayList<>();
for (final MappingEntry entry : entrys) {
RestUploadFile mupload = null;
Class muploadType = null;
final Method method = entry.mappingMethod;
final Class returnType = method.getReturnType();
final String methodDesc = Type.getMethodDescriptor(method);
final Parameter[] params = method.getParameters();
final RestConvert[] rcs = method.getAnnotationsByType(RestConvert.class);
if (rcs != null && rcs.length > 0) restConverts.add(rcs);
mv = new AsmMethodVisitor(cw.visitMethod(ACC_PUBLIC, entry.name, "(" + reqDesc + respDesc + ")V", null, new String[]{"java/io/IOException"}));
//mv.setDebug(true);
mv.debugLine();
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, newDynName, REST_SERVICEMAP_FIELD_NAME, "Ljava/util/Map;");
Label lmapif = new Label();
mv.visitJumpInsn(IFNONNULL, lmapif);
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, newDynName, REST_SERVICE_FIELD_NAME, serviceDesc);
Label lserif = new Label();
mv.visitJumpInsn(GOTO, lserif);
mv.visitLabel(lmapif);
mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, newDynName, REST_SERVICEMAP_FIELD_NAME, "Ljava/util/Map;");
mv.visitVarInsn(ALOAD, 1);
mv.visitLdcInsn(REST_HEADER_RESOURCE_NAME);
mv.visitLdcInsn("");
mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getHeader", "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;", false);
mv.visitMethodInsn(INVOKEINTERFACE, "java/util/Map", "get", "(Ljava/lang/Object;)Ljava/lang/Object;", true);
mv.visitTypeInsn(CHECKCAST, serviceTypeInternalName);
mv.visitLabel(lserif);
mv.visitFrame(Opcodes.F_SAME1, 0, null, 1, new Object[]{serviceTypeInternalName});
mv.visitVarInsn(ASTORE, 3);
final int maxStack = 3 + params.length;
List<int[]> varInsns = new ArrayList<>();
int maxLocals = 4;
List<String> asmParamNames = asmParamMap == null ? null : asmParamMap.get(method.getName() + ":" + Type.getMethodDescriptor(method));
List<Object[]> paramlist = new ArrayList<>();
//解析方法中的每个参数
for (int i = 0; i < params.length; i++) {
final Parameter param = params[i];
final Class ptype = param.getType();
String n = null;
String comment = "";
boolean required = true;
int radix = 10;
RestHeader annhead = param.getAnnotation(RestHeader.class);
if (annhead != null) {
if (ptype != String.class) throw new RuntimeException("@RestHeader must on String Parameter in " + method);
n = annhead.name();
radix = annhead.radix();
comment = annhead.comment();
if (n.isEmpty()) throw new RuntimeException("@RestHeader.value is illegal in " + method);
}
RestCookie anncookie = param.getAnnotation(RestCookie.class);
if (anncookie != null) {
if (annhead != null) throw new RuntimeException("@RestCookie and @RestHeader cannot on the same Parameter in " + method);
if (ptype != String.class) throw new RuntimeException("@RestCookie must on String Parameter in " + method);
n = anncookie.name();
radix = anncookie.radix();
comment = anncookie.comment();
if (n.isEmpty()) throw new RuntimeException("@RestCookie.value is illegal in " + method);
}
RestSessionid annsid = param.getAnnotation(RestSessionid.class);
if (annsid != null) {
if (annhead != null) throw new RuntimeException("@RestSessionid and @RestHeader cannot on the same Parameter in " + method);
if (anncookie != null) throw new RuntimeException("@RestSessionid and @RestCookie cannot on the same Parameter in " + method);
if (ptype != String.class) throw new RuntimeException("@RestSessionid must on String Parameter in " + method);
}
RestAddress annaddr = param.getAnnotation(RestAddress.class);
if (annaddr != null) {
if (annhead != null) throw new RuntimeException("@RestAddress and @RestHeader cannot on the same Parameter in " + method);
if (anncookie != null) throw new RuntimeException("@RestAddress and @RestCookie cannot on the same Parameter in " + method);
if (annsid != null) throw new RuntimeException("@RestAddress and @RestSessionid cannot on the same Parameter in " + method);
if (ptype != String.class) throw new RuntimeException("@RestAddress must on String Parameter in " + method);
comment = annaddr.comment();
}
RestBody annbody = param.getAnnotation(RestBody.class);
if (annbody != null) {
if (annhead != null) throw new RuntimeException("@RestBody and @RestHeader cannot on the same Parameter in " + method);
if (anncookie != null) throw new RuntimeException("@RestBody and @RestCookie cannot on the same Parameter in " + method);
if (annsid != null) throw new RuntimeException("@RestBody and @RestSessionid cannot on the same Parameter in " + method);
if (annaddr != null) throw new RuntimeException("@RestBody and @RestAddress cannot on the same Parameter in " + method);
if (ptype.isPrimitive()) throw new RuntimeException("@RestBody cannot on primitive type Parameter in " + method);
comment = annbody.comment();
}
RestUploadFile annfile = param.getAnnotation(RestUploadFile.class);
if (annfile != null) {
if (mupload != null) throw new RuntimeException("@RestUploadFile repeat in " + method);
mupload = annfile;
muploadType = ptype;
if (annhead != null) throw new RuntimeException("@RestUploadFile and @RestHeader cannot on the same Parameter in " + method);
if (anncookie != null) throw new RuntimeException("@RestUploadFile and @RestCookie cannot on the same Parameter in " + method);
if (annsid != null) throw new RuntimeException("@RestUploadFile and @RestSessionid cannot on the same Parameter in " + method);
if (annaddr != null) throw new RuntimeException("@RestUploadFile and @RestAddress cannot on the same Parameter in " + method);
if (annbody != null) throw new RuntimeException("@RestUploadFile and @RestBody cannot on the same Parameter in " + method);
if (ptype != byte[].class && ptype != File.class && ptype != File[].class) throw new RuntimeException("@RestUploadFile must on byte[] or File or File[] Parameter in " + method);
comment = annfile.comment();
}
RestURI annuri = param.getAnnotation(RestURI.class);
if (annuri != null) {
if (annhead != null) throw new RuntimeException("@RestURI and @RestHeader cannot on the same Parameter in " + method);
if (anncookie != null) throw new RuntimeException("@RestURI and @RestCookie cannot on the same Parameter in " + method);
if (annsid != null) throw new RuntimeException("@RestURI and @RestSessionid cannot on the same Parameter in " + method);
if (annaddr != null) throw new RuntimeException("@RestURI and @RestAddress cannot on the same Parameter in " + method);
if (annbody != null) throw new RuntimeException("@RestURI and @RestBody cannot on the same Parameter in " + method);
if (annfile != null) throw new RuntimeException("@RestURI and @RestUploadFile cannot on the same Parameter in " + method);
if (ptype != String.class) throw new RuntimeException("@RestURI must on String Parameter in " + method);
comment = annuri.comment();
}
RestParam annpara = param.getAnnotation(RestParam.class);
if (annpara != null) radix = annpara.radix();
if (annpara != null) comment = annpara.comment();
if (annpara != null) required = annpara.required();
if (n == null) n = (annpara == null || annpara.name().isEmpty()) ? null : annpara.name();
if (n == null && ptype == userType) n = "&"; //用户类型特殊处理
if (n == null && asmParamNames != null && asmParamNames.size() > i) n = asmParamNames.get(i);
if (n == null) {
if (param.isNamePresent()) {
n = param.getName();
} else if (ptype == Flipper.class) {
n = "flipper";
} else {
throw new RuntimeException("Parameter " + param.getName() + " not found name by @RestParam in " + method);
}
}
if (annhead == null && anncookie == null && annaddr == null && annbody == null && annfile == null
&& (entry.name.startsWith("find") || entry.name.startsWith("delete")) && params.length == 1) {
if (ptype.isPrimitive() || ptype == String.class) n = "#";
}
if (annhead == null && anncookie == null && annsid == null && annaddr == null && annbody == null && annfile == null
&& !ptype.isPrimitive() && ptype != String.class && ptype != Flipper.class && !AsyncHandler.class.isAssignableFrom(ptype)
&& !ptype.getName().startsWith("java") && n.charAt(0) != '#' && !"&".equals(n)) { //判断Json对象是否包含@RestUploadFile
Class loop = ptype;
do {
if (loop == null || loop.isInterface()) break; //接口时getSuperclass可能会得到null
for (Field field : loop.getDeclaredFields()) {
if (Modifier.isStatic(field.getModifiers())) continue;
if (Modifier.isFinal(field.getModifiers())) continue;
RestUploadFile ruf = field.getAnnotation(RestUploadFile.class);
if (ruf == null) continue;
if (mupload != null) throw new RuntimeException("@RestUploadFile repeat in " + method + " or field " + field);
mupload = ruf;
muploadType = field.getType();
}
} while ((loop = loop.getSuperclass()) != Object.class);
}
paramlist.add(new Object[]{param, n, ptype, radix, comment, required, annpara, annsid, annaddr, annhead, anncookie, annbody, annfile, annuri, param.getParameterizedType()});
}
Map<String, Object> mappingMap = new LinkedHashMap<>();
{ // 设置 Annotation
//设置 HttpMapping
boolean reqpath = false;
for (Object[] ps : paramlist) {
if ("#".equals((String) ps[1])) {
reqpath = true;
break;
}
}
av0 = mv.visitAnnotation(mappingDesc, true);
String url = (catalog.isEmpty() ? "/" : ("/" + catalog + "/")) + defmodulename.toLowerCase() + "/" + entry.name + (reqpath ? "/" : "");
av0.visit("url", url);
av0.visit("auth", entry.auth);
av0.visit("cacheseconds", entry.cacheseconds);
av0.visit("actionid", entry.actionid);
av0.visit("comment", entry.comment);
AnnotationVisitor av1 = av0.visitArray("methods");
for (String m : entry.methods) {
av1.visit(null, m);
}
av1.visitEnd();
java.lang.reflect.Type grt = method.getGenericReturnType();
av0.visit("result", grt == returnType ? returnType.getName() : String.valueOf(grt));
av0.visitEnd();
mappingMap.put("url", url);
mappingMap.put("auth", entry.auth);
mappingMap.put("cacheseconds", entry.cacheseconds);
mappingMap.put("actionid", entry.actionid);
mappingMap.put("comment", entry.comment);
mappingMap.put("methods", entry.methods);
mappingMap.put("result", grt == returnType ? returnType.getName() : String.valueOf(grt));
}
{ // 设置 Annotation
av0 = mv.visitAnnotation(webparamsDesc, true);
AnnotationVisitor av1 = av0.visitArray("value");
//设置 WebParam
for (Object[] ps : paramlist) { //{param, n, ptype, radix, comment, required, annpara, annsid, annaddr, annhead, anncookie, annbody, annfile, annuri, pgentype}
final boolean ishead = ((RestHeader) ps[9]) != null; //是否取getHeader 而不是 getParameter
final boolean iscookie = ((RestCookie) ps[10]) != null; //是否取getCookie
AnnotationVisitor av2 = av1.visitAnnotation(null, webparamDesc);
av2.visit("name", (String) ps[1]);
av2.visit("type", Type.getType(Type.getDescriptor((Class) ps[2])));
av2.visit("radix", (Integer) ps[3]);
av2.visitEnum("src", sourcetypeDesc, ishead ? HttpParam.HttpParamSourceType.HEADER.name()
: (iscookie ? HttpParam.HttpParamSourceType.COOKIE.name() : HttpParam.HttpParamSourceType.PARAMETER.name()));
av2.visit("comment", (String) ps[4]);
av2.visit("required", (Boolean) ps[5]);
av2.visitEnd();
}
av1.visitEnd();
av0.visitEnd();
}
if (mupload != null) { //存在文件上传
if (muploadType == byte[].class) {
mv.visitVarInsn(ALOAD, 1);
mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getMultiContext", "()Lorg/redkale/net/http/MultiContext;", false);
mv.visitLdcInsn(mupload.maxLength());
mv.visitLdcInsn(mupload.fileNameReg());
mv.visitLdcInsn(mupload.contentTypeReg());
mv.visitMethodInsn(INVOKEVIRTUAL, "org/redkale/net/http/MultiContext", "partsFirstBytes", "(JLjava/lang/String;Ljava/lang/String;)[B", false);
mv.visitVarInsn(ASTORE, maxLocals);
} else if (muploadType == File.class) {
mv.visitVarInsn(ALOAD, 1);
mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getMultiContext", "()Lorg/redkale/net/http/MultiContext;", false);
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, newDynName, "_redkale_home", "Ljava/io/File;");
mv.visitLdcInsn(mupload.maxLength());
mv.visitLdcInsn(mupload.fileNameReg());
mv.visitLdcInsn(mupload.contentTypeReg());
mv.visitMethodInsn(INVOKEVIRTUAL, "org/redkale/net/http/MultiContext", "partsFirstFile", "(Ljava/io/File;JLjava/lang/String;Ljava/lang/String;)Ljava/io/File;", false);
mv.visitVarInsn(ASTORE, maxLocals);
} else if (muploadType == File[].class) { //File[]
mv.visitVarInsn(ALOAD, 1);
mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getMultiContext", "()Lorg/redkale/net/http/MultiContext;", false);
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, newDynName, "_redkale_home", "Ljava/io/File;");
mv.visitLdcInsn(mupload.maxLength());
mv.visitLdcInsn(mupload.fileNameReg());
mv.visitLdcInsn(mupload.contentTypeReg());
mv.visitMethodInsn(INVOKEVIRTUAL, "org/redkale/net/http/MultiContext", "partsFiles", "(Ljava/io/File;JLjava/lang/String;Ljava/lang/String;)[Ljava/io/File;", false);
mv.visitVarInsn(ASTORE, maxLocals);
}
maxLocals++;
}
List<Map<String, Object>> paramMaps = new ArrayList<>();
//获取每个参数的值
boolean hasAsyncHandler = false;
for (Object[] ps : paramlist) {
Map<String, Object> paramMap = new LinkedHashMap<>();
final Parameter param = (Parameter) ps[0]; //参数类型
String pname = (String) ps[1]; //参数名
Class ptype = (Class) ps[2]; //参数类型
int radix = (Integer) ps[3];
String comment = (String) ps[4];
boolean required = (Boolean) ps[5];
RestParam annpara = (RestParam) ps[6];
RestSessionid annsid = (RestSessionid) ps[7];
RestAddress annaddr = (RestAddress) ps[8];
RestHeader annhead = (RestHeader) ps[9];
RestCookie anncookie = (RestCookie) ps[10];
RestBody annbody = (RestBody) ps[11];
RestUploadFile annfile = (RestUploadFile) ps[12];
RestURI annuri = (RestURI) ps[13];
java.lang.reflect.Type pgentype = (java.lang.reflect.Type) ps[14];
final boolean ishead = annhead != null; //是否取getHeader 而不是 getParameter
final boolean iscookie = anncookie != null; //是否取getCookie
paramMap.put("name", pname);
paramMap.put("type", ptype.getName());
if (AsyncHandler.class.isAssignableFrom(ptype)) { //HttpResponse.createAsyncHandler() or HttpResponse.createAsyncHandler(Class)
if (ptype == AsyncHandler.class) {
mv.visitVarInsn(ALOAD, 2);
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "createAsyncHandler", "()Lorg/redkale/util/AsyncHandler;", false);
mv.visitVarInsn(ASTORE, maxLocals);
varInsns.add(new int[]{ALOAD, maxLocals});
} else {
mv.visitVarInsn(ALOAD, 3);
mv.visitVarInsn(ALOAD, 2);
mv.visitLdcInsn(Type.getType(Type.getDescriptor(ptype)));
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "createAsyncHandler", "(Ljava/lang/Class;)Lorg/redkale/util/AsyncHandler;", false);
mv.visitTypeInsn(CHECKCAST, ptype.getName().replace('.', '/'));
mv.visitVarInsn(ASTORE, maxLocals);
varInsns.add(new int[]{ALOAD, maxLocals});
}
hasAsyncHandler = true;
} else if (annsid != null) { //HttpRequest.getSessionid(true|false)
mv.visitVarInsn(ALOAD, 1);
mv.visitInsn(annsid.create() ? ICONST_1 : ICONST_0);
mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getSessionid", "(Z)Ljava/lang/String;", false);
mv.visitVarInsn(ASTORE, maxLocals);
varInsns.add(new int[]{ALOAD, maxLocals});
} else if (annaddr != null) { //HttpRequest.getRemoteAddr
mv.visitVarInsn(ALOAD, 1);
mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getRemoteAddr", "()Ljava/lang/String;", false);
mv.visitVarInsn(ASTORE, maxLocals);
varInsns.add(new int[]{ALOAD, maxLocals});
} else if (annbody != null) { //HttpRequest.getBodyUTF8 / HttpRequest.getBody
if (ptype == String.class) {
mv.visitVarInsn(ALOAD, 1);
mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getBodyUTF8", "()Ljava/lang/String;", false);
mv.visitVarInsn(ASTORE, maxLocals);
varInsns.add(new int[]{ALOAD, maxLocals});
} else if (ptype == byte[].class) {
mv.visitVarInsn(ALOAD, 1);
mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getBody", "()[B", false);
mv.visitVarInsn(ASTORE, maxLocals);
varInsns.add(new int[]{ALOAD, maxLocals});
} else { //JavaBean 转 Json
String typefieldname = "_redkale_body_jsontype_" + bodyTypes.size();
bodyTypes.put(typefieldname, pgentype);
mv.visitVarInsn(ALOAD, 1);
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, newDynName, typefieldname, "Ljava/lang/reflect/Type;");
mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getBodyJson", "(Ljava/lang/reflect/Type;)Ljava/lang/Object;", false);
mv.visitTypeInsn(CHECKCAST, Type.getInternalName(ptype));
mv.visitVarInsn(ASTORE, maxLocals);
varInsns.add(new int[]{ALOAD, maxLocals});
}
} else if (annfile != null) { //MultiContext.partsFirstBytes / HttpRequest.partsFirstFile / HttpRequest.partsFiles
mv.visitVarInsn(ALOAD, 4);
mv.visitVarInsn(ASTORE, maxLocals);
varInsns.add(new int[]{ALOAD, maxLocals});
} else if (annuri != null) { //HttpRequest.getRequestURI
mv.visitVarInsn(ALOAD, 1);
mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getRequestURI", "()Ljava/lang/String;", false);
mv.visitVarInsn(ASTORE, maxLocals);
varInsns.add(new int[]{ALOAD, maxLocals});
} else if ("#".equals(pname)) { //从request.getRequstURI 中取参数
if (ptype == boolean.class) {
mv.visitVarInsn(ALOAD, 1);
mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getRequstURILastPath", "()Ljava/lang/String;", false);
mv.visitMethodInsn(INVOKESTATIC, "java/lang/Boolean", "parseBoolean", "(Ljava/lang/String;)Z", false);
mv.visitVarInsn(ISTORE, maxLocals);
varInsns.add(new int[]{ILOAD, maxLocals});
} else if (ptype == byte.class) {
mv.visitVarInsn(ALOAD, 1);
mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getRequstURILastPath", "()Ljava/lang/String;", false);
mv.visitIntInsn(BIPUSH, radix);
mv.visitMethodInsn(INVOKESTATIC, "java/lang/Byte", "parseByte", "(Ljava/lang/String;I)B", false);
mv.visitVarInsn(ISTORE, maxLocals);
varInsns.add(new int[]{ILOAD, maxLocals});
} else if (ptype == short.class) {
mv.visitVarInsn(ALOAD, 1);
mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getRequstURILastPath", "()Ljava/lang/String;", false);
mv.visitIntInsn(BIPUSH, radix);
mv.visitMethodInsn(INVOKESTATIC, "java/lang/Short", "parseShort", "(Ljava/lang/String;I)S", false);
mv.visitVarInsn(ISTORE, maxLocals);
varInsns.add(new int[]{ILOAD, maxLocals});
} else if (ptype == char.class) {
mv.visitVarInsn(ALOAD, 1);
mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getRequstURILastPath", "()Ljava/lang/String;", false);
mv.visitInsn(ICONST_0);
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/String", "charAt", "(I)C", false);
mv.visitVarInsn(ISTORE, maxLocals);
varInsns.add(new int[]{ILOAD, maxLocals});
} else if (ptype == int.class) {
mv.visitVarInsn(ALOAD, 1);
mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getRequstURILastPath", "()Ljava/lang/String;", false);
mv.visitIntInsn(BIPUSH, radix);
mv.visitMethodInsn(INVOKESTATIC, "java/lang/Integer", "parseInt", "(Ljava/lang/String;I)I", false);
mv.visitVarInsn(ISTORE, maxLocals);
varInsns.add(new int[]{ILOAD, maxLocals});
} else if (ptype == float.class) {
mv.visitVarInsn(ALOAD, 1);
mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getRequstURILastPath", "()Ljava/lang/String;", false);
mv.visitMethodInsn(INVOKESTATIC, "java/lang/Float", "parseFloat", "(Ljava/lang/String;)F", false);
mv.visitVarInsn(FSTORE, maxLocals);
varInsns.add(new int[]{FLOAD, maxLocals});
} else if (ptype == long.class) {
mv.visitVarInsn(ALOAD, 1);
mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getRequstURILastPath", "()Ljava/lang/String;", false);
mv.visitIntInsn(BIPUSH, radix);
mv.visitMethodInsn(INVOKESTATIC, "java/lang/Long", "parseLong", "(Ljava/lang/String;I)J", false);
mv.visitVarInsn(LSTORE, maxLocals);
varInsns.add(new int[]{LLOAD, maxLocals});
maxLocals++;
} else if (ptype == double.class) {
mv.visitVarInsn(ALOAD, 1);
mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getRequstURILastPath", "()Ljava/lang/String;", false);
mv.visitMethodInsn(INVOKESTATIC, "java/lang/Double", "parseDouble", "(Ljava/lang/String;)D", false);
mv.visitVarInsn(DSTORE, maxLocals);
varInsns.add(new int[]{DLOAD, maxLocals});
maxLocals++;
} else if (ptype == String.class) {
mv.visitVarInsn(ALOAD, 1);
mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getRequstURILastPath", "()Ljava/lang/String;", false);
mv.visitVarInsn(ASTORE, maxLocals);
varInsns.add(new int[]{ALOAD, maxLocals});
} else {
throw new RuntimeException(method + " only " + RestParam.class.getSimpleName() + "(#) to Type(primitive class or String)");
}
} else if (pname.charAt(0) == '#') { //从request.getRequstURIPath 中去参数
if (ptype == boolean.class) {
mv.visitVarInsn(ALOAD, 1);
mv.visitLdcInsn(pname.substring(1));
mv.visitLdcInsn("false");
mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getRequstURIPath", "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;", false);
mv.visitMethodInsn(INVOKESTATIC, "java/lang/Boolean", "parseBoolean", "(Ljava/lang/String;)Z", false);
mv.visitVarInsn(ISTORE, maxLocals);
varInsns.add(new int[]{ILOAD, maxLocals});
} else if (ptype == byte.class) {
mv.visitVarInsn(ALOAD, 1);
mv.visitLdcInsn(pname.substring(1));
mv.visitLdcInsn("0");
mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getRequstURIPath", "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;", false);
mv.visitIntInsn(BIPUSH, radix);
mv.visitMethodInsn(INVOKESTATIC, "java/lang/Byte", "parseByte", "(Ljava/lang/String;I)B", false);
mv.visitVarInsn(ISTORE, maxLocals);
varInsns.add(new int[]{ILOAD, maxLocals});
} else if (ptype == short.class) {
mv.visitVarInsn(ALOAD, 1);
mv.visitLdcInsn(pname.substring(1));
mv.visitLdcInsn("0");
mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getRequstURIPath", "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;", false);
mv.visitIntInsn(BIPUSH, radix);
mv.visitMethodInsn(INVOKESTATIC, "java/lang/Short", "parseShort", "(Ljava/lang/String;I)S", false);
mv.visitVarInsn(ISTORE, maxLocals);
varInsns.add(new int[]{ILOAD, maxLocals});
} else if (ptype == char.class) {
mv.visitVarInsn(ALOAD, 1);
mv.visitLdcInsn(pname.substring(1));
mv.visitLdcInsn("0");
mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getRequstURIPath", "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;", false);
mv.visitInsn(ICONST_0);
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/String", "charAt", "(I)C", false);
mv.visitVarInsn(ISTORE, maxLocals);
varInsns.add(new int[]{ILOAD, maxLocals});
} else if (ptype == int.class) {
mv.visitVarInsn(ALOAD, 1);
mv.visitLdcInsn(pname.substring(1));
mv.visitLdcInsn("0");
mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getRequstURIPath", "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;", false);
mv.visitIntInsn(BIPUSH, radix);
mv.visitMethodInsn(INVOKESTATIC, "java/lang/Integer", "parseInt", "(Ljava/lang/String;I)I", false);
mv.visitVarInsn(ISTORE, maxLocals);
varInsns.add(new int[]{ILOAD, maxLocals});
} else if (ptype == float.class) {
mv.visitVarInsn(ALOAD, 1);
mv.visitLdcInsn(pname.substring(1));
mv.visitLdcInsn("0");
mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getRequstURIPath", "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;", false);
mv.visitMethodInsn(INVOKESTATIC, "java/lang/Float", "parseFloat", "(Ljava/lang/String;)F", false);
mv.visitVarInsn(FSTORE, maxLocals);
varInsns.add(new int[]{FLOAD, maxLocals});
} else if (ptype == long.class) {
mv.visitVarInsn(ALOAD, 1);
mv.visitLdcInsn(pname.substring(1));
mv.visitLdcInsn("0");
mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getRequstURIPath", "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;", false);
mv.visitIntInsn(BIPUSH, radix);
mv.visitMethodInsn(INVOKESTATIC, "java/lang/Long", "parseLong", "(Ljava/lang/String;I)J", false);
mv.visitVarInsn(LSTORE, maxLocals);
varInsns.add(new int[]{LLOAD, maxLocals});
maxLocals++;
} else if (ptype == double.class) {
mv.visitVarInsn(ALOAD, 1);
mv.visitLdcInsn(pname.substring(1));
mv.visitLdcInsn("0");
mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getRequstURIPath", "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;", false);
mv.visitMethodInsn(INVOKESTATIC, "java/lang/Double", "parseDouble", "(Ljava/lang/String;)D", false);
mv.visitVarInsn(DSTORE, maxLocals);
varInsns.add(new int[]{DLOAD, maxLocals});
maxLocals++;
} else if (ptype == String.class) {
mv.visitVarInsn(ALOAD, 1);
mv.visitLdcInsn(pname.substring(1));
mv.visitLdcInsn("");
mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getRequstURIPath", "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;", false);
mv.visitVarInsn(ASTORE, maxLocals);
varInsns.add(new int[]{ALOAD, maxLocals});
} else {
throw new RuntimeException(method + " only " + RestParam.class.getSimpleName() + "(#) to Type(primitive class or String)");
}
} else if ("&".equals(pname) && ptype == userType) { //当前用户对象的类名
mv.visitVarInsn(ALOAD, 1);
mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "currentUser", "()Ljava/lang/Object;", false);
mv.visitTypeInsn(CHECKCAST, Type.getInternalName(ptype));
mv.visitVarInsn(ASTORE, maxLocals);
varInsns.add(new int[]{ALOAD, maxLocals});
} else if (ptype == boolean.class) {
mv.visitVarInsn(ALOAD, 1);
mv.visitLdcInsn(pname);
mv.visitInsn(ICONST_0);
mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, ishead ? "getBooleanHeader" : "getBooleanParameter", "(Ljava/lang/String;Z)Z", false);
mv.visitVarInsn(ISTORE, maxLocals);
varInsns.add(new int[]{ILOAD, maxLocals});
} else if (ptype == byte.class) {
mv.visitVarInsn(ALOAD, 1);
mv.visitLdcInsn(pname);
mv.visitLdcInsn("0");
mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, ishead ? "getHeader" : "getParameter", "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;", false);
mv.visitIntInsn(BIPUSH, radix);
mv.visitMethodInsn(INVOKESTATIC, "java/lang/Byte", "parseByte", "(Ljava/lang/String;I)B", false);
mv.visitVarInsn(ISTORE, maxLocals);
varInsns.add(new int[]{ILOAD, maxLocals});
} else if (ptype == short.class) {
mv.visitVarInsn(ALOAD, 1);
mv.visitIntInsn(BIPUSH, radix);
mv.visitLdcInsn(pname);
mv.visitInsn(ICONST_0);
mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, ishead ? "getShortHeader" : "getShortParameter", "(ILjava/lang/String;S)S", false);
mv.visitVarInsn(ISTORE, maxLocals);
varInsns.add(new int[]{ILOAD, maxLocals});
} else if (ptype == char.class) {
mv.visitVarInsn(ALOAD, 1);
mv.visitLdcInsn(pname);
mv.visitLdcInsn("0");
mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, ishead ? "getHeader" : "getParameter", "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;", false);
mv.visitInsn(ICONST_0);
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/String", "charAt", "(I)C", false);
mv.visitVarInsn(ISTORE, maxLocals);
varInsns.add(new int[]{ILOAD, maxLocals});
} else if (ptype == int.class) {
mv.visitVarInsn(ALOAD, 1);
mv.visitIntInsn(BIPUSH, radix);
mv.visitLdcInsn(pname);
mv.visitInsn(ICONST_0);
mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, ishead ? "getIntHeader" : "getIntParameter", "(ILjava/lang/String;I)I", false);
mv.visitVarInsn(ISTORE, maxLocals);
varInsns.add(new int[]{ILOAD, maxLocals});
} else if (ptype == float.class) {
mv.visitVarInsn(ALOAD, 1);
mv.visitLdcInsn(pname);
mv.visitInsn(FCONST_0);
mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, ishead ? "getFloatHeader" : "getFloatParameter", "(Ljava/lang/String;F)F", false);
mv.visitVarInsn(FSTORE, maxLocals);
varInsns.add(new int[]{FLOAD, maxLocals});
} else if (ptype == long.class) {
mv.visitVarInsn(ALOAD, 1);
mv.visitIntInsn(BIPUSH, radix);
mv.visitLdcInsn(pname);
mv.visitInsn(LCONST_0);
mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, ishead ? "getLongHeader" : "getLongParameter", "(ILjava/lang/String;J)J", false);
mv.visitVarInsn(LSTORE, maxLocals);
varInsns.add(new int[]{LLOAD, maxLocals});
maxLocals++;
} else if (ptype == double.class) {
mv.visitVarInsn(ALOAD, 1);
mv.visitLdcInsn(pname);
mv.visitInsn(DCONST_0);
mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, ishead ? "getDoubleHeader" : "getDoubleParameter", "(Ljava/lang/String;D)D", false);
mv.visitVarInsn(DSTORE, maxLocals);
varInsns.add(new int[]{DLOAD, maxLocals});
maxLocals++;
} else if (ptype == String.class) {
mv.visitVarInsn(ALOAD, 1);
mv.visitLdcInsn(pname);
mv.visitLdcInsn("");
mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, iscookie ? "getCookie" : (ishead ? "getHeader" : "getParameter"), "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;", false);
mv.visitVarInsn(ASTORE, maxLocals);
varInsns.add(new int[]{ALOAD, maxLocals});
} else if (ptype == Flipper.class) {
mv.visitVarInsn(ALOAD, 1);
mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getFlipper", "()" + flipperDesc, false);
mv.visitVarInsn(ASTORE, maxLocals);
varInsns.add(new int[]{ALOAD, maxLocals});
} else { //其他Json对象
mv.visitVarInsn(ALOAD, 1);
if (param.getType() == param.getParameterizedType()) {
mv.visitLdcInsn(Type.getType(Type.getDescriptor(ptype)));
} else {
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, newDynName, REST_PARAMTYPES_FIELD_NAME, "[[Ljava/lang/reflect/Type;");
if (entry.methodidx <= 5) { //方法下标
mv.visitInsn(ICONST_0 + entry.methodidx);
} else {
mv.visitIntInsn(BIPUSH, entry.methodidx);
}
mv.visitInsn(AALOAD);
int paramidx = 0;
for (int i = 0; i < params.length; i++) {
if (params[i] == param) {
paramidx = i;
break;
}
}
if (paramidx <= 5) { //参数下标
mv.visitInsn(ICONST_0 + paramidx);
} else {
mv.visitIntInsn(BIPUSH, paramidx);
}
mv.visitInsn(AALOAD);
}
mv.visitLdcInsn(pname);
mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, ishead ? "getJsonHeader" : "getJsonParameter", "(Ljava/lang/reflect/Type;Ljava/lang/String;)Ljava/lang/Object;", false);
mv.visitTypeInsn(CHECKCAST, ptype.getName().replace('.', '/'));
mv.visitVarInsn(ASTORE, maxLocals);
varInsns.add(new int[]{ALOAD, maxLocals});
//构建 RestHeader、RestCookie、RestAddress 等赋值操作
Class loop = ptype;
Set<String> fields = new HashSet<>();
Map<String, Object[]> attrParaNames = new LinkedHashMap<>();
do {
if (loop == null || loop.isInterface()) break; //接口时getSuperclass可能会得到null
for (Field field : loop.getDeclaredFields()) {
if (Modifier.isStatic(field.getModifiers())) continue;
if (Modifier.isFinal(field.getModifiers())) continue;
if (fields.contains(field.getName())) continue;
RestHeader rh = field.getAnnotation(RestHeader.class);
RestCookie rc = field.getAnnotation(RestCookie.class);
RestSessionid rs = field.getAnnotation(RestSessionid.class);
RestAddress ra = field.getAnnotation(RestAddress.class);
RestBody rb = field.getAnnotation(RestBody.class);
RestUploadFile ru = field.getAnnotation(RestUploadFile.class);
RestURI ri = field.getAnnotation(RestURI.class);
if (rh == null && rc == null && ra == null && rb == null && rs == null && ru == null && ri == null) continue;
if (rh != null && field.getType() != String.class) throw new RuntimeException("@RestHeader must on String Field in " + field);
if (rc != null && field.getType() != String.class) throw new RuntimeException("@RestCookie must on String Field in " + field);
if (rs != null && field.getType() != String.class) throw new RuntimeException("@RestSessionid must on String Field in " + field);
if (ra != null && field.getType() != String.class) throw new RuntimeException("@RestAddress must on String Field in " + field);
if (rb != null && field.getType().isPrimitive()) throw new RuntimeException("@RestBody must on cannot on primitive type Field in " + field);
if (ru != null && field.getType() != byte[].class && field.getType() != File.class && field.getType() != File[].class) {
throw new RuntimeException("@RestUploadFile must on byte[] or File or File[] Field in " + field);
}
if (ri != null && field.getType() != String.class) throw new RuntimeException("@RestURI must on String Field in " + field);
org.redkale.util.Attribute attr = org.redkale.util.Attribute.create(loop, field);
String attrFieldName;
String restname = "";
if (rh != null) {
attrFieldName = "_redkale_attr_header_" + restAttributes.size();
restname = rh.name();
} else if (rc != null) {
attrFieldName = "_redkale_attr_cookie_" + restAttributes.size();
restname = rc.name();
} else if (rs != null) {
attrFieldName = "_redkale_attr_sessionid_" + restAttributes.size();
restname = rs.create() ? "1" : ""; //用于下面区分create值
} else if (ra != null) {
attrFieldName = "_redkale_attr_address_" + restAttributes.size();
//restname = "";
} else if (rb != null && field.getType() == String.class) {
attrFieldName = "_redkale_attr_bodystring_" + restAttributes.size();
//restname = "";
} else if (rb != null && field.getType() == byte[].class) {
attrFieldName = "_redkale_attr_bodybytes_" + restAttributes.size();
//restname = "";
} else if (rb != null && field.getType() != String.class && field.getType() != byte[].class) {
attrFieldName = "_redkale_attr_bodyjson_" + restAttributes.size();
//restname = "";
} else if (ru != null && field.getType() == byte[].class) {
attrFieldName = "_redkale_attr_uploadbytes_" + restAttributes.size();
//restname = "";
} else if (ru != null && field.getType() == File.class) {
attrFieldName = "_redkale_attr_uploadfile_" + restAttributes.size();
//restname = "";
} else if (ru != null && field.getType() == File[].class) {
attrFieldName = "_redkale_attr_uploadfiles_" + restAttributes.size();
//restname = "";
} else if (ri != null && field.getType() == String.class) {
attrFieldName = "_redkale_attr_uri_" + restAttributes.size();
//restname = "";
} else {
continue;
}
restAttributes.put(attrFieldName, attr);
attrParaNames.put(attrFieldName, new Object[]{restname, field.getType(), field.getGenericType(), ru});
fields.add(field.getName());
}
} while ((loop = loop.getSuperclass()) != Object.class);
if (!attrParaNames.isEmpty()) { //参数存在 RestHeader、RestCookie、RestSessionid、RestAddress、RestBody字段
mv.visitVarInsn(ALOAD, maxLocals);
Label lif = new Label();
mv.visitJumpInsn(IFNULL, lif); //if(bean != null) {
for (Map.Entry<String, Object[]> en : attrParaNames.entrySet()) {
RestUploadFile ru = (RestUploadFile) en.getValue()[3];
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, newDynName, en.getKey(), attrDesc);
mv.visitVarInsn(ALOAD, maxLocals);
boolean upload = en.getKey().contains("_upload");
mv.visitVarInsn(ALOAD, upload ? (maxLocals - 1) : 1);
if (en.getKey().contains("_header_")) {
mv.visitLdcInsn(en.getValue()[0].toString());
mv.visitLdcInsn("");
mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getHeader", "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;", false);
} else if (en.getKey().contains("_cookie_")) {
mv.visitLdcInsn(en.getValue()[0].toString());
mv.visitLdcInsn("");
mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getCookie", "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;", false);
} else if (en.getKey().contains("_sessionid_")) {
mv.visitInsn(en.getValue()[0].toString().isEmpty() ? ICONST_0 : ICONST_1);
mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getSessionid", "(Z)Ljava/lang/String;", false);
} else if (en.getKey().contains("_address_")) {
mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getRemoteAddr", "()Ljava/lang/String;", false);
} else if (en.getKey().contains("_uri_")) {
mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getRequestURI", "()Ljava/lang/String;", false);
} else if (en.getKey().contains("_bodystring_")) {
mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getBodyUTF8", "()Ljava/lang/String;", false);
} else if (en.getKey().contains("_bodybytes_")) {
mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getBody", "()[B", false);
} else if (en.getKey().contains("_bodyjson_")) {//JavaBean 转 Json
String typefieldname = "_redkale_body_jsontype_" + bodyTypes.size();
bodyTypes.put(typefieldname, (java.lang.reflect.Type) en.getValue()[2]);
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, newDynName, typefieldname, "Ljava/lang/reflect/Type;");
mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getBodyJson", "(Ljava/lang/reflect/Type;)Ljava/lang/Object;", false);
mv.visitTypeInsn(CHECKCAST, Type.getInternalName((Class) en.getValue()[1]));
} else if (en.getKey().contains("_uploadbytes_")) {
//只需mv.visitVarInsn(ALOAD, 4), 无需处理
} else if (en.getKey().contains("_uploadfile_")) {
//只需mv.visitVarInsn(ALOAD, 4), 无需处理
} else if (en.getKey().contains("_uploadfiles_")) {
//只需mv.visitVarInsn(ALOAD, 4), 无需处理
}
mv.visitMethodInsn(INVOKEINTERFACE, attrInternalName, "set", "(Ljava/lang/Object;Ljava/lang/Object;)V", true);
}
mv.visitLabel(lif); // end if }
mv.visitFrame(Opcodes.F_APPEND, 1, new Object[]{ptype.getName().replace('.', '/')}, 0, null);
}
}
maxLocals++;
paramMaps.add(paramMap);
} // end params for each
//mv.visitVarInsn(ALOAD, 0); //调用this
//mv.visitFieldInsn(GETFIELD, newDynName, REST_SERVICE_FIELD_NAME, serviceDesc);
mv.visitVarInsn(ALOAD, 3);
for (int[] ins : varInsns) {
mv.visitVarInsn(ins[0], ins[1]);
}
mv.visitMethodInsn(INVOKEVIRTUAL, serviceTypeInternalName, method.getName(), methodDesc, false);
if (hasAsyncHandler) {
mv.visitInsn(RETURN);
} else if (returnType == void.class) {
mv.visitVarInsn(ALOAD, 2);
mv.visitMethodInsn(INVOKESTATIC, retInternalName, "success", "()" + retDesc, false);
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finishJson", "(" + retDesc + ")V", false);
mv.visitInsn(RETURN);
} else if (returnType == boolean.class) {
mv.visitVarInsn(ISTORE, maxLocals);
mv.visitVarInsn(ALOAD, 2); //response
mv.visitVarInsn(ILOAD, maxLocals);
mv.visitMethodInsn(INVOKESTATIC, "java/lang/String", "valueOf", "(Z)Ljava/lang/String;", false);
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finish", "(Ljava/lang/String;)V", false);
mv.visitInsn(RETURN);
maxLocals++;
} else if (returnType == byte.class) {
mv.visitVarInsn(ISTORE, maxLocals);
mv.visitVarInsn(ALOAD, 2); //response
mv.visitVarInsn(ILOAD, maxLocals);
mv.visitMethodInsn(INVOKESTATIC, "java/lang/String", "valueOf", "(I)Ljava/lang/String;", false);
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finish", "(Ljava/lang/String;)V", false);
mv.visitInsn(RETURN);
maxLocals++;
} else if (returnType == short.class) {
mv.visitVarInsn(ISTORE, maxLocals);
mv.visitVarInsn(ALOAD, 2); //response
mv.visitVarInsn(ILOAD, maxLocals);
mv.visitMethodInsn(INVOKESTATIC, "java/lang/String", "valueOf", "(I)Ljava/lang/String;", false);
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finish", "(Ljava/lang/String;)V", false);
mv.visitInsn(RETURN);
maxLocals++;
} else if (returnType == char.class) {
mv.visitVarInsn(ISTORE, maxLocals);
mv.visitVarInsn(ALOAD, 2); //response
mv.visitVarInsn(ILOAD, maxLocals);
mv.visitMethodInsn(INVOKESTATIC, "java/lang/String", "valueOf", "(C)Ljava/lang/String;", false);
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finish", "(Ljava/lang/String;)V", false);
mv.visitInsn(RETURN);
maxLocals++;
} else if (returnType == int.class) {
mv.visitVarInsn(ISTORE, maxLocals);
mv.visitVarInsn(ALOAD, 2); //response
mv.visitVarInsn(ILOAD, maxLocals);
mv.visitMethodInsn(INVOKESTATIC, "java/lang/String", "valueOf", "(I)Ljava/lang/String;", false);
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finish", "(Ljava/lang/String;)V", false);
mv.visitInsn(RETURN);
maxLocals++;
} else if (returnType == float.class) {
mv.visitVarInsn(FSTORE, maxLocals);
mv.visitVarInsn(ALOAD, 2); //response
mv.visitVarInsn(FLOAD, maxLocals);
mv.visitMethodInsn(INVOKESTATIC, "java/lang/String", "valueOf", "(F)Ljava/lang/String;", false);
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finish", "(Ljava/lang/String;)V", false);
mv.visitInsn(RETURN);
maxLocals++;
} else if (returnType == long.class) {
mv.visitVarInsn(LSTORE, maxLocals);
mv.visitVarInsn(ALOAD, 2); //response
mv.visitVarInsn(LLOAD, maxLocals);
mv.visitMethodInsn(INVOKESTATIC, "java/lang/String", "valueOf", "(J)Ljava/lang/String;", false);
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finish", "(Ljava/lang/String;)V", false);
mv.visitInsn(RETURN);
maxLocals += 2;
} else if (returnType == double.class) {
mv.visitVarInsn(DSTORE, maxLocals);
mv.visitVarInsn(ALOAD, 2); //response
mv.visitVarInsn(DLOAD, maxLocals);
mv.visitMethodInsn(INVOKESTATIC, "java/lang/String", "valueOf", "(D)Ljava/lang/String;", false);
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finish", "(Ljava/lang/String;)V", false);
mv.visitInsn(RETURN);
maxLocals += 2;
} else if (returnType == String.class) {
mv.visitVarInsn(ASTORE, maxLocals);
mv.visitVarInsn(ALOAD, 2); //response
mv.visitVarInsn(ALOAD, maxLocals);
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finish", "(Ljava/lang/String;)V", false);
mv.visitInsn(RETURN);
maxLocals++;
} else if (returnType == File.class) {
mv.visitVarInsn(ASTORE, maxLocals);
mv.visitVarInsn(ALOAD, 2); //response
mv.visitVarInsn(ALOAD, maxLocals);
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finish", "(Ljava/io/File;)V", false);
mv.visitInsn(RETURN);
maxLocals++;
} else if (RetResult.class.isAssignableFrom(returnType)) {
mv.visitVarInsn(ASTORE, maxLocals);
mv.visitVarInsn(ALOAD, 2); //response
if (rcs != null && rcs.length > 0) {
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, newDynName, REST_JSONCONVERT_FIELD_PREFIX + restConverts.size(), convertDesc);
mv.visitVarInsn(ALOAD, maxLocals);
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finishJson", "(" + convertDesc + retDesc + ")V", false);
} else {
mv.visitVarInsn(ALOAD, maxLocals);
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finishJson", "(" + retDesc + ")V", false);
}
mv.visitInsn(RETURN);
maxLocals++;
} else if (HttpResult.class.isAssignableFrom(returnType)) {
mv.visitVarInsn(ASTORE, maxLocals);
mv.visitVarInsn(ALOAD, 2); //response
if (rcs != null && rcs.length > 0) {
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, newDynName, REST_JSONCONVERT_FIELD_PREFIX + restConverts.size(), convertDesc);
mv.visitVarInsn(ALOAD, maxLocals);
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finishJson", "(" + convertDesc + httprsDesc + ")V", false);
} else {
mv.visitVarInsn(ALOAD, maxLocals);
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finishJson", "(" + httprsDesc + ")V", false);
}
mv.visitInsn(RETURN);
maxLocals++;
} else if (Number.class.isAssignableFrom(returnType) || CharSequence.class.isAssignableFrom(returnType)) { //returnType == String.class 必须放在前面
mv.visitVarInsn(ASTORE, maxLocals);
mv.visitVarInsn(ALOAD, 2); //response
mv.visitVarInsn(ALOAD, maxLocals);
mv.visitMethodInsn(INVOKESTATIC, "java/lang/String", "valueOf", "(Ljava/lang/Object;)Ljava/lang/String;", false);
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finish", "(Ljava/lang/String;)V", false);
mv.visitInsn(RETURN);
maxLocals++;
} else if (CompletableFuture.class.isAssignableFrom(returnType)) {
mv.visitVarInsn(ASTORE, maxLocals);
mv.visitVarInsn(ALOAD, 2);//response
if (rcs != null && rcs.length > 0) {
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, newDynName, REST_JSONCONVERT_FIELD_PREFIX + restConverts.size(), convertDesc);
mv.visitVarInsn(ALOAD, maxLocals);
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finishJson", "(" + convertDesc + futureDesc + ")V", false);
} else {
mv.visitVarInsn(ALOAD, maxLocals);
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finishJson", "(" + futureDesc + ")V", false);
}
mv.visitInsn(RETURN);
maxLocals++;
} else {
mv.visitVarInsn(ASTORE, maxLocals);
mv.visitVarInsn(ALOAD, 2); //response
if (rcs != null && rcs.length > 0) {
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, newDynName, REST_JSONCONVERT_FIELD_PREFIX + restConverts.size(), convertDesc);
mv.visitVarInsn(ALOAD, maxLocals);
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finishJson", "(" + convertDesc + "Ljava/lang/Object;)V", false);
} else {
mv.visitVarInsn(ALOAD, maxLocals);
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finishJson", "(Ljava/lang/Object;)V", false);
}
mv.visitInsn(RETURN);
maxLocals++;
}
mv.visitMaxs(maxStack, maxLocals);
mappingMap.put("params", paramMaps);
mappingMaps.add(mappingMap);
} // end for each
for (Map.Entry<String, java.lang.reflect.Type> en : bodyTypes.entrySet()) {
fv = cw.visitField(ACC_PRIVATE, en.getKey(), "Ljava/lang/reflect/Type;", null, null);
fv.visitEnd();
}
for (String attrname : restAttributes.keySet()) {
fv = cw.visitField(ACC_PRIVATE, attrname, attrDesc, null, null);
fv.visitEnd();
}
for (int i = 1; i <= restConverts.size(); i++) {
fv = cw.visitField(ACC_PRIVATE, REST_JSONCONVERT_FIELD_PREFIX + i, convertDesc, null, null);
fv.visitEnd();
}
//classMap.put("mappings", mappingMaps); //不显示太多信息
{ //toString函数
mv = new AsmMethodVisitor(cw.visitMethod(ACC_PUBLIC, "toString", "()Ljava/lang/String;", null, null));
//mv.setDebug(true);
mv.visitLdcInsn(JsonConvert.root().convertTo(classMap));
mv.visitInsn(ARETURN);
mv.visitMaxs(1, 1);
mv.visitEnd();
}
cw.visitEnd();
Class<?> newClazz = new RestClassLoader(loader).loadClass(newDynName.replace('/', '.'), cw.toByteArray());
try {
T obj = ((Class<T>) newClazz).newInstance();
for (Map.Entry<String, org.redkale.util.Attribute> en : restAttributes.entrySet()) {
Field attrField = newClazz.getDeclaredField(en.getKey());
attrField.setAccessible(true);
attrField.set(obj, en.getValue());
}
for (Map.Entry<String, java.lang.reflect.Type> en : bodyTypes.entrySet()) {
Field genField = newClazz.getDeclaredField(en.getKey());
genField.setAccessible(true);
genField.set(obj, en.getValue());
}
for (int i = 0; i < restConverts.size(); i++) {
Field genField = newClazz.getDeclaredField(REST_JSONCONVERT_FIELD_PREFIX + (i + 1));
genField.setAccessible(true);
genField.set(obj, createJsonConvert(restConverts.get(i)));
}
Field typesfield = newClazz.getDeclaredField(REST_PARAMTYPES_FIELD_NAME);
typesfield.setAccessible(true);
java.lang.reflect.Type[][] paramtypeArray = new java.lang.reflect.Type[paramtypes.size()][];
paramtypeArray = paramtypes.toArray(paramtypeArray);
typesfield.set(obj, paramtypeArray);
return obj;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private static boolean checkName(String name) { //不能含特殊字符
if (name.isEmpty()) return true;
if (name.charAt(0) >= '0' && name.charAt(0) <= '9') return false;
for (char ch : name.toCharArray()) {
if (!((ch >= '0' && ch <= '9') || ch == '_' || (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z'))) { //不能含特殊字符
return false;
}
}
return true;
}
private static class RestClassLoader extends ClassLoader {
public RestClassLoader(ClassLoader parent) {
super(parent);
}
public final Class<?> loadClass(String name, byte[] b) {
return defineClass(name, b, 0, b.length);
}
}
private static class MappingEntry {
private static final RestMapping DEFAULT__MAPPING;
static {
try {
DEFAULT__MAPPING = MappingEntry.class.getDeclaredMethod("mapping").getAnnotation(RestMapping.class);
} catch (Exception e) {
throw new Error(e);
}
}
public MappingEntry(int methodidx, RestMapping mapping, final String defmodulename, Method method) {
if (mapping == null) mapping = DEFAULT__MAPPING;
this.methodidx = methodidx;
this.ignore = mapping.ignore();
String n = mapping.name();
if (n.isEmpty()) {
String t = method.getName();
int pos = t.indexOf(defmodulename);
n = pos > 0 ? t.substring(0, pos) : t;
}
this.name = n;
this.mappingMethod = method;
this.methods = mapping.methods();
this.auth = mapping.auth();
this.actionid = mapping.actionid();
this.cacheseconds = mapping.cacheseconds();
this.comment = mapping.comment();
}
public final int methodidx; // _paramtypes 的下标从0开始
public final Method mappingMethod;
public final boolean ignore;
public final String name;
public final String comment;
public final String[] methods;
public final boolean auth;
public final int actionid;
public final int cacheseconds;
@RestMapping()
void mapping() { //用于获取Mapping 默认值
}
@Override
public int hashCode() {
return this.name.hashCode();
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null || getClass() != obj.getClass()) return false;
return this.name.equals(((MappingEntry) obj).name);
}
}
}