OSGiフレームワーク Apache Felix その4

前回の

blog1.mammb.com

では、クライアントバンドルを作成し、サービスを利用する簡単な例を見てきました。今回は、クライアントバンドルを改良していきます。

前回のクライアントバンドルの問題点

前回作成のクライアントバンドルは、バンドルをスタート時にサービスを取得していたため、サービスが変更された場合に、クライアントバンドルがその変更内容を知るすべがありませんでした。ここではクライアントバンドルがサービスの状態変更を検知するよう変更を行います。

クライアントバンドルの作成

前々回見たように、他のバンドルの変更を検知するには、ServiceListener を実装し、そのコールバックメソッドである serviceChanged により変更を検知することができます。ここでは serviceChanged を実装し、その内部で該当サービスの再取得を行います。

package tutorial.example4;

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.InvalidSyntaxException;
import org.osgi.framework.ServiceReference;
import org.osgi.framework.ServiceListener;
import org.osgi.framework.ServiceEvent;

import tutorial.example2.service.DictionaryService;

public class Activator implements BundleActivator, ServiceListener {
    private BundleContext m_context = null;
    private ServiceReference m_ref = null;
    private DictionaryService m_dictionary = null;

    public void start(BundleContext context) throws Exception {
        m_context = context;

        synchronized (this) {
            m_context.addServiceListener(this,
                "(&(objectClass=" + DictionaryService.class.getName() + ")" +
                "(Language=*))");

            ServiceReference[] refs = m_context.getServiceReferences(
                DictionaryService.class.getName(), "(Language=*)");
            if (refs != null) {
                m_ref = refs[0];
                m_dictionary = (DictionaryService) m_context.getService(m_ref);
            }
        }

        try {
            System.out.println("Enter a blank line to exit.");
            String word = "";
            BufferedReader in = new BufferedReader(new InputStreamReader(System.in));

            while (true) {
                System.out.print("Enter word: ");
                word = in.readLine();
                if (word.length() == 0) {
                    break;
                } else if (m_dictionary == null) {
                    System.out.println("No dictionary available.");
                } else if (m_dictionary.checkWord(word)) {
                    System.out.println("Correct.");
                } else {
                    System.out.println("Incorrect.");
                }
            }
        } catch (Exception ex) { }
    }

    public void stop(BundleContext context) { }

    public synchronized void serviceChanged(ServiceEvent event) {
        String[] objectClass =
            (String[]) event.getServiceReference().getProperty("objectClass");

        if (event.getType() == ServiceEvent.REGISTERED) {
            if (m_ref == null) {
                m_ref = event.getServiceReference();
                m_dictionary = (DictionaryService) m_context.getService(m_ref);
            }
        } else if (event.getType() == ServiceEvent.UNREGISTERING) {
            if (event.getServiceReference() == m_ref) {
                m_context.ungetService(m_ref);
                m_ref = null;
                m_dictionary = null;

                ServiceReference[] refs = null;
                try {
                    refs = m_context.getServiceReferences(
                        DictionaryService.class.getName(), "(Language=*)");
                } catch (InvalidSyntaxException ex) { }
                if (refs != null) {
                    m_ref = refs[0];
                    m_dictionary = (DictionaryService) m_context.getService(m_ref);
                }
            }
        }
    }
}

serviceChanged の中で、サービスの解除 (ServiceEvent.UNREGISTERING) が行われた場合、クライアントが使用することのできる他のサービスを検索して再取得しています。
新しいサービスの登録時には (ServiceEvent.REGISTERED) クライアントが利用するサービスが未登録であった場合にサービスを取得しています。

MANIFEST.MFの作成

Bundle-Name: Dynamic dictionary client
Bundle-Description: A bundle that uses the dictionary service whenever it becomes available
Bundle-Vendor: Apache Felix
Bundle-Version: 1.0.0
Bundle-Activator: tutorial.example4.Activator
Import-Package: org.osgi.framework,
 tutorial.example2.service

バンドルの作成

前回までと同様にバンドルを作成します。

> javac -classpath ..\..\bin\felix.jar;..\example2\example2.jar tutorial\example4\*.java
> jar cfm example4.jar MANIFEST.MF tutorial\example4

クライアントバンドルの実行

> java -jar bin/felix.jar
-> ps
START LEVEL 1
   ID   State         Level  Name
[   0] [Active     ] [    0] System Bundle (2.0.0)
[   1] [Active     ] [    1] Apache Felix Bundle Repository (1.4.1)
[   2] [Active     ] [    1] Apache Felix Shell Service (1.4.0)
[   3] [Active     ] [    1] Apache Felix Shell TUI (1.4.0)
-> start file:work\example2\example2.jar
-> start file:work\example4\example4.jar
Enter a blank line to exit.
Enter word: osgi
Correct.
Enter word:
-> ps
START LEVEL 1
   ID   State         Level  Name
[   0] [Active     ] [    0] System Bundle (2.0.0)
[   1] [Active     ] [    1] Apache Felix Bundle Repository (1.4.1)
[   2] [Active     ] [    1] Apache Felix Shell Service (1.4.0)
[   3] [Active     ] [    1] Apache Felix Shell TUI (1.4.0)
[   4] [Active     ] [    1] English dictionary (1.0.0)
[   5] [Active     ] [    1] Dynamic dictionary client (1.0.0)
-> stop 4
Ex4: Service of type tutorial.example2.service.DictionaryService
-> start 4
Ex4: Service of type tutorial.example2.service.DictionaryService
->

English dictionary の停止と起動のイベントがDynamic dictionary client側に通知されていることが確認できます。ServiceListener にてサービスの変化を追跡することで、動的にサービスを取得することができるようになります。


次回は、サービスの変更を追従する ServiceTracker を見ていきます。