Einführung in OSGi

Einführung in OSGi

1. Einführung

Einige Java-Anwendungen für geschäftskritische Anwendungen und Middleware-Anwendungen stellen hohe technologische Anforderungen.

Einige müssen Hot Deploy unterstützen, um die ausgeführten Dienste nicht zu stören. Andere müssen in der Lage sein, mit verschiedenen Versionen desselben Pakets zu arbeiten, um externe Legacy-Systeme zu unterstützen.

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

The Open Service Gateway Initiative is a specification defining a Java-based component system. Es wird derzeit vonOSGi 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. DasEclipse IDE ist beispielsweise eine aufOSGi basierende Anwendung.

In diesem Artikel werden einige grundlegende Funktionen vonOSGi erläutert, die die vonApache bereitgestellte Implementierung nutzen.

2. OSGi-Grundlagen

In OSGi wird eine einzelne Komponente als Bundle bezeichnet.

Logischerweisea bundle is a piece of functionality that has an independent lifecycle –, was bedeutet, dass es unabhängig gestartet, gestoppt und entfernt werden kann.

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

Die PlattformOSGibietet 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, auch wenn ein Dienst, von dem er abhängt, momentan nicht verfügbar ist.

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

3. Tools erhalten

Wir beginnen unsere Reise inOSGi, indem wir die neueste Version vonApache Karaf vonthis link herunterladen. Apache Karaf ist eine Plattform, auf derOSGi--basierte Anwendungen ausgeführt werden. Es basiert auf der Implementierung derOSGi-SpezifikationApache FelixdurchApache.

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

UmKaraf zu installieren, können Sie den Installationsanweisungen vonofficial documentation folgen.

4. Bundle-Einstiegspunkt

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

Beginnen wir also mit der Erstellung einer aufOSGi-basierenden "Hello World" -Anwendung.

Wir beginnen mit dem Aufbau einer einfachen Abhängigkeit vom KernOSGi API:


    org.osgi
    org.osgi.core
    6.0.0
    provided

Die Abhängigkeit wird alsprovided deklariert, da sie in der Laufzeit vonOSGiverfügbar ist und das Bundle sie nicht einbetten muss.

Schreiben wir nun die einfacheHelloWorld-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 vonOSGi bereitgestellte Schnittstelle, die von Klassen implementiert werden muss, die Einstiegspunkte für ein Bundle sind.

Diestart()-Methode wird von derOSGi-Plattform aufgerufen, wenn das Bundle mit dieser Klasse gestartet wird. Andererseits wirdstop() aufgerufen, bevor das Bündel gestoppt wird.

Beachten Sie, dass jedes Bundle höchstens einBundleActivator enthalten kann. Das für beide Methoden bereitgestellteBundleContext-Objekt ermöglicht die Interaktion mit der Laufzeit vonOSGi. Wir werden bald darauf zurückkommen.

5. Ein Bundle bauen

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

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

bundle

Dann nutzen wir diemaven-bundle-plugin,mit freundlicher Genehmigung derApache Felix-Community, um dieHelloWorld-Klasse alsOSGi-Bündel zu verpacken:


    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
            
        
    

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

Bundle-Activator ist der vollständig qualifizierte Name der Implementierung vonBundleActivator, 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 Befehlmvn clean install erstellen.

6. Bundle installieren und ausführen

Beginnen wir mitKaraf, indem wir den folgenden Befehl ausführen:

/bin/karaf start

Dabei ist<KARAF_HOME> der Ordner, in demKaraf installiert ist. Wenn die Eingabeaufforderung derKaraf-Konsole angezeigt wird, können Sie den folgenden Befehl ausführen, um das Bundle zu installieren:

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

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

Im Gegenzug druckt Karaf die dem Bundle zugewiesene numerische ID aus, die von der Anzahl der bereits installierten Bundles abhängt und variieren kann. Das Bundle ist jetzt gerade installiert, wir können es jetzt mit dem folgenden Befehl starten:

> bundle:start 63
Hello World

Sobald das Bundle gestartet ist, erscheint sofort „Hello World“. Wir können das Bundle jetzt stoppen und deinstallieren mit:

> bundle:stop 63
> bundle:uninstall 63

„Goodbye World“ wird auf der Konsole entsprechend dem Code in derstop()-Methode angezeigt.

7. Ein OSGi-Dienst

Schreiben wir weiter einen einfachenOSGi-Dienst, eine Schnittstelle, die eine Methode zur Begrüßung von Personen bereitstellt:

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

Schreiben wir eine Implementierung, die ebenfallsBundleActivatorbeträgt, damit wir den Dienst instanziieren und auf der Plattform registrieren können, wenn das Bundle gestartet wird:

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

Wir verwendenBundleContext als Mittel, um die PlattformOSGianzufordern, eine neue Instanz des Dienstes zu registrieren.

Wir sollten auch den Typ des Dienstes und eine Karte der möglichen Konfigurationsparameter bereitstellen, die in unserem einfachen Szenario nicht benötigt werden. Fahren wir nun mit der Konfiguration dermaven-bundle-plugin fort:


    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
            
        
    

Es ist erwähnenswert, dass diesmal nur das Paketcom.example.osgi.sample.service.definitionüber den HeaderExport-Packageexportiert wurde.

Dank dessen ermöglichtOSGi anderen Bundles, nur die in der Serviceschnittstelle angegebenen Methoden aufzurufen. Das Paketcom.example.osgi.sample.service.implementation ist als privat markiert, sodass kein anderes Bundle direkt auf die Mitglieder der Implementierung zugreifen kann.

8. Ein OSGi-Client

Schreiben wir jetzt den Client. Der Dienst wird beim Start einfach nachgeschlagen und aufgerufen:

public class Client implements BundleActivator, ServiceListener {
}

Implementieren wir dieBundleActivator start()-Methode:

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

Mit der MethodeaddServiceListener()kann der Client die Plattform auffordern, Benachrichtigungen über den Dienst zu senden, der dem angegebenen Ausdruck entspricht.

Der Ausdruck verwendet eine ähnliche Syntax wie der LDAP. In unserem Fall fordern wir Benachrichtigungen über den Dienst vonGreeteran.

Fahren wir mit der Rückrufmethode fort:

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

Wenn Änderungen am DienstGreetervorgenommen werden, wird die Methode benachrichtigt.

Wenn der Service auf der Plattform registriert ist, erhalten wir einen Verweis darauf, speichern ihn lokal und verwenden ihn dann, um das Serviceobjekt zu erfassen und aufzurufen.

Wenn der Server später abgemeldet wird, verwenden wir die zuvor gespeicherte Referenz, um ihn zu deaktivieren. Dies bedeutet, dass wir der Plattform mitteilen, dass wir ihn nicht mehr verwenden werden.

Wir müssen jetzt nur noch die Methodestop()chreiben:

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

Auch hier wird der Dienst deaktiviert, um den Fall abzudecken, in dem der Client vor dem Beenden des Dienstes gestoppt wird. Lassen Sie uns einen letzten Blick auf die Abhängigkeiten inpom.xml werfen:


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


    org.osgi
    org.osgi.core
    6.0.0

9. Kunde und Service

Installieren Sie nun die Client- und Service-Bundles in Karaf wie folgt:

> 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

Denken Sie immer daran, dass die jedem Bündel zugewiesenen Identifizierungsnummern variieren können.

Starten wir nun das Client-Bundle:

> start 65

Daher passiert nichts, weil der Client aktiv ist und auf den Dienst wartet, mit dem wir beginnen können:

> start 64
Registering service.
Service registered.
Hello John

Sobald der BundleActivator des Dienstes gestartet wird, wird der Dienst auf der Plattform registriert. Dies wiederum benachrichtigt den Client darüber, dass der Dienst verfügbar ist, auf den er gewartet hat.

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

10. Fazit

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 tragfähige Lösung sein, wenn wir garantieren müssen, dass eine einzelne Anwendung ohne Probleme aktualisiert werden muss.

Der Code für diesen Beitrag kannover on GitHub gefunden werden.