远程函数:新增支持 JavaScript 脚本,方便动态配置业务逻辑以及作为 Serveless 服务
This commit is contained in:
parent
64274fd3b5
commit
21df376a60
@ -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>
|
||||||
|
@ -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 {
|
||||||
|
|
||||||
|
@ -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) {
|
||||||
|
15
APIJSONORM/src/main/java/apijson/orm/model/Script.java
Normal file
15
APIJSONORM/src/main/java/apijson/orm/model/Script.java
Normal 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 {
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user