Gut eingepackt
Die Java 2 Enterprise Edition hat einen bemerkenswerten Siegeszug hinter sich. Jedoch arbeiten viele Firmen nicht mit J2EE im Rohzustand, sondern setzen darauf basierende Frameworks ein. Diese ergänzen die Plattform und verstecken deren Komplexität unter einer Abstraktionsschicht.
- Philipp Oser
Mittlerweile existieren zahlreiche Frameworks für die Java 2 Enterprise Edition (J2EE) (siehe Tabelle „Freie und kommerzielle Frameworks“). Das liegt vor allem daran, dass viele für die Java 2 Standard Edition (J2SE) konzipierte Frameworks auch mit dem großen Ableger auf sinnvolle Weise kooperieren. In diesem Artikel soll es jedoch nur um die Varianten für das Enterprise-Umfeld gehen, die Unternehmen zusätzlich, neben oder als Schicht über der J2EE einsetzen können. Diese Rahmenwerke sollten nicht nur Teilprobleme lösen, sondern die Anforderungen der Firmen weitgehend abdecken. Unerlässlich ist, dass alle eingesetzten Komponenten in der Lage sind, zusammenzuarbeiten.
Einen kompletten Überblick über derartige Produkte zu geben, ist kaum möglich. Über die meisten existieren lediglich spärliche Informationen und viele Anwender haben ihr privates J2EE-Framework, etwa die Credit Suisse und die Schweizerischen Bundesbahnen.
Es gibt etliche Motive für den Einsatz solcher Frameworks. Eines davon ist, Hersteller- und Spezifikationsabhängigkeiten in den Griff zu bekommen. Die Abstraktionsschicht, die ein Framework zur Verfügung stellt, hilft auch dabei, Fehler und Unzulänglichkeiten der darunter liegenden Plattformen zu korrigieren beziehungsweise abzustellen. J2EE bietet beispielsweise wenig Unterstützung für lange dauernde Verarbeitungen (Batch Jobs), Scheduling (ab EJB 2.1 gibt es einfachen Support) sowie für Notifikation (Benachrichtigung), transaktionales Journaling, Locking oder nicht invasive Performanzmessungen. Reine Anwenderfirmen können sich normalerweise auf einen J2EE-Hersteller beschränken, Serviceunternehmen müssen jedoch oftmals alle Implementierungen ihrer Kunden kennen. Nur über Abstraktionen ist es möglich, Code und Konfigurationen zwischen verschiedenen J2EE-Plattformen auszutauschen, ohne diese jedes Mal speziell anzupassen.
Abstraktionen helfen auch dabei, die bei der Programmierung mit EJBs aus dem verletzten Prinzip des „Single point of Reference“ entstehenden Probleme zu lösen: Stehen Informationen wie die Signaturen der Business-Methoden oder die Attribute der Entity Beans nicht an einem Ort, müssen sie in mehrere Dateien verteilt werden. Die Spezifikation fordert zudem, dass remote zugängliche Methoden RemoteExceptions werfen, was man selten auf allen Business Interfaces möchte (EJB 3.0 soll dies abstellen).
Auf Umwegen zum Einfachen
Einige der Core J2EE Patterns dienen lediglich dazu, solche EJB-Spezialitäten zu umgehen. Zu den Patterns gehören zum Beispiel der Service Locator, der zuständig ist für das Holen von EJB Beans, Business Delegate, das dem Business Interface seine EJB-Spezifika nimmt, und die Home Factory, die die Home Interfaces finden soll. Bessere Generalisierungen können diese Patterns überflüssig machen. Außerdem ist es oft gewünscht, EJBs außerhalb eines Container laufen zu lassen, etwa für einfache Tests, für schnellere Entwicklungs-Roundtrips oder wenn man auf ihn ganz verzichten will. Leider funktionieren EJB Session Bean Interfaces nur innerhalb eines Containers. Auch dafür existieren Service-Abstraktionen, die dafür sorgen, dass die EJBs auch außerhalb des Containers lauffähig sind.
Die Aufzählung zeigt eine Menge Potenzial für Verbesserungen der J2EE. Typischerweise bieten entsprechende Frameworks eine Sammlung von Dokumenten, die die verfeinerte Referenzarchitektur beschreiben, über Best Practices berichten und Coding Guidelines bereitstellen. Da die J2EE dem Entwickler zu viele Freiheiten einräumt, kann die Erfüllung der Vorgaben des Frameworks den Start eines Projekts beträchtlich beschleunigen. Da die Entwickler dann nach einem bewährten Plan arbeiten, erreichen sie, dass die Endprodukte homogener sind, also sich innerhalb der Firma nicht großartig unterscheiden.
Normalerweise entwirrt der Programmierer die Verwicklungen, die die Verletzung des Single Point of Reference erzeugt, mit Codegeneratoren. Die notwendigen Metadaten als Input bekommen diese auf verschiedenen Wegen: Entweder über Annotationen (Xdoclet), über GUI-Dialoge (IDE) oder über UML-Notationen in einem MDA-Ansatz. Fehlende Funktionen fügt der Entwickler über externe Bibliotheken hinzug. Objektrelationales Mapping unterstützen die Frameworks in der Regel, da viele Anwender mit den Entity Beans unglücklich sind. Letztere gelten als zu kompliziert, funktionieren nur im EJB Container und können Performance-Engpässe verursachen.
Als nützlich haben sich Erweiterungsfunktionen für den Object Request Broker (ORB) erwiesen. Dazu gehören Interzeptoren und implizites Context Passing. Erstere erlauben es, vor, nach beziehungsweise anstatt von ORB-Aufrufen eigene Aktionen anzustoßen. Interzeptoren lassen sich vielfältig einsetzen, etwa für Sicherheitschecks, für das Auditing, für Performanzmessungen, um RuntimeExceptions zu notifizieren oder um Serviceaufrufe zwischenzuspeichern. Sie sind mittlerweile recht populär, und es gibt einige Mechanismen, um sie in Java-Programme einzufügen. Interzeptoren werden entweder zur Build-Time (zum Beispiel über Aspect/J) oder zur Laufzeit (etwa über CGLib) im Code verankert.
Durchgeschleuste Informationen
Über implizites Context Passing kann der Entwickler eigene Daten in den ORB-Aufrufen übergeben, beispielsweise die aktuelle Locale, Performanzmessergebnisse oder den jeweiligen Mandanten. Somit muss er in den Service-Interfaces nicht überall zusätzliche Parameter einfügen, wenn er solche Werte mitliefern will.
Der Entwickler programmiert gegen Interfaces, die Implementierungen der Dienste wählt er über eine separate Konfiguration. Um die Services im Code zu finden, stehen ihm zwei Möglichkeiten zur Verfügung: Er holt sie sich über eine zentralisierte Lookup-Methode oder er definiert Methoden, über die das Framework sie übergeben kann. Für Letzteres kann er Konstruktoren, explizite Setter-Methoden oder Interfaces einsetzen.
Martin Fowler nennt die Lookup-Methode „Service Locator“, die andere Variante „Dependency Injection“ (siehe Kasten „Infos im Web“). Beide Ansätze (Pull/Push) verwendet man auch für die Konfigurationsparameter; so lassen sich diese bequem vom Code trennen. Ebenfalls nützlich sind Abstraktionen für die Aufteilung des Frameworks in Module, die sich separat programmieren und einrichten lassen. Obsolete Teile kann der Entwickler somit leicht entfernen. Frameworks können außerdem Firmenstandards unterstützen und bei der Integration von vorhandenen Systemen helfen.
Drei J2EE-Frameworks - Spring, Keel und Leaf - werden im Folgenden vorgestellt. Die ersten beiden sind Open-Source Projekte, das dritte verwenden wir intern.
Den vollständigen Text sowie zwei weitere Artikel finden Sie in der aktuellen Print-Ausgabe der iX. (jd)