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の仕様には、まだこの辺の仕様は定義されてません。