Die anpassbare Anwendung

Webanwendungen mit OSGi entwickeln

Jan Ehrhardt

Eine Open-Source-Anwendung, die durch Plug-ins erweiterbar ist, bietet einer Entwicklergemeinde viele Anreize und ein solides Fundament für Anpassungen. Anwendungen wie Firefox und WordPress zeigen das deutlich. Ihre Popularität basiert nicht zuletzt auf den unzähligen, frei verfügbaren Add-ons. In der Java-Welt gibt es mit OSGi sogar einen Standard, der für die Entwicklung eines Plug-in-Systems sehr gut geeignet ist.

W-JAX 2011

Apache Integration Day

Donnerstag, 10. November 2011

Mehr zur OSGi-Entwicklung mit Apache Karaf gibt´s auf dem Apache Integration Day der W-JAX 2011!

Die Sessions:

  • Flexibles Service Enabling mit Apache CXF
  • Apache CXF Transporte: Architektur, Design und Customizierung
  • OSGi auf dem Server. Mit Apache Karaf noch besser
  • Integration ganz einfach mit Apache Camel
  • How to build a Integration Application using Talend ESB based on Apache Projects
  • Integrationsprojekte auf dem Weg zur Continuous Delivery

Alle Infos gibt´s auf dem W-JAX-Zeitplaner!

Open-Source-Anwendungen haben es in der Regel nicht leicht, die Bedürfnisse all ihrer Benutzer zu erfüllen. Je größer die Community ist, die sich rund um eine Software gebildet hat, desto schwieriger wird es, jeden Spezialwunsch zu berücksichtigen. Die Mozilla Foundation hat daher für ihren Browser Firefox einen guten Kompromiss gefunden. Während Firefox eine weitestgehend schlanke Anwendung ist, die sich auf ihre Kernaufgabe, das Surfen im Netz, konzentriert, gibt es gleichzeitig ein Add-on-System, das es jedem erlaubt, dem Browser diverse neue Funktionen zu verschaffen. Das geht sogar so weit, dass manch ein Benutzer gar zum Add-on-Junkie wird und ohne seine erlesene Auswahl an Add-ons gar nicht mehr im Internet surfen kann. Das Modell der erweiterbaren Anwendung hat in der Open-Source-Welt natürlich Schule gemacht, und so zeigt sich, dass Popularität auch zu einem gewissen Teil von der Erweiterbarkeit abhängt.

Plug-ins in der Unternehmensanwendung

Auch Unternehmen können davon lernen und Anpassbarkeit bei der Entwicklung eigener Anwendungen berücksichtigen. Nehmen wir beispielsweise ein Mitarbeiterverzeichnis, dessen Kernaufgabe die Suche nach Mitarbeitern und die Anzeige von Personenprofilen mit Foto, E-Mail-Adresse und Telefonnummer ist. Parallel dazu hinterlassen Mitarbeiter ihre Spuren auch in anderen Unternehmensanwendungen, z. B. in der Projektmanagementsoftware, wo sie verschiedenen Projekten zugeordnet sind. Es ist daher sinnvoll, Daten aus anderen Anwendungen im Unternehmen abzugreifen und in das Mitarbeiterverzeichnis zu integrieren. Ein Plug-in-System kann dabei sehr hilfreich sein, denn es erlaubt weitestgehend die Trennung der eigentlichen Kernaufgabe von den Zusatzfunktionen der Plug-ins. Außerdem sollte es eine Abstraktionsschicht bieten, die ein generisches Modell zur Erweiterung der Anwendung bereitstellt, wodurch ein Hinzufügen neuer Funktionen schrittweise erleichtert wird.

OSGi als Basis

OSGi ist ein System, mit dem sich Java-Anwendungen modularisieren lassen. Die Bundle-genannten Module können dynamisch zur Laufzeit in einen OSGi-Container installiert werden, haben einen eigenen isolierten Classloader und sie können ihre Funktionalität in Form von OSGi-Serviceobjekten anderen Bundles zur Verfügung stellen. Für eine erweiterbare Anwendung sind das gute Voraussetzungen. Grundsätzlich gibt es zwei Möglichkeiten für die Nutzung von OSGi in der eigenen Anwendung. Die erste Variante ist die Einbettung eines OSGi-Frameworks. Seit der Version 4.2 des OSGi-Standards gibt es dafür ein einheitliches API. Listing 1 zeigt, wie dieses API benutzt wird. Das Interessante ist, dass über das Frameworkobjekt auf einen BundleContext zugegriffen werden kann, wodurch die Services anderer Bundles zur Verfügung stehen und eigene Services exportiert werden können.

Listing 1
Framework frameworkFactory = load(FrameworkFactory).iterator().next();
framework = frameworkFactory.newFramework(config);
framework.start();
framework.getBundleContext.installBundle("file:bundles/slf4j-api-1.6.1.jar");

Diese Variante ist vor allem dann sehr praktisch, wenn die Laufzeitumgebung der Anwendung klar vorgegeben ist, z. B. wenn es sich um einen Java EE Application Server handelt. Die zweite Möglichkeit ist, die Anwendung selbst als OSGi Bundle in einen OSGi-Container zu deployen. Setzt man auch schon bei der Entwicklung auf eine gewisse Modularisierung und die Verwendung von OSGi-Services, werden damit oft schon gute Ansatzpunkte für eine spätere Erweiterbarkeit geschaffen. Die Vorteile einer OSGi-basierten Anwendung haben auch die Application-Server-Hersteller längst begriffen, und so wird die kommende Generation der Application-Server neben Java EE 6 größtenteils auch OSGi unterstützen. Eine besondere Rolle unter den OSGi-Containern spielt Apache Karaf, der als Kernel von Apache Servicemix schließlich in ein eigenständiges Projekt ausgegliedert wurde. Karaf ist besonders für den Einsatz auf Servern geeignet und bietet ein paar nette Funktionen, z. B. eine erweiterbare Console für die Administration. Die Konsole kann im Übrigen über SSH angesprochen und durch eigene Befehle erweitert werden. Wer eine eigene Standalone-Anwendung entwickeln möchte, bekommt mit Karaf eine schlanke Basis und kann diese flexibel anpassen. Darüber hinaus bietet Karaf einen eigenen Deployment-Mechanismus auf Basis so genannter Features. Ein Feature fasst eine Reihe von Bundles zusammen, sodass diese als Paket in den Container installiert werden können. Des Weiteren können Features wiederum voneinander abhängen, wodurch sie gleichzeitig installiert werden können. Neben einer Reihe von Standardfeatures können zudem selbst entwickelte Features direkt aus einem Maven Repository installiert werden. Gleichermaßen wird damit eine Lösung gewonnen, wie ein zentrales Plug-in-Verzeichnis und das Abhängigkeitsmanagement realisiert werden können.

Webanwendungen als OSGi Bundles

Traditionell wird eine Webanwendung in einen Servlet-Container wie Apache Tomcat oder Eclipse Jetty deployt. In einem OSGi-Container ist das eigentlich nicht viel anders, nur dass dabei sowohl der Servlet-Container als auch die Anwendung selbst OSGi Bundles sind. OSGi nimmt dabei also die Rolle des führenden Systems ein. Wird Apache Karaf oder ein OSGi-fähiger Application-Server eingesetzt, ist ein Servlet-Container-Bundle schon vorhanden, sodass die eigentliche Herausforderung die eigene Webanwendung ist. Eine Webanwendung wird typischerweise als WAR-Archiv deployt. Innerhalb des WARs befindet sich der Web-Descriptor WEB-INF/web.xml, der Java-Bytecode in WEB-INF/classes und die externen Bibliotheken, die nicht vom Application-Server bereitgestellt werden, in WEB-INF/lib. Ein OSGi-Container lädt jedes Bundle mit einem eigenen, isolierten Classloader und erhält die nötigen Informationen über das Bundle aus der Datei META-INF/MANIFEST.MF, deren Inhalt der OSGi-Spezifikation entsprechen muss. Um eine Webanwendung als OSGi Bundle zu deployen, wird also nur eine OSGi-konforme Manifest-Datei im WAR-Archiv benötigt. In der Manifest-Datei muss auch der Klassenpfad des Bundles angegeben werden. Der Standardwert ist das Root-Verzeichnis des Bundles, was für ein normales JAR-Archiv auch völlig ausreichend ist. Bei einer Webanwendung müssen hier allerdings das Verzeichnis WEB-INF/classes und der Pfad jedes einzelnen JAR-Archivs im Verzeichnis WEB-INF/lib explizit angegeben werden, damit der Bundle Classloader diese laden kann. In einem Application-Server sind bestimmte Bibliotheken wie das Servlet API standardmäßig auf dem Klassenpfad einer Webanwendung. In einem OSGi-Container hingegen sind diese Bibliotheken OSGi Bundles und haben einen eigenen Classloader. Um auf die Klassen dieser Bundles zugreifen zu können, müssen die benötigten Packages einzeln importiert werden, was auch im Manifest angegeben werden muss. Die daraus resultierende Suche nach all diesen Packages und Bibliotheken sowie die Pflege des Manifests sind entsprechend aufwändig. Dadurch stellt sich schnell die Frage, ob es sich überhaupt lohnt. Glücklicherweise lässt sich ein OSGi-konformes Manifest bequem generieren, weshalb der Aufwand stark sinkt. Für Maven gibt es z. B. das Maven-Bundle-Plug-in des Apache-Felix-Projekts, das diese Aufgabe übernimmt. Listing 2 zeigt eine Beispielkonfiguration für ein Spring-MVC-Projekt.

Listing 2
org.apache.felixmaven-bundle-plugin
  ...
  
        org.ducktools.samples.plugins.web
      Jan Ehrhardt
        javax.servlet.*,
        org.apache.commons.logging,
        org.slf4j;version="[1.5.0,1.7.0)"
      
        javax.*,
        org.xml.sax,
        org.xml.sax.*,
        org.w3c.*
      
        .,
        WEB-INF/classes
      WEB-INF/lib*;scope=compile|runtimetruemein_context_pfadmein_context_pfad

Die Spring JARs sind dabei im WAR-Archiv enthalten und das Maven-Bundle-Plug-in fügt jedes JAR automatisch in das Manifest ein. Durch das Verwenden von Wildcards bei den Package-Importen fügt das Plug-in sämtliche Subpackages hinzu. In dem Beispiel werden zugleich verschiedene Logging APIs importiert, was den Vorteil hat, dass die bereitgestellten Bundles verwendet werden. Die meisten OSGi-Container stellen einen eigenen Logging-Service bereit, den man auf diese Weise für die eigene Anwendung nutzen kann. Zu guter Letzt wird dem Manifest noch der Eintrag Web-ContextPath hinzugefügt, der den Pfad der Anwendung im Servlet-Container festlegt. Durch diesen Eintrag erkennt der OSGi-Container außerdem, dass es sich um eine Webanwendung handelt, die einen Web-Descriptor hat und Servlets bereitstellt. Natürlich möchte man das Ganze auch ausprobieren, was besonders gut mit dem Maven-Pax-Plug-in geht. Listing 3 zeigt eine einfache Pax-Konfiguration für eine Webanwendung.

Listing 3
org.ops4jmaven-pax-pluginfelixcompendium,web,wartrue

Mit dem Befehl mvn clean install pax:run lässt sich die Anwendung starten. Leider erkennt das Plug-in den Web-ContextPath-Eintrag nicht richtig. Es merkt zwar, dass es sich um eine Webanwendung handelt, stellt diese dennoch unter einem ganz anderen Pfad zur Verfügung. Das kann dadurch behoben werden, dass dem Manifest zusätzlich der proprietäre Webapp-Context-Eintrag hinzugefügt wird, der denselben Wert erhält. Damit ist die Anwendung nun unter http://localhost:8080/mein_context_pfad erreichbar.

Geschrieben von
Jan Ehrhardt
Kommentare

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht.