2015-11-09 10 views
5

私はいくつかのグローバル関数とオブジェクトで設定したカスタムNashornランタイムを用意しています。これらのいくつかはステートレスであり、一部はステートフルです。このランタイムに対して、カスタムスクリプトをいくつか実行しています。 、私が読んだものに基づいてNashornの特定のコンテキストで関数を実行

myContext.setBindings(engine.createBindings(), ScriptContext.ENGINE_SCOPE); 
engine.eval(myScript, myContext); 

スクリプトの観点から、グローバルスコープ(への変更を:各実行のために

は、私はグローバルコンテキストによって支えられて新しいコンテキストを作成する上で計画しています)は、私が作成した新しいコンテキストに限定されます。

これらのスクリプトは、評価されると、いくつかのオブジェクト(明確に定義された名前とメソッド名を持つ)を公開します。 をInvocableにキャストして、オブジェクトのメソッドを呼び出すことができます。しかし、関数が実行されるコンテキストをどのように知っていますか?それは問題でも、それが評価されたコンテキストに基づいて設定されたその関数の実行コンテキストですか?

すべてのスレッドが同じスクリプトエンジンインスタンスを共有し、すべてが同じスクリプト(グローバルオブジェクトを公開する)を実行しようとするマルチスレッドの状況では、どのような動作が期待できますか。次に、オブジェクトのメソッドを呼び出すと、どのコンテキストで関数が実行されますか?使用するオブジェクトのインスタンスをどのように知っていますか?

コンテキストを指定できる方法はinvokeですが、これは当てはまりません。これを行う方法はありますか、これについては完全に間違っていますか?

これを回避する簡単な方法は、実行ごとに新しいスクリプトエンジンインスタンスを作成することですが、わかっているように、私は(特に共有コードで)最適化を失います。それは、ここで助けを事前にコンパイルすると言われていますか?

+0

プレコンパイルは実際にはNashornのノーオペレーションなので、それは役に立たないと読んでいます。しかし、ソースを見つけることができません。私は間違っている可能性がある。 –

答えて

9

私はこれを理解しました。私はに実行していた問題は、カスタムスクリプトによって公開された機能は、エンジンのデフォルトの範囲からバインディングに存在していなかったので、invokeFunctionNoSuchMethodExceptionを投げるというものであった:

ScriptContext context = new SimpleScriptContext(); 
context.setBindings(nashorn.createBindings(), ScriptContext.ENGINE_SCOPE); 
engine.eval(customScriptSource, context); 
((Invocable) engine).invokeFunction(name, args); //<- NoSuchMethodException thrown 

だから私がしなければならなかったものを引き出すましたコンテキストの名前から関数を呼び出すと、次のように明示的に呼び出すことができます。

JSObject function = (JSObject) context.getAttribute(name, ScriptContext.ENGINE_SCOPE); 
function.call(null, args); //call to JSObject#isFunction omitted brevity 

これは、新しく作成されたコンテキストに存在する関数を呼び出します。あなたはまた、オブジェクトのメソッドをこのように呼び出すことができます。

JSObject object = (JSObject) context.getAttribute(name, ScriptContext.ENGINE_SCOPE); 
JSObject method = (JSObject) object.getMember(name); 
method.call(object, args); 

callはチェックされない例外がスローされます(ThrowableではJavaScriptスタックフレームの情報で初期化されているRuntimeExceptionまたはNashornExceptionに包まれたいずれか)ので、あなたがあれば、明示的にことを処理する必要があります有用なフィードバックを提供したい。

スレッドごとに別々のコンテキストが存在するため、この方法ではスレッド間でステップオーバーすることはできません。また、スレッド間でカスタムランタイムコードを共有し、カスタムランタイムによって公開される可変オブジェクトへの状態変更がコンテキストごとに分離されていることを確認できました。これを行うには

、私は私のカスタムランタイム・ライブラリのコンパイル表現を含むCompiledScriptインスタンスを作成します。

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) { 
      ... 
     } 
    } 

    ... 
} 

は私がスクリプトを実行する必要があるときそれから私は、コンパイルされたソースを評価して、評価しますそのコンテキストに対してスクリプトだけでなく:

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); 

私は複数のスレッドでこれをテストし、私は状態変化が個々のスレッドに属しているコンテキストに限られていたことを確認することができました。これは、コンパイルされた表現が特定のコンテキスト内で実行されるためです。つまり、それによって公開されるすべてのインスタンスがそのコンテキストにスコープされます。

小さな欠点は、スレッド固有の状態を持つ必要のないオブジェクトのオブジェクト定義を不必要に再評価することです。これを回避するには、エンジンの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) { 
     ... 
    } 
} 

その後、あなたは、エンジンのENGINE_SCOPEからスレッド固有のコンテキストを移入することができます

context.getBindings(ScriptContext.ENGINE_SCOPE).putAll(engine.getBindings(ScriptContext.ENGINE_SCOPE)); 

あなたがする必要があることは、そのようなオブジェクトが公開されていることを確認することです。それ以外の場合は、プロパティを再定義または追加することができます。

+2

ご質問ありがとうございます。非常に役立ちます! –

+0

しかし、質問。コンパイルされたスクリプトとソースの評価の違いは何ですか?それは二重だと私には思われる? –

+0

私はこれに基づいて実装しましたが、 'engine.eval(source、context)'を省略しました。うまく動作しているようです。 –

関連する問題