OSGi-Konzepte verstehen. Versuchen Sie, dem Puzzle-Ansatz zu folgen
Veröffentlicht: 2013-04-20OSGi
wurde heute dank seines modularen Ansatzes und seiner Fähigkeit, logische Grenzen zwischen Modulen durchzusetzen, sehr beliebt. Wenn wir es zum ersten Mal entdecken, stellt sich die Frage, wo wir anfangen sollen zu verstehen, wie es funktioniert.
Um OSGi-Konzepte zu verstehen, werden wir versuchen, dem Puzzle-Ansatz zu folgen, die Idee ist, mit dem trivialen Teil dieser Technologie zu beginnen und nach anderen Teilen zu suchen, die mit den gefundenen verwandt sind. Und um das Puzzle zusammenzusetzen, werden wir von JArchitect unterstützt, das hilfreich sein wird, um das interne OSGi-Design zu erkennen.
Genauer gesagt analysieren wir mit JArchitect eine Anwendung, die OSGi-Technologie verwendet, es handelt sich um die berühmte Eclipse-IDE, die den OSGi-Container Äquinoktium verwendet.
Beginnen wir mit der typischen OSGi-Definition:
OSGi reduziert die Komplexität, indem es eine modulare Architektur für die heutigen großen verteilten Systeme sowie kleine eingebettete Anwendungen bereitstellt. Der Aufbau von Systemen aus eigenen und handelsüblichen Modulen reduziert die Komplexität und damit den Entwicklungs- und Wartungsaufwand erheblich. Das OSGi-Programmiermodell verwirklicht das Versprechen komponentenbasierter Systeme.
Der triviale Teil von OSGi ist die Modularität. Lassen Sie uns herausfinden, was die OSGi-Module sind.
OSGI-Module werden Bundles genannt und jede Anwendung besteht daher aus mindestens einem Bundle.
Diese Bundles werden in einem Container ausgeführt, und da bei jedem modularen Ansatz, bei dem ein Container die Module verwaltet, die Frage lautet, welcher Vertrag jedes Modul implementieren muss, das in den Container integriert werden soll.
Nehmen wir als Beispiel das Bundle org.eclipse.equinox.jsp.jasper und suchen nach allen implementierten Schnittstellen, dafür können wir die folgende CQLinq-Anfrage ausführen:
1 2 3 4 |
from t in Types where t . ParentProject . Name ==” org . eclipse . equinox . jsp . jasper_1 . 0.300.v20110502 “ let interfaces = t . InterfacesImplemented from i in interfaces select i |
Die BundleActivator-Schnittstelle aus dem OSGi-Paket ist implementiert, diese Schnittstelle enthält zwei Start- und Stoppmethoden, die nützlich sind, um das Starten und Stoppen des Bundles anzupassen.
Eine weitere Besonderheit eines Bundles ist seine Manifestdatei, hier ist ein Teil der Manifestdatei org.eclipse.equinox.jsp.jasper:
1 2 3 4 5 6 7 8 9 |
Manifest - Version : 1.0 Bundle - Localization : plugin Bundle - RequiredExecutionEnvironment : CDC - 1.0 / Foundation - 1.0 , J2SE - 1.3 Bundle - SymbolicName : org . eclipse . equinox . jsp . jasper Eclipse - LazyStart : true Eclipse - SourceReferences : scm : cvs : pserver : dev . eclipse . org : / cvsroot / rt : org . eclipse . equinox / server - side / bundles / org . eclipse . equinox . jsp . jaspe r ; tag = v20110502 Bundle - Activator : org . eclipse . equinox . internal . jsp . jasper . Activator |
Wie wir sehen können, enthält dieses Manifest einige Metainformationen, die vom Container benötigt werden, wie z. B. die Angabe der Bundle-Aktivatorklasse, die die BundleActivator-Schnittstelle implementiert.
Das Bündel stellt unser erstes Teil des Puzzles dar, und hier ist eine vereinfachte Darstellung eines Bündels:
Um eine Vorstellung von allen von Eclipse verwendeten Bundles zu bekommen, suchen wir nach allen Klassen, die die BundleActivator-Schnittstelle implementieren.
1 2 |
from t in Types where t . Implement ( “ org . osgi . framework . BundleActivator “ ) select new { t , t . NbBCInstructions } |
Wer verwaltet das Bundle und ruft BundleActivator-Methoden auf?
Um das herauszufinden, suchen wir nach Methoden, die BundleActivator.start direkt oder indirekt aufrufen
1 2 3 4 |
from m in Methods let depth0 = m . DepthOfIsUsing ( “ org . eclipse . osgi . framework . internal . core . BundleContextImpl . startActivator ( BundleActivator ) “ ) where depth0 > = 0 orderby depth0 select new { m , depth0 } |
Das Bundle wird beim Start vom OSGi-Framework aktiviert. Die Framework-Klasse wird vom Equinox-Container gestartet. Und um besser zu verstehen, was passiert, wenn der Container gestartet wird, sind hier einige Aktionen, die ausgeführt werden, wenn der Container gestartet wird:
Wenn der Container gestartet wird, initialisiert er das OSGi-Framework, das Framework ruft alle installierten Bundles ab und erstellt für jedes einzelne eine Instanz der BundleHost-Klasse und speichert das gefundene Bundle in einem Repository.
Die BundleHost-Klasse implementiert die Bundle-Schnittstelle, die Methoden wie Start, Stopp, Deinstallation und Aktualisierung enthält. Diese Methoden werden benötigt, um den Bundle-Lebenszyklus zu verwalten.
Unser zweites Puzzleteil ist also der OSGi-Container, er wird vom Equinoxlauncher gestartet, der das Framework initialisiert. Die Framework-Klasse ist für das Laden von Bundles verantwortlich und aktiviert sie.
Nachdem wir einige grundlegende Konzepte zu Bundles und Containern kennengelernt haben, gehen wir tief in die Bundles ein und entdecken, wie sie intern funktionieren.
Nehmen wir als Beispiel das Bündel org.eclipse.equinox.http.servlet und suchen nach Methoden, die von der Startmethode seiner Activator-Klasse aufgerufen werden.
Dieses Bundle erstellt einen Dienst und registriert ihn im Container. Ein Service in OSGi wird durch eine standardmäßige Java-Klasse oder -Schnittstelle definiert. Typischerweise wird eine Java-Schnittstelle verwendet, um die Dienstschnittstelle zu definieren. Der Dienst ist die bevorzugte Methode, die Bundles verwenden sollten, um miteinander zu kommunizieren.

Hier sind einige nützliche Szenarien für die Verwendung von Diensten:
- Exportieren Sie die Funktionalität von einem Bundle in andere Bundles.
- Importieren Sie Funktionen aus anderen Paketen.
- Registrieren Sie Listener für Ereignisse aus anderen Paketen.
Eine weitere Anmerkung aus dem vorherigen Abhängigkeitsdiagramm ist, dass eine Dienstfabrik verwendet wird, um die Dienstinstanz zu erstellen.
Unser drittes Teil des Puzzles ist die OSGi-Services-Schicht, jedes Bündel kann einige Dienste verwenden oder deklarieren, was den Ansatz des Komponentendesigns durchsetzt, hier ist die neue Darstellung des OSGi-Bündels.
Wenn das Bundle Dienste verwendet, um mit anderen Bundles zu kommunizieren, wie kommuniziert es dann mit anderen Jars?
Wenn wir ein Bundle entwickeln und versuchen, eine Klasse aus einem anderen JAR zu verwenden, können wir überrascht sein, dass es nicht wie erwartet funktioniert, der Grund dafür ist, dass der ClassLoader vom OSGi-Container eingehakt ist, um zu überprüfen, ob wir suchen, welche Methode Java aufruft. lang.Thread.setContextClassLoader.
1 2 |
from m in Methods where m . IsUsing ( “ java . lang . Thread . setContextClassLoader ( ClassLoader ) “ ) select new { m , m . NbBCInstructions } |
Viele Methoden rufen es auf, einschließlich des EquinoxLauncher. Jedes Mal, wenn das Bundle versucht, eine Klasseninstanz zu erstellen, prüft der OSGi-Container, ob der Code diese Aktion ausführen darf oder nicht, und hier kommt die Rolle des importierten und exportierten Pakets in der Manifestdatei.
1 2 3 4 |
Export - Package : org . eclipse . equinox . http . servlet ; version =” 1.1.0 ″ Import - Package : javax . servlet ; version =” 2.3 ″ , javax . servlet . http ; version =” 2.3 ″ , org . osgi . framework ; version =” 1.3.0 ″ , org . osgi . service . http ; versi on =” [ 1.2 , 1.3 ) ” |
Das Bundle deklariert explizit exportierte und importierte Pakete, und um dies zu überprüfen, suchen wir nach Paketen, die vom org.eclipse.equinox.http.servlet-Bundle verwendet werden, und überprüfen, ob es nur das importierte Paket verwendet.
1 2 |
from n in Packages where n . IsUsedBy ( “ org . eclipse . equinox . http . servlet_1 . 1.200.v20110502 “ ) select new { n , n . NbBCInstructions } |
Wie wir sehen können, sind alle verwendeten Pakete in der Manifestdatei im Abschnitt Importpaket angegeben.
Das exportierte Paket stellt jedoch das Paket dar, das von anderen Bundles verwendet werden kann. Es wird durch die ExportedPackage-Schnittstelle dargestellt, und wir können mit dieser Schnittstelle nach den Containerklassen suchen.
1 2 |
from t in Types where t . IsUsing ( “ org . osgi . service . packageadmin . ExportedPackage “ ) select new { t , t . NbBCInstructions } |
Das Interessante an dieser neuen Funktion ist, dass das Bundle eine gut definierte Grenze haben wird und was es verwendet und was es als Dienste verfügbar macht, ist sehr genau spezifiziert.
Wir können die Überprüfung der Verwendung importierter Pakete durch die Verwendung anderer Tools erzwingen, zum Beispiel können wir mit CQLinq einige Regeln schreiben, die jedes Mal warnen, wenn ein Projekt andere Pakete als die angegebenen verwendet, aber es ist besser, diese Überprüfung in der Ausführungsumgebung zu haben, so der Entwickler kann diese Regeln nicht brechen.
Diese Fähigkeit, importierte und exportierte Pakete zu behandeln, wird von der OSGi-Modulebene verwaltet und war unser viertes Puzzleteil.
Kommen wir zurück zum OSGi-Container und entdecken Sie, welche Dienste er bereitstellt.
Containerdienste
Wie wir festgestellt haben, bevor der Container von der EquinoxLauncher-Klasse gestartet wird und die Framework-Klasse die Bundles initialisiert und startet, suchen wir nach allen Klassen, die in der Framework-Initialisierungsmethode verwendet werden, um zu ermitteln, welche Dienste bereitgestellt werden.
1 2 |
from t in Types where t . IsUsedBy ( “ org . eclipse . osgi . framework . internal . core . Framework . initialize ( FrameworkAdaptor ) “ ) select new { t , t . NbBCInstructions } |
Einige Klassen wurden bereits zuvor entdeckt, wie BundleRepository,BundleHost,PackageAdminImpl und ServiceRegistry.
Was ist mit den anderen Klassen:
- StartLevelManager:
Jedem OSGi-Bundle ist eine Startebene zugeordnet, die es dem Server ermöglicht, die relative Start- und Stoppreihenfolge von Bundles zu steuern. Es müssen nur Bundles aktiv sein, deren Startlevel kleiner oder gleich dem aktiven Startlevel des Server-Frameworks ist. Normalerweise wird ein Bundle mit einem kleineren Startlevel tendenziell früher gestartet. - SicherheitsAdmin:
Die Sicherheitsebene kümmert sich um die Sicherheitsaspekte, indem sie die Bundle-Funktionalität auf vordefinierte Fähigkeiten beschränkt. - Event Manager:
Der Event Admin-Dienst stellt ein Publish-Subscribe-Modell zur Behandlung von Ereignissen bereit. Er wird gemäß der OSGi Event Admin Service Specification realisiert. Der Event Admin Service verteilt Events zwischen Event Publishern und Event Subscribern (Event Handlern), indem er einen Event Channel zwischenschaltet. Publisher posten Ereignisse im Kanal und der Ereigniskanal definiert, welche Handler benachrichtigt werden müssen. Somit haben Herausgeber und Handler keine direkte Kenntnis voneinander, was das Ereignismanagement vereinfacht.
Das Gesamtbild von OSGi
Lassen Sie uns alle zuvor beschriebenen Puzzleteile zusammensetzen, und Sie erhalten das folgende OSGi-Bild:
Diese Architektur hat die folgenden interessanten Vorteile:
- Einfach.
- Reduzierte Komplexität.
- Einfache Bereitstellung.
- Sicher.
Was es sehr attraktiv und einen Umweg wert macht, und Sie werden Ihre Zeit nicht verschwenden, wenn Sie es gründlich studieren.