OSGi 調査報告 ~ モジュール化可能なサーブレットエンジンの準備 ~
こんにちは。リサーチグループアルバイトの海野です。
今回は Java 用のサービス管理プラットフォーム、OSGi の調査報告をします。
この、OSGi、初めて名前を聞いた方もいらっしゃるかと思いますが、多くの方がこの恩恵を受けていると思います。実は Java 用の開発環境である Eclipse のプラグイン機構が OSGi プラットフォームの上で動いているのです。
OSGi によって、プラグイン機構を容易に作ることが可能になります。今回の調査の最終目的は、これを利用してモジュール化可能なサーブレットエンジンの基盤を作ることです。
OSGi とは何なのか?
まず OSGi とは何なのかについてお話しします。
OSGi ( Open Services Gateway initiative ) とは「サービス管理のプラットフォーム」だと先ほど述べましたが、それだけではいまいちよくわかりません。Eclipse を例にして見ましょう。

図表 1
Eclipse には拡張プラグインがあります。このプラグインはインストール・アンインストールを自由にできますし、他のプラグインと連携して作動させたりできます。見方を変えると、Eclipse というプラットフォームの上でプラグインを、インストール・起動・終了・アンインストールしていると見ることができます。OSGi ではプラグインのことをバンドルと呼んでいます。実は Eclipse 自体が OSGi プラットフォームとなっていて、大量のバンドルを起動させることで成り立っているのです。
オリジナルの Java エンジンは、このようにパッケージ化された形でアプリケーションを動的にインストールしたりアンインストールしたりする機能がない、つまりアプリケーションのライフサイクルを管理できません。OSGi プラットフォームはまさにこの機能を提供するための基盤なのです。
さて、先ほどから OSGi というものが具体的なライブラリのような書き方をしていますが、これは正確ではありません。OSGi 自体は仕様です。つまり、実際には OSGi の具体的な実装が存在します。私は正確には把握していませんが、組み込み向けアプリケーションではよく使われている仕様のようです。今回は一般的な PC 環境を想定していますから、それ向けの実装が必要です。有名なものは以下の 3 つです。
- Apache Felix
- Knopflerfish
- Equinox
これらの中から、今回は 3 つ目の Equinox を使用することにします。この Equinox こそが、Eclipse で使われている OSGi プラットフォーム実装なのです。
バンドルとは何か?
もう少しだけバンドルについて詳しくお話ししておきましょう。
バンドルとは、OSGi プラットフォームの上でライフサイクルを持つ処理単位といえます。ライフサイクルといっても、具体的にスレッドがあって処理を行うということではありません。インストールやアンインストールといったイベントによって、状態遷移を行います。具体的な遷移の仕方が下図です。

図表 2
それぞれの遷移のタイミングで処理を行うことが可能なので、たとえば Start のタイミングでスレッドを作り、Stop のタイミングでスレッドを止めればバックグラウンドでジョブを走らせることも可能です。あるいは、Start のタイミングで別のバンドルにあるデータを登録して、Stop のタイミングで登録を解除するといったこともできます。
もう少し具体的な処理を見てみましょう。バンドルは実際には単体の jar ファイルとして実現します。この中にはバンドルに使用される class ファイルと設定を書いた MANIFEST.MF などが含まれます。Start や Stop で行う処理はどうするのでしょう。OSGi のライブラリに org.osgi.framework.BundleActivator インターフェースがあります。このインターフェースは start と stop という 2 つのメソッドを持っています。このインターフェースの実装クラスをバンドルの設定ファイル ( MANIFEST.MF ) に書いておくことで、バンドルの起動・終了のタイミングで処理を行うことができるようになるのです。
Tomcatと連携させるには?
以上で、具体的に何ができるのかイメージをつかめたと思います。しかしこのままではまだ最初の目的である「モジュール化可能なサーブレットエンジン」を作ることができません。なぜなら、サーブレットエンジンがないからです。しかし、実は Eclipse の OSGi プラットフォーム、Equinox にはこれを行うための方法が用意されています。至れり尽くせりですね。今回は Tomcat との連携方法を紹介します。
現在の問題は 2 つあります。一つは Tomcat から OSGi プラットフォームを起動させないといけないこと、もう一つはバンドルから Tomcat にアクセスしないといけないことです。これを実現してくれるのが、bridge.war というアプリケーションです ( Equinox のサイトからダウンロード可能 ) 。これを Tomcat の webapps ディレクトリに配置すると、Tomcat は bridge.war を展開、起動します。 bridge.war は起動のタイミングで OSGi プラットフォームを起動してくれます。うまい仕組みです。OSGi プラットフォームが起動しただけではアプリケーションになりませんが、bridge.war 中の config ファイルを修正することで初回起動時に自動起動させるバンドルを指定することができます。従って、ベースとなるバンドルを同梱させればアプリケーションになるわけです。

図表 3
2 つ目の問題、Tomcat へのアクセスはどうするのでしょう。プラグインとなるバンドルを登録させることでページの動的な追加・削除を行いたいわけですが、このようなことは次の仕組みを利用することで実現できます。
OSGi には org.osgi.service.http.HttpService というインターフェースがあって、これには URL とファイルをひも付けするための registerResource メソッドと、URL とサーブレットオブジェクトをひも付けするための registerServlet メソッドがあります。各メソッドを呼び出した後に、その URL にアクセスすると対応する HTML ページやサーブレットを参照することができます。bridge.war は起動したときに、HttpService の実装クラスのインスタンスを、後述する「サービス」という機能を使って OSGi プラットフォームに登録しています。そして、各バンドルは、この機能を使って OSGi プラットフォームから bridge.war で登録した HttpService を取得し、HTML ページやサーブレットの動的な登録・解除を行うことができるという寸法になっています。

図表 4
バンドルの連携をするには?
以上までで URL の動的な登録・削除の可能なサーブレットエンジンまでできました。しかし、これだけではまだちょっと足りません。メニューページのようなものを想像してください。このページでは動的に登録されたページの一覧を表示したいところですが、今までの機能だけでは別バンドルの登録を監視できません。こうした処理を実現するのが、OSGi の「サービス」という機能です。
サービスとは何でしょうか。これは非常に簡単です。文字列にひも付けされた単なる Java オブジェクトです。org.osgi.framework.BundleContext インターフェースには registerService というメソッドがあります。このメソッドは引数に String と Object を取るのですが、String の方が文字列、Object の方が登録するサービス ( となる Java オブジェクト ) です。Object の方は String が示すクラス名のサブクラスになっていないといけないことだけ注意してください。

図表 5
勘の良い方なら想像されているかと思いますが、このサービスの登録を監視することができます。それが、org.osgi.util.tracker.ServiceTracker クラスです。ServiceTracker クラスのコンストラクタは、監視するクラス名の文字列 ( あるいは監視対象か否かのフィルタ ) を引数にもちます。監視対象のサービスが登録されると、ServiceTracer の addingService が、削除されると removedServiceが呼び出されます。ですので、ServiceTracker のサブクラスで、これらのメソッドをオーバーライドしてアプリケーション特有の処理を行えば、バンドル間で通信を行うことが可能となるわけです。
もう少し具体的に、メニューページの作り方をふまえて説明しましょう。サービスは任意のオブジェクトが登録可能なのですが、ふつうは共通のインターフェースを持たせることになるでしょう。ここでは ModuleInformation インターフェースを作ります。このインターフェースは、モジュール化されたページの情報を取得するためのインターフェースです。具体的には String getPageName() メソッド、String getDescription() メソッドなどを持たせます。モジュール化された、具体的なページとして、subpage1 というバンドルを作ります。このページの情報である SubPage1Information というクラスを、ModuleInformation の実装クラスとして作ります。このバンドルは Start のタイミングで、「 Tomcat と連携させるには?」の章で紹介した方法で URL とページをひも付けさせますが、同時にサービスとして " ModuleInformation " という文字列と SubPage1Information のインスタンスを登録します。メニューページである menupage バンドルは、このサービスの登録を監視しなければなりません。menupage バンドルに ModuleTracker という ServiceTracker のサブクラスを作ります。ModuleTracker は addingService で、登録されたサービス、つまり ModuleInformation の実装クラスが渡されてきますので、これをモジュールページの一覧に追加登録します。実際にメニューページを doGet などで表示させるときは、この一覧を参照してメニューを作ります。
サービス機能は非常に単純ですが、であるからこそ非常に汎用的です。メニューページはその応用の一つですが、開発者のアイデア次第でいくらでも活用できるでしょう。
まとめ
今回は OSGi というプラットフォームの上でモジュール化可能なサーブレットエンジンの開発に関して説明しました。少しまとめますと、以下のようになります。
- OSGi はバンドルという処理単位のライフサイクルを管理するためのプラットフォーム
- Equinox は OSGi 実装の一つで、Eclipse は Equinox 上で動くアプリケーションである
- bridge.war を使えば Tomcat からの Equinox の起動と、バンドルから Tomcat の操作ができる
- サービス機能を使うことでバンドル間でデータのやりとりができる
具体的なソースや、具体的な設定方法などは割愛させて頂きましたが、OSGi がどこからどこまでの機能と役割を担ってくれるかは把握できたかと思います。実際の開発方法、設定方法などに関してはリンク先のチュートリアルなどを参照してください。
- OSGi の仕様書 ( メールアドレス登録が必要 )
- http://www2.osgi.org/Specifications/HomePage
- Equinox のチュートリアル
- http://www.eclipse.org/equinox/documents/quickstart.php
- Equinox の Tomcat 連携 ( と bridge.war )
- http://www.eclipse.org/equinox/server/http_in_container.php
- サービスを使うためのチュートリアル
- http://neilbartlett.name/blog/osgi-articles/
- OSGi 関連クラスの JavaDoc
- http://www2.osgi.org/javadoc/r4/
