PR

今月も引き続きスクリプト言語のサポートについて解説していきます。

スクリプトとJavaでのオブジェクト共有

前回は、JavaScriptのスクリプトの中でJavaのオブジェクトを生成し、操作する方法を紹介しました。新たにオブジェクトを生成するのであれば、先週紹介した手法でかまわないですが、新たに生成できない場合もあります。

つまり、Javaで生成したオブジェクトをスクリプトからアクセスすることができないかということです。また、これとは逆に、スクリプトで作成したオブジェクトをJavaからアクセスすることもあるかもしれません。

このような場合、Javaとスクリプトでオブジェクトを共有するために、javax.script.Bindingsインタフェースが使用されます。

Bindingインタフェースはjava.util.Mapインタフェースのサブインタフェースで、キーは文字列、バリューがオブジェクトとなります。

スクリプトエンジンはこのBindingsオブジェクトを介して、スクリプトとJavaの間でオブジェクトの共有を行ないます。Bindingsオブジェクトのキーの名称をスクリプトではグローバル変数として扱うことができるのです(図1)。

TickerSampleの実行結果
図1 Bindingsインタフェース

理屈はこのくらいにして、実際にサンプルで試してみましょう。

サンプルのソース BindingSample1.java

スクリプトエンジンにはBindingsオブジェクトにアクセスするgetメソッドとsetメソッドが定義されています。BindingsSample1クラスではsetメソッドを使用して、BindingsオブジェクトにDateオブジェクトを保持させています。

    public BindingSample1() {
        ScriptEngineManager manager = new ScriptEngineManager();
        ScriptEngine engine = manager.getEngineByName("js");
        
        // Bindingsオブジェクトにdateを保持
        Date date = new Date();
        engine.put("date", date);
        
        try {
            engine.eval(SCRIPT);
        } catch (ScriptException ex) {
            System.err.println("スクリプトの実行に失敗しました");
            System.err.println(ex.getMessage());
        }
    }

赤字の部分がBindingsオブジェクトにDateオブジェクトを保持させている部分です。ここではキーに"date"を使用しました。

このサンプルで実行するスクリプトは次のようになります。

    private String SCRIPT = "var date;"
                          + "println(date);";

dateがグローバル変数として定義してあります。そして、次の行でdateを標準出力に出力しているだけです。実際には1行目は不要ですが、ここではグローバル変数として定義してあることを明示するために記述してみました。

これを実行すると、次のように出力されました。

C:\scripting>java BindingsSample1
Thu Feb 21 23:16:57 JST 2008

このスクリプトではdate変数の初期化はおこなっていないので、スクリプトからJavaで生成したオブジェクトにアクセスできたことが分かります。

ここで示したように、スクリプトエンジンにはデフォルトでBindingsオブジェクトを保有しており、通常はこのBindingオブジェクトが使用されます。もし、スクリプトによってBindingsオブジェクトを変更したい場合はScriptEngine#evalメソッドの第2引数でBindingsオブジェクトを指定します。

サンプルのソース BindingSample2.java

evalメソッドでBindindsオブジェクトを指定した場合は、デフォルトのBindingsオブジェクトはまったく使用されません。2つのBindingsオブジェクトが使われるわけではないので、ご注意ください。

    public BindingSample2() {
        ScriptEngineManager manager = new ScriptEngineManager();
        ScriptEngine engine = manager.getEngineByName("js");
 
        // デフォルトのBindingsオブジェクトに値を保持
        Date now = new Date();
        engine.put("date", now);
 
        // Bindingsオブジェクトを生成し、値を保持させる
        Bindings bindings = engine.createBindings();
        bindings.put("date", "DATE");
 
        try {
            // デフォルトのBindingsオブジェクトを用いて、
            // スクリプトを評価
            engine.eval(SCRIPT);
 
            // Bindingsオブジェクトを指定して、スクリプトを評価
            engine.eval(SCRIPT, bindings);
         } catch (ScriptException ex) {
            System.err.println("スクリプトの実行に失敗しました");
            System.err.println(ex.getMessage());
        }
    }

Bindingsはインタフェースなので、直接newすることはできません。Bindingsオブジェクトの生成には、青字で示したようにScriptEngineインタフェースのcreateBindingsメソッドを使用します。

ここではデフォルトのBindingsオブジェクトを使用した場合と、赤字で示したBindingsオブジェクトを指定した場合で、2度スクリプトを評価しています。

スクリプトは先ほどと同じで、単にdate変数を出力しているだけです。

さて、実行結果はどうなるでしょう。

C:\scripting>java BindingsSample2
Thu Feb 21 23:21:06 JST 2008
DATE

2行目がデフォルトBindingsオブジェクト、3行目がBindingsオブジェクトを指定した場合です。同じスクリプトでもBindingsオブジェクトを変更するだけで、このように動作を変更することが可能です。