重构HttpServlet
This commit is contained in:
@@ -11,7 +11,7 @@ import java.util.*;
|
||||
import javax.persistence.*;
|
||||
import org.redkale.convert.json.JsonConvert;
|
||||
import org.redkale.net.http.*;
|
||||
import org.redkale.service.Service;
|
||||
import org.redkale.service.*;
|
||||
import org.redkale.source.*;
|
||||
import org.redkale.util.*;
|
||||
|
||||
@@ -24,7 +24,9 @@ import org.redkale.util.*;
|
||||
*
|
||||
* @author zhangjx
|
||||
*/
|
||||
public class ApiDocsService extends DefaultRestServlet implements Service {
|
||||
@AutoLoad(false)
|
||||
@LocalService
|
||||
public final class ApiDocsService extends AbstractService {
|
||||
|
||||
private final Application app; //Application全局对象
|
||||
|
||||
@@ -34,7 +36,8 @@ public class ApiDocsService extends DefaultRestServlet implements Service {
|
||||
|
||||
public void run() throws Exception {
|
||||
List<Map> serverList = new ArrayList<>();
|
||||
|
||||
Field __prefix = HttpServlet.class.getDeclaredField("__prefix");
|
||||
__prefix.setAccessible(true);
|
||||
Map<String, Map<String, Map<String, Object>>> typesmap = new LinkedHashMap<>();
|
||||
for (NodeServer node : app.servers) {
|
||||
if (!(node instanceof NodeHttpServer)) continue;
|
||||
@@ -52,7 +55,7 @@ public class ApiDocsService extends DefaultRestServlet implements Service {
|
||||
continue;
|
||||
}
|
||||
final Map<String, Object> servletmap = new LinkedHashMap<>();
|
||||
String prefix = _prefix(servlet);
|
||||
String prefix = (String) __prefix.get(servlet);
|
||||
String[] urlregs = ws.value();
|
||||
if (prefix != null && !prefix.isEmpty()) {
|
||||
for (int i = 0; i < urlregs.length; i++) {
|
||||
@@ -73,7 +76,7 @@ public class ApiDocsService extends DefaultRestServlet implements Service {
|
||||
if (Modifier.isAbstract(clz.getModifiers())) break;
|
||||
for (Method method : clz.getMethods()) {
|
||||
if (method.getParameterCount() != 2) continue;
|
||||
WebMapping action = method.getAnnotation(WebMapping.class);
|
||||
HttpMapping action = method.getAnnotation(HttpMapping.class);
|
||||
if (action == null) continue;
|
||||
if (!action.inherited() && selfClz != clz) continue; //忽略不被继承的方法
|
||||
final Map<String, Object> mappingmap = new LinkedHashMap<>();
|
||||
@@ -123,7 +126,7 @@ public class ApiDocsService extends DefaultRestServlet implements Service {
|
||||
typesmap.put(rtype.getName(), typemap);
|
||||
}
|
||||
mappingmap.put("results", results);
|
||||
for (WebParam param : method.getAnnotationsByType(WebParam.class)) {
|
||||
for (HttpParam param : method.getAnnotationsByType(HttpParam.class)) {
|
||||
final Map<String, Object> parammap = new LinkedHashMap<>();
|
||||
final boolean isarray = param.type().isArray();
|
||||
final Class ptype = isarray ? param.type().getComponentType() : param.type();
|
||||
|
||||
@@ -158,10 +158,22 @@ public abstract class Response<C extends Context, R extends Request<C>> {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用 public void recycleListener(BiConsumer recycleListener) 代替
|
||||
*
|
||||
* @param recycleListener BiConsumer
|
||||
*
|
||||
* @deprecated
|
||||
*/
|
||||
@Deprecated
|
||||
public void setRecycleListener(BiConsumer<R, Response<C, R>> recycleListener) {
|
||||
this.recycleListener = recycleListener;
|
||||
}
|
||||
|
||||
public void recycleListener(BiConsumer<R, Response<C, R>> recycleListener) {
|
||||
this.recycleListener = recycleListener;
|
||||
}
|
||||
|
||||
public Object getOutput() {
|
||||
return output;
|
||||
}
|
||||
|
||||
@@ -21,9 +21,4 @@ public class DefaultRestServlet extends RestHttpServlet<Object> {
|
||||
return new Object();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void authenticate(int moduleid, int actionid, HttpRequest request, HttpResponse response, HttpServlet next) throws IOException {
|
||||
next.execute(request, response);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -22,31 +22,31 @@ import static jdk.internal.org.objectweb.asm.Opcodes.*;
|
||||
import org.redkale.service.RetResult;
|
||||
|
||||
/**
|
||||
* 直接只用HttpServlet代替, 将在1.9版本移除
|
||||
*
|
||||
* <p>
|
||||
* 详情见: https://redkale.org
|
||||
*
|
||||
* @deprecated
|
||||
* @author zhangjx
|
||||
*/
|
||||
@Deprecated
|
||||
public abstract class HttpBaseServlet extends HttpServlet {
|
||||
|
||||
public static final int RET_SERVER_ERROR = 1800_0001;
|
||||
|
||||
public static final int RET_METHOD_ERROR = 1800_0002;
|
||||
|
||||
/**
|
||||
* 配合 HttpBaseServlet 使用。
|
||||
* 当标记为 @AuthIgnore 的方法在执行execute之前不会调用authenticate 方法。
|
||||
* 使用 org.redkale.util.AuthIgnore 代替
|
||||
*
|
||||
* <p>
|
||||
* 详情见: https://redkale.org
|
||||
*
|
||||
* @deprecated
|
||||
* @author zhangjx
|
||||
*/
|
||||
@Inherited
|
||||
@Documented
|
||||
@Target({METHOD, TYPE})
|
||||
@Retention(RUNTIME)
|
||||
@Deprecated
|
||||
protected @interface AuthIgnore {
|
||||
|
||||
}
|
||||
@@ -66,18 +66,20 @@ public abstract class HttpBaseServlet extends HttpServlet {
|
||||
}
|
||||
|
||||
/**
|
||||
* 配合 @WebMapping 使用。
|
||||
* 用于对@WebMapping方法中参数描述
|
||||
*
|
||||
* 使用 org.redkale.net.http.HttpParam 代替
|
||||
*
|
||||
* <p>
|
||||
* 详情见: https://redkale.org
|
||||
*
|
||||
* @deprecated
|
||||
* @author zhangjx
|
||||
*/
|
||||
@Documented
|
||||
@Target({METHOD})
|
||||
@Retention(RUNTIME)
|
||||
@Repeatable(WebParams.class)
|
||||
@Deprecated
|
||||
protected @interface WebParam {
|
||||
|
||||
String name(); //参数名
|
||||
@@ -102,16 +104,17 @@ public abstract class HttpBaseServlet extends HttpServlet {
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用 WebMapping 替代。
|
||||
* 使用 org.redkale.net.http.HttpMapping 替代。
|
||||
* <p>
|
||||
* 详情见: https://redkale.org
|
||||
*
|
||||
* @deprecated
|
||||
* @author zhangjx
|
||||
*/
|
||||
@Deprecated
|
||||
@Documented
|
||||
@Target({METHOD})
|
||||
@Retention(RUNTIME)
|
||||
@Deprecated
|
||||
protected @interface WebAction {
|
||||
|
||||
int actionid() default 0;
|
||||
@@ -130,17 +133,18 @@ public abstract class HttpBaseServlet extends HttpServlet {
|
||||
}
|
||||
|
||||
/**
|
||||
* 配合 HttpBaseServlet 使用。
|
||||
* 用于对@WebServlet对应的url进行细分。 其url必须是包含WebServlet中定义的前缀, 且不能是正则表达式
|
||||
* 使用 org.redkale.net.http.HttpMapping 替代。
|
||||
*
|
||||
* <p>
|
||||
* 详情见: https://redkale.org
|
||||
*
|
||||
* @deprecated
|
||||
* @author zhangjx
|
||||
*/
|
||||
@Documented
|
||||
@Target({METHOD})
|
||||
@Retention(RUNTIME)
|
||||
@Deprecated
|
||||
protected @interface WebMapping {
|
||||
|
||||
int actionid() default 0;
|
||||
@@ -159,18 +163,15 @@ public abstract class HttpBaseServlet extends HttpServlet {
|
||||
}
|
||||
|
||||
/**
|
||||
* 配合 HttpBaseServlet 使用。
|
||||
* 当标记为 @HttpCacheable 的方法使用response.finish的参数将被缓存一段时间(默认值 seconds=15秒)。
|
||||
* 通常情况下 @HttpCacheable 需要与 @AuthIgnore 一起使用,没有标记@AuthIgnore的方法一般输出的结果与当前用户信息有关。
|
||||
*
|
||||
* <p>
|
||||
* 详情见: https://redkale.org
|
||||
* 使用 org.redkale.net.http.HttpCacheable 替代。
|
||||
*
|
||||
* @deprecated
|
||||
* @author zhangjx
|
||||
*/
|
||||
@Documented
|
||||
@Target({METHOD})
|
||||
@Retention(RUNTIME)
|
||||
@Deprecated
|
||||
protected @interface HttpCacheable {
|
||||
|
||||
/**
|
||||
@@ -215,8 +216,7 @@ public abstract class HttpBaseServlet extends HttpServlet {
|
||||
if (entry.ignore) {
|
||||
authSuccessServlet.execute(request, response);
|
||||
} else {
|
||||
response.nextEvent(authSuccessServlet);
|
||||
authenticate(entry.moduleid, entry.actionid, request, response, authSuccessServlet);
|
||||
HttpBaseServlet.this.authenticate(entry.moduleid, entry.actionid, request, response, authSuccessServlet);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -225,43 +225,29 @@ public abstract class HttpBaseServlet extends HttpServlet {
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 使用 public void preExecute(HttpRequest request, HttpResponse response) throws IOException 代替
|
||||
*
|
||||
* @param request HttpRequest
|
||||
* @param response HttpResponse
|
||||
* @param next HttpServlet
|
||||
*
|
||||
* @deprecated
|
||||
*
|
||||
* @throws IOException IOException
|
||||
*/
|
||||
@Deprecated
|
||||
public void preExecute(HttpRequest request, HttpResponse response, final HttpServlet next) throws IOException {
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 预执行方法,在execute方法之前运行,通常用于常规统计或基础检测,例如 : <br>
|
||||
* <blockquote><pre>
|
||||
* @Override
|
||||
* public void preExecute(final HttpRequest request, final HttpResponse response) throws IOException {
|
||||
* public void preExecute(final HttpRequest request, final HttpResponse response, HttpServlet next) throws IOException {
|
||||
* if (finer) response.setRecycleListener((req, resp) -> { //记录处理时间比较长的请求
|
||||
* long e = System.currentTimeMillis() - ((HttpRequest) req).getCreatetime();
|
||||
* if (e > 200) logger.finer("http-execute-cost-time: " + e + " ms. request = " + req);
|
||||
* });
|
||||
* response.nextEvent();
|
||||
* next.execute(request, response);
|
||||
* }
|
||||
* </pre></blockquote>
|
||||
* <p>
|
||||
*
|
||||
* @param request HttpRequest
|
||||
* @param response HttpResponse
|
||||
* @param next HttpServlet
|
||||
*
|
||||
* @throws IOException IOException
|
||||
*/
|
||||
public void preExecute(HttpRequest request, HttpResponse response) throws IOException {
|
||||
response.nextEvent();
|
||||
public void preExecute(HttpRequest request, HttpResponse response, final HttpServlet next) throws IOException {
|
||||
next.execute(request, response);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -313,10 +299,7 @@ public abstract class HttpBaseServlet extends HttpServlet {
|
||||
|
||||
@Override
|
||||
public final void execute(HttpRequest request, HttpResponse response) throws IOException {
|
||||
response.nextEvent(preSuccessServlet);
|
||||
preExecute(request, response);
|
||||
//兼容以前
|
||||
//preExecute(request, response, preSuccessServlet);
|
||||
preExecute(request, response, preSuccessServlet);
|
||||
}
|
||||
|
||||
public final void preInit(HttpContext context, AnyValue config) {
|
||||
|
||||
33
src/org/redkale/net/http/HttpCacheable.java
Normal file
33
src/org/redkale/net/http/HttpCacheable.java
Normal file
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* 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.lang.annotation.*;
|
||||
import static java.lang.annotation.ElementType.METHOD;
|
||||
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||
|
||||
/**
|
||||
* 配合 HttpServlet 使用。
|
||||
* 当标记为 @HttpCacheable 的方法使用response.finish的参数将被缓存一段时间(默认值 seconds=15秒)。
|
||||
* 通常情况下 @HttpCacheable 需要与 @AuthIgnore 一起使用,没有标记@AuthIgnore的方法一般输出的结果与当前用户信息有关。
|
||||
*
|
||||
* <p>
|
||||
* 详情见: https://redkale.org
|
||||
*
|
||||
* @author zhangjx
|
||||
*/
|
||||
@Documented
|
||||
@Target({METHOD})
|
||||
@Retention(RUNTIME)
|
||||
public @interface HttpCacheable {
|
||||
|
||||
/**
|
||||
* 超时的秒数
|
||||
*
|
||||
* @return 超时秒数
|
||||
*/
|
||||
int seconds() default 15;
|
||||
}
|
||||
@@ -11,6 +11,8 @@ import java.nio.charset.*;
|
||||
import java.security.*;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.logging.*;
|
||||
import jdk.internal.org.objectweb.asm.*;
|
||||
import static jdk.internal.org.objectweb.asm.Opcodes.*;
|
||||
import org.redkale.net.*;
|
||||
import org.redkale.util.*;
|
||||
import org.redkale.watch.*;
|
||||
@@ -27,6 +29,8 @@ public class HttpContext extends Context {
|
||||
|
||||
protected final SecureRandom random = new SecureRandom();
|
||||
|
||||
protected final ConcurrentHashMap<Class, Creator> asyncHandlerCreators = new ConcurrentHashMap<>();
|
||||
|
||||
public HttpContext(long serverStartTime, Logger logger, ExecutorService executor, int bufferCapacity, ObjectPool<ByteBuffer> bufferPool,
|
||||
ObjectPool<Response> responsePool, int maxbody, Charset charset, InetSocketAddress address, PrepareServlet prepare,
|
||||
WatchFactory watch, int readTimeoutSecond, int writeTimeoutSecond) {
|
||||
@@ -54,4 +58,113 @@ public class HttpContext extends Context {
|
||||
return responsePool;
|
||||
}
|
||||
|
||||
protected <H extends AsyncHandler> Creator<H> loadAsyncHandlerCreator(Class<H> handlerClass) {
|
||||
Creator<H> creator = asyncHandlerCreators.get(handlerClass);
|
||||
if (creator == null) {
|
||||
creator = createAsyncHandlerCreator(handlerClass);
|
||||
asyncHandlerCreators.put(handlerClass, creator);
|
||||
}
|
||||
return creator;
|
||||
}
|
||||
|
||||
private <H extends AsyncHandler> Creator<H> createAsyncHandlerCreator(Class<H> handlerClass) {
|
||||
//生成规则与SncpAsyncHandler.Factory 很类似
|
||||
//-------------------------------------------------------------
|
||||
final boolean handlerinterface = handlerClass.isInterface();
|
||||
final String handlerClassName = handlerClass.getName().replace('.', '/');
|
||||
final String handlerName = AsyncHandler.class.getName().replace('.', '/');
|
||||
final String handlerDesc = Type.getDescriptor(AsyncHandler.class);
|
||||
final String newDynName = handlerClass.getName().replace('.', '/') + "_Dync" + AsyncHandler.class.getSimpleName() + "_" + (System.currentTimeMillis() % 10000);
|
||||
|
||||
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
|
||||
FieldVisitor fv;
|
||||
AsmMethodVisitor mv;
|
||||
AnnotationVisitor av0;
|
||||
cw.visit(V1_8, ACC_PUBLIC + ACC_SUPER, newDynName, null, handlerinterface ? "java/lang/Object" : handlerClassName, handlerinterface ? new String[]{handlerClassName} : new String[]{handlerName});
|
||||
|
||||
{ //handler 属性
|
||||
fv = cw.visitField(ACC_PRIVATE, "handler", handlerDesc, null, null);
|
||||
fv.visitEnd();
|
||||
}
|
||||
{//构造方法
|
||||
mv = new AsmMethodVisitor(cw.visitMethod(ACC_PUBLIC, "<init>", "(" + handlerDesc + ")V", null, null));
|
||||
//mv.setDebug(true);
|
||||
{
|
||||
av0 = mv.visitAnnotation("Ljava/beans/ConstructorProperties;", true);
|
||||
{
|
||||
AnnotationVisitor av1 = av0.visitArray("value");
|
||||
av1.visit(null, "handler");
|
||||
av1.visitEnd();
|
||||
}
|
||||
av0.visitEnd();
|
||||
}
|
||||
mv.visitVarInsn(ALOAD, 0);
|
||||
mv.visitMethodInsn(INVOKESPECIAL, handlerinterface ? "java/lang/Object" : handlerClassName, "<init>", "()V", false);
|
||||
mv.visitVarInsn(ALOAD, 0);
|
||||
mv.visitVarInsn(ALOAD, 1);
|
||||
mv.visitFieldInsn(PUTFIELD, newDynName, "handler", handlerDesc);
|
||||
mv.visitInsn(RETURN);
|
||||
mv.visitMaxs(2, 2);
|
||||
mv.visitEnd();
|
||||
}
|
||||
|
||||
for (java.lang.reflect.Method method : handlerClass.getMethods()) { //
|
||||
if ("completed".equals(method.getName()) && method.getParameterCount() == 2) {
|
||||
mv = new AsmMethodVisitor(cw.visitMethod(ACC_PUBLIC, "completed", Type.getMethodDescriptor(method), null, null));
|
||||
mv.visitVarInsn(ALOAD, 0);
|
||||
mv.visitFieldInsn(GETFIELD, newDynName, "handler", handlerDesc);
|
||||
mv.visitVarInsn(ALOAD, 1);
|
||||
mv.visitVarInsn(ALOAD, 2);
|
||||
mv.visitMethodInsn(INVOKEINTERFACE, handlerName, "completed", "(Ljava/lang/Object;Ljava/lang/Object;)V", true);
|
||||
mv.visitInsn(RETURN);
|
||||
mv.visitMaxs(3, 3);
|
||||
mv.visitEnd();
|
||||
} else if ("failed".equals(method.getName()) && method.getParameterCount() == 2) {
|
||||
mv = new AsmMethodVisitor(cw.visitMethod(ACC_PUBLIC, "failed", Type.getMethodDescriptor(method), null, null));
|
||||
mv.visitVarInsn(ALOAD, 0);
|
||||
mv.visitFieldInsn(GETFIELD, newDynName, "handler", handlerDesc);
|
||||
mv.visitVarInsn(ALOAD, 1);
|
||||
mv.visitVarInsn(ALOAD, 2);
|
||||
mv.visitMethodInsn(INVOKEINTERFACE, handlerName, "failed", "(Ljava/lang/Throwable;Ljava/lang/Object;)V", true);
|
||||
mv.visitInsn(RETURN);
|
||||
mv.visitMaxs(3, 3);
|
||||
mv.visitEnd();
|
||||
} else if (handlerinterface || java.lang.reflect.Modifier.isAbstract(method.getModifiers())) {
|
||||
mv = new AsmMethodVisitor(cw.visitMethod(ACC_PUBLIC, method.getName(), Type.getMethodDescriptor(method), null, null));
|
||||
Class returnType = method.getReturnType();
|
||||
if (returnType == void.class) {
|
||||
mv.visitInsn(RETURN);
|
||||
mv.visitMaxs(0, 1);
|
||||
} else if (returnType.isPrimitive()) {
|
||||
mv.visitInsn(ICONST_0);
|
||||
if (returnType == long.class) {
|
||||
mv.visitInsn(LRETURN);
|
||||
mv.visitMaxs(2, 1);
|
||||
} else if (returnType == float.class) {
|
||||
mv.visitInsn(FRETURN);
|
||||
mv.visitMaxs(2, 1);
|
||||
} else if (returnType == double.class) {
|
||||
mv.visitInsn(DRETURN);
|
||||
mv.visitMaxs(2, 1);
|
||||
} else {
|
||||
mv.visitInsn(IRETURN);
|
||||
mv.visitMaxs(1, 1);
|
||||
}
|
||||
} else {
|
||||
mv.visitInsn(ACONST_NULL);
|
||||
mv.visitInsn(ARETURN);
|
||||
mv.visitMaxs(1, 1);
|
||||
}
|
||||
mv.visitEnd();
|
||||
}
|
||||
}
|
||||
cw.visitEnd();
|
||||
byte[] bytes = cw.toByteArray();
|
||||
Class<AsyncHandler> newHandlerClazz = (Class<AsyncHandler>) new ClassLoader(handlerClass.getClassLoader()) {
|
||||
public final Class<?> loadClass(String name, byte[] b) {
|
||||
return defineClass(name, b, 0, b.length);
|
||||
}
|
||||
}.loadClass(newDynName.replace('/', '.'), bytes);
|
||||
return (Creator<H>) Creator.create(newHandlerClazz);
|
||||
}
|
||||
}
|
||||
|
||||
39
src/org/redkale/net/http/HttpMapping.java
Normal file
39
src/org/redkale/net/http/HttpMapping.java
Normal file
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* 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.lang.annotation.*;
|
||||
import static java.lang.annotation.ElementType.METHOD;
|
||||
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||
|
||||
/**
|
||||
* 配合 HttpServlet 使用。
|
||||
* 用于对@WebServlet对应的url进行细分。 其url必须是包含WebServlet中定义的前缀, 且不能是正则表达式
|
||||
*
|
||||
* <p>
|
||||
* 详情见: https://redkale.org
|
||||
*
|
||||
* @author zhangjx
|
||||
*/
|
||||
@Documented
|
||||
@Target({METHOD})
|
||||
@Retention(RUNTIME)
|
||||
public @interface HttpMapping {
|
||||
|
||||
int actionid() default 0;
|
||||
|
||||
String url();
|
||||
|
||||
String[] methods() default {};//允许方法(不区分大小写),如:GET/POST/PUT,为空表示允许所有方法
|
||||
|
||||
String comment() default ""; //备注描述
|
||||
|
||||
boolean inherited() default true; //是否能被继承, 当 HttpServlet 被继承后该方法是否能被子类继承
|
||||
|
||||
String result() default "Object"; //输出结果的数据类型
|
||||
|
||||
Class[] results() default {}; //输出结果的数据类型集合,由于结果类型可能是泛型而注解的参数值不支持泛型,因此加入明细数据类型集合
|
||||
}
|
||||
61
src/org/redkale/net/http/HttpParam.java
Normal file
61
src/org/redkale/net/http/HttpParam.java
Normal file
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* 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.lang.annotation.*;
|
||||
import static java.lang.annotation.ElementType.METHOD;
|
||||
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||
|
||||
/**
|
||||
* 配合 @HttpMapping 使用。
|
||||
* 用于对@HttpMapping方法中参数描述
|
||||
*
|
||||
* <p>
|
||||
* 详情见: https://redkale.org
|
||||
*
|
||||
* @author zhangjx
|
||||
*/
|
||||
@Documented
|
||||
@Target({METHOD})
|
||||
@Retention(RUNTIME)
|
||||
@Repeatable(HttpParam.HttpParams.class)
|
||||
public @interface HttpParam {
|
||||
|
||||
String name(); //参数名
|
||||
|
||||
Class type(); //参数的数据类型
|
||||
|
||||
String comment() default ""; //备注描述
|
||||
|
||||
HttpParamSourceType src() default HttpParamSourceType.PARAMETER; //参数来源类型
|
||||
|
||||
int radix() default 10; //转换数字byte/short/int/long时所用的进制数, 默认10进制
|
||||
|
||||
boolean required() default true; //参数是否必传
|
||||
|
||||
/**
|
||||
* 配合 @WebParam 使用。
|
||||
* 用于对@WebParam中参数的来源类型
|
||||
*
|
||||
* <p>
|
||||
* 详情见: https://redkale.org
|
||||
*
|
||||
* @author zhangjx
|
||||
*/
|
||||
public static enum HttpParamSourceType {
|
||||
|
||||
PARAMETER, HEADER, COOKIE;
|
||||
}
|
||||
|
||||
@Documented
|
||||
@Target({METHOD})
|
||||
@Retention(RUNTIME)
|
||||
@interface HttpParams {
|
||||
|
||||
HttpParam[] value();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -43,11 +43,7 @@ public class HttpPrepareServlet extends PrepareServlet<String, HttpContext, Http
|
||||
public void init(HttpContext context, AnyValue config) {
|
||||
Collection<HttpServlet> servlets = getServlets();
|
||||
servlets.forEach(s -> {
|
||||
if (s instanceof WebSocketServlet) {
|
||||
((WebSocketServlet) s).preInit(context, getServletConf(s));
|
||||
} else if (s instanceof HttpBaseServlet) {
|
||||
((HttpBaseServlet) s).preInit(context, getServletConf(s));
|
||||
}
|
||||
s.preInit(context, getServletConf(s));
|
||||
s.init(context, getServletConf(s));
|
||||
});
|
||||
final WatchFactory watch = context.getWatchFactory();
|
||||
@@ -213,11 +209,7 @@ public class HttpPrepareServlet extends PrepareServlet<String, HttpContext, Http
|
||||
this.resourceHttpServlet.destroy(context, config);
|
||||
getServlets().forEach(s -> {
|
||||
s.destroy(context, getServletConf(s));
|
||||
if (s instanceof WebSocketServlet) {
|
||||
((WebSocketServlet) s).postDestroy(context, getServletConf(s));
|
||||
} else if (s instanceof HttpBaseServlet) {
|
||||
((HttpBaseServlet) s).postDestroy(context, getServletConf(s));
|
||||
}
|
||||
s.postDestroy(context, getServletConf(s));
|
||||
});
|
||||
this.allMapStrings.clear();
|
||||
this.wsmappings.clear();
|
||||
|
||||
@@ -64,6 +64,10 @@ public class HttpRequest extends Request<HttpContext> {
|
||||
|
||||
protected boolean boundary = false;
|
||||
|
||||
protected int moduleid;
|
||||
|
||||
protected int actionid;
|
||||
|
||||
private final String remoteAddrHeader;
|
||||
|
||||
Object attachment; //供 HttpBaseServlet传递Entry使用
|
||||
@@ -236,6 +240,24 @@ public class HttpRequest extends Request<HttpContext> {
|
||||
return super.removeProperty(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取模块ID,来自@HttpServlet.moduleid()
|
||||
*
|
||||
* @return 模块ID
|
||||
*/
|
||||
public int getModuleid() {
|
||||
return this.moduleid;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取操作ID,来自@HttpMapping.actionid()
|
||||
*
|
||||
* @return 模块ID
|
||||
*/
|
||||
public int getActionid() {
|
||||
return this.actionid;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取客户端地址IP
|
||||
*
|
||||
@@ -350,6 +372,8 @@ public class HttpRequest extends Request<HttpContext> {
|
||||
this.contentLength = -1;
|
||||
this.boundary = false;
|
||||
this.bodyparsed = false;
|
||||
this.moduleid = 0;
|
||||
this.actionid = 0;
|
||||
|
||||
this.attachment = null;
|
||||
|
||||
|
||||
@@ -213,7 +213,7 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建AsyncHandler实例,将非字符串对象以JSON格式输出,字符串以文本输出
|
||||
* 创建AsyncHandler实例
|
||||
*
|
||||
* @return AsyncHandler
|
||||
*/
|
||||
@@ -232,6 +232,21 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建AsyncHandler子类的实例 <br>
|
||||
*
|
||||
* 传入的AsyncHandler子类必须是public,且保证其子类可被继承且completed、failed可被重载且包含空参数的构造函数。
|
||||
*
|
||||
* @param <H> 泛型
|
||||
* @param handlerClass AsyncHandler子类
|
||||
*
|
||||
* @return AsyncHandler AsyncHandler
|
||||
*/
|
||||
public <H extends AsyncHandler> H createAsyncHandler(Class<H> handlerClass) {
|
||||
if (handlerClass == null || handlerClass == AsyncHandler.class) return (H) createAsyncHandler();
|
||||
return context.loadAsyncHandlerCreator(handlerClass).create(createAsyncHandler());
|
||||
}
|
||||
|
||||
/**
|
||||
* 将对象以JSON格式输出
|
||||
*
|
||||
@@ -354,6 +369,44 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 将HttpResult的结果对象以JSON格式输出
|
||||
*
|
||||
* @param result HttpResult对象
|
||||
*/
|
||||
public void finishJson(final HttpResult result) {
|
||||
finishJson(request.getJsonConvert(), result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将HttpResult的结果对象以JSON格式输出
|
||||
*
|
||||
* @param convert 指定的JsonConvert
|
||||
* @param result HttpResult对象
|
||||
*/
|
||||
public void finishJson(final JsonConvert convert, final HttpResult result) {
|
||||
if (output == null) {
|
||||
finish("");
|
||||
return;
|
||||
}
|
||||
if (result.getContentType() != null) setContentType(result.getContentType());
|
||||
addHeader(result.getHeaders()).addCookie(result.getCookies()).setStatus(result.getStatus() < 1 ? 200 : result.getStatus());
|
||||
if (result.getResult() instanceof File) {
|
||||
try {
|
||||
finish((File) result.getResult());
|
||||
} catch (IOException e) {
|
||||
getContext().getLogger().log(Level.WARNING, "HttpServlet finishJson HttpResult File occur, forece to close channel. request = " + getRequest(), e);
|
||||
finish(500, null);
|
||||
}
|
||||
} else if (result.getResult() instanceof String) {
|
||||
finish((String) result.getResult());
|
||||
} else if (result.getResult() == null) {
|
||||
finish(result.getMessage());
|
||||
} else {
|
||||
finishJson(result.getResult());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将CompletableFuture的结果对象以JSON格式输出
|
||||
*
|
||||
|
||||
103
src/org/redkale/net/http/HttpResult.java
Normal file
103
src/org/redkale/net/http/HttpResult.java
Normal file
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
* To change this license header, choose License Headers in Project Properties.
|
||||
* To change this template file, choose Tools | Templates
|
||||
* and open the template in the editor.
|
||||
*/
|
||||
package org.redkale.net.http;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.net.HttpCookie;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
*
|
||||
* <p>
|
||||
* 详情见: https://redkale.org
|
||||
*
|
||||
* @author zhangjx
|
||||
* @param <T> 结果对象的类型
|
||||
*/
|
||||
public class HttpResult<T> {
|
||||
|
||||
public static final String SESSIONID_COOKIENAME = HttpRequest.SESSIONID_NAME;
|
||||
|
||||
private Map<String, String> headers;
|
||||
|
||||
private List<HttpCookie> cookies;
|
||||
|
||||
private String contentType;
|
||||
|
||||
private T result;
|
||||
|
||||
private int status = 0; //不设置则为 200
|
||||
|
||||
private String message;
|
||||
|
||||
public HttpResult() {
|
||||
}
|
||||
|
||||
public HttpResult(T result) {
|
||||
this.result = result;
|
||||
}
|
||||
|
||||
public HttpResult<T> addHeader(String name, Serializable value) {
|
||||
if (this.headers == null) this.headers = new HashMap<>();
|
||||
this.headers.put(name, String.valueOf(value));
|
||||
return this;
|
||||
}
|
||||
|
||||
public HttpResult<T> addCookie(HttpCookie cookie) {
|
||||
if (this.cookies == null) this.cookies = new ArrayList<>();
|
||||
this.cookies.add(cookie);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Map<String, String> getHeaders() {
|
||||
return headers;
|
||||
}
|
||||
|
||||
public void setHeaders(Map<String, String> headers) {
|
||||
this.headers = headers;
|
||||
}
|
||||
|
||||
public List<HttpCookie> getCookies() {
|
||||
return cookies;
|
||||
}
|
||||
|
||||
public void setCookies(List<HttpCookie> cookies) {
|
||||
this.cookies = cookies;
|
||||
}
|
||||
|
||||
public String getContentType() {
|
||||
return contentType;
|
||||
}
|
||||
|
||||
public void setContentType(String contentType) {
|
||||
this.contentType = contentType;
|
||||
}
|
||||
|
||||
public T getResult() {
|
||||
return result;
|
||||
}
|
||||
|
||||
public void setResult(T result) {
|
||||
this.result = result;
|
||||
}
|
||||
|
||||
public int getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setStatus(int status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
public void setMessage(String message) {
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -5,9 +5,20 @@
|
||||
*/
|
||||
package org.redkale.net.http;
|
||||
|
||||
import org.redkale.net.Servlet;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
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 org.redkale.net.*;
|
||||
import org.redkale.service.RetResult;
|
||||
import org.redkale.util.*;
|
||||
|
||||
/**
|
||||
* HTTP版的Servlet, 执行顺序 execute --> preExecute --> authenticate --> HttpMapping对应的方法
|
||||
*
|
||||
* <p>
|
||||
* 详情见: https://redkale.org
|
||||
@@ -16,6 +27,337 @@ import org.redkale.net.Servlet;
|
||||
*/
|
||||
public abstract class HttpServlet extends Servlet<HttpContext, HttpRequest, HttpResponse> {
|
||||
|
||||
public static final int RET_SERVER_ERROR = 1800_0001;
|
||||
|
||||
public static final int RET_METHOD_ERROR = 1800_0002;
|
||||
|
||||
String _prefix = ""; //当前HttpServlet的path前缀
|
||||
|
||||
private Map.Entry<String, Entry>[] mappings;
|
||||
|
||||
//这里不能直接使用HttpServlet,会造成死循环初始化HttpServlet
|
||||
private final Servlet<HttpContext, HttpRequest, HttpResponse> authSuccessServlet = new Servlet<HttpContext, HttpRequest, HttpResponse>() {
|
||||
@Override
|
||||
public void execute(HttpRequest request, HttpResponse response) throws IOException {
|
||||
Entry entry = (Entry) request.attachment;
|
||||
if (entry.cacheseconds > 0) {//有缓存设置
|
||||
CacheEntry ce = entry.cache.get(request.getRequestURI());
|
||||
if (ce != null && ce.time + entry.cacheseconds > System.currentTimeMillis()) { //缓存有效
|
||||
response.setStatus(ce.status);
|
||||
response.setContentType(ce.contentType);
|
||||
response.finish(ce.getBuffers());
|
||||
return;
|
||||
}
|
||||
response.setBufferHandler(entry.cacheHandler);
|
||||
}
|
||||
entry.servlet.execute(request, response);
|
||||
}
|
||||
};
|
||||
|
||||
//preExecute运行完后执行的Servlet
|
||||
private final Servlet<HttpContext, HttpRequest, HttpResponse> preSuccessServlet = new Servlet<HttpContext, HttpRequest, HttpResponse>() {
|
||||
@Override
|
||||
public void execute(HttpRequest request, HttpResponse response) throws IOException {
|
||||
for (Map.Entry<String, Entry> en : mappings) {
|
||||
if (request.getRequestURI().startsWith(en.getKey())) {
|
||||
Entry entry = en.getValue();
|
||||
if (!entry.checkMethod(request.getMethod())) {
|
||||
response.finishJson(new RetResult(RET_METHOD_ERROR, "Method(" + request.getMethod() + ") Error"));
|
||||
return;
|
||||
}
|
||||
request.attachment = entry;
|
||||
request.moduleid = entry.moduleid;
|
||||
request.actionid = entry.actionid;
|
||||
if (entry.ignore) {
|
||||
authSuccessServlet.execute(request, response);
|
||||
} else {
|
||||
response.nextEvent(authSuccessServlet);
|
||||
authenticate(request, response);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
throw new IOException(this.getClass().getName() + " not found method for URI(" + request.getRequestURI() + ")");
|
||||
}
|
||||
};
|
||||
|
||||
void preInit(HttpContext context, AnyValue config) {
|
||||
String path = _prefix == null ? "" : _prefix;
|
||||
WebServlet ws = this.getClass().getAnnotation(WebServlet.class);
|
||||
if (ws != null && !ws.repair()) path = "";
|
||||
HashMap<String, Entry> map = load();
|
||||
this.mappings = new Map.Entry[map.size()];
|
||||
int i = -1;
|
||||
for (Map.Entry<String, Entry> en : map.entrySet()) {
|
||||
mappings[++i] = new AbstractMap.SimpleEntry<>(path + en.getKey(), en.getValue());
|
||||
}
|
||||
//必须要倒排序, /query /query1 /query12 确保含子集的优先匹配 /query12 /query1 /query
|
||||
Arrays.sort(mappings, (o1, o2) -> o2.getKey().compareTo(o1.getKey()));
|
||||
}
|
||||
|
||||
void postDestroy(HttpContext context, AnyValue config) {
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 预执行方法,在execute方法之前运行,通常用于常规统计或基础检测,例如 : <br>
|
||||
* <blockquote><pre>
|
||||
* @Override
|
||||
* public void preExecute(final HttpRequest request, final HttpResponse response) throws IOException {
|
||||
* if (finer) response.recycleListener((req, resp) -> { //记录处理时间比较长的请求
|
||||
* long e = System.currentTimeMillis() - ((HttpRequest) req).getCreatetime();
|
||||
* if (e > 200) logger.finer("http-execute-cost-time: " + e + " ms. request = " + req);
|
||||
* });
|
||||
* response.nextEvent();
|
||||
* }
|
||||
* </pre></blockquote>
|
||||
* <p>
|
||||
*
|
||||
* @param request HttpRequest
|
||||
* @param response HttpResponse
|
||||
*
|
||||
* @throws IOException IOException
|
||||
*/
|
||||
protected void preExecute(HttpRequest request, HttpResponse response) throws IOException {
|
||||
response.nextEvent();
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 用户登录或权限验证, 没有注解为@AuthIgnore 的方法会执行authenticate方法, 若验证成功则必须调用response.nextEvent();进行下一步操作, 例如: <br>
|
||||
* <blockquote><pre>
|
||||
* @Override
|
||||
* public void authenticate(HttpRequest request, HttpResponse response) throws IOException {
|
||||
* UserInfo info = currentUser(request);
|
||||
* if (info == null) {
|
||||
* response.finishJson(RET_UNLOGIN);
|
||||
* return;
|
||||
* } else if (!info.checkAuth(request.getModuleid(), request.getActionid())) {
|
||||
* response.finishJson(RET_AUTHILLEGAL);
|
||||
* return;
|
||||
* }
|
||||
* response.nextEvent();
|
||||
* }
|
||||
* </pre></blockquote>
|
||||
* <p>
|
||||
*
|
||||
*
|
||||
* @param request HttpRequest
|
||||
* @param response HttpResponse
|
||||
*
|
||||
* @throws IOException IOException
|
||||
*/
|
||||
protected void authenticate(HttpRequest request, HttpResponse response) throws IOException {
|
||||
response.nextEvent();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(HttpRequest request, HttpResponse response) throws IOException {
|
||||
response.nextEvent(preSuccessServlet);
|
||||
preExecute(request, response);
|
||||
}
|
||||
|
||||
private HashMap<String, Entry> load() {
|
||||
final boolean typeIgnore = this.getClass().getAnnotation(AuthIgnore.class) != null;
|
||||
WebServlet module = this.getClass().getAnnotation(WebServlet.class);
|
||||
final int serviceid = module == null ? 0 : module.moduleid();
|
||||
final HashMap<String, Entry> map = new HashMap<>();
|
||||
HashMap<String, Class> nameset = new HashMap<>();
|
||||
final Class selfClz = this.getClass();
|
||||
Class clz = this.getClass();
|
||||
do {
|
||||
if (java.lang.reflect.Modifier.isAbstract(clz.getModifiers())) break;
|
||||
for (final Method method : clz.getMethods()) {
|
||||
//-----------------------------------------------
|
||||
String methodname = method.getName();
|
||||
if ("service".equals(methodname) || "preExecute".equals(methodname) || "execute".equals(methodname) || "authenticate".equals(methodname)) continue;
|
||||
//-----------------------------------------------
|
||||
Class[] paramTypes = method.getParameterTypes();
|
||||
if (paramTypes.length != 2 || paramTypes[0] != HttpRequest.class
|
||||
|| paramTypes[1] != HttpResponse.class) continue;
|
||||
//-----------------------------------------------
|
||||
Class[] exps = method.getExceptionTypes();
|
||||
if (exps.length > 0 && (exps.length != 1 || exps[0] != IOException.class)) continue;
|
||||
//-----------------------------------------------
|
||||
|
||||
final HttpMapping mapping = method.getAnnotation(HttpMapping.class);
|
||||
if (mapping == null) continue;
|
||||
final boolean inherited = mapping.inherited();
|
||||
if (!inherited && selfClz != clz) continue; //忽略不被继承的方法
|
||||
final int actionid = mapping.actionid();
|
||||
final String name = mapping.url().trim();
|
||||
final String[] methods = mapping.methods();
|
||||
if (nameset.containsKey(name)) {
|
||||
if (nameset.get(name) != clz) continue;
|
||||
throw new RuntimeException(this.getClass().getSimpleName() + " have two same " + HttpMapping.class.getSimpleName() + "(" + name + ")");
|
||||
}
|
||||
nameset.put(name, clz);
|
||||
map.put(name, new Entry(typeIgnore, serviceid, actionid, name, methods, method, createHttpServlet(method)));
|
||||
}
|
||||
} while ((clz = clz.getSuperclass()) != HttpServlet.class);
|
||||
return map;
|
||||
}
|
||||
|
||||
private HttpServlet createHttpServlet(final Method method) {
|
||||
//------------------------------------------------------------------------------
|
||||
final String supDynName = HttpServlet.class.getName().replace('.', '/');
|
||||
final String interName = this.getClass().getName().replace('.', '/');
|
||||
final String interDesc = jdk.internal.org.objectweb.asm.Type.getDescriptor(this.getClass());
|
||||
final String requestSupDesc = jdk.internal.org.objectweb.asm.Type.getDescriptor(Request.class);
|
||||
final String responseSupDesc = jdk.internal.org.objectweb.asm.Type.getDescriptor(Response.class);
|
||||
final String requestDesc = jdk.internal.org.objectweb.asm.Type.getDescriptor(HttpRequest.class);
|
||||
final String responseDesc = jdk.internal.org.objectweb.asm.Type.getDescriptor(HttpResponse.class);
|
||||
String newDynName = interName + "_Dyn_" + method.getName();
|
||||
int i = 0;
|
||||
for (;;) {
|
||||
try {
|
||||
Class.forName(newDynName.replace('/', '.'));
|
||||
newDynName += "_" + (++i);
|
||||
} catch (Throwable ex) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
ClassWriter cw = new ClassWriter(COMPUTE_FRAMES);
|
||||
FieldVisitor fv;
|
||||
MethodVisitor mv;
|
||||
AnnotationVisitor av0;
|
||||
final String factfield = "_factServlet";
|
||||
cw.visit(V1_8, ACC_PUBLIC + ACC_FINAL + ACC_SUPER, newDynName, null, supDynName, null);
|
||||
{
|
||||
fv = cw.visitField(ACC_PUBLIC, factfield, interDesc, null, null);
|
||||
fv.visitEnd();
|
||||
}
|
||||
{ //构造函数
|
||||
mv = (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();
|
||||
}
|
||||
{
|
||||
mv = (cw.visitMethod(ACC_PUBLIC, "execute", "(" + requestDesc + responseDesc + ")V", null, new String[]{"java/io/IOException"}));
|
||||
mv.visitVarInsn(ALOAD, 0);
|
||||
mv.visitFieldInsn(GETFIELD, newDynName, factfield, interDesc);
|
||||
mv.visitVarInsn(ALOAD, 1);
|
||||
mv.visitVarInsn(ALOAD, 2);
|
||||
mv.visitMethodInsn(INVOKEVIRTUAL, interName, method.getName(), "(" + requestDesc + responseDesc + ")V", false);
|
||||
mv.visitInsn(RETURN);
|
||||
mv.visitMaxs(3, 3);
|
||||
mv.visitEnd();
|
||||
}
|
||||
{
|
||||
mv = cw.visitMethod(ACC_PUBLIC + ACC_BRIDGE + ACC_SYNTHETIC, "execute", "(" + requestSupDesc + responseSupDesc + ")V", null, new String[]{"java/io/IOException"});
|
||||
mv.visitVarInsn(ALOAD, 0);
|
||||
mv.visitVarInsn(ALOAD, 1);
|
||||
mv.visitTypeInsn(CHECKCAST, HttpRequest.class.getName().replace('.', '/'));
|
||||
mv.visitVarInsn(ALOAD, 2);
|
||||
mv.visitTypeInsn(CHECKCAST, HttpResponse.class.getName().replace('.', '/'));
|
||||
mv.visitMethodInsn(INVOKEVIRTUAL, newDynName, "execute", "(" + requestDesc + responseDesc + ")V", false);
|
||||
mv.visitInsn(RETURN);
|
||||
mv.visitMaxs(3, 3);
|
||||
mv.visitEnd();
|
||||
}
|
||||
cw.visitEnd();
|
||||
//------------------------------------------------------------------------------
|
||||
byte[] bytes = cw.toByteArray();
|
||||
Class<?> newClazz = new ClassLoader(this.getClass().getClassLoader()) {
|
||||
public final Class<?> loadClass(String name, byte[] b) {
|
||||
return defineClass(name, b, 0, b.length);
|
||||
}
|
||||
}.loadClass(newDynName.replace('/', '.'), bytes);
|
||||
try {
|
||||
HttpServlet instance = (HttpServlet) newClazz.newInstance();
|
||||
instance.getClass().getField(factfield).set(instance, this);
|
||||
return instance;
|
||||
} catch (Exception ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private static final class Entry {
|
||||
|
||||
public Entry(boolean typeIgnore, int moduleid, int actionid, String name, String[] methods, Method method, HttpServlet servlet) {
|
||||
this.moduleid = moduleid;
|
||||
this.actionid = actionid;
|
||||
this.name = name;
|
||||
this.methods = methods;
|
||||
this.method = method;
|
||||
this.servlet = servlet;
|
||||
this.ignore = typeIgnore || method.getAnnotation(AuthIgnore.class) != null;
|
||||
HttpCacheable hc = method.getAnnotation(HttpCacheable.class);
|
||||
this.cacheseconds = hc == null ? 0 : hc.seconds() * 1000;
|
||||
this.cache = cacheseconds > 0 ? new ConcurrentHashMap() : null;
|
||||
this.cacheHandler = cacheseconds > 0 ? (HttpResponse response, ByteBuffer[] buffers) -> {
|
||||
int status = response.getStatus();
|
||||
if (status != 200) return null;
|
||||
CacheEntry ce = new CacheEntry(response.getStatus(), response.getContentType(), buffers);
|
||||
cache.put(response.getRequest().getRequestURI(), ce);
|
||||
return ce.getBuffers();
|
||||
} : null;
|
||||
}
|
||||
|
||||
public boolean isNeedCheck() {
|
||||
return this.moduleid != 0 || this.actionid != 0;
|
||||
}
|
||||
|
||||
public boolean checkMethod(final String reqMethod) {
|
||||
if (methods.length == 0) return true;
|
||||
for (String m : methods) {
|
||||
if (reqMethod.equalsIgnoreCase(m)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public final HttpResponse.BufferHandler cacheHandler;
|
||||
|
||||
public final ConcurrentHashMap<String, CacheEntry> cache;
|
||||
|
||||
public final int cacheseconds;
|
||||
|
||||
public final boolean ignore;
|
||||
|
||||
public final int moduleid;
|
||||
|
||||
public final int actionid;
|
||||
|
||||
public final String name;
|
||||
|
||||
public final String[] methods;
|
||||
|
||||
public final Method method;
|
||||
|
||||
public final HttpServlet servlet;
|
||||
}
|
||||
|
||||
private static final class CacheEntry {
|
||||
|
||||
public final long time = System.currentTimeMillis();
|
||||
|
||||
private final ByteBuffer[] buffers;
|
||||
|
||||
private final int status;
|
||||
|
||||
private final String contentType;
|
||||
|
||||
public CacheEntry(int status, String contentType, ByteBuffer[] bufs) {
|
||||
this.status = status;
|
||||
this.contentType = contentType;
|
||||
final ByteBuffer[] newBuffers = new ByteBuffer[bufs.length];
|
||||
for (int i = 0; i < newBuffers.length; i++) {
|
||||
newBuffers[i] = bufs[i].duplicate().asReadOnlyBuffer();
|
||||
}
|
||||
this.buffers = newBuffers;
|
||||
}
|
||||
|
||||
public ByteBuffer[] getBuffers() {
|
||||
final ByteBuffer[] newBuffers = new ByteBuffer[buffers.length];
|
||||
for (int i = 0; i < newBuffers.length; i++) {
|
||||
newBuffers[i] = buffers[i].duplicate();
|
||||
}
|
||||
return newBuffers;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -122,12 +122,12 @@ public final class Rest {
|
||||
final String flipperDesc = Type.getDescriptor(Flipper.class);
|
||||
final String restoutputDesc = Type.getDescriptor(RestOutput.class);
|
||||
final String attrDesc = Type.getDescriptor(org.redkale.util.Attribute.class);
|
||||
final String authDesc = Type.getDescriptor(HttpBaseServlet.AuthIgnore.class);
|
||||
final String cacheDesc = Type.getDescriptor(HttpBaseServlet.HttpCacheable.class);
|
||||
final String mappingDesc = Type.getDescriptor(HttpBaseServlet.WebMapping.class);
|
||||
final String webparamDesc = Type.getDescriptor(HttpBaseServlet.WebParam.class);
|
||||
final String webparamsDesc = Type.getDescriptor(HttpBaseServlet.WebParams.class);
|
||||
final String sourcetypeDesc = Type.getDescriptor(HttpBaseServlet.ParamSourceType.class);
|
||||
final String authDesc = Type.getDescriptor(AuthIgnore.class);
|
||||
final String cacheDesc = Type.getDescriptor(HttpCacheable.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);
|
||||
@@ -218,7 +218,7 @@ public final class Rest {
|
||||
|
||||
final List<MappingEntry> entrys = new ArrayList<>();
|
||||
final Map<String, org.redkale.util.Attribute> restAttributes = new LinkedHashMap<>();
|
||||
//获取所有可以转换成WebMapping的方法
|
||||
//获取所有可以转换成HttpMapping的方法
|
||||
int methodidex = 0;
|
||||
final List<java.lang.reflect.Type[]> paramtypes = new ArrayList<>();
|
||||
for (final Method method : serviceType.getMethods()) {
|
||||
@@ -254,9 +254,9 @@ public final class Rest {
|
||||
}
|
||||
methodidex++;
|
||||
}
|
||||
if (entrys.isEmpty()) return null; //没有可WebMapping的方法
|
||||
if (entrys.isEmpty()) return null; //没有可HttpMapping的方法
|
||||
|
||||
//将每个Service可转换的方法生成HttpServlet对应的WebMapping方法
|
||||
//将每个Service可转换的方法生成HttpServlet对应的HttpMapping方法
|
||||
final Map<String, List<String>> asmParamMap = MethodParamClassVisitor.getMethodParamNames(serviceType);
|
||||
for (final MappingEntry entry : entrys) {
|
||||
final Method method = entry.mappingMethod;
|
||||
@@ -380,7 +380,7 @@ public final class Rest {
|
||||
|
||||
Map<String, Object> mappingMap = new LinkedHashMap<>();
|
||||
{ // 设置 Annotation
|
||||
//设置 WebMapping
|
||||
//设置 HttpMapping
|
||||
boolean reqpath = false;
|
||||
for (Object[] ps : paramlist) {
|
||||
if ("#".equals((String) ps[1])) {
|
||||
@@ -425,8 +425,8 @@ public final class Rest {
|
||||
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 ? HttpBaseServlet.ParamSourceType.HEADER.name()
|
||||
: (iscookie ? HttpBaseServlet.ParamSourceType.COOKIE.name() : HttpBaseServlet.ParamSourceType.PARAMETER.name()));
|
||||
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();
|
||||
|
||||
@@ -19,7 +19,7 @@ import org.redkale.util.*;
|
||||
* @author zhangjx
|
||||
* @param <T> 当前用户对象类型
|
||||
*/
|
||||
public abstract class RestHttpServlet<T> extends HttpBaseServlet {
|
||||
public abstract class RestHttpServlet<T> extends HttpServlet {
|
||||
|
||||
protected abstract T currentUser(HttpRequest req) throws IOException;
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ public @interface RestMapping {
|
||||
String name() default "";
|
||||
|
||||
/**
|
||||
* 备注描述, 对应@WebMapping.comment
|
||||
* 备注描述, 对应@HttpMapping.comment
|
||||
*
|
||||
* @return String
|
||||
*/
|
||||
@@ -54,7 +54,7 @@ public @interface RestMapping {
|
||||
boolean auth() default false;
|
||||
|
||||
/**
|
||||
* 操作ID值,鉴权时用到, 对应@WebMapping.actionid
|
||||
* 操作ID值,鉴权时用到, 对应@HttpMapping.actionid
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
@@ -68,7 +68,7 @@ public @interface RestMapping {
|
||||
int cacheseconds() default 0;
|
||||
|
||||
/**
|
||||
* 允许方法(不区分大小写),如:GET/POST/PUT,为空表示允许所有方法, 对应@WebMapping.methods
|
||||
* 允许方法(不区分大小写),如:GET/POST/PUT,为空表示允许所有方法, 对应@HttpMapping.methods
|
||||
*
|
||||
* @return String[]
|
||||
*/
|
||||
|
||||
@@ -10,13 +10,16 @@ import java.net.HttpCookie;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* 使用 HttpResult 代替
|
||||
*
|
||||
* <p>
|
||||
* 详情见: https://redkale.org
|
||||
*
|
||||
* @deprecated
|
||||
* @author zhangjx
|
||||
* @param <T> 结果对象的类型
|
||||
*/
|
||||
@Deprecated
|
||||
public class RestOutput<T> {
|
||||
|
||||
private Map<String, String> headers;
|
||||
|
||||
@@ -71,7 +71,8 @@ public abstract class WebSocketServlet extends HttpServlet implements Resourcabl
|
||||
|
||||
protected WebSocketEngine engine;
|
||||
|
||||
public final void preInit(HttpContext context, AnyValue conf) {
|
||||
@Override
|
||||
final void preInit(HttpContext context, AnyValue conf) {
|
||||
InetSocketAddress addr = context.getServerAddress();
|
||||
this.engine = new WebSocketEngine(addr.getHostString() + ":" + addr.getPort() + "-[" + resourceName() + "]", this.node, logger);
|
||||
if (this.node == null) this.node = createWebSocketNode();
|
||||
@@ -84,7 +85,8 @@ public abstract class WebSocketServlet extends HttpServlet implements Resourcabl
|
||||
this.engine.init(conf);
|
||||
}
|
||||
|
||||
public final void postDestroy(HttpContext context, AnyValue conf) {
|
||||
@Override
|
||||
final void postDestroy(HttpContext context, AnyValue conf) {
|
||||
this.node.postDestroy(conf);
|
||||
super.destroy(context, conf);
|
||||
engine.close();
|
||||
|
||||
27
src/org/redkale/util/AuthIgnore.java
Normal file
27
src/org/redkale/util/AuthIgnore.java
Normal file
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* To change this license header, choose License Headers in Project Properties.
|
||||
* To change this template file, choose Tools | Templates
|
||||
* and open the template in the editor.
|
||||
*/
|
||||
package org.redkale.util;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
import static java.lang.annotation.ElementType.*;
|
||||
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||
|
||||
/**
|
||||
* 配合 HttpServlet 使用。
|
||||
* 当标记为 @AuthIgnore 的方法在执行execute之前不会调用authenticate 方法。
|
||||
*
|
||||
* <p>
|
||||
* 详情见: https://redkale.org
|
||||
*
|
||||
* @author zhangjx
|
||||
*/
|
||||
@Inherited
|
||||
@Documented
|
||||
@Target({METHOD, TYPE})
|
||||
@Retention(RUNTIME)
|
||||
public @interface AuthIgnore {
|
||||
|
||||
}
|
||||
@@ -25,16 +25,16 @@ public class SimpleRestServlet extends RestHttpServlet<UserInfo> {
|
||||
|
||||
//普通鉴权
|
||||
@Override
|
||||
public void authenticate(int module, int actionid, HttpRequest request, HttpResponse response, HttpServlet next) throws IOException {
|
||||
public void authenticate(HttpRequest request, HttpResponse response) throws IOException {
|
||||
UserInfo info = currentUser(request);
|
||||
if (info == null) {
|
||||
response.finishJson(RET_UNLOGIN);
|
||||
return;
|
||||
} else if (!info.checkAuth(module, actionid)) {
|
||||
} else if (!info.checkAuth(request.getModuleid(), request.getActionid())) {
|
||||
response.finishJson(RET_AUTHILLEGAL);
|
||||
return;
|
||||
}
|
||||
next.execute(request, response);
|
||||
response.nextEvent();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -48,7 +48,7 @@ public class _DynHelloRestServlet1 extends SimpleRestServlet {
|
||||
}
|
||||
|
||||
@AuthIgnore
|
||||
@WebMapping(url = "/hello/create")
|
||||
@HttpMapping(url = "/hello/create")
|
||||
public void create(HttpRequest req, HttpResponse resp) throws IOException {
|
||||
HelloService service = _servicemap == null ? _service : _servicemap.get(req.getHeader(Rest.REST_HEADER_RESOURCE_NAME, ""));
|
||||
HelloEntity bean = req.getJsonParameter(HelloEntity.class, "bean");
|
||||
@@ -60,7 +60,7 @@ public class _DynHelloRestServlet1 extends SimpleRestServlet {
|
||||
}
|
||||
|
||||
@AuthIgnore
|
||||
@WebMapping(url = "/hello/delete/")
|
||||
@HttpMapping(url = "/hello/delete/")
|
||||
public void delete(HttpRequest req, HttpResponse resp) throws IOException {
|
||||
HelloService service = _servicemap == null ? _service : _servicemap.get(req.getHeader(Rest.REST_HEADER_RESOURCE_NAME, ""));
|
||||
int id = Integer.parseInt(req.getRequstURILastPath());
|
||||
@@ -69,7 +69,7 @@ public class _DynHelloRestServlet1 extends SimpleRestServlet {
|
||||
}
|
||||
|
||||
@AuthIgnore
|
||||
@WebMapping(url = "/hello/update")
|
||||
@HttpMapping(url = "/hello/update")
|
||||
public void update(HttpRequest req, HttpResponse resp) throws IOException {
|
||||
HelloService service = _servicemap == null ? _service : _servicemap.get(req.getHeader(Rest.REST_HEADER_RESOURCE_NAME, ""));
|
||||
String clientaddr = req.getRemoteAddr();
|
||||
@@ -81,7 +81,7 @@ public class _DynHelloRestServlet1 extends SimpleRestServlet {
|
||||
}
|
||||
|
||||
@AuthIgnore
|
||||
@WebMapping(url = "/hello/partupdate")
|
||||
@HttpMapping(url = "/hello/partupdate")
|
||||
public void partupdate(HttpRequest req, HttpResponse resp) throws IOException {
|
||||
HelloService service = _servicemap == null ? _service : _servicemap.get(req.getHeader(Rest.REST_HEADER_RESOURCE_NAME, ""));
|
||||
HelloEntity bean = req.getJsonParameter(HelloEntity.class, "bean");
|
||||
@@ -93,7 +93,7 @@ public class _DynHelloRestServlet1 extends SimpleRestServlet {
|
||||
}
|
||||
|
||||
@AuthIgnore
|
||||
@WebMapping(url = "/hello/query")
|
||||
@HttpMapping(url = "/hello/query")
|
||||
public void query(HttpRequest req, HttpResponse resp) throws IOException {
|
||||
HelloService service = _servicemap == null ? _service : _servicemap.get(req.getHeader(Rest.REST_HEADER_RESOURCE_NAME, ""));
|
||||
HelloBean bean = req.getJsonParameter(HelloBean.class, "bean");
|
||||
@@ -107,7 +107,7 @@ public class _DynHelloRestServlet1 extends SimpleRestServlet {
|
||||
}
|
||||
|
||||
@AuthIgnore
|
||||
@WebMapping(url = "/hello/list")
|
||||
@HttpMapping(url = "/hello/list")
|
||||
public void list(HttpRequest req, HttpResponse resp) throws IOException {
|
||||
HelloService service = _servicemap == null ? _service : _servicemap.get(req.getHeader(Rest.REST_HEADER_RESOURCE_NAME, ""));
|
||||
HelloBean bean = req.getJsonParameter(HelloBean.class, "bean");
|
||||
@@ -120,7 +120,7 @@ public class _DynHelloRestServlet1 extends SimpleRestServlet {
|
||||
}
|
||||
|
||||
@AuthIgnore
|
||||
@WebMapping(url = "/hello/find/")
|
||||
@HttpMapping(url = "/hello/find/")
|
||||
public void find(HttpRequest req, HttpResponse resp) throws IOException {
|
||||
HelloService service = _servicemap == null ? _service : _servicemap.get(req.getHeader(Rest.REST_HEADER_RESOURCE_NAME, ""));
|
||||
int id = Integer.parseInt(req.getRequstURILastPath());
|
||||
@@ -129,7 +129,7 @@ public class _DynHelloRestServlet1 extends SimpleRestServlet {
|
||||
}
|
||||
|
||||
@AuthIgnore
|
||||
@WebMapping(url = "/hello/asyncfind/")
|
||||
@HttpMapping(url = "/hello/asyncfind/")
|
||||
public void asyncfind(HttpRequest req, HttpResponse resp) throws IOException {
|
||||
HelloService service = _servicemap == null ? _service : _servicemap.get(req.getHeader(Rest.REST_HEADER_RESOURCE_NAME, ""));
|
||||
int id = Integer.parseInt(req.getRequstURILastPath());
|
||||
|
||||
@@ -11,7 +11,7 @@ import javax.annotation.Resource;
|
||||
import org.redkale.net.http.*;
|
||||
import org.redkale.service.RetResult;
|
||||
import org.redkale.source.Flipper;
|
||||
import org.redkale.util.Sheet;
|
||||
import org.redkale.util.*;
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -27,8 +27,8 @@ public class _DynHelloRestServlet2 extends SimpleRestServlet {
|
||||
private Map<String, HelloService2> _servicemap;
|
||||
|
||||
@AuthIgnore
|
||||
@WebMapping(url = "/hello/create", comment = "创建Hello对象")
|
||||
@WebParam(name = "bean", type = HelloEntity.class, comment = "Hello对象")
|
||||
@HttpMapping(url = "/hello/create", comment = "创建Hello对象")
|
||||
@HttpParam(name = "bean", type = HelloEntity.class, comment = "Hello对象")
|
||||
public void create(HttpRequest req, HttpResponse resp) throws IOException {
|
||||
HelloService2 service = _servicemap == null ? _service : _servicemap.get(req.getHeader(Rest.REST_HEADER_RESOURCE_NAME, ""));
|
||||
HelloEntity bean = req.getJsonParameter(HelloEntity.class, "bean");
|
||||
@@ -42,8 +42,8 @@ public class _DynHelloRestServlet2 extends SimpleRestServlet {
|
||||
}
|
||||
|
||||
@AuthIgnore
|
||||
@WebMapping(url = "/hello/delete/", comment = "根据id删除Hello对象")
|
||||
@WebParam(name = "#", type = int.class, comment = "Hello对象id")
|
||||
@HttpMapping(url = "/hello/delete/", comment = "根据id删除Hello对象")
|
||||
@HttpParam(name = "#", type = int.class, comment = "Hello对象id")
|
||||
public void delete(HttpRequest req, HttpResponse resp) throws IOException {
|
||||
HelloService2 service = _servicemap == null ? _service : _servicemap.get(req.getHeader(Rest.REST_HEADER_RESOURCE_NAME, ""));
|
||||
int id = Integer.parseInt(req.getRequstURILastPath());
|
||||
@@ -52,8 +52,8 @@ public class _DynHelloRestServlet2 extends SimpleRestServlet {
|
||||
}
|
||||
|
||||
@AuthIgnore
|
||||
@WebMapping(url = "/hello/update", comment = "修改Hello对象")
|
||||
@WebParam(name = "bean", type = HelloEntity.class, comment = "Hello对象")
|
||||
@HttpMapping(url = "/hello/update", comment = "修改Hello对象")
|
||||
@HttpParam(name = "bean", type = HelloEntity.class, comment = "Hello对象")
|
||||
public void update(HttpRequest req, HttpResponse resp) throws IOException {
|
||||
HelloService2 service = _servicemap == null ? _service : _servicemap.get(req.getHeader(Rest.REST_HEADER_RESOURCE_NAME, ""));
|
||||
HelloEntity bean = req.getJsonParameter(HelloEntity.class, "bean");
|
||||
@@ -66,8 +66,8 @@ public class _DynHelloRestServlet2 extends SimpleRestServlet {
|
||||
}
|
||||
|
||||
@AuthIgnore
|
||||
@WebMapping(url = "/hello/query", comment = "查询Hello对象列表")
|
||||
@WebParam(name = "bean", type = HelloBean.class, comment = "过滤条件")
|
||||
@HttpMapping(url = "/hello/query", comment = "查询Hello对象列表")
|
||||
@HttpParam(name = "bean", type = HelloBean.class, comment = "过滤条件")
|
||||
public void query(HttpRequest req, HttpResponse resp) throws IOException {
|
||||
HelloService2 service = _servicemap == null ? _service : _servicemap.get(req.getHeader(Rest.REST_HEADER_RESOURCE_NAME, ""));
|
||||
HelloBean bean = req.getJsonParameter(HelloBean.class, "bean");
|
||||
@@ -81,8 +81,8 @@ public class _DynHelloRestServlet2 extends SimpleRestServlet {
|
||||
}
|
||||
|
||||
@AuthIgnore
|
||||
@WebMapping(url = "/hello/find/", comment = "根据id删除Hello对象")
|
||||
@WebParam(name = "#", type = int.class, comment = "Hello对象id")
|
||||
@HttpMapping(url = "/hello/find/", comment = "根据id删除Hello对象")
|
||||
@HttpParam(name = "#", type = int.class, comment = "Hello对象id")
|
||||
public void find(HttpRequest req, HttpResponse resp) throws IOException {
|
||||
HelloService2 service = _servicemap == null ? _service : _servicemap.get(req.getHeader(Rest.REST_HEADER_RESOURCE_NAME, ""));
|
||||
int id = Integer.parseInt(req.getRequstURILastPath());
|
||||
|
||||
Reference in New Issue
Block a user