Eclipse

ソフトウェアを上手くモジュール化して実装することの難しさは誰もが知っています。多様なコミュニティによって書かれた巨大なコードベースの相互運用の管理もまた困難です。Eclipse において、私たちはこの両方を成し遂げました。2010 年 6 月には Helios という名前のリリースが Eclipse Foundation によって公開されましたが、このリリースには 39 のプロジェクトが含まれ、40 以上の会社から 490 人のコミッターが同じアーキテクチャ基盤の上にプログラムを作成しました。この Eclipse アーキテクチャは初期バージョンにおいてどのようなものだったのでしょうか?どのように進化してきたのでしょうか?アプリケーションのアーキテクチャがどのようにしてコミュニティの管理と成長を助けてきたのでしょうか?まずはその起源を見ましょう。

2001 年 12 月 7 日、Eclipse 1.0 というオープンソースプロジェクトがリリースされました。当時の Eclipse に付いていた説明文は「何にでも使える、用途の決められていない統合開発環境」です。説明文が抽象的な理由は、Eclipse がビジョンに据えたアーキテクチャがフレームワークであり、“yet another”なツールの集合体ではなかったからです。Eclipse が目指したのはモジュール化された、スケーラブルなフレームワークでした。Eclipse は開発者のためのツールを開発するときの基礎となるコンポーネントベースのプラットフォームを提供します。Eclipse の拡張性の高いアーキテクチャはコミュニティを刺激し、コアプラットフォームはオリジナルのバージョンの制限を超えて拡張されました。当初の Eclipse はプラットフォームであり、Eclipse SDK は概念実証に過ぎません。つまり Eclipse SDK はセルフホストでき、Eclipse SDK 自身を使った新しいバージョンの Eclipse のビルドも可能でした。

オープンソースの開発者というと、他者を助けることに熱心で、夜遅くまで大変な苦労をしてバグを直し、興味の赴くままにファンタスティックな新機能を実装しているというのがステレオタイプなイメージです。しかし Eclipse プロジェクトの初期の歴史を振り返ってみると、このイメージは必ずしも当てはまりません。初期のコードのいくらかは IBM によって開発された VisualAge for Java から受け継いだものであり、Eclipse というオープンソースのプロジェクトの最初のコミッターは Object Technology International (OTI) という IBM の子会社の従業員でした。この従業員たちはオープンソースプロジェクトにフルタイムで取り組み、ニュースグループの質問に答え、バグを見つけ、新しい機能を実装することで給料を得ていたのです。後には興味を持ったソフトウェアのベンダーからなるコンソーシアムがこのオープンなツールの活動を拡張するために作られています。このコンソーシアム (Eclipse consortium) の初期メンバーは Borland, IBM, Merant, QNX Software System, Rational Software, Redhat, SuSE, TogetherSoft です。

Eclipse プロジェクトに参加した会社は Eclipse のノウハウを手に入れることになり、それが Eclipse を使った商用製品の出荷に繋がりました。自社の製品を支えているオープンソフトウェアの改善に従業員を動員するという意味で、これは Linux カーネルに様々な会社が出資するのに似ています。2004 年初頭には Eclipse コミュニティの成長を管理、拡大するための非営利団体 Eclipse Foundation が設立されました。資金は法人メンバーによって支えられ、委員会によって運営されます。Eclipse のコミュニティは広がり続け、現在 Eclipse Foundation には 170 社以上の企業と約 1000 人のコミッターが所属しています。

かつて“Eclipse”と言えばその SDK のことを指しましたが、今ではもっと広範なものを指します。2010 年 6 月の時点で 250 個のプロジェクトが eclipse.org の下で開発されおり、これらのツールによって C/C++ や PHP を使った開発、ウェブサービスの開発、モデルドリブン開発、各種ビルドツールの利用が可能になります。これらのプロジェクトはプロジェクト管理委員会 (project management committee, PMC) によって管理され、この委員会のメンバーは技術的価値やリリース目標において実績を示したプロジェクトのメンバーからなります。話を簡単にするために、この章で扱うのは Eclipse SDK のアーキテクチャの進化の中でも Eclipse1 および Runtime Equinox2 に関するものだけとします。集中して解説するのは初期の Eclipse および 3.0、3.4、4.0 リリースです。

初期の Eclipse

二十一世紀の初めの時点でソフトウェア開発者のためのツールはたくさんありましたが、組み合わせて使うことができるのはごく少数でした。そんな中で Eclipse が目指したのは、アプリケーション開発者が相互運用可能なツールを作成するためのオープンソースプラットフォームです。これがあれば開発者は新しいツールの開発に集中でき、ファイルシステムとのやり取り、ソフトウェアの更新、ソースコードリポジトリとの通信といったインフラの問題から解放されます。Eclipse はおそらく Java Development Tools (JDT) としてもっとも良く知られていますが、JDT は他の言語に対するツールを作成しようと思っている開発者のためのサンプルとして作られました。

Eclipse のアーキテクチャを掘り下げる前に、Eclipse SDK が開発者にどう映るかを見ましょう。Eclipse を起動してワークベンチを選択すると、Java パースペクティブが表示されます。パースペクティブは現在使用中のツールに固有のビューやエディタの管理を行います。

Java パースペクティブ
図 6.1. Java パースペクティブ

初期バージョンの Eclipse SDK アーキテクチャは主に三つの部分からなっており、それぞれがサブプロジェクトに対応していました。つまり Eclipse プラットフォーム、JDT (Java Development Tools)、PDE (Plug-in Development Environment) の三つです。

プラットフォーム

Eclipse プラットフォームは Java で書かれており、実行には Java VM が必要です。Eclipse はプラグインという少数の機能を持つ単位が合わさってできており、このプラグインが Eclipse のコンポーネントモデルにおける基礎となります。プラグインの実体は JAR ファイルです。このファイルにはプログラムが含まれるほか、説明文、依存関係、使用方法、拡張方法などを含んだマニフェストも含まれています。初期バージョンではマニフェスト情報は plug-in.xml というプラグインディレクトリのルートに置かれたファイルに書かれていました。Java Development Tools は Java 開発のためのプラグインを提供し、Plug-in Development Environment (PDE) は Eclipse を拡張するプラグイン開発のためのツールを提供します。Eclipse のプラグインは Java で書かれますが、オンラインドキュメントのための HTML ファイルなどのコードでないファイルも含むことができます。各プラグインにはクラスローダーがあり、他のプラグインに対する依存関係は plugin.xmlrequires 文を使って指定されます。以下に示すのは org.eclipse.ui プラグインに対する plugin.xml です。名前やバージョン、そして他のプラグインからインポートする必要のある依存関係が列挙されています。

<?xml version="1.0" encoding="UTF-8"?>
<plugin
   id="org.eclipse.ui"
   name="%Plugin.name"
   version="2.1.1"
   provider-name="%Plugin.providerName"
   class="org.eclipse.ui.internal.UIPlugin">

   <runtime>
      <library name="ui.jar">
         <export name="*"/>
         <packages prefixes="org.eclipse.ui"/>
      </library>
   </runtime>
   <requires>
      <import plugin="org.apache.xerces"/>
      <import plugin="org.eclipse.core.resources"/>
      <import plugin="org.eclipse.update.core"/>
      :       :        :
      <import plugin="org.eclipse.text" export="true"/>
      <import plugin="org.eclipse.ui.workbench.texteditor" export="true"/>
      <import plugin="org.eclipse.ui.editors" export="true"/>
   </requires>
</plugin>

ユーザーに Eclipse プラットフォームを使った開発を行ってもらうには、プラットフォームに対するコントリビューションを行うための仕組み、そしてそのコントリビューションを受け入れる仕組みが必要です。Eclipse のコンポーネントモデルにおけるエクステンションとエクステンションポイントはこのために導入されました。他の拡張機能から利用可能なインターフェースはエクスポート ID によって指定され、これによってエクスポートしているプラグインの外側から利用可能なクラスが制限されます。パブリックなクラスとメソッドの全てが利用者から利用可能になることはありません。エクスポートされたプラグインはパブリックな API とみなされ、その他全てはプライベートな実装詳細とみなされます。例えば Eclipse のツールバーのメニューに項目として現れるプラグインを書くには、 org.eclipse.ui プラグインの actionSets エクステンションポイントを使います。このエクステンションポイントは次のように定義されています:

<extension-point id="actionSets"  name="%ExtPoint.actionSets"  schema="schema/actionSets.exsd"/>
<extension-point id="commands"    name="%ExtPoint.commands"    schema="schema/commands.exsd"/>
<extension-point id="contexts"    name="%ExtPoint.contexts"    schema="schema/contexts.exsd"/>
<extension-point id="decorators"  name="%ExtPoint.decorators"  schema="schema/decorators.exsd"/>
<extension-point id="dropActions" name="%ExtPoint.dropActions" schema="schema/dropActions.exsd"/>

開発者が書くプラグインのエクステンションは次のようになります。このコードによって org.eclipse.ui.actionSet エクステンションポイントにメニュー項目が追加されます。

<?xml version="1.0" encoding="UTF-8"?>
<plugin
   id="com.example.helloworld"
   name="com.example.helloworld"
   version="1.0.0">
   <runtime>
      <library name="helloworld.jar"/>
   </runtime>
   <requires>
      <import plugin="org.eclipse.ui"/>
   </requires>
   <extension
         point="org.eclipse.ui.actionSets">
      <actionSet
            label="Example Action Set"
            visible="true"
            id="org.eclipse.helloworld.actionSet">
         <menu
               label="Example &Menu"
               id="exampleMenu">
            <separator
                  name="exampleGroup">
            </separator>
         </menu>
         <action
               label="&Example Action"
               icon="icons/example.gif"
               tooltip="Hello, Eclipse world"
               class="com.example.helloworld.actions.ExampleAction"
               menubarPath="exampleMenu/exampleGroup"
               toolbarPath="exampleGroup"
               id="org.eclipse.helloworld.actions.ExampleAction">
         </action>
      </actionSet>
   </extension>
</plugin>

Eclipse を起動すると、ランタイムのプラットフォームがインストール済みのプラグインのマニフェストをスキャンし、プラグインレジストリをメモリ上に作成します。エクステンションポイントと対応するエクステンションは名前でマップされます。こうしてできるプラグインレジストリは Eclipse プラットフォームを通して提供される API を使って参照することが可能です。レジストリはディスクにキャッシュされ、次回の Eclipse の起動ではそれがロードされます。全てのプラグインは起動時に発見されてレジストリに登録されますが、コードが実際に使われるまでは有効化 (クラスがロード) されません。このアプローチは遅延有効化 (lazy activation) と呼ばれます。クラスのロードを必要になるまで遅らせることで、新しいバンドルの追加によるパフォーマンスへの影響を小さくできます。例えば org.eclipse.io.actionSet のエクステンションポイントに追加されたプラグインは、ツールバー内に新しく追加される項目をユーザーが選択するまで有効化されません。

メニューの例
図 6.2. メニューの例

このメニュー項目は次のコードで作られます:

package com.example.helloworld.actions;

import org.eclipse.jface.action.IAction;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.IWorkbenchWindowActionDelegate;
import org.eclipse.jface.dialogs.MessageDialog;

public class ExampleAction implements IWorkbenchWindowActionDelegate {
    private IWorkbenchWindow window;

    public ExampleAction() {
    }

    public void run(IAction action) {
        MessageDialog.openInformation(
            window.getShell(),
            "org.eclipse.helloworld",
            "Hello, Eclipse architecture world");
    }

    public void selectionChanged(IAction action, ISelection selection) {
    }

    public void dispose() {
    }

    public void init(IWorkbenchWindow window) {
        this.window = window;
    }
}

ユーザーがツールバー内の新しい項目を選択すると、エクステンションポイントを実装するプラグインがエクステンションレジストリに問い合わせを行います。その後エクステンションを提供するプラグインがコントリビューションをインスタンス化し、プラグインをロードします。プラグインが有効化されると、この例では ExampleAction のコンストラクタが実行され、続いて IWorkbenchWindowActionDelegate が初期化されます。ワークベンチの選択項目が変更されデレゲーションが作成されることから、アクションが変更されます。つまり“Hello Eclipse architecture world”と書かれたメッセージダイアログが表示されるということです。

この拡張可能なアーキテクチャこそが Eclipse エコシステムの成長を可能にしたものの一つです。企業や個人はプラグインを個別に開発でき、さらにそれをオープンソースあるいは商用で配布できます。

Eclipse で最も重要なコンセプトの一つは、全てがプラグインであることです。Eclipse プラットフォームに含まれるものであれ、自分で書いたものであれ、全てのプラグインはアプリケーション全体においてファーストクラスのコンポーネントとして扱われます。初期バージョンの Eclipse に入っていた機能をまとまりごとに示すと 図 6.3 のようになります。

初期の Eclipse アーキテクチャ
図 6.3. 初期の Eclipse アーキテクチャ

ワークベンチはユーザーが最も良く目にする UI 要素であり、デスクトップにおける Eclipse の表示を管理するための構造を提供します。ワークベンチはパースペクティブ、ビュー、エディタからなっています。エディタにはファイルの種類が関連付いており、ファイルを開いたときには対応するエディタが起動します。ビューの例としては、Java コードのエラーや警告を表示する“problems”ビューがあります。エディタとビューが合わさってパースペクティブとなり、パースペクティブが各ツールを整った形でユーザーに表示します。

Eclipse のワークベンチは Standard Widget Toolkit (SWT) と JFace の上に作られています。SWT については少し説明が必要でしょう。ウィジェットツールキットはネイティブのものとエミュレートされたものに分類されます。ネイティブのウィジェットツールキットではオペレーティングシステムの関数を使ってリストやプッシュボタンなどの UI コンポーネントが作成され、コンポーネントとの対話はオペレーティングシステムが行います。これに対してエミュレートされたウィジェットツールキットでは、UI コンポーネントがオペレーティングシステムの外側で作成され、マウスやキーボード、描画、フォーカスなどの機能が独自に実装されます。どちらのデザインにも利点と欠点があります。

ネイティブのウィジェットツールキットは“ピクセル単位で完璧 (pixel perfect)”であり、ユーザーのデスクトップで動く他のアプリケーションと同じような見た目となります。オペレーティングシステムは定期的にウィジェットの見た目と動作を変更しますが、ネイティブのウィジェットツールはこの更新をタダで受け取れます。ただシステムウィジェットの実装はオペレーティングシステムごとに大きく異なるので、ネイティブのウィジェットツールキットは実装が難しく、見た目が一貫せず、プログラムもポータブルでなくなります。

エミュレートされたウィジェットツールキットは独自の見た目と動作を持つこともできますし、オペレーティングシステムのものに似せることもできます。ネイティブツールと比べたときの大きな利点は柔軟性です (ただし Windows Presentation Platform (WPF) のような現代的なネイティブツールキットも同じぐらいの柔軟性を持ちます)。ウィジェットを実装するコードがツールキットの一部でありオペレーティングシステムに組み込まれていないので、ウィジェットの見た目と動作は好きに変更でき、プログラムの可搬性は高まります。エミュレートされたウィジェットツールキットというものが表れ出したころの評判はひどいもので、動作は遅く、オペレーティングシステムのエミュレートはお粗末で、ウィジェットがデスクトップからはみ出してしまうこともしょっちゅうありました。中でも当時の Smalltalk-80 プログラムはエミュレートされたウィジェットの見た目が分かりやすく、ユーザーは“Smalltalk のプログラム”を使っていることがすぐに分かったので、Smalltalk を使ったアプリケーションの信用を損ねる結果となりました。

C や C++ のような他のプログラミング言語と異なり、Java の一番最初のバージョンには Abstract Window Toolkit (AWT) と呼ばれるネイティブのウィジェットツールキットライブラリが付属していました。しかし AWT は機能の少なくてバグの多い、見た目の一貫しないライブラリとみなされ、方々から非難を受けました。この AWT の経験もあり、Sun など様々な場所で「ポータブルでパフォーマンスの良いネイティブのウィジェットツールキットなんてものは不可能なのではないか」とささやかれていました。しかしこの問題は、完全な機能を持ったエミュレートされたウィジェットツールキット Swing によって解決されます。

1999 年ごろ、OTI は Java を使って VisualAge Micro Edition と呼ばれる製品を作成していました。VisualAge Micro Edition の最初のバージョンは Swing を利用していましたが、OTI が Swing を使った感触はあまり良くありませんでした。初期の Swing にはバグが多く、タイミングやメモリ周りに問題を抱え、当時のハードウェアでは許容できるパフォーマンスを達成できませんでした。一方で OTI は Smalltalk-80 用のネイティブのウィジェットツールおよびいくつかの Smalltalk 実装も作成しており、このときの Smalltalk の評判は上々でした。この経験が SWT の初期バージョンの作成で活かされます。VisualAge Micro Edition と SWT の開発は成功し、SWT は Eclipse の開発を開始するときの自然な選択肢となりました。Eclipse が Swing ではなくて SWT を選択したことで Java コミュニティは分断され、陰謀論さえあったほどです。しかし Eclipse は成功し、SWT を使ったことこそが Eclipse と他の Java プログラムの間の主な違いとなっています。Eclipse はパフォーマンスが良く、ピクセル単位で完璧であり、人々からは「これが Java で書かれたプログラムとは信じられない」という声が聞かれました。

初期の Eclipse SDK を利用できるのは Linux と Windows だけでしたが、2010 年現在では数十のプラットフォームがサポートされています。開発者がアプリケーションをひとつのプラットフォームに対して書けば、それだけで複数のプラットフォームでデプロイが可能です。Java 用の新しいウィジェットツールキットの開発は Java コミュニティ内で論争の的のなった問題でしたが、Eclipse のコミッターたちはデスクトップで最良のネイティブ GUI を構築することが労力に見合うと感じていました。この確信は現在でも薄らいでおらず、SWT を使うコードは何百万行も存在します。

JFace は SWT の上のレイヤーであり、UI プログラミングで必要になる一般的なタスクのためのツールを提供します。例えば設定やウィザードのためのフレームワークなどです。SWT と同じように様々ウィンドウシステムで動作するように設計されていますが、JFace にはネイティブのプラットフォームコードは一切含まれず、Java のコードだけが含まれます。

プラットフォームからは統合ヘルプシステムも提供されます。ヘルプはトピックと呼ばれる小さな情報の単位の集合であり、トピックはラベルと位置への参照からなります。ここで位置とは HTML ドキュメント、あるいは追加リンクを表す XML ドキュメントです。トピックはまとめられて目次 (table of content, TOC) となります。トピックを葉と考えれば、TOC は枝です。 org.eclipse.help.toc エクステンションポイントにコントリビュートすれば、自分で書いたアプリケーションに関するヘルプコンテンツを追加できます。次に示すのは、org.eclipse.platform.doc.isv にヘルプコンテンツを追加する plugin.xml の例です。

<?xml version="1.0" encoding="UTF-8"?>
<?eclipse version="3.0"?>
<plugin>

<!-- ===================================================================== -->
<!-- Define primary TOC                                                    -->
<!-- ===================================================================== -->
   <extension
         point="org.eclipse.help.toc">
      <toc
            file="toc.xml"
            primary="true">
      </toc>
      <index path="index"/>
   </extension>
<!-- ===================================================================== -->
<!-- Define TOCs                                                           -->
<!-- ===================================================================== -->
   <extension
         point="org.eclipse.help.toc">
      <toc
            file="topics_Guide.xml">
      </toc>
      <toc
            file="topics_Reference.xml">
      </toc>
      <toc
            file="topics_Porting.xml">
      </toc>
      <toc
            file="topics_Questions.xml">
      </toc>
      <toc
            file="topics_Samples.xml">
      </toc>
   </extension>

オンラインのヘルプドキュメントのインデックスと検索には Apache Lucene が使われます。初期の Eclipse ではオンラインヘルプが Tomcat を使ったウェブアプリケーションとして実装されていました。あるいは Eclipse 内部でヘルプを提供する方針を取るなら、ヘルププラグインの一部を使ってスタンドアローンのヘルプサーバーを提供できます3

他にも Eclipse にはソースコードリポジトリでパッチを作るなどの各種の作業を行うためのチームサポートがあります。ワークスペースにはファイルシステム上の作業データに関するファイルとメタデータがまとまっており、Java コード中の問題を追跡するためのデバッガーもあります。言語固有のデバッガーを作るためのフレームワークももちろんあります。

Eclipse プロジェクトの目標の一つは、Eclipse テクノロジをオープンソースおよび商用で利用する人々が必要に応じてプラットフォームを拡張できるようにすることです。これを達成するための手段の一つが、安定した API です。API はアプリケーションの動作を指定する技術的かつ社会的な契約であると言えます。Eclipse プロジェクトおけるマントラは「API は永遠なり」です。API は時を超えて無限に使われることを想定しているのですから、その設計には注意深くならなければなりません。安定した API とはクライアント (API を使う人々) とプロバイダの間の契約です。この契約があってこそ、長期間に渡ってクライアントのコードを書き直さなくてもよい API が Eclipse プラットフォームから提供されることが保証され、クライアントは安心してそれを使うことができます。優れた API は柔軟で、実装を進化させるものであることが必要です。

Java 開発ツール (JDT)

JDT は Java のエディタ、ウィザード、リファクタリングのサポート、デバッガ、コンパイラ、インクリメンタルビルドを提供します。コンパイラはコンテンツのアシストやナビゲートなどの編集機能のためにも使われます。Eclipse には Java SDK が付いてこないので、ユーザーは好きなバージョンの SDK をデスクトップにインストールできます。ユーザーの Java コードを Eclipse 内でコンパイルするときには JDT チームが独自に書いたコンパイラが使われています。その理由は、JDT チームが VisualAge Micro Edition の開発から受け継いだコンパイラを最初に持っており、そのコンパイラに付け足す形でツールの開発が計画されたので、独自のコンパイラを書くというのが理にかなった選択であったためです。このアプローチにより、コンパイラを拡張するエクステンションポイントがJDT のコミッターによって提供可能になります。もしコンパイラがサードパーティによって提供されるコマンドラインアプリケーションだったら、違った方法を取らざるを得なかったでしょう。

コンパイラを独自に実装することで、IDE 内でインクリメンタルビルドをサポートするためのメカニズムが可能になりました。インクリメンタルビルダーを使えば変更されたファイルとそれに依存するファイルだけが再コンパイルされるようになるので、パフォーマンスが上昇します。インクリメンタルビルダーの動作を説明しましょう。Eclipse で Java プロジェクトを作成すると、ワークスペースの中にリソースが作成され、その中にファイルが保存されます。Eclipse のインクリメンタルビルダーはワークスペースに含まれる入力ファイル (.java ファイル) を受け取って出力ファイル (.class ファイル) を出力します。ビルド処理を通して、ビルダーはワークスペース内のファイルのタイプ (クラスかインターフェースか) と参照関係を取得し、さらにソースファイルがコンパイルされる度にビルドの状態がコンパイラからビルダーに伝わります。インクリメンタルビルドが開始されると、ビルダーにはリソースの差分、つまりファイルの追加、削除、更新を表すデータが与えられます。削除されたソースファイルに対応するクラスファイルがあるならそれも削除され、追加、更新されたファイルはキューに追加されます。キューに入っているファイルは順にコンパイルされ、古いクラスファイルとの比較によって構造的な変更があったかどうかが判定されます。構造的な変更とはクラスへの変更であってそのクラスを参照する別のタイプに影響を及ぼすものを言い、例えばメソッドの削除や追加が該当します。そのような変更があった場合には、変更された部分を参照する全てのタイプがキューに追加されます。もしタイプ自身が変更された場合には、新しいクラスファイルがビルド出力フォルダに書き出されます。最後にビルドの状態がコンパイルされたタイプに対する参照情報で更新されます。以上の処理がキューが空になるまで繰り返され、コンパイルエラーがあった場合には Java エディタが問題マーカーを作成します。長年の間に、JDT が提供するツールは新しいバージョンの Java ランタイムと共に大きく拡張されました。

プラグイン開発環境 (PDE)

プラグイン開発環境 (PDE) が提供するのは、Eclipse の機能を拡張するプラグインおよびその他の生成物の開発、ビルド、デプロイ、テストのためのツールです。Eclipse のプラグインは Java の世界における新しいタイプの生成物なので、ソースをプラグインに変換するためのビルドシステムが存在しませんでした。そのため PDE チームは PDE Build と呼ばれるコンポーネントを作成しました。PDE Build はプラグイン同士の依存関係の検査とビルドのための Ant スクリプトの生成を行います。

Eclipse 3.0: ランタイム、RCP、ロボット

ランタイム

Eclipse 3.0 はおそらく Eclipse のリリースの中でも最も重要なものの一つです。その理由はこのリリースサイクルの間にたくさんの重要な変更が行われたからです。3.0 より前の Eclipse のアーキテクチャにおいては、Eclipse のコンポーネントモデルでプラグイン同士がやり取りをする方法は二つありました。一つ目は依存関係を plugin.xmlrequires 文で記述するという方法です。プラグイン A がプラグイン B を require すると、プラグイン A は B に含まれる全ての Java クラスとリソースを見ることができ、そのときには Java のクラスの可視性に関する規則が使われました。各プラグインはバージョンを持ち、依存関係でバージョンを指定することも可能です。二つ目はエクステンションとエクステンションポイントという、コンポーネントモデルによって提供される方法です。長い間、Eclipse のコミッターたちは Eclipse SDK のランタイムを自分たちで書くことで、クラスオーバーロードやプラグインの依存関係、エクステンション、エクステンションポイントを管理してきました。

Equinox プロジェクトは Eclipse における新たなインキュベータープロジェクトとして開始されました。Equinox プロジェクトが目指したのは、Eclipse のコンポーネントを (既存の) 新しいモデルで置き換え、さらに動的プラグインをサポートすることです。JMX、Jakarta Avalon、OSGi などのソリューションが候補に挙がりました。しかし JMX は発展途上のコンポーネントモデルだったので適切だとは言えず、また Jakarta Avalon はプロジェクトの勢いが失われていたので選ばれませんでした。技術的な要件を満たすだけではなく、技術を支えるコミュニティが活発であることも重要でした。つまり、「Eclipse 固有の変更を受け入れてくれるだろうか?」「開発は活発で、新しい利用者を惹き付けているだろうか?」といった質問に対してきちんと答えられる必要があったのです。Equinox チームは最終的な技術の選択において、コミュニティを技術的要件と同じぐらい重要視しました。

利用可能な選択肢の調査の後、コミッターたちは OSGi を選択しました。その理由は、OSGi はセマンティックなバージョン化スキームを使って依存関係を管理しており、JDK には無いモジュール化されたフレームワークが利用可能だからです。他のバンドルで利用可能なパッケージは明示的にエクスポートされなければならず、エクスポートされないものは全て隠蔽されます。また OSGi には独自のクラスローダがあるので、Equinox チームが自身のローダーを保守する必要がなくなります。Eclipse 以外にも様々な採用例のあるコンポーネントモデルを標準とすることで、広いコミュニティを惹き付け、Eclipse の採用例を増やすこともできるだろうと期待されました。

Eclipse のコンポーネントモデルで必要となる機能を OSGi に追加する際には当時既に存在していた活発なコミュニティからの協力を受けることができ、Equinox チームは非常に助かりました。例えば OSGi は要求のリスティングをパッケージレベルでしかサポートしていませんでしたが、Eclipse にはプラグインレベルのサポートが必要でした。また Eclipse がプラットフォームや環境に固有のコードをプラグインに追加するために使われる、フラグメントという仕組みが OSGi には存在していませんでした。フラグメントは例えば Linux と Windows のファイルシステムに関するコードや言語の翻訳に関する部分を管理します。

OSGi が新しいランタイムとして採用されると、次に必要となったのはオープンソースのフレームワーク実装です。Apache Felix の前身にあたる Oscar と、IBM によって開発された Service Management Framework (SMF) が調査されました。当時 Oscar はまだ研究プロジェクトであり、デプロイの数も限られていました。最終的には SMF が選択されましたが、その主な理由は SMF が既に出荷されたプロダクトで使われており、エンタープライズでも利用可能だと思われたためです。Equinox の実装は OSGi という仕様のリファレンス実装としても使われています。

Eclipse 3.0 をインストールしたとしても既存のプラグインが利用できるよう、互換性レイヤーも開発されました。Eclipse の新しい基礎アーキテクチャに関する変更を受け入れるようプラグインを書き直してくれと開発者たちに要求していたら、Eclipse はツールプラットフォームとしての勢いを失っていたでしょう。Eclipse の利用者から期待されるのは、プラットフォームがこれまで通り動作することただそれだけなのです。

OSGi に切り替えたことで、Eclipse のプラグインはバンドルという名前で呼ばれるようになりました。プラグインとバンドルは全く同じものです。両方ともモジュール化された小さな機能を提供し、メタデータはマニフェストで記述されます。以前は依存関係、エクスポートされるパッケージ、エクステンション、エクステンションポイントは plugin.xml に記述されていました。OSGi への移行に伴ってエクステンションとエクステンションポイントは (Eclipse 独自の概念なので) plugin.xml に残りましたが、他の情報は META-INF/MANIFEST.MF という OSGi のバンドルマニフェストに記述されるようになりました。この変更をサポートするために、Eclipse 内で動作するマニフェストエディタが PDE によって新しく用意されました。各バンドルは名前とバージョンを持ち、例えば org.eclipse.ui バンドルは次のようになっています:

Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: %Plugin.name
Bundle-SymbolicName: org.eclipse.ui; singleton:=true
Bundle-Version: 3.3.0.qualifier
Bundle-ClassPath: .
Bundle-Activator: org.eclipse.ui.internal.UIPlugin
Bundle-Vendor: %Plugin.providerName
Bundle-Localization: plugin
Export-Package: org.eclipse.ui.internal;x-internal:=true
Require-Bundle: org.eclipse.core.runtime;bundle-version="[3.2.0,4.0.0)",
 org.eclipse.swt;bundle-version="[3.3.0,4.0.0)";visibility:=reexport,
 org.eclipse.jface;bundle-version="[3.3.0,4.0.0)";visibility:=reexport,
 org.eclipse.ui.workbench;bundle-version="[3.3.0,4.0.0)";visibility:=reexport,
 org.eclipse.core.expressions;bundle-version="[3.3.0,4.0.0)"
Eclipse-LazyStart: true
Bundle-RequiredExecutionEnvironment: CDC-1.0/Foundation-1.0, J2SE-1.3

Eclipse 3.1 では、バンドルが必要とする実行環境 (bundle required execution environment, BREE) をマニフェストからも指定できます。Java コンパイラ自身はバンドルや OSGi マニフェストを解釈しないためにこの指定が必要になります。PDE には OSGi バンドルを開発するためのツールが含まれるので、PDE はバンドルのマニフェストをパースし、バンドルに対するクラスパスを生成します。例えば実行環境として J2SE-1.4 を指定した上でジェネリクスを含んだコードを書くとコンパイルエラーとなります。これによってユーザーのコードがマニフェストで指定した“契約”に忠実であることが保証されます。

OSGi はモジュール化されたフレームワークを Java に提供します。OSGi フレームワークが管理するのは自己記述的なバンドルの集合と、それらバンドルが持つクラスローダーです。バンドルが利用可能なクラスパスはマニフェストの依存関係から生成されます。OSGi アプリケーションはバンドルの集合体と言うことができます。モジュール化を完全にするためには、利用者にとっても信頼性の高いフォーマットで依存関係を記述できなければなりません。バンドルがパッケージをエクスポートするときに、クライアントが使うパブリック API 定めたマニフェストを記述するのはこのためです。そしてその API を使うバンドルはそのパッケージをインポートしなければなりません。この他にも、マニフェストには依存するバンドルのバージョンの範囲も記述できます。例えば上記のマニフェストの Require-Bundle の部分を見ると、 org.eclipse.ui が依存する org.eclipse.core.runtime バンドルのバージョンが 3.2.0 以上 4.0.0 未満であることが必要だと分かります。

OSGi のバンドルライフサイクル
図 6.4. OSGi のバンドルライフサイクル

OSGi は動的なフレームワークであり、バンドルのインストール、スタート、ストップ、アンインストールをサポートします。前述の通りプラグインのクラスを必要となるまで起動しない Eclipse には遅延起動という機能も必要でしたが、OSGi のバンドルライフサイクルを使えばこれも可能です。OSGi アプリケーションが開始したとき全てのバンドルは UNINSTALLED 状態にあり、依存関係を解決していった結果として必要となったバンドルは RESOLVED 状態となります。RESOLVED 状態となったバンドルに含まれるクラスはロード・実行が可能となります。起動ポリシーに沿って起動されているバンドルは STARTING 状態であり、起動が完了すると ACTIVE 状態となります。この状態のバンドルは必要なリソースの要求や他のバンドルとの対話が可能です。ACTIVE 状態の間に確保したリソースはアクチベーターの stop メソッドで解放され、このメソッドを実行しているバンドルは STOPPING 状態となります。さらに、バンドルを UNINSTALLED 状態にして利用不可能にすることも可能です。

API が進化するにつれ、利用者に API の変更を通知する機能が必要となりました。この問題を解く一つの方法はセマンティックバージョニングを使ってバンドルを管理し、マニフェストで依存するバンドルのバージョンの範囲を指定する方法です。OSGi が使う四分割バージョン命名スキームを 図 6.5 に示します。

バージョン命名スキーム
図 6.5. バージョン命名スキーム

OSGi のバージョン命名スキームでは全てのバンドルに識別子があり、四つの部分からなるバージョン番号と名前を持ちます。これによって名前とバージョンの組で特定のバイト列を利用者に伝えることができます。Eclipse では、バンドルに加えられる変更の種類はバージョン番号の部分によって伝えられるようになっています。API に破壊的な変更が加えられたときには最初の部分 (メジャー) が進み、API を加えたときには二番目の部分 (マイナー) が進み、API が変わらないバグ修正のときには三番目の部分 (サービス) が進みます。最後の四番目の部分 (修飾子) はビルド ID やソース管理レポジトリのタグに応じて変更されます。

バンドル間の固定された依存関係を表すのマニフェストに加えて、OSGi にはサービスと呼ばれるメカニズムがあり、これを使うとバンドル同士をさらに切り離すことができます。サービスは OSGi のサービスレジストリに登録されたプロパティの集合です。エクステンションは起動時に Eclipse がバンドルをスキャンすることでエクステンションレジストリに登録されますが、サービスは動的に登録されます。サービスを利用するバンドルはサービスコントラクトを定義することでパッケージをインポートし、これを受けて OSGi フレームワークがサービスの実装をサービスレジストリから探索します。

Java クラスファイルにおける main メソッドと同様に、Eclipse を起動するための特別なアプリケーションが存在し、Eclipse アプリケーションはエクステンションを使って定義されます。例えば Eclipse IDE を起動するアプリケーションは org.eclipse.ui.ide.workbench であり、 org.eclipse.ui.ide.application バンドルで定義されます。

<plugin>
    <extension
         id="org.eclipse.ui.ide.workbench"
         point="org.eclipse.core.runtime.applications">
      <application>
         <run
               class="org.eclipse.ui.internal.ide.application.IDEApplication">
         </run>
      </application>
  </extension>
</plugin>

スタンドアローンのヘルプサーバーや Ant タスク、JUnit テストなど、様々なプログラムを実行するためのアプリケーションが Eclipse によって提供されています。

リッチクライアントプラットフォーム (RCP)

オープンソースコミュニティで活動する中で最も興味深いことの一つは、人々がソフトウェアを全く思いもよらない方法で使うことです。Eclipse の最初に目指していたのは、IDE の作成、拡張のためのツールとプラットフォームでした。しかし 3.0 リリースが近づいてきたころ、コミュニティからのバグレポートから明らかになったことによると、人々はプラットフォームバンドルの一部分を利用してリッチクライアントプラットフォーム (Rich Client Platform, RCP) を構築しており、多くの人はこれを Java アプリケーションだと認識していました。Eclipse がもともと IDE を中心として作られたことから、RCP のような利用がコミュニティにとって容易になるリファクタリングがバンドルに対して行われていたことは間違いありません。RCP アプリケーションは IDE の全ての機能を必要としないことから、いくつかのバンドルは小さく切り分けられて RCP アプリケーションを作るときに必要に応じて利用できるようになっています。

実際に活用されている RCP アプリケーションの例としては、NASA の Jet Propulsion Laboratory で作成された火星探査ロボットをモニターするためのソフトウェア、バイオインフォマティクスのデータ可視化に使われる Bioclipse、鉄道の運行状況の監視に使われる Dutch Railway などがあります。これらのアプリケーションに共通するのは、ECP プラットフォームを利用することで、特殊な用途に対するツールの作成に集中できると開発チームが判断したことです。長期のサポートが保証されている安定した API を選択した結果、これらのチームは開発期間と開発予算を削減できました。

clipse 3.0 のアーキテクチャ
図 6.6. clipse 3.0 のアーキテクチャ

図 6.6 に Eclipse 3.0 のアーキテクチャを示します。Eclipse Runtime はアプリケーションモデルとエクステンションレジストリを提供するためにまだ存在しています。コンポーネント間の依存関係を管理するプラグインモデルは OSGi によって管理されます。Eclipse を IDE のために拡張するのはこれまで通り行えますが、RCP アプリケーションフレームワークを使ってより汎用的なアプリケーションを作成することも可能になっています。

Eclipse 3.4

アプリケーションを新しいバージョンにアップデートして新しい要素を追加する処理は、簡単にできて当然と思われています。例えば Firefox ではこの処理はシームレスに行えます。しかし Eclipse にとっては簡単なことではありませんでした。この問題を解決するために最初に導入されたのが更新マネージャで、これを使うと Eclipse に新しい要素を追加したり、Eclipse を新しいバージョンに更新できます。

更新やインストールの処理で何が変更されるのかを理解するには、まず Eclipse において「フィーチャー」という単語が意味するものを理解する必要があります。フィーチャーとはビルドおよびインストールにおいて一つにまとめられるバンドルの集合を定義するものであり、PDE によって生成されます。フィーチャーは他のフィーチャーを含むことができます (図 6.7 参照)。

EClipse 3.3 SDK のフィーチャー階層
図 6.7. EClipse 3.3 SDK のフィーチャー階層

Eclipse の新しいビルドをインストールしようとしたとします。更新マネージャのメカニズムが大雑把なために、そのビルドに新しいバンドルが一つしか含まれなかったとしても、フィーチャー全体が更新されなければなりません。これは非効率的です。

フィーチャーを作成し、ワークスペースからそれをビルドするための PDE ウィザードがあります。フィーチャーに含まれるバンドルと、そのバンドルに関する簡単なプロパティは feature.xml ファイルで定義されます。フィーチャーはバンドルと同じく名前とバージョンを持ちます。フィーチャーは他のフィーチャーを含むことができ、そのバージョンの範囲も指定できます。フィーチャーに含まれるバンドルには特定のプロパティが付いており、例えば下記の feature.xmlorg.eclipse.launcher.gtk.linux.x86_64 という部分を見ると、このフィーチャーが仮定しているオペレーティングシステム (os)、ウィンドウシステム (ws)、アーキテクチャ (arch) が分かります。新しいリリースに更新するときには、この部分はこのプラットフォームにしかインストールできないということです。これらプラットフォームフィルターはバンドルの OSGi マニフェストに含まれます。

<?xml version="1.0" encoding="UTF-8"?>
<feature
      id="org.eclipse.rcp"
      label="%featureName"
      version="3.7.0.qualifier"
      provider-name="%providerName"
      plugin="org.eclipse.rcp"
      image="eclipse_update_120.jpg">

   <description>
      %description
   </description>

   <copyright>
      %copyright
   </copyright>

   <license url="%licenseURL">
      %license
   </license>

   <plugin
         id="org.eclipse.equinox.launcher"
         download-size="0"
         install-size="0"
         version="0.0.0"
         unpack="false"/>

   <plugin
         id="org.eclipse.equinox.launcher.gtk.linux.x86_64"
         os="linux"
         ws="gtk"
         arch="x86_64"
         download-size="0"
         install-size="0"
         version="0.0.0"
         fragment="true"/>

Eclipse アプリケーションはフィーチャーとバンドルだけからできているわけではありません。Eclipse を起動するためのプラットフォーム固有の実行形式、ライセンスファイル、プラットフォーム固有のライブラリなどが存在します。次に示すのは Eclipse アプリケーションに含まれるこのようなファイルのリストです。

com.ibm.icu
org.eclipse.core.commands
org.eclipse.core.conttenttype
org.eclipse.core.databinding
org.eclipse.core.databinding.beans
org.eclipse.core.expressions
org.eclipse.core.jobs
org.eclipse.core.runtime
org.eclipse.core.runtime.compatibility.auth
org.eclipse.equinox.common
org.eclipse.equinox.launcher
org.eclipse.equinox.launcher.carbon.macosx
org.eclipse.equinox.launcher.gtk.linux.ppc
org.eclipse.equinox.launcher.gtk.linux.s390
org.eclipse.equinox.launcher.gtk.linux.s390x
org.eclipse.equinox.launcher.gtk.linux.x86
org.eclipse.equinox.launcher.gtk.linux.x86_64

これらのファイルはフィーチャーとしかやり取りをしないので、更新マネージャによって更新されません。これらのファイルの多くはメジャーリリースにおいて更新されており、ユーザーはリリースのたびに zip ファイルを自分でダウンロードしてこなければなりませんでした。これは Eclipse コミュニティに受け入れられるものではなく、PDE がプロダクトファイルに対するサポートを提供することになりました。この機能を使うと Eclipse RCP アプリケーションをビルドするのに必要な全てのファイルを指定できます。しかし更新マネージャにはユーザーの PC に必須ファイルを用意する (プロビジョンを行う) 機能が存在せず、ユーザーと開発者にとって大きなストレスの種でした。これを受けて 2008 年 3 月にはこの機能を持った p2 が SDK の一部としてリリースされました。後方互換性の関係から更新マネージャは利用可能になっていますが、デフォルトでは p2 が使われるようになっています。

p2 のコンセプト

Equinox p2 で中心となるコンセプトはインストールユニット (installation unit, IU) です。IU にはインストールしようとしている生成物の名前、ID、機能、依存関係が記述されます。機能が特定の環境でしか有効でない場合には、そのことを表すフィルターを書くことも可能です。例えば org.eclipse.swt.gtk.linux.x86 というフラグメントは Linux gtk x86 マシンにインストールしようとしているときにだけ有効になります。IU のメタデータはバンドルのマニフェストに含まれる情報と基本的に同じであり、IU の生成物は単なるバイナリ列です。メタデータと生成物を分けることで関心の分離を達成しています。p2 のリポジトリにはメタデータと生成物レポジトリの両方が含まれます。

p2 のコンセプト
図 6.8. p2 のコンセプト

プロファイルはインストールされている IU のリストです。例えば Eclipse SDK にはインストールされている IU を記述したプロファイルがあり、Eclipse を新しいバージョンに更新すると新しいプロファイルが作られます。プロファイルにはインストールに関連したプロパティのリストも含まれ、例えばオペレーティングシステム、ウィンドウシステム、アーキテクチャパラメータ、インストールディレクトリ、インストール場所が含まれます。プロファイルはプロファイルレジストリで管理され、複数のプロファイルを保存することが可能です。ディレクターはセットアップ操作の起動を受け持ち、プラナーおよびエンジンと強調して動作します。まずプラナーが既存のプロファイルを調べ、インストールを更新するために必要な操作を計算します。そしてエンジンがその操作を行い、新しい生成物をディスクにインストールします。タッチポイントはエンジンの一部であり、インストールを行っているシステムのランタイム実装とのやり取りを行います。例えば Eclipse SDK では、Eclipse タッチポイントがバンドルのインストール場所を知っています。Linux システムで Eclipse が RPM バイナリからインストールされた場合には、エンジンは RPM タッチポイントとのやり取りも行います。p2 のインストールは同じプロセスで行うこともできますし、ビルドのように別のプロセスで行うこともできます。

新しい p2 プロビジョニングシステムにはたくさんの利点がありました。Eclipse のインストールで必要になる生成物はリリースごとに更新でき、そのとき前回のプロファイルがディスク上に保存されていることから、前のインストールに戻す処理も可能です。さらにプロファイルとレポジトリがあれば Eclipse のインストールを再構築できるので、ユーザーからのバグ報告を別のマシンで再現できるようになります。p2 のプロビジョンを使って更新、インストールできるのは Eclipse SDK だけではなく、RCP や OSGi といったプラットフォームでも使うことができます。Equinox チームは Eclipse Communication Framework (ECF) という別の Eclipse プロジェクトのメンバーとも協力し、p2 レポジトリの生成物やメタデータを安全に扱う方法を提供しました。

p2 が SDK に組み込まれてリリースされたときには、Eclipse コミュニティの中でたくさんの激しい議論が交わされました。当時の更新マネージャは Eclipse のインストールを管理するソリューションとして最適なものではなかったので、Eclipse のユーザーはバンドルを zip ファイルから解凍して、インストール場所に配置し、Eclipse を再起動するという操作を自分で行う必要がありました。このアプローチではバンドルの解決がユーザーの努力に任され、インストールの衝突がインストール時ではなく実行時に解決されます。インストールについての制約は実行時ではなくインストール時に解決するべきなのですが、ユーザーはこの点についてあまり注意を払わず、バンドルがディスクにあるのだから動いているのだろうと思ってしまいがちでした。また以前の Eclipse の更新サイトに置いてあったのはバンドルとフィーチャーの JAR ファイルだけであり、その名前は単純な site.xml ファイルに書かれていました。しかし p2 が導入されたことで、メタデータは p2 レポジトリが使用するはるかに複雑なものになりました。ビルドプロセスがメタデータを作成するときには、ビルド時にメタデータを生成するか、別のバンドルのジェネレータタスクを実行するかします。当初はメタデータの変更方法に関するドキュメントが不足しており、新しい技術を広い聴衆に公開することで予想外のバグが (予想通り) 発覚しました。それでも Equinox チームは長い時間をかけてドキュメントを追加し、バグを修正することで、問題点を克服しました。現在 p2 はたくさんの商用製品を支えるプロビジョンエンジンとなっています。また Eclipse Foundation が毎年行っている Eclipse の調整済みリリース (coordinated release) では、Eclipse にコントリビュートされた全てのプロジェクトを集約した p2 レポジトリが使われています。

Eclipse 4.0

アーキテクチャには、古くなっていないかどうかのテストが定期的に必要です。つまり、「新しい技術を取り込めるだろうか?」「コミュニティの成長を促しているだろうか?」「新しいコントリビューターを惹き付けているだろうか?」といった質問に yes と答えられなければなりません。2007 年の終わりごろ、Eclipse プロジェクトのコミッターたちはこれらの質問に対する答えが no であると判断し、Eclipse の新しいバージョンの設計に取り掛かりました。そうは言っても、既存の API に依存している Eclipse アプリケーションが数千もあることは彼らも認識していました。2008 年の終わりに始まったこのインキュベーターテクノロジプロジェクトは三つの目標を持っていました。すなわち、Eclipse のプログラミングモデルを簡略化すること、新しいコミッターを呼び寄せること、そしてオープンなアーキテクチャを提供しながらも、ウェブベースの新たな技術を活用できるプラットフォームを作成することです。

Eclipse 4.0 SDK Early Adopter Release
図 6.9. Eclipse 4.0 SDK Early Adopter Release

Eclipse 4.0 は最初 2010 年の 7 月にアーリーアダプターからのフィードバックを受けるためにリリースされました。このリリースは 3.6 リリースに含まれていた SDK バンドルとテクノロジプロジェクトから卒業した新しいバンドルからなります。3.0 と同様に、既存のバンドルが新しいリリースでも動くようにするための互換性レイヤーが存在しますが、この互換機能を利用した場合にはパブリックな API を使うよう警告を受けます。どのバンドルにも内部コードを使っていることの保証はありませんでした。この節では 4.0 リリースの Eclipse 4 Application Platform に含まれる機能を見ていきます。

モデルワークベンチ

4.0 ではモデルワークベンチの作成に Eclipse Modeling Framework (EMFgc) が使われます。ここではビューのレンダリングとモデルの間で関心の分離が行われており、レンダラはモデルと対話を行って SWT コードを生成します。デフォルトで使われるのは SWT レンダラですが、他のレンダラも使えます。4.x アプリケーションを作成するとデフォルトのワークベンチに対応するモデルを表す XML ファイルが作られ、モデルを変更するとワークベンチもすぐに更新されます。図 6.10 に 4.x アプリケーションに対して作成されたモデルの例を示します。

6.10 アプリケーション用に作られたモデル
図 6.10. 6.10 アプリケーション用に作られたモデル

CSS によるスタイリング

Eclipse がリリースされたのは 2001 年であり、CSS で装飾されたリッチなインターネットアプリケーションが表れる前のことでした。Eclipse 4.0 で追加されたこの機能を使うと、Eclipse アプリケーションの見た目と動作をスタイルシートで簡単に変更できます。デフォルトの CSS スタイルシートは org.eclipse.platform バンドルの css フォルダにあります。

依存性の注入

Eclipse のエクステンションレジストリと OSGi のサービスはどちらもサービスというプログラミングモデルを使っています。慣習的な命名に従うと、サービスには提供者 (producer) と消費者 (consumer) が存在し、両者の間の関係はブローカーによって管理されます。

提供者と消費者の関係
図 6.11. 提供者と消費者の関係

以前から Eclipse 3.4.x アプリケーションでは、サービスを利用しようとしている消費者がサービスの実装の場所とフレームワークにおける継承関係を理解しておく必要がありました。そのため消費者に応じてサービスの実装をオーバーライドして切り替えることは不可能であり、利便性に劣りました。例えば Eclipse 3.x でステータスラインのメッセージを更新するコードは次のようになります:

getViewSite().getActionBars().getStatusLineManager().setMessage(msg);

Eclipse 3.6 はコンポーネントから作られていましたが、コンポーネント同士の結合は密でした。Eclipse 4.0 ではアプリケーションを素に結合したコンポーネントに分解するために、クライアントへのサービスの提供に依存性の注入 (dependency injection) が使われています。Eclipse 4.x で依存性の注入のために使われているのは、消費者に提供するサービスの探索に使われるコンテキストという概念を扱う、カスタムされたフレームワークです。コンテキストが受け取ったリクエストを処理できないときには、親のコンテキストにリクエストを委譲します。Eclipse が持つ IEclipseContext というコンテキストは利用可能なサービスを格納し、OSGi のサービスの探索を行います。コンテキストは基本的に名前やクラスからオブジェクトへの Java の map であり、モデル要素とサービスを管理します。コンポーネントモデルの全ての要素はコンテキストを持つことになります。4.x におけるサービスは OSGi のサービスの仕組みを使って公開されます。

サービスブローカーコンテキスト
図 6.12. サービスブローカーコンテキスト

提供者はサービスとオブジェクトをコンテキストに追加することでサービスを保存し、その後コンテキストがサービスを消費者のオブジェクトに注入します。消費者は希望するものを宣言し、コンテキストがこのリクエストを満たすための方法を判断するということです。これによって動的なサービスが簡単に作れるようになります。Eclipse 3.x では、サービスが利用可能かどうかの通知を受け取るためには消費者がリスナーをアタッチする必要がありました。これに対して Eclipse 4.x では、コンテキストを消費者のオブジェクトに注入してしまえば後は全ての変更が自動的にそのオブジェクトに伝わるようになります。言い換えると、依存性の注入が自動的にもう一度起こるということです。

消費者がコンテキストを利用するときには Java 5 のアノテーション機能が使われます。JSR 330 規格に適合する @inject のようなアノテーションの他に、Eclipse 専用のカスタムされたアノテーションも使用可能であり、注入がサポートされているのはクラスのコンストラクタ、メソッド、フィールドです。4.x ランタイムはオブジェクトに含まれるアノテーションをスキャンし、見つかったアノテーションに応じた処理を行います。

以上のようにコンテキストとアプリケーションの間で関心を分離したおかげで、コンポーネントの再利用は容易になり、消費者は実装を理解する必要から解放されました。4.x においてはステータスを更新するコードは次のようになります:

@Inject
IStatusLineManager statusLine;
...    ...    ...
statusLine.setMessage(msg);

アプリケーションサービス

Eclipse 4.0 の主たる目標の一つは、利用者が使う API を単純化して、一般的なサービスを簡単に実装できるようにすることでした。この単純なサービスのリストは“the twenty things”と呼ばれるようになり、Eclipse application services としても知られています。これらのサービスでは、クライアントが利用可能な API の全てを深く理解していなくても使えるようなスタンドアローンの API が目標とされました。各サービスは JavaScript などの Java 以外の言語でも使えるように設計されています。例えばアプリケーションモデルにアクセスする API、設定を変更する API、エラーや警告を報告する API などがあります。

結論

Eclipse のコンポーネントベースのアーキテクチャは、新しい技術を取り込みながらも後方互換性を保ちながら進化してきました。このコストは高く付きましたが、結果として Eclipse のコミュニティは成長しました。安定した API で安定したプロダクトを出荷できるという信用を利用者から得ることができたからです。

Eclipse の利用者はとても多く、その使い道は様々です。その結果として完成した広大な API は、新しい利用者にとって採用しにくく、理解の難しいものになってしまいました。今思えば、もっと以前において、API をもっと単純に保つ努力をしておくべきだったでしょう。利用者の 80% が 20 % の API しか利用しないのであれば、API の単純化が必要なはずです。これは Eclipse 4.x でストリームが追加された理由の一つです。

群衆の知恵により、Eclipse の興味深い使い道が発見されたことがあります。IDE をバンドルに分解して RCP アプリケーションの構築に使うことなどがそうです。しかし逆に群衆がノイズを発生させることも良くありました。エッジケースシナリオに対するリクエストによって、実装のための時間が多く消費されました。

Eclipse プロジェクトの初期、コミッターには時間の余裕があり、ドキュメントやサンプルプログラムを書いたり、コミュニティからの質問に答えたりするのに多くの時間を費やすことができました。しかし時が経つにつれ、この作業はコミュニティ全体が行うものとなりました。私たち開発者がドキュメントや使い方を書いてコミュニティを助けることができればその方が良かったのですが、リリース毎に追加される機能があまりにも多く、これは困難でした。このこともあって、ソフトウェアのリリースは遅れるものだというもっぱらの噂を覆す形で、Eclipse は定期的なリリースをスケジュール通りに行うことができています。これによって利用者との間にこれからもリリースが行われるだろうという信用が生まれました。

私たちは新しい技術を採用して Eclipse の見た目と動作を再開発していますが、これは利用者との対話を続け、コミュニティの興味を捉え続けるためです。もし Eclipse の開発に興味を持っているなら、ぜひ http://www.eclipse.org を訪れてください。


  1. http://www.eclipse.org[return]

  2. http://www.eclipse.org/equinox[return]

  3. 例えば http://help.eclipse.org[return]