![]() ![]() ![]() ![]() Eclipse RCPを好きなスクリプト言語で拡張するこの記事のURLhttp://japan.internet.com/developer/20070828/27.html
著者:Riccardo Govoni
海外internet.com発の記事
はじめにJava Specification Request (JSR) 223は、Javaプラットフォームとスクリプト言語を連係させるための一連のAPIと関連フレームワークを定義します。このAPIは、Java SE 6に標準装備されている標準ライブラリの一部であるため、SE6 JVM上でアプリケーションを実行する場合にはスクリプトサポートを無償で受けられます。これは、Eclipseプラットフォーム上で構築されたアプリケーションにもあてはまります。 JSR-223は、スクリプト言語とJavaプラットフォームとの間で行われるさまざまな対話を定義します。たとえば次のような対話があります。
この記事では、Eclipseプラットフォームと、このプラットフォーム上で構築されるアプリケーションを、これらの新しいJava SE 6機能とスクリプト言語のメリットを利用して拡張する方法を紹介します。Eclipseプラットフォームをスクリプトで拡張する方法を覚えると、以下のことが可能になります。
これらの利点の多くは、スクリプト言語の性質に由来しています。スクリプト言語は、一般的には動的に型付けされ、場合によっては特定の問題分野に固有のものであることもあります。また、通常はコンパイラ型言語よりも読み書きが簡単で、JavaやCなどの古い言語のように「記述→コンパイル→実行」といったサイクルに縛られません。 前提条件この記事は、JSR-223に関する基本的な知識のみがあれば理解できます。実際のところ、理解しておく必要があるのは次のコードだけです。 Map このコードを実行すると、基本的には次の3つの処理が行われます。
このjavax.script.ScriptEngineManagerは、スクリプトエンジンを見つけてインスタンス化します。スクリプトエンジンとは、スクリプトを評価して、効率よく実行するコンポーネントのことです。
これにより、Javaオブジェクトとスクリプト環境間のバインディングが定義され、このようなオブジェクトをスクリプトで処理できるようになります。一般的には、Java環境をスクリプトでコントロールできます。
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で示すように、これは、アプリケーションがサポートするすべてのスクリプト言語に対応します。 図1 com.devx.scriptingプラグインアーキテクチャ ![]() すべてのフラグメントは指定のインタプリタ(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 外部スクリプトの実行これらの基本エレメントだけで、既にEclipseアプリケーション内でカスタムスクリプトを実行する手段を実現できます。たとえば、ユーザーがファイルシステムからスクリプトを選択し、プラットフォーム内で実行するためのEclipseアクションを提供できます。図2と図3は、これを実装した場合の画面例です。 図2 「Run Script」アクション ![]() 図3 サポートされているすべての種類のスクリプトを選択できるファイルセレクタ ![]() 「Run Script」アクションは、ファイルセレクタを表示します。このファイルセレクタは、com.devx.scripting.ScriptSupportクラスに対してクエリを実行することで、使用可能なスクリプト言語のみをフィルタを使って抽出します。クエリを受け取ったcom.devx.scripting.ScriptSupportクラスは、javax.script.ScriptEngineManagerに対してサポート言語を要求します。最後に、javax.script.ScriptEngineManagerが、Service Providerメカニズムを使ってプラグインクラスパスとそのフラグメントをスキャンします。 図のような結果を得るためには、org.eclipse.ui.actionSets拡張ポイントに対して拡張を定義する必要があります。この拡張ポイントは、リスト1のcom.devx.scripting.actions.RunScriptActionクラスによって実装されている、アプリケーションに対する追加メニューアクションを提供します。 必要な作業はこれだけです。これで、アプリケーション内でRuby、Groovyなどのスクリプトを実行して、それぞれのスクリプトの性能や特性を利用することができます。たとえば、ワークスペースの一部で一括変更を行うスクリプトを作成したり、ビルドおよび配備プロセスの一部をスクリプト言語で書いて、必要に応じて開発者がプラットフォームから呼び出せるようにしたりできます。 リスト1 RunScriptActionクラス
public class RunScriptAction implements IWorkbenchWindowActionDelegate { // unneeded methods public void dispose() {} public void init(IWorkbenchWindow window) {} public void selectionChanged(IAction action, ISelection selection) {} public void run(IAction action) { FileDialog dlg = new FileDialog( PlatformUI.getWorkbench().getActiveWorkbenchWindow(). getShell(), SWT.OPEN); List プラットフォームでスクリプトコントリビューションを活用するこれで、次の段階に進む準備ができました。今度は、スクリプト言語を使用してEclipseプラットフォームを直接操作して変更する方法、そしてスクリプトを使ってプラットフォームにコントリビューションを追加する方法を見ていきましょう。まずは、スクリプトコントリビューションとプラットフォームとの間を結ぶバインディングレイヤーの定義が必要です。このレイヤーには、以下のものが含まれます。
JavaScript実装に支えられているEclipseビューの例を見てみます。図4は、この例のサイクル全体を表しています。これを見ると、構成/スタートアップ時に実行されるアクションと、実行時に実行されるアクションの違いがわかります(Eclipseアクションセットなど、他の種類のコントリビューションについては、サンプルコードを参照してください)。 まず、次のコードを使用して、スクリプトコントリビューションを定義します。 <plugin> <extension point="com.devx.scripting.scriptedContribution"> <scriptedView allowMultiple="false" id="com.devx.scripting.jsCalculator name="JavaScript Calculator"> <script extension="js" id="com.devx.scripting.jsCalculator.script" uri="scripts/jsCalculator.js"> script> scriptedView> extension> plugin> この拡張は標準のorg.eclipse.ui.viewsと非常によく似ています。唯一異なるのは追加の ScriptingStartupクラスは、スクリプトコントリビューションと、スクリプトのプロキシとして動作するコントリビューションとの間で変換を行います。リスト2は、変換プロセスに関連する おわかりのように、このコードはスクリプトコントリビューションをコピーして、それを標準のorg.eclipse.ui.viewsに変換するだけです。ビュー実装クラスであるcom.devx.scripting.view.ScriptProxyViewは、Eclipseプラットフォームによるビューへの呼び出しを基幹スクリプトに委任します。たとえば、次のコードは、スクリプトへのレンダリングプロセスの委任を示しており、これは、Eclipseが scriptParent = new Composite(parent,SWT.NONE); scriptParent.setLayoutData(new GridData(GridData.FILL_BOTH)); scriptParent.setLayout(new FillLayout(SWT.HORIZONTAL)); // this call returns the script associated with this view IScript script = getScript(); Map これにより、スクリプトに対する変更がすぐに反映されるため(ビューを閉じて開くだけで確認できます)、開発者はユーザーインターフェイスのプロトタイプを短時間で作成できるようになります。 JavaScriptを使った計算機のサンプル これで、スクリプト言語のメリットを利用して、従来のJavaコードでは実現に手間がかかっていた機能も実現できるようになります。たとえば、単純な計算機なども実現可能です。JavaScriptとその 図5 JavaScript計算機 ![]() リフレッシュボタンを押すとスクリプトが再度読み込まれ、変更が即座にUIに反映されます。 スクリプト言語は、他にも多数のシナリオでEclipse内でインターフェイスを描画するのに役立ちます。たとえば、Groovyを使用してSWTインターフェイスを作成したり、このRubyライブラリを使用してRubyでSWTインターフェイスを作成することで、ビルダパラダイムを利用できます。 リスト2 ScriptingStartupから呼び出される^^getContribution()^^メソッド
public String getContribution(IConfigurationElement el) { StringBuilder sb = new StringBuilder(); sb.append(" リスト3 JavaScript計算機
importPackage(org.eclipse.ui); importPackage(org.eclipse.swt); importPackage(org.eclipse.swt.widgets); importPackage(org.eclipse.swt.events); importPackage(org.eclipse.swt.layout); c = new Composite(parent,SWT.NONE); c.setLayout(new GridLayout(1,true)); t = new Text(c,SWT.BORDER); t.layoutData = new GridData(GridData.FILL_HORIZONTAL); t.editable = false; buttonArea = new Composite(c,SWT.NONE); buttonArea.setLayout(new GridLayout(4,true)); buttonArea.setLayoutData(new GridData(GridData.FILL_BOTH)); var labels = new Array( "Clr","Bck","(",")", "7","8","9","/", "4","5","6","*", "1","2","3","-", "0",".","=","+"); keylistener = { widgetSelected: function(event) { t.text += event.widget.text } , widgetDefaultSelected: function(event) { } } evallistener = { widgetSelected: function(event) { t.text = eval("res = " + t.text) } , widgetDefaultSelected: function(event) { } } clearlistener = { widgetSelected: function(event) { t.text = "" } , widgetDefaultSelected: function(event) { } } backlistener = { widgetSelected: function(event) { s = "" + t.text; // conversion to javascript string t.text = s.substring(0,s.length-1); }, widgetDefaultSelected: function(event) { } } for (i = 0; i < labels.length; i++ ) { b = new Button(buttonArea, SWT.BORDER); b.layoutData = new GridData(GridData.FILL_BOTH); b.text = labels[i]; switch(labels[i]) { case "Clr" : b.addSelectionListener( new SelectionListener(this.clearlistener)); break; case "Bck" : b.addSelectionListener( new SelectionListener(this.backlistener)); break; case "=" : b.addSelectionListener( new SelectionListener(this.evallistener)); break; default: b.addSelectionListener( new SelectionListener(this.keylistener)); } } Eclipseとスクリプト言語との完全統合に向けて以上の説明で、Javaプラットフォームが提供する新しいスクリプト機能とEclipseとの統合がいかに簡単であるかという点についてはおわかりいただけたのではないでしょうか。これで、スクリプトエンジンをEclipseプラグインアーキテクチャに組み込んで、Eclipse環境からスクリプトを実行できます。また、Eclipseプラットフォームのまさに核となる部分でスクリプト言語を使用して、ビューやアクションセットなど、標準の拡張ポイントへの拡張を提供する方法も説明しました。 ここで説明した以外にも、Eclipse MonkeyやEclipseShellなどのプロジェクトが、Eclipseプラットフォームに対してスクリプトサポートを提供しています。これを書いている時点では、この記事の一番の目的であるJSR-223をサポートしていなかったため触れていませんが、両方とも興味深い内容なので、その一部をサンプルコードで使用しました(Monkey Domなど。Eclipse拡張を使ってカスタムオブジェクトをスクリプトに提供しています)。少し時間を割いて見てみてもいいでしょう。 補足説明1 EclipseのRich Client Platform
EclipseのRich Client Platform(RCP)は、アプリケーション開発のための完全に整備された基盤を提供するという目的で選び出されたEclipseプラットフォームのコアコンポーネントの集まりです。RCPを使用すると、設定管理、更新管理、GUIエレメントなど、すべてのアプリケーションに共通の機能を手動でプログラムする必要がなくなり、アプリケーション固有のロジックに集中できます。
補足説明2 Eclipseプラグインのしくみ
Eclipseプラットフォームのプラグインは、このプラットフォームに機能を追加するためのもので、プロプライエタリなプラグインとユーザー提供のプラグインの両方があり、メニュー、ツールバー、ビュー、エディタ、ウィザード、基幹サービスなど、あらゆる形式のプラグインが使われています。プラグインは、他のプラグインに対して拡張を宣言することで、そのプラグインに依存できます。また、他のプラグインに対して拡張ポイントを公開することもできます。この観点から見ると、Eclipseというプラットフォームは、それ全体が一連の相互接続された通信プラグインにすぎません。具体的に説明すれば、プラグインは、「plugin.xml」記述子ファイルを使用して依存と拡張ポイントを宣言しているただのjarファイルです。プラグインには、ゼロ以上のフラグメントを含めることができます。プラグインフラグメントは、そのターゲットプラグインに対して追加機能を提供します。実行時には、これらのフラグメントがターゲットプラグインにマージされます。フラグメントを使用すると、プラグインの一部をオプションにすることができ、プラグインを部分的にインストールしたり、アンインストールしたり、更新したりすることが可能になります。
参考資料
著者紹介Riccardo Govoni(Riccardo Govoni)
2003年からJ2EE開発者としてイタリアの金融サービス会社に勤務。ソフトウェアアーキテクトとして従来の銀行システムのWebフロントエンドの設計に従事。物理学修士。SCJP(Sun Certified Java Programmer)資格を持つ。
|