OSGi Anwendungen vs. Ansammlung von Bundles

4 Kommentare

Wenn ich von einer OSGi-Anwendung spreche, dann meine ich in der Regel eine Gruppe von Bundles, die gemeinsam eine Anwendung ergeben. Eine solche Aufteilung bietet sich förmlich an, wenn man OSGi verwendet. Die Kommunikation zwischen den einzelnen Teilen der Anwendung findet dann über die OSGi-Registry statt. Einer der großen Vorteile, den man dadurch gewinnt, ist die Möglichkeit Teile der eigenen Anwendung zur Laufzeit austauschen zu können, ohne dass der Rest der Anwendung davon betroffen wird.

Leider bietet OSGi keine Möglichkeit Bundles, die zu zwei unterschiedlichen Anwendungen gehören voneinander zu unterscheiden. Das Fehlen einer solchen Funktion lässt sich vielleicht historisch erklären. OSGi war ursprünglich für den Einsatz auf Kleingeräten gedacht, wo die Anzahl der Bundles klein ausfällt. Heute wird OSGi in Projekten wie zum Beispiel der Eclipse IDE eingesetzt, wo die Anzahl an Bundles dreistellig oder mehr werden kann. Wenn ich meine aktuelle Eclipse-Installation starte (./eclipse -console) und mir mit dem Befehl „ss“ die Anzahl der installierten Bundles anzeigen lasse, komme ich auf über 600 Bundles. Und dabei habe ich kaum zusätzliche Plugins installiert. Die eine oder andere Installation der Eclipse IDE wird durchaus auf 1000+ kommen. Würde ich jetzt ein bestimmtes Plugin über die Konsole deinstallieren wollen, hätte ich alle Hände voll zu tun, die Bundles, die zu diesem Plugin gehören zu finden und zu deinstallieren. Bei Eclipse nimmt man eher den Update-Manager und spart sich die Mühe. In einem anderen Projekt, wo ich einen OSGi-Container (Equinox in diesem Fall) einsetze, habe ich leider keinen Update-Manager – nur die Konsole.

Zurzeit befinden sich neben meiner Anwendung (bestehend aus 6 Bundles) ein Servlet-Container, das Spring Framework und eine Reihe von Bibliotheken im OSGi-Container. Wenn ich an meiner Anwendung arbeite, interessieren mich die restlichen Bundles eigentlich nicht. Manche davon werden nach der Installation gar nicht mehr verändert. Trotzdem kommt man nicht umhin, die Liste mit allen installierten Bundles durchzugehen, wenn man Bundles aus der eigenen Anwendung sucht. Es wäre deutlich eleganter, wenn man mit einem Befehl alle Bundles anzeigen lassen könnte, die zu einer bestimmten Anwendung gehören und direkt deren Status ändern könnte. Wie die Blog-Einträge [1][2] zeigen, bin ich nicht der einzige, der eine solche Funktion vermisst. Also habe ich mich nach einer bestehenden Lösung umgeschaut.

Dabei bin ich auf das Platform Archive von SpringSource gestoßen. Mit dem SpringSource dm Server (früher SpringSource Application Platform) hat SpringSource das Platform Archive (PAR) eingeführt. Das PAR ist eine JAR-Datei, die mehrere Bundles beinhalten kann. Auf diese Weise kann man alle Bundles, die zu einer Anwendung gehören gleichzeitig installieren, deinstallieren, etc. Das PAR bietet somit genau die gewünschten Funktionen mit einer Funktionsweise für OSGI, die man sonst von WAR und EAR Archiven gewohnt ist. Leider lassen sich PAR Dateien zurzeit nur in einem SpringSource dm Server installieren. Als Nachteil kann man zudem anführen, dass man das PAR Archiv neu bauen muss, wenn ein Bundle ausgetauscht werden soll, was das Austauschen einzelner OSGI Bundle komplexer macht.

Um mehr Flexibilität zu haben, wollte ich das Equinox Framework um die gewünschte Funktion selbst erweitern. Was ich erreichen wollte, ist die Möglichkeit Gruppen von Bundles zu definieren, ohne dass ich dabei die OSGi Spezifikation verletze. Schließlich sollen die Bundles auch in jedem anderen OSGi-Container installiert werden können. Außerdem sollen die Bundles weiterhin getrennt voneinander vorliegen.

Nach einem Blick in den Sourcecode von Equinox habe ich festgestellt, dass ich an der OSGi Implementierung nicht einmal etwas ändern musste. Es reicht aus das CommandProvider Interface aus dem org.eclipse.osgi.framework.console Package zu implementieren und ein Bundle zu bauen, was eine Instanz meiner Implementierung in die OSGi-Registry einträgt. Auf diese Weise kann man die Equinox Konsole um eigene Befehle erweitern.

Die entstandene Klasse sieht wie folg aus:

public class GroupCommandProvider implements CommandProvider {
   private BundleContext bctx;
 
   public GroupCommandProvider(BundleContext bctx) {
      this.bctx = bctx;
   }
 
   public String getHelp() {
      return "\tstopgroup <group> - stops all bundles in the specified group.";
   }
 
   public void _stopgroup(CommandInterpreter ci) throws BundleException {
      String groupToStop = ci.nextArgument();
      if (groupToStop == null)
         return;
 
      Bundle[] bundles = bctx.getBundles();
      for (Bundle bundle : bundles) {
         String currentGroup = (String) bundle.getHeaders().get("Bundle-Group");
         if (currentGroup == null || !currentGroup.equals(groupToStop))
            continue;
 
         if (bundle.getState() == Bundle.ACTIVE) {
            bundle.stop();
         }
      }
   }
}

Die interessante Methode ist _stopgroup(). Wenn sie aufgerufen wird, werden alle Bundles gestoppt, die einen bestimmten Wert für „Bundle-Group“ in der Manifest-Datei stehen haben.

Eine Manifest-Datei könnte also wie folgt aussehen:

Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-SymbolicName: de.codecentric.mybundle
Bundle-Group: myGroup
Bundle-Version: 0.1.0
Import-Package: org.osgi.framework

Alle Bundles aus der gleichen Gruppe können dann über die Equinox Konsole mit “stopgroup myGroup” auf einen Schlag gestoppt werden.

Damit der GroupCommandProvider aktiv wird, braucht man nur noch einen Activator. Dieser fällt sehr einfach aus:

public class Activator implements BundleActivator {
   private CommandProvider commandProvider;
 
   public void start(BundleContext bctx) throws Exception {
      commandProvider = new GroupCommandProvider(bctx);
      bctx.registerService(CommandProvider.class.getName(), commandProvider, null);
   }
 
   public void stop(BundleContext arg0) throws Exception {
      commandProvider = null;
   }
}

Damit der GroupCommandProvider sinnvoll eingesetzt werden kann, braucht er ein paar weitere Methoden, um zum Beispiel Gruppen von Bundles starten, deinstallieren oder einfach anzeigen zu können. Bei der Methode für das Starten einer ganzen Gruppen müsste man sich noch überlegen, wie man Bundles kennzeichnet, welche gestartet werden sollen und welche nicht. Was mir da vorschwebt ist eine Erweiterung des „Bundle-Group“ Eintrags in der Manifest-Datei, zum Beispiel „Bundle-Group: myGroup;startable=true“. Aber ich denke, dass die _stopgroup() Methode die Idee deutlich macht.

Es bleibt abzuwarten, ob sich bei der OSGi Spezifikation in der nächsten Version etwas in diese Richtung tut. Für mich persönlich reicht die beschriebene Lösung aus.

Yevgeniy Melnichuk

Yevgeniy unterstützt seit 2009 im Namen von codecentric AG die Kunden dabei ihre Ideen in Software zu gießen. Dabei ist die Software bevorzugt verteilt, gut getestet und natürlich Open Source.

Share on FacebookGoogle+Share on LinkedInTweet about this on TwitterShare on RedditDigg thisShare on StumbleUpon

Kommentare

  • Eberhard Wolff

    Der Begriff der Anwendung fehlt sicher in OSGi. Allerdings reicht es nicht, nur Bundles zusammen zu starten. Man muss auch ein Format haben, um eine Anwendung als Einheit zu deployen. Und man muss verhindern, dass Typen oder Services in andere Applicationen „hineinwuchern“. Wenn man beispielsweise zwei Anwendungen hat, die beide dieselbe Klasse enthalten, kann man mit OSGi die beiden Klassen nicht außeinander halten. Wenn also ein Bundle einer Anwendung eine solche Klasse importiert, ist unklar, ob es die richtige bekommt oder die der anderen Anwendung.

    Noch schlimmer bei Services: Eine DataSource als OSGi-Service kann ohne weiteres auch von einer anderen Anwendung „aus versehen“ verwendet werden. All diese Probleme lösen die PARs des dm Server – und sie ermöglichen auch eine sinnvolle Nutzung von Hibernate oder JPA in OSGi-Umgebungen.

    Übrigens ist das hier vorgestellte Feature Bundle-Group eine proprietäre Erweiterung von Equinox und kein OSGi-Standard – genauso wie PARs eine Erweiterung des dm Servers sind.

  • Yevgeniy Melnichuk

    5. Dezember 2008 von Yevgeniy Melnichuk

    Hallo Eberhard,

    Danke für deinen Kommentar.
    Das ist richtig, diese Lösung ist Equinox-spezifisch. Allerdings lassen sich Bundles mit dem „Bundle-Group“ Manifest-Eintrag auch in anderen OSGi Containern installieren und starten. Der Manifest-Eintrag wird dann nicht interpretiert.

    Mit freundlichen Grüssen,
    Yevgeniy Melnichuk

  • Juri Lazur

    14. Dezember 2008 von Juri Lazur

    helo Yevgeniy!

    How can I make your GroupCommandProvider run on Apache Felix?

    Juri.

  • Yevgeniy Melnichuk

    15. Dezember 2008 von Yevgeniy Melnichuk

    Hello Juri,

    GroupCommandProvider implements the CommandProvider interface, which is a part of the Equinox Framework. It won’t work for the Apache Felix Framework.

    You can build a similar solution for the Apache Felix Framework using the ShellService interface. The following article might be helpful http://felix.apache.org/site/apache-felix-shell-service.html

    Greetings,
    Yevgeniy Melnichuk

Kommentieren

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.