Einführung in OSGi

1. Einleitung

Mehrere unternehmenskritische Java- und Middleware-Anwendungen stellen hohe technologische Anforderungen.

Einige müssen Hot Deployment unterstützen, um die laufenden Dienste nicht zu stören - und andere müssen in der Lage sein, mit verschiedenen Versionen desselben Pakets zu arbeiten, um externe Legacy-Systeme zu unterstützen.

Die OSGi- Plattformen stellen eine praktikable Lösung dar, um diese Art von Anforderungen zu unterstützen.

Die Open Service Gateway Initiative ist eine Spezifikation, die ein Java-basiertes Komponentensystem definiert. Es wird derzeit von der OSGi Alliance verwaltet und seine erste Version stammt aus dem Jahr 1999.

Seitdem hat es sich als großer Standard für Komponentensysteme erwiesen und ist heutzutage weit verbreitet. Die Eclipse-IDE ist beispielsweise eine OSGi- basierte Anwendung.

In diesem Artikel werden einige grundlegende Funktionen von OSGi erläutert, die die von Apache bereitgestellte Implementierung nutzen .

2. OSGi-Grundlagen

In OSGi wird eine einzelne Komponente als Bundle bezeichnet.

Ein Bundle ist logischerweise eine Funktionalität mit einem unabhängigen Lebenszyklus. Dies bedeutet, dass es unabhängig gestartet, gestoppt und entfernt werden kann.

Technisch gesehen ist ein Bundle nur eine JAR-Datei mit einer MANIFEST.MF- Datei, die einige OSGi-spezifische Header enthält.

Die OSGi- Plattform bietet eine Möglichkeit, Benachrichtigungen über die Verfügbarkeit von Bundles oder deren Entfernung von der Plattform zu erhalten. Auf diese Weise kann ein ordnungsgemäß gestalteter Client weiterarbeiten, möglicherweise mit eingeschränkter Funktionalität, selbst wenn ein Dienst, von dem er abhängt, momentan nicht verfügbar ist.

Aus diesem Grund muss ein Bundle explizit angeben, auf welche Pakete es Zugriff haben muss, und die OSGi- Plattform startet es nur, wenn die Abhängigkeiten im Bundle selbst oder in anderen bereits auf der Plattform installierten Bundles verfügbar sind.

3. Die Tools erhalten

Wir beginnen unsere Reise in OSGi, indem wir die neueste Version von Apache Karaf von diesem Link herunterladen . Apache Karaf ist eine Plattform, auf der OSGi- basierte Anwendungen ausgeführt werden. es basiert auf der Apache ‚s Implementierung von OSGi - Spezifikation namens Apache Felix .

Karaf bietet neben Felix einige nützliche Funktionen , die uns helfen, OSGi kennenzulernen , beispielsweise eine Befehlszeilenschnittstelle, mit der wir mit der Plattform interagieren können.

Um Karaf zu installieren , können Sie den Installationsanweisungen aus der offiziellen Dokumentation folgen.

4. Einstiegspunkt bündeln

Um eine Anwendung in einer OSGi-Umgebung auszuführen, müssen wir sie als OSGi- Bundle packen und den Anwendungseinstiegspunkt definieren. Dies ist nicht die übliche öffentliche statische void main- Methode (String [] args) .

Beginnen wir also mit der Erstellung einer OSGi- basierten „Hello World“ -Anwendung.

Wir beginnen mit dem Einrichten einer einfachen Abhängigkeit von der OSGi- Kern- API :

 org.osgi org.osgi.core 6.0.0 provided 

Die Abhängigkeit wird als bereitgestellt deklariert , da sie in der OSGi- Laufzeit verfügbar ist und vom Bundle nicht eingebettet werden muss.

Schreiben wir nun die einfache HelloWorld- Klasse:

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 ist eine von OSGi bereitgestellte Schnittstelle , die von Klassen implementiert werden muss, die Einstiegspunkte für ein Bundle sind.

Die start () -Methode wird von der OSGi- Plattform aufgerufen , wenn das Bundle mit dieser Klasse gestartet wird. Auf der anderen Seite wird stop () aufgerufen, bevor das Bundle gestoppt wird.

Beachten Sie, dass jedes Bundle höchstens einen BundleActivator enthalten kann . Das BundleContext- Objekt, das beiden Methoden zur Verfügung gestellt wird, ermöglicht die Interaktion mit der OSGi- Laufzeit. Wir werden bald darauf zurückkommen.

5. Ein Bundle erstellen

Ändern wir die Datei pom.xml und machen sie zu einem tatsächlichen OSGi-Bundle.

Zunächst müssen wir ausdrücklich angeben, dass wir ein Bundle bauen werden, kein Glas:

bundle

Dann nutzen wir das Maven-Bundle-Plugin mit freundlicher Genehmigung der Apache Felix- Community, um die HelloWorld- Klasse als OSGi- Bundle zu verpacken :

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

Im Abschnitt mit den Anweisungen geben wir die Werte der OSGi- Header an, die in die MANIFEST-Datei des Bundles aufgenommen werden sollen.

Bundle-Activator ist der vollständig qualifizierte Name der BundleActivator- Implementierung, die zum Starten und Stoppen des Bundles verwendet wird. Er bezieht sich auf die Klasse, die wir gerade geschrieben haben.

Private-Package ist kein OSGi-Header, wird jedoch verwendet, um das Plugin anzuweisen, das Paket in das Bundle aufzunehmen, es aber nicht für andere verfügbar zu machen. Wir können das Bundle jetzt mit dem üblichen Befehl mvn clean install erstellen .

6. Installieren und Ausführen des Bundles

Beginnen wir Karaf mit der Ausführung des Befehls:

/bin/karaf start

wo ist der Ordner, in dem Karaf installiert ist. Wenn die Eingabeaufforderung der Karaf- Konsole angezeigt wird, können Sie den folgenden Befehl ausführen, um das Bundle zu installieren:

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

Dies weist Karaf an, das Bundle aus dem lokalen Maven-Repository zu laden.

In return Karaf prints out the numeric ID assigned to the bundle that depends on the number of bundles already installed and may vary. The bundle is now just installed, we can now start it with the following command:

> bundle:start 63 Hello World

“Hello World” immediately appears as soon the bundle is started. We can now stop and uninstall the bundle with:

> bundle:stop 63 > bundle:uninstall 63

“Goodbye World” appears on the console, accordingly to the code in the stop() method.

7. An OSGi Service

Let's go on writing a simple OSGi service, an interface that exposes a method for greeting people:

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

Let's write an implementation of it that is a BundleActivator too, so we'll be able to instantiate the service and register it on the platform when the bundle is started:

package com.baeldung.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(); } }

We use the BundleContext as a mean of requesting the OSGi platform to register a new instance of the service.

We should also provide the type of the service and a map of the possible configuration parameters, which aren't needed in our simple scenario. Let's now proceed with the configuration of the maven-bundle-plugin:

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

It's worth noting that only the com.baeldung.osgi.sample.service.definition package has been exported this time, through the Export-Package header.

Thanks to this, OSGi will allow other bundles to invoke only the methods specified in the service interface. Package com.baeldung.osgi.sample.service.implementation is marked as private, so no other bundle will be able to access the members of the implementation directly.

8. An OSGi Client

Let's now write the client. It simply looks up the service at startup and invokes it:

public class Client implements BundleActivator, ServiceListener { }

Let's implement the BundleActivator start() method:

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(); } }

The addServiceListener() method allows the client to ask the platform to send notifications about the service that complies with the provided expression.

The expression uses a syntax similar to the LDAP's one, and in our case, we're requesting notifications about a Greeter service.

Let's go on to the callback method:

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; } }

When some modification involving the Greeter service happens, the method is notified.

When the service is registered to the platform, we get a reference to it, we store it locally, and we then use it to acquire the service object and invoke it.

When the server is later unregistered, we use the previously stored reference to unget it, meaning that we tell the platform that we are not going to use it anymore.

We now just need to write the stop() method:

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

Here again, we unget the service to cover the case in which the client is stopped before the service is being stopped. Let's give a final look at the dependencies in the pom.xml:

 com.baeldung osgi-intro-sample-service 1.0-SNAPSHOT provided   org.osgi org.osgi.core 6.0.0 

9. Client and Service

Let's now install the client and service bundles in Karaf by doing:

> 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

Always keep in mind that the identifier numbers assigned to each bundle may vary.

Let's now start the client bundle:

> start 65

Therefore, nothing happens because the client is active and it's waiting for the service, that we can start with:

> start 64 Registering service. Service registered. Hello John

What happens is that as soon as the service's BundleActivator starts, the service is registered to the platform. That, in turn, notifies the client that the service it was waiting for is available.

Der Client erhält dann einen Verweis auf den Dienst und ruft damit die über das Dienstpaket bereitgestellte Implementierung auf.

10. Schlussfolgerung

In diesem Artikel haben wir die wesentlichen Funktionen von OSGi anhand eines einfachen Beispiels untersucht, das ausreicht, um das Potenzial von OSGi zu verstehen.

Zusammenfassend kann OSGi eine praktikable Lösung sein, wenn wir sicherstellen müssen, dass eine einzelne Anwendung ohne einen schlechten Service aktualisiert werden muss.

Den Code für diesen Beitrag finden Sie auf GitHub.