2011年11月13日

OSGI入門 その2 〜Serviceについて〜


前回は導入として、Hello Worldをやりましたが、今回からはOSGIの肝であるサービスの話をしていきます。
前述の通り、OSGIはバンドルと呼ばれるプログラムコンポーネントの集合体を動かすためのフレームワークです。
Serviceとはそのひとつのバンドルが公開する一連の処理を意味します。Javaオブジェクトであればなんでも
サービスになりえます。しかし、実際にはまず公開するIFを作成して、サービスとして登録するのはそのIFの
実装クラスになるでしょう。

・サービスバンドルの作成
まずはサービスバンドルを作ります。前回と同様、プラグインプロジェクトにて以下のプロジェクトを作成します。

プロジェクト名:ServiceBundle
Activator:test.service1.activator.Activator

続いてIFの作成

package test.service1.serviceif;

/**
 * サービスinterface
 * @author rag-timer
 *
 */
public interface MyServiceIF {
	/**
	 * なんらかのサービス
	 */
	public void print();
}

お次に実装の作成

package test.service1.serviceimpl;

import test.service1.serviceif.MyServiceIF;

public class MyServiceImpl implements MyServiceIF {
	@Override
	public void print() {
		System.out.println("This is my service!");
	}
}

なんの意味もないサービスですが、とりあえずこれで登録するネタはそろいました。では、Activatorで登録します。
サービスの登録にはBundleContext#registerService()を使用します。

/*
 * (non-Javadoc)
 * @see org.osgi.framework.BundleActivator#start(org.osgi.framework.BundleContext)
 */
public void start(BundleContext bundleContext) throws Exception {
	Activator.context = bundleContext;

	System.out.println("ServiceBundle start");

	//サービスの実体
	MyServiceIF service = new MyServiceImpl();

	//サービスの登録を行う
	context.registerService(MyServiceIF.class.getName(), service, null);

	System.out.println("Service registed");
}

registerService()の一つ目の引数はサービスのこのOSGIフレームワーク内で識別するための文字列です。
別に文字列であればなんでもかまいませんが、普通IFの名前を使用します。二つ目の引き数にはサービス
の実体を指定します。三つ目はこのサービスのプロパティを指定しますが今回は使いません。

続いて、このサービスを「公開する」ための作業を行います。バンドル間でのサービス呼び出しは一般の
Javaオブジェクトの呼び出しと大して変わりませんが、別バンドルが公開しているIFをそのままimportする
ことはできません。かならず「サービス公開者がexportし」「サービス消費者がimportする」という作業が
必要になります。この設定を行うのがMETA-INF/MANIFEST.MFです。なお公開はパッケージ単位で行います。

ではサービスの公開を行いましょう。MANIFEST.MFをダブルクリックし、[ランタイム」タブを選択して下さい。
左側の[エクスポートされるパッケージ]欄で追加を押下し、IFのパッケージを指定します。

1.png
これでサービス側のバンドルの準備は完了しました。

・サービス消費バンドルを作成

次にクライアントとなるサービス消費バンドルを作成します。

プロジェクト名:ServiceConsumerBundle
Activator:test.serviceconsumer1.activator.Activator

さて、まずはimportを行いましょう。これを行わないと、サービスIFが参照できません。
MANIFEST.MFをダブルクリックして、[依存関係]タブから[インポート済みパッケージ]を選んでサービスIFの
パッケージを選択します。なお、当然、exportをこの段階で行っていなければリストに出てきません。

2.png
続いて以下のようにActivatorを実装します。

package test.serviceconsumer1.activator;

import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceReference;

import test.service1.serviceif.MyServiceIF;

public class Activator implements BundleActivator {

	private static BundleContext context;

	private boolean flg = true;

	private final Thread myThread = new Thread(){
		public void run(){

			while(flg){

				//ServeiceReferenceの取得
				ServiceReference ref = Activator.context.getServiceReference(MyServiceIF.class.getName());

				//サービスの生成
				MyServiceIF service = (MyServiceIF)Activator.context.getService(ref);

				//サービスの呼び出し
				service.print();

				try {
					Thread.sleep(5000);
				} catch (InterruptedException e) {
					break;
				}

			}
		}
	};

	static BundleContext getContext() {
		return context;
	}

	public void start(BundleContext bundleContext) throws Exception {

		System.out.println("ServiceConsumerBundle start");

		Activator.context = bundleContext;

		//スレッドのスタート
		myThread.start();
	}

	public void stop(BundleContext bundleContext) throws Exception {

		System.out.println("ServiceConsumerBundle stop");

		flg = false;
		Activator.context = null;
	}

}


Activator#startでは、定期的にサービスを使用するスレッドを起動してます。そのスレッドのrun()の中で
BundleContext#getServiceReference()でServiceReferenceオブジェクトを作成します。引数で渡すのは
先ほどサービスの登録に使用したサービス識別用の文字列、すなわちサービスのクラス名です。
作成したServiceReferenceを引数に、BundleContext#getService()でサービスが取得できます。
とりあえずはさらっと流します。

これで実行構成からこの2つのバンドルを構成し、実行すると・・・

osgi> ServicConsumerBundle start
ServiceBundle start
Service registed
This is my service!
This is my service!
・・・

てな感じで表示されるはずです。ちゃんとサービス呼べてますね。

・でもサービスをupdateすると・・・

では前回のようにサービスを書き換えてみましょう。適当にprintlnの文字列を変更して保存し、ssで
IDを確認後

update サービスバンドルのid

を実行します。すると・・・

osgi> Exception in thread "Thread-2" java.lang.NullPointerException: A null service reference is not allowed.
at org.eclipse.osgi.framework.internal.core.BundleContextImpl.getService(BundleContextImpl.java:660)
at test.serviceconsumer1.activator.Activator$1.run(Activator.java:22)

こんなんなっちゃいました。update後、スレッド内の処理でServiceReferenceが取れていないようです。
この解決策は次回にします。

では。
posted by sandman at 00:07| Comment(0) | Java | このブログの読者になる | 更新情報をチェックする
この記事へのコメント
コメントを書く
お名前:

メールアドレス:

ホームページアドレス:

コメント:

認証コード: [必須入力]


※画像の中の文字を半角で入力してください。
×

この広告は1年以上新しい記事の投稿がないブログに表示されております。