2009年11月2日月曜日

Apache Felix: getting started

Eclipse が OSGi ベースになった頃からか、何かといろいろ、OSGi 化し始めている。

と言うわけで、とっかかりになるような簡単な OSGi バンドルを作ってみる。ただし一個のバンドルで"Hello World"をやっても何が OSGi なのか分からないので、サービス用とクライアント用の二つのバンドルを、OSGiを介在させて連携させてみる。

モノは、Apache の OSGi release4 実装、Felix を使用。

  ■ こんな作り
Greeting Service
名前を受け取り、その名前を含むあいさつを返すメソッドを持つサービス
Greeting Service Client
標準入力から読み取った名前を Greeting Service に渡し、結果を標準出力に表示する。ただし、Greeting Service が動いてなかったら、サービスが無い旨を表示する。
環境
felix-framework-2.0.1 jdk1.6.0_13


  ■ 作り方
今回は IDE 等を使わずに、cygwin で vi、javac、jar を使って実装してみる。 c:\work\の下で作業すると仮定する。

◆ Greeting Service
適当な作業ディレクトリ(仮に c:\work\service とする)を作る。 以下のようにソース群を書き、パッケージ名と適当に対応させたディレクトリに置く。
  • GreetingService.java: サービスの I/F を定義
    package trial1.service;
    
    public interface GreetingService {
        public String greet(String name);
    }
  • GreetingServiceImpl.java: サービスの I/F を実装
    package  trial1.service.impl;
    
    import  trial1.service.GreetingService;
    
    public class GreetingServiceImpl implements GreetingService {
        public String greet(String name) {
            return "Hello " + name + "!";
        }
    }
  • Activator.java:OSGi コマンドの処理の実装
    package  trial1.service.activator;
    
    import org.osgi.framework.BundleActivator;
    import org.osgi.framework.BundleContext;
    
    import  trial1.service.GreetingService;
    import  trial1.service.impl.GreetingServiceImpl;
    
    public class Activator implements BundleActivator {
        public void start(BundleContext context) {
            context.registerService(
                GreetingService.class.getName(), new GreetingServiceImpl(), null);
        }
        public void stop(BundleContext context) {}
    }
  • この時点で javac でコンパイルできるので、classes 下にでも classファイルを作っておく。(クラスパスに felix.jar が含まれている事が必要。)
    $ javac -d classes trial1/service/activator/Activator.java
    
  • 以下のような manifest.mfを書く: 上3行は適当だけど、下3行は大事。
    Bundle-Name: Greeting Service
    Bundle-Description: A bundle that registers an Greeting service
    Bundle-Version: 1.0.0
    Bundle-Activator: trial1.service.activator.Activator
    Export-Package: trial1.service
    Import-Package: org.osgi.framework
  • ここで service.jar に丸めて、とりあえずサービス側は終わり
    $ jar cfm service.jar manifest.mf -C classes trial1/service/
    jar の中身はこんな風になる
    $ jar tvf service.jar
         0 Mon Nov 02 02:03:10 JST 2009 META-INF/
       312 Mon Nov 02 02:03:10 JST 2009 META-INF/MANIFEST.MF
         0 Mon Nov 02 01:58:44 JST 2009 trial1/service/
         0 Mon Nov 02 01:58:44 JST 2009 trial1/service/activator/
       736 Mon Nov 02 01:58:44 JST 2009 trial1/service/activator/Activator.class
       183 Mon Nov 02 01:58:44 JST 2009 trial1/service/GreetingService.class
         0 Mon Nov 02 01:58:44 JST 2009 trial1/service/impl/
       546 Mon Nov 02 01:58:44 JST 2009 trial1/service/impl/GreetingServiceImpl.class
◆Greeting Service Client
  • 別の作業ディレクトリ(仮に c:\work\client とする)を適当に作る。
  • Activator.java を書いて、適当にパッケージに合わせたフォルダに置く。
    package trial1.client.activator;
    
    import java.io.BufferedReader;
    import java.io.InputStreamReader;
    import java.io.IOException;
    
    import org.osgi.framework.BundleActivator;
    import org.osgi.framework.BundleContext;
    import org.osgi.framework.Filter;
    import org.osgi.util.tracker.ServiceTracker;
    
    import trial1.service.GreetingService;
    
    public class Activator implements BundleActivator {
    
        public void start(BundleContext context) throws Exception {
            Filter filter = context.createFilter(
                    "(&(objectClass=" + GreetingService.class.getName() + "))");
            ServiceTracker tracker = new ServiceTracker(context, filter, null);
    
            tracker.open();
    
            try {
                BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
    
                System.out.print("Enter name: ");
                String name = in.readLine();
    
                GreetingService service = (GreetingService) tracker.getService();
                if (null == service) System.out.println("no available service");
                else System.out.println(service.greet(name));
    
            } catch (Exception ex) { }
        }
        public void stop(BundleContext context) {}
    }
  • ここでコンパイル。但し、クラスパスには felix.jar と上で作った service.jar を含めておく。
    javac -d classes trial1/client/activator/Activator.java
  • manifest.mf は以下のように書く。大事なのは下4行。
    Bundle-Name: Greeting Service Client
    Bundle-Description: A greeting service client
    Bundle-Version: 1.0.0
    Bundle-Activator: trial1.client.activator.Activator
    Import-Package: org.osgi.framework,
     org.osgi.util.tracker,
     trial1.service
  • client.jar に丸める。
    $ jar cfm client.jar manifest.mf -C classes trial1/client/
    以下のような中身になっている事を確認
    $ jar tvf client.jar
         0 Mon Nov 02 02:12:44 JST 2009 META-INF/
       308 Mon Nov 02 02:12:44 JST 2009 META-INF/MANIFEST.MF
         0 Mon Nov 02 02:10:56 JST 2009 trial1/client/
         0 Mon Nov 02 02:10:56 JST 2009 trial1/client/activator/
      1652 Mon Nov 02 02:10:56 JST 2009 trial1/client/activator/Activator.class
以上、作りこみ終了

  ■ 確認

◆デプロイ
-> install file:/c:/work/service/service.jar
Bundle ID: 23
-> install file:/c:/work/client/client.jar
Bundle ID: 24
ここで ps と打ち込んでみると以下のような感じ
-> ps
START LEVEL 1
   ID   State         Level  Name
[   0] [Active     ] [    0] System Bundle (2.0.1)
[   1] [Active     ] [    1] Apache Felix Bundle Repository (1.4.2)
[   2] [Active     ] [    1] Apache Felix Shell Service (1.4.1)
[   3] [Active     ] [    1] Apache Felix Shell TUI (1.4.1)
[  23] [Installed  ] [    1] Greeting Service (1.0.0)
[  24] [Installed  ] [    1] Greeting Service Client (1.0.0)
Greeting Service(23) が Active になっていないのを知りつつ、あえてGreeting Service Client(24) を開始してみる。
-> start 24
Enter name: World
no available service
ちゃんと必要なサービスが無い旨が表示されていて、期待通り。 次にGreeting Service(23) を起動してから、Client(24)を開始してみる。
-> stop 24
-> start 23
-> start 24
Enter name: World
Hello World!
できた。ちゃんとサービスとクライアントが連携して"Hello World!"が表示された。
 
■ 資料
・参考にしたチュートリアル→Apache Felix OSGi Tutorial
・OSGi のJavaDoc →OSGi"! Service Platform Release 4 Version 4.2.0

0 件のコメント:

コメントを投稿