我有一个java类,如下所示
public class StaticBean {
public static String returnString(int num){
String json = "[{\"name\" : \"John Doe\", \"age\" : 30}]";
return json;
}
}在下面的测试中,我有两个引擎实例。
在执行时,我发现不可能将绑定实例复制到另一个实例,并使用它在同一个引擎或不同引擎中执行。即使我复制了,如果使用相同的引擎/绑定,结果也与我得到的结果不匹配。
@Test
public void testParsingStringObjects() {
Bindings b = engine.createBindings();
b.put("ndb", getBindingObject(StaticBean.class, engine));
engine.setBindings(b, ScriptContext.ENGINE_SCOPE);
String source = "print('Definition in engine instance 1');"
+ "var SysU = {};\n"
+ "SysU.returnObject = function returnObjectJS(){\n"
+ "var string = ndb.returnString(1);\n"
+ "return JSON.parse(string);\n" + "}\n"
+ "SysU.returnString = function returnStringJS(){\n"
+ "var string = ndb.returnString(1);\n"
+ "print('String returned by the java function SysU.returnString() '+string);\n"
+ "return string;\n" + "};\n"
+ "print('====================Using the same engine instance for execution====================');\n"
+ "(function (){" + "var json = {};\n"
+ "print(\"String Returned in Caller SysU.returnString(): \"+SysU.returnString());\n"
+ "print(\"Object Returned in Caller SysU.returnObject(): \"+SysU.returnObject());\n"
+ "print(\"**Stringified Object Returned in Caller JSON.stringify(SysU.returnObject()): \"+JSON.stringify(SysU.returnObject()));\n"
+ "print('Adding the object in another ( json.ext = SysU.returnObject();) ...');\n"
+ "json.ext = SysU.returnObject();\n"
+ "print(\"Added JSON object which is stringified to display JSON.stringify(json): \"+JSON.stringify(json));\n" + "})();";
try {
engine.eval(source);
Bindings oldEngineBindings = engine.getBindings(ScriptContext.ENGINE_SCOPE);
Bindings localBindings = engine2.getBindings(ScriptContext.ENGINE_SCOPE);
Bindings newBindings = engine.createBindings();
oldEngineBindings.put("fileName","oldEngine");
localBindings.put("fileName","localEngine");
newBindings.putAll(oldEngineBindings);
newBindings.putAll(localBindings);
oldEngineBindings.putAll(localBindings);
ScriptContext ctxt = new SimpleScriptContext();
ctxt.setBindings(oldEngineBindings, ScriptContext.ENGINE_SCOPE);
engine.setContext(ctxt);
engine.eval(""
+ "print('====================Using the same engine with original binding ====================');\n"
+ "(function (){" + "var json = {};\n"
+ "print(\"String Returned in Caller SysU.returnString(): \"+SysU.returnString());\n"
+ "print(\"Object Returned in Caller SysU.returnObject(): \"+SysU.returnObject());\n"
+ "print(\"**Stringified Object Returned in Caller JSON.stringify(SysU.returnObject()): \"+JSON.stringify(SysU.returnObject()));\n"
+ "print('Adding the object in another ( json.ext = SysU.returnObject();) ...');\n"
+ "json.ext = SysU.returnObject();\n"
+ "print(\"Added JSON object which is stringified to display JSON.stringify(json): \"+JSON.stringify(json));\n" + "})();");
ctxt.setBindings(newBindings, ScriptContext.ENGINE_SCOPE);
engine.setContext(ctxt);
engine.eval(""
+ "print('====================Using the same engine with copied new binding ====================');\n"
+ "(function (){" + "var json = {};\n"
+ "print(\"String Returned in Caller SysU.returnString(): \"+SysU.returnString());\n"
+ "print(\"Object Returned in Caller SysU.returnObject(): \"+SysU.returnObject());\n"
+ "print(\"**Stringified Object Returned in Caller JSON.stringify(SysU.returnObject()): \"+JSON.stringify(SysU.returnObject()));\n"
+ "print('Adding the object in another ( json.ext = SysU.returnObject();) ...');\n"
+ "json.ext = SysU.returnObject();\n"
+ "print(\"Added JSON object which is stringified to display JSON.stringify(json): \"+JSON.stringify(json));\n" + "})();",newBindings);
ctxt.setBindings(oldEngineBindings, ScriptContext.ENGINE_SCOPE);
engine2.setContext(ctxt);
engine2.eval(""
+ "print('====================Using a different engine instance with original binding ====================');\n"
+ "(function (){" + "var json = {};\n"
+ "print(\"String Returned in Caller SysU.returnString(): \"+SysU.returnString());\n"
+ "print(\"Object Returned in Caller SysU.returnObject(): \"+SysU.returnObject());\n"
+ "print(\"**Stringified Object Returned in Caller JSON.stringify(SysU.returnObject()): \"+JSON.stringify(SysU.returnObject()));\n"
+ "print('Adding the object in another ( json.ext = SysU.returnObject();) ...');\n"
+ "json.ext = SysU.returnObject();\n"
+ "print(\"Added JSON object which is stringified to display JSON.stringify(json): \"+JSON.stringify(json));\n" + "})();");
} catch (ScriptException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}这是一种公认的行为吗?还是虫子?我应该能够在同一个引擎或不同的引擎实例中复制绑定并在不同的作用域中使用它们,并产生相同的结果。
我正在Java8u101上测试
结果当你运行测试。当绑定或引擎实例更改时,ReturnObject()函数似乎会失败.
Definition in engine instance 1
====================Using the same engine instance for execution====================
String returned by the java function SysU.returnString() [{"name" : "John Doe", "age" : 30}]
String Returned in Caller SysU.returnString(): [{"name" : "John Doe", "age" : 30}]
Object Returned in Caller SysU.returnObject(): [object Object]
**Stringified Object Returned in Caller JSON.stringify(SysU.returnObject()): [{"name":"John Doe","age":30}]
Adding the object in another ( json.ext = SysU.returnObject();) ...
Added JSON object which is stringified to display JSON.stringify(json): {"ext":[{"name":"John Doe","age":30}]}
====================Using the same engine with original binding ====================
String returned by the java function SysU.returnString() [{"name" : "John Doe", "age" : 30}]
String Returned in Caller SysU.returnString(): [{"name" : "John Doe", "age" : 30}]
Object Returned in Caller SysU.returnObject(): [object Object]
**Stringified Object Returned in Caller JSON.stringify(SysU.returnObject()): [{"name":"John Doe","age":30}]
Adding the object in another ( json.ext = SysU.returnObject();) ...
Added JSON object which is stringified to display JSON.stringify(json): {"ext":[{"name":"John Doe","age":30}]}
====================Using the same engine with copied new binding ====================
String returned by the java function SysU.returnString() [{"name" : "John Doe", "age" : 30}]
String Returned in Caller SysU.returnString(): [{"name" : "John Doe", "age" : 30}]
Object Returned in Caller SysU.returnObject(): [object Object]
**Stringified Object Returned in Caller JSON.stringify(SysU.returnObject()): undefined
Adding the object in another ( json.ext = SysU.returnObject();) ...
Added JSON object which is stringified to display JSON.stringify(json): {}
====================Using a different engine instance with original binding ====================
String returned by the java function SysU.returnString() [{"name" : "John Doe", "age" : 30}]
String Returned in Caller SysU.returnString(): [{"name" : "John Doe", "age" : 30}]
Object Returned in Caller SysU.returnObject(): [object Object]
**Stringified Object Returned in Caller JSON.stringify(SysU.returnObject()): undefined
Adding the object in another ( json.ext = SysU.returnObject();) ...
Added JSON object which is stringified to display JSON.stringify(json): {}编辑:-
找到了这个线程https://bugs.openjdk.java.net/browse/JDK-8067642。这提到了一些关于外部对象是ScriptObjectMirror实例的内容。我使用typeof操作符来显示在失败和成功的情况下返回的对象类型,这两次都是ScriptObjectMirror,但是如果我在上下文中使用原始绑定对象,stringify就会像预期的那样工作。
编辑2:-
添加了一个非常简单的测试来演示上面的内容。有点像上面的TLDR :)。执行下面的代码将演示绑定对象上的putAll()不能像我们预期的那样工作。
@Test
public void testParsingObjects() throws ScriptException {
String source = "var Func = {};\n"
+ "Func.getJavaScriptObject = function(){"
+ "var jsString = '{\"foo\":\"bar\"}';\n"
+ "return JSON.parse(jsString);"
+ "};";
String executor = "(function(){ "
+ "var obj = Func.getJavaScriptObject();"
+ "print(JSON.stringify(obj));"
+ " })();";
System.out.println("Executing source...");
engine.eval(source);
System.out.println("\nUsing the same binding instance and engine\n");
engine.eval(executor);
Bindings originalBinding = engine.getBindings(ScriptContext.ENGINE_SCOPE);
Bindings copiedBinding = engine.createBindings();
copiedBinding.putAll(originalBinding);
System.out.println("\nUsing the copied binding instance and engine\n");
engine.eval(executor,copiedBinding);
}执行的结果。
Executing source...
Using the same binding instance and engine
{"foo":"bar"}
Using the copied binding instance and engine
undefined发布于 2016-08-25 00:16:38
下面是我使用不同线程使用的ScriptContext实例之间共享编译的ScriptContext代码的代码。这里的主要好处是只编译一次代码,尽管我也受益于不需要多次从REST中流代码。为了简洁起见,我没有把剩下的部分包括进去。
import javax.script.CompiledScript;
import javax.script.ScriptContext;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
import javax.script.SimpleScriptContext;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.ExecutionException;
import java.util.ArrayList;
import java.util.List;
public class ContextDemo {
static CompiledScript codeLib;
static ScriptEngine engine;
static ScriptContext context;
static List <Future<String>> taskResults;
static ExecutorService executor;
static List <Callable<String>> tasks = new ArrayList<Callable<String>> ();
public static void main(String[] args) {
try {
// Initialize workers and execute
run(4);
} catch(InterruptedException | ExecutionException | ScriptException e) {
System.out.println(e.getMessage());
}
}
static void run(int workers) throws InterruptedException, ExecutionException, ScriptException {
// Initialize engine and initial context
engine = new ScriptEngineManager().getEngineByName("nashorn");
context = new SimpleScriptContext();
engine.setContext(context);
// Compile a JavaScript object with a function
codeLib = ((javax.script.Compilable)engine).compile("var lib = { func1: function(n, s) { return 'thread number ' + n + ': ' + s; } };");
// Create executor with specified number of workers
executor = Executors.newFixedThreadPool((int)workers);
for (int i = 0; i < workers; i++) {
tasks.add(workerLambda(i));
}
// Invoke worker pool
taskResults = executor.invokeAll(tasks);
// Iterate futures list and report results
for (int i = 0; i < workers; i++) {
Future < String > f = taskResults.get(i);
if (f.isDone()) {
System.out.println(f.get());
} else {
System.out.println("Thread " + i + " not done");
}
}
// Shutdown the executor
executor.shutdown();
}
static Callable <String> workerLambda(int n) {
int workerNum = n;
// Thread-specific script context initialization
SimpleScriptContext threadContext = new SimpleScriptContext();
threadContext.setBindings(engine.createBindings(), ScriptContext.ENGINE_SCOPE);
try {
// Inject compiled code library into thread-specific ScriptContext
codeLib.eval(threadContext);
} catch (ScriptException e1) {
System.out.println(e1.getMessage());
}
// Return the lambda
return () -> {
// Call the injected object method and return the result
return (String)engine.eval("lib.func1(" + workerNum + ", 'Hello!');", threadContext);
};
}
}这一产出如下:
thread number 0: Hello!
thread number 1: Hello!
thread number 2: Hello!
thread number 3: Hello!发布于 2016-08-12 23:45:09
我希望我能提供一个可行的解决方案,但我怀疑这可能是一个纳什霍恩虫。作为证据,我提交了这个链接到一个旧的JDK bug:
https://bugs.openjdk.java.net/browse/JDK-8023363
链接中提供了Test.java (以下)作为两个问题的证据(在Map中缺少关键存在,以及在.putAll()进入新绑定之后无法执行函数)“不是一个问题”。不过,我测试了相同的代码,得到了不同的结果:
Test.java如下:
import javax.script.*;
import java.util.Map;
public class Test {
public static void main(String[] args) throws Exception {
ScriptEngineManager m = new ScriptEngineManager();
ScriptEngine scriptEngine = m.getEngineByName("nashorn");
Bindings engineBindings = scriptEngine.getBindings(ScriptContext.ENGINE_SCOPE);
scriptEngine.eval("function func(x) { print('I am func ' + x); }");
// print stuff exposed in engineBindings
for (Map.Entry<?,?> entry : engineBindings.entrySet()) {
System.out.println(entry.getKey());
System.out.println(entry.getValue());
}
Bindings localBindings = scriptEngine.createBindings();
// copy all exposed from other bindings
localBindings.putAll(engineBindings);
// put additional variable
localBindings.put("event", new Object());
scriptEngine.setBindings(localBindings, ScriptContext.ENGINE_SCOPE);
scriptEngine.eval("func(event)");
}
}https://stackoverflow.com/questions/38862075
复制相似问题