OSGi-Scripting mit Groovy

Die OSGi-Plattform entwickelt sich immer stärker zu einer Plattform für Enterprise-Java-Anwendungen. In diesem Umfeld besitzt der Aspekt des Remote-Managements des Containers und darin laufender Anwendungen eine hohe Bedeutung. Klassische Java-EE-Application-Server bieten hierfür neben grafischen Oberflächen mächtige Scripting-Schnittstellen an.

Logo MAEXOIn diesem Bereich ist es derzeit um OSGi-basierte Anwendungen schlecht bestellt – neben Telnet-basierten proprietären Konsolen existieren zwar Web-Konsolen (z. B. für Apache Felix), eine sinnvolle Automatisierung der Installation von Bundles oder Konfiguration von Services ist damit nicht zu erreichen. Der Einsatz von MAEXO [1] in Kombination mit Groovy schafft hier Abhilfe. Wie dies funktioniert, soll im folgenden Beispiel veranschaulicht werden.

Ein Fall für das Management

Folgendes Szenario soll verwirklicht werden: In einem leeren OSGi-Container soll eine neue Webanwendung installiert werden. Neben der eigentlichen Anwendung muss dafür ein HttpService zur Verfügung gestellt werden. Dafür fallen folgende Management-Aktivitäten an:

  • Abfrage des Container-Zustandes
  • Installation der notwendigen Bundles (Apache Felix HTTP Service [4], basierend auf Jetty)
  • Konfiguration des zu verwendenden Ports (8085)
  • Start des Bundles, welches den HttpService publiziert
  • Überprüfung der Verfügbarkeit des HttpService

Die Anwendung wird in einem Rechenzentrum gehostet, so dass kein direkter Zugang möglich ist und das Management per JMX abgewickelt werden muss. Glücklicherweise sind im OSGi-Container die entsprechenden MAEXO-Bundles bereits installiert und aktiviert.

Applikation und Werkzeugkasten

Zur Nachvollziehbarkeit des geschilderten Szenarios kann das mit MAEXO ausgelieferte Beispiel im Ordner „samples/basic“ verwendet werden. Es stellt einen vorkonfigurierten Equinox- bzw. Felix-Container zur Verfügung, welcher alle benötigten MBeans beinhaltet. Nach dem Start über das Shell-Skript kann mittels JConsole eine Verbindung zum MBean-Server des Containers hergestellt werden. Da die Anwendung im beschriebenen Szenario nicht lokal verfügbar ist, muss die Verbindung über eine JMX-Connection-URL hergestellt werden. Für das vorliegende Beispiel sieht diese URL folgendermaßen aus:

service:jmx:rmi:///jndi/rmi://​localhost:1099/jmxrmi.

Nach erfolgreichem Verbindungsaufbau erscheint im Reiter MBeans eine Domain „com.buschmais.maexo“, unter welcher von MAEXO verschiedene MBeans zur Verfügung gestellt werden (Abbildung 1).

JConsole mit MAEXO-MBeansAbbildung 1: JConsole mit MAEXO-MBeans

Für das Erstellen und Ausführen der Admin-Skripte wird weiterhin eine Groovy-Installation benötigt. Das entsprechende ZIP-Archiv bzw. ein Windows-Installer können von der Homepage des Projektes [3] bezogen werden. Die ersten Management-Aktionen können direkt in der GroovyConsole ausgeführt werden. Diese Konsole ist in der Installation enthalten und wird über ein Shell-Skript im $GROOVY_HOME/bin-Verzeichnis gestartet.

Der erste Kontakt

Zunächst muss eine Verbindung zum entfernten MBean-Server aufgebaut werden. Dazu wird die o. g. JMX-Connection-URL benötigt.

url = new javax.management.remote.JMXServiceURL(
   'service:jmx:rmi:///jndi/rmi://localhost:1099/jmxrmi');

con = javax.management.remote.JMXConnectorFactory
   .connect(url).MBeanServerConnection;

con.getAttribute(new javax.management.ObjectName(
   'com.buschmais.maexo:type=Framework'),'vendor');

Nach dem Ausführen dieser Anweisungen in der GroovyConsole ([Strg+R]) wird von Groovy der letzte Rückgabewert, in diesem Fall der Name des Herstellers des OSGi-Containers, angezeigt. Dieser wird durch ein Attribut einer MBean zur Verfügung gestellt, welche das OSGi-Framework repräsentiert und über den Namen „buschmais.maexo:type=Framework“ erreichbar ist.

Es besteht die Möglichkeit, eine Groovy-Console direkt innerhalb der JConsole auszuführen. Unter [5] ist eine entsprechende Anleitung zu finden. Daraus ergeben sich zwei Vorteile: Zum einen kann im Skript die bereits existierende Verbindung zum entfernten MBean-Server genutzt werden:

con = plugin.context.MBeanServerConnection;

Andererseits kann innerhalb der JConsole je nach Anwendungsfall bzw. Nutzer-Präferenz durch einfaches Wechseln des Reiters mit der GUI oder mit der Script Shell gearbeitet werden. Ein wichtiger Unterschied zur Groovy-eigenen Konsole besteht darin, dass die Eingaben unmittelbar nach jeder Zeile interpretiert werden – das kann situationsabhängig von Vor- oder Nachteil sein.

Kleines Werkzeug, große Wirkung

Als nächstes soll die Menge installierter Bundles abgefragt werden. Unter Nutzung der verfügbaren Java-APIs und der durch MAEXO bereitgestellten Framework-MBean sähe dies wie folgt aus:

con.getAttribute(
   new javax.management.ObjectName(
      'com.buschmais.maexo:type=Framework'), 
   'bundles');

Leider liefert diese umständliche Anweisung einen für den Nutzer unbrauchbaren Rückgabewert: Es wird ein ObjectName[]-Array anderer MBeans zurückgeliefert, welche die Menge installierter Bundles repräsentieren. Zur Vereinfachung wird daher eine Hilfsmethode geschaffen, welche aus einem übergebenen ObjectName einen Proxy erzeugt, der die entsprechende MBean im OSGi-Container repräsentiert:

def asMBean(objectName) {
   return new groovy.util.GroovyMBean(con, objectName);
}

Damit können Attribute und Methoden in objektorientierter Art verwendet werden. Die Abfrage des Framework-Herstellers bzw. der installierten Bundles gestaltet sich nun wie folgt:

framework = asMBean('com.buschmais.maexo:type=Framework');
println framework.vendor;
framework.bundles.each {
   bundle = asMBean(it);
   println it.getKeyProperty('name') + ' ' + bundle.stateAsName;
}

Die letzte Anweisung ermittelt das ObjectName[]-Array der MBeans, welche die installierten Bundles repräsentieren. Mittels „each{ … }“ wird darüber iteriert und für jedes Element der Symbolic-Name und der jeweilige Zustand des Bundles ausgegeben.

Vier Bündel und ein Service

Als nächste Aufgabe steht die Installation und Konfiguration der Bundles des Apache Felix HttpService auf der Agenda. Dies ist mit Hilfe des erzeugten MBean-Proxys „framework“ und der von ihm bereitgestellten Methode jetzt einfach zu realisieren. Es werden der Einfachheit halber Artefakte aus einem öffentlichen Maven2-Repository verwendet.

framework.installBundle("http://repo2.maven.org/maven2/" +
   "org/apache/felix/org.apache.felix.log/1.0.0/" +
   "org.apache.felix.log-1.0.0.jar");

framework.installBundle("http://repo2.maven.org/maven2/" +
   "org/apache/felix/org.apache.felix.http.api/2.0.4/" +
   "org.apache.felix.http.api-2.0.4.jar");

framework.installBundle("http://repo2.maven.org/maven2/" +
   "org/apache/felix/org.apache.felix.http.base/2.0.4/" +
   "org.apache.felix.http.base-2.0.4.jar");

jettyObjectName = framework.installBundle(
   "http://repo2.maven.org/maven2/" +
   "org/apache/felix/org.apache.felix.http.jetty/2.0.4/" +
   "org.apache.felix.http.jetty-2.0.4.jar");

jettyBundle = asMBean(jettyObjectName);
jettyBundle.stateAsName;

Die letzte Anweisung ermittelt den Zustand des Bundles, welches den HttpService bereitstellt. Unmittelbar nach der Installation ist dies „INSTALLED“.

Für die gewünschte Konfiguration des HttpService wird eine Instanz des ConfigurationAdmin-Services benötigt; entsprechende MBeans werden von MAEXO zur Verfügung gestellt. Um diese zu erhalten, muss die MBeanServer-Verbindung abgefragt werden:

con.queryNames(new javax.management.ObjectName(
   'com.buschmais.maexo:type=ConfigurationAdmin,id=*'),null);

Als Ergebnis wird ein ObjectName-Set geliefert, zum Beispiel [com.buschmais.maexo:type=​ConfigurationAdmin,id=78]. Der erhaltene ObjectName – genau genommen seine String-Repräsentation – wird verwendet, um einen MBean-Proxy für diesen Service („cfgAdmin“) zu erzeugen. Mit Hilfe des ConfigurationAdmin-MBeans kann nun auf die Konfiguration des Jetty-Bundles („cfg“) zugegriffen werden.

cfgAdmin=asMBean(
   'com.buschmais.maexo:type=ConfigurationAdmin,id=78');

cfg=asMBean(
   cfgAdmin.getConfiguration('org.apache.felix.http',
                             jettyBundle.location));

Das Setzen der Konfigurationsparameter erfolgt daraufhin am MBean-Proxy der Konfiguration; mit dem Aufruf der Methode „update“ wird diese Konfiguration publiziert:

cfg.setString('org.osgi.service.http.port', '8085');
cfg.update();

Als letzte Schritte stehen lediglich das Starten des Jetty-Bundles sowie die Überprüfung des Vorhandenseins des HttpServices aus:

jettyBundle.start();
jettyBundle.registeredServices.each {
   println it.getKeyProperty('name');
}

Fazit

Der skizzierte Anwendungsfall hätte problemlos über eine grafische Oberfläche, wie sie von JConsole & Co. angeboten wird, abgearbeitet werden können. Die Korrektheit und Nachvollziehbarkeit der solcherart vorgenommenen Management-Aktivitäten hängt dabei stark vom Konzentrationsvermögen des Anwenders ab: Es kann nur bedingt garantiert werden, dass auf verschiedenen Instanzen in unterschiedlichen Umgebungen wirklich die gleichen Aktionen ausgeführt werden. Abhilfe kann durch die Verwendung vordefinierter Skripte und dem damit verbundenen Automatisierungsgrad erreicht werden. Begriffe, wie Reproduzierbarkeit und Testbarkeit, die in anderen Bereichen der Softwareentwicklung zum Standardvokabular zählen, halten damit Einzug im Bereich des OSGi-Laufzeitmanagements. Für komplexere als das geschilderte Szenario ist dies eine unverzichtbare Voraussetzung. Die Basis dafür stellt das Vorhandensein entsprechender Werkzeuge dar. Im vorliegenden Beispiel sind dies Groovy und die durch MAEXO bereitgestellten MBeans.

Kurzportrait zu MAEXO

MAEXO (Management Extensions for OSGi) ist ein Open-Source-Framework, welches das Laufzeitmanagement von OSGi-basierten Anwendungen ermöglicht. Es stellt sowohl die Infrastruktur zur Integration der Java Management Extensions (JMX) in den OSGi-Container als auch entsprechende Managed Beans (MBeans) zur Verwaltung häufig genutzter OSGi-Ressourcen zur Verfügung. Dazu zählen z. B. installierte Bundles und laufende Services. Diese können so unter Zuhilfenahme beliebiger JMX-Management-Konsolen von entfernten Rechnern überwacht bzw. gesteuert werden.

Darüber hinaus stellt MAEXO ein Programmiermodell zur Verfügung, das die Implementierung eigener MBeans samt Verwaltung ihres Lebenszyklus unterstützt. Der grundlegende Ansatz besteht in der Nutzung der Service-Infrastruktur des OSGi-Containers und der transparenten Registrierung aller MBeans und MBean-Server als OSGi-Services.

Quellen

[1] MAEXO, https://github.com/buschmais/maexo
[2] VisualVM, https://visualvm.java.net/
[3] Groovy, http://groovy.codehaus.org/
[4] Apache Felix HTTP Service,
http://felix.apache.org/site/apache-felix-http-service.html
[5] Groovier jconsole!,
https://blogs.oracle.com/sundararajan/entry/groovier_jconsole

Diesen Artikel können Sie sich auch als PDF herunterladen:
Download OSGi-Scripting mit Groovy

Das vollständige Groovy-Skript zum Installieren der Jetty-Bundles:
InstallJetty.groovy

Kommentare sind abgeschaltet.