Apache Felix に自作のサービス(OSGi service)を登録する
今回は、Apache Felix に自作のサービス(OSGi service)を登録・実行してみる。
- サービス登録用の Bundle 作成
- サービス実行用の Bundle 作成
Bundle の作成手順やインストール方法は前回 id:fits:20081118 の通り。
サービス登録用の Bundle 作成
サービスを登録するための Bundle を作成する。
サービス登録用 Bundle 作成の特徴は以下の通り。
- サービス用のインターフェースを定義
- Bundle クラスでサービスを登録
- MANIFEST.MF に Export-Package を追加
まず、サービス用のインターフェースを定義する。
sample/service/TestService.java
package sample.service; //サービス用インターフェース public interface TestService { String test(String msg); }
次に、Bundle クラスの start メソッド内でサービスインターフェースのクラス名と共にサービスの実装オブジェクトを registerService する。
なお、registerService したサービスは Bundle の停止時に自動的に unregister されるようなので、そのための処理を実装する必要は無い。
sample/TestActivator.java
package sample; import org.osgi.framework.BundleActivator; import org.osgi.framework.BundleContext; import sample.service.TestService; //サービス登録用 Bundle クラス public class TestActivator implements BundleActivator { public void start(BundleContext context) { context.registerService(TestService.class.getName(), new TestServiceImpl(), null); } public void stop(BundleContext context) { } //サービスの実装クラス private class TestServiceImpl implements TestService { public String test(String msg) { return msg + "_test"; } } }
そして、サービス登録側ではサービスインターフェースのパッケージを MANIFEST.MF の Export-Package に設定する。
META-INF/MANIFEST.MF の例
・・・ Bundle-Name: Test Service Bundle-Description: Bundle-Vendor: Bundle-Version: 1.0.0 Bundle-Activator: sample.TestActivator Import-Package: org.osgi.framework Export-Package: sample.service
こうして作成した Bundle(JAR ファイル)を Felix にインストール・開始し、Felix Shell の services コマンドでサービスが登録されている事を確認。
-> services ・・・ Test Service (8) provides: -------------------------- sample.service.TestService
サービス実行用の Bundle 作成
サービスを利用する側も Bundle として作成する。
start メソッド内でサービスリファレンスを使ってサービスを getService し、使い終わると ungetService する。
なお、サービス登録用 Bundle の時と同様に、停止時には自動的にサービスがリリース(ungetService)される模様。
sample/client/TestClientActivator
package sample.client; import org.osgi.framework.BundleActivator; import org.osgi.framework.BundleContext; import org.osgi.framework.ServiceReference; import sample.service.TestService; //サービス実行用 Bundle クラス public class TestClientActivator implements BundleActivator { public void start(BundleContext context) { //サービスリファレンス取得 ServiceReference sr = context.getServiceReference(TestService.class.getName()); if (sr != null) { //サービスの取得 TestService service = (TestService)context.getService(sr); if (service != null) { //サービスの実行 System.out.println("test service result : " + service.test("ABC")); context.ungetService(sr); } } } public void stop(BundleContext context) { } }
サービスの利用側では、利用するサービスインターフェースのパッケージを MANIFEST.MF の Import-Package に追加する。
META-INF/MANIFEST.MF の例
・・・ Bundle-Name: Test Client Bundle-Description: Bundle-Vendor: Bundle-Version: 1.0.0 Bundle-Activator: sample.client.TestClientActivator Import-Package: org.osgi.framework, sample.service
こうして作成した Bundle(JAR ファイル)を Felix にインストール・開始すると、サービスの実行結果が出力される。
-> install file:///d:/felix_service_sample/dest/clienttest.jar Bundle ID: 9 -> start 9 test service result : ABC_test
ビルドファイル
今回の JAR ファイル生成に使用した Gant ビルドファイルは以下の通り。
サービス登録・実行用の設定を BundleInfo クラスにまとめて、処理の共通化を図っている。
Gant ではこういう事ができるから使い勝手が非常によい。
build.gant
includeTargets << gant.targets.Clean Ant.property(environment: "env") felixHome = Ant.antProject.properties."env.FELIX_HOME" destDir = "dest" classesDir = "classes" jarFile = "test.jar" cleanDirectory << destDir list = [ //サービス登録用 Bundle 情報 new BundleInfo(type: "service", name: "Test Service", activator: "sample.TestActivator", importPackage: "org.osgi.framework", exportPackage: "sample.service"), //サービス実行用 Bundle 情報 new BundleInfo(type: "client", name: "Test Client", activator: "sample.client.TestClientActivator", importPackage: "org.osgi.framework, sample.service") ] //classesDir のパスを取得 def getClassesDirPath = { "${destDir}/${it.type}/${classesDir}" } target("default": "") { compile() } target(init: "") { path(id: "project.classpath") { list.each { pathelement(path: getClassesDirPath(it)) } fileset(dir: felixHome) { include(name: "bin/*.jar") } } } target(compile: "") { depends(init) list.each { def tempDir = getClassesDirPath(it) Ant.mkdir(dir: tempDir) Ant.javac(srcdir: "src_${it.type}", destdir: tempDir) { classpath(refid: "project.classpath") } } } target(build: "") { depends(compile) list.each { b -> Ant.jar(destfile: "${destDir}/${b.type}${jarFile}", basedir: getClassesDirPath(b)) { manifest { attribute(name: "Bundle-Name", value: b.name) attribute(name: "Bundle-Description", value: "") attribute(name: "Bundle-Vendor", value: "") attribute(name: "Bundle-Version", value: "1.0.0") attribute(name: "Bundle-Activator", value: b.activator) attribute(name: "Import-Package", value: b.importPackage) if (b.exportPackage != null) { attribute(name: "Export-Package", value: b.exportPackage) } } } } } //Bundle 情報クラス class BundleInfo { def type def name def activator def importPackage def exportPackage }