远程函数:新增支持 JavaScript 脚本,方便动态配置业务逻辑以及作为 Serveless 服务

This commit is contained in:
TommyLemon 2022-11-17 01:33:01 +08:00
parent 64274fd3b5
commit 21df376a60
4 changed files with 148 additions and 17 deletions

View File

@ -5,7 +5,7 @@
<groupId>com.github.Tencent</groupId> <groupId>com.github.Tencent</groupId>
<artifactId>APIJSON</artifactId> <artifactId>APIJSON</artifactId>
<version>5.4.0</version> <version>5.5.0</version>
<packaging>jar</packaging> <packaging>jar</packaging>
<name>APIJSONORM</name> <name>APIJSONORM</name>

View File

@ -5,6 +5,7 @@ This source code is licensed under the Apache License Version 2.0.*/
package apijson.orm; package apijson.orm;
import java.io.FileReader;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashMap; import java.util.HashMap;
@ -12,7 +13,11 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import javax.activation.UnsupportedDataTypeException; import javax.activation.UnsupportedDataTypeException;
import javax.script.Invocable;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject; import com.alibaba.fastjson.JSONObject;
@ -27,11 +32,17 @@ import apijson.StringUtil;
public class AbstractFunctionParser implements FunctionParser { public class AbstractFunctionParser implements FunctionParser {
// private static final String TAG = "AbstractFunctionParser"; // private static final String TAG = "AbstractFunctionParser";
public static final int TYPE_REMOTE_FUNCTION = 0;
//public static final int TYPE_SQL_FUNCTION = 1;
public static final int TYPE_SCRIPT_FUNCTION = 1;
// <methodName, JSONObject> // <methodName, JSONObject>
// <isContain, <arguments:"array,key", tag:null, methods:null>> // <isContain, <arguments:"array,key", tag:null, methods:null>>
public static Map<String, JSONObject> FUNCTION_MAP; public static Map<String, JSONObject> FUNCTION_MAP;
public static Map<String, JSONObject> SCRIPT_MAP;
static { static {
FUNCTION_MAP = new HashMap<>(); FUNCTION_MAP = new HashMap<>();
SCRIPT_MAP = new HashMap<>();
} }
private RequestMethod method; private RequestMethod method;
@ -157,13 +168,19 @@ public class AbstractFunctionParser implements FunctionParser {
throw new UnsupportedOperationException("不允许调用远程函数 " + fb.getMethod() + " !"); throw new UnsupportedOperationException("不允许调用远程函数 " + fb.getMethod() + " !");
} }
int v = row.getIntValue("version"); int type = row.getIntValue("type");
if (parser.getVersion() < v) { if (type < TYPE_REMOTE_FUNCTION || type > TYPE_SCRIPT_FUNCTION) {
throw new UnsupportedOperationException("不允许 version = " + parser.getVersion() + " 的请求调用远程函数 " + fb.getMethod() + " ! 必须满足 version >= " + v + " !"); throw new UnsupportedOperationException("type = " + type + " 不合法!必须是 [0, 1, 2] 中的一个 !");
}
int version = row.getIntValue("version");
if (parser.getVersion() < version) {
throw new UnsupportedOperationException("不允许 version = " + parser.getVersion() + " 的请求调用远程函数 " + fb.getMethod() + " ! 必须满足 version >= " + version + " !");
} }
String t = row.getString("tag"); String tag = row.getString("tag");
if (t != null && t.equals(parser.getTag()) == false) { if (tag != null && tag.equals(parser.getTag()) == false) {
throw new UnsupportedOperationException("不允许 tag = " + parser.getTag() + " 的请求调用远程函数 " + fb.getMethod() + " ! 必须满足 tag = " + t + " !"); throw new UnsupportedOperationException("不允许 tag = " + parser.getTag() + " 的请求调用远程函数 " + fb.getMethod() + " ! 必须满足 tag = " + tag + " !");
} }
String[] methods = StringUtil.split(row.getString("methods")); String[] methods = StringUtil.split(row.getString("methods"));
List<String> ml = methods == null || methods.length <= 0 ? null : Arrays.asList(methods); List<String> ml = methods == null || methods.length <= 0 ? null : Arrays.asList(methods);
@ -172,7 +189,7 @@ public class AbstractFunctionParser implements FunctionParser {
} }
try { try {
return invoke(parser, fb.getMethod(), fb.getTypes(), fb.getValues()); return invoke(parser, fb.getMethod(), fb.getTypes(), fb.getValues(), currentObject, type);
} catch (Exception e) { } catch (Exception e) {
if (e instanceof NoSuchMethodException) { if (e instanceof NoSuchMethodException) {
throw new IllegalArgumentException("字符 " + function + " 对应的远程函数 " + getFunction(fb.getMethod(), fb.getKeys()) + " 不在后端工程的DemoFunction内" throw new IllegalArgumentException("字符 " + function + " 对应的远程函数 " + getFunction(fb.getMethod(), fb.getKeys()) + " 不在后端工程的DemoFunction内"
@ -201,17 +218,114 @@ public class AbstractFunctionParser implements FunctionParser {
* @param args * @param args
* @return * @return
*/ */
public static Object invoke(@NotNull AbstractFunctionParser parser, @NotNull String methodName, @NotNull Class<?>[] parameterTypes, @NotNull Object[] args) throws Exception { public static Object invoke(@NotNull AbstractFunctionParser parser, @NotNull String methodName
return parser.getClass().getMethod(methodName, parameterTypes).invoke(parser, args); , @NotNull Class<?>[] parameterTypes, @NotNull Object[] args) throws Exception {
return invoke(parser, methodName, parameterTypes, args, null, TYPE_REMOTE_FUNCTION);
}
public static Object invoke(@NotNull AbstractFunctionParser parser, @NotNull String methodName
, @NotNull Class<?>[] parameterTypes, @NotNull Object[] args, JSONObject currentObject, int type) throws Exception {
if (type == TYPE_SCRIPT_FUNCTION) {
return invokeScript(parser, methodName, parameterTypes, args, currentObject);
}
return parser.getClass().getMethod(methodName, parameterTypes).invoke(parser, args);
} }
/**解析函数 public static Invocable INVOCABLE;
* @param function public static ScriptEngine SCRIPT_ENGINE;
* @param request static {
* @param isSQLFunction try {
* @return System.setProperty("Dnashorn.args", "language=es6");
* @throws Exception System.setProperty("Dnashorn.args", "--language=es6");
*/ System.setProperty("-Dnashorn.args", "--language=es6");
/*获取执行JavaScript的执行引擎*/
SCRIPT_ENGINE = new ScriptEngineManager().getEngineByName("javascript");
INVOCABLE = (Invocable) SCRIPT_ENGINE;
} catch (Exception e) {
e.printStackTrace();
}
}
public static Object invokeScript(@NotNull AbstractFunctionParser parser, @NotNull String methodName
, @NotNull Class<?>[] parameterTypes, @NotNull Object[] args, JSONObject currentObject) throws Exception {
JSONObject row = SCRIPT_MAP.get(methodName);
if (row == null) {
throw new UnsupportedOperationException("调用远程函数脚本 " + methodName + " 不存在!");
}
String script = row.getString("script");
//SCRIPT_ENGINE.eval(script);
//Object[] newArgs = args == null || args.length <= 0 ? null : new Object[args.length];
SCRIPT_ENGINE.eval(script);
// APIJSON 远程函数不应该支持
//if (row.getBooleanValue("simple")) {
// return SCRIPT_ENGINE.eval(script);
//}
Object result;
if (args == null || args.length <= 0) {
result = INVOCABLE.invokeFunction(methodName);
}
else {
//args[0] = JSON.toJSONString(args[0]); // Java js 函数只支持传基本类型改用 invokeMethod
//for (int i = 0; i < args.length; i++) {
// Object a = currentObject == null ? null : currentObject.get(args[i]);
// newArgs[i] = a == null || apijson.JSON.isBooleanOrNumberOrString(a) ? a : JSON.toJSONString(a);
//}
// TODO 先试试这个
result = INVOCABLE.invokeFunction(methodName, args);
//result = INVOCABLE.invokeMethod(args[0], methodName, args);
//switch (newArgs.length) {
// case 1:
// result = INVOCABLE.invokeFunction(methodName, newArgs[0]);
// break;
// case 2:
// result = INVOCABLE.invokeFunction(methodName, newArgs[0], newArgs[1]);
// break;
// case 3:
// result = INVOCABLE.invokeFunction(methodName, newArgs[0], newArgs[1], newArgs[2]);
// break;
// case 4:
// result = INVOCABLE.invokeFunction(methodName, newArgs[0], newArgs[1], newArgs[2], newArgs[3]);
// break;
// case 5:
// result = INVOCABLE.invokeFunction(methodName, newArgs[0], newArgs[1], newArgs[2], newArgs[3], newArgs[4]);
// break;
// case 6:
// result = INVOCABLE.invokeFunction(methodName, newArgs[0], newArgs[1], newArgs[2], newArgs[3], newArgs[4], newArgs[5]);
// break;
// case 7:
// result = INVOCABLE.invokeFunction(methodName, newArgs[0], newArgs[1], newArgs[2], newArgs[3], newArgs[4], newArgs[5], newArgs[6]);
// break;
// case 8:
// result = INVOCABLE.invokeFunction(methodName, newArgs[0], newArgs[1], newArgs[2], newArgs[3], newArgs[4], newArgs[5], newArgs[6], newArgs[7]);
// break;
// case 9:
// result = INVOCABLE.invokeFunction(methodName, newArgs[0], newArgs[1], newArgs[2], newArgs[3], newArgs[4], newArgs[5], newArgs[6], newArgs[7], newArgs[8]);
// break;
// case 10:
// result = INVOCABLE.invokeFunction(methodName, newArgs[0], newArgs[1], newArgs[2], newArgs[3], newArgs[4], newArgs[5], newArgs[6], newArgs[7], newArgs[8], newArgs[9]);
// break;
//}
}
System.out.println("invokeScript " + methodName + "(..) >> result = " + result);
return result;
}
/**解析函数
* @param function
* @param request
* @param isSQLFunction
* @return
* @throws Exception
*/
@NotNull @NotNull
public static FunctionBean parseFunction(@NotNull String function, @NotNull JSONObject request, boolean isSQLFunction) throws Exception { public static FunctionBean parseFunction(@NotNull String function, @NotNull JSONObject request, boolean isSQLFunction) throws Exception {

View File

@ -60,6 +60,7 @@ import apijson.orm.model.Column;
import apijson.orm.model.Document; import apijson.orm.model.Document;
import apijson.orm.model.ExtendedProperty; import apijson.orm.model.ExtendedProperty;
import apijson.orm.model.Function; import apijson.orm.model.Function;
import apijson.orm.model.Script;
import apijson.orm.model.PgAttribute; import apijson.orm.model.PgAttribute;
import apijson.orm.model.PgClass; import apijson.orm.model.PgClass;
import apijson.orm.model.Request; import apijson.orm.model.Request;
@ -151,6 +152,7 @@ public abstract class AbstractVerifier<T extends Object> implements Verifier<T>,
SYSTEM_ACCESS_MAP.put(Access.class.getSimpleName(), getAccessMap(Access.class.getAnnotation(MethodAccess.class))); SYSTEM_ACCESS_MAP.put(Access.class.getSimpleName(), getAccessMap(Access.class.getAnnotation(MethodAccess.class)));
SYSTEM_ACCESS_MAP.put(Function.class.getSimpleName(), getAccessMap(Function.class.getAnnotation(MethodAccess.class))); SYSTEM_ACCESS_MAP.put(Function.class.getSimpleName(), getAccessMap(Function.class.getAnnotation(MethodAccess.class)));
SYSTEM_ACCESS_MAP.put(Script.class.getSimpleName(), getAccessMap(Script.class.getAnnotation(MethodAccess.class)));
SYSTEM_ACCESS_MAP.put(Request.class.getSimpleName(), getAccessMap(Request.class.getAnnotation(MethodAccess.class))); SYSTEM_ACCESS_MAP.put(Request.class.getSimpleName(), getAccessMap(Request.class.getAnnotation(MethodAccess.class)));
if (Log.DEBUG) { if (Log.DEBUG) {

View File

@ -0,0 +1,15 @@
/*Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
This source code is licensed under the Apache License Version 2.0.*/
package apijson.orm.model;
import apijson.MethodAccess;
/**代码脚本
* @author Lemon
*/
@MethodAccess(POST = {}, PUT = {}, DELETE = {})
public class Script {
}