package core.framework.impl.web; import core.framework.api.util.Exceptions; import core.framework.api.web.Controller; import core.framework.api.web.Request; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.security.AccessController; import java.security.PrivilegedAction; /** * due to Java 8 doesn't provide formal way to reflect lambda method reference, we uses sun internal API for now * and wait for JDK update in future * * @author neo */ class ControllerInspector { private static final Method GET_CONSTANT_POOL; private static final Method CONTROLLER_METHOD; private static final int JDK_8_MINOR_VERSION; static { try { GET_CONSTANT_POOL = Class.class.getDeclaredMethod("getConstantPool"); AccessController.doPrivileged((PrivilegedAction<Method>) () -> { GET_CONSTANT_POOL.setAccessible(true); return GET_CONSTANT_POOL; }); CONTROLLER_METHOD = Controller.class.getDeclaredMethod("execute", Request.class); } catch (NoSuchMethodException e) { throw new Error("failed to initialize controller inspector, please contact arch team", e); } String jdkVersion = System.getProperty("java.version"); if (!jdkVersion.startsWith("1.8.0_")) throw Exceptions.error("unsupported jdk version, please contact arch team, jdk={}", jdkVersion); JDK_8_MINOR_VERSION = Integer.parseInt(jdkVersion.substring(6)); } final String targetClassName; final String targetMethodName; final Method targetMethod; ControllerInspector(Controller controller) { Class<?> controllerClass = controller.getClass(); try { if (!controllerClass.isSynthetic()) { targetMethod = controllerClass.getMethod(CONTROLLER_METHOD.getName(), CONTROLLER_METHOD.getParameterTypes()); targetClassName = controllerClass.getCanonicalName(); targetMethodName = CONTROLLER_METHOD.getName(); } else { Object constantPool = GET_CONSTANT_POOL.invoke(controllerClass); // constantPool is sun.reflect.ConstantPool, it can be changed in future JDK Method getSize = constantPool.getClass().getMethod("getSize"); int size = (int) getSize.invoke(constantPool); Method getMemberRefInfoAt = constantPool.getClass().getMethod("getMemberRefInfoAt", int.class); String[] methodRefInfo = (String[]) getMemberRefInfoAt.invoke(constantPool, methodRefIndex(size)); Class<?> targetClass = Class.forName(methodRefInfo[0].replaceAll("/", ".")); targetClassName = targetClass.getCanonicalName(); targetMethodName = methodRefInfo[1]; if (targetMethodName.contains("$")) { targetMethod = controllerClass.getMethod(CONTROLLER_METHOD.getName(), CONTROLLER_METHOD.getParameterTypes()); } else { targetMethod = targetClass.getMethod(targetMethodName, CONTROLLER_METHOD.getParameterTypes()); } } } catch (NoSuchMethodException | InvocationTargetException | ClassNotFoundException | IllegalAccessException e) { throw new Error("failed to inspect controller, please contact arch team", e); } } private int methodRefIndex(int size) { if (JDK_8_MINOR_VERSION >= 60) return size - 3; // from 1.8.0_60, the index of methodRefInfo is different from previous jdk return size - 2; } }