OSGiの紹介

OSGiの概要

1. 前書き

いくつかのJavaミッションクリティカルなミドルウェアアプリケーションには、いくつかの厳しい技術的要件があります。

実行中のサービスを中断させないようにホットデプロイをサポートする必要があるものもあれば、外部のレガシーシステムをサポートするために同じパッケージの異なるバージョンで作業できる必要があるものもあります。

OSGiプラットフォームは、この種の要件をサポートするための実行可能なソリューションを表しています。

The Open Service Gateway Initiative is a specification defining a Java-based component system.現在はOSGi Allianceによって管理されており、最初のバージョンは1999年にさかのぼります。

それ以来、コンポーネントシステムの優れた標準であることが証明され、今日では広く使用されています。 たとえば、Eclipse IDEOSGiベースのアプリケーションです。

この記事では、Apacheによって提供される実装を活用して、OSGiのいくつかの基本的な機能について説明します。

2. OSGiの基本

OSGiでは、単一のコンポーネントはバンドルと呼ばれます。

論理的には、a bundle is a piece of functionality that has an independent lifecycle –は、個別に開始、停止、および削除できることを意味します。

技術的には、バンドルは、いくつかのOSGi固有のヘッダーを含むMANIFEST.MFファイルを含む単なるjarファイルです。

OSGiプラットフォームは、バンドルが利用可能になったとき、またはバンドルがプラットフォームから削除されたときに通知を受け取る方法を提供します。 これにより、適切に設計されたクライアントは、依存するサービスが一時的に利用できない場合でも、機能が低下している可能性があります。

そのため、バンドルはアクセスする必要のあるパッケージを明示的に宣言する必要があり、OSGiプラットフォームは、依存関係がバンドル自体またはプラットフォームにすでにインストールされている他のバンドルで利用可能な場合にのみ開始します。

3. ツールの入手

this linkから最新バージョンのApache Karafをダウンロードして、OSGiから旅を始めます。 Apache Karafは、OSGi-ベースのアプリケーションを実行するプラットフォームです。これは、Apache Felixと呼ばれるOSGi仕様のApacheの実装に基づいています。

Karafは、Felixに加えて、プラットフォームとの対話を可能にするコマンドラインインターフェイスなど、OSGiに精通するのに役立ついくつかの便利な機能を提供します。

Karafをインストールするには、official documentationのインストール手順に従います。

4. バンドルエントリポイント

OSGi環境でアプリケーションを実行するには、アプリケーションをOSGiバンドルとしてパックし、アプリケーションのエントリポイントを定義する必要があります。これは、通常のpublic static void main(String[] args)メソッドではありません。

それでは、OSGi-ベースの「HelloWorld」アプリケーションを構築することから始めましょう。

コアOSGi APIへの単純な依存関係の設定を開始します。


    org.osgi
    org.osgi.core
    6.0.0
    provided

依存関係はOSGiランタイムで使用可能であり、バンドルに埋め込む必要がないため、providedとして宣言されます。

簡単なHelloWorldクラスを書いてみましょう。

public class HelloWorld implements BundleActivator {
    public void start(BundleContext ctx) {
        System.out.println("Hello world.");
    }
    public void stop(BundleContext bundleContext) {
        System.out.println("Goodbye world.");
    }
}

BundleActivatorは、バンドルのエントリポイントであるクラスによって実装される必要があるOSGiによって提供されるインターフェイスです。

start()メソッドは、このクラスを含むバンドルが開始されるときに、OSGiプラットフォームによって呼び出されます。 一方、stop()は、バンドルが停止する直前に呼び出されます。

各バンドルには最大で1つのBundleActivatorを含めることができることに注意してください。 両方のメソッドに提供されるBundleContextオブジェクトにより、OSGiランタイムとの対話が可能になります。 すぐに戻ります。

5. バンドルの構築

pom.xmlを変更して、実際のOSGiバンドルにしましょう。

まず、jarではなくバンドルを作成することを明示的に示す必要があります。

bundle

次に、Apache Felixコミュニティの好意でmaven-bundle-plugin,を活用して、HelloWorldクラスをOSGiバンドルとしてパッケージ化します。


    org.apache.felix
    maven-bundle-plugin
    3.3.0
    true
    
        
            
                ${pom.groupId}.${pom.artifactId}
            
            ${pom.name}
            ${pom.version}
            
                com.example.osgi.sample.activator.HelloWorld
            
            
                com.example.osgi.sample.activator
            
        
    

手順のセクションでは、バンドルのMANIFESTファイルに含めるOSGiヘッダーの値を指定します。

Bundle-Activatorは、バンドルの開始と停止に使用されるBundleActivator実装の完全修飾名であり、今作成したクラスを指します。

Private-PackageはOSGiヘッダーではありませんが、プラグインにパッケージをバンドルに含めるように指示するために使用されますが、他のパッケージでは使用できません。 これで、通常のコマンドmvn clean installを使用してバンドルを構築できます。

6. バンドルのインストールと実行

次のコマンドを実行して、Karafを開始しましょう。

/bin/karaf start

ここで、<KARAF_HOME>は、Karafがインストールされているフォルダーです。 Karafコンソールのプロンプトが表示されたら、次のコマンドを実行してバンドルをインストールできます。

> bundle:install mvn:com.example/osgi-intro-sample-activator/1.0-SNAPSHOT
Bundle ID: 63

これにより、ローカルMavenリポジトリーからバンドルをロードするようにKarafに指示されます。

代わりに、Karafは、すでにインストールされているバンドルの数に依存し、異なる場合があるバンドルに割り当てられた数値IDを出力します。 バンドルがインストールされたので、次のコマンドで開始できます。

> bundle:start 63
Hello World

バンドルが開始されるとすぐに「Hello World」が表示されます。 これで、バンドルを停止してアンインストールできます。

> bundle:stop 63
> bundle:uninstall 63

stop()メソッドのコードに応じて、「GoodbyeWorld」がコンソールに表示されます。

7. OSGiサービス

簡単なOSGiサービス、つまり人々に挨拶するためのメソッドを公開するインターフェースを書き続けましょう。

package com.example.osgi.sample.service.definition;
public interface Greeter {
    public String sayHiTo(String name);
}

BundleActivatorでもある実装を記述して、バンドルの開始時にサービスをインスタンス化し、プラットフォームに登録できるようにします。

package com.example.osgi.sample.service.implementation;
public class GreeterImpl implements Greeter, BundleActivator {

    private ServiceReference reference;
    private ServiceRegistration registration;

    @Override
    public String sayHiTo(String name) {
        return "Hello " + name;
    }

    @Override
    public void start(BundleContext context) throws Exception {
        System.out.println("Registering service.");
        registration = context.registerService(
          Greeter.class,
          new GreeterImpl(),
          new Hashtable());
        reference = registration
          .getReference();
    }

    @Override
    public void stop(BundleContext context) throws Exception {
        System.out.println("Unregistering service.");
        registration.unregister();
    }
}

OSGiプラットフォームにサービスの新しいインスタンスを登録するように要求する手段として、BundleContextを使用します。

また、サービスのタイプと可能な構成パラメーターのマップを提供する必要があります。これらは、単純なシナリオでは必要ありません。 次に、maven-bundle-pluginの構成に進みましょう。


    org.apache.felix
    maven-bundle-plugin
    true
    
        
            
                ${project.groupId}.${project.artifactId}
            
            
                ${project.artifactId}
            
            
                ${project.version}
            
            
                com.example.osgi.sample.service.implementation.GreeterImpl
            
            
                com.example.osgi.sample.service.implementation
            
            
                com.example.osgi.sample.service.definition
            
        
    

今回は、Export-Packageヘッダーを介してcom.example.osgi.sample.service.definitionパッケージのみがエクスポートされたことに注意してください。

このおかげで、OSGiは、他のバンドルがサービスインターフェイスで指定されたメソッドのみを呼び出すことを許可します。 パッケージcom.example.osgi.sample.service.implementationはプライベートとしてマークされているため、他のバンドルは実装のメンバーに直接アクセスできません。

8. OSGiクライアント

それでは、クライアントを作成しましょう。 起動時にサービスを検索して呼び出します。

public class Client implements BundleActivator, ServiceListener {
}

BundleActivator start()メソッドを実装しましょう:

private BundleContext ctx;
private ServiceReference serviceReference;

public void start(BundleContext ctx) {
    this.ctx = ctx;
    try {
        ctx.addServiceListener(
          this, "(objectclass=" + Greeter.class.getName() + ")");
    } catch (InvalidSyntaxException ise) {
        ise.printStackTrace();
    }
}

addServiceListener()メソッドを使用すると、クライアントは、提供された式に準拠するサービスに関する通知を送信するようにプラットフォームに要求できます。

この式はLDAPの構文と同様の構文を使用しており、この場合、Greeterサービスに関する通知を要求しています。

コールバックメソッドに進みましょう:

public void serviceChanged(ServiceEvent serviceEvent) {
    int type = serviceEvent.getType();
    switch (type){
        case(ServiceEvent.REGISTERED):
            System.out.println("Notification of service registered.");
            serviceReference = serviceEvent
              .getServiceReference();
            Greeter service = (Greeter)(ctx.getService(serviceReference));
            System.out.println( service.sayHiTo("John") );
            break;
        case(ServiceEvent.UNREGISTERING):
            System.out.println("Notification of service unregistered.");
            ctx.ungetService(serviceEvent.getServiceReference());
            break;
        default:
            break;
    }
}

Greeterサービスに関連する変更が発生すると、メソッドに通知されます。

サービスがプラットフォームに登録されると、サービスへの参照を取得してローカルに保存し、それを使用してサービスオブジェクトを取得して呼び出します。

サーバーが後で登録解除されると、以前に保存された参照を使用してサーバーを削除します。つまり、プラットフォームにそれ以上使用しないことを伝えます。

ここで、stop()メソッドを記述する必要があります。

public void stop(BundleContext bundleContext) {
    if(serviceReference != null) {
        ctx.ungetService(serviceReference);
    }
}

ここでも、サービスが停止される前にクライアントが停止される場合をカバーするために、サービスをアンゲットします。 pom.xmlの依存関係を最後に見てみましょう。


    com.example
    osgi-intro-sample-service
    1.0-SNAPSHOT
    provided


    org.osgi
    org.osgi.core
    6.0.0

9. クライアントとサービス

次の手順で、クライアントとサービスのバンドルをKarafにインストールしましょう。

> install mvn:com.example/osgi-intro-sample-service/1.0-SNAPSHOT
Bundle ID: 64
> install mvn:com.example/osgi-intro-sample-client/1.0-SNAPSHOT
Bundle ID: 65

各バンドルに割り当てられる識別子番号は異なる場合があることに常に留意してください。

クライアントバンドルを開始しましょう。

> start 65

したがって、クライアントがアクティブであり、サービスを待機しているため、何も起こりません。

> start 64
Registering service.
Service registered.
Hello John

何が起こるかというと、サービスのBundleActivatorが開始するとすぐに、サービスがプラットフォームに登録されます。 それは、クライアントが待機していたサービスが利用可能であることをクライアントに通知します。

次に、クライアントはサービスへの参照を取得し、それを使用して、サービスバンドルを通じて提供される実装を呼び出します。

10. 結論

この記事では、OSGiの可能性を理解するのに十分であるという簡単な例を使用して、OSGiの重要な機能について説明しました。

結論として、1つのアプリケーションを障害なしで更新する必要があることを保証する必要がある場合は常に、OSGiが実行可能なソリューションになる可能性があります。

この投稿のコードはover on GitHubにあります。