はじめに
Java Specification Request (JSR) 223は、Javaプラットフォームとスクリプト言語を連係させるための一連のAPIと関連フレームワークを定義します。このAPIは、Java SE 6に標準装備されている標準ライブラリの一部であるため、SE6 JVM上でアプリケーションを実行する場合にはスクリプトサポートを無償で受けられます。これは、Eclipseプラットフォーム上で構築されたアプリケーションにもあてはまります。
JSR-223は、スクリプト言語とJavaプラットフォームとの間で行われるさまざまな対話を定義します。たとえば次のような対話があります。
- インタプリタ型スクリプトをJavaアプリケーションに埋め込む
- Javaオブジェクトをスクリプトコンテキスト内から変更およびコントロールする
- Java言語を使用してスクリプトインタプリタを書き、公開する
この記事では、Eclipseプラットフォームと、このプラットフォーム上で構築されるアプリケーションを、これらの新しいJava SE 6機能とスクリプト言語のメリットを利用して拡張する方法を紹介します。Eclipseプラットフォームをスクリプトで拡張する方法を覚えると、以下のことが可能になります。
- 統合開発環境内でよく行う繰り返しの操作を自動化する
- ユーザーインターフェイスとコントロールロジックの両方をその場で変更することにより、ユーザーインターフェイスのプロトタイプを短時間で作成する
- アプリケーションユーザーが一般的な環境設定の域を超えてアプリケーションをカスタマイズできるようにする。たとえばユーザーがお気に入りのスクリプトまたはドメイン固有言語(DSL)にロジックを追加できるようにする
これらの利点の多くは、スクリプト言語の性質に由来しています。スクリプト言語は、一般的には動的に型付けされ、場合によっては特定の問題分野に固有のものであることもあります。また、通常はコンパイラ型言語よりも読み書きが簡単で、JavaやCなどの古い言語のように「記述→コンパイル→実行」といったサイクルに縛られません。
前提条件
この記事は、JSR-223に関する基本的な知識のみがあれば理解できます。実際のところ、理解しておく必要があるのは次のコードだけです。
Map<String,Object> vars = getScriptVariables(); // fictional method String scriptBody = getScriptBody(); // fictional method ScriptEngineManager sem = new ScriptEngineManager(); ScriptEngine engine = sem.getEngineByExtension("js"); for (String key : vars.keySet()) engine.put(key, vars.get(key)); engine.eval(scriptBody); // ScriptException handling code omitted
このコードを実行すると、基本的には次の3つの処理が行われます。
- javax.script.ScriptEngineManagerを作成する(サポートされている拡張で検索)
ScriptEngine.put(String,Object)
メソッドで、一連のスクリプト変数を設定する
- 指定されたスクリプトを、
eval(String script)
メソッドで評価する
JVMは、Service Providerメカニズムを使って使用可能なエンジンを探します。このとき、アプリケーションで使用できるjarファイルのMETA-INF/サービスディレクトリをスキャンして、特定の構成ファイルを検索します。スクリプトエンジンをアプリケーションで使用できるようにするには、そのスクリプトエンジンを含む、正しく構成されたjarファイルをクラスパスに追加します。この記事では、dev.java.netのスクリプトプロジェクトで提供されているスクリプトエンジンのいくつか、たとえばRubyやGroovyのエンジンを使用します。
今回のサンプルではスクリプトAPIの使用をかなり抑えていますが、チュートリアルとしての目的は十分に果たしているのではないかと思います。詳細については、最後に紹介する参考資料を参照してください。
スクリプトプラグインとそのフラグメント
この記事のサンプルコードでは、スクリプト言語とEclipse(補足説明1「EclipseのRich Client Platform」を参照。補足説明はこの記事の最後にあります)を、com.devx.scriptingプラグイン(補足説明2「Eclipseプラグインのしくみ」を参照)を介して接続しています。これにより、プラットフォームの他の部分が、スクリプトリソースと一連のプラグインフラグメントに共通アクセスできるようになります。図1で示すように、これは、アプリケーションがサポートするすべてのスクリプト言語に対応します。
すべてのフラグメントは指定のインタプリタ(Ruby、JavaScript、AppleScriptなど)とそのJSR-223エンジンを提供しており、そのインタプリタをjavax.scriptスクリプトAPIを通じて公開します。インタプリタとJSR-223ラッパーは、セットにすることも、個別に開発して提供することもできます。
このプラグインセットアップにはさまざまなメリットがあり、たとえば次のようなことが考えられます。
- 分散フラグメントの数を制限し、プラットフォームに追加する言語を明確にコントロールできる。
- ユーザーが必要なスクリプト言語のみを更新サイトから選択できるため、アプリケーションのインストールに伴う自分の作業を軽減できる。
プラグインは次のようなIScriptインターフェイスを定義します。
public interface IScript { // @return the URI which points to the script code. public String getURI(); // @return the script extension public String getExtension(); // @return a Reader which points to the script code public Reader getReader() throws IOException; // @return the script unique id public String getId(); // @return the namespace (plug-in id) of the script public String getNamespace(); // @return run the script in a modal context? public boolean isModal(); }
スクリプトプラグインはcom.devx.scripting.ScriptSupportクラスを公開し、Eclipseプラットフォームで発生するスクリプト関連の一般的なニーズに対応するためにパブリックメソッドを定義します。一般的なニーズとしては、たとえば、進捗状況のモニタ(Eclipseでソースをコンパイルしているときに表示されるモニタなど)に照らしてスクリプトを実行する、ScriptEngineManagerに対してクエリを実行してサポート言語の一覧を取得する、などの処理が考えられます。次のコードは、クラスのパブリックインターフェイスの一部を示しています(実装についてはダウンロードサンプル中のソースコードを参照)。
public void runScript(final IScript script, Map<String,Object> params) throws ScriptException; public List<Language> getSupportedLanguages();