Blueprint Container 仕様による OSGi アプリケーションの構築

モジュール式の動的アプリケーションを開発するための優れたメカニズムを提供する OSGi (Open Service Gateway initiative) Alliance フレームワークがよく使われるようになってきています。最新の OSGi Service Platform Release 4、V4.2 仕様では新たにBlueprint Container 仕様が導入されました。この記事では、Blueprint Container が Java™ コードを複雑にすることなく、OSGi 環境で動的アプリケーションを作成する単純なプログラミング・モデルとなる仕組みを説明します。記事に記載する多数の例が、Blueprint XML ファイルとコンポーネント XML 定義を使い始める際の参考となるはずです。

Jarek Gawor, Advisory Software Engineer, IBM

Author photoJarek Gawor は、ノースキャロライナ州 Research Triangle Park にある IBM で顧問ソフトウェア・エンジニアを務めています。彼は Apache Geronimo のコミッターであり、PMC のメンバーです。Illinois Institute of Technology でコンピューター・サイエンスの修士号を取得しました。



2009年 11月 17日 (初版 2009年 10月 27日)

はじめに

モジュール式の動的アプリケーションを開発するための優れたメカニズムを提供する OSGi フレームワークが、最近よく使われるようになってきています。最新の OSGi Service Platform Release 4、V4.2 仕様には、Blueprint Container と呼ばれる仕様が加わりました。

Spring の動的モジュール

Spring フレームワークをよくご存知の方は、Spring と Blueprint Container 仕様には多くの類似点があることにお気付きのことでしょう。Blueprint 仕様は Spring Dynamic Modules プロジェクトをベースとしています。

Blueprint Container 仕様は、OSGi 向けの依存性注入フレームワークを定義します。その目的は、サービスが随時、使用可能にも、使用不可にもなる OSGi の動的性質に対処することです。この仕様は POJO (Plain Old Java Object) でも機能するように作られているため、同じオブジェクトを OSGi フレームワークの内外で使用することができます。Blueprint プログラミング・モデルの鍵となるのは、アプリケーションのさまざまなコンポーネントを定義し、記述する Blueprint XML ファイルです。仕様では、これらのコンポーネントをインスタンス化して関連付けることで、1 つの動作するアプリケーションを形成する方法を記述しています。

Blueprint Container 仕様はエクステンダー・パターンを使用します。このパターンでは、エクステンダー・バンドルがフレームワーク内のバンドルの状態をモニタリングし、その状態に応じてバンドルに代わってアクションを実行します。Blueprint のエクステンダー・バンドルはバンドルが起動するまで待機し、起動した時点で、そのバンドルが Blueprint バンドルであるかどうかをチェックします。バンドルに 1 つ以上の Blueprint XML ファイルが含まれていれば、そのバンドルは Blueprint バンドルであると見なされます。これらの XML ファイルは OSGI-INF/blueprint/ ディレクトリーの決められた場所に置かれる場合もあれば、Bundle-Blueprint マニフェスト・ヘッダーに明示的に指定される場合もあります。

エクステンダーは、あるバンドルが Blueprint バンドルであると判別すると、そのバンドルのために Blueprint Container を作成します。Blueprint Container には以下の役割があります。

  • Blueprint XML ファイルの構文解析
  • インスタンス化
  • コンポーネント同士の関連付け

Blueprint Container は初期化中に、必須となるサービス参照がすべて満たされていることを確認した後、すべてのサービスをサービス・レジストリーに登録し、初期コンポーネント・インスタンスを作成します。バンドルが停止された時点でそのバンドルの Blueprint Container を破棄するのも、Blueprint エクステンダー・バンドルです。

この記事では、Blueprint XML に焦点を当てます。数々の例を参考にしながら、コンポーネント XML 定義とその使用方法について学んでください。


Blueprint XML

Blueprint XML ファイルは、最上位の blueprint 要素によって識別されます (リスト 1 を参照)。

リスト 1. Blueprint XML ファイルのフラグメント
<?xml version="1.0" encoding="UTF-8"?>
<blueprint xmlns=”http://www.osgi.org/xmlns/blueprint/v1.0.0”>
    ...
</blueprint>

Blueprint XML ファイルには、各種コンポーネント・マネージャーの定義が含まれます。Blueprint Container 仕様では 4 つのコンポーネント・マネージャーを定義しています。具体的には、Bean マネージャーサービス・マネージャー、そして 2 つのサービス参照マネージャーです。これらのマネージャーのそれぞれが、コンポーネントを作成し、作成したコンポーネントのライフサイクルを管理します。そして要求に応じて、コンポーネントのインスタンスを提供します。マネージャーにはそれぞれに対応する XML 要素があり、この要素がそのマネージャーのプロパティーを記述します。マネージャーは最上位のマネージャーにすることも、他のマネージャー定義の中にインライン化することもできます。すべてのマネージャーには以下に挙げるような共通する属性もあります。

id
マネージャー ID を定義する属性です。id 属性はオプションであり、指定されない場合には一意に決まる ID が自動的に生成されて、その ID が最上位のマネージャーに割り当てられます。インライン化されたマネージャーは匿名と見なされるため、id 属性を設定することはできません。マネージャー ID は Blueprint Container に含まれる最上位のマネージャーすべてに対して一意に決まるものでなければなりません。この ID を使用することで、マネージャー同士が互いに参照することができます。例えば、マネージャーは依存性注入の際に、自ら作成しているコンポーネントに注入するオブジェクトを提供するよう、参照先のマネージャーに対して要求することができます。
activation
このオプション属性は、マネージャーの起動モードを定義します。サポートされる起動モードは以下の 2 つです。
  • Eager: マネージャーを Blueprint Container 初期化中に起動します。
  • Lazy: マネージャーをオンデマンドで起動します。
デフォルトでは、eager 起動モードが想定されます。ただし、blueprint 要素の default-activation 属性を設定すれば、Blueprint XML ファイル内のすべてのマネージャーに対してデフォルトの起動モードを変更することができます。このモードでは、マネージャーは最初のコンポーネント・インスタンスを提供するように求められた時点で起動され、Blueprint Container が破棄されるときに停止されます。起動、停止については、マネージャーごとに独自のステップがあります。
dependsOn
このオプション属性は、マネージャー ID のリストを指定します。この属性が設定されたマネージャーは、このリストに記載されたマネージャーが起動された後で起動されます。マネージャーの依存関係は、明示的にすることも、暗黙的にすることもできます。dependsOn 属性は、明示的依存関係を定義します。暗黙的依存関係は、マネージャー定義に含まれる他のマネージャーへの参照によって定義されます。

Bean マネージャー

Bean マネージャーは、指定された引数とプロパティーを使用して Java オブジェクトのインスタンスを作成しますが、スコープの設定に応じて、単一のオブジェクト・インスタンスまたは複数のオブジェクト・インスタンスを作成することができます。さらに Bean マネージャーでは、オブジェクトのライフサイクルを管理し、すべてのプロパティーが注入された時点、またはオブジェクトが破棄される際に通知することも可能です。

Blueprint XML では、bean 要素が Bean マネージャーを定義します。オブジェクトを作成するために使用する引数は argument 要素で指定し、注入するプロパティーは property サブ要素で指定します。

シグニチャーの明確化

Blueprint 仕様ではシグニチャーを明確化するために、多段アルゴリズムを定義しています。このアルゴリズムは、考えられるすべてのコンストラクターと各コンストラクター内のパラメーターの並びを考慮して、最もよく一致するものを見つけます。メソッドでも、これと同じアルゴリズムが使用されます。詳細については、「OSGi Service Platform Enterprise Specification」内のセクション 121.9.1 を参照してください。

オブジェクトを作成する際に Blueprint Container にまず必要なのは、XML に指定された引数と一致するパラメーター・セットを持つ適切なコンストラクターかファクトリー・メソッドを見つけることです。デフォルトでは、Blueprint Container は XML 内の argument 要素の数と順序を基準に、適切なコンストラクターまたはメソッドを検索します。argument 要素をその順序どおりにパラメーターにマッピングできない場合、Blueprint Container は argument 要素を並び替えて最適な並びを見つけ出します。

Blueprint Container が適切なコンストラクター、メソッド、またはパラメーターの並びを選択できるようにするためには、indextype といった追加の属性を argument 要素に指定することができます。例えば type 属性を使ってクラス名を指定することで、argument 要素とパラメーターの型を正確に一致させることができます。

property 要素は、注入するプロパティーの名前と値を指定します。プロパティー名は Java クラスのセッター・メソッド名に対応します。例えば、プロパティー名が foo だとすると、これに対応するセッター・メソッドは setFoo(arg) となります。プロパティー名とこれに対応するセッター・メソッド名は、JavaBeans 仕様に定義されたプロパティーのデザイン・パターンに従います。

argument 要素と property 要素の値を指定するには、value 属性または ref 属性を使用することができます。あるいは、値をインライン化することも可能です。最上位のマネージャーの ID を指定する ref 属性は、参照先マネージャーからオブジェクトを引数またはプロパティーの値として取得するために使用されます。インライン化する値は、「オブジェクトの値」セクションで説明する XML 値のどれにでもすることができます。

作成

Bean を作成するには、以下の 3 つの手段があります。

  • クラス・コンストラクター
  • 静的ファクトリー・メソッド
  • インスタンス・ファクトリー・メソッド

リスト 2、リスト 3、リスト 4 に、Java オブジェクトを作成する 3 つの方法を示します。それぞれのリストに Java クラスの一部と、それに対応する Blueprint XML のなかから、オブジェクトの作成を制御する部分を抜粋して記載します。

まずリスト 2 に記載するのは、コンストラクターを使って作成する単純な例です。この例では、class 属性が初期化対象の Java クラスの名前を指定します。Blueprint Container はコンストラクターに引数として 1 を渡し、setDescription() メソッドを呼び出して description プロパティーを注入することによって、Account オブジェクトを作成します。

リスト 2. コンストラクターの例
   public class Account {      
       public Account(long number) {
          ...
       }
       public void setDescription(String description) {
          ...
       }
       public String getDescription() {
          ...
       }
   }

   <bean id=”accountOne” class=“org.apache.geronimo.osgi.Account”>
       <argument value=”1”/>
       <property name=”description” value=”#1 account”/>
   </bean>

リスト 3 に記載するのは、静的ファクトリー・メソッドを使って作成する例です。この例では、class 属性が、静的ファクトリー・メソッドが含まれるクラスの名前を指定します。静的ファクトリー・メソッドの名前を指定する属性は factory-method 属性です。Blueprint Container は AccountFactory クラスの createAccount() 静的メソッドを呼び出し、引数として 2 を渡して Account オブジェクトを作成します。ファクトリーが作成したオブジェクトを返すと、コンテナーがそのオブジェクトを description プロパティーと併せて注入します。

リスト 3. 静的ファクトリー・メソッドの例
   public class StaticAccountFactory {      
       public static Account createAccount(long number) {
          return new Account(number);
       }
   }

   <bean id=”accountTwo” class=“org.apache.geronimo.osgi.StaticAccountFactory” 
         factory-method=“createAccount”>   
       <argument value=”2”/>
       <property name=”description” value=”#2 account”/>     
   </bean>

インスタンス・ファクトリー・メソッドを使用して作成する場合は、リスト 4 に示すように 2 つのマネージャーが使用されます。一方のマネージャーがファクトリーで、もう一方のマネージャーがそのファクトリーを使用してオブジェクトを作成します。factory-ref 属性によって最上位の Bean、またはファクトリーのように機能する参照マネージャーの ID を指定します。提供されるファクトリー・オブジェクトには、factory-method 属性で指定されたファクトリー・メソッドが含まれることになります。

以下の例では、accountFactory Bean マネージャーがファクトリーです。Blueprint Container はまず、AccountFactory インスタンスをこれに固有の引数とプロパティーを使って作成します。この例の場合、指定されている引数は唯一、ファクトリー名だけです。次に、Blueprint Container は AccountFactory インスタンスの createAccount() メソッドを呼び出し、引数として 3 を渡して Account オブジェクトを作成します。ファクトリーが作成したオブジェクトを返すと、コンテナーがそのオブジェクトを description プロパティーと併せて注入します。

リスト 4. インスタンス・ファクトリー・メソッドの例
   public class AccountFactory {      
       public AccountFactory(String factoryName) {
          ...
       }
       public Account createAccount(long number) {
          return new Account(number);
       }
   }

   <bean id=”accountFactory” class=“org.apache.geronimo.osgi.AccountFactory”>  
       <argument value=”account factory”/>      
   </bean>

   <bean id=”accountThree”
         factory-ref=“accountFactory” 
         factory-method=“createAccount”>   
       <argument value=”3”/>
       <property name=”description” value=”#3 account”/>      
   </bean>

スコープ

スコープの設定に応じて、Bean マネージャーは単一のオブジェクト・インスタンスを作成することも、複数のオブジェクト・インスタンスを作成することもできます。Blueprint Container 仕様で定義している主要なスコープには以下の 2 つがあります。

singleton
このスコープが設定されている場合、Bean マネージャーは単一の Bean インスタンスを作成し、オブジェクトを提供するように求められるたびに、そのインスタンスを返します。
prototype
このスコープが設定されている場合、Bean マネージャーはオブジェクトを提供するように求められるたびに、Bean のインスタンスを新しく作成します。

デフォルトでは、singleton スコープが最上位の Bean マネージャーに対して想定されます。scope 属性をインライン化された Bean マネージャーに設定することはできません。そのため、インライン化されたマネージャーのスコープは常に prototype であると見なされます。

スコープ設定を指定するには scope 属性を使用します。リスト 5 に、それぞれに異なるスコープを設定した 2 つの Bean 定義を記載します。

リスト 5. スコープの例
   <bean id=”prototypeAccount” class=“org.apache.geronimo.osgi.Account” 
         scope=”prototype”>
       <argument value=”4”/>
   </bean>

   <bean id=”singletonAccount” class=“org.apache.geronimo.osgi.Account” 
         scope=”singleton”>
       <argument value=”5”/>
   </bean>

ライフサイクル・コールバック

Bean マネージャーは、作成したオブジェクトのライフサイクルを管理して、すべてのプロパティーが注入された時点、またはオブジェクトが破棄される時点で通知することもできます。Blueprint Container 仕様では以下の 2 つのコールバック・メソッドを定義しています。

init-method
すべてのプロパティーが注入された時点で呼び出すメソッドを指定します。
destroy-method
Blueprint Container がオブジェクト・インスタンスを破棄する際に呼び出すメソッドを指定します。

destroy-method コールバックは、prototype スコープの Bean ではサポートされません。このスコープの Bean インスタンスを破棄するのは、アプリケーションの役割だからです。上記の 2 つのライフサイクル・メソッドは両方とも public メソッドである必要があり、引数を取らず、値も返しません。

リスト 6 に、ライフサイクル・メソッドを持つ Java クラスと、init-method 属性と destroy-method 属性を指定した Blueprint XML の bean エントリーの例を記載します。

リスト 6. ライフサイクル・コールバックの例
   public class Account {      
       public Account(long number) {
          ...
       }
       public void init() {
          ...
       }
       public void destroy() {
          ...
       }
   }

   <bean id=”accountFour” class=“org.apache.geronimo.osgi.Account” 
         init-method=”init” destroy-method=”destroy”>
       <argument value=”6”/>
       <property name=”description” value=”#6 account”/>
   </bean>

サービス参照マネージャー

サービス参照マネージャーは、OSGi サービス・レジストリーに登録されたサービスへのアクセスを提供します。Blueprint 仕様で定義しているサービス参照マネージャーは 2 つあります。1 つは参照マネージャー、もう 1 つは参照リスト・マネージャーです。

参照マネージャー

参照マネージャーが提供するオブジェクトは、サービス・レジストリーに登録された実際のサービスへのプロキシーです。プロキシーにより、バッキング・サービス (プロキシーの背後にあるサービス) が追加、削除されたり、あるいは別のサービスに置き換えられたりしても、注入されたオブジェクトをそのまま維持することができます。バッキング・サービスのないプロキシーで呼び出しを行うと、その呼び出しはサービスが使用可能になるか、タイムアウトが発生するまでブロックされます。

参照マネージャーは、reference 要素によって定義されます。バッキング・サービスが使用可能になるまでプロキシーが待機する時間は、timeout 属性を使用して指定します。timeout 属性を指定しない場合は、デフォルトで 5 分間のタイムアウトが設定されます。Blueprint XML ファイルに含まれるすべての参照マネージャーに対してデフォルトのタイムアウトを変更することもできます。それには、blueprint 要素の default-timeout 属性を使用します。タイムアウトの値は一貫してミリ秒単位で指定します。0 の値は、無期限のタイムアウトを意味します。

リスト 7 に、単純な参照マネージャーの例を記載します。この例では、サービス・プロキシーに 30 秒のタイムアウトが設定されることになります。

リスト 7. 参照マネージャーの例
   <reference id=”serviceReferenceOne” 
              interface=”java.io.Serializable” 
              timeout=”30000”/>

参照リスト・マネージャー

参照リスト・マネージャーが提供する List オブジェクトには、メンバー・タイプの設定に応じて、サービス・プロキシー・オブジェクトまたは ServiceReference オブジェクトが含まれます。この List オブジェクトに含まれるオブジェクトに対応したサービスが、サービス・レジストリーに追加されたり、あるいはそこから削除されたりすると、それに伴い List オブジェクトは動的に拡大、縮小します。List オブジェクトは読み取り専用であり、List API のサブセットのみをサポートします。

参照リスト・マネージャーのプロキシーは、参照マネージャーのプロキシーとは異なり、決められたバッキング・サービスがあるため、タイムアウトはなく、バッキング・サービスが使用不可能になると同時に ServiceUnavailableException がスローされます。

参照リスト・マネージャーを定義するのは、reference-list 要素です。このマネージャーによって提供される List オブジェクトのメンバーのタイプは、member-type 属性で指定されます。member-type 属性は以下の 2 つの値をサポートします。

service-object
サービス・プロキシー・オブジェクトのリストを注入します。デフォルトではこの値が想定されます。
service-reference
ServiceReference オブジェクトのリストを注入します。

リスト 8 に記載する単純な参照リスト・マネージャーの例では、List メンバーがサービス・プロキシーになります。

リスト 8. 参照リスト・マネージャーの例
   <reference-list id=”serviceReferenceListOne” 
                   interface=”java.io.Serializable” 
                   member-type=”service-object”/>

サービス選択とプロキシー

参照マネージャーと参照リスト・マネージャーは、いくつかの属性を共有します。サービスを選択するために共通して使用する属性には、interfacecomponent-namefilter の 3 つがあります。

interface 属性は、インターフェース・クラスを指定するために使用することができます。インターフェース・クラスを使用する目的は、サービス選択、そしてサービス・プロキシーの 2 つです。interface 属性はオプションですが、これを設定するとしたら、インターフェース・クラスを指定しなければなりません。サービス選択を目的に使用する場合、この属性で指定したインターフェース・クラスの名前で登録されたサービス・レジストリーから、サービスが選択されます。一方、サービス・プロキシーのために使用する場合には、サービス参照マネージャーから返されるプロキシーが、指定したインターフェース・クラスに定義されたすべてのメソッドを実装しなければならないことになります。interface 属性を指定しないと、プロキシーは、メソッドを持たないインターフェースを実装するかの如く振る舞います。

サービス選択には、component-name 属性と filter 属性も使用することができます。component-name 属性は、osgi.service.blueprint.compname=<component-name> 式を選択フィルターに追加するのに便利です。filter 属性も同じく、選択フィルターに追加する OSGi フィルター式そのものを指定します。interfacecomponent-namefilter 属性を組み合わせると、1 つのメイン OSGi フィルター式としてサービスの選択に適用されます。

例えば、リスト 7 の参照を対象とした選択フィルターは (&(objectClass=java.io.Serializable)) となる一方、リスト 9 の参照を対象とした選択フィルターは (&(objectClass=java.io.Serializable)(osgi.service.blueprint.compname=myAccount)(mode=shared)) となります。

リスト 9. サービス選択の例
   <reference id=”serviceReferenceTwo”              
              interface=”java.io.Serializable” 
              component-name=”myAccount”
              filter=”(mode=shared)”/>

アベイラビリティー

サービス参照マネージャーは、選択基準と一致するサービスが少なくとも 1 つ存在しなければ、Blueprint Container の初期化を続行できないことを要件にすることができます。この要件を制御するための属性が、availability です。availability 属性に使用できる値には、以下の 2 つがあります。

optional
選択基準に一致するサービスは存在しても、しなくても構いません。
mandatory
選択基準に一致するサービスが 1 つ以上存在していなければなりません。

デフォルトでこのアベイラビリティーには mandatory が設定されることになっています。Blueprint XML ファイルに含まれるすべてのサービス参照マネージャーに対してアベイラビリティーのデフォルト設定を変更するには、blueprint 要素の default-availability 属性を使用します。

アベイラビリティーが mandatory に設定されたサービス参照マネージャーは、一致するサービスが 1 つあれば要件を満たしていると見なされます。アベイラビリティーが optional に設定されたサービス参照マネージャーの場合には、一致するサービスがないとしても常に要件を満たしていると見なされます。Blueprint Container の初期化は、必要となるすべてのサービス参照マネージャーの要件が満たされるまで遅延されます。

mandatory のアベイラビリティーが考慮されるのは、Blueprint Container の初期化の際だけであることを理解しておくことは重要です。初期化が完了した後は、サービスが任意の時点で動的に追加、削除されるため、必須のサービス参照が満たされていない状態になることもあります。

リスト 10 は、アベイラビリティーが mandatory に設定された参照マネージャーの例です。

リスト 10. アベイラビリティーの例
   <reference id=”serviceReferenceThree” 
              interface=”java.io.Serializable” 
              timeout=”30000” 
              availability=”mandatory”/>

参照リスナー

両方のサービス参照マネージャーは、参照リスナーを使用しないことも、1 つ以上の参照リスナーを使用することもできます。参照リスナーとは、サービス参照マネージャーがサービスを選択したとき、またはサービス参照マネージャーがサービスを使わなくなったときに呼び出されるコールバック・メソッドを持つオブジェクトのことです。参照リスナーは、reference-listener 要素を使用して指定し、そのコールバック・メソッドは bind-method 属性と unbind-method 属性を使用して指定します。コールバック・メソッドを提供するオブジェクトは、reference-listener 要素の中にインライン化することも、最上位のマネージャーへの参照として指定することもできます。

バインドおよびアンバインドのコールバック・メソッドには、以下のシグニチャーを設定することができます (anyMethod は、任意のメソッド名を表します)。

void anyMethod(ServiceReference)
引数は、バインドまたはアンバインドされるサービスの ServiceReference オブジェクトです。
void anyMethod(? super T)
引数は、バインドまたはアンバインドされるサービス・オブジェクト・プロキシーです。型 T は、サービス・オブジェクトから割り当て可能でなければなりません。
void anyMethod(? super T, Map)
最初の引数は、バインドまたはアンバインドされるサービス・オブジェクト・プロキシーです。型 T は、サービス・オブジェクトから割り当て可能でなければなりません。2 番目の引数は、サービスと関連付けられたサービス・プロパティーを指定します。

参照リスナーが 1 つのコールバックに対して、オーバーロードされた複数のメソッドを持つ場合には、シグニチャーが一致するすべてのメソッドが呼び出されます。

参照リスト・マネージャーの場合、List オブジェクトに含まれるオブジェクトに対応したサービスが、サービス・レジストリーに追加されたり、あるいはそこから削除されたりすると、参照リスナーのコールバックが呼び出されます。一方、参照マネージャーでは、その参照マネージャーがすでにサービスにバインドされていて、それに対応する下位のランキングのサービスがサービス・レジストリーに追加される場合には、バインドのコールバックは呼び出されません。同様に、マネージャーがバインドされたサービスが削除された後に、対応する別のサービスと即時に置き換えることができる場合には、アンバインドのコールバックは呼び出されません。

理解しておく必要がある重要な点として、参照マネージャーを使用してステートフルなサービスと対話する際には、サービスの状態を適切に管理するために、参照リスナーを使ってプロキシーのバッキング・サービスを追跡してください。

リスト 11 に、単純な参照リスナーの例を記載します。ReferenceListener クラスには 2 つの bind コールバック・メソッドと、1 つの unbind コールバック・メソッドがあります。サービス参照リスト・マネージャーのサービスが、バインドまたはアンバインドされると、これらのコールバック・メソッドがそれぞれ呼び出されます。

リスト 11. 参照リスナーの例
   public class ReferenceListener {
       public void bind(ServiceReference reference) {
           ...
       }
       public void bind(Serializable service) {
           ...
       }
       public void unbind(ServiceReference reference) {
           ...
       }       
   }

   <reference-list id=”serviceReferenceListTwo” interface=”java.io.Serializable”
              availability=”optional”>
      <reference-listener 
              bind-method=”bind” unbind-method=”unbind”>
          <bean class=“org.apache.geronimo.osgi.ReferenceListener”/>        
      </reference-listener>
   </reference-list>

サービス・マネージャー

サービス・マネージャーの役割は、サービスを OSGi サービス・レジストリーに登録することです。サービス・マネージャーは、サービスの依存関係に含まれるすべての必須サービス参照マネージャーが揃っているかどうかによって、そのサービスを登録、あるいは登録解除します。必須のサービス参照は任意の時点で満たされなくなる可能性があるため、サービス・マネージャーは呼び出しを実際の ServiceRegistration オブジェクトに委任する ServiceRegistration プロキシー・オブジェクトを返します。

Blueprint Container は初期化の際に、実際のサービス・オブジェクトではなく、Blueprint Container に含まれる各サービス・マネージャーに、ServiceFactory ベースのサービスを登録します。これらのサービスがサービス・リクエストをインターセプトし、必要なときにだけ実際のサービスをインスタンス化するため、Blueprint Container はオブジェクトの作成を遅延させることができます。実際のサービス・オブジェクトは別のマネージャー (通常は Bean マネージャー) によって提供されます。OSGi ServiceFactory インターフェースを実装できるのは、実際のサービス・オブジェクトです。

Blueprint XML でサービス・マネージャーを定義するのは、service 要素です。サービス・オブジェクトを提供するマネージャーは、ref 属性を使って参照するか、あるいは service 要素の中にインライン化することができます。リスト 12 に、マネージャーを参照するサービスと、インライン化したマネージャーを使用するサービスの 2 つを記載します。

リスト 12. サービス・マネージャーの例
   <service id=”serviceOne” ref=”account” ... />

   <service id=”serviceTwo” … >
      <bean class=“org.apache.geronimo.osgi.Account”>
          <argument value=”123”/>
      </bean>
   </service>

サービス・インターフェース

サービスのそれぞれは、1 つ以上のインターフェース名で登録されなければなりません。サービスのインターフェース名のリストは、明示的に指定することができます。あるいは、自動エクスポートの設定に応じて自動的にサービス・オブジェクトから判断することも可能です。インターフェース名を明示的に設定するには、以下のいずれかを使用します。

  • interface 属性: この場合、指定できるインターフェース名は 1 つだけです。
  • interfaces サブ要素: 任意の数のインターフェースを設定することができます。

自動エクスポートの設定は、auto-export 属性で指定します。サポートされるオプションには、以下の 4 つがあります。

disabled
auto-export 属性が指定されない場合のデフォルト値です。インターフェースのリストを interface 属性または interfaces サブ要素を使用して指定する必要があります。
interfaces
サービス・クラスとそのスーパー・クラスのいずれかによって実装されたすべての公開インターフェースを使用してサービスを登録します。
class-hierarchy
サービス・クラスとその公開スーパー・クラスのいずれかを使用してサービスを登録します。
all-classes
interfaces オプションと class-hierarchy オプションを組み合わせたオプションです。

リスト 13 に、3 通りの方法でサービス・インターフェースを指定したサービスを記載します。この例の serviceOne および serviceTwo サービスは単一の Serializable インターフェース・クラスを使用して登録され、serviceThree サービスは MyAccountAccount、および Serializable の 3 つのクラスを使用して登録されます。

リスト 13. サービス・インターフェースの例
   public class MyAccount extends Account implements java.io.Serializable {
       ...
   }

   <bean id=”myAccount” class=”org.apache.geronimo.osgi.MyAccount”>
       <argument value=”7”/>
       <property name=”description” value=”MyAccount”/> 
   </bean>

   <service id=”serviceOne” ref=”myAccount” interface=”java.io.Serializable”/>

   <service id=”serviceTwo” ref=”myAccount”>
      <interfaces>
          <value>java.io.Serializable</value>
      </interfaces>
   </service>

   <service id=”serviceThree” ref=”myAccount” autoExport=”all-classes”/>

サービス・プロパティー

一連のプロパティーを使ってサービスを登録することもできます。これらのプロパティーは、service-properties サブ要素を使用して指定することができます。service-properties 要素では、そこに含まれる複数の entry サブ要素が個々のプロパティーを表します。プロパティーのキーを指定するには key 属性を使用しますが、プロパティーの値は value 属性として指定することも、要素の中にインライン化することもできます。サービス・プロパティーの値はさまざまな型にすることができますが、許可されるのはプリミティブ、プリミティブ・ラッパー・クラス、コレクション、またはプリミティブの配列だけです。

リスト 14 に、2 つのサービス・プロパティーを使ってサービスを登録する例を記載します。一方の active サービス・プロパティーが持つ値の型は java.lang.Boolean で、もう一方の mode プロパティーの型は String です。

リスト 14. サービス・プロパティーの例
   <service id=”serviceFour” ref=”myAccount” autoExport=”all-classes”>
      <service-properties>
          <entry key=”mode” value=”shared”/>
          <entry key=”active”>
              <value type=”java.lang.Boolean”>true</value>
          </entry>
      </service-properties>
   </service>

サービスのサービス・オブジェクトを提供するマネージャーが最上位のマネージャーである場合、サービス・プロパティーには自動的に osgi.service.blueprint.compname というプロパティーが追加されます。osgi.service.blueprint.compname プロパティーの値は、サービス・インスタンスを提供する最上位のマネージャーの ID に設定されます。例えば、リスト 14 のサービスを例にとると、osgi.service.blueprint.compname プロパティーの値は myAccount に設定されることになります。

サービスのランキング

サービスに特定のランキングを設定して公開するには、ranking 属性を使用することができます。ranking 属性が指定されていないサービスは、ランキングなしで登録されます。この場合、OSGi フレームワークはサービスのデフォルト・ランキングとして 0 を想定します。

以下は、ランキングを設定してサービスを登録する例です。

リスト 15. サービスのランキングの例
   <service id=”serviceFive” ref=”myAccount” auto-export=”all-classes” ranking=”3”/>

登録リスナー

サービス・マネージャーは、登録リスナーを使用しないことも、1 つ以上の登録リスナーを使用することもできます。登録リスナーとは、サービスが登録された直後、またはサービスが登録解除される直前に呼び出されるコールバック・メソッドを持つオブジェクトのことです。登録リスナーは、registration-listener サブ要素を使用して指定します。コールバック・メソッドを指定するのは、registration-method 属性と unregistration-method 属性です。コールバック・メソッドを提供するオブジェクトは、registration-listener 要素の中にインライン化することも、最上位のマネージャーへの参照として指定することもできます。

登録および登録解除のコールバック・メソッドのシグニチャーは、サービス・オブジェクトが ServiceFactory インターフェースを実装するかどうかに依存します。サービスが ServiceFactory インターフェースを実装する場合、どちらのメソッドにも void anyMethod(ServiceFactory, Map) シグニチャーが必要です (anyMethod は任意のメソッド名を表します)。

サービスが ServiceFactory インターフェースを実装しない場合、両方のコールバック・メソッドは void anyMethod(? super T, Map) シグニチャーと一致しなければなりません。ここで、型 T はサービス・オブジェクトの型から割り当てることができます。コールバック・メソッドへの最初の引数はサービス・オブジェクトのインスタンス、2 番目の引数は登録プロパティーです。登録リスナーが 1 つのコールバックに対して、オーバーロードされた複数のメソッドを持つ場合には、シグニチャーが一致するすべてのメソッドが呼び出されます。

リスト 16 に、単純な登録リスナーの例を記載します。

リスト 16. 登録リスナーの例
   public class RegistrationListener {
       public void register(Account account, Map properties) {
           ...
       }
       public void unregister(Account account, Map properties) {
           ...
       }
   }

   <service id=”serviceSix” ref=”myAccount” auto-export=”all-classes”>
      <registration-listener 
              registration-method=”register” unregistration-method=”unregister”>
          <bean class=“org.apache.geronimo.osgi.RegistrationListener”/>         
      </registration-listener>
   </service>

環境マネージャー

Blueprint Container 仕様では、複数の特殊な環境マネージャーも定義しています。環境マネージャーには ID が設定され、環境マネージャーによって環境コンポーネントへアクセスできるようになります。環境マネージャー用の ID は確保されており、他のマネージャーでは使用できないことから、環境マネージャーには XML 定義がなく、環境マネージャーをオーバーライドすることはできません。環境マネージャーによって提供されるオブジェクトを他のマネージャーに注入する唯一の方法は、参照を使用することです。Blueprint Container 仕様が定義する環境マネージャーには以下の 4 つがあります。

blueprintBundle
バンドルの Bundle オブジェクトを提供します。
blueprintBundleContext
バンドルの BundleContext オブジェクトを提供します。
blueprintContainer
バンドルの BlueprintContainer オブジェクトを提供します。
blueprintConverter
バンドルの Converter オブジェクトを提供します。このオブジェクトによって、Blueprint Container の型変換機能を利用できるようになります。型変換については後ほど詳しく説明します。

リスト 17 に、blueprintBundle 環境マネージャーが提供する Bundle オブジェクトを accountManagerOne Bean に注入する単純な例を記載します。

リスト 17. 環境マネージャーの例
   public class AccountManager {
       public void setManagerBundle(Bundle bundle) {
           ...
       }
   }

   <bean id=”accountManagerOne” class=”org.apache.geronimo.osgi.AccountManager”>
      <property name=”managerBundle” ref=”blueprintBundle”/>
   </bean>

オブジェクトの値

Blueprint Container 仕様には、さまざまな型のオブジェクトの値を記述する多数の XML 要素が定義されています。値を示すこれらの XML 要素は、マネージャー定義内で使用します。例えば、Bean マネージャーで引数やプロパティーの値を指定するために使用したり、サービス・マネージャーでサービス・プロパティーの値を指定するために使用したりすることができます。値を示す XML 要素は、実際の値のオブジェクトに変換されてから、マネージャー・コンポーネントに注入されます。

ref 要素は最上位のマネージャーへの参照を定義し、この要素の component-id 属性が、最上位のマネージャーの ID を指定します。注入される値は、参照先のマネージャーから返されたオブジェクトとなります。

リスト 18 に ref 要素の component-id 属性の値の例を記載します。この例では、accountOne Bean インスタンスが accountManagerTwo Bean の managedAccount プロパティーに注入されます。

リスト 18. 参照の値の例
   public class AccountManager {
       ...
       public void setManagedAccount(Account account) {
           ...   
       }
   }

   <bean id=”accountOne” class=“org.apache.geronimo.osgi.Account”>
       <argument value=”1”/>
       <property name="description" value="#1 account"/>
   </bean>

   <bean id=”accountManagerTwo” class=“org.apache.geronimo.osgi.AccountManager”>
       <property name=”managedAccount”>
           <ref component-id=”accountOne”/>
       </property>
   </bean>

idref 要素は、最上位のマネージャーの ID を定義します。注入される値は、component-id 属性によって指定されたコンポーネント ID です。idref 要素は、指定された ID のマネージャーを起動する前に、そのマネージャーが実際に存在することを確認するために使用されます。

value 要素は、要素に含まれるストリングから作成されるオブジェクトを表します。ストリングの内容をどの型に変換するかは、オプションの type 属性を使用して指定することができます。type 属性を指定しなければ、ストリングの内容は注入先の型に変換されます。

null 要素は Java の null を表します。

listsetarray の各要素は、それぞれ java.util.List オブジェクト、java.util.Set オブジェクト、Object[] 配列を表すコレクションです。これらのコレクションのサブ要素は、このセクションで説明した、値を示す XML 要素のどれにでもすることができます。コレクションのサブ要素にデフォルトの型を指定するには、要素にオプションの value-type 属性を設定するという方法を使用することができます。

リスト 19 の例に、値を示す XML 要素を組み合わせてリストを作成する方法を示します。作成されるリストには、以下の 4 つの項目が含まれることになります。

  • “123” という String
  • 値に 456 が設定された java.math.BigInteger オブジェクト
  • null
  • java.lang.Integer 型の値を 2 つ持つ java.util.Set オブジェクト
リスト 19. 複合リストの値の例
   <list>
       <value>123</value>
       <value type=”java.math.BigInteger”>456</value>
       <null/>
       <set value-type=”java.lang.Integer”>
           <value>1</value>
           <value>2</value>
       </set>      
   </list>

props 要素は、キーと値がString 型の java.util.Properties オブジェクトを表します。個々のプロパティーを表す prop サブ要素では、key 属性を使用してプロパティーのキーを指定しますが、プロパティーの値は value 属性として指定することも、この要素の内容として指定することもできます。

リスト 20 に props 要素の値を示す例を記載します。

リスト 20. プロパティーの値の例
    <props>
        <prop key=”yes”>good</prop>
        <prop key=”no” value=”bad”/>       
    </props>

map 要素は java.util.Map オブジェクトを表します。この場合、キーと値は任意のオブジェクトにすることができます。個々のプロパティーを表す entry サブ要素では、プロパティーのキーを key 属性または key-ref 属性として指定することも、key サブ要素内にインライン化することもできます。プロパティーの値は、value 属性または value-ref 属性として指定するか、あるいはインライン化することもできます。インライン化された値は、このセクションで説明した、値を示す XML 要素のいずれかとして指定することができます。一方、インライン化したキーについては、値を示す XML 要素 (ただし null 要素を除く) のいずれかとして指定することができます。Blueprint 仕様では、map 要素での null キーの使用は許可されていません。最上位のマネージャーの ID を指定する key-ref 属性および value-ref 属性は、指定されたマネージャーからプロパティーのキーまたは値としてオブジェクトを取得するために使用します。map 要素は key-type 属性と value-type 属性を指定することで、キーおよび値にデフォルトの型を定義することができます。

リスト 21 に map 要素の値を示す例を記載します。このリストには、map オブジェクトのエントリーを作成する複数の方法も示されています。作成された map オブジェクトには、以下のエントリーが含まれることになります。

  • “myKey1” という String のキーと、このキーにマッピングされた “myValue” という String の値
  • account” というBean マネージャーから返されるオブジェクトのキーと、このキーにマッピングされた “myValue” という String の値
  • 123 という値を持つ java.lang.Integer オブジェクトのキーと、このキーにマッピングされた “myValue” という String の値
  • “myKey2” という String のキーと、このキーにマッピングされた、“account” という Bean マネージャーから返されるオブジェクトの値
  • “myKey3” という String のキーと、このキーにマッピングされた、345 という値を持つ java.lang.Long オブジェクトの値
  • urn:ibm という値を持つ java.net.URI オブジェクトのキーと、このキーにマッピングされた、http://ibm.com という値を持つ java.net.URL オブジェクトの値
リスト 21. map の値の例
   <map>
       <entry key=”myKey1” value=”myValue”/>

       <entry key-ref=”account” value=”myValue”/>
       <entry value=”myValue”>
           <key>
               <value type=”java.lang.Integer”>123</value>
           <key/>
       </entry>

       <entry key=”myKey2” value-ref=”account”>
       <entry key=”myKey3”>           
           <value type=”java.lang.Long”>345</value>          
       </entry>

       <entry> 
           <key>
               <value type=”java.net.URI”>urn:ibm</value>
           <key/>
           <value type=”java.net.URL”>http://ibm.com</value>
       </entry>
   </map>

各マネージャーを値としてインライン化することもできます。リスト 22 に、Bean マネージャーをインライン化した場合の例を示します。

リスト 22. マネージャーを値としてインライン化した例
   <bean id=”accountManagerThree” class=“org.apache.geronimo.osgi.AccountManager”>
       <property name=”managedAccount”>
           <bean class=“org.apache.geronimo.osgi.Account”>
               <argument value=”10”/>
               <property name="description" value="Inlined Account"/>
           </bean>
       </property>
   </bean>

型変換

Blueprint Container は注入の際に、値を示す XML 要素を実際のオブジェクトに変換します。要素の変換は、注入されたプロパティーの型に基づいて行われます。例えばリスト 23 では、Blueprint Container は 123 という String 値を long 値に変換します。Blueprint Container には、以下の変換をはじめとするいくつもの組み込み変換機能が用意されています。

  • String 値からすべてのプリミティブ型、ラッパー型、または String 値を引数に取る public コンストラクターを持つ任意の型への変換
  • array 要素から (これらの要素に対応したメンバー型を持つ) コレクション・オブジェクトへの変換
  • list 要素または set 要素から (これらの要素に対応したメンバー型を持つ) 配列オブジェクトへの変換

Blueprint Container は総称型もサポートします。総称型の情報を使用可能な場合、Blueprint Container はその情報を使用して変換を行います。例えばリスト 23 では、list 要素が java.util.Long オブジェクトのリストに変換されます。

リスト 23. 総称型の変換の例
   public class AccountManager {      
       ...
       public void setAccountNumbers(List<Long> accounts) {
          ...
       }
   }

   <bean id=”accountManagerFour” class=“org.apache.geronimo.osgi.AccountManager”>
       <property name=”accountNumbers”>
           <list>
               <value>123</value>
               <value>456</value>
               <value>789</value>
           </list>
       </property>
   </bean>

組み込み変換機能に加え、Blueprint バンドルがそのバンドルに専用のコンバーターを用意することもできます。これらのカスタム・コンバーターは、Blueprint の Converter インターフェースを実装したオブジェクトを提供する Bean マネージャーです。blueprint 要素に含まれる type-converters 要素の中でカスタム・コンバーターを指定すると、Blueprint Container 初期化中に型コンバーターが初期化され、他のマネージャーがカスタム・コンバーターを利用できるようになります。カスタム・コンバーターについての詳細は、Blueprint Container 仕様を参照してください。


Blueprint の試用

Blueprint アプリケーションを試してみるには、OSGi プラットフォームおよび Blueprint Container 実装の 2 つが必要です。この記事では OSGi プラットフォームとして Equinox を使用しますが、Apache Felix などの他のフレームワークでも問題なく機能します。Blueprint Container には、Apache Aries インキュベーター・プロジェクトの実装を使用します。Aries の Blueprint 実装は当初、Apache Geronimo プロジェクトで開発されましたが、その後 Aries に移され、現在も活発に開発が進められています。

Blueprint を試すには、記事に付属のサンプルが絶好の出発点となります。このサンプルは、blueprint-sampleblueprint-assembly という 2 つのモジュールで構成されています。blueprint-sample モジュールはサンプル Blueprint バンドルで、記事に記載した Java クラスと Blueprint XML 定義の大多数はここに含まれています。blueprint-assembly モジュールには、初心者でも簡単に使えるコンテナーに Equinox フレームワーク、 Apache Aries の Blueprint 実装、およびサンプル・バンドルがまとめてパッケージ化されています。

サンプルをビルドする

付属のサンプル・ソースをダウンロードして任意のディレクトリーに解凍してください。サンプル・ソースをビルドするには、Apache Maven を使用します。リスト 24 に記載するコマンドを実行すると、サンプルがビルドされます。

リスト 24. サンプルをビルドする
cd sample
mvn install

上記のコマンドによって、サンプルに含まれる 2 つのモジュールを Maven が自動的にビルドします。サンプル全体が正常にビルドされると、blueprint-assembly モジュールの target ディレクトリー内に多数の JAR ファイルが生成されます。このディレクトリー内には、Apache Aries Blueprint 実装、サンプル・バンドル、そして OSGi フレームワークにログ機能を提供するいくつかの JAR ファイルの他、Equinox フレームワークを提供する osgi-3.5.0.v20090520.jar ファイルもあるはずです。Equinox フレームワークを起動すると、その他すべての JAR ファイルが target ディレクトリーに自動的にインストールされて開始されます。Equinox フレームワークを起動するには、リスト 25 のコマンドを実行してください。

リスト 25. Equinox を起動する
cd blueprint-assembly/target
java -jar osgi-3.5.0.v20090520.jar -console

起動が正常に完了すると、Equinox コンソールが表示されるので、ss コマンドを使用してインストール済みバンドルを表示してください。図 1 に、ss コマンドの出力を示します。

図 1. インストール済みバンドルの出力
起動の完了時に表示される Equinox コンソール

サンプル・バンドルを実行する

サンプル・バンドルは Equinox フレームワークに自動的にインストールされますが、自動的には起動されません。サンプル・バンドルを起動するには、Equinox コンソールで start 5 コマンドを実行します。5 という数字は、org.apache.aries.sample_1.0.0.SNAPSHOT サンプル・バンドルの ID です。サンプル・バンドルが正常に起動すると、コンソールに、以下の図 2 に示す出力が表示されます。

図 2. サンプル・バンドルの出力
サンプル・バンドルが正常に起動するとコンソールに表示される出力

まとめ

最新の OSGi 仕様に導入された Blueprint Container 仕様は、Java コードを複雑にすることなく、OSGi 環境で動的なアプリケーションを作成するための単純でわかりやすいプログラミング・モデルを提供します。この記事でも使用した Aries の Blueprint Container 実装を使用して、早速 Blueprint ベースのアプリケーションを構築してください。


ダウンロード

内容ファイル名サイズ
Sample sourceos-osgiblueprint-sample.zip11KB

参考文献

学ぶために

  • JavaWorld の OSGi バンドルの初心者向けチュートリアルで、OSGi コンテナーのバンドルを作成、実行、管理する方法を学んでください。
  • 依存性注入についての説明を読んでください。
  • OSGi Alliance の詳細を学び、最新のニュース、イベント、フォーラム、仕様などの情報を入手してください。
  • Apache Geronimo はオープンソースの代替手段を集結させたサーバー・ランタイム・フレームワークとして、開発者とシステム管理者のニーズを満たすランタイムの作成を支援します。
  • Apache Felix の全容を学んでください。
  • Apache Aries の概要を読んでください。
  • プロパティー名とこれに対応するセッター・メソッド名は、JavaBeans 仕様に定義されたプロパティーのデザイン・パターンに従います。
  • Spring の動的モジュールが、OSGi フレームワークで稼働する Spring アプリケーションの構築を容易にする仕組みを学んでください。
  • developerWorks podcasts ではソフトウェア開発者のための興味深いインタビューや議論を聞くことができます。
  • developerWorks の Technical events and webcasts で最新情報を入手してください。
  • Twitter で developerWorks をフォローしてください。
  • 世界中で近日中に予定されている IBM オープンソース開発者を対象とした会議、見本市、ウェブキャストやその他のイベントをチェックしてください。
  • オープンソース技術を使用して開発し、IBM の製品と併用するときに役立つ広範囲のハウツー情報、ツール、およびプロジェクト・アップデートについては、developerWorks Open source ゾーンを参照してください。
  • 無料の developerWorks On demand demos で、IBM およびオープンソースの技術と製品機能を調べて試してみてください。

製品や技術を入手するために

議論するために

コメント

developerWorks: サイン・イン

必須フィールドは(*)で示されます。


IBM ID が必要ですか?
IBM IDをお忘れですか?


パスワードをお忘れですか?
パスワードの変更

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


お客様が developerWorks に初めてサインインすると、お客様のプロフィールが作成されます。会社名を非表示とする選択を行わない限り、プロフィール内の情報(名前、国/地域や会社名)は公開され、投稿するコンテンツと一緒に表示されますが、いつでもこれらの情報を更新できます。

送信されたすべての情報は安全です。

ディスプレイ・ネームを選択してください



developerWorks に初めてサインインするとプロフィールが作成されますので、その際にディスプレイ・ネームを選択する必要があります。ディスプレイ・ネームは、お客様が developerWorks に投稿するコンテンツと一緒に表示されます。

ディスプレイ・ネームは、3文字から31文字の範囲で指定し、かつ developerWorks コミュニティーでユニークである必要があります。また、プライバシー上の理由でお客様の電子メール・アドレスは使用しないでください。

必須フィールドは(*)で示されます。

3文字から31文字の範囲で指定し

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


送信されたすべての情報は安全です。


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=Open source, XML
ArticleID=604958
ArticleTitle=Blueprint Container 仕様による OSGi アプリケーションの構築
publish-date=11172009