OSGiの紹介
1前書き
いくつかのJavaのミッションクリティカルなミドルウェアアプリケーションには、厳しい技術的要件があります。
実行中のサービスを中断しないようにホットデプロイをサポートしなければならないものもあれば、外部のレガシーシステムをサポートするために同じパッケージの異なるバージョンで作業できる必要があるものもあります。
OSGiプラットフォームは、この種の要件をサポートするための実行可能なソリューションです。
-
「Open Service Gateway Initiative」は、Javaベースのコンポーネントシステムを定義する仕様です。
それ以来、これはコンポーネントシステムの優れた標準となり、今日では広く使用されています。たとえば、
Eclipse IDE
は、
OSGi
ベースのアプリケーションです。
この記事では、
Apache
が提供する実装を利用した
OSGi
の基本機能をいくつか探ります。
2 OSGiの基本
OSGiでは、単一のコンポーネントをバンドルと呼びます。
論理的には、
バンドルは独立したライフサイクルを持つ機能の一部です –
これは独立して起動、停止、削除できることを意味します。
技術的には、バンドルはOSGi固有のヘッダーをいくつか含む
MANIFEST.MF
ファイルを含む単なるjarファイルです。
OSGi
プラットフォームは、バンドルが利用可能になったとき、またはバンドルがプラットフォームから削除されたときについての通知を受け取る方法を提供します。
これにより、適切に設計されたクライアントは、依存しているサービスが瞬間的に利用できなくても、おそらく機能が低下しても機能し続けることができます。
そのため、バンドルは、どのパッケージにアクセスする必要があるかを明示的に宣言する必要があり、
OSGi
プラットフォームは、依存関係がバンドル自体またはプラットフォームにすでにインストールされている他のバンドルで使用可能な場合にのみ開始します。
3ツールを入手する
私たちは
OSGi
で私たちの旅を始めることができます。http://www.apache.org/dyn/closer.lua/karaf/4.1.3/apache-karaf-4.1.3.zip[この記事から
Apache Karaf
の最新版をダウンロードしてください。リンク]。
Apache Karaf
は、
__ OSGiベースのアプリケーションを実行するプラットフォームです。これは、
Apache Felix
と呼ばれる
OSGi
仕様の
Apache
による実装に基づいています。
Karaf
は
Felix
の上にいくつかの便利な機能を提供しています。それは
OSGi
に精通するのを手助けするでしょう、例えば私達がプラットフォームと対話することを可能にするコマンドラインインターフェース。
Karaf
をインストールするには、https://karaf.apache.org/manual/latest/#
quick
start[公式のドキュメント]からインストール手順に従ってください。
4.バンドルエントリポイント
OSGi環境でアプリケーションを実行するには、それを
OSGi
バンドルとしてパックし、アプリケーションのエントリポイントを定義する必要があります。これは通常の
public static void main(String[]args)
メソッドではありません。
それでは、まず__OSGiベースの「Hello World」アプリケーションを作成しましょう。
コアOSGi APIへの単純な依存関係の設定を始めます。
<dependency>
<groupId>org.osgi</groupId>
<artifactId>org.osgi.core</artifactId>
<version>6.0.0</version>
<scope>provided</scope>
</dependency>
依存関係は
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ではなくバンドルを構築するつもりであることを明示的に述べなければなりません。
<packaging>bundle</packaging>
次に、
HelloWorld
クラスを
OSGi
バンドルとしてパッケージ化するために、__maven-bundle-pluginを利用します。
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<version>3.3.0</version>
<extensions>true</extensions>
<configuration>
<instructions>
<Bundle-SymbolicName>
${pom.groupId}.${pom.artifactId}
</Bundle-SymbolicName>
<Bundle-Name>${pom.name}</Bundle-Name>
<Bundle-Version>${pom.version}</Bundle-Version>
<Bundle-Activator>
com.baeldung.osgi.sample.activator.HelloWorld
</Bundle-Activator>
<Private-Package>
com.baeldung.osgi.sample.activator
</Private-Package>
</instructions>
</configuration>
</plugin>
説明セクションでは、バンドルのMANIFESTファイルに含める
OSGi
ヘッダーの値を指定します。
Bundle-Activator
は、バンドルの開始と停止に使用される
BundleActivator
実装の完全修飾名です。これは、今作成したクラスを指します。
Private-Package
はOSGiヘッダではありませんが、パッケージをバンドルに含めるようにプラグインに指示するために使用されますが、他のパッケージには使用できません。通常のコマンド
mvn clean install
を使ってバンドルをビルドできます。
6. バンドルのインストールと実行
次のコマンドを実行して
Karaf
を起動しましょう。
<KARAF__HOME>/bin/karaf start
ここで、
<KARAF
HOME>
は、
Karaf
がインストールされているフォルダーです。
Karaf__コンソールのプロンプトが表示されたら、次のコマンドを実行してバンドルをインストールします。
> bundle:install mvn:com.baeldung/osgi-intro-sample-activator/1.0-SNAPSHOT
Bundle ID: 63
assignedTheバンドルがインストールされました。次のコマンドで起動できます
> bundle:start 63
Hello World
バンドルが開始されるとすぐに「Hello World」がすぐに表示されます。これでバンドルを停止してアンインストールすることができます。
> bundle:stop 63
> bundle:uninstall 63
stop()
メソッドのコードに従って、「Goodbye World」がコンソールに表示されます。
7. OSGiサービス
それでは、シンプルな
OSGi
サービスを書きましょう。
package com.baeldung.osgi.sample.service.definition;
public interface Greeter {
public String sayHiTo(String name);
}
それを実装した
BundleActivator
も書いてみましょう。そのため、バンドルの起動時にサービスをインスタンス化してプラットフォームに登録できます。
package com.baeldung.osgi.sample.service.implementation;
public class GreeterImpl implements Greeter, BundleActivator {
private ServiceReference<Greeter> reference;
private ServiceRegistration<Greeter> 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<String, String>());
reference = registration
.getReference();
}
@Override
public void stop(BundleContext context) throws Exception {
System.out.println("Unregistering service.");
registration.unregister();
}
}
OSGi
プラットフォームにサービスの新しいインスタンスを登録するように要求する手段として、
BundleContext
を使用します。
私達はまた私達の簡単なシナリオでは必要とされないサービスの種類と可能な設定パラメータのマップを提供するべきです。
それでは、
maven-bundle-plugin
の設定を進めましょう。
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<extensions>true</extensions>
<configuration>
<instructions>
<Bundle-SymbolicName>
${project.groupId}.${project.artifactId}
</Bundle-SymbolicName>
<Bundle-Name>
${project.artifactId}
</Bundle-Name>
<Bundle-Version>
${project.version}
</Bundle-Version>
<Bundle-Activator>
com.baeldung.osgi.sample.service.implementation.GreeterImpl
</Bundle-Activator>
<Private-Package>
com.baeldung.osgi.sample.service.implementation
</Private-Package>
<Export-Package>
com.baeldung.osgi.sample.service.definition
</Export-Package>
</instructions>
</configuration>
</plugin>
今回は、
Export-Package
ヘッダーを介して
com.baeldung.osgi.sample.service.definition
パッケージのみがエクスポートされたことに注意する必要があります。
これのおかげで、
OSGi
は他のバンドルがサービスインターフェースで指定されたメソッドだけを呼び出すことを可能にします。パッケージ
com.baeldung.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
の依存関係を見てみましょう。
<dependency>
<groupId>com.baeldung</groupId>
<artifactId>osgi-intro-sample-service</artifactId>
<version>1.0-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.osgi</groupId>
<artifactId>org.osgi.core</artifactId>
<version>6.0.0</version>
</dependency>
9クライアントとサービス
次の手順を実行して、Karafにクライアントとサービスのバンドルをインストールしましょう。
> install mvn:com.baeldung/osgi-intro-sample-service/1.0-SNAPSHOT
Bundle ID: 64
> install mvn:com.baeldung/osgi-intro-sample-client/1.0-SNAPSHOT
Bundle ID: 65
各バンドルに割り当てられているID番号は異なる可能性があることに常に留意してください。
それでは、クライアントバンドルを起動しましょう。
> start 65
したがって、クライアントがアクティブで、サービスを待っているので、何も起こりません。
> start 64
Registering service.
Service registered.
Hello John
サービスのBundleActivatorが起動するとすぐに、サービスがプラットフォームに登録されます。それは順番に、それが待っていたサービスが利用可能であることをクライアントに通知します。
その後、クライアントはサービスへの参照を取得し、それを使用してサービスバンドルを通じて配信された実装を呼び出します。
10結論
この記事では、OSGiの潜在的な機能を理解するのに十分であるという直接的な例を用いて、OSGiの本質的な機能を調べました。
結論として、単一のアプリケーションが何の問題もなく更新されなければならないということを保証しなければならないときはいつでも、OSGiは実行可能な解決策になり得る。
この記事のコードはhttps://github.com/eugenp/tutorials/tree/master/osgi[over on GitHub]にあります。