DynamicImport-PackageディレクティブとEquinoxのBuddy Class Loading
GroovyのBundleを見て驚いたのが、MANIFEST.MFに
DynamicImport-Package: *
と書かれていたこと。これを見て頭がクラクラした。
何が問題?
こう書かれてしまうと、OSGi実行環境上で動いているBundleがExport-Packageで宣言しているパッケージのクラスに全てアクセスできてしまう。せっかくBundleごとにアクセスをコントロールするための壁を作ったのに、Groovyエンジンを通して、全てアクセスできてしまう。これって、OSGiを使わない、クラスパスべったりの開発との違いがなくなってしまう。
解決策はあるの?
調べた感じ、解決できるのはEquinoxくらいしかなさそう。Equinoxは、DynamicImport-Packageを横断的に宣言できるEclipse-BuddyPolicyという宣言の種別があって、それを利用するとアクセスできるBundleのコントロールができる。宣言例はこちら。
Eclipse-BuddyPolicy: dependent
それと定義。
Eclipse-BuddyPolicy ::= buddy-policy ( ',' buddy-policy )* buddy-policy ::= ('dependent' | 'registered' | 'global' | 'app' | 'ext' | 'boot')
- dependent - このBundleに依存している全てのBundle間のクラスローダアクセスを解放。直接依存していなくてもどこかのBundleがRequire-Bundleを使っていて、visibilityの宣言がreexportとなっていると、同様に解放される。依存するBundleが増えるにつれ、クラスを検索するBundleの数が増えるため、パフォーマンス上の問題を引き起こす。
- registered - 依存しているBundleの中でEclipse-RegisterBuddyで宣言しされているBundleとの間のクラスローダアクセスを解放。
- global - 全てのexportされているパッケージにクラスローダアクセスを解放。DynamicImport-Packageと同じ。
- app - アプリケーションのクラスローダに解放。起動時に指定するクラスパスに指定されたJARに対し解放すると言った方がわかりやすいかも。
- ext - JRE/lib/ext以下に配置されているJARに対し、解放する。
- boot - ブートクラスローダに対し解放する。
これをBuddy Class Loadingと言います。Jiemamyチームによる翻訳があるので、そちらも参照ください。Felixでどうやるんだろ、と思って調べたら、ちょっとおもしろかったです。
ただ、Buddy Class Loadingによるクラスローダの解放は、いわば裏口を開けることになるので、モジューラなアプリケーション開発の足かせになる可能性があります。気をつけてお使いくだし。そうはいっても、Class#forName()でクラス定義を取ってこようと思ったら、今はこの方法を取らざるおえませんが。OSGiの仕様には、まだこの辺の仕様は定義されてません。