Was ist OSGi? Ein anderer Ansatz zur Java-Modularität

OSGi erleichtert das Erstellen und Verwalten modularer Java-Komponenten (sogenannte Bundles ), die in einem Container bereitgestellt werden können. Als Entwickler verwenden Sie die OSGi-Spezifikation und -Tools, um ein oder mehrere Bundles zu erstellen. OSGi definiert den Lebenszyklus für diese Bundles. Es hostet sie auch und unterstützt ihre Interaktionen in einem Container. Sie können sich einen OSGi-Container als ungefähr analog zu einer JVM mit zusätzlichen Fähigkeiten vorstellen. Stellen Sie sich Bundles ebenfalls als Java-Anwendungen mit einzigartigen Fähigkeiten vor. Bundles werden im OSGi-Container als Client- und Serverkomponenten ausgeführt.

Die OSGi-Allianz

OSGi wurde 1999 gestartet und im Gegensatz zu vielen anderen Spezifikationen wird der Standard nicht von Oracle, dem Java Community Process oder der Eclipse Foundation verwaltet. Stattdessen wird es von der OSGi-Allianz verwaltet.

Wie OSGi anders ist

Die Philosophie von OSGi unterscheidet sich von der anderer Java-basierter Frameworks, insbesondere von Spring. In OSGi können mehrere Anwendungen in demselben Container vorhanden sein: die OSGi-Bundle-Laufzeitumgebung . Der Container stellt sicher, dass jede Komponente ausreichend isoliert ist und Zugriff auf alle erforderlichen Abhängigkeiten hat. OSGi kann die Abhängigkeitsinjektion unterstützen, die vom Aries Blueprint-Projekt standardisiert wird. Aries bietet nicht nur den IoC-Container (Inversion of Control) von OSGi, sondern unterstützt auch Standard-Java-Frameworks wie die Java Persistence API (JPA).

In OSGi können Bundles Dienste verfügbar machen, die andere Bundles verwenden. Ein Bundle kann auch eine Version deklarieren und definieren, von welchen anderen Bundles es abhängt. Die Laufzeit lädt dann automatisch alle ihre Bundles in der Reihenfolge ihrer Abhängigkeit. In OSGi können mehrere Versionen desselben Bundles nebeneinander existieren, wenn dies aufgrund von Bundle-Abhängigkeiten erforderlich ist.

OSGi in Eclipse IDE und Equinox

OSGi gibt es seit einigen Jahrzehnten in irgendeiner Form. Es wird für viele bekannte Anwendungen verwendet, von eingebetteten Mobilgeräten bis hin zu Anwendungsservern und IDEs.

Die beliebte Eclipse-IDE basiert auf OSGi. Die Implementierung des OSGi-Containers durch Eclipse heißt Equinox. Es ist ein großartiges Beispiel für das Verständnis von OSGi. Equinox basiert auf OSGi und ist eine modulare Plattform. Es enthält eine Vielzahl von Diensten, die Entwickler nach Belieben hinzufügen können. Jedes dieser Funktionen bietet eine Funktion, die ein Entwickler möglicherweise in seiner IDE benötigt. Sie können Editoren für Java und JavaScript, einen App-Server und einen Datenbank-Connector hinzufügen. Jedes dieser Pakete wird als OSGi-Bundle implementiert, das dem Container hinzugefügt wird und mit anderen Diensten im Container interagieren kann.

In letzter Zeit hat das Interesse an der Verwendung von OSGi für das Internet der Dinge (IoT) zugenommen. OSGi ist eine natürliche Ergänzung für diese Art der Entwicklung, bei der eine Vielzahl von Softwarekomponenten nebeneinander auf Geräten ausgeführt werden, ohne dass sie sich gegenseitig kennen müssen. Ein OSGi-Container bietet eine einfache und standardisierte Möglichkeit, diese dynamischen Softwarekomponenten zu hosten.

Verwenden von OSGi in einem Java-Projekt: Knoplerfish OSGi

Wir werden eine Beispielanwendung durcharbeiten, die OSGi-Konzepte konkreter macht. Unser Beispiel basiert auf der Knoplerfish OSGi-Laufzeit, die in vielen Produktionsbereitstellungen verwendet wird. Knoplerfish enthält eine GUI und eine Befehlszeilenschnittstelle (Command Line Interface, CLI) zum Verwalten des OSGi-Containers und seiner Bundles.

Als erstes laden Sie Knoplerfish herunter. Die aktuelle Version zum Zeitpunkt dieses Schreibens ist Knoplerfish OSGi 6.1.3. Sie können diese Version durch die aktuellste Version ersetzen, wenn Sie diesen Artikel lesen.

Nachdem Sie heruntergeladen haben und Knoplerfish installiert ist , verwenden Sie die CLI in das Verzeichnis fallen , wo Sie die JAR - Datei heruntergeladen haben , und geben Sie ein: java -jar framework.jar. Dadurch wird die ausführbare JAR ausgeführt und Sie sollten mit einem GUI-Fenster begrüßt werden.

Die Knoplerfish OSGi GUI

Die Benutzeroberfläche von Knoplerfish OSGi kann zunächst überwältigend erscheinen, aber die Grundlagen sind einfach:

  • Am oberen Bildschirmrand befindet sich das Menü.
  • Links sehen Sie die Bundles, die in die Laufzeit geladen wurden.
  • Rechts ist ein Informationsfenster.
  • Unten befindet sich eine Textausgabekonsole.
  • Ganz unten befindet sich eine Eingabekonsole.
Matthew Tyson

Geben Sie helpin die Eingabekonsole ein, wenn Sie die Hilfeoptionen anzeigen möchten.

Bevor wir uns dem Beispiel zuwenden, werfen Sie einen Blick auf die laufenden Bundles. Es wird ein Bundle mit dem Namen angezeigt. HTTP ServerDies bedeutet, dass ein Bundle mit einem HTTP-Server aktiv ist. Gehen Sie zu Ihrem Browser und überprüfen Sie // localhost: 8080. Sicher genug, Sie werden eine Knoplerfish-Webseite sehen.

Das 'Hello JavaWorld'-Bundle

Verwenden wir die OSGi-Laufzeit, um ein einfaches Bundle zu erstellen, das ich aufrufen werde Hello JavaWorld. Dieses Bundle gibt eine Nachricht an die Konsole aus.

In Listing 1 verwenden wir Maven, um das Bundle zu erstellen. Es gibt nur eine Abhängigkeit, die von der OSGi-Allianz bereitgestellt wird.

Listing 1. OSGi-Abhängigkeit im Maven POM

   org.osgi org.osgi.core   

Jetzt werden wir auch ein Plug-In verwenden, das vom Apache Felix-Projekt zur Verfügung gestellt wird. Dieses Plug-In übernimmt das Packen der App als OSGi-Bundle zur Verwendung. Listing 2 zeigt die Konfiguration, die wir verwenden werden.

Listing 2. OSGi Felix-Plug-In im Maven POM

   org.apache.felix maven-bundle-plugin true   org.javaworld.osgi org.javaworld.osgi.Hello     

Jetzt können wir uns die einfache Klasse ansehen, die ein "Hallo" ausgibt.

Listing 3. Hallo JavaWorld OSGi-Bundle

 package com.javaworld.osgi; import org.osgi.framework.BundleActivator; import org.osgi.framework.BundleContext; public class HelloJavaWorld implements BundleActivator { public void start(BundleContext ctx) { System.out.println("Hello JavaWorld."); } public void stop(BundleContext bundleContext) { } } 

Erstellen Sie das Bundle, indem Sie zur Befehlszeile gehen und eingeben mvn clean install. Dadurch wird eine JAR-Datei ausgegeben, die das Bundle enthält. Gehen Sie nun zum FileMenü in der Knoplerfish-GUI und wählen Sie Add Bundle. Dadurch wird ein Dateibrowser bereitgestellt. Suchen Sie die JAR, die wir gerade erstellt haben, und wählen Sie sie aus.

Verwalten von OSGi-Bundles im Container

In the output window of the Knoplerfish UI, you’ll see your “Hello, JavaWorld” message appear. Click on the bundle in the Knoplerfish GUI, and you can see the ID the container has assigned to it. When you are ready to stop the bundle, you could click the Stop menu item. Another way is to enter stop [bundle number] on the command line. You can manage bundles in the container using either the GUI or the command line.

Now you have a sense of how a simple bundle works in the OSGi container. Anywhere an OSGi container exists, you will find the same simplicity in starting and stopping bundles. OSGi creates an environment and lifecycle for the bundle.

Bundle Interactions: Services and clients

Next, we’ll look at how bundles communicate with each other.

The first thing we’ll do is create a service bundle. A service bundle is analogous to an EJB session bean: It provides a component that can be accessed by other bundles via a remote interface. To create a service bundle, we need to provide both an interface and an implementation class.

Listing 4. The service bundle interface

 package com.javaworld.osgi.service; public interface WhatIsOsgi { public Integer addNum(Integer x, Integer y); } 

Listing 4 is a simple interface. The only method is a addNum() method that will do what it implies: return the addition of two numbers. The implementation shown in Listing 5 is equally simple but adds a couple of OSGi-specific methods.

Listing 5. The service bundle implementation

 package com.javaworld.osgi.service; public class WhatIsOsgiImpl implements WhatIsOsgi, BundleActivator { private ServiceReference ref; private ServiceRegistration reg; @Override public Integer addNum(Integer x, Integer y){ return x + y; } @Override public void start(BundleContext context) throws Exception { reg = context.registerService( WhatIsOsgi.class, new WhatIsOsgiImpl(), new Hashtable()); ref = reg.getReference(); } @Override public void stop(BundleContext context) throws Exception { reg.unregister(); } } 

Let’s look closer at what’s happening in Listing 5:

  1. public class WhatIsOsgiImpl implements WhatIsOsgi, BundleActivator: Here we are implementing the interface we created. Note that we also implement the BundleActivator interface, as we did with the HelloJavaWorld example. The latter is because this bundle will activate itself.
  2. private ServiceReference ref; private ServiceRegistration reg;: These are variables for the OSGi registration service and the bundle reference for this service, respectively.
  3. public Integer addNum(Integer x, Integer y): This is the simple implementation of the add method.
  4. public void start(BundleContext context): This start method is part of the BundleActivator interface, and is executed by the container. In this example, we obtain a reference to the OSGi registration service and apply it to our WhatIsOsgi interface and implementation. The empty Hashtable is for config params, which we aren’t using here. We also get a reference to the service we have just created.
  5. public void stop(BundleContext context): Here, we simply unregister the service. This simple service just manages the barest elements of its lifecycle. Its main purpose is to expose the addNum method to the OSGi container.

The OSGi client

Next up, let’s write a client that can use the service. This client will again make use of the BundleActivator interface. It will also add the ServiceListener interface, as shown in Listing 6.

Listing 6. The OSGi service client bundle

 public class OsgiClient implements BundleActivator, ServiceListener { private BundleContext ctx; private ServiceReference service; public void start(BundleContext ctx) { this.ctx = ctx; try { ctx.addServiceListener(this, "(objectclass=" + WhatIsOsgi.class.getName() + ")"); } catch (InvalidSyntaxException ise) { ise.printStackTrace(); } } } 

Listing 6 has a start method that will add a service listener. This listener is filtered by the class name of the service we created in Listing 5. When the service is updated, it will call the serviceChanged() method, as shown in Listing 7.

Listing 7. serviceChanged method

 public void serviceChanged(ServiceEvent event) { int type = event.getType(); switch (type){ case(ServiceEvent.REGISTERED): serviceReference = event.getServiceReference(); Greeter service = (Greeter)(ctx.getService(service)); System.out.println("Adding 10 and 100: " + service.addNum(10, 100) ); break; case(ServiceEvent.UNREGISTERING): System.out.println("Service unregistered."); ctx.ungetService(event.getServiceReference()); // Releases reference to service so it can be GC'd break; default: break; } } 

Note that the serviceChanged method is used to determine what event has occurred for a service we are interested in. The service will then respond as specified. In this case, when the REGISTERED event appears, we make use of the addNum() method.

The OSGi alternative

This has been a quick introduction to OSGi, the Open Services Gateway Initiative. As you’ve seen through the Knoplerfish example, OSGi provides a runtime environment where you can define modular Java components (bundles). It provides a defined lifecycle for hosting bundles in the client, and it supports bundles interacting as clients and services within the container. All of these capabilities taken together provide an interesting alternative to standard Java runtimes and frameworks, especially for mobile and IoT applications.

Beachten Sie schließlich, dass im vorherigen Artikel in der Reihe „Was ist: Java?“ Das Java Platform Module System vorgestellt wurde, das einen anderen Ansatz für dieselbe Herausforderung der Java-Modularität bietet.

Diese Geschichte "Was ist OSGi? Ein anderer Ansatz zur Java-Modularität" wurde ursprünglich von JavaWorld veröffentlicht.