From e6d6ec93ef9382900689672a336ba4d198643a56 Mon Sep 17 00:00:00 2001 From: cloudAndMonkey Date: Mon, 16 Jan 2023 11:16:01 +0800 Subject: [PATCH 1/5] =?UTF-8?q?function=E6=94=AF=E6=8C=81=E8=84=9A?= =?UTF-8?q?=E6=9C=AC=E5=BC=95=E6=93=8E=EF=BC=8C=E6=AF=94=E5=A6=82JavaScrip?= =?UTF-8?q?t=E3=80=81lua=E7=AD=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit https://github.com/Tencent/APIJSON/issues/495 --- .../apijson/orm/AbstractFunctionParser.java | 129 ++---------------- .../main/java/apijson/orm/AbstractParser.java | 4 +- 2 files changed, 16 insertions(+), 117 deletions(-) diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractFunctionParser.java b/APIJSONORM/src/main/java/apijson/orm/AbstractFunctionParser.java index 277e08aa6..0dde62824 100755 --- a/APIJSONORM/src/main/java/apijson/orm/AbstractFunctionParser.java +++ b/APIJSONORM/src/main/java/apijson/orm/AbstractFunctionParser.java @@ -26,7 +26,9 @@ import apijson.NotNull; import apijson.RequestMethod; import apijson.StringUtil; +import apijson.framework.APIJSONApplication; import apijson.orm.exception.UnsupportedDataTypeException; +import apijson.orm.script.ScriptExecutor; /**可远程调用的函数类 * @author Lemon @@ -47,11 +49,11 @@ public class AbstractFunctionParser implements FunctionParser { // // > + public static Map SCRIPT_EXECUTOR_MAP; public static Map FUNCTION_MAP; - public static Map SCRIPT_MAP; static { FUNCTION_MAP = new HashMap<>(); - SCRIPT_MAP = new HashMap<>(); + SCRIPT_EXECUTOR_MAP = new HashMap<>(); } private RequestMethod method; @@ -198,20 +200,10 @@ public static Object invoke(@NotNull AbstractFunctionParser parser, @NotNull Str throw new UnsupportedOperationException("language = " + language + " 不合法!AbstractFunctionParser.ENABLE_SCRIPT_FUNCTION" + " == false 时不支持远程函数中的脚本形式!如需支持则设置 AbstractFunctionParser.ENABLE_SCRIPT_FUNCTION = true !"); } - ScriptEngine engine = lang == null ? null : SCRIPT_ENGINE_MAP.get(lang); - if (lang != null) { - if (engine == null) { - engine = new ScriptEngineManager().getEngineByName(lang); - } - if (engine == null) { - engine = new ScriptEngineManager(null).getEngineByName(lang); - } - if (engine == null) { - throw new ClassNotFoundException("找不到脚本语言 " + language + " 对应的执行引擎!请先依赖相关库并在后端 ScriptEngineManager 中注册!"); - } - - SCRIPT_ENGINE_MAP.put(lang, engine); - } + + if (lang != null && SCRIPT_EXECUTOR_MAP.get(lang) == null) { + throw new ClassNotFoundException("找不到脚本语言 " + lang + " 对应的执行引擎!请先依赖相关库并在后端 APIJSONFunctionParser 中注册!"); + } int version = row.getIntValue("version"); if (parser.getVersion() < version) { @@ -228,7 +220,7 @@ public static Object invoke(@NotNull AbstractFunctionParser parser, @NotNull Str } try { - return invoke(parser, fb.getMethod(), fb.getTypes(), fb.getValues(), row.getString("returnType"), currentObject, engine); + return invoke(parser, fb.getMethod(), fb.getTypes(), fb.getValues(), row.getString("returnType"), currentObject, SCRIPT_EXECUTOR_MAP.get(lang)); } catch (Exception e) { if (e instanceof NoSuchMethodException) { @@ -278,9 +270,9 @@ public static Object invoke(@NotNull AbstractFunctionParser parser, @NotNull Str */ public static Object invoke(@NotNull AbstractFunctionParser parser, @NotNull String methodName , @NotNull Class[] parameterTypes, @NotNull Object[] args, String returnType - , JSONObject currentObject, ScriptEngine engine) throws Exception { - if (engine != null) { - return invokeScript(parser, methodName, parameterTypes, args, returnType, currentObject, engine); + , JSONObject currentObject, ScriptExecutor scriptExecutor) throws Exception { + if (scriptExecutor != null) { + return invokeScript(parser, methodName, parameterTypes, args, returnType, currentObject, scriptExecutor); } Method m = parser.getClass().getMethod(methodName, parameterTypes); // 不用判空,拿不到就会抛异常 @@ -303,29 +295,6 @@ public static Object invoke(@NotNull AbstractFunctionParser parser, @NotNull Str return m.invoke(parser, args); } - public static Invocable INVOCABLE; - public static ScriptEngine SCRIPT_ENGINE; - public static Map SCRIPT_ENGINE_MAP; - static { - try { - System.setProperty("Dnashorn.args", "language=es6"); - System.setProperty("Dnashorn.args", "--language=es6"); - System.setProperty("-Dnashorn.args", "--language=es6"); - - /*获取执行JavaScript的执行引擎*/ - SCRIPT_ENGINE = new ScriptEngineManager().getEngineByName("javascript"); - INVOCABLE = (Invocable) SCRIPT_ENGINE; - - SCRIPT_ENGINE_MAP = new HashMap<>(); - SCRIPT_ENGINE_MAP.put("JavaScript", SCRIPT_ENGINE); - SCRIPT_ENGINE_MAP.put("javascript", SCRIPT_ENGINE); - SCRIPT_ENGINE_MAP.put("js", SCRIPT_ENGINE); - } - catch (Exception e) { - e.printStackTrace(); - } - } - /**Java 调用 JavaScript 函数 * @param parser * @param methodName @@ -337,78 +306,8 @@ public static Object invoke(@NotNull AbstractFunctionParser parser, @NotNull Str * @throws Exception */ public static Object invokeScript(@NotNull AbstractFunctionParser parser, @NotNull String methodName - , @NotNull Class[] parameterTypes, @NotNull Object[] args, String returnType, JSONObject currentObject, ScriptEngine engine) throws Exception { - JSONObject row = SCRIPT_MAP.get(methodName); - if (row == null) { - throw new UnsupportedOperationException("调用的远程函数脚本 " + methodName + " 不存在!"); - } - - String script = row.getString("script"); - - if (engine == null) { - engine = SCRIPT_ENGINE; - } - engine.eval(script); // 必要,未执行导致下方 INVOCABLE.invokeFunction 报错 NoSuchMethod - - //Object[] newArgs = args == null || args.length <= 0 ? null : new Object[args.length]; - - // APIJSON 远程函数不应该支持 - //if (row.getBooleanValue("simple")) { - // return SCRIPT_ENGINE.eval(script); - //} - - Invocable invocable = engine instanceof Invocable ? (Invocable) engine : null; - - 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); - //} - - // 支持 JSONObject - 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; - //} - } - + , @NotNull Class[] parameterTypes, @NotNull Object[] args, String returnType, JSONObject currentObject, ScriptExecutor scriptExecutor) throws Exception { + Object result = scriptExecutor.execute(parser, currentObject, methodName, args); if (Log.DEBUG && result != null) { Class rt = result.getClass(); // 作为远程函数的 js 类型应该只有 JSON 的几种类型 String fullReturnType = (StringUtil.isSmallName(returnType) diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractParser.java b/APIJSONORM/src/main/java/apijson/orm/AbstractParser.java index b4499ee2f..c379fc22e 100755 --- a/APIJSONORM/src/main/java/apijson/orm/AbstractParser.java +++ b/APIJSONORM/src/main/java/apijson/orm/AbstractParser.java @@ -2120,8 +2120,8 @@ private JSONObject batchVerify(RequestMethod method, String tag, int version, St case apijson.JSONObject.KEY_DATABASE: object_attributes_map.put(apijson.JSONObject.KEY_DATABASE, objAttrJson.getString(objAttr)); break; - case apijson.JSONObject.VERSION: - object_attributes_map.put(apijson.JSONObject.VERSION, objAttrJson.getString(objAttr)); + case JSONRequest.KEY_VERSION: + object_attributes_map.put(JSONRequest.KEY_VERSION, objAttrJson.getString(objAttr)); break; case apijson.JSONObject.KEY_ROLE: object_attributes_map.put(apijson.JSONObject.KEY_ROLE, objAttrJson.getString(objAttr)); From 017d876357ad4566ebffd9540b4f416ff40510fb Mon Sep 17 00:00:00 2001 From: cloudAndMonkey Date: Mon, 16 Jan 2023 11:21:36 +0800 Subject: [PATCH 2/5] =?UTF-8?q?apijson=20function=E6=94=AF=E6=8C=81?= =?UTF-8?q?=E8=84=9A=E6=9C=AC=E5=BC=95=E6=93=8E=EF=BC=8C=E6=AF=94=E5=A6=82?= =?UTF-8?q?JavaScript=E3=80=81lua=E7=AD=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit https://github.com/Tencent/APIJSON/issues/495 --- .../orm/script/JavaScriptExecutor.java | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 APIJSONORM/src/main/java/apijson/orm/script/JavaScriptExecutor.java diff --git a/APIJSONORM/src/main/java/apijson/orm/script/JavaScriptExecutor.java b/APIJSONORM/src/main/java/apijson/orm/script/JavaScriptExecutor.java new file mode 100644 index 000000000..893ac6d11 --- /dev/null +++ b/APIJSONORM/src/main/java/apijson/orm/script/JavaScriptExecutor.java @@ -0,0 +1,27 @@ +package apijson.orm.script; + +import com.alibaba.fastjson.JSONObject; + +import apijson.orm.AbstractFunctionParser; + +/** + * JavaScript脚本语言的执行器实现 + */ +public class JavaScriptExecutor extends JSR223ScriptExecutor { + + @Override + protected String scriptEngineName() { + return "javascript"; + } + + @Override + protected Object extendParameter(AbstractFunctionParser parser, JSONObject currentObject, String methodName, Object[] args) { + return null; + } + + @Override + protected boolean isLockScript(String methodName) { + return false; + } + +} From a4c2f4cddb5d78bdca8a06f7ce9ebc640d22db7f Mon Sep 17 00:00:00 2001 From: cloudAndMonkey Date: Mon, 16 Jan 2023 11:22:12 +0800 Subject: [PATCH 3/5] =?UTF-8?q?apijson=20function=E6=94=AF=E6=8C=81?= =?UTF-8?q?=E8=84=9A=E6=9C=AC=E5=BC=95=E6=93=8E=EF=BC=8C=E6=AF=94=E5=A6=82?= =?UTF-8?q?JavaScript=E3=80=81lua=E7=AD=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit https://github.com/Tencent/APIJSON/issues/495 --- .../orm/script/JSR223ScriptExecutor.java | 86 +++++++++++++++++++ .../apijson/orm/script/ScriptExecutor.java | 17 ++++ 2 files changed, 103 insertions(+) create mode 100644 APIJSONORM/src/main/java/apijson/orm/script/JSR223ScriptExecutor.java create mode 100644 APIJSONORM/src/main/java/apijson/orm/script/ScriptExecutor.java diff --git a/APIJSONORM/src/main/java/apijson/orm/script/JSR223ScriptExecutor.java b/APIJSONORM/src/main/java/apijson/orm/script/JSR223ScriptExecutor.java new file mode 100644 index 000000000..b71f0fa09 --- /dev/null +++ b/APIJSONORM/src/main/java/apijson/orm/script/JSR223ScriptExecutor.java @@ -0,0 +1,86 @@ +package apijson.orm.script; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import javax.script.Bindings; +import javax.script.Compilable; +import javax.script.CompiledScript; +import javax.script.ScriptEngine; +import javax.script.ScriptEngineManager; +import javax.script.SimpleBindings; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.alibaba.fastjson.JSONObject; + +import apijson.orm.AbstractFunctionParser; + +/** + * JSR223 script engine的统一实现抽象类 + */ +public abstract class JSR223ScriptExecutor implements ScriptExecutor { + + protected final Logger log = LoggerFactory.getLogger(this.getClass()); + + protected ScriptEngine scriptEngine; + + private final Map compiledScriptMap = new ConcurrentHashMap<>(); + + @Override + public ScriptExecutor init() { + ScriptEngineManager scriptEngineManager = new ScriptEngineManager(); + scriptEngine = scriptEngineManager.getEngineByName(scriptEngineName()); + return this; + } + + protected abstract String scriptEngineName(); + + protected abstract Object extendParameter(AbstractFunctionParser parser, JSONObject currentObject, String methodName, Object[] args); + + protected abstract boolean isLockScript(String methodName); + + protected String convertScript(String script) { + return script; + } + + @Override + public void load(String name, String script) { + try { + CompiledScript compiledScript = ((Compilable) scriptEngine).compile(convertScript(script)); + compiledScriptMap.put(name, compiledScript); + } catch (Exception e) { + e.printStackTrace(); + } + + } + + @Override + public Object execute(AbstractFunctionParser parser, JSONObject currentObject, String methodName, Object[] args) throws Exception { + CompiledScript compiledScript = compiledScriptMap.get(methodName); + Bindings bindings = new SimpleBindings(); + // 往脚本上下文里放入元数据 + // 把 RequestMethod method, String tag, int version, @NotNull JSONObject request, + // HttpSession session 等参数作为全局参数传进去供脚本使用 + + // 加载扩展属性 + Object extendParameter = this.extendParameter(parser, currentObject, methodName, args); + if(extendParameter != null) { + bindings.put("extParam", extendParameter); + } + + Map metaMap = new HashMap<>(); + metaMap.put("version", parser.getVersion()); + metaMap.put("tag", parser.getTag()); + metaMap.put("args", args); + bindings.put("_meta", metaMap); + return compiledScript.eval(bindings); + } + + @Override + public void cleanCache() { + compiledScriptMap.clear(); + } +} diff --git a/APIJSONORM/src/main/java/apijson/orm/script/ScriptExecutor.java b/APIJSONORM/src/main/java/apijson/orm/script/ScriptExecutor.java new file mode 100644 index 000000000..b9296d153 --- /dev/null +++ b/APIJSONORM/src/main/java/apijson/orm/script/ScriptExecutor.java @@ -0,0 +1,17 @@ +package apijson.orm.script; + +import com.alibaba.fastjson.JSONObject; + +import apijson.orm.AbstractFunctionParser; + +public interface ScriptExecutor { + + ScriptExecutor init(); + + void load(String name, String script); + + Object execute(AbstractFunctionParser parser, JSONObject currentObject, String methodName, Object[] args) throws Exception; + + void cleanCache(); + +} From 22ba3305ceb713ff1c8f4f068bada8a08acbecd5 Mon Sep 17 00:00:00 2001 From: cloudAndMonkey Date: Mon, 16 Jan 2023 11:42:46 +0800 Subject: [PATCH 4/5] =?UTF-8?q?=20=E5=88=A0=E9=99=A4=E6=97=A0=E6=95=88?= =?UTF-8?q?=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/apijson/orm/script/JSR223ScriptExecutor.java | 6 ------ 1 file changed, 6 deletions(-) diff --git a/APIJSONORM/src/main/java/apijson/orm/script/JSR223ScriptExecutor.java b/APIJSONORM/src/main/java/apijson/orm/script/JSR223ScriptExecutor.java index b71f0fa09..be0afc511 100644 --- a/APIJSONORM/src/main/java/apijson/orm/script/JSR223ScriptExecutor.java +++ b/APIJSONORM/src/main/java/apijson/orm/script/JSR223ScriptExecutor.java @@ -11,9 +11,6 @@ import javax.script.ScriptEngineManager; import javax.script.SimpleBindings; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import com.alibaba.fastjson.JSONObject; import apijson.orm.AbstractFunctionParser; @@ -22,9 +19,6 @@ * JSR223 script engine的统一实现抽象类 */ public abstract class JSR223ScriptExecutor implements ScriptExecutor { - - protected final Logger log = LoggerFactory.getLogger(this.getClass()); - protected ScriptEngine scriptEngine; private final Map compiledScriptMap = new ConcurrentHashMap<>(); From 9db1b50f9103e4e993385f84342c2df0ad4903bc Mon Sep 17 00:00:00 2001 From: cloudAndMonkey Date: Mon, 16 Jan 2023 19:08:53 +0800 Subject: [PATCH 5/5] =?UTF-8?q?=E5=81=87=E5=88=A0=E9=99=A4=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0null=20=E5=88=A4=E6=96=AD,=E9=81=BF=E5=85=8D=E5=AE=A2?= =?UTF-8?q?=E6=88=B7=E7=AB=AFjson=E4=BC=A0=E9=80=92=E4=B8=8D=E5=AD=98?= =?UTF-8?q?=E5=9C=A8=E7=9A=84=E5=AF=B9=E8=B1=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java index cac9dd328..0f569a2df 100755 --- a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java +++ b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java @@ -5004,7 +5004,7 @@ else if (userId instanceof Subquery) {} if(config.isFakeDelete()) { // 查询Access假删除 Map accessFakeDeleteMap = AbstractVerifier.ACCESS_FAKE_DELETE_MAP.get(config.getTable()); - if (StringUtil.isNotEmpty(accessFakeDeleteMap.get("deletedKey"), true)) { + if (accessFakeDeleteMap != null && StringUtil.isNotEmpty(accessFakeDeleteMap.get("deletedKey"), true)) { Map fakeDeleteMap = new HashMap<>(); boolean isFakeDelete = true; if(method != DELETE) {