先週はスクリプトとJavaの間でオブジェクトの共有を行なう方法を解説しました。
グローバルスコープとエンジンスコープ
スクリプトとJavaでオブジェクトの共有を行うにはBindingsインタフェースを使います。BindingオブジェクトにはScriptEngineクラスのputメソッド、getメソッドを用いてアクセスします。このように書くと、Bindingsオブジェクトはスクリプトエンジンにつき1つのようですが、ちょっと違うのです。
今まで使用してきたBindingsオブジェクトはスクリプトエンジンが保持しています。この他にスクリプトエンジンマネージャが保持しているBindingsオブジェクトがあるのです。
この2つのBindingsオブジェクトをまとめているのが、javax.script.ScriptContextインタフェースです。
今まではスクリプトエンジンが直接Bindingsオブジェクトを保持しているように記述していました。しかし、実際はスクリプトエンジンはScriptContextオブジェクトを保持しており、ScriptContextオブジェクトがBindingsオブジェクトを保持しているのです。これを表したのが図1です。
図1 ScriptContext |
---|
スクリプトエンジンマネージャで管理し、すべてのスクリプトエンジンに対しても適用されるものをグローバルスコープといいます。その一方、個々のスクリプトエンジンが管理し、適用するスコープをエンジンスコープといいます。
Bindingsオブジェクトもこのスコープで区別されます。今までのサンプルで扱ってきたのはすべてエンジンスコープのBindingsオブジェクトです。
それではグローバルスコープのBindingsオブジェクトとエンジンスコープのBindingsオブジェクトで同じキーを使っていたらどうなるのでしょうか。サンプルで試してみましょう。
サンプルのソース | BindingSample5.java |
---|
グローバルスコープのBindingsオブジェクトには、ScriptEngineManagerクラスのputメソッド、getメソッドを使用してアクセスします。
public BindingSample5() { ScriptEngineManager manager = new ScriptEngineManager(); // グローバルスコープのバインディング manager.put("date", "DATE"); ScriptEngine engine = manager.getEngineByName("js"); // エンジンスコープのバインディング Date now = new Date(); engine.put("date", now); try { engine.eval(SCRIPT); } catch (ScriptException ex) { System.err.println("スクリプトの実行に失敗しました"); System.err.println(ex.getMessage()); } }
青字がグローバルスコープ、赤字がエンジンスコープのBindingsオブジェクトにアクセスしている部分です。
グローバルスコープはdateに対して"DATE"という文字列、エンジンスコープでは現在時刻を保持させています。
スクリプトは今まで同じく、単に変数を出力しているだけです。これを実行させたらどうなるでしょう。
C:\scripting>java BindingsSample5 Thu Feb 21 23:36:56 JST 2008
どうやら、エンジンスコープのBindingsオブジェクトが優先されるようです。
グローバルスコープのBindngsオブジェクトを使用すると、複数のスクリプト間でオブジェクトを共有することができます。
サンプルのソース | BindingSample6.java |
---|
このサンプルではJavaとJavaScript、そしてJRubyでオブジェクトを共有します。
private String JS_SCRIPT = "println('JS: ' + date);"; private String RUBY_SCRIPT = "puts 'Ruby: ' + $date.to_s"; public BindingSample6() { ScriptEngineManager manager = new ScriptEngineManager(); Date date = new Date(); System.out.println("Java: " + date); // グローバルスコープのバインディング manager.put("date", date); // Rhinoで実行 ScriptEngine jsEngine = manager.getEngineByName("js"); try { jsEngine.eval(JS_SCRIPT); } catch (ScriptException ex) { System.err.println("スクリプトの実行に失敗しました"); System.err.println(ex.getMessage()); } // JRubyで実行 ScriptEngine rubyEngine = manager.getEngineByName("ruby"); try { rubyEngine.eval(RUBY_SCRIPT); } catch (ScriptException ex) { System.err.println("スクリプトの実行に失敗しました"); System.err.println(ex.getMessage()); } }
BindingSample5クラスと同様に現在時刻をグローバルスコープのBindingsオブジェクトに保持させます(赤字部分)。
Rubyではグローバル変数は変数名の前に$を付加します。また、文字列に変更するためto_sメソッドをコールしています。to_sメソッドは、JRubyスクリプトエンジンの内部でtoStringメソッドに委譲されます。
さて、これを実行してみましょう。ただしく実行できれば、同じオブジェクトを3種類の言語からアクセスできるはずです。
ここでは、JRubyはカレントディレクトに配置しました。詳しくは再来週に解説しますが、JRubyスクリプトエンジンを起動する場合、com.sun.script.jruby.loadpath環境変数をセットする必要があります。
C:\scripting>java -Dcom.sun.script.jruby.loadpath=jruby \lib\jruby.jar -cp jruby\lib\*;jruby\build\jruby-engine.jar; . BindingSample6 Java: Sat Feb 21 23:47:02 JST 2008 JS: Sat Feb 21 23:47:02 JST 2008 Ruby: Sat Feb 21 23:47:02 JST 2008
このようにグローバルスコープとエンジンスコープは用途によって使い分けることで、いろいろおもしろいことができそうですよ。
さて、今週まで7回に渡ってJava SE 6に標準で提供されてきたRhinoスクリプトエンジンを主に使用してきました。来週から他のスクリプト言語を扱っていきます。お楽しみに。
著者紹介 櫻庭祐一 横河電機 ネットワーク開発センタ所属。Java in the Box 主筆 今月の櫻庭櫻庭が使用しているデジカメはニコンのD70です。もうすぐ、丸4年になります。このカメラで撮った写真は8万枚を超えます。 さすがにこれだけ使っていると、いろいろとガタが来るものですね。ボディの傷はいたるところ、ファインダー内のゴミや、コンパクトフラッシュとの接点不良など、など。 でも、一番心配なのはシャッターユニットが壊れてしまうのではないかということ。このカメラのシャッターはメカニカルシャッター。つまり、稼動部がとても多いということです。ということは、壊れやすいわけですね。内心ひやひやしながら、写真を撮り続ける日々です。
|