JSR 223: Scripting for the Java Platform

現在、Javaプラットフォームではスクリプト言語のサポートが急速に進められている。同時に、Java VM上で動作するさまざまなスクリプト言語の実装が登場しており、JavaプラットフォームはすでにJava言語という枠を飛び越え、マルチ言語環境に進化しようとしている。本連載第31回で紹介したBeanShellも、Javaベースのスクリプト言語のひとつだ。

今回取り上げるのは、そのようなさまざまなスクリプト言語とJavaを結びつける役割を果たす「JSR 223: Scripting for the Java Platform」だ。ご存知のように同APIはJava SE 6よりJavaの標準クラスライブラリとして統合されている。JSR 223ではJavaプログラム内で各種スクリプトを実行する、およびスクリプト側からJavaプログラムにアクセスするための標準的な方法が提供される。

JDK 6にはJavaで実装されたJavaScriptエンジンであるRhinoがバンドルされており、標準で利用できるようになっている。その他のスクリプト言語についても対応が進められていて、scripting.dev.java.netで公開されている最新の参照実装には各種エンジンが搭載されている。

最新の参照実装でいろいろなスクリプトを実行する

まず、JDK 6でJavaScriptを利用する方法をおさらいしておこう。スクリプトの実行にはjavax.script.ScriptEngineクラスを利用する。まずファクトリクラスであるjavax.script.ScriptEngineManagerのインスタンスを生成し、そこからScriptEngineを取得してeval()メソッドでスクリプトを実行すればよい。具体的にはリスト1のようになる。

リスト1 ScriptingForJavaScript.java - JavaプログラムでJavaScriptを実行

package apisample;

import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;

public class ScriptingForJavaScript {
    public static void main(String[] args) throws Exception {
        ScriptEngineManager factory = new ScriptEngineManager();
        ScriptEngine engine = factory.getEngineByName("js");

        try {
            engine.eval("print(\"Hello, JavaScript!\")");
        } catch (ScriptException ex) {
            ex.printStackTrace();
        }
    }
}

JavaScriptは標準でサポートされているので、ScriptingForJavaScript.javaは通常通りコンパイル/実行できる。

さて、JavaScript以外のスクリプト言語を利用するには別途JSR 223の参照実装を入手しなければならない。最新の実装は前述のプロジェクトサイトにあるドキュメント&ファイルセクションからダウンロードできる。jsr223-engines.zipまたは jsr223-engines.tar.gzを任意の場所 (ここでは[JSR223-ENGINES]と記載する) に解凍して使用する。

配布ファイルにはサポートされるスクリプトエンジン用のフォルダが含まれており、各フォルダ毎に以下の3つのフォルダが用意されている。

  • bin - コマンドライン環境実行用のシェルスクリプトまたはbatファイルを格納
  • build - JSR 223対応のエンジンを格納
  • lib - 各スクリプトの実装を格納

そのうちbinとbuildについてはすでに用意されているが、libに格納する実装だけはそれぞれのプロジェクトサイトから別途ダウンロードしてこなければならない。必要とするバージョンについてはREADME.TXTに記載されている。

ここではBeanShellを使用してみる。BeanShellの場合はバージョン2.0 Beta5が必要と書かれているが、BeanShellの公式サイトで配布されている最新版は2.0 Beta4なのでこれを使う。このページよりbsh-2.0b4.jarをダウンロードし、[JSR223-ENGINES]\beanshell\libに配置する。

このbsh-2.0b4.jarとbuildフォルダのbsh-engine.jarをクラスパスに含めることで、JavaプログラムからBeanShellを利用できるようになる。ためしにJDK 6付属のjrunscriptコマンドを使ってみよう。jrunscriptコマンドはqオプションで利用可能なスクリプト言語を調べることができ、lオプションでコマンドラインモードでスクリプトを実行できる(プロンプト1)。

プロンプト1 jrunscriptコマンドでBeanShellを利用する

> jrunscript -cp [JSR223-ENGINES]/build/bsh-engine.jar;[JSR223-ENGINES]/lib/bsh-2.0b4.jar -q
Language BeanShell 2.0b5 implemention "BeanShell Engine" 1.0
Language ECMAScript 1.6 implemention "Mozilla Rhino" 1.6 release 2

> jrunscript -cp [JSR223-ENGINES]/build/bsh-engine.jar;[JSR223-ENGINES]/lib/bsh-2.0b4.jar -l bsh
beanshell>

続いてJavaプログラムからBeanShellスクリプトを実行してみよう。手順はJavaScriptの場合とまったく同じで、リスト2のようにする。ここでは簡単なウィンドウを表示させるスクリプトを実行している(第31回参照)。

リスト2 ScriptingForBeanShell.java- JavaプログラムからBeanShellスクリプトを実行

package apisample;

import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;

public class ScriptingForBeanShell {
    public static void main(String[] args) throws Exception {
        ScriptEngineManager factory = new ScriptEngineManager();
        ScriptEngine engine = factory.getEngineByName("bsh");

        try {
            engine.eval(
                    "import javax.swing.*;" + 
                    "f = new JFrame(\"BeanShell Sample\");" + 
                    "f.getContentPane().setLayout(new FlowLayout());" + 
                    "button1 = new JButton(\"Hello\");" + 
                    "button2 = new JButton(\"BeanShell!\");" + 
                    "f.getContentPane().add(button1);" + 
                    "f.getContentPane().add(button2);" + 
                    "f.setSize(300, 200);" + 
                    "f.setVisible(true);"
            );
        } catch (ScriptException ex) {
            ex.printStackTrace();
        }
    }
}

コンパイルおよび実行は、スクリプトエンジンのJARファイルをクラスパスに含めてプロンプト2のように行う。実行するとBeanSehllスクリプトによって図1のようなウィンドウが表示されるはずだ。

プロンプト2 リスト2を実行

> javac -cp ./;[JSR223-ENGINES]/build/bsh-engine.jar;[JSR223-ENGINES]/lib/bsh-2.0b4.jar apisample\ScriptingForBeanShell.java
> java -cp ./;[JSR223-ENGINES]/build/bsh-engine.jar;[JSR223-ENGINES]/lib/bsh-2.0b4.jar apisample.ScriptingForBeanShell

図1 BeanShellスクリプトによって生成されたウィンドウ

次に、Javaプログラムからスクリプトのメソッドをコールしてみよう。メソッドのコールには、スクリプトエンジン(ScriptEngine実装)がjavax.script.Invocableインタフェースをimplementsしている必要がある。しかしBeanShellのエンジンはこれをimplementsしていないので(TODOということになっている)、今回はInvocableをサポートしているGroovyを使って試してみることにする。

README.TXTによればGroovyを使うには1.1-beta-2が必要とのことなので、公式サイトのリポジトリより「groovy-all-1.1-beta-2.jar」をダウンロードして[JSR223-ENGINES]\groovy\libに配置する。

プログラムはリスト3のようになる。ここではGroovyスクリプトでadd()メソッドを定義し、それをJavaプログラムからInvocableのinvokeFunction()を利用して呼び出している。

リスト3 ScriptingForGroovy.java - JavaプログラムからGroovyのメソッドを呼び出す

package apisample;

import javax.script.Invocable;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;

public class ScriptingForGroovy {
    public static void main(String[] args) throws Exception {
        ScriptEngineManager factory = new ScriptEngineManager();
        ScriptEngine engine = factory.getEngineByName("groovy");

        String script = "def add(x, y) { x + y }";
        try {
            engine.eval(script);
            Invocable invocable = (Invocable)engine;
            Object[] params = { new Integer(50), new Integer(100) };
            Object result = invocable.invokeFunction("add", params);
            System.out.println(result);
        } catch (ScriptException ex) {
            ex.printStackTrace();
        }
    }

}

コンパイルおよび実行は、Groovyのスクリプトエンジンをクラスパスに含めてプロンプト3のように行う。

プロンプト3 リスト3の実行

> javac -cp ./;[JSR223-ENGINES]/build/groovy-engine.jar;[JSR223-ENGINES]/lib/groovy-all-1.1-beta-2.jar apisample\ScriptingForGroovy.java
> java -cp ./;[JSR223-ENGINES]/build/groovy-engine.jar;[JSR223-ENGINES]/lib/groovy-all-1.1-beta-2.jar apisample.ScriptingForGroovy
150

JSR 223を使用すれば、JavaScriptだけでなくさまざまなスクリプト言語を共通の方法で利用できる。今回はBeanShellとGroovyを取り上げたが、ぜひ他のスクリプト言語も試してみてほしい。なお、Java SE 7ではJavaScriptに加えてBeanShellやGroovy、JRubyなどといったスクリプト言語の標準サポートが検討されている。