OSGiとEquinoxを効果的に使う - Jeff McAffer氏とのインタビュー

少し前になりますが、EquinoxのプロジェクトリーダのJeff McAffer氏にJames Sugrue氏がインタビューした記事が、DZoneに上がってました。で、内容があまりに面白かったので、こそっと翻訳しました。DIを考慮したプログラミングモデルがbundle開発にとてもマッチする、という話があったり、全てのプロジェクトにモジュール化が合う訳ではないが、モジュール化をするのであれば、早い段階から考慮しないと、とてつもない痛みを伴うなど、自分も思っていた事が述べられています。

原題:Using OSGi & Equinox Effectively: An Interview With Jeff McAffer

OSGiとEquinoxを効果的に使うには?Jeff McAfferとのインタビュー
投稿者:James Sugrue 翻訳者:kompiro

Jeff McAffer氏の新著OSGi & Equinoxの出版後、私は彼にインタビューをする機会を得た。このインタビューでは、OSGiの基本と、その実装であるEquinox、そしてモジュール化の価値と、Equinoxを使う上でのいくつかのベストプラクティスについて伺うことができた。

James: まず最初に自己紹介と、Eclipseとの関係について話してくださいますか?

Jeff: Jeff McAfferと申します。EclipseSourceの共同設立者で、CTOを勤めています。Eclipseは10年前、IBMで最初のプロトタイプが開発されてからずっと携わって来ました。その時から、私は主にランタイム環境について、例えばRich Client Platformや、今ではEclipseRTと呼ばれる、Eclipseのランタイム環境に強く影響する技術をずっとリードし、Equinoxを進化させてきました。そのさなか、私はディレクタの議長になり、いくつかのPMCやアーキテクチャ委員会に所属しています。

James:この本はどういった人たちを対象にかかれたんですか?Eclipseアプリケーションを構築する人々のみならず、他の人々を対象にしたんですか?

Jeff:もちろん。タイトルはOSGi と Equinoxです。なのでOSGiと、いくつかの部分はEquinoxの詳細について書いています。ご存知のように、OSGiEclipseの中核ですが、その技術が適用できる範囲は非常に広いスコープを持っています。私たちはOSGiが組み込み環境に適用されているのを知っていますし(組み込みは元々のOSGiのターゲット)ですし、デスクトップアプリデモ使えますし(Eclipse RCPや他のアプリケーションインフラでも)多くのメジャーなアプリケーションサーバのようなサーバサイドでもとても広い領域を対象にしています。エンタープライズJavaに関わる多くの人は、OSGiについて目にし、話しています。この本は、こういった人々を対象にしています。OSGiの基本を明確にし、OSGiの一般的な使い方における、高度な使い方を探求しています。もちろんその例にはEquinoxを使い、Eclipseをツールとして利用しています。Equinoxをベースにしていますが、本の成果物であるサンプルは、どんなOSGiシステムでも動作できるものを提供しています。

James: OSGiについてやEquinoxがなんなのか、簡単に説明していただけますか?

Jeff:簡単に述べると、OSGiは、Javaにおけるモジュール実行環境の仕様です。 そして、EquinoxはOSGiの核となる部分の仕様の参照実装です。もちろん、どちらも重ならない領域も相当あります。OSGiは組み込みからエンタープライズ領域へ移るとたくさんのサービスが追加されます。Equinoxは仕様では与えられていない多くのトピックをカバーします。(例えばプロビジョニングや、ネイティブコードの起動、OSGiではないライブラリの統合、動的なアスペクトの織り込み等々)

James: Equinox & OSGiがどんなモジューラシステムの核にでもなると思われますか?またいくつかのケースでは過剰であることはありませんか?

Jeff: OSGiは驚くほど小さいです。ただ27個のJavaのタイプがコア仕様で定義されているだけです。もしJavaのアプリケーションを構築し、モジュール化に興味があったとき、OSGi以外の実行可能な実装を見たことがありません。このコンシェルジュのような実装はとても小さいです(100k以下)。他の実装は大きく、過剰な機能がついているでしょう。いつものように、やりたいことに忠実な実行環境を使いましょう。

James: OSGiのキーになる価値はなんですか?

Jeff: OSGiJavaにおけるシンプルで強力なモジュールフレームワークです。OSGiを使用すると言う事は、明確な境界を持ち、実行時にはその境界を(強制的に)利用する事になる自己完結したモジュールを開発者が書く事が出来ると言う事です。これは開発しているシステムで書かなければならないコード量を減少させます。なぜなら他の開発者がすぐに使えるようにしたモジュールを再利用できるからです。これはギークにとってもいいことにきこえますが、多くの人にうけいれられるでしょう。
この本のチャプター1では、"モジュール化とは...円滑なコラボレーションを助ける"と述べました。モジュール化はモジュールの提供者とモジュールの利用者のどちらの技術にも振る舞いに自由をもたらします。これは、あなた個人、そして組織、両者の開発とソフトウェアのデプロイを根本的に変えるものです。NASAの人々はOSGiを使った驚異的なサンプルを作っています。彼らは宇宙という領域のミッションに対して、多様な人々のチームで立ち向かい、コラボしています。OSGiEclipse RCPが形作られたの初期から利用していますが、現在ではサーバでもよく動作し、チームすべてのメンバーでソリューションを作成することを強力に支援します。OSGiの力のいくらかはシステムエンジニアリングを越えて拡大し、ソーシャルエンジニアリングに及んでいます。

James: Equinoxが他の実装より優れているのはどこですか?

Jeff: 私は"優れている"とは思っておらず、"少しずつ異なっている"と思っています。OSGiの実装は片手で数えられるくらいあり、どれも違ったまとまりのユーザを対象にしています。EquinoxはEclipse IDEとRCPベースのアプリケーションのランタイムとして広範囲に使われてきたため、年々改良されてきました。毎日、文字通りに何百万人の人々がデスクトップでEclipseベースのシステムを実行しています。そういったアプリケーションの要求は、よりスケーラビリティの優れた、堅牢で隅々まで注意の行き届いた、現実のソフトウェアシステムでは滅多に発生しない領域でも対応するようにEquinoxチームの背中を押してきました。ここ数年のEquinoxはWebsphereからSpring dm Server、そしてSAP NetWeaverまでのサーバ実装のフレームワークとして選ばれるようになりました。これはEquinoxの状態をより一層改善し、OSGiフレームワーク業界における強みになっています。

James: 本を書くのはどれだけ大変でしたか?長い時間かかりましたか?

Jeff: 私が本を書くのは2度めの経験ですが、一通り書くには、締切を少し守れませんでした。(1年半くらいかかりました。)でも実際はそんなに大変ではなかったです。特にレッスン用の土台は既にある成果物から始めることができました。いくつかのチャプターはRCP本を引用し、育てることができましたし、Toastという、この本のために考え抜いたシンプルなアプリケーションは、既におおきく、たくさんのファンがいました。Paul氏とSimon氏はToastの開発に精力的に働いてくれたおかげでとても素晴らしい仕事をしてくれました。Toastによりかっこいい事柄を追加しきるまで、このチャレンジは止まることがありませんでした。
たぶんこの大きなチャレンジは、詳細をよく知っている読者、よく知らない読者それぞれの心を打つのではないでしょうか?私たちはこの本がOSGiを導入するための簡単に理解できるものにしたかったですし、中途半端に放っておくことはしたくなかったのです。なので、多くのチュートリアルはどれも簡単に見え、あなた自身が試してみるときにはたくさんの質問に答えられていないようにしています。OSGiは表面上は本当にとてもシンプルです。bundleをつくり、依存性を定義し、システムに投入する。本当のシステムを構築するまでだったらそれだけです。なので、実際に本当のシステムを構築するときは手探りでサードパーティOSGiではないコード、API、そして自分たちのシステムを扱っていくわけです。そういう部分を想像し、作り込んでいくことはとても大変な仕事でした。

James: OSGiを既に使っていたり、使いたいと思っている人にアドバイスするとしたら、どんなことがありますか?

Jeff: 「よい垣根はよい隣人を育む」です。あなたの家に直接8つの家に隣接していることを想像してみてください。救急隊員や警察官、重機操縦者、ビル検査官、弁護士、裁判官、脚本家、そしてあなたの名前もあります。よく考えてみると、これらの話の大部分では、ほとんど定義されていなかった曖昧な境界ふるまいがあります。そういうことをぐるぐる頭の中で考えます。そしてソフトウェアシステムに置き換えて考え見てください。OSGiはあなたのAPI定義と、進化、そしてあなたの垣根を保守させるためのツールを与えてくれるのです。

James: OSGiの特殊な部分は何でしょうか?

Jeff: OSGiにイラついている人々の多くは、OSGiによらない部分でイラつかされていますが、何がそうさせるかというと、モジュール化と、ダイナミズム、つまり動的な部分にあります。モジュールを定義するには、あなたは境界と、モジュールがどういう振る舞いを持つのか、考えなければなりません。多くのシステムでは、アーキテクチャを大体決めたら早く開発しようとします。この事が、モジュールについて考えることや、インタラクション、繰り返しリファクタリングすることを難しくします。OSGiはあなたがこれらのインタラクションを把握し、実施できるようにします。もちろんあなたが好むのであれば大きなスパゲッティコードの塊を書くこともできますが、それはOSGiと独立して考えることでしょう。同じように、あなたがシステムに何を持たせるたいのか、自発的に考え直したいときはそうすべき時もでてくるでしょう。繰り替えしますが、OSGiはそういったシステムを作ることもできますが、タダのランチはありません。あなたは適宜コードを書く必要があるのです。
もちろんOSGiの動きにはいくつか特異な部分もあり、他の拡張メカニズム、例えばコンテキストクラスローダなどはインピーダンスミスマッチがあります。が、そういう部分に泣かされる人は後をたちません。これらの問題の多くは一度判明すると、順調に対応されています。

James: あなたの例を聞いていると、一般的なプログラミングのベストプラクティスを得たように聞こえます。OSGiアプリケーションを構築していく最中みつけたあなたのベストプラクティスをいくつか紹介していただけませんか?

Jeff: 「OSGiでプログラミングしていると考えない」 -- 私のOSGiについての話や、トレーニングコースでは、最初に「OSGi環境でプログラミングを行うベストプラクティスは、OSGiを使うためにプログラミングするわけではない」と言うようにしています。何を伝えたいのかというと、POJOでコードをかいて、DIのスタイルを貫く事が大切、ということなのです。多くの人々は、いたるところをコードで結びつけ、OSGiAPI呼び出しを至る所に散らかします。こういったやりかたはコードを読みづらくし、テストを難しくし、他のコンテキストでの再利用を難しくします。Toast全体で80のbundleがありますが、OSGiのcore framework APIのみを参照しています!
「Declarative Serviceを使う」 -- ServiceTrackerという重石の代わりに、Declarative Servicesに移行してください。DSはOSGi APIの受け渡しを簡単にます。これは決まり文句である、もろく複雑な他で必要なコードを、サービスへ直接受け渡します。そのため、かなりbundle間でOSGiサービスの連携を単純化します。
「bundleの食物連鎖を理解する」 -- 別の言い方をすると、依存関係を小さく、管理しやすいようにしてください。今使っているbundleについて現実的になり、なんで使っているのか考えてください。重複のファンになったことはありませんが、多くのメガバイトのコードが単純な制限で巻き込まれている事もあります。
「Bundle Activatorを使うな」 -- えぇ。ときどき例外的にBundleの機能によってはセットアップをすべきものもあります。これは特にDSを使っているときに考えるべきです。Activatorの悪い面は、起動時に時間をとり、通常シングルトンのような振る舞いをとります。例えばHTTPService bundleはHTTPサーバを起動し、受け付けるポートを設定します。何をしたいんでしょう?そして別々のときに起動したかったんでしたっけ?それから・・・。

James: OSGiを使う上で一番よくある典型的な間違いは何でしょうか?

Jeff: 今言ったベストプラクティスを基にする私が知っている最大の問題は特にサービス周りにおいてOSGiAPIを使いすぎることです。冗談抜きに、bundleのmanifestに"org.osgi"がないか、探してみてください。そしてどれもまっとうな使い方をしているか考えてください。多くの場合、他の世界とつなぐために手を出しています。これは周辺や、あなたのbundleがどのようなシチュエーションで使われるのか、またその結果がどうなっていなければいかないいのか、想定することを強制します。DSを使うことや、他の注入メカニズムを用いてあなたのbundleに依存しているものを注入してください。

James: モジュール化についてのご意見を伺わせてください。前もって設計から考えますか?もしくは必要なときにリファクタリングしますか?

Jeff: 素晴らしい質問です!設計や実装は実際の全ての要求をより良く実現できるかです。私は全てのチームにとってモジュール化の価値が優先順位の一番上にあると思うことを恐れています。ですが、現実的にはモジュール化はあなたが考えておくべき事実であれば、本当に可能な限り早く設計することです。モジュール化に取り組んでいるたくさんの有名なプロジェクトではもがき苦しんでいます。Eclipse自身も本来備わっていたモジュール化と同等にするには、大変な思いをしました
。多くの悩みは強結合や、異なる関心毎がごちゃ混ぜになっているところからくるのです。(言い換えれば凝集性されていないのです。)
本来であれば、本の中で述べている凝集度を高め、結合を弱めることはプログラミングにおけるベストプラクティスと明確に言えます。はっきり言ってモジュール化されていようが、いまいが、これらがよくできているのであれば、あなたのソフトウェアはよりモジュールに分割でき、デザインプロセスの中で明確に考えるべきことです。またそうしておけばリファクタリングも簡単になるでしょう。(そして、リファクタリングが必要になることも減ります。)そのため、結合を弱めるために設計や、コーディングに時間をかけることは前もってやるべきですが、あまり時間をかけずにいると、中長期的な視野からみたとき、手痛い目にあうでしょう。
最後に、現実的には要求ははっきりせず変わります。あなたはそういった事を意識し、リファクタリングできるように準備しなければなりません。より、構造を改善するために。そして、繰り返してください。早い時期からモジュール化を進めることはよいデザインプロセスであり、より重要な事なのです。

James: 今後Equinoxはどういう方向で進化しますか?e4の本質の核になりつづけますか?

Jeff: Equinoxというフレームワークは、OSGiの仕様と共に進化しつづけ、より強力な、そして堅牢な実装になっていくでしょう。e4のようなプロジェクトを活動する中で、またエンドユーザや現実世界のユースケースで揉まれていく中で、ニーズを見つけ、進化していくでしょう。Equinoxプロジェクト自身は、より幅広いモジュール化のニーズに拡大しています。昨年のリリースで、私たちは動的にコードを織り込む機能を追加しました。今年はEquinoxにScalaModulesの成果が取り込まれる事に興奮しています。加えて、私たちはEquinoxにサーバサイドやプロビジョニングに適した改善に取り組んでいます。
本当のことですが、Equinoxにとどまらない大きなものになっています。それはコミュニティとエコシステムによって実現されています。一昨年、Eclipseは実行環境におけるEclipseの技術の利用を広めていくため、EclipseRTプロジェクトを始めました。EclipseRTは成長し、(OracleからTopLinkとしてリリースされていた)EclipesLinkやJettyのような多くの良く知られたランタイムテクノロジを含んでいます。最近発表のあったGeminiとVirgoプロジェクトですが、OracleとSpringSourceのランタイムテクノロジがEclipseRTに貢献され始めています。特にSpring dm ServerがVirgoプロジェクトの一部として、移行しています。だからEquinoxとEclipseRTは近い将来、多くの、そしてとても面白いプロジェクトになると思います。

James: p2はそういったものにあっていますか?それらをプロビジョニングするための選択肢となり得ますか?

Jeff: p2はEquinoxの一部で、Eclipseの新世代のプロビジョニング技術です。そのため、汎用的なプロビジョニングのインフラとしてデザインされ、大なり小なりOSGiベースのシステムを管理において役立てることができます。p2が存在する理由として、みんなが抱えるプロビジョニングの問題が異なっているためです。とてもフレキシブルで、拡張ができ、設定できます。その本では、p2の役立つ詳細をカバーし、動的に、そしてリモートに対してプロビジョニングをサポートする事のできる深いレベルでp2をどのように統合するのか、サンプルコードを使って示しています。
もちろん、そこには多様なニーズがあり、それにあった多様な実装が提供されてます。要するにp2以外の選択肢があります。それらも強みと弱みがあるので、チームはチームにあうベストの選択肢を選ぶべきです。OSGi仕様では初期のプロビジョニングと、デプロイ管理という二つのサービスが定義されており、Apache Aceは相対的に新しい場面を対象にしてますし、これまでのOSGiベンダでは商用の選択肢も提供されています。たぶん普及の傾向にはあるものとして、内製のソリューションもあります。プロビジョニングの領域において驚くくらい創意あふれる人々により提供されています。

James: Eclipseのために働き始めてから10年経ちますが、あなたが見てきた一番興奮した変化はなんですか?

Jeff: 今あなたが言及したように、私は1999年の早くからEclipseのために働いてきました。なんて長い時間働いてきたんでしょう。その中で3つの大きな変遷をみてきました。一つ目は、「誰にも知られていないプロプライエタリーでニッチなものから、オープンソースにした経験とツールビジネスにおいて優位に立てた」ということです。これに興奮と、満足感を覚えました。その中で私は技術、モジュール化、コミュニティとオープンソースについてたくさん学びました。次の大きな変遷は、「EclipseのRich Client Platformとしての進化」です。とてもクールです。私たちと共に開発を進めてきたコミュニティからの希望で、私たちが考えもしなかった利用シーンを見つけられました。そして私にとってはモジュール化の裏にある全てのコンセプトを確かめるものになりました。。3つ目の変遷は、今目の当たりにしている「ランタイムとしてのEclipse、もしくはEclipseRT」です。ランタイム領域に適応していく割合とペースにはくじけそうでした。毎日工作のようなものが使われたり、何か新しいものとしてプロジェクトに貢献されました。私たちは昨秋、EclipseRT daysと題したシリーズを、様々な領域の参加者と共に行いとても楽しみました。私はそれを越えるものをしりません。

Commands Part 3:パラメータを使ったコマンド

Eclipse Tipsの翻訳Commandシリーズ第3段です。Part3では動的にパラメータを変更するコマンドについて書かれています。

原文

著者:Prakash G.R.
2009/01/07に記す

エディタの中からファイルを開くコマンドを考えてみましょう。「ファイルを開く - somefile.txt」とか、「ファイルを開く - someother.txt」とか...そういったメニューを動的に変更するコマンドはなかなか作成できませんよね。「ファイルを開く」と、実際に開いているファイルを組み合わせるというようなコマンドも作ってみたいですよね。このTipsでは、そういったコマンドの作り方について解説していきます。

こういったコマンドとパラメータは、下記のように拡張ポイントを定義します。

<command name="Open File"
      defaultHandler="com.eclipse_tips.commads.OpenFileHandler"
      id="com.eclipse-tips.commands.openFileCommand">
   <commandParameter
         id="com.eclipse-tips.commands.filePathParameter"
         name="File Path">
   </commandParameter>
</command>

そして対応するhandlerのコードはこんな感じになります。

public Object execute(ExecutionEvent event) throws ExecutionException {   
    String filePathParam = event.getParameter("com.eclipse-tips.commands.filePathParameter");
    IPath filePath = new Path(filePathParam);
    IFile file = ResourcesPlugin.getWorkspace().getRoot().getFile(filePath);
    IWorkbenchPage activePage = ...// get the activePage
    try {
        IDE.openEditor(activePage, file);
    } catch (PartInitException e) {
        ;// display error message
    }
    return null;
}

こんな感じでExecutionEventからあらかじめ定義しておいたidを通してパラメータを取得することができます。パラメータを取得すれば、あなたの使いたいように使うことができます。さてほんとにちゃんと違ったパラメータがわたってきているんでしょうか?ツールバー/メニューに配置するときに、合わせてパラメータを定義することができます。

<menuContribution
      locationURI="toolbar:org.eclipse.ui.main.toolbar">
   <toolbar
         id="com.eclipse-tips.commands.toolbar1">
      <command
            commandId="com.eclipse-tips.commands.someCommand"
            id="com.eclipse-tips.commands.someCommandInToolBar">
      </command>
      <command style="push"
            commandId="com.eclipse-tips.commands.openFileCommand"
            label="Opens somefile">            
         <parameter
               name="com.eclipse-tips.commands.filePathParameter"
               value="MyProject/somefile.txt">
         </parameter>
      </command>
      <command style="push"
            commandId="com.eclipse-tips.commands.openFileCommand"
            label="Opens some other file">
         <parameter
               name="com.eclipse-tips.commands.filePathParameter"
               value="MyProject/someotherfile.txt">
         </parameter>
      </command>
   </toolbar>
</menuContribution>

上記の例は最も簡単な固定のパラメータの例です。それではより現実的な例として"Show View"コマンドを見ていきましょう。このコマンドは動的で、すべてのビューを見られるコマンドです。

ビューのIDはこんな感じでコマンドにパラメータとして与えます。、

<command
        name="%command.showView.name"
        description="%command.showView.description"
        categoryId="org.eclipse.ui.category.views"
        id="org.eclipse.ui.views.showView"
        defaultHandler="org.eclipse.ui.handlers.ShowViewHandler">
   <commandParameter
            id="org.eclipse.ui.views.showView.viewId"
            name="%command.showView.viewIdParameter"
            values="org.eclipse.ui.internal.registry.ViewParameterValues" />
  <commandParameter
        id="org.eclipse.ui.views.showView.makeFast"
        name="%command.showView.makeFastParameter"
        optional="true">
  </commandParameter>

パラメータとしてとりうる全ての値のリストをViewParameterValuesクラスから与えます。このクラスはビューレジストリを走査し、ビューのIDを返します。この情報はキーバインディングの定義に使われます。

原文をみてください

パラメータの値を定義すると、キーバインディングのページには値が定義されたコマンドを表示できるのです。

原文をみてください

名前とIDのマップが返す全ての値がパラメータとして定義されます。

public class FileParameters implements IParameterValues {
    public Map getParameterValues() {
        Map params = new HashMap();
        params.put("Some File", "MyProject/somefile.txt");
        params.put("Some Other File", "MyProject/someotherfile.txt");
        return params;
    }
}

キーバインディングページでは定義した名前が表示され、定義したキーシーケンスが入力されると、そのコマンドが実行されるでしょう。

Commands Part 2: 選択によるHandlerの有効化

Eclipse Tipsの翻訳Commandについてのシリーズ第2段です。Part2ではIHandlerの活性、非活性に関して解説がかかれています。IHandlerにメニューを押したときなどの実際の振る舞いを実装します。同じコマンドでも選んでいるオブジェクトによって振る舞いを変える、と言うのはこの解説を斜め読むとなんとなく分かるでしょう。ちなみに訳者も勉強中><。翻訳しているのは、その辺の知見を得たいから、と言う事もあるのです。

原文

著者:Prakash G.R.
2009/01/05に記す

前回、私たちは一つのCommandからいくつかに分けられた定義によって、handlerが定義される事を見てきました。こうする事で複数のHandlerを同じコマンドから有効にできます。私たちはplugin.xmlに書くことでhandlerの活性化や可視化をカスタマイズすることができました。handlerに"デフォルトhandler"と呼べるような状態はありません。特定のコンテキストの中でcommandとその他のhandlerに関係がないときは、デフォルトのhandlerとしてそのhandlerが選ばれ、実行されます。覚えていて欲しいのですが、commandにはほとんどの場合一つのhandlerが割り当てられます。
それでは与えられたコンテキストによってどうやったら特定のhandlerが割り当てられるか見ていきましょう。
handlerははactiveWhen状態を特定の式言語によって定義するが出来ます。例えば2つの異なるhandlerを例にとって説明しましょう。一つはIFileが現在選択されている時に活性化するようにしたもの、もう一方はIFolderを現在選択した時に活性化するようにしたものです。これらの式は下記のようになります。

<handler
      class="com.eclipse_tips.commads.SomeCommandHandler1"
      commandId="com.eclipse-tips.commands.someCommand"><!-- (1) -->
   <activeWhen>
      <with
            variable="selection">
         <iterate
               operator="or">
            <instanceof
                  value="org.eclipse.core.resources.IFile">
            </instanceof>
         </iterate>
      </with>
   </activeWhen>
</handler>
<handler
      class="com.eclipse_tips.commads.SomeCommandHandler2"
      commandId="com.eclipse-tips.commands.someCommand"><!-- (2) -->
   <activeWhen>
      <with
            variable="selection">
         <iterate
               operator="or">
            <instanceof
                  value="org.eclipse.core.resources.IFolder">
            </instanceof>
         </iterate>
      </with>
   </activeWhen>
</handler> 

式言語についての説明は別のチップにお任せしますが、簡単に説明します。(1),(2)どちらも同じcommandIdで宣言しているので同じcommandのためのhandlerとして宣言されています。最初のhandlerは少なくとも一つのIFileオブジェクト*1が選択されているときに活性化され、二つ目のhandlerは少なくとも一つ一つのIFolderオブジェクト*2が選択されている時に活性化されます。さてここで二つの疑問がうかびました。

(1)IProject*3だけを選択している場合はどうなるの?

この場合は、どのhandlerも該当しません。よってcommandは無効化されます。これはデフォルトのhandlerを提供していないためです。もしcommandにデフォルトのhandlerが提供されていれば、このcommandは有効になるでしょう。

(2)IFileとIFolderを両方共選択に含んでいる場合はどうなるの?

この場合はどちらのhandleも有効化されます。しかしフレームワークはどちらかを単純に無効化することができません。もしデフォルトのhandlerを提供されている場合、デフォルトのhandlerはcommandと関連付けられる事はありません。なぜならどちらのhandlerもデフォルトのものよりもより特定しているためです。

それではどうやって"あいまい"なhandlerを定義するのでしょうか。それはactiveWhen句の中の状態によります。この例ではISourcesクラスを定義します。あなたはこの例を一通り見ることで定義の優先順位を理解できるはずです。

  • アクティブなコンテキスト(activeContexts)
  • アクティブなアクションセット(メニューやツールバー)(activeActionSets)
  • アクティブなShell(activeShell)
  • アクティブなWorkbench Window(activeWorkbenchWindow)
  • アクティブなエディタのid(activeEditorId)
  • アクティブなパートのId(activePartId)
  • 現在選択しているオブジェクト(selection)

もしhandlerに、activeWhenがアクティブなコンテキスト(activeContext)の場合と定義されていて、一方では現在選択しているオブジェクト(selection)の場合と定義されている場合、二つめの例の方がより明確に定義されています。activeWhenは全てのhandlerから評価されます。そしてより特化した状態が選ばれ、trueと返します。
commandに一つもデフォルトのhandlerが関連しておらず、二つのhandlerがtrueを返したとき、より状態が特化している方が有効になります。(例えば私たちの例ではIFileとIFolderの両方を選択している時のように)これはすべてメモリにhandlerを読み込まずに行われます。そうあなたのPluginが読み込まれなくてもです!

ここまでのtipで、私たちは一つ以上のhandlerが宣言されているとき、activeWhen式でどのようにhandlerが選択されるのかを学びました。ここまでhandlerが選ばれていると仮定しましたが、commandが有効になるか、どうかはenabledWhen式によって定義されます。私たちの以前の例では、handlerは選択しているものの中に少なくとも一つのIFileが含まれ、活性化されているものを示しました。

それでは二つのオブジェクトが選ばれているときだけ有効になる例を見てみましょう。

<handler
      class="com.eclipse_tips.commads.SomeCommandHandler1"
      commandId="com.eclipse-tips.commands.someCommand">
   <activeWhen>
      <with
            variable="selection">
         <iterate
               operator="or">
            <instanceof
                  value="org.eclipse.core.resources.IFile">
            </instanceof>
         </iterate>
      </with>
   </activeWhen>
   <enabledWhen>
      <with
            variable="selection">
         <count
               value="2">
         </count>
      </with>
   </enabledWhen>
</handler>

少なくともIFileオブジェクトが一つでも選ばれているいれば、handlerはコマンドに関連付けられ、活性化しています。しかしコマンドは2つのオブジェクトが選ばれているときだけ有効になります。

activeWhenとenabledWhenは似ています。

  • 式言語と共に定義され、commandが実行されるまでpluginは遅延ロードされます。

しかし小さな違いがあります。

  • handlerはenabledWhen式がtrueを返すとロードされ、pluginもロードされます。

有効化は下記のように動作します。

  • enabledWhenが特定されていない状態で、pluginはロードされていない。 - コマンドは有効になっています。
  • enabledWhenが定義されていない状態でpluginがロードされている場合 - handler.isEnabled()を参照し、commandはそれに応じて設定されます。
  • enabledWhenが定義され、falseが返される場合、commandは無効になっています。(pluginがロードされていてもいなくても変わりません。)
  • enabledWhenが定義され、trueが返され、pluginはロードされていない場合、コマンドは有効になっています。
  • enabledWhenが定義され、trueが返され、pluginがロードされている場合、handler.isEnabled()を参照し、commandはそれに応じて設定されます。

ここまでcommand、handlerと有効化をみてきました。それではより一般的なcommandはどう表現されるでしょうか?次回のtipではcommandにparameterを追加してできることを見ていきましょう。

  • Part 1: Actions対Commands
  • Part 3: コマンドのパラメータ
  • Part 4: 様々なアイテム
  • Part 5: ISourceProviderとコマンドを動的に更新する
  • Part 6: 'toggle'と'radio'スタイルのメニューへの追加

*1:例えばパッケージエクスプローラー上のファイル

*2:同じくパッケージエクスプローラー上のフォルダ

*3:同上でプロジェクト

Commands Part 1: Actions vs Commands

Eclipse Tipsの翻訳です。Eclipse Platformの3.3で導入されたコマンドフレームワークについていいシリーズがまとまっていたので翻訳しました。Part1ではなぜコマンドフレームワークが必要なのか、語られています。
このシリーズは全部で6パートに分かれていますが、一通り翻訳するつもりです。画像に関しては元記事を参照してください。

(2009/03/17 22時追記)
訳がひどかったので、直しました。ごめんなさい><

原文

著者:Prakash G.R.
2009/01/02に記述

あなたはワークベンチに対して機能を追加する時、二つの異なる方法があることを既に知っていますよね。ActionとCommandです。Commandは新しく、進歩したものではありますが、普段は簡単なのでActionを使っています。私はActionを使うことに不自由を感じていません。私はコマンドフレームワークのいくつかのバグを修正し始めました。その詳細について、中を見なければならなかったのです。Commandについてより詳しく見ていくことで、私はCommandにより愛着を持ったのです。なので私はCommandについてのシリーズを書くことにし、この記事はシリーズの一番最初のものです。*1ここに上げられるたくさんの情報は、いくつかの古いバグや、Wiki、CVSのヒストリーなどを探す時に見つけたものです。もしなにか間違っていたり、下記漏れていることがあれば筆者まで教えてください。

それではActionから始めましょう。私達はメニューやツールバー、プルダウンなどをワークベンチにコントリビュートすることができます。また私たちはラベルやツールチップなどplugin.xmlにUIの詳細を記述することができたり、実行時にロードする、遅延ロードを実現しています。なのに何が間違っているのでしょう?

  • UIとハンドリングはいつも強固に結びつきます。あなたはUIとハンドリングを分離する方法がないのです。
  • アクションはワークベンチ(ポップアップメニューやツールバー)の違うパートにコントリビュートするために、いくつかの場所に重複してXMLに記述しなければなりません。すべての拡張ポイントが同じ設定とならないことが一番まずいポイントです。
  • 複数の場所に記述されたアクションをメンテナンスする事は悪夢のような事です。もしあなたがアクションのアイコンを変えたとしましょう。あなたは全ての場所に記述されたアイコンを修正する必要があるのです。
  • 他にもplugin.xmlにアクションを重複して登録する問題として、メモリ内に同じアクションのインスタンスが複数作成される事が上げられるでしょう。

それではコマンドフレームワークがこれらをどのように取り除くのか、見ていきましょう。

コマンドは"org.eclipse.ui.commands"という拡張ポイントによって定義されます。典型的なコマンドの定義は下記のようになります。:

<command
         id="com.eclipse-tips.commands.someCommand"
         name="Some Command">
</command>

そうです。これだけです。これだけでコマンドを定義できました。
もしこのコマンドをツールバーに置くとしましょう。"org.eclipse.ui.menus"拡張ポイントを使わなければなりません。

<menuContribution
      locationURI="toolbar:org.eclipse.ui.main.toolbar">
   <toolbar
         id="com.eclipse-tips.commands.toolbar1">
      <command
            commandId="com.eclipse-tips.commands.someCommand"
            id="com.eclipse-tips.commands.someCommandInToolBar">
      </command>
   </toolbar>
</menuContribution>

これでUIに配置することが出来ました。

それではどうやってコードを実行するのでしょうか?それには別の拡張ポイントである"org.eclipse.ui.handlers"を使って定義します。:

<handler
         class="com.eclipse_tips.commads.SomeCommandHandler"
         commandId="com.eclipse-tips.commands.someCommand">
</handler>

最後の1ピースはコマンドにアイコンを加えることです。
推測できると思いますが、これにも別の拡張ポイントである"org.eclipse.ui.commandImages"を使う必要があります。:

<image
         commandId="com.eclipse-tips.commands.someCommand"
         icon="icons/sample.gif">
</image>

これで全て設定できました。それではアクションの中のコマンドを見てみましょう。


画像については原文を参照してください

ご存知のとおり、アクションを定義するためには私たちは唯一つの拡張ポイントを使うだけで済ませることができました。対してコマンドを使った場合は4つの拡張ポイントを使わなければなりません。これは回り道をしているように聞こえるかもしれません。たぶん私たちはコマンドを使って複数のハンドラについて知ったり、コンテキストベースのつながり、ハンドラーの有効化など、私たちはこのフレームワークの素晴らしさを知らないためにそう思うのです。このシリーズでは今後下記について見ていきます。

  • Part 2: 選択によるHandlerの有効化
  • Part 3: コマンドのパラメータ
  • Part 4: 様々なアイテム
  • Part 5: ISourceProviderとコマンドを動的に更新する
  • Part 6: 'toggle'と'radio'スタイルのメニューへの追加

*1:このシリーズではコントリビュートと言う単語が何度も出てきます。コントリビュートとはEclipseのワークベンチに機能を追加することを指します。Eclipseはpluginを導入することで機能を追加する事ができますが、それをコントリビュートと呼んでいます。

Eclipseプラグイン開発に良くある10の過ち

Planet Eclipse経由で流れていた良記事を翻訳してみた。Eclipse Tipsの記事です。

翻訳の元記事
http://blog.eclipse-tips.com/2009/01/top-10-mistakes-in-eclipse-plug-in.html
元のタイトルは「Top 10 mistakes in Eclipse Plug-in Development」なので、「Eclipseプラグイン開発の間違いトップ10」の方が正しいんだろう。10位から順番に上がっていきます。また、画像は引用していません。

引用ここから

私はEclipseプラグイン開発をし始めた方をトレーニングしてきました。そしていつも同じように間違えてしまう点を見つけました。そこで今回、そうやって見つけてきた10の良くある過ちを一覧にしました。もしあなたがこれらの間違いをしていたとしても、それはあなただけのことではないのです。

(10) JavaDocを読まない


これは特にEclipseプラグイン開発に限った話ではありません。Javaプログラマーにとって共通の話題だし、もっと言えば全てのプログラマーにとって同じことでしょう。そう、ドキュメントを読まないことです。どれだけ多くの、継承してはいけないクラスや実装をプラグイン開発者であるあなたからされることが考えられていないインターフェースがあるか知っていますか?もちろんAPIツールはあなたを助けてくれますが、JavaDocを読むのとは違った角度からの助けでしょう。私はいつも間違いを犯します。正直に言えば長い間、Commandのhandlerはnullを返されることが仮定されていることを知りませんでした。(これは実行結果です。)あなたはいつこれを知りましたか?
念のために言うと、JDTが提供しているJavaDocビューを使うとJavaDocが整形されて表示されます。

(9) デフォルトコンストラクタを追加することを忘れる

あなたがナイスなウィザードクラスを作りました。そしてウィザードダイアログの中に入れて動かしてみるとちゃんと動きます。でもINewWizardと一緒に動かそうとすると動きません。その後「ちょっと」デバッグしてみると、ウィザードクラスの中でデフォルトコンストラクタが定義されていないことを見つけました!そう、これはウィザードクラスに限った話ではなく、plugin.xmlに記述する、"class"属性で書くクラス全てにデフォルトコンストラクタが必要です。これらのクラスはリフレクションを使ってインスタンスが作成されるためです。

(8) 異なったプラグインに分割していない

新規開発者の間で共通して良くみられます。かれらは全てのものを一つのプラグインにしてしまう傾向があるのです。幾つかのプラグインに分けることでコードを分けることはモジュール性や保守性を上げるでしょう。あなたは少なくともUI部分とコア部分に分けるべきです。そうすることでコア部分のテストを行うことを簡単に行えるでしょう。

(7) "internal"なコードを使う(internalとは隠蔽されたクラスのこと。パッケージがinternal以下のクラスであることが多いため、そのままにしておく)

私は"internal"なコードを使うのを避けられないケースをたくさん見てきました。あなたにとって必要な機能が"internal"なコードとなっていたので、それを使ってきました。"internal"なクラスは数千以上ありますが、信じてほしいのですが、それらのクラスは次回のリリースでリファクタされ、変更されているでしょう。するとあなたは新しいリリースに対応するために大変な思いをするでしょう。私は何回もこれがおきたけど、なんで"internal"なクラス全部におこるんだ?はぁ。
もし"internal"なコードが一般的でとても使えるように見えたなら、APIのバグとしてbugzillaへ登録しよう。"internal"なクラスを使うためのもう一つあります。それはあなたのコードベースへクラスをコピーし、それを使うようにすることです。

(6) クラスパスを直接指定している。

全てのプラグインプロジェクトはJavaプロジェクトでもあります。しかしそれはあなたが必要とするJarファイルをJavanoクラスパスに追加すればいい、というものではありません。なぜならプラグインの実行時のクラスパスはそれとは異なるからです。もしJarをクラスパスに追加したいのであれば、RuntimeタブにあるClasspathセクションでAddボタンを押してください。

(5) build.propertiesを無視する

新規開発者の中で最も多い間違いです。icon/resourceなど、新しく作成したフォルダを追加してください。プラグインをテストするときは完璧に動作しますが、エクスポートし、Eclipseへ配置すると何かがおかしいことはないですか?理由ですか?あなたが追加したフォルダやファイルがbuild.properitesのエントリに追加されていないからです。だからエクスポートされたプラグインはそのリソースがなかったのです。覚えていてほしいのは、フォルダーや個別のリソースを追加したときはbuild.properitesを追加することを忘れないでください。

(4) 空のdisposeメソッド

この黄金律は忘れないでください。「何かを作ったら、捨てる(dispose)」。SWTのリソース、例えば画像やフォントはいつも使い終わったらdisposeメソッドを呼ぶこと。私はたくさんの人たちがこれをするのを忘れているのをみてきました。他にもdisposeメソッドをオーバーライドしたときにsuper.dispose()を呼んで親クラスで作られたプロパティのdisposeを確保してください。また、IPartListenerやIResourceChageListenerなど、あなたのクラスがライフサイクルを終えたらこれらのリスナーを確実に削除するよう、disposeメソッドで削除してください。

(3) monitor.isCanceled()を無視している

あなたのJobは非常に長期に渡る仕事をしていて、ユーザーがキャンセルをしたいとしますよね。そういう時はProgressMonitorダイアログやウィザード、またはProgressViewからキャンセルを押すことで終了できます。キャンセルボタンを押すと、IProgressMonitorはキャンセルフラグをtrueにするだけです。あなたは一定の期間でisCancelled()をチェックし、操作を終了させるようにする責任があります。ユーザーが"キャンセル"を押した後に何も起きなかったら、信じてほしいんですが、それはとても悪いユーザーエクスペリエンスでしょう。

(2) やみくもにいろんなところにコントリビュートしている。

プラグイン開発者は自分のプラグインを追加できる場所全て、と言っていいくらいの場所に追加しがちです。しかしユーザーからしてみるとそれはイライラさせる大きな原因となってしまいます。だから

  • あなたのビューをあなたが知っている全てのパースペクティブに追加するのを止めてください。
  • 本当に、本当に必要なとき以外org.eclipse.ui.startupを使わないでください。
  • 全てのパースペクティブであなたの作ったアクションが使えないようにしてください。
  • モーダルダイアログを作らないようにしてください。
  • objectContributionsを使ってメニューにアイテムを追加するときは、シンプルなObjectを使うのではなく、特定のクラスを使うようにしてくだし。

(1) ディスプレイスレッドで長い処理を行っている

これについて言う必要があるかい?新規開発者達だけではなく、多くのプラグイン開発者たちがこうした間違いを犯す傾向がある。私は前の仕事で、内製RCPアプリケーションを普段使わなければなりませんでしが、ほとんど全ての操作でこの間違いが犯されていました。最後に結果が得られるまで全くUIに反応がないUIはユーザーにとってフラストレーションがたまります。私は一度コードの一部を指し、開発者になぜこんな長くて大きい計算をディスプレイスレッドで行っているのか尋ねたことがあります。彼はとても驚き、Display.asyncExec()でもUIJobでもないと言っていました。だからこのコードはディスプレイスレッドで動作していないと言っていました。これには経験則があります。SWTのリスナーから実行される操作はディスプレイスレッドで動作するのです。ほとんどの開発者はこのことを認識していません。またDisplay.asyncExec()もしくはDisplay.syncExec()の中でしかUIスレッドで実行されないと思っています。だからSWTのリスナーから長い計算や、サーバーへネットワークを渡って接続しないようにしてください。もしあなたがこういった長い操作をすることになったらジョブを分けて、速く処理を返すようにしてください。するとUIからの反応が残るでしょう。

このほか、どんな良くある間違いと遭遇しますか?

引用ここまで