Java tutorial
/******************************************************************************* * The MIT License (MIT) * Copyright 2015 Inshua,inshua@gmail.com, All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and * associated documentation files (the Software?), to deal in the Software without restriction, * including without limitation the rights to use, copy, modify, merge, publish, distribute, * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all copies or substantial * portions of the Software. * * THE SOFTWARE IS PROVIDED AS IS?, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *******************************************************************************/ package org.siphon.common.js; import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.PrintStream; import java.io.Reader; import java.lang.reflect.Field; import java.util.concurrent.Callable; import javax.script.Invocable; import javax.script.ScriptContext; import javax.script.ScriptEngine; import javax.script.ScriptEngineManager; import javax.script.ScriptException; import javax.script.SimpleScriptContext; import jdk.nashorn.api.scripting.NashornScriptEngine; import jdk.nashorn.api.scripting.ScriptObjectMirror; import jdk.nashorn.internal.objects.NativeArray; import jdk.nashorn.internal.objects.NativeError; import jdk.nashorn.internal.runtime.ECMAException; import org.apache.commons.io.FileUtils; import org.apache.commons.lang3.exception.ExceptionUtils; public class JsEngineUtil { private final static String importsFn = "/*******************************************************************************\n" + " * The MIT License (MIT)\n" + " * Copyright 2015 Inshua,inshua@gmail.com, All rights reserved.\n" + " *\n" + " * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and\n" + " * associated documentation files (the Software?), to deal in the Software without restriction,\n" + " * including without limitation the rights to use, copy, modify, merge, publish, distribute,\n" + " * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is\n" + " * furnished to do so, subject to the following conditions:\n" + " *\n" + " * The above copyright notice and this permission notice shall be included in all copies or substantial\n" + " * portions of the Software.\n" + " *\n" + " * THE SOFTWARE IS PROVIDED AS IS?, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING\n" + " * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n" + " * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES\n" + " * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\n" + " * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n" + " *******************************************************************************/\n" + "if(typeof println == 'undefined') engine.put('println', print);\n" + "/**\n" + " * ??????\n" + " * js??????\n" + " * java eval engine['IMPORTS_PATH_STACK'] ?? IMPORTS_PATH_STACK.last() ??\n" + " * java eval \"a.js\" ? ['a.js']\n" + " * a.js ? b/b1.js './b/b1.js'? ['a.js', 'b/b1.js']\n" + " * b1.js b/b2.js? imports('b2.js')?? b/b1.js??\n" + " * import(b/b2.js) ?? ['a.js', 'b/b1.js', 'b/b2.js']\n" + " * b2.js eval ???['a.js', 'b/b1.js']\n" + " * b1.js eval ??? ['a.js']\n" + " * a.js ??\n" + " * \n" + " * ????? DEFAULT_IMPORTS_PATHS \n" + " * ? ['WEB-INF/jslib/']\n" + " * ??? ['js/', 'jslib/']\n" + " * @param scriptFile\n" + " */\n" + "function imports(scriptFile){\n" + " var stk = IMPORTS_PATH_STACK;\n" + " var last = stk[stk.length-1];\n" + " var currFile = new java.io.File(last);\n" + " var abspath = new java.io.File(currFile, '../' + scriptFile).getCanonicalPath();\n" + " var result;\n" + " if(new java.io.File(abspath).exists()){\n" + " stk.push(abspath);\n" + " try{\n" + " if(!IMPORTED_FILES[abspath]){\n" + " //println('include ' + abspath + ' found at stack');\n" + " IMPORTED_FILES[abspath] = true;\n" + " engine.put(\"javax.script.filename\", abspath);\n" + " result = engine.eval(new java.io.InputStreamReader(new java.io.FileInputStream(abspath), \"utf-8\"));\n" + " }\n" + " } catch(e){\n" + " throw e;\n" + " } finally {\n" + " stk.pop();\n" + " }\n" + " return result;\n" + " } else {\n" + " var defaults = DEFAULT_IMPORTS_PATHS;\n" + " for(var i=0; i<defaults.length; i++){\n" + " var file = new java.io.File(defaults[i]);\n" + " abspath = new java.io.File(file, scriptFile).getCanonicalPath();\n" + " if(new java.io.File(abspath).exists()){\n" + " stk.push(abspath);\n" + " try{\n" + " if(!IMPORTED_FILES[abspath]){\n" + " //println('include ' + abspath + ' found at default path');\n" + " IMPORTED_FILES[abspath] = true;\n" + " engine.put(\"javax.script.filename\", scriptFile);\n" + " result = engine.eval(new java.io.InputStreamReader(new java.io.FileInputStream(abspath), \"utf-8\"));\n" + " }\n" + " } catch(e){\n" + " throw e;\n" + " } finally {\n" + " stk.pop();\n" + " }\n" + " return result;\n" + " }\n" + " }\n" + " println(scriptFile + ' not found, imports failed, default paths: ' + DEFAULT_IMPORTS_PATHS + ' from: ' + last);\n" + " return result;\n" + " }\n" + "} \n" + "/**\n" + " * imports ????\n" + " * @param scriptFile\n" + " */\n" + "function include(scriptFile){\n" + " var stk = IMPORTS_PATH_STACK;\n" + " var last = stk[stk.length-1];\n" + " var currFile = new java.io.File(last);\n" + " var abspath = new java.io.File(currFile, '../' + scriptFile).getCanonicalPath();\n" + " var result;\n" + " if(new java.io.File(abspath).exists()){\n" + " stk.push(abspath);\n" + " try{\n" + " //println('include ' + abspath + ' found at stack');\n" + " IMPORTED_FILES[abspath] = true;\n" + " engine.put(\"javax.script.filename\", scriptFile);\n" + " result = engine.eval(new java.io.InputStreamReader(new java.io.FileInputStream(abspath), \"utf-8\"));\n" + " } catch(e){\n" + " throw e;\n" + " } finally {\n" + " stk.pop();\n" + " }\n" + " return result;\n" + " } else {\n" + " var defaults = DEFAULT_IMPORTS_PATHS;\n" + " for(var i=0; i<defaults.length; i++){\n" + " var file = new java.io.File(defaults[i]);\n" + " abspath = new java.io.File(file, scriptFile).getCanonicalPath();\n" + " if(new java.io.File(abspath).exists()){\n" + " stk.push(abspath);\n" + " //println('include ' + abspath + ' found at default path');\n" + " try{\n" + " IMPORTED_FILES[abspath] = true;\n" + " engine.put(\"javax.script.filename\", abspath);\n" + " result = engine.eval(new java.io.InputStreamReader(new java.io.FileInputStream(abspath), \"utf-8\"));\n" + " } catch(e){\n" + " throw e;\n" + " } finally {\n" + " stk.pop();\n" + " }\n" + " return result;\n" + " }\n" + " }\n" + " println(scriptFile + ' not found, include failed, default paths: ' + DEFAULT_IMPORTS_PATHS + ' from: ' + last);\n" + " return result;\n" + " }\n" + "}\n" + "/**\n" + " * ??????eval? include imports\n" + " * @param filename\n" + " */\n" + "function findResource(filename){\n" + " var stk = IMPORTS_PATH_STACK;\n" + " var last = stk[stk.length-1];\n" + " var currFile = new java.io.File(last);\n" + " if(new java.io.File(filename).exists()){\n" + " return filename;\n" + " }\n" + " var abspath = new java.io.File(currFile, '../' + filename).getCanonicalPath();\n" + " var result;\n" + " if(new java.io.File(abspath).exists()){\n" + " return abspath;\n" + " } else {\n" + " var defaults = DEFAULT_IMPORTS_PATHS;\n" + " for(var i=0; i<defaults.length; i++){\n" + " var file = new java.io.File(defaults[i]);\n" + " abspath = new java.io.File(file, filename).getCanonicalPath();\n" + " if(new java.io.File(abspath).exists()){\n" + " return abspath;\n" + " }\n" + " }\n" + " }\n" + "}\n" + "/**\n" + " * JSON????\n" + " * usage: JSON.parse(string, parseDate)\n" + " * @param key\n" + " * @param value\n" + " * @returns\n" + " */\n" + "function parseDate(key, value) {\n" + " switch(typeof value){\n" + " case 'string':\n" + " var a = parseDate.reg.exec(value);\n" + " if (a) {\n" + " return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[5] || 0, a[6] || 0, +a[7] || 0));\n" + " }\n" + " break;\n" + " }\n" + " return value;\n" + "}\n" + "parseDate.reg = /^(\\d{4})-(\\d{2})-(\\d{2})(T(\\d{2}):(\\d{2}):(\\d{2}(?:\\.\\d*)?)Z?)?$/;\n" + "engine.put('parseDate', parseDate);\n" + "// nashorn \n" + "///**\n" + "// * engine ?\n" + "// * ? try catch(e){throw e} ?\n" + "// * try{\n" + "// * (function(){}).invoke()\n" + "// * } catch(e){throw e}\n" + "// */\n" + "//Function.prototype.invoke = function(scope){\n" + "// engine.invokeMethod(this, \"call\", scope);\n" + "//}"; /** * * @param jsEngine * @param libs [path, path, ...] */ public static void initEngine(ScriptEngine jsEngine, Object[] libs) { try { jsEngine.put("engine", jsEngine); NashornScriptEngine nashornScriptEngine = (NashornScriptEngine) jsEngine; JsTypeUtil jsTypeUtil = new JsTypeUtil(jsEngine); ScriptObjectMirror importedFiles = jsTypeUtil.newObject(); jsEngine.put("IMPORTED_FILES", importedFiles); ScriptObjectMirror stk = jsTypeUtil.newArray(); jsEngine.put("IMPORTS_PATH_STACK", stk); ScriptObjectMirror defaults = (libs.length > 0 && libs[0] != null && libs[0] instanceof String && ((String) libs[0]).length() > 0) ? jsTypeUtil.newArray(libs) : jsTypeUtil.newArray(); jsEngine.put("DEFAULT_IMPORTS_PATHS", defaults); jsEngine.put(ScriptEngine.FILENAME, "common/engine.js"); jsEngine.eval(importsFn); } catch (ScriptException e) { e.printStackTrace(); } } public static ScriptEngine newEngine() { ScriptEngine engine = new ScriptEngineManager().getEngineByName("JavaScript"); initEngine(engine, new Object[] {}); return engine; } public static Object eval(ScriptEngine jsEngine, String srcFile) throws ScriptException, IOException { jsEngine.put(ScriptEngine.FILENAME, srcFile); return jsEngine.eval(FileUtils.readFileToString(new File(srcFile))); } public static Object eval(ScriptEngine jsEngine, String srcFile, String code) throws ScriptException { jsEngine.put(ScriptEngine.FILENAME, srcFile); return jsEngine.eval(code); } public static Object eval(ScriptEngine jsEngine, String srcFile, boolean onlyOnce, boolean preservePathInStack) throws Exception { ScriptObjectMirror importedFiles = (ScriptObjectMirror) jsEngine.get("IMPORTED_FILES"); if (importedFiles.containsKey(srcFile)) { if (onlyOnce) return null; } else { importedFiles.put(srcFile, true); } ScriptObjectMirror stk = (ScriptObjectMirror) jsEngine.get("IMPORTS_PATH_STACK"); // NativeArray.pushObject(stk.to(NativeArray.class), srcFile); //stk.callMember("push", srcFile); try { String code = FileUtils.readFileToString(new File(srcFile), "utf-8"); return eval(jsEngine, srcFile, code); } catch (ScriptException | FileNotFoundException e) { throw e; } finally { if (!preservePathInStack) NativeArray.pop(stk.to(NativeArray.class)); } } public static Object eval(ScriptEngine jsEngine, String srcFile, String script, boolean preservePathInStack) throws NoSuchMethodException, ScriptException { return eval(jsEngine, srcFile, srcFile, script, false, preservePathInStack); } public static Object eval(ScriptEngine jsEngine, String srcFile, String aliasPath, String script, boolean onlyOnce, boolean preservePathInStack) throws NoSuchMethodException, ScriptException { ScriptObjectMirror importedFiles = (ScriptObjectMirror) jsEngine.get("IMPORTED_FILES"); if (importedFiles.containsKey(srcFile)) { if (onlyOnce) return null; } else { importedFiles.put(srcFile, true); } ScriptObjectMirror stk = (ScriptObjectMirror) jsEngine.get("IMPORTS_PATH_STACK"); //NativeArray.pushObject((Object)(stk.to(NativeArray.class)), (Object)srcFile); stk.callMember("push", srcFile); try { return eval(jsEngine, aliasPath, script); } catch (ScriptException e) { throw e; } finally { if (!preservePathInStack) NativeArray.pop(stk.to(NativeArray.class)); } } /** * ? js new Error new ? js java ? * @return js name value Scriptable java throwable */ public static Object parseJsException(Throwable e) { for (Throwable throwable : ExceptionUtils.getThrowables(e)) { if (throwable instanceof ECMAException) { ECMAException je = (ECMAException) throwable; if (je.thrown != null) { Object thrown = je.thrown; if (thrown instanceof Throwable) { return parseJsException((Throwable) thrown); } else if (thrown instanceof NativeError) { return thrown; // Object nashornException = ((NativeError) thrown).nashornException; // if (nashornException != null) { // if (nashornException instanceof Throwable) { // return parseJsException((Throwable) nashornException); // } else { // return nashornException; // } // } else { // return thrown; // } } else { return thrown; } } } } return e; } public static Object invokeMethodCrossEngine(ScriptEngine masterEngine, ScriptEngine serverEngine, Object object, String method, Object[] arguments) throws NoSuchMethodException, ScriptException { if (masterEngine == serverEngine) { return ((Invocable) serverEngine).invokeMethod(object, method, arguments); } else { Object[] arguments2 = new Object[arguments.length]; for (int i = 0; i < arguments.length; i++) { if (arguments[i] instanceof ScriptObjectMirror) { arguments2[i] = ((ScriptObjectMirror) arguments[i]).to(Object.class); } else { arguments2[i] = arguments[i]; } } Object res = null; if (object instanceof ScriptObjectMirror) { res = ((ScriptObjectMirror) object).callMember(method, arguments2); } else { res = ((Invocable) serverEngine).invokeMethod(object, method, arguments2); } if (res instanceof ScriptObjectMirror) { return ((ScriptObjectMirror) res).to(Object.class); } else { return res; } } } public static Object getGlobal(ScriptEngine engine) { ScriptContext context = engine.getContext(); ScriptObjectMirror binding = (ScriptObjectMirror) context.getBindings(ScriptContext.ENGINE_SCOPE); Field globalField = null; try { globalField = ScriptObjectMirror.class.getDeclaredField("global"); globalField.setAccessible(true); Object global = globalField.get(binding); return global; } catch (NoSuchFieldException e) { } catch (SecurityException e) { } catch (IllegalArgumentException e) { } catch (IllegalAccessException e) { } return null; } public static Object getGlobal(ScriptObjectMirror scriptObjectMirror) { Field globalField = null; try { globalField = ScriptObjectMirror.class.getDeclaredField("global"); globalField.setAccessible(true); Object global = globalField.get(scriptObjectMirror); return global; } catch (NoSuchFieldException e) { } catch (SecurityException e) { } catch (IllegalArgumentException e) { } catch (IllegalAccessException e) { } return null; } }