L'ho capito. Il problema stavo correndo in era che invokeFunction
sarebbe gettare un NoSuchMethodException
perché le funzioni esposte dal script personalizzato non esistevano negli attacchi dal campo di applicazione di default del motore:
ScriptContext context = new SimpleScriptContext();
context.setBindings(nashorn.createBindings(), ScriptContext.ENGINE_SCOPE);
engine.eval(customScriptSource, context);
((Invocable) engine).invokeFunction(name, args); //<- NoSuchMethodException thrown
Quindi quello che dovevo fare era tirare fuori la funzione dal contesto per nome e lo chiamano esplicitamente in questo modo:
JSObject function = (JSObject) context.getAttribute(name, ScriptContext.ENGINE_SCOPE);
function.call(null, args); //call to JSObject#isFunction omitted brevity
Ciò chiamare la funzione che esiste nel vostro contesto appena creato. È inoltre possibile richiamare metodi su oggetti in questo modo:
JSObject object = (JSObject) context.getAttribute(name, ScriptContext.ENGINE_SCOPE);
JSObject method = (JSObject) object.getMember(name);
method.call(object, args);
call
genera un'eccezione incontrollato (sia Throwable
avvolto in un RuntimeException
o NashornException
che è stato inizializzato con JavaScript informazioni stackframe) in modo da avere per gestire in modo esplicito che se si voglio fornire un feedback utile.
In questo modo i thread non possono sovrapporsi perché esiste un contesto separato per thread. Sono stato anche in grado di condividere codice runtime personalizzato tra i thread e garantire che le modifiche di stato agli oggetti mutabili esposti dal runtime personalizzato fossero isolate dal contesto.
Per fare questo, ho creare un'istanza CompiledScript
che contiene una rappresentazione compilata di mia abitudine runtime-biblioteca:
public class Runtime {
private ScriptEngine engine;
private CompiledScript compiledRuntime;
public Runtime() {
engine = new NashornScriptEngineFactory().getScriptEngine("-strict");
String source = new Scanner(
this.getClass().getClassLoader().getResourceAsStream("runtime/runtime.js")
).useDelimiter("\\Z").next();
try {
compiledRuntime = ((Compilable) engine).compile(source);
} catch(ScriptException e) {
...
}
}
...
}
Poi, quando ho bisogno di eseguire uno script valuto la sorgente compilato, e poi valutare la script in quel contesto così:
ScriptContext context = new SimpleScriptContext();
context.setBindings(engine.createBindings(), ScriptContext.ENGINE_SCOPE);
//Exception handling omitted for brevity
//Evaluate the compiled runtime in our new context
compiledRuntime.eval(context);
//Evaluate the source in the same context
engine.eval(source, context);
//Call a function
JSObject jsObject = (JSObject) context.getAttribute(function, ScriptContext.ENGINE_SCOPE);
jsObject.call(null, args);
ho provato questo con più thread e sono stato in grado di fare in modo che i cambiamenti di stato sono stati limitati ai contesti che appartengono a singoli thread. Questo perché la rappresentazione compilata viene eseguita all'interno di un contesto specifico, il che significa che le istanze di qualsiasi cosa esposta da esso sono portate a tale contesto.
Un piccolo svantaggio è che è possibile rivalutare inutilmente le definizioni di oggetti per oggetti che non devono avere uno stato specifico del thread. Per aggirare il problema, valutarli sul motore direttamente, a cui si aggiungeranno le associazioni per quegli oggetti al motore ENGINE_SCOPE
:
public Runtime() {
...
String shared = new Scanner(
this.getClass().getClassLoader().getResourceAsStream("runtime/shared.js")
).useDelimiter("\\Z").next();
try {
...
nashorn.eval(shared);
...
} catch(ScriptException e) {
...
}
}
Poi, più tardi, è possibile popolare del contesto specifico in filo del motore ENGINE_SCOPE
:
context.getBindings(ScriptContext.ENGINE_SCOPE).putAll(engine.getBindings(ScriptContext.ENGINE_SCOPE));
Una cosa che dovrai fare è assicurarti che tutti gli oggetti che esponi siano stati congelati. Altrimenti è possibile ridefinire o aggiungere proprietà a loro.
Ho letto che la pre-compilazione è in realtà un no-op in Nashorn, quindi non aiuta. Non riesci a trovare la fonte però. Potrei sbagliarmi. –