!!!Chapter 5. Packaging and Deploying Spring-based OSGi applications 伝統的なSpringアプリケーションは1つのアプリケーション・コンテキスト、又は親コンテキストを含むサービス・レイヤー、データ・レイヤー、又はWebレイヤーのコンポーネントを含んだ子コンテキストを持つドメイン・オブジェクトしか使いません。 アプリケーション・コンテキストは複数の設定ファイルの内容から合成されます。 アプリケーションをOSGiにデプロイする場合、最も自然な方法は、OSGiサービス・レジストリを通じてアプリケーションをBundleの集合(アプリケーション・コンテキスト)としてパッケージすることです。 独立したサブシステムは1つのパッケージか、もしくはBundleのセットとなっているべきです。(バーチャル・パーティショニング) あるサブシステムは1つのBundleとしてパッケージされるかもしれませんし、レイヤーによっていくつかのBundleに分割されているかもしれません。(horizontal partitioning:ホリゾンタル・パティショニング) 例えば、ある真っ直ぐな(straightforward)Webアプリケーションがあり、4つのモジュール(Bundle)に分割されているとします。 Web Bundle、サービス・レイヤーBundle、データ・レイヤーBundle、そしてドメイン・モデルBundleという具合です。 そのようなアプリケーションを図示すると以下のようになります。 ''(訳注:本家ではここで上記の例のWebアプリケーションが概念図として図示されています。'' ''[このページ|http://static.springframework.org/osgi/docs/1.1.0/reference/html/app-deploy.html]の一番上の[図|http://static.springframework.org/osgi/docs/1.1.0/reference/html/images/spring-osgi-model.png]です。)'' この例の場合、データ・レイヤーバンドルはいくつかの内部的コンポーネント(bean)を含むデータ・レイヤー・アプリケーション・コンテキストに処理を委譲しています。このうち2つのbeanはOSGiサービス・レジストリ内のサービスとして公開されている外部のアプリケーション・コンテキストから構成されています。 サービス・レイヤーBundleはいくつかの内部的なコンポーネント(bean)を含んだサービス・レイヤー・アプリケーション・コンポーネントに委譲しています。これらのうちいくつかのコンポーネントはデータ・レイヤー・サービスに依存し、それらのサービスをOSGiレジストリからインポートしています。 サービス・レイヤーコンポーネントのうち2つはOSGiサービス・レジストリを通して外部に公開されています。 WebコンポーネントBundleは数個のコンポーネント(bean)を含むWebアプリケーション・コンテキストに処理を委譲しています。 それらのいくつかのコンポーネントはアプリケーション・サービスに依存し、OSGiレジストリを通してそれらのサービスをインポートしています。 ドメイン・モデルBundleはドメイン・モデル・タイプにだけ関与していますが、しかし自分自身のコンポーネントを作る必要がありません。アプリケーション・コンテキストには関連していません。 !! 5.1. Bundle format and Manifest headers 全てのアプリケーションはOSGi Bundleとしてパッケージングされていなければなりません。 Bundleとは、OSGiサービス・プラットフォームに認識される一連のヘッダーが記述されているMETA-INF/MANIFEST.MFを含んだjarファイルに過ぎません。 詳細はOSGiサービス・プラットフォーム・コア仕様のsection 3.2を参照してください。 いくつかのOSGi実装では解凍されたjarファイルもサポートしますが、フォーマットは一緒です。 Spring Extenderは以下の片方か両方の条件を満たしたBundleを'Spring-powered'と認識し、そしてBundleが起動された時に付随するアプリケーション・コンテキストを作成します。 * META-INF/springフォルダ内に1つ以上の".xml"ファイルを持つ場合。 * META-INF/MANIFEST.MFが'Spring-Context'のマニフェスト・ヘッダーを持つ場合。 加えて、オプションの'SpringExtender-Version'がBundleマニフェスト内に定義されている場合、ExtenderはBundleマニフェストに定義されているバージョンがExtender Bundleのバージョン(Bundle-Version)を満たしている場合にのみそのBundleを認識します。 'SpringExtender-Version'ヘッダーはOSGiプラットフォーム・コア仕様のsection 3.2.5で規定されているバージョン範囲の文法に従っている必要があります。 'Spring-Context'ヘッダーがない場合、ExtenderはMETA-INF/spring内の各".xml"が有効なSpring設定ファイルでないか確認し、全てのディレクティブ(下記参照)をデフォルト値として設定されたとみなします。 アプリケーション・コンテキストはこれらの設定ファイルから作成されます。 推奨される方法はアプリケーション・コンテキストの設定を最低2つのファイルに分割することです。「''modulename''-context.xml」と「''modulename''-osgi-context.xml」です。 ''modulename''-context.xmlにはOSGiには依存しない普通のbean定義を、''modulename''-osgi-context.xmlにはOSGiを通して公開されたり参照したりするbeanの定義を記述します。 (このファイル分割方法が必須というわけではありませんが、)こうするとSpring 'beans'namespaceの代わりにSpring Dynamic Modules OSGi schemaをトップレベルnamespaceとして使用することもできます。 Spring-Contextマニフェスト・ヘッダーは設定ファイルの代替セットを指定する為に使うこともできます。 各リソース・パスは相対パスとみなされ、Bundle内でのエントリー又は付属の[[フラグメント|dm-appendix-tips]](attached fragments)として解決されます。 Spring-Contextヘッダーが1つ以上の設定ファイルの場所を定義している場合、META-INF/spring内のファイルはSpring-Contextヘッダーで指し示されない限り無視されます。 Spring-Contextヘッダーの文法は、下記の通りです。 Spring-Context-Value ::= context ( ',' context ) * context ::= path ( ';' path ) * (';' directive) * この文法はOSGiサービス・プラットフォーム共通ヘッダー文法と一貫しています。 (OSGiサービス・プラットフォーム・コア仕様のsection 3.2.3を参照) 例えば、マニフェスト内に、 Spring-Context: config/account-data-context.xml, config/account-security-context.xml とある場合、Bundleのjarファイルの中のaccount-data-context.xmlとaccount-security-context.xmlがアプリケーション・コンテキストの初期化に使用される。 Spring-Contextにはいくつかの有効なディレクティブがある。 !create-asynchronously (false|true): アプリケーション・コンテキストの生成の同期/非同期(デフォルト)を指定する。 例: Spring-Context: *;create-asynchronously=false アプリケーション・コンテキストの生成を同期で行う。設定ファイルはMETA-INF/spring内の全ての"*.xml"ファイルが使用される。 Spring-Context: config/account-data-context.xml;create-asynchrously:=false アプリケーション・コンテキストの生成をconfig/account-data-context.xmlを使用し同期で行う。 注意する点としては、「同期」オプションでアプリケーション・コンテキストの生成を行う場合、生成作業がOSGiのイベントスレッド内で行われるという事です。 生成終了まで次のイベント処理は保留されます。 また、同期オプションでアプリケーション・コンテキスト作成中にエラーがあった場合、FrameworkEvent.ERRORが発生します。 そのBundleはアクティブ化中のステートのままになります。''(訳注:ずーっとstartedのままということだと思います。)'' !wait-for-dependencies (true|false): アプリケーション・コンテキストの生成が全ての必須の依存サービスを待つか、全ての依存が解決される前に次に進むか(デフォルト)を指定する。 例: Spring-Context: config/osgi-*.xml;wait-for-dependencies:=false confディレクトリ内の"osgi-*.xml"にマッチするファイルを使用してアプリケーション・コンテキストを作成する。 アプリケーション・コンテキストの作成は依存性の解決を待たずに次に進む。 この動作は本質的に、必須のサービス参照がオプショナルのように扱われる。オブジェクトの使用者(client)はその初期において本来の状態にないサービスオブジェクトがインジェクトされているかもしれない。 詳細はsection 5.2を参照。 ! timeout (300): 必須な依存の解決をあきらめてアプリケーション・コンテキスト作成がタイムアウトするまでの時間(秒数)。 この設定値は「wait-for-dependencies:=false」が設定されると無視される。 デフォルトは5分(300秒) 例: Spring-Context: *;timeout:=60 アプリケーション・コンテキストの作成は、必須の依存を1分(60秒)まで待ちます。 ! publish-context (true|false) アプリケーション・コンテキスト自身をOSGiサービス・レジストリへ公開するか公開しないかを設定する。 デフォルトは「公開する」。 例: Spring-Context: *;publish-context:=false Spring-Contextのマニフェスト設定がないか、設定がされていないディレクティブがある場合、デフォルトが自動的に設定される。 !! 5.2. Extender configuration options Bundle固有の設定とは別に、Spring-DMではコアのExtenderの基本的な動作を設定することができます。 これはSpring-DMが'managed'な環境の中に組み込まれていたり、Bundle-wideな機能性を求められるときに便利です。 設定の変更を有効にする為に、Extenderはデフォルトを上書きするためにOSGiの[[フラグメント|dm-appendix-tips]]を利用します。 ExtenderはそのBundle空間内のMETA-INF/springディレクトリにあるXMLファイルを見に行きます。そしてそれを元に自分自身を設定するための内部的なアプリケーション・コンテキスト(OsgiBundleXmlApplicationContext型)を作成します。 Extenderのデフォルト設定を変更するには、目的に応じたbean名を下記のテーブルから探し、適切なフォーマットで設定してから下記のようにspring-osgi-extender.jar[[フラグメント|dm-appendix-tips]]にアタッチする必要があります。 Fragment-Host: org.springframework.bundle.osgi.extender 現在の所、下記のbean名がExtenderに対して有効です。 '''Table 5.1. Extender Configuration Options''' ,Bean名,Type,Role,デフォルトの動作/値 ,taskExecutor,TaskExecutor [a],Creates,各Bundleに関連付けられたSpringアプリケーション・コンテキストの生成、有効化、廃棄を行う。task executerはアプリケーション・コンテキスト自身のスレッド・プールの管理について責任を持ちます。, ''SimpleAsyncTaskExecutor''がデフォルトで使用され、各アプリケーション・コンテキストについて新しいスレッドが作成されます。これは開発やテストの為には良いかもしれませんが、本番環境ではスレッド・プールを使用することを強くお勧めします。 ,shutdownTaskExecutor,TaskExecutor [b],各Bundleに関連付けられているSpringアプリケーション・コンテキストを廃棄します。task executerはアプリケーション・コンテキスト自身のスレッド・プールの管理について責任を持ちます。,''TimerTaskExecutor''がデフォルトで使用され、全てのアプリケーション・コンテキストは適切な順番で非並列的に破棄されます(一つ一つ順番に破棄されるということ?)。普通は破棄の順番は適切であるべきなのでデフォルトの設定か、もしくは管理された同時に1つだけ破棄が実行されるスレッド・プールのような実装を使用することが推奨されます。 ,extenderProperties,java.util.Properties,「コンテキスト全体の終了までの最大許容時間」などのプロパティーです。,下記の「デフォルト」を参照。 ,applicationEventMulticaster,ApplicationEventMulticaster [c],[ApplicationEventMultiCaster|http://static.springframework.org/spring/docs/2.5.x/api/org/springframework/context/event/ApplicationEventMulticaster.html]はSpring-DMのイベントを他のベンダーのコードに伝達します。, [SimpleApplicationEventMulticaster|http://static.springframework.org/spring/docs/2.5.x/api/org/springframework/context/event/SimpleApplicationEventMulticaster.html]のインスタンスが使用されます。アプリケーション・コンテキスト内での使用可能なbeanに関する情報は「AbstractApplicationContext」の[javadoc|http://static.springframework.org/spring/docs/2.5.x/api/org/springframework/context/support/AbstractApplicationContext.html]を参照してください。 ,applicationContextCreator,OsgiApplicationContextCreator [d],extenderによって作成されるアプリケーション・コンテキストのカスタマイズ機能を提供します。これはアプリケーション・コンテキストのclass型の変更やさらなる追加処理を含みます。(下記参照),Extenderのデフォルトの動作が適用されます。 ,''(名前なし)'',OsgiBeanFactoryPostProcessor [d],SpringのBeanFactoryPostProcessorインターフェースと同じように,OsgiBeanFactoryPostProcessor 型のbeanは自動的に検知され、extenderによって作成された全てのコンテキストに適用されます(user-definedかどうかにかかわらず)。このようなpost processorはbean factoryによって既存のbeanの追加/削除/変更や新しいbeanのインスタンスの追加に便利です。,Extenderのデフォルト動作が適用されます。 ''[a] org.springframework.core.task'' ''[b] org.springframework.core.task'' ''[c] org.springframework.context.event'' ''[d] org.springframework.osgi.extender package'' extenderPropertiesでは、下記のプロパティーが使用できます。 '''Table 5.2. Available extenderProperties''' ,名前,Type,概要,デフォルト値 ,shutdown.wait.time,java.lang.Number,extenderが各アプリケーション・コンテキストの完全な停止を待つ合計時間。ミリ秒単位で指定します。,10000ms(10秒) ,process.annotations,java.lang.Boolean,extenderがSpring-DMのアノテーションを処理するかどうかのフラグです。この機能はプロセスBundleごとに適切なbean post processorを設定することによって有効になることに注意してください。詳しくは[[Section B.1, “Annotation-based injection”|dm-appendix-extensions]]を参照してください。,false '''Note''' アプリケーション・コンテキストが作成された後から、Spring IoCコンテナの全機能はExtender設定beanを作るために使用できます。 ''(訳注:ちょっと意味が不明。原文:Since an application context is used, the full power of the Spring IoC container can be used for creating the extender configuration beans)'' !5.2.1. Listening to Extender events (例えば、)ログを残す為にアプリケーション・コンテキスト作成の成功・失敗を知りたいことがあります。 このような場合、Spring-DMではpackage org.springframework.osgi.context.eventというOSGiアプリケーション・コンテキストがそのライフサイクル中に発するイベントを扱うパッケージを提供しています。 現時点では以下のイベントがあります。 '''Table 5.3. Spring-DM build-in events''' ,Event,Explanation ,OsgiBundleContextRefreshedEvent,OSGiアプリケーション・コンテキストが正常に初期化、又はリフレッシュされた時に発行されます。(例:ConfigurableApplicationContextインターフェースのrefresh()メソッドが使用された場合。)アプリケーション・コンテキストのライフサイクル中でこのイベントが何回発生するかは保障できません。使用している実装に依存します。 ,OsgiBundleContextFailedEvent,アプリケーション・コンテキストが異常によりcloseされた時に発行されます。このイベントはアプリケーション・コンテキストのライフサイクル中にいつでも発生する可能性があります。リフレッシュ前、リフレッシュ中、リフレッシュ後。一般的には設定的なエラーを表します。ミスタイプ、文法的誤り、必要なbeanの定義忘れ、など。 これらのイベントを受け取りたい場合は、OsgiBundleApplicationContextListenerを実装し、それをOSGiサービスとして公開しておく必要があります。 Spring-DM extenderは自動的にlistenerを発見し、そのlistenerにイベントを送信します。 OSGiサービス・レジストリよりも優れている点として、extenderはイベント発行側からの結合を疎なものにておくことができ、さらに登録/登録解除を簡単にします。 例えば、クライアントがlistenerを登録解除することは何も特別なことではありません。単にBundleを停止すると自動的に全ての発行されているサービス(その中にはlistenerも含む)が登録解除されます。extenderによって検出されたイベントはlistenerを登録解除します。もちろん、Bundleのライフサイクル中にクライアントがマニュアルでlistenerを登録解除することもできます。 '''Note''' Spring-DMのイベントの意味づけは[Springのイベントの意味づけ|http://static.springframework.org/spring/docs/2.5.x/reference/beans.html#context-functionality-events]と若干異なります。 OSGiイベントは原因となったアプリケーションの内部のbeanには送信されませんが、他の(おそらく他のアプリケーション・コンテキスト内のbean)監視をしているbeanには送信されます。 !! 5.3. Required Spring Framework and Spring Dynamic Modules Bundles Spring Dynamic ModulesプロジェクトはSpring extenderが正常に動作する為にいくつかのOSGiプラットフォームにインストールされるべきBundleを提供しています。 ** extender自身のBundle : org.springframework.osgi.extender ** Spring Dynamic Modulesのコア実装 : org.springframework.osgi.core ** Spring Dynamic ModulesのI/OライブラリBundle : org.springframework.osgi.io 加えて、Spring FrameworkのいくつかのBundleをインストールする必要があります。 Spring Framework release 2.5の場合、Springの配布物の中のいくつかの.jarはOSGiのBundle形式になっていて、OSGiプラットフォームへ直接インストールすることができます。 必要とされる最低限のBundleは以下のとおりです。 ** spring-core.jar (bundle symbolic name : org.springframework.bundle.spring.core) ** spring-context.jar (bundle symbolic name : org.springframework.bundle.spring.context) ** spring-beans.jar (bundle symbolic name : org.springframework.bundle.spring.beans) ** spring-aop.jar (bundle symbolic name : org.springframework.bundle.spring.aop) さらに、下記のライブラリのBundleが必要です。 OSGi対応バージョンのこれらのライブラリがSpring Dynamic Modulesの配布物の中に含まれています。 ** aopalliance ** backport-util (JDK 1.4上で使用する場合) ** cglib-nodep (インターフェースではなくproxy classを使う場合。大抵の場合で必要。) ** commons-logging API (SLF4J versionを強く推奨します。) ** log4jのようなlogging実装。 !! 5.4. Spring XML authoring support Spring 2.0では(その他の機能と共に)[使いやすいXMLによる設定ファイル|http://static.springframework.org/spring/docs/2.5.x/reference/xsd-config.html]と[拡張可能なXML設定|http://static.springframework.org/spring/docs/2.5.x/reference/extensible-xml.html]が導入されました。 このことは、classpathに含めるだけで(OSGi環境外であっても)Spring XML infrastructureによってカスタムXMLスキーマを自動的に検出する機能を含みます。 Spring-DMにおいてはOSGi環境の中でも追加コードの記述やmanifestの記述なしにBundleに対してカスタム・スキーマの検出が行われます。 OSGi環境に配備された全てのBundleは(それがSpring-poweredであろうとそうでなかろうと、)カスタムSpring namespace宣言を作成する為にSpring-DMによってスキャンされます。(Bundle内のMETA-INF/spring.handlersとMETA-INF/spring.schemas) 検出された場合、Spring-DMはスキーマとnamespaceをOSGiサービス内に作成し、自動的にSpring-poweredの他のBundleから使用可能にします。 このことは、カスタム・スキーマを使用しているBundleを配備した場合、他にはそれに該当するnamespaseとスキーマを処理するライブラリを配備するだけです。 カスタム・スキーマをそのclasspathに内包しているBundleは利用可能なOSGi環境を通してそれを利用できます。 しかし、内包されているライブラリのnamespaceは他のBundleと共有されません。それらは他のBundleからは不可視です。 簡単に言えば、Spring-DMを使用している際は、カスタムSpring namespaceは他の作用なく透過的にサポートされます。 内包されたnamespaceプロバイダは優先的に使用されますが、共有はされません。反対に、Bundleとして配備されたプロバイダは他のBundleに対しても公開されます。 !! 5.5. Importing and Exporting packages Manifestヘッダー内の「Import-Package」と「Export-Package」についての詳細はOSGiプラットフォームのドキュメントを参照してください。 BundleはBundleが依存する全ての外部パッケージへの「Import-Package」エントリーが必要になります。 もしそのBundleが他のBundleがアクセスする為の型(原文:type、多分Javaで言えばclassとかenumとかでしょう)を提供している場合、それらの外部から見えるようにしたいパッケージの全てに「Export-Package」エントリーが必要です。 ---- ''(訳注:ここは5.6.章の横の囲みのコラムです)'' '''What is the context class loader?''' 第3のコンテキストclass loaderがJ2SEで大したファンファーレ無しでこっそり導入されました。 下記はJavaサイトにあるチュートリアルの中から引用した短い定義です。 Java2プラットフォームは「コンテキストclass loader」の概念を導入しました。 スレッドのコンテキストclass loaderは、デフォルトでは、スレッドの親のコンテキストclass loaderにセットされています。 スレッドの階層構造は根源スレッド(プログラムが動いているスレッド)を根としています。。 根源スレッドのコンテキストclass loaderはアプリケーションによってロードされたコンテキストclass loaderにセットされています。 なのであなたが厳密にスレッドのコンテキストclass loaderを変更しない限り、どのコンテキストclass loaderもアプリケーションのclass loaderにセットされています。 つまり、コンテキストclass loaderはアプリケーションがロードすることのできるclassをロードすることができるということです。 この(コンテキストclass)loaderはRMI(Java Remote Method Invocation)などのJava runtimeがユーザアプリケーションに代わってclassやリソースをロードするのに使われます。 コンテキストclass loaderは(全てのJava2プラットフォームのclass loaderと同じように)親class loaderを持ち、先に説明したのと同じ委譲モデルをサポートしています。 ''(囲みコラムここまで)'' ---- !! 5.6. Considerations when using external libraries 多くのエンタープライズアプリケーションは、アプリケーションが含む全ての型とリソースがコンテキストclass loaderからアクセスできるとものと仮定しています。 ほとんどの開発者がコンテキストclass loaderを使っておらず、ローダーは主にマルチスレッド化されているアプリケーション・サーバやコンテナから使用されています。 OSGi R4において、コンテキストclass loaderから参照可能な型やリソースのセットは定義されていません。 このとこはつまり、OSGiプラットフォームはスレッドclass loaderからの値を保障しない、別の言い方をすれば「管理していない」ということです。 したがって、独自にclassを読み込んだりclassを動的に生成するコード(例えばライブラリのようなコード)はOSGi環境内では問題を引き起こす可能性があります。 Spring Dynamic ModulesはBundleに代わってアプリケーション・コンテキストを生成することにより、Bundleのclasspath上の全ての型とリソースがコンテキストclass loaderを通してアクセス可能であることを保障します。 Spring Dynamic Modulesは外部のサービスを使用する際や外部へ公開したサービスが使用される際にコンテキストclass loaderを通して何がアクセス可能かをコントロールすることを可能にします。 このことについての詳細はセクション5を参照してください。 動的生成されたクラスとサードパーティーのライブラリによる暗黙的なclasspath依存に関する標準化されたサポートを実装する作業はOSGi R5の開発の中で進んでいます それまでの間、DynamicImport-Package manifest headerやEquinoxのbuddyメカニズムのように特定のOSGi実装が提供する機能に依存し続けなくてはなりません。 Spring Dynamic Modulesのドキュメントではよく知られたエンタープライズ・ライブラリーの知られていない問題やその回避策にも言及します。 !! 5.7. Diagnosing problems 選択したOSGiプラットフォーム実装はOSGi環境の現在の状態についての十分多くの情報提供しているはずです。 例えば、Equinoxを-console引数で起動した場合、コマンドライン・コンソールを通じて、インストールされているBundleとそのステータス、Bundleが公開しているサービスやパッケージを確認することができ、Bundleがfailしている理由を確認ることができ、Bundleのライフサイクルをコントロールすることができます。 加えて、Spring自身や Spring Dynamic ModulesのBundleは問題解決の助けとなる拡張可能なロギングシステムを含んでいます。 お勧めのアプローチはSimple Logging Facade for Java ([slf4j|http://www.slf4j.org/])のslf4j-api.jarとslf4j-log4j13.jar Bundle(配布されている.jarが既にOSGi Bundleの形式になっています)を配備することです。 あとは普通にlog4j.propertiesをBundle classpathのrootに作成するだけです。 Spring Dynamic Modulesは内部でcommons-logging APIを使用しており、ロギング実装は完全にプラガブルです。 log4j以外の実装を使用したロギングの設定方法の詳細についてはFAQや巻末の情報ページを参照してください。 ---- [[次へ|dm-service-registry]] [[前へ|dm-bnd-app-ctx]] [[目次へ|sdm_index]] [[技術的雑談へ戻る|技術的雑談]]