All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.redkale.net.http.HttpServlet Maven / Gradle / Ivy

/*
 * 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.IOException;
import java.lang.reflect.Method;
import java.nio.ByteBuffer;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.BiFunction;
import org.redkale.asm.*;
import static org.redkale.asm.ClassWriter.COMPUTE_FRAMES;
import static org.redkale.asm.Opcodes.*;
import org.redkale.net.*;
import org.redkale.service.RetResult;
import org.redkale.util.*;

/**
 * HTTP版的Servlet, 执行顺序 execute --> preExecute --> authenticate --> HttpMapping对应的方法
 *
 * 

* 详情见: https://redkale.org * * @author zhangjx */ public class HttpServlet extends Servlet { public static final int RET_SERVER_ERROR = 1200_0001; public static final int RET_METHOD_ERROR = 1200_0002; String _prefix = ""; //当前HttpServlet的path前缀 HashMap _tmpentrys; //Rest生成时赋值, 字段名Rest有用到 private Map.Entry[] mappings; //字段名Rest有用到 //这里不能直接使用HttpServlet,会造成死循环初始化HttpServlet private final Servlet authSuccessServlet = new Servlet() { @Override public void execute(HttpRequest request, HttpResponse response) throws IOException { InnerActionEntry entry = (InnerActionEntry) 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 preSuccessServlet = new Servlet() { @Override public void execute(HttpRequest request, HttpResponse response) throws IOException { for (Map.Entry en : mappings) { if (request.getRequestURI().startsWith(en.getKey())) { InnerActionEntry 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.auth) { response.thenEvent(authSuccessServlet); authenticate(request, response); } else { authSuccessServlet.execute(request, response); } return; } } throw new IOException(this.getClass().getName() + " not found method for URI(" + request.getRequestURI() + ")"); } }; @SuppressWarnings("unchecked") void preInit(HttpContext context, AnyValue config) { if (this.mappings != null) return; //无需重复preInit String path = _prefix == null ? "" : _prefix; WebServlet ws = this.getClass().getAnnotation(WebServlet.class); if (ws != null && !ws.repair()) path = ""; HashMap map = this._tmpentrys != null ? this._tmpentrys : loadActionEntry(); this.mappings = new Map.Entry[map.size()]; int i = -1; for (Map.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) { } /** *

* 预执行方法,在execute方法之前运行,设置当前用户信息,或者加入常规统计和基础检测,例如 :
*

     *      @Override
     *      public void preExecute(final HttpRequest request, final HttpResponse response) throws IOException {
     *          //设置当前用户信息
     *          final String sessionid = request.getSessionid(false);
     *          if (sessionid != null) request.setCurrentUser(userService.current(sessionid));
     *
     *          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();
     *      }
     * 
*

* * @param request HttpRequest * @param response HttpResponse * * @throws IOException IOException */ protected void preExecute(HttpRequest request, HttpResponse response) throws IOException { response.nextEvent(); } /** *

* 用户登录或权限验证, 注解为@HttpMapping.auth == true 的方法会执行authenticate方法, 若验证成功则必须调用response.nextEvent();进行下一步操作, 例如:
*

     *      @Override
     *      public void authenticate(HttpRequest request, HttpResponse response) throws IOException {
     *          UserInfo info = request.currentUser();
     *          if (info == null) {
     *              response.finishJson(RET_UNLOGIN);
     *              return;
     *          } else if (!info.checkAuth(request.getModuleid(), request.getActionid())) {
     *              response.finishJson(RET_AUTHILLEGAL);
     *              return;
     *          }
     *          response.nextEvent();
     *      }
     * 
*

* * * @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.thenEvent(preSuccessServlet); preExecute(request, response); } private HashMap loadActionEntry() { WebServlet module = this.getClass().getAnnotation(WebServlet.class); final int serviceid = module == null ? 0 : module.moduleid(); final HashMap map = new HashMap<>(); HashMap 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 InnerActionEntry(serviceid, actionid, name, methods, method, createActionServlet(method))); } } while ((clz = clz.getSuperclass()) != HttpServlet.class); return map; } protected static final class InnerActionEntry { InnerActionEntry(int moduleid, int actionid, String name, String[] methods, Method method, HttpServlet servlet) { this(moduleid, actionid, name, methods, method, auth(method), cacheseconds(method), servlet); } //供Rest类使用,参数不能随便更改 public InnerActionEntry(int moduleid, int actionid, String name, String[] methods, Method method, boolean auth, int cacheseconds, HttpServlet servlet) { this.moduleid = moduleid; this.actionid = actionid; this.name = name; this.methods = methods; this.method = method; //rest构建会为null this.servlet = servlet; this.auth = auth; this.cacheseconds = cacheseconds; 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; } private static boolean auth(Method method) { HttpMapping mapping = method.getAnnotation(HttpMapping.class); return mapping == null || mapping.auth(); } private static int cacheseconds(Method method) { HttpMapping mapping = method.getAnnotation(HttpMapping.class); return mapping == null ? 0 : mapping.cacheseconds(); } boolean isNeedCheck() { return this.moduleid != 0 || this.actionid != 0; } boolean checkMethod(final String reqMethod) { if (methods.length == 0) return true; for (String m : methods) { if (reqMethod.equalsIgnoreCase(m)) return true; } return false; } final BiFunction cacheHandler; final ConcurrentHashMap cache; final int cacheseconds; final boolean auth; final int moduleid; final int actionid; final String name; final String[] methods; final Method method; final HttpServlet servlet; } private HttpServlet createActionServlet(final Method method) { //------------------------------------------------------------------------------ final String supDynName = HttpServlet.class.getName().replace('.', '/'); final String interName = this.getClass().getName().replace('.', '/'); final String interDesc = org.redkale.asm.Type.getDescriptor(this.getClass()); final String requestSupDesc = org.redkale.asm.Type.getDescriptor(Request.class); final String responseSupDesc = org.redkale.asm.Type.getDescriptor(Response.class); final String requestDesc = org.redkale.asm.Type.getDescriptor(HttpRequest.class); final String responseDesc = org.redkale.asm.Type.getDescriptor(HttpResponse.class); String newDynName = interName + "_Dyn_" + method.getName(); int i = 0; for (;;) { try { Thread.currentThread().getContextClassLoader().loadClass(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, "", "()V", null, null)); //mv.setDebug(true); mv.visitVarInsn(ALOAD, 0); mv.visitMethodInsn(INVOKESPECIAL, supDynName, "", "()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.getDeclaredConstructor().newInstance(); instance.getClass().getField(factfield).set(instance, this); return instance; } catch (Exception ex) { throw new RuntimeException(ex); } } 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; } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy