Skip to content

Commit 44ff47a

Browse files
authored
Merge pull request #500 from cloudAndMonkey/master
apijson function支持脚本引擎,比如JavaScript、lua等
2 parents ced3fb9 + 9db1b50 commit 44ff47a

File tree

6 files changed

+141
-118
lines changed

6 files changed

+141
-118
lines changed

APIJSONORM/src/main/java/apijson/orm/AbstractFunctionParser.java

Lines changed: 14 additions & 115 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,9 @@
2626
import apijson.NotNull;
2727
import apijson.RequestMethod;
2828
import apijson.StringUtil;
29+
import apijson.framework.APIJSONApplication;
2930
import apijson.orm.exception.UnsupportedDataTypeException;
31+
import apijson.orm.script.ScriptExecutor;
3032

3133
/**可远程调用的函数类
3234
* @author Lemon
@@ -47,11 +49,11 @@ public class AbstractFunctionParser implements FunctionParser {
4749

4850
// <methodName, JSONObject>
4951
// <isContain, <arguments:"array,key", tag:null, methods:null>>
52+
public static Map<String, ScriptExecutor> SCRIPT_EXECUTOR_MAP;
5053
public static Map<String, JSONObject> FUNCTION_MAP;
51-
public static Map<String, JSONObject> SCRIPT_MAP;
5254
static {
5355
FUNCTION_MAP = new HashMap<>();
54-
SCRIPT_MAP = new HashMap<>();
56+
SCRIPT_EXECUTOR_MAP = new HashMap<>();
5557
}
5658

5759
private RequestMethod method;
@@ -198,20 +200,10 @@ public static Object invoke(@NotNull AbstractFunctionParser parser, @NotNull Str
198200
throw new UnsupportedOperationException("language = " + language + " 不合法!AbstractFunctionParser.ENABLE_SCRIPT_FUNCTION" +
199201
" == false 时不支持远程函数中的脚本形式!如需支持则设置 AbstractFunctionParser.ENABLE_SCRIPT_FUNCTION = true !");
200202
}
201-
ScriptEngine engine = lang == null ? null : SCRIPT_ENGINE_MAP.get(lang);
202-
if (lang != null) {
203-
if (engine == null) {
204-
engine = new ScriptEngineManager().getEngineByName(lang);
205-
}
206-
if (engine == null) {
207-
engine = new ScriptEngineManager(null).getEngineByName(lang);
208-
}
209-
if (engine == null) {
210-
throw new ClassNotFoundException("找不到脚本语言 " + language + " 对应的执行引擎!请先依赖相关库并在后端 ScriptEngineManager 中注册!");
211-
}
212-
213-
SCRIPT_ENGINE_MAP.put(lang, engine);
214-
}
203+
204+
if (lang != null && SCRIPT_EXECUTOR_MAP.get(lang) == null) {
205+
throw new ClassNotFoundException("找不到脚本语言 " + lang + " 对应的执行引擎!请先依赖相关库并在后端 APIJSONFunctionParser 中注册!");
206+
}
215207

216208
int version = row.getIntValue("version");
217209
if (parser.getVersion() < version) {
@@ -228,7 +220,7 @@ public static Object invoke(@NotNull AbstractFunctionParser parser, @NotNull Str
228220
}
229221

230222
try {
231-
return invoke(parser, fb.getMethod(), fb.getTypes(), fb.getValues(), row.getString("returnType"), currentObject, engine);
223+
return invoke(parser, fb.getMethod(), fb.getTypes(), fb.getValues(), row.getString("returnType"), currentObject, SCRIPT_EXECUTOR_MAP.get(lang));
232224
}
233225
catch (Exception e) {
234226
if (e instanceof NoSuchMethodException) {
@@ -278,9 +270,9 @@ public static Object invoke(@NotNull AbstractFunctionParser parser, @NotNull Str
278270
*/
279271
public static Object invoke(@NotNull AbstractFunctionParser parser, @NotNull String methodName
280272
, @NotNull Class<?>[] parameterTypes, @NotNull Object[] args, String returnType
281-
, JSONObject currentObject, ScriptEngine engine) throws Exception {
282-
if (engine != null) {
283-
return invokeScript(parser, methodName, parameterTypes, args, returnType, currentObject, engine);
273+
, JSONObject currentObject, ScriptExecutor scriptExecutor) throws Exception {
274+
if (scriptExecutor != null) {
275+
return invokeScript(parser, methodName, parameterTypes, args, returnType, currentObject, scriptExecutor);
284276
}
285277

286278
Method m = parser.getClass().getMethod(methodName, parameterTypes); // 不用判空,拿不到就会抛异常
@@ -303,29 +295,6 @@ public static Object invoke(@NotNull AbstractFunctionParser parser, @NotNull Str
303295
return m.invoke(parser, args);
304296
}
305297

306-
public static Invocable INVOCABLE;
307-
public static ScriptEngine SCRIPT_ENGINE;
308-
public static Map<String, ScriptEngine> SCRIPT_ENGINE_MAP;
309-
static {
310-
try {
311-
System.setProperty("Dnashorn.args", "language=es6");
312-
System.setProperty("Dnashorn.args", "--language=es6");
313-
System.setProperty("-Dnashorn.args", "--language=es6");
314-
315-
/*获取执行JavaScript的执行引擎*/
316-
SCRIPT_ENGINE = new ScriptEngineManager().getEngineByName("javascript");
317-
INVOCABLE = (Invocable) SCRIPT_ENGINE;
318-
319-
SCRIPT_ENGINE_MAP = new HashMap<>();
320-
SCRIPT_ENGINE_MAP.put("JavaScript", SCRIPT_ENGINE);
321-
SCRIPT_ENGINE_MAP.put("javascript", SCRIPT_ENGINE);
322-
SCRIPT_ENGINE_MAP.put("js", SCRIPT_ENGINE);
323-
}
324-
catch (Exception e) {
325-
e.printStackTrace();
326-
}
327-
}
328-
329298
/**Java 调用 JavaScript 函数
330299
* @param parser
331300
* @param methodName
@@ -337,78 +306,8 @@ public static Object invoke(@NotNull AbstractFunctionParser parser, @NotNull Str
337306
* @throws Exception
338307
*/
339308
public static Object invokeScript(@NotNull AbstractFunctionParser parser, @NotNull String methodName
340-
, @NotNull Class<?>[] parameterTypes, @NotNull Object[] args, String returnType, JSONObject currentObject, ScriptEngine engine) throws Exception {
341-
JSONObject row = SCRIPT_MAP.get(methodName);
342-
if (row == null) {
343-
throw new UnsupportedOperationException("调用的远程函数脚本 " + methodName + " 不存在!");
344-
}
345-
346-
String script = row.getString("script");
347-
348-
if (engine == null) {
349-
engine = SCRIPT_ENGINE;
350-
}
351-
engine.eval(script); // 必要,未执行导致下方 INVOCABLE.invokeFunction 报错 NoSuchMethod
352-
353-
//Object[] newArgs = args == null || args.length <= 0 ? null : new Object[args.length];
354-
355-
// APIJSON 远程函数不应该支持
356-
//if (row.getBooleanValue("simple")) {
357-
// return SCRIPT_ENGINE.eval(script);
358-
//}
359-
360-
Invocable invocable = engine instanceof Invocable ? (Invocable) engine : null;
361-
362-
Object result;
363-
if (args == null || args.length <= 0) {
364-
result = invocable.invokeFunction(methodName);
365-
}
366-
else {
367-
//args[0] = JSON.toJSONString(args[0]); // Java 调 js 函数只支持传基本类型,改用 invokeMethod ?
368-
369-
//for (int i = 0; i < args.length; i++) {
370-
// Object a = currentObject == null ? null : currentObject.get(args[i]);
371-
// newArgs[i] = a == null || apijson.JSON.isBooleanOrNumberOrString(a) ? a : JSON.toJSONString(a);
372-
//}
373-
374-
// 支持 JSONObject
375-
result = invocable.invokeFunction(methodName, args);
376-
//result = INVOCABLE.invokeMethod(args[0], methodName, args);
377-
378-
//switch (newArgs.length) {
379-
// case 1:
380-
// result = INVOCABLE.invokeFunction(methodName, newArgs[0]);
381-
// break;
382-
// case 2:
383-
// result = INVOCABLE.invokeFunction(methodName, newArgs[0], newArgs[1]);
384-
// break;
385-
// case 3:
386-
// result = INVOCABLE.invokeFunction(methodName, newArgs[0], newArgs[1], newArgs[2]);
387-
// break;
388-
// case 4:
389-
// result = INVOCABLE.invokeFunction(methodName, newArgs[0], newArgs[1], newArgs[2], newArgs[3]);
390-
// break;
391-
// case 5:
392-
// result = INVOCABLE.invokeFunction(methodName, newArgs[0], newArgs[1], newArgs[2], newArgs[3], newArgs[4]);
393-
// break;
394-
// case 6:
395-
// result = INVOCABLE.invokeFunction(methodName, newArgs[0], newArgs[1], newArgs[2], newArgs[3], newArgs[4], newArgs[5]);
396-
// break;
397-
// case 7:
398-
// result = INVOCABLE.invokeFunction(methodName, newArgs[0], newArgs[1], newArgs[2], newArgs[3], newArgs[4], newArgs[5], newArgs[6]);
399-
// break;
400-
// case 8:
401-
// result = INVOCABLE.invokeFunction(methodName, newArgs[0], newArgs[1], newArgs[2], newArgs[3], newArgs[4], newArgs[5], newArgs[6], newArgs[7]);
402-
// break;
403-
// case 9:
404-
// result = INVOCABLE.invokeFunction(methodName, newArgs[0], newArgs[1], newArgs[2], newArgs[3], newArgs[4], newArgs[5], newArgs[6], newArgs[7], newArgs[8]);
405-
// break;
406-
// case 10:
407-
// result = INVOCABLE.invokeFunction(methodName, newArgs[0], newArgs[1], newArgs[2], newArgs[3], newArgs[4], newArgs[5], newArgs[6], newArgs[7], newArgs[8], newArgs[9]);
408-
// break;
409-
//}
410-
}
411-
309+
, @NotNull Class<?>[] parameterTypes, @NotNull Object[] args, String returnType, JSONObject currentObject, ScriptExecutor scriptExecutor) throws Exception {
310+
Object result = scriptExecutor.execute(parser, currentObject, methodName, args);
412311
if (Log.DEBUG && result != null) {
413312
Class<?> rt = result.getClass(); // 作为远程函数的 js 类型应该只有 JSON 的几种类型
414313
String fullReturnType = (StringUtil.isSmallName(returnType)

APIJSONORM/src/main/java/apijson/orm/AbstractParser.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2120,8 +2120,8 @@ private JSONObject batchVerify(RequestMethod method, String tag, int version, St
21202120
case apijson.JSONObject.KEY_DATABASE:
21212121
object_attributes_map.put(apijson.JSONObject.KEY_DATABASE, objAttrJson.getString(objAttr));
21222122
break;
2123-
case apijson.JSONObject.VERSION:
2124-
object_attributes_map.put(apijson.JSONObject.VERSION, objAttrJson.getString(objAttr));
2123+
case JSONRequest.KEY_VERSION:
2124+
object_attributes_map.put(JSONRequest.KEY_VERSION, objAttrJson.getString(objAttr));
21252125
break;
21262126
case apijson.JSONObject.KEY_ROLE:
21272127
object_attributes_map.put(apijson.JSONObject.KEY_ROLE, objAttrJson.getString(objAttr));

APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5004,7 +5004,7 @@ else if (userId instanceof Subquery) {}
50045004
if(config.isFakeDelete()) {
50055005
// 查询Access假删除
50065006
Map<String, Object> accessFakeDeleteMap = AbstractVerifier.ACCESS_FAKE_DELETE_MAP.get(config.getTable());
5007-
if (StringUtil.isNotEmpty(accessFakeDeleteMap.get("deletedKey"), true)) {
5007+
if (accessFakeDeleteMap != null && StringUtil.isNotEmpty(accessFakeDeleteMap.get("deletedKey"), true)) {
50085008
Map<String, Object> fakeDeleteMap = new HashMap<>();
50095009
boolean isFakeDelete = true;
50105010
if(method != DELETE) {
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
package apijson.orm.script;
2+
3+
import java.util.HashMap;
4+
import java.util.Map;
5+
import java.util.concurrent.ConcurrentHashMap;
6+
7+
import javax.script.Bindings;
8+
import javax.script.Compilable;
9+
import javax.script.CompiledScript;
10+
import javax.script.ScriptEngine;
11+
import javax.script.ScriptEngineManager;
12+
import javax.script.SimpleBindings;
13+
14+
import com.alibaba.fastjson.JSONObject;
15+
16+
import apijson.orm.AbstractFunctionParser;
17+
18+
/**
19+
* JSR223 script engine的统一实现抽象类
20+
*/
21+
public abstract class JSR223ScriptExecutor implements ScriptExecutor {
22+
protected ScriptEngine scriptEngine;
23+
24+
private final Map<String, CompiledScript> compiledScriptMap = new ConcurrentHashMap<>();
25+
26+
@Override
27+
public ScriptExecutor init() {
28+
ScriptEngineManager scriptEngineManager = new ScriptEngineManager();
29+
scriptEngine = scriptEngineManager.getEngineByName(scriptEngineName());
30+
return this;
31+
}
32+
33+
protected abstract String scriptEngineName();
34+
35+
protected abstract Object extendParameter(AbstractFunctionParser parser, JSONObject currentObject, String methodName, Object[] args);
36+
37+
protected abstract boolean isLockScript(String methodName);
38+
39+
protected String convertScript(String script) {
40+
return script;
41+
}
42+
43+
@Override
44+
public void load(String name, String script) {
45+
try {
46+
CompiledScript compiledScript = ((Compilable) scriptEngine).compile(convertScript(script));
47+
compiledScriptMap.put(name, compiledScript);
48+
} catch (Exception e) {
49+
e.printStackTrace();
50+
}
51+
52+
}
53+
54+
@Override
55+
public Object execute(AbstractFunctionParser parser, JSONObject currentObject, String methodName, Object[] args) throws Exception {
56+
CompiledScript compiledScript = compiledScriptMap.get(methodName);
57+
Bindings bindings = new SimpleBindings();
58+
// 往脚本上下文里放入元数据
59+
// 把 RequestMethod method, String tag, int version, @NotNull JSONObject request,
60+
// HttpSession session 等参数作为全局参数传进去供脚本使用
61+
62+
// 加载扩展属性
63+
Object extendParameter = this.extendParameter(parser, currentObject, methodName, args);
64+
if(extendParameter != null) {
65+
bindings.put("extParam", extendParameter);
66+
}
67+
68+
Map<String, Object> metaMap = new HashMap<>();
69+
metaMap.put("version", parser.getVersion());
70+
metaMap.put("tag", parser.getTag());
71+
metaMap.put("args", args);
72+
bindings.put("_meta", metaMap);
73+
return compiledScript.eval(bindings);
74+
}
75+
76+
@Override
77+
public void cleanCache() {
78+
compiledScriptMap.clear();
79+
}
80+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package apijson.orm.script;
2+
3+
import com.alibaba.fastjson.JSONObject;
4+
5+
import apijson.orm.AbstractFunctionParser;
6+
7+
/**
8+
* JavaScript脚本语言的执行器实现
9+
*/
10+
public class JavaScriptExecutor extends JSR223ScriptExecutor {
11+
12+
@Override
13+
protected String scriptEngineName() {
14+
return "javascript";
15+
}
16+
17+
@Override
18+
protected Object extendParameter(AbstractFunctionParser parser, JSONObject currentObject, String methodName, Object[] args) {
19+
return null;
20+
}
21+
22+
@Override
23+
protected boolean isLockScript(String methodName) {
24+
return false;
25+
}
26+
27+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package apijson.orm.script;
2+
3+
import com.alibaba.fastjson.JSONObject;
4+
5+
import apijson.orm.AbstractFunctionParser;
6+
7+
public interface ScriptExecutor {
8+
9+
ScriptExecutor init();
10+
11+
void load(String name, String script);
12+
13+
Object execute(AbstractFunctionParser parser, JSONObject currentObject, String methodName, Object[] args) throws Exception;
14+
15+
void cleanCache();
16+
17+
}

0 commit comments

Comments
 (0)