(WS09-01) Management von OSGi-basierten Anwendungen

Aus Verteilte Systeme - Wiki
Zur Navigation springen Zur Suche springen

Aufgabe

Grundlagen

Das OSGi Framework

Die OSGi Service Plattform ist eine Anwendungsplattform, deren Konzept auf dem Komponeten-Gedanken basiert. Die OSGi-Spezifikation wurde von der OSGi Alliance, einem Konsortium von verschiedenen Technologie-Unternehmen und Entwicklern erstellt und definiert ein dynamisches Modul-System für Java Anwendungen.

Die OSGi Komponenten werden als Bundles bezeichnet, deren Laufzeitumgebung das OSGi Framework ist. Bundles können dynamisch installiert, gestartet, gestoppt und aktualisiert werden, ohne dass ein Neustart des Frameworks erforderlich ist.

Es sind verschiedene Open-Source OSGi Implementierungen vorhanden. Die beiden bekanntesten sind Equinox und das Apache Projekt Felix.

Das OSGi Framework ist in verschiedene Schichten unterteilt, die in der folgenden Abbildung dargestellt sind:

caption

  • Services:
Diese Schicht definiert, wie Services veröffentlicht und so von anderen
Bundles benutzt werden können.
  • Life-Cycle:
Diese Schicht beschreibt die API zum Installieren, Starten, Stoppen,
Aktualisieren und Deinstallieren von Bundles.
  • Modules:
Hier wird ein Bundle als Modularisierungseinheit festgelegt und
beschrieben, wie Pakete anderen Bundles zur Verfügung gestellt werden können.
  • Security:
Diese Schicht spezifiziert verschiedene Sicherheitsaspekte. Es ist
beispielsweise möglich, Bundles zu signieren.
  • Execution Environment:
OSGi ist in verschiedenen Java Plattformen lauffähig.
Es wird jedoch keine konkrete Laufzeitumgebung angegeben (wie z.B.
JRE 1.5), statt dessen wird über die Execution Environment lediglich beschrieben, welche
Klassen und Methoden in einer bestimmten Plattform verfügbar sein müssen.

Das Framework und alle dort installierten Bundles werden in der gleichen Java Virtual Machine (JVM) ausgeführt. Dabei ist das Framework selbst ebenfalls ein Bundle (= System Bundle).

OSGi Bundles

In Java besteht die Möglichkeit der Modularisierung normalerweise nur auf Packageebene, der Austausch von Modulen zur Laufzeit ist hier nicht möglich. Das OSGi Framework definiert Bundles als Modularisierungseinheit. Ein Bundle ist eine Java Archiv Datei (*.jar), die um OSGi-spezifische Metainformationen in der Manifestdatei (META-INF/MANIFEST.MF) ergänzt wird.

Um Anwendungslogik nach außen verfügbar zu machen, werden im Manifest sogenannte Export-Packages definiert. Dieser Eintrag wird im Manifest vorgenommen. Andere Bundles können die bereitgestellte Logik verwenden, indem sie das entsprechende Package im Manifest als Import-Package deklarieren.

In OSGi kann das gleiche Package (und somit die darin enthaltenen Klassen) in mehreren Versionen installiert sein. Unter welcher Versionsnummer ein Paket exportiert werden soll, wird im Manifest über den Header Export-Package festgelegt.Beim Importieren kann dann angegeben werden, welche der vorhandenen Versionen verwendet werden soll.

OSGi Services

OSGi bietet Bundles die Möglichkeit, Services bereit zu stellen, die von anderen Bundles benutzt werden können. Ein Service wird im Rahmen von OSGi als Java Objekt realisiert, das in der Regel ein oder mehrere Interfaces implementiert. Der Service wird vom anbietenden Bundle unter einem oder mehreren Interfacenamen bei der Service Registry angemeldet. Die Service Registry ist die zentrale Verwaltungseinheit für Services und wird vom Framework bereitgestellt. Dort können die von anderen Bundles registrierten Dienste (unter Angabe des Interfacenamens) abgefragt werden.

Das Spring Framework

Das Spring Framework ist ein Application Framework, das die Entwicklung von Geschäftsanwendungen unterstützt. Die Basis des Spring Frameworks bildet der so genannte IoC Container. Die Abkürzung IoC steht für Inversion of Control und bezeichnet ein Programmierkonzept, bei dem bestimmte Verantwortlichkeiten an eine höhere Instanz abgegeben werden. Im Fall von Spring ist dies die Verantwortung zum Erzeugen und Verwalten von Abhängigkeiten, die hier vom IoC Container (statt von jedem Objekt selbst) übernommen wird. Für diese Art der Inversion of Control führte Martin Fowler in [3] den Begriff Dependency Injection ein.

Klassen, die vom IoC Container instanziiert und verwaltet werden, sind in der Spring-Terminologie Beans. Über das Interface ApplicationContext wird die Konfiguration einer Spring-Anwendung festgelegt; diese Konfigurationsdaten können von der Anwendung auch zur Laufzeit abgefragt werden. Spring bietet verschiedene Implementierungen dieses Interfaces, es wird jedoch in der Regel eine Implementierung verwendet, die es ermöglicht, Beans deklarativ in einer XML-Datei zu definieren:

 <bean id="exampleBean" class="examples.ExampleBean">
     <property name="beanOne">
         <ref bean="anotherExampleBean"/>
     </property>

     <property name="beanTwo" ref="yetAnotherBean"/>

     <property name="integerProperty" value="1"/>
 </bean>

 <bean id="anotherExampleBean" class="examples.AnotherBean"/>
 <bean id="yetAnotherBean" class="examples.YetAnotherBean"/>

Diese Datei wird im Folgenden als Application Context bezeichnet.

Spring Dynamic Modules

Mit dem Spring Projekt Dynamic Modules (Spring DM) können Spring Anwendungen erstellt werden, die anschließend als Bundles im OSGi-Framework installiert werden können. So können auch OSGi-Bundles von den bewährten Spring Konzepten (z.B. Dependency Injection) profitieren. Durch den Einsatz von Spring DM ist es zudem möglich, das Registrieren und Verwenden von Services deklarativ in einer Konfigurationsdatei zu definieren. So bleibt der Anwendungscode frei von OSGi-spezifischen Implementierungen.

Um Spring Anwendungen im OSGi Framework zu installieren, wird das Spring Extender-Bundle benötigt. Dieses Bundle übernimmt die Instanziierung des Application Contexts für alle gestarteten Bundles. Damit der Extender ein OSGi-Bundle als Spring-basiert erkennen kann, muss sich dessen Application Context (in Form einer XML-Datei) entweder im Verzeichnis META-INF/spring befinden oder es muss über den Manifest Header Spring-Context der entsprechende Pfad zur Datei definiert werden.

Spring Services

Ein Service wird bei der Verwendung von Spring DM über den Namen des Interfaces bei der Service Registry angemeldet, dessen Funktionen für alle verfügbar sein sollen. Mit den folgenden beiden Zeilen Code wird zuerst die Bean definiert und diese anschließend als Service im OSGi Framework bereitgestellt:

 <bean name="serviceExample" class="de.einsundeins.service.impl.ServiceExampleImpl"/>

 <osgi:service ref="serviceExample" interface="de.einsundeins.service.ServiceExample" />

Die Klasse ServiceExampleImpl implementiert das Interface ServiceExample und wird zunächst als Bean definiert. Über das Tag <osgi:service/> wird die Implementierung des Interfaces ServiceExample als Service bei der Registry angemeldet. Diese weist dabei jedem Service eine eindeutige ID zu. Soll ein registrierter Service verwendet werden, wird dieser über den Interfacenamen identifiziert und kann anschließend wie eine "normale" Bean verwendet werden:

 <osgi:reference id="myServiceExample"  interface="de.einsundeins.service.ServiceExample" />

JMX

Die Java Management Extensions (= JMX) Spezifikation ist Teil der Java Platform Standard Edition und definiert eine Technologie zum Überwachen und Verwalten von Anwendungen. Um das Anwendungsmanagement zu ermöglichen, muss die zu verwaltende Java Klasse instrumentiert werden. Instrumentierung bedeutet, dass die Anwendung oder allgemeiner noch, die Ressource mit Messwerkzeugen ausgestattet wird, die Informationen über den internen Zustand der Ressource nach außen bereitstellen. Die Instrumentierung geschieht mit sog. Managed Beans (auch einfach MBeans).

Die JMX Architektur besteht aus den drei Ebenen

  • Instrumentation
  • Agent und
  • Remote Management,

die in den folgenden Abschnitten näher beschrieben.

Instrumentation

Wie bereits erwähnt erfolgt die Instrumentierung über sogenannte MBeans. Die MBeans sind Java Objekte, die bestimmten JMX-Design Richtlinien genügen. Teil dieser Richtlinien ist die Bereitstellung eines Management-Interface, das u.a. alle aufrufbaren Operationen definiert.

Die JMX Spezifikation unterscheidet verschiedene MBean-Typen, und zwar:

  • Standard MBeans
  • Dynamic MBeans
  • Open MBeans
  • Model MBeans
  • MXBeans

Im Rahmen dieses Projekts werden nur die Standard MBeans verwendet. Eine Standard MBean besteht aus einem Interface und einer Klasse, die dieses implementiert. Der Interfacename muss das Suffix MBean haben, also beispielsweise FooMBean. Der Name der zugehörigen Klasse wäre demnach Foo. Das Interface listet alle Attribute und Operationen auf, die nach außen angeboten werden sollen.

Beispiel:

 
 public interface FooMBean {
    public void setName(String name);
    public String getName();
    public void writeName();
 }

Dieses Interface definiert das (veränderbare) Attribut name und die Operation writeName(), die den Namen ausgeben soll. Die zugehörige Klasse könnte beispielsweise folgende sein:

 
public class Foo implements FooMBean {
   private String name;

  @Override
  public String getName() {
       return this.name;
  }
 
  @Override
  public void setName(String name) {
      this.name=name;
  }
 
  @Override
  public void writeName() {
      System.out.println("Name :"+this.name);
  }
 
}

Die implementierte MBean wird jetzt zur Verwaltung an den JMX Agent übergeben.

Agent

Die Spezifikation definiert den JMX Agent zur Verwaltung der MBeans. Dieser besteht aus dem MBean Server, bei dem die MBeans registriert werden und weiteren Diensten u.a. zur Verwaltung der MBeans. Der Agent "läuft" in der Regel in der gleichen JVM wie die zu verwaltenden Ressourcen/Anwendungn und ermöglicht den Remote-Zugriff auf die registrierten MBeans, bzw. auf deren Attribute und Operationen.

Die Registrierung der MBean beim Server kann z.B. programmatisch erfolgen:

 
      MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); 
      ObjectName name = new ObjectName("de.example:Name=Foo"); 
      Foo mbean = new Foo(); 
      mbs.registerMBean(mbean, name);

Wie hier zu sehen ist, wird jede MBean unter einem ObjectName beim MBean Server registriert.

Remote Management

Der MBean Server verwendet Konnektoren (= JMX Connectors), um den Zugriff auf den JMX Agent auch von Management-Anwendungen aus zu ermöglichen, die in einer anderen JVM als der Agent betrieben werden (= Remote Zugriff). Ein JMX Connector besteht aus einem Connector Client und einem Connector Server. Letzterer hängt am MBean Server und wartet auf Client-Anfragen. Der "connector client" läuft, wie oben bereits erwähnt, in der Regel in einer anderen JVM, als der connector server. Die Ausführung einer Operation/Methode der MBean erfolgt über ein auf RMI (kurz für Remote Method Invocation) basierendes Protokoll (siehe auch Using JMX Agents).

  • JConsole

Die JConsole ist ein Monitoring Tool und Teil der Java SE. Mit Hilfe der JConsole kann (u.a.) ein Remote Zugriff auf die MBeans erfolgen.
Die oben registrierte MBean FooMBean würde in der JConsole zu folgender Ansicht führen:

JConsoleExample.jpg



Hier kann jetzt beispielsweise das Attribut name mit einem Wert belegt oder die Methode writeName() ausgeführt werden.

  • JMX Client

Statt die JConsole als JMX Client zu verwenden, kann auch ein eigener Client erstellt werden, der Operationen ausführen, Attributwerte lesen/ändern oder Informationen über registrierte MBeans sammeln kann (näheres siehe JMX Tutorial ).

Notifications

Mit den Notifications definiert die JMX-API eine Möglichkeit, mit der MBeans andere über Events informieren können. So kann die MBean beispielsweise Interessenten mitteilen, wenn sich ihrer Attributwerte geändert haben oder ein Fehler aufgetreten ist. Damit MBeans Notifications versenden können, wird entweder das Interface NotificationEmitter implementiert oder von der Klasse NotificationBroadcasterSupport abgeleitet. Zum Empfangen von Notifications wird das Interface NotificationListener implementiert.

Anforderungen

Ziel des Projektes ist die Erstellung einer möglichst generischen, programmatischen Managementlösung für OSGi-Anwendungen. Es soll sowohl eine Überwachungslösung als auch eine Möglichkeit zur Re-Konfiguration von Bundles realisiert werden. Beides soll über einen entfernten Zugriff auf die zu verwaltende Anwendung erfolgen.

Welche Parameter überwacht oder re-konfiguriert werden können, soll jedes Bundle (bzw. der Bundle-Entwickler) selbst festlegen. Im Rahmen der Re-Konfiguration sollen, wenn möglich, bestimmte Methoden des zu verwaltenden Bundles "von außen" aufgerufen werden können.

Die Re-Konfiguration soll neben dem Ändern bestimmter Parameter einer Anwendung auch noch die Möglichkeit zu einem Modi-Wechsel umfassen. Das heißt, jede Anwendung, die von dem zu realisierenden Management-Modul verwaltet wird, soll bestimmte Modi, wie beispielsweise einen "Notfall-Modus", kennen. Ein Modus könnte beispielsweise die Definition eines bestimmten Logging-Levels (bspw. DEBUG, INFO,…) umfassen und definieren, ob bestimmte Funktionen der Anwendung in diesem Modus zu- oder abgeschaltet werden sollen. Über die Management-Anwendung sollen ausgewählte Anwendungen in einen bestimmten Modus gebracht werden können. Im Rahmen dieses Projekts soll dies nur beispielhaft umgesetzt werden, das heißt der Modi-Wechsel soll von der Management-Anwendung angestoßen und von den zu verwaltenden Anwendungen auch empfangen, aber dort nicht tatsächlich durchgeführt werden.

Des Weiteren soll die Managementlösung das (entfernte) Starten und Stoppen von Bundles ermöglichen.

Zum Testen der Management- und Konfigurationsfunktionalität soll eine prototypische OSGi-Anwendung realisiert werden. Die Anwendung soll eine Schnittstelle bieten, mit deren Hilfe die Werte bestimmter Attribute programmatisch geändert werden können und die Anwendung soll den Wechsel in bestimmte Modi (wie oben beschrieben) unterstützen.

Zusammenfassend sind folgende Funktionen umzusetzen:

Management-Anwendung:

  • Überwachung von OSGi-Bundles
  • (entfernte) programmatische Re-Konfiguration von OSGi-Bundles
  • Änderung von Parametern und Aufruf von Methoden (der zu verwaltenden Anwendungen)
  • Definition einer Schnittstelle für den Modiwechsel
  • initiieren von Modi-Wechseln
  • entferntes Start/Stoppen von Bundles


Prototypische OSGi-Anwendung:

  • Bereitstellung von Parametern und Operationen zur Re-Konfiguration über eine entspr. Schnittstelle
  • im Rahmen der Überwachung: Management-Anwendung über Attribut(wert) Änderungen informieren
  • Anbieten einer Schnittstellte, um Wunsch zum Modi-Wechsel zu empfangen



Als OSGi-Implementierung soll das Apache Projekt Felixeingesetzt werden, jedoch sollen nach Möglichkeit keine Abhängigkeiten von einer bestimmten OSGi-Implementierung bestehen. Für die Auswahl einer geeigneten Lösung sollen OSGi-spezifische Ansätze anderen vorgezogen werden (sofern möglich). Hierbei sollen auch die Neuerung des OSGi-Release 4.2 beachtet werden.

Mögliche Lösungsansätze

Im Folgenden Abschnitt werden die verschiedenen Lösungsmöglichkeiten zum Management von Bundles evaluiert.

Configuration Admin Service

Die „OSGi-spezifische" Lösung zur Konfiguration von Bundles ist der Configuration Admin Service. Dieser ist Teil des OSGi Service Kompendiums und ermöglicht die Konfiguration von Bundles und Services zur Laufzeit. Die Spezifikation definiert das Interface org.osgi.service.cm.ConfigurationAdmin, das verschiedene Methoden zum Abfragen und Ablegen von Konfiguration bereitstellt. Implementierungen dieses Interfaces sind z.B. für die OSGi-Implementierungen Equinox und Felix [1] vorhanden.

Eine Bundle-Konfiguration wird hier in Form von Schlüssel/Wert-Paaren hinterlegt und vom Configuration Admin Service persistent gespeichert (je nach Implementierung beispielsweise im Dateisystem). Soll ein Bundle oder ein Service konfigurierbar sein, muss entweder das Interface ManagedService oder das Interface ManagedServiceFactory implementiert und die Implementierung als Service registriert werden. Damit der Configuration Admin Service die verschiedenen gespeicherten Konfigurationen den (richtigen) Bundles zuordnen kann, gibt es die Persistent Identity (PID). Diese wird bei der Registrierung als Service-Property angegeben.

Nachteil
Der Configuration Admin Service ist in erster Linie für die Konfiguration innerhalb des OSGi-Frameworks gedacht. Das heißt ein "Remote"-Zugriff ist ohne weiteres nicht möglich. Eine Lösung hierfür könnten die in der Version 4.2 neu eingeführten Remote Services sein. Allerdings ist noch keine, bzw. keine stabile, Implementierung der Remote Services vorhaben, daher kommt der Configuration Admin Service hier nicht in frage und es muss eine eine Alternative zur (Remote) Konfiguration von Bundles gewählt werden.

JMX

Als Alternative zum Configuration Admin Service kommen die im Grundlagenteil vorgestellten Java Management Extensions in Frage. Es gibt verschiedene Projekte, die eine JMX-Implementierung bzw. einen JMX-Agent für OSGi realisieren. Im Rahmen dieses Projekts wurden JMood und MOSGi getestet:

  • JMood
Das JMX basierte Management-Tool JMood ist im Rahmen einer Master Thesis entstanden. JMood liefert ein Management Bundle, das u.a. einen MBean-Server bereitstellt. Es wird hier automatisch für jedes installierte Bundle eine MBean erzeugt und beim MBean-Server registriert. Diese MBeans stellen verschiedene Operationen, wie beispielsweise das Starten und Stoppen des zur MBean gehörenden Bundles, bereit.
Nachteile
Die Projekt-Dokumentation ist leider nur in Form der spanischen Master Thesis vorhanden. Zudem scheint das Projekt nicht mehr weiter gepflegt zu werden. Die automatisch erstellten MBeans sind ebenfalls für dieses Projekt nicht geeignet, da jedes zu überwachende/konfigurierende Bundle diese Funktionalität individuell selbst anbieten können soll (was dieses ansonsten gute Feature somit unnötig macht).


  • MOSGi
MOSGi ist ein Apache Felix Projekt, das das Remote-Management von OSGi-basierten Anwendung ermöglich und basiert, wie JMood ebenfalls auf JMX. Das Framework stellt unter anderem einen JMX-Agent zur Verfügung und hier können für jedes Bundle individuelle MBeans erstellt werden. Um eine MBean beim Server zu registrieren, wird die Interface-Implementierung als OSGi-Service bereitgestellt. Als Service Property wird der Name der MBean angegeben. Der JMX-Agent erkennt den Service dann als MBean und registriert ihn beim Server. In Kombination mit Spring DM genügt folgende XML-Datei, um das Beispiel aus dem Grundlagenteil als MOSGi-MBean zu veröffentlichen:
 <osgi:service ref="hello" interface="com.example.mbeans.HelloMBean">
    <!-- define  beans object name  -->
    <osgi:service-properties>
        <entry key="jmxagent.objectname" value="bean:name=helloBean" />
    </osgi:service-properties>
 < /osgi:service>
Wird hier kein ObjectName angegeben, generiert MOSGi den ObjectName u.a. auf Basis der Bundle- und Service-ID selbst. Da die zu wählende Lösung möglichst unabhängig von einer bestimmten OSGi-Implementierung sein soll, wurde MOSGi auch unter Equinox getestet, was einwandfrei funktionierte.

Konzept

Aufgrund der Nachteile der anderen Lösungen wird MOSGi zur Realisierung der Management- und Überwachungsfunktionalität basierend auf JMX verwendet. Jedes Bundle, das Überwachungs- und Re-Konfigurationsfunktionalität anbieten möchte, stellt hierzu eine JMX-MBean bereit und registriert das MBean-Interface als OSGi-Service.

In den Anforderungen wurden zwei Bereiche definiert, die im Rahmen dieses Projekts zu realisieren sind: die Management-Anwendung und die prototypische OSGi-Anwendung. Als Beispiel-Anwendung wird ein Taschenrechner implementiert, dessen Rechenart re-konfiguriert werden kann. Bei jeder Änderung der Rechenart wird eine JMX-Notification versandt, um so eine Überwachung der Attributwerte zu realisieren. Weiterhin kann „von außen“ der Rechenvorgang angestoßen werden. Die Management-Anwendung soll zeigen, wie Bundles überwacht und re-konfiguriert werden können. Die Überwachung bezieht sich hier zunächst nur auf die Änderung von Attributwerten. Damit die Management-Anwendung die von den zu überwachenden Bundles versendeten Notifications empfangen kann, wird ein entsprechender Listener beim JMX-Agent registriert.

Bundles starten:
Eine weitere Anforderung ist die Möglichkeit zum entfernten Starten/Stoppen von Bundles. Um dies zu ermöglichen, wird in der Local-Felix-Instanz das Bundle BundleStarter bereitgestellt, dass folgende MBean registiert:

public interface BundleStarterMBean {

	public boolean startBundle(int bundleId);
	public boolean startBundleByName(String symbolicName,String version);
	
	public boolean stopBundle(int bundleId);
	public boolean stopBundleByName(String symbolicName,String version);
}

Mit Hilfe dieser Methoden können Bundles remote entweder mit Hilfe der Bundle-ID oder des symbolischen Namens gestartet bzw. gestoppt werden.

Modi-Wechsel:
Für die Modi-Thematik wurden zwei Lösungsansätze erarbeitet. Da beide Möglichkeiten verschiedene Ansätze verfolgen, werden die Management-Anwendung und die Beispielanwendung in jeweils zwei Varianten realisiert, die in den folgenden Abschnitten näher beschrieben werden. Die erste Variante basiert auf Methodenaufrufen, um einen Modi-Wechsel zu veranlassen, die zweite verwendet hierzu JMX- Notifications.


Variante 1

Die erste Variante basiert auf einem Interface, das alle Bundles implementieren, die einen Modi-Wechsel unterstützen. Dieses Interface heißt ModeMBean und definiert die Methode changeMode(). Jedes Bundle, das einen Modi-Wechsel anbietet, erstellt eine MBean, die entweder direkt dieses Interface implementiert, oder ihr eigenes MBean-Interface von dem ModeMBean -Interface ableitet.

Die Management-Anwendung wird über einen entsprechenden JMX-Notification-Listener über jede MBean-Registrierung informiert. Ist die MBean vom Typ ModeMBean, so merkt sich die Management-Anwendung den zugehörigen ObjectName. Bei einem Modi-Wechsel wird dann an jeder gemerkten ModeMBean die changeMode()-Methode aufgerufen und so der Modi-Wechsel angestoßen.

Das ModeMBean-Interface wird in einem separaten Bundle namens ModeMBean bereitgestellt, welches das Package mit dem Interface exportiert. Die folgende Abbildung zeigt die drei Bundles, die im Rahmen dieser Variante implementiert werden: ‎
BundleÜbersichtV1.PNG

Das Bundle ManagementBundleV1 beinhaltet die Management-Anwendung und importiert das Package mit dem ModeMBean-Interface. Ebenso importiert die Beispielanwendung im Bundle ManageableCalcV1 das Package mit dem Modi-Interface, implementiert dieses Interface und registriert es als OSGi-Service. Dadurch erkennt MOSGi die MBean und stellt deren Attribute und Methoden zum Remote Zugriff bereit.

Das folgende Listing zeigt das MBean-Interface der Beispiel-Anwendung:

 
public interface MgmtCalcV1MBean extends ModeMBean {

    public void setCalculationType(int calculationType);
    public int getCalculationType();
    public void startCalculation();
}

Die Klasse MgmtCalcV1 implementiert dieses MBean-Interface. Damit diese Klasse Notifications versenden kann, erbt sie zusätzlich von NotificationBroadcasterSupport.

Management-Anwendung

Die zu überwachenden Bundles senden bei allen Attributwert-Änderungen eine Notification. Damit die Management-Anwendung solche Notifications empfangen kann, muss ein Objekt vom Typ NotificationListener beim JMX-Agent registriert werden. Es müssen hier zwei Arten von Notification-Listener registriert werden. Zum einen muss die Management-Anwendung über Attributwert-Änderungen informiert werden, dies übernimmt die Klasse AttributChangeListener, zum anderen muss die Management-Anwendung über jede neu registrierte MBean informiert werden, um deren Typ zu prüfen. Falls eine neue MBean vom Typ ModeMBean registriert wurde, muss der ObjectName gespeichert werden, um später den Modi-Wechsel anstoßen zu können. Diese Funktionalität übernimmt die Klasse RegistrationListener.

Variante 2

Variante 2 basiert auf Notifications, um den Modi-Wechsel anzustoßen. Das Bundle ModeNotification enthält die Klasse ModeChangeNotification, die von Notification erbt und die Nachricht repräsentiert, mit der ein Modi-Wechsel angestoßen wird. Diese Nachricht wird von der Management-Anwendung versandt (Bundle ManagementBundleV2). Um dies zu ermöglichen, importiert das ManagementBundleV2 das Package, in dem die Notification-Klasse enthalten ist. Die folgende Abbildung soll die Beziehungen verdeutlichen:

BundleÜbersichtV2.PNG


Prototypische OSGi-Anwendung

Damit die Beispiel-Anwendung (Bundle ManageableCalcV2) auch die Benachrichtigung zum Modi-Wechsel empfangen kann, importiert sie ebenfalls das Notification-Package und registriert einen Listener für diesen Notification-Typ beim JMX-Agent. Hierzu wird das Interface NotificationListener implementiert. Das MBean-Interface der Beispiel-Anwendung enthält folgende Methoden:

public interface MgmtCalcV2MBean{

    public void setCalculationType(int calculationType);
    public int getCalculationType();
    public void startCalculation();
    public void registerListener(String rmiConnection);
    public void removeListener();
}

Im Gegensatz zu Variante 1 sind hier die Methoden registerListener() und removeListener() ergänzt worden. Über diese Methoden kann konfiguriert werden, ob die Anwendung aktuell ModeChangeNotifications empfangen soll oder nicht.

Management-Anwendung

Damit die Management-Anwendung Notifications versenden kann, muss sie selbst ebenfalls eine MBean sein. Hierzu wird das folgende Interface definiert:

public interface ManagementV2MBean {
    public void sendModeNofication(int mobiNr, String group);
}

Zudem muss die Klasse ManagementV2, die dieses Interface implementiert, von NotificationBroadcasterSupport abgeleitet werden, um den Nachrichtenversand zu ermöglichen. Über die Methode sendModeNofication() kann hier der Modi-Wechsel z.B. über die JConsole angestoßen werden. Dies ist hier lediglich zu Demonstrationszwecken gedacht. Auch hier registriert die Management-Anwendung die Klasse AttributChangeListener, die bereits in Variante 1 vorgestellt wurde, beim JMX-Agent, um über alle Attributwert-Änderungen informiert zu werden.

Installation

Bevor die Implementierung der verschiedenen Bundles beschrieben wird, wird hier zunächst erläutert, worauf beim Einrichten des Frameworks zu achten ist.

Versionsübersicht/Systemvoraussetzungen:

  • Java (& JMX) 1.6.0
  • Apache Felix 2.0.1
  • Spring Framework 2.5.6
  • Spring DM 1.2.0 (Hier sollte die Version „with dependencies“ beim Download gewählt werden. Diese enthält im „lib“ Ordner des Archivs bereits alle von Spring DM benötigten Bundles (auch die Spring Bundles))


Felix
Zur Installation von Felix muss lediglich das heruntergeladene Framework-Archiv entpackt werden. Alle zu installierenden Bundles können in den Ordner bundle kopiert werden. Felix installiert dann beim Start die enthaltenen Bundles automatisch. Das Framework-Archiv enthält im Ordner bundle bereits das Bundle für die Felix Shell und das Bundle Repository. Gestartet wird das Framework vom Installationsordner aus mit java –jar bin\felix.jar. Mit dem Befehl help können alle verfügbaren Konsolenbefehle angezeigt werden.


MOSGi
Um MOSGi zu installieren, werden die folgenden vier Bundles benötigt:

  • JMX-Agent
  • JMX-Registry
  • RMI-Connector
  • Log Service

Die Bundles können z.B. vom Felix Maven Repository heruntergeladen werden. Nach dem Starten dieser vier Bundles kann im Log (Konsolen-Befehl log) ausgelesen werden, unter welcher Adresse der MBean-Server erreichbar ist (z.B. unter: service:jmx:rmi:///jndi/rmi://localhost:1099/null).


Spring DM
Nach dem Entpacken der Archiv-Datei werden die im „lib“ Ordner enthaltenen Bundles in den Felix „bundle“ Ordner kopiert.


Das „ps“ Kommando sollte nach der Installation eine ähnliche Ansicht zeigen:

   ID   State         Level  Name
[   0] [Active     ] [    0] System Bundle (2.0.1)
[   1] [Active     ] [    1] AOP Alliance API (1.0.0)
[   2] [Active     ] [    1] Apache Commons Logging (1.1.1)
[   3] [Active     ] [    1] log4j.osgi (1.2.15.SNAPSHOT)
[   4] [Resolved   ] [    1] Log4j Fragment (1.0.0)
[   5] [Active     ] [    1] Apache Felix Log Service (1.0.0)
[   6] [Active     ] [    1] MOSGi JMX-MX4J Agent Service (0.9.0.SNAPSHOT)
[   7] [Active     ] [    1] MOSGi JMX rmiregistry (0.9.0.SNAPSHOT)
[   8] [Active     ] [    1] MOSGi JMX-MX4J RMI Connector (0.9.0.SNAPSHOT)
[   9] [Active     ] [    1] Apache Felix Shell Service (1.4.1)
[  10] [Active     ] [    1] Apache Felix Shell TUI (1.4.1)
[  11] [Active     ] [    1] Spring AOP (2.5.6.A)
[  12] [Active     ] [    1] Spring Beans (2.5.6.A)
[  13] [Active     ] [    1] Spring Context (2.5.6.A)
[  14] [Active     ] [    1] Spring Core (2.5.6.A)
[  15] [Active     ] [    1] spring-osgi-core (1.2.0)
[  16] [Active     ] [    1] spring-osgi-extender (1.2.0)
[  17] [Active     ] [    1] spring-osgi-io (1.2.0)

Wichtig ist, dass das Spring Extender Bundle (hier ID 16) den Status Active hat, damit der Application Context in installierten „Spring-Bundles“ erkannt wird.

HINWEIS:
Werden zusätzliche Logging-Ausgaben gewünscht, kann ein Logging-Framework installiert werden. Wie oben in der „ps-Liste“ zu sehen, wurde hier Log4j verwendet. Das zugehörige Bundle wird mit Spring DM geliefert. Log4j kann jetzt Anhand eines Fragment-Bundles, das die log4j.xml-Datei enthält, konfiguriert werden.

Realisierung

Da hier das "Remote Management" von OSGi-Anwendungen demonstriert wird, werden für Variante 1 und für Variante 2 jeweils zwei Felix-Instanzen erstellt. In einer Instanz wird die Management-Anwendung (Bundle: ManagementBundle[V1|V2]) installiert, in der anderen die Beispiel-Anwendung (Bundle: ManageabeCalc[V1|V2]). Die Instanz, in der die Management-Anwendung installiert wird, wird im Folgenden als Remote-Felix bezeichnet, die zweite Felix Instanz als Local-Felix.
Felix Instanzen.PNG

Variante 1

Die Management-Anwendung wird, zusammen mit dem ModeMBean-Bundle, in der Remote-Felix-Instanz installiert. Die Beispiel-Anwendung wird, ebenfalls zusammen mit dem ModeMBean-Bundle, in der Local-Felix-Instanz installiert. Bei dieser Variante muss MOSGi nur in der Local-Felix-Instanz installiert werden.

Die folgende Abbildung zeigt zur Übersicht die Bundles und darin enthaltenen Klassen dieser Variante.
KlassenÜbersichtV1.PNG


Management-Anwendung

Damit die Management-Anwendung mit dem JMX-Agent der Local-Felix-Instanz interagieren kann, muss zunächst eine Verbindung zu diesem aufgebaut werden. Dies erfolgt über:

   serverUrl = new JMXServiceURL(getUrl());
   JMXConnector jmxc = JMXConnectorFactory.connect(serverUrl);
   mbsc = jmxc.getMBeanServerConnection();

Die Methode getUrl() liefert hier die URL des MBean-Servers der Local-Felix-Instanz. Diese URL wird in einer Property-Datei namens connection.properties hinterlegt, die folgenden Eintrag enthält:

    mgmt.url=service:jmx:rmi:///jndi/rmi://<ipAdresse>:1099/null

Der Platzhalter <ipAdresse> steht für die IP-Adresse des MBean-Servers. Das Auslesen der Datei erfolgt über Spring, hierzu wird im Application Context der PropertyPlaceholderConfigurer verwendet:

    <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        <property name="locations">
            <value>file:connection.properties</value>
	</property>
    </bean>

    <bean id="managementBundle" class="de.hs.rm.mgmt.ManagementV1Impl" init-method="init">
  	<property name="url" value="${mgmt.url}"></property>
    </bean>



Damit die Management-Anwendung die von den zu überwachenden Bundles versendeten AttributeChangeNotifications empfangen kann, wird in der Klasse ManagementV1Impl eine Instanz von AttributChangeListener beim JMX-Agent registriert. Hierzu wird die Methode MBeanServerConnection.addNotificationListener() verwendet. Als Parameter wird hier u.a. der ObjectName der MBean angegeben, von der die Notifications empfangen werden sollen. Da die Management-Anwendung an Informationen von allen MBeans interessiert ist, werden zunächst die ObjectNames von allen installierten MBeans abgefragt und dann für jede MBean der Listener registriert. Auf die gleiche Weise wird auch eine Instanz des RegistrationListener registriert, der für den Modi-Wechsel benötigt wird:

    for (Iterator i = names.iterator(); i.hasNext();) {
	ObjectName objectName = (ObjectName) i.next();
			
        try {
            // add listener
            mbsc.addNotificationListener(objectName, regListener, null,null);
            mbsc.addNotificationListener(objectName, changeListener, null,null);

        } catch (IllegalArgumentException e) {
           // mbean is no notification broadcaster - ignore it
        }
				

        //check if the MBean supports different modes
        if (mbsc.isInstanceOf(objectName, ModeMBean.class.getName())) {
            modiMBeans.add(objectName);
        }
}

Am Ende der Schleife wird geprüft, ob der ObjectName zu einer MBean vom Type ModeMBean gehört. Ist das der Fall, wird der ObjectName in einer Liste gespeichert, um so später die jeweiligen MBeans über Modi-Wechsel informieren zu können.

Der RegistrationListener ist dafür zuständig, die Liste der ModeMBeans zu aktualisieren. Werden nach dem Start der Management-Anwendung weiterer ModeMBeans registriert, fügt er den jeweiligen ObjectName der Liste hinzu, wird eine ModeMBean de-registriert, wird der ObjectName aus der Liste gelöscht. Dies ist möglich, da der MBean-Server jeweils eine Notification versendet, wenn eine MBean registriert/de-registriert wird. Die Aktualisierung der Liste erfolgt in der Methode handleNotification(), die beim Empfang von Notifications aufgerufen wird:  

public void handleNotification(Notification notification, Object handback) {

    // is there a new or unregistered MBean?
    if (notification instanceof MBeanServerNotification) {
        MBeanServerNotification msn = (MBeanServerNotification) notification;

	//REGISTRATION
	if (msn.getType().equals(MBeanServerNotification.REGISTRATION_NOTIFICATION)) {
	    try {
	        if (mbsc.isInstanceOf(msn.getMBeanName(),ModeMBean.class.getName())) {
		    this.modiMBeans.add(msn.getMBeanName());
		}
	    } catch (InstanceNotFoundException e) {
		logger.debug("InstanceNotFoundException: "+e.getMessage(),e);
	    } catch (IOException e) {
		logger.debug("IOException : "+e.getMessage(),e);			
	    }

	}// UNREGISTRATION
	else if (msn.getType().equals(MBeanServerNotification.UNREGISTRATION_NOTIFICATION)) {
	    try {
	        if (mbsc.isInstanceOf(msn.getMBeanName(),ModeMBean.class.getName())) {
		    this.modiMBeans.remove(msn.getMBeanName());
		}
	    } catch (InstanceNotFoundException e) {
		logger.debug("InstanceNotFoundException: "+e.getMessage(),e);
	    } catch (IOException e) {
		logger.debug("IOException : "+e.getMessage(),e);			
	    }
	}
    }
}

Der eigentliche Modi-Wechsel wird über die Methode changeMode() der Klasse ManagementV1Impl angestoßen:

@Override
public void changeMode(int modiNr) {

    for (ObjectName objectName : modiMBeans) {
        ModeMBean mbeanProxy = JMX.newMBeanProxy(mbsc, objectName,ModeMBean.class, false);
        boolean status=mbeanProxy.changeMode(2);
			
	if(status==true){
	    logger.debug("Mode change for MBean "+objectName+" successful.");
	}else{
	    logger.error("Mode change failed for MBean: "+objectName);
	}
    }

}

Hier wird über die Liste der ModeMBeans iteriert und an jeder MBean die changeMode()-Methode aufgerufen.


Der AttributChangeListener schreibt für alle empfangenen AttributeChangeNotifications jeweils den alten und neuen Attributwert in eine Log-Datei. Damit die Management-Anwendung auch von neu installierten MBeans die AttributeChangeNotifications erhalten kann, "lauscht" der AttributChangeListener auf die vom MBean-Server versendeten REGISTRATION_NOTIFICATIONs und fügt beim Empfang einer solchen sich selbst als Notification Listener hinzu (über MBeanServerConnection.addNotificationListener()). Folgendes Listing zeigt den vom AttributChangeListener erzeugten Log-Eintrag:

Received notification:
11:07:21,304 DEBUG [ClientListener]     ClassName: javax.management.AttributeChangeNotification
11:07:21,307 DEBUG [ClientListener]     Source: bean:name=Calcul
11:07:21,310 DEBUG [ClientListener]     Type: jmx.attribute.change
11:07:21,315 DEBUG [ClientListener]     Message: Calculation Typ changed
11:07:21,321 DEBUG [ClientListener]     AttributeName: calculationType
11:07:21,323 DEBUG [ClientListener]     AttributeType: String
11:07:21,328 DEBUG [ClientListener]     NewValue: 2
11:07:21,332 DEBUG [ClientListener]     OldValue: 1

Prototypische OSGi-Anwendung

Die Beispiel-Anwendung ist, wie in der Übersicht dargestellt, im Bundle ManageableCalcV1 enthalten. Die Klasse MgmtCalcV1 implementiert das MBean-Interface und wird von NotificationBroadcasterSupport abgeleitet (vgl. Bundle-Übersicht). Hierdurch wird der Versand von Notifications ermöglicht.

Damit Attributänderungen transparent erfolgen, wird bei jedem Aufruf einer setter-Methode eine AttributeChangeNotification versendet, die den alten und den neuen Wert enthält:

public void setCalculationType(int calculationType) {
    this.calculationTypeOld=this.calculationType;
    this.calculationType = calculationType;

    Notification n = 
            new AttributeChangeNotification(this, 
				            sequenceNumber++, 
				            System.currentTimeMillis(), 
					    "Calculation Typ changed", 
				            "calculationType", 
					    "int", 
					    calculationTypeOld, 
					    this.calculationType); 
    sendNotification(n); 
}

Die Methode changeMode(), die vom ModeMBean-Interface definiert wird, wird in der Klasse MgmtCalcV1 implementiert und enthält zu Demonstrationszwecken nur eine Log-Ausgabe:

@Override
public boolean changeMode(int mode) {
    logger.debug("changeMode() called with mode: "+mode);
    return true;
}


Nun wird im Spring Application Context (Datei mgmt-calc.xml) die Bean definiert:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="calculationService" class="de.hs.rm.calc.MgmtCalcV1">
        <property name="calculationType" value="1"/>
    </bean>
										
</beans>

Damit MOSGi die MgmtCalcV1MBean als solche "erkennen" kann, wird die Implementierung unter dem Namen des MBean-Interfaces als OSGi-Service registriert. Da hier Spring DM eingesetzt wird, erfolgt die Registrierung deklarativ in einer XML-Datei:

Datei mgmt-calc-osgi.xml:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:osgi="http://www.springframework.org/schema/osgi" xsi:schemaLocation="http://www.springframework.org/schema/beans 
       http://www.springframework.org/schema/beans/spring-beans.xsd  http://www.springframework.org/schema/osgi 
       http://www.springframework.org/schema/osgi/spring-osgi.xsd">

       <!--
     	 Export the calculationService bean (defined in the  mgmt-calc.xml file)
	 as an OSGi service
	-->
	<osgi:service interface="de.hs.rm.calc.MgmtCalcV1MBean"
		ref="calculationService">

		<!-- define  beans object name  -->
		<osgi:service-properties>
			<entry key="jmxagent.objectname" value="bean:name=CalcVl" />
		</osgi:service-properties>

	</osgi:service>

</beans>

Bei der Registrierung wird dann auch der ObjectName festgelegt. Wird dieses Attribut nicht gesetzt, erstellt MOSGi automatisch einen ObjectName für die MBean, der sich u.a. aus der Bundle Nummer und der Service-ID zusammensetzt. Da sich diese beiden Attribute aber z.B. bei jedem update ändern können, ist es sinnvoll (auch im Hinblick auf Notification Listener, die sich an den ObjectName binden) den Objekt Namen beim Registrieren anzugeben.

Variante 2

Wie bereits erwähnt, erfolgt bei dieser Variante der Modi-Wechsel über von der Management-Anwendung versandte Notifications. Damit Notifications versendet werden können, muss die Management-Anwendung ebenfalls eine MBean beim MBean-Server registrieren. Demnach muss hier MOSGi sowohl in der Local-Felix-Instanz als auch in der Remote-Felix-Instanz installiert werden.

Die folgende Abbildung zeigt zur Übersicht die Bundles und darin enthaltenen Klassen dieser Variante:
KlassenÜbersichtV2.PNG


ModeNotification

Um eigene Notification-Typen zu erstellen, können Klassen von Notification abgeleitet werden und einen eigenen Konstruktor definieren, über den die Attribute der Notification angegeben werden. Die Klasse ModeNotification, die die Notification zum Anstossen des Modi-Wechsels repräsentiert, wurde auf diese Weise implementiert:

public class ModeChangeNotification extends Notification implements Serializable {
    private int mode;
    private String group;
    private static final long serialVersionUID = 1L;
    public static final String MODE_CHANGE="de.hs.rm.mgmt.mode.change";
	
	
    public ModeChangeNotification(Object source, long sequenzNr,int mode,String group) {
        super(MODE_CHANGE, source, sequenzNr);
        setTimeStamp(System.currentTimeMillis());
        this.mode=mode;
        this.group=group;
   }


    public int getMode() {
	return mode;
    }

    public String getGroup() {
        return group;
    }

}

Beim Instanziieren dieser Klasse müssen folgenden Parameter angegeben werden:

  • die Quelle, also das Objekt, das die Notification versendet
  • die Sequenznummer
  • die Nummer des Modus, in den gewechselt werden soll und
  • die Gruppe von MBeans, für die die Notification bestimmt ist.

Mit dem letzten Parameter kann angegeben werden, für welche Gruppe von MBeans die Notification bestimmt ist und so ein Filter-Mechanismus erzeugt werden. Da die Notifications an alle MBeans versendet werden, können diese so beim Empfang auswerten, ob die Nachricht überhaupt für sie bestimmt war.

Management-Anwendung

Im Gegensatz zu Variante 1 muss bei dieser Variante auch die Management-Anwendung als MBean registriert werden. Dazu implementiert die Klasse ManagementV2 zunächst das zugehörige MBean-Interface und wird, damit Notifications versendet werden können, von NotificationBroadcasterSupport abgeleitet.

Beim Bundle-Start wird die init()-Methode ausgeführt. Hier werden, analog zu Variante 1, zunächst beim MBean-Server der Local-Felix-Instanz die ObjectNames aller registrierten MBeans abgefragt und für jede MBean der AttributChangeListener registriert. Die URL zum MBean-Server wird auch hier wieder in der Property-Datei connection.properties hinterlegt. Der RegistrationListener wird bei dieser Variante nicht benötigt.

Das Interface ManagementV2MBean definiert die Methode sendModeNofication(), die wie folgt implementiert wird:

@Override
public void sendModeNofication(int modeNr,String group) {
		
    ModeChangeNotification note=new ModeChangeNotification(this,sequenzNr++,modeNr,group);
    sendNotification(note);

}

Diese Methode kann beispielsweise von der JConsole aus aufgerufen werden. Der Parameter modeNr definiert den Modus, in den gewechselt werden soll.

Klassen, die Notifications versenden, können die Methode getNotificationInfo() überschreiben und so nähere Informationen zu den gesendeten Notifications bereitstellen. In der Klasse ManagementV2 wird die Methoden wie folgt implementiert:

@Override 
public MBeanNotificationInfo[] getNotificationInfo() {
	     
    String[] types = new String[]{ ModeChangeNotification.MODE_CHANGE};
    String name = ModeChangeNotification.class.getName();
    String description = "Notification causes a mode change.";
    
    MBeanNotificationInfo info = new MBeanNotificationInfo(types, name, description);

    return (new MBeanNotificationInfo[]{ info });
}

So ist beispielsweise in der JConsole zu sehen, welche Notifications versandt werden:

JConsole Notifications.PNG

Prototypische OSGi-Anwendung

Das MBean-Interface MgmtCalcV2MBean unterscheidet sich von dem der 1. Variante durch die beiden Methoden registerListener() und removeListener() und wird nicht von ModeMBean abgeleitet. Da die Management-Anwendung Notifications versendet, um den Modi-Wechsel anzustoßen, müssen Bundles, die Modi-Wechsel unterstützen möchten einen NotificationListener registrieren. Die Notifications, die die Management-Anwendung versendet, gehen an den MBean-Server der Remote-Felix-Instanz. Aus diesem Grund muss die Beispiel-Anwendung eine Verbindung zum MBean-Server der Remote-Felix-Instanz herstellen und dort den Listener registrieren. Damit das möglich ist, muss wieder die URL bekannt sein. Diese wird, zusammen mit dem ObjectName der Management-Anwendung über die Methoden registerListener() gesetzt:

@Override
public void registerListener(String rmiConnection,String objectName) {
    try {
				
        this.mgmtBundleObjectName=objectName;
				
        JMXServiceURL serverUrl = new JMXServiceURL(rmiConnection);
        JMXConnector jmxc = JMXConnectorFactory.connect(serverUrl);
        mbsc = jmxc.getMBeanServerConnection();
				
        // add listener
        mbsc.addNotificationListener(new ObjectName(objectName), this, null,null);
				
				
    } catch (MalformedURLException e) {
        logger.debug(e.getMessage(),e);
    } catch (IOException e) {
        logger.debug(e.getMessage(),e);
    }catch (InstanceNotFoundException e) {
        logger.debug(e.getMessage(),e);
    } catch (MalformedObjectNameException e) {
        logger.debug(e.getMessage(),e);
    } catch (NullPointerException e) {
        logger.debug(e.getMessage(),e);
    }
}

Hier implementiert die Klasse MgmtCalcV2 das NotificationListener-Interface. In der Methode handleNotification(), die beim Empfang einer Notification aufgerufen wird, kann dann der Modi-Wechsel durchgeführt werden. In diesem Beispiel werden allerdings nur die mit der Notification versendeten Parameter ins Log geschreiben:

@Override
public void handleNotification(Notification notification, Object handback) {

    if (notification instanceof ModeChangeNotification) {
        ModeChangeNotification mode=(ModeChangeNotification)notification;
        logger.debug("Got ModeChangedNotification");
        logger.debug(" for mode: "+mode.getMode());
        logger.debug(" and group: "+mode.getGroup());
    }
		
}

Ansonsten unterscheidet sich diese Variante der Beispiel-Anwendung nicht von der ersten. Bei Änderung des Attributwerts von calculationType wird auch hier eine AttributeChangeNotification versendet und der Rechenvorgang kann ebenfalls "von außen" angestoßen werden. Der ObjectName der MBean dieser Variante lautet: bean:name=CalcV2.

Bundles starten

Wie bereits erwähnt, können mit Hife des Bundles BundleStarter Bundles der Local-Felix-Instanz remote gestartet/gestoppt werden. Um ein Bundle starten/stoppen zu können, muss zunächst das zugehörige Bundle-Objekt vorhanden sein. Diese können mit Hilfe des vom OSGi-Framework bereitgestellten Interface BundleContext über die Bundle-ID gesucht werden. Diese Suchvariante wird in den beiden Methoden startBundle() und stopBundle() verwendet.


Um ein BundleContext-Objekt zu erhalten, kann das OSGi-Interface BundleActivator implementiert werden. Der dort definierten Methode start() wird von OSGi ein entsprechendes Objekt übergeben. Da hier Spring DM eingesetzt wird, wird stattdessen das Interface BundleContextAware verwendet, um ein BundleContext-Objekt zu erhalten. Dieses Spring DM Interface definiert die Methode setBundleContext(), der als Parameter das entsprechende Objekt übergeben wird. Dieses wird dann in den Methoden BundleStarterMBean.startBundle() und BundleStarterMBean.stopBundle() verwendet, um mit der Methode BundleContext.getBundle() ein Bundle zu suchen:

@Override
public boolean startBundle(int bundleId) {
    if (context != null) {

	Bundle bundle = context.getBundle(bundleId);
	if (bundle == null) {
	    logger.error("No such Bundle with id "+bundleId);
	    return false;
	}
			

	if (bundle.getState() == Bundle.RESOLVED ||bundle.getState()==Bundle.INSTALLED) {
	    try {

                bundle.start();
		return true;

	    } catch (BundleException e) {
		logger.error(e.getMessage(), e);
		return false;
	    }
	}

    }
    
    return false;
}

Die Methode BundleContext.getBundle() liefert ein Bundle-Objekt. An diesem kann nun die start()/stop()-Methode aufgerufen werden. Gestartet wird ein Bundle, wenn es sich im Zustand RESOLVED oder INSTALLED befindet, gestoppt werden kann es aus dem Zustand ACTIVE. Der aktuelle Zustand des Bundles kann mit Bundle.getState() abgefragt werden (siehe Listing oben).

Über die beiden Methoden startBundleByName() und stopBundleByName() des BundleStarterMBean-Interfaces können die zu startenden/stoppenden Bundles über ihren symbolischen Namen und ihre Versionsnummer definiert werden. Das Suchen von Bundles über ihren symbolischen Namen ist mit Hilfe des OSGi-PackageAdmin-Service möglich. Eine Instanz dieses Services wird deklarativ mit Spring DM abfragt und anschließend der Klasse BundleStarter als Wert der Property pa übergeben:

    <osgi:reference id="packageAd" interface="org.osgi.service.packageadmin.PackageAdmin" />
 
    <bean id="bundleStarter" class="de.hs.rm.mgmt.starter.BundleStarter">
        <property name="pa" ref="packageAd"/>
    </bean>

Über die Methode PackageAdmin.getBundles() können Bundles dann entsprechend mit symbolischem Namen und Versions-Bereich gesucht werden. Rückgabewert ist ein Array von Bundle. Hier soll jedoch jeweils nur das Starten/Stoppen eines einzelnen Bundles möglich sein. Daher muss statt einem Versions-Bereich als Parameter eine genaue Version angegeben werden. Demnach darf das Ergebnis des Aufrufs von getBundles() auch nur einen Eintrag enthalten:

@Override
public boolean startBundleByName(String symbolicName, String version) {
    if (pa == null) {
        logger.error("No Package Admin Service available");
	return false;
    }

    Bundle[] bundles = pa.getBundles(symbolicName, version);

		
    if (bundles != null && bundles.length == 1 && bundles[0] != null) {
        Bundle bundle = bundles[0];

	if (bundle.getState() == Bundle.RESOLVED || bundle.getState() == Bundle.INSTALLED) {
	    try {
                bundle.start();
		return true;
            } catch (BundleException e) {
		logger.error(e.getMessage(), e);
		return false;
	    }
	}
    } else {
	logger.error("No such Bundle with symbolic Name " + symbolicName+ " and version " + version + ".");
	return false;
    }
    
    return false;
}

Fazit

Durch den Einsatz von MOSGi zum JMX-basierten Management von OSGi-Anwendungen gibt es die Möglichkeit JMX und OSGi zu verbinden. Da MBeans mit Hilfe von OSGi-Services veröffentlicht werden können, bleibt diese Management-Art trotz JMX noch "OSGi-spezifisch". Für die Unterstützung von Modi-Wechseln wurden im Rahmen dieses Projekts zwei Varianten entwickelt und implementiert. Über die 1. Variante, bei der ein Modi-Wechsel mit Hilfe von Methodenaufrufen angestoßen wird, kann genau kontrolliert und beeinflusst werden, bei welchem Bundle bzw. welcher MBean ein Modi-Wechsel veranlasst wird. Durch den Rückgabewert der changeMode()-Methode kann zudem festgestellt werden, ob der Wechsel erfolgreich durchgeführt wurde. Die 2.Variante, die JMX-Notifications zum Auslösen eines Modi-Wechsels verwendet, setzt hingegen auf eine lose Kopplung von Management-Anwendung und re-konfigurierbaren Bundles.

Eine Gruppierung von Bundles im Rahmen des Modi-Wechsels ist über den ObjectName der jeweiligen MBean möglich. Beispielweise könnte der ObjectName lauten:
de.hs.rm.mgmt:name=Foo,group=e1
Beim Auslösen eines Modi-Wechsels kann dann dieser Gruppenname (also "e1") entweder darüber entscheiden, ob die Management-Anwendung die Methode changeMode() einer ModeMBean aufgerufen werden soll oder er wird als Parameter des Konstruktors von ModeChangeNotification angegeben. Dann können die re-konfigurierbaren Bundles selbst entscheiden, ob der Aufruf zum Modi-Wechsel für sie bestimmt war. Diese Unterscheidung nach Gruppen wurde jedoch im Rahmen dieses Projektes nicht benötigt.

Literatur

[1] THE OSGI ALLIANCE: OSGi Service Platform Core Specification Release 4, Version 4.2. http://www.osgi.org/Download/Release4V42. Abruf: 21.02.2010

[2] GERD WÜTHERICH, NILS HARTMANN, BERND KOLB und MATTHIAS LÜBKEN: Die OSGi Service Platform. dpunkt.verlag, 1. Auflage, 2008.

[3] FOWLER, MARTIN: Inversion of Control Containers and the Dependency Injection pattern. http://martinfowler.com/articles/injection.html, 23 Jan 2004. Abruf: 21.02.2010

[4] Java JMX Tutorial. http://java.sun.com/docs/books/tutorial/jmx/TOC.html. Abruf:21.02.2010.