目次


OSGi と Spring

第 1 回 Apache Felix を使用して OSGi バンドルを作成し、デプロイする

オープンソースの OSGi コンテナーである Apache Felix を使用して Java コンポーネントを作成し、OSGi バンドルとしてパッケージ化するための開発者向けステップバイステップ・ガイド

コンテンツシリーズ

このコンテンツは全#シリーズのパート#です: OSGi と Spring

このシリーズの続きに乞うご期待。

このコンテンツはシリーズの一部分です:OSGi と Spring

このシリーズの続きに乞うご期待。

はじめに

連載第 1 回目のこの記事では、クライアント・サイドとサーバー・サイドのコンポーネントを使って注文用アプリケーションを開発し、これらのコンポーネントを OSGi バンドルとしてパッケージ化します。このアプリケーションではクライアントが注文を処理するためにサービス・コンポーネントを呼び出すと、サービス・コンポーネントのメソッドが注文を処理して注文 ID を出力します。この記事を読み終えれば、Apache Felix の概念と機能を適用して Java コンポーネント・クラスを OSGi バンドルとして作成し、パッケージ化できるようになっているはずです。

システム要件

この記事のサンプル・コードを実行するには、お使いのマシンに必ず以下のソフトウェアをインストールして、セットアップしてください。

  • Java 5 以降
  • Ant ビルド・ツール
  • Apache Felix バイナリー・ディストリビューション 1.0.4

次に、以下の環境変数を設定します (一例として、ANT_HOME=C:\apache-ant-1.7.0 と設定します)。

  • JAVA_HOME (Java 用)
  • ANT_HOME (Ant 用)

続いて PATH 環境変数に以下の内容を追加します。

  • JAVA_HOME\bin
  • ANT_HOME\bin

OSGi

OSGi は、動的に Java アプリケーションをモジュールとして定義し、そのモジュールをネットワーク上でやりとりするための仕様です。従来、Java アプリケーションは JAR バンドルとしてモジュール化されていますが、JAR ファイルを扱う際には以下の限界があります。

  • JAR バンドルはクラスパス環境変数によって解決されるため、JAR 依存関係を管理するには堅牢なフレームワークとは言えません。
  • JAR バンドルのバージョン管理はできません。したがって、JAR バンドルの作成あるいは変更の履歴を辿ることができません。
  • コードの変更に対応して JAR ファイルを実行時に動的に更新するフレームワークがありません。

上記の問題に対処するには、OSGi フレームワークを使用することができます。OSGi は、Java のモジュール・システムを再定義するからです。OSGiベースのシステムには、従来の JAR モジュールに勝る以下の利点があります。

  • OSGi が提供する堅牢な統合環境では、バンドルをサービスとして公開し、他のバンドルが使用できるようにエクスポートすることができます。
  • OSGi は、あらゆる新しいデプロイメントに際してバンドルのバージョン管理を行います。そのため、バンドルの作成および変更の履歴を追跡することが可能です。
  • OSGi では、コードの変更に応じて常に、実行時にバンドルを動的に更新することができます。

現在、以下の 3 つが OSGi の実装として知られています。

  • Equinox
  • Knopflerfish
  • Felix

この記事では、Felix を OSGi コンテナーとして使用する例を説明します。連載全体では以下のトピックを取り上げます。

  • 第 1 回では、OSGi API を使用してコンポーネントを開発する方法を説明します。
  • 第 2 回では OSGi コンテナー内で Spring を使用し、OSGi API 依存関係をコンポーネント・クラスからなくすことによって POJO (Plain Old Java Object) にします。この方法の利点は、開発者がコンポーネント・クラス内のビジネス・メソッドの作成だけに専念できるようになり、コンポーネントのライフサイクルの処理は Spring 構成ファイルに任せられることです。

注文用アプリケーション

それでは早速、Felix ベースの OSGi フレームワークを使用して注文用アプリケーション・バンドルの作成に取り掛かります。このアプリケーションには、OrderClient.java (クライアント・サイド) と OrderService.java (サーバー・サイド) という 2 つのコンポーネントがあります。クライアント・コンポーネントは client.jar としてバンドルし、サーバー・コンポーネントは order.jar としてバンドルします。まずは OrderClient クラスを見てください。

リスト 1. クライアント・コンポーネント、OrderClient
public class OrderClient implements BundleActivator {

  private ServiceTracker orderTracker;
  private OrderService orderService;

  public void setService(OrderService orderService) {
    this.orderService = orderService;
  }

  public void removeService() {
    this.orderService = null;
  }

  public void start(BundleContext context) throws Exception {
   orderTracker = 
   new ServiceTracker(context, OrderService.class.getName(), null);
   orderTracker.open();
   OrderService order = (OrderService) orderTracker.getService();

   if (order == null) {
     System.out.println("Order service not available");
   } else {
     order.processOrder();
   }
  }

  public void stop(BundleContext context) {
   System.out.println("Bundle stopped");
   orderTracker.close();
 }

}

リスト 1 を見るとわかるように、client.jar バンドルが起動されると、OrderClient クラスが OrderService コンポーネントの processOrder メソッドを呼び出し、orderID を出力します。このクラスが実装する BundleActivator インターフェースには 2 つのコールバック・メソッド、start()stop() があります。Felix コンテナーはクライアント・バンドルの起動時、停止時に、実装された start() メソッドと stop() メソッドをそれぞれ呼び出します。

start() メソッドを詳しく見てみると、start() メソッドで最初に取得するのは ServiceTracker クラスの参照です。このクラスはファクトリー・クラスだと思ってください。次に、このファクトリー・クラスの getService メソッドを呼び出して、このクラスからサービス参照を取得します。ServiceTracker を構成するパラメーターは、バンドルのコンテキストと OrderService クラスの名前です。

リスト 2. OrderService の実装
public class OrderServiceImpl implements OrderService, BundleActivator {

  private ServiceRegistration registration;

  public void start(BundleContext context) {
   registration = 
   context.registerService(OrderService.class.getName(), this, null);
   System.out.println("Order Service registered");
 }

  public void stop(BundleContext context) {
   System.out.println("Order Service stopped");
 }

  public void processOrder() {
   System.out.println("Order id: ORD123") ;
  }
}

サーバー・サイドの order.jar ファイルに含まれるコンポーネントは、OrderService インターフェースと OrderServiceImpl クラスの 2 つです。このインターフェースには抽象メソッド、processOrder があり、このメソッドは OrderServiceImpl クラスによって実装されます。このクラスは、Felix コンテナーが order.jar バンドルを起動、停止するために呼び出す BundleActivator インターフェースも実装します。start() メソッドはレジストリーに OrderService コンポーネントを登録して、クライアント・バンドルが使用できるようにします。登録とは、他のバンドルが使用できるようにオブジェクトをエクスポートすることと同じです。

マニフェストによる情報伝達

ここで疑問となるのは、クライアント・バンドルはどうやって登録されたサービス・バンドルを認識するのかという点です。サービス・バンドルを認識するための情報は、マニフェスト・ファイルを使って伝達され、マニフェスト・ファイルの中で、インポートおよびエクスポート・パッケージの一部としてバンドルの参照を指定します。通常、クライアント・バンドルのマニフェストはサービス・コンポーネント・パッケージをインポートする一方、サービス・バンドルのマニフェストはこのバンドル自体のパッケージをエクスポートします。つまり、例えばバンドル A がバンドル B のパッケージをインポートするときには、バンドル B はその固有のパッケージをエクスポートする必要があるということです。適切なインポートとエクスポートの定義がなければ、この情報伝達は失敗します。

リスト 3. クライアントのマニフェスト・ファイル
Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Order Service Client
Bundle-SymbolicName: orderclient
Bundle-Version: 1.0.0
Bundle-Activator: order.client.OrderClient
Import-Package: org.osgi.framework, org.osgi.util.tracker, order
リスト 4. サービスのマニフェスト・ファイル
Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Order Service
Bundle-SymbolicName: orderservice
Bundle-Version: 1.0.0
Export-Package: order
Bundle-Activator: order.impl.OrderServiceImpl
Import-Package: org.osgi.framework

この注文用アプリケーションの場合、client.jar バンドルのマニフェストには Import-Package: org.osgi.framework, org.osgi.util.tracker, order というエントリーがあります。このエントリーが事実上、意味していることは、コアとなる OSGi パッケージ、そして OrderService パッケージをクライアント・バンドルがインポートするということです。同様に、order.jar バンドルのマニフェストには Export-Package: order というエントリーがあり、このバンドルがバンドル自体をパッケージとしてエクスポートしてクライアントが使用できるようにすることを意味します。このようにインポートとエクスポートが明示的に宣言されていなければ、OSGi はランタイム・エラーをスローします。

マニフェスト・ファイルには、その他の情報としてバンドル・アクティベーター・クラスの名前なども含まれます。アクティベーター・クラスの役割は、バンドルの start() メソッドと stop() メソッドを呼び出すことです。この例では、client.jar バンドルのアクティベーター・クラスは order.jar バンドルの OrderClient および OrderService となります。

デプロイメント

バンドルをデプロイして操作する前に、以下のステップを行います。

  1. ルートである C:\osgi フォルダーの配下に図 1 に示すディレクトリー構造を作成し、この記事でこれまでに説明したコンポーネントを以下のように配置してください。
    • Java コードは、該当するパッケージ・フォルダー (クライアントとサービスの両方) に配置します。
    • マニフェスト・ファイルは、該当する META-INF フォルダー (クライアントとサービスの両方) に配置します。
    図 1. コードのディレクトリー構造
    コードのディレクトリー構造

次のステップは、これらのバンドルをデプロイすることです。Felix OSGi コンテナーを使用して、以下のステップに従ってクライアント・バンドルとサービス・バンドルをデプロイしてください。

  1. サービス・フォルダー内に置かれた ANT を使用して build.xml を実行します。
  2. クライアント・フォルダー内に置かれた ANT を使用して build.xml を実行します。

上記のビルド・ファイルを実行すると、client/bin フォルダーと service/bin フォルダーにそれぞれ client.jar バンドル、order.jar バンドルが作成されます。

  1. Microsoft® Windows® のコマンド・プロンプトで startfelix.bat を実行し、Felix ランタイムを起動します。

Felix ランタイムを起動するたびに、プロファイル名を入力するよう求めるプロンプトが出されます。プロファイル名はいわば、プロジェクト名のようなものです。プロジェクト・プロファイルには任意の名前を指定して構いません。以降の Felix ランタイムの起動時に、前に入力したプロファイル名と同じ名前を指定すると、Felix はそのプロジェクト名に関連するインストール済みのバンドルをすべてロードします。新しいプロファイル名を指定した場合には、バンドルを再度、明示的にインストールする必要があります。

  1. Felix シェルで以下のコマンドを入力してバンドルをインストールします。
    • install file:service/bin/order.jar
    • install file: client/bin/client.jar
    • start service_bundle_id
    • start client_bundle_id

バンドルの ID を示すには、ps コマンドを実行してください。バンドルを起動する際は、まずサービス・バンドルを起動してから、クライアント・バンドルを起動する必要があります。それぞれのバンドルが起動すると、以下の出力が表示されます (図 2 を参照)。

図 2. プログラムの出力
プログラムの出力
プログラムの出力

バンドルは更新することもできます。それには、Felix シェルで update bundle_id コマンドを実行してください。すると、バンドルが実行時にオンザフライで更新されます。

まとめ

この記事では、OSGi フレームワークの機能と概念を簡単に説明した後、このフレームワークを使用して動的 JAR バンドルを作成する方法を例を用いて説明しました。そして、コンポーネントを OSGi バンドルとして作成してパッケージ化する方法と、Felix ランタイム環境でその OSGi バンドルを実行する方法を学びました。さらに、バンドル間の情報伝達用インターフェースとしての役割を担うバンドルのマニフェスト・ファイルの作成についても取り上げました。

OSGi は、JAR バンドルを作成、管理する新たな方法を提供します。連載第 2 回では、Spring フレームワークが OSGi 環境で OSGi に代わってバンドルを管理する仕組みについて説明します。お楽しみに。


ダウンロード可能なリソース


関連トピック

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=SOA and web services, Open source
ArticleID=354452
ArticleTitle=OSGi と Spring: 第 1 回 Apache Felix を使用して OSGi バンドルを作成し、デプロイする
publish-date=10302008