Seit der Veröffentlichung von JEE 6 scheinen die Befürworter des JEE-Standards gegenüber den Anhängern der Spring-Welt im Vorteil zu sein. Schließlich ist die JEE-Plattform inzwischen technologisch ausgereift. Da stellt sich die Frage, warum man von diesem Standard abweichen soll.
Konkurrenz zwischen verschiedenen Konzepten führt meistens zu immer besseren Lösungen auf beiden Seiten. Daher kann man als Java-Entwickler froh sein, dass es in der Java-Welt viele gute Produkte gibt. In der Welt der Frameworks für die Enterprise-Entwicklung konkurrieren derzeit vor allem JEE 6 und das Spring-Ökosystem. Und natürlich streiten die Befürworter beider Lager mit Vehemenz, welche Technologie die bessere ist.
Wenn man jedoch liest, mit welchen Argumenten die beiden Lager teilweise streiten, bekommt man den Eindruck, dass viele Argumente an den Anforderungen der Praxis vorbei gehen. Ausgereifte Standards sorgen nämlich noch lange nicht für ausgereifte Produkte und einen einfachen Upgrade-Pfad. Darum möchte ich anhand eines Erfahrungsberichts zeigen, mit welchen inhärenten Problemen man in der JEE-Welt zu kämpfen hat.
Zunächst einmal kurz zur Grundlage dieses Berichts: Ich arbeite schon lange und gerne mit Spring. Mein aktuelles Projekt ist zur Abwechslung jedoch Spring-frei. Stattdessen konnte ich auf der grünen Wiese eine JEE6/EJB 3.1/JPA2-Anwendung aufsetzen. Mangels großer Auswahl war Glassfish als Applikationsserver gesetzt.
Das Ziel: Update von Glassfish 3.1.1 auf 3.1.2
Nachdem das Projekt initial mit Glassfish 3.1.1 gestartet und produktiv gegangen ist, wollten wir das Update auf die Version 3.1.2 durchführen. Die Motivation kam u.a. daher, dass das Deployment unserer Anwendung inzwischen gut zehn Minuten dauerte und dass die neue EclipseLink-Version (ein Update von 2.3.0 auf 2.3.2) einige störende Bugs zu beheben versprach.
Bei einem ersten Test stellte sich heraus, dass mit der neuen Version das Deployment in rund 1/5 der Zeit durchläuft und dass der Embedded-Glassfish, den wir zur Entwicklung verwenden, in rund 1/3 der Zeit startet. Die Aktualisierung des Applikationsservers schien also eine gute Idee zu sein.
Das Update an sich war einfach. Für die Entwicklungsumgebung mussten wir nur die Versionsnummern im Maven-POM austauschen. Und Standalone führt das pkg-Tool das Update in wenigen Minuten durch.
Problem I: Neue Bugs in Eclipselink
Als erstes waren die Backend-Unit-Tests an der Reihe. Mit Hilfe des Embedded-Glassfish hatten wir viele Tests geschrieben, die prüfen, ob die deployten EJBs wie gewünscht funktionieren.
Der erste Test-Durchlauf nach dem Update brachte große Ernüchterung und viel Rot mit sich!
Die Probleme ließen sich auf fünf verschiedene Fehlerarten eingrenzen. Zwei davon waren einfach zu beheben: Eclipselink legt in der neuen Version die JPA-Spezifikation genauer aus als zuvor. An sich ist es sicher nicht nicht verkehrt, eine Spezifikation genauer zu befolgen. Aber hier wurde in Kauf genommen, dass durch ein Minor-Update (Eclipselink 2.3.0 zu 2.3.2) bestehende Anwendungen nicht mehr laufen!
Die übrigen drei Fehlerarten waren neu eingeführte Bugs in EclipseLink beim Ausführen von JPA2-Criteria-Queries. Syntaktisch falsch generiertes SQL war genauso dabei wie semantisch falsch generiertes SQL. Die Suche nach geeigneten Workarounds für diese Fehler benötigte rund drei Arbeitstage. Schließlich sollte der Code zunächst in beiden Glassfish- bzw. EclipseLink-Versionen laufen.
Am Ende liefen alle Backend-Tests durch und wir konnten unsere Anwendung endlich hochfahren. Zumindest konnten wir es versuchen.
Problem II: Neu eingebundene, aber veraltete Bibliotheken in Glassfish
Der Embedded-Glassfish-Server besteht aus einem einzigen Jar mit allen Klassen aller verwendeter Bibliotheken. Hier wurde beim Update von 3.1.1 auf 3.1.2 die Bibliothek JodaTime hinzugefügt – leider in einer veralteten Fassung. Denn unsere Anwendung nutzt diese Bibliothek ebenfalls, aber mit der aktuellsten API. Nach einiger Recherche fanden wir den Workaround: Wir mussten unsere Fassung der JodaTime-Bibliothek in ein Verzeichnis kopieren, auf das per System-Property java.endorsed.dir verwiesen wird.
Jetzt endlich lief unsere Anwendung wieder und wir glaubten am Ziel zu sein. Doch dann begann unser Product-Owner das Testen.
Problem III: Neue Bugs im Web-Core von Glassfish
Schon nach kurzer Zeit erklärte er uns, dass weder das Hochladen von Bildern noch unsere Ajax-basierte Validierung funktionieren.
Eine lange Debugging-Session in die Tiefen des Glassfish-Servers später war klar, dass die neue Glassfish-Version einen Bug enthält, der dazu führt, dass Multi-Form-Uploads nicht mehr funktionieren. Erfreulicherweise gibt es dazu auch einen Fehlerbericht und einen Bugfix bei Oracle (für alle Betroffenen: http://java.net/jira/browse/GLASSFISH-18444). Nur: Offiziell ist dieser Bugfix in der freien Version der 3.1.x-Reihe nicht erhältlich! Und das obwohl dieser Bug das Bilder-Hochladen in vielen Java-Webframeworks unterbindet. Immerhin hat ein Oracle-Entwickler ein gepatches Jar-Archiv an den Fehlerbericht angehängt, der das Problem beseitigt.
Das heißt: Wir mussten nach dem Update von 3.1.1 auf 3.1.2 alle Glassfish-Instanzen manuell mit einem neuen Jar für web-core patchen und konnten nur hoffen, dass dieser Patch keine weiteren Nebenwirkungen besitzt. Für den Embedded-Glassfish half dieser Patch natürlich nicht – hier mussten wir den Code manuell patchen und eine von uns aktualisierte Fassung des Jar-Archivs in unser Nexus-System einspielen.
Fazit: Zu viele Abhängigkeiten, zu viel Komplexität, die sich nicht aufbrechen lässt
Eigentlich wollten wir ja nur von den Verbesserungen beim Deployment profitieren. Im Endeffekt haben wir über eine Mannwoche Aufwand investieren müssen, um unsere Anwendung wieder zum Laufen zu bekommen – und das nur bei einem Wechsel in der dritten Stelle der Versionsnummer!
Ich möchte dem Glassfish-Team beileibe keinen Vorwurf machen! An sich läuft der Server rund und lässt sich gut bedienen und wir haben praktisch keine Probleme im Produktivbetrieb.
Das Grundproblem liegt in der inhärenten Komplexität von JEE-Applikationsservern.
Die JEE-Advokaten bringen häufig das Argument, dass es trivial sei, ein JEE-Projekt aufzusetzen: Server installieren und loslegen. Bei einem Spring-Projekt hingegen muss man immer wieder neu entscheiden, welche Bibliotheken in welchen Versionen integriert werden soll.
Aus diesem Grund dauert es auch mehrere Tage, bis man ein Spring-Projekt für eine komplexe Enterprise-Anwendung aufgesetzt hat – jedenfalls, wenn man dies manuell erledigt und sich nicht durch Spring Roo helfen lässt. Aber dafür hat man anschließend die volle Kontrolle über das eigene System bzw. die verwendeten Komponenten.
Tausche ich freiwillig Dutzende Teilkomponenten meines Systems gleichzeitig aus, wenn eine dieser Teilkomponenten ein Problem hat, das in einer neuen Version behoben wurde? Natürlich nicht! Wenn die neue Version der Persistenz-Bibliothek Fehler hat, warte ich, bis die Fehler behoben sind und aktualisiere diese Bibliothek erst dann. Aktualisierungen für das Web-Framework sind davon jedenfalls nicht betroffen.
Anders bei Applikationsservern: Auch wenn es Beispiele für Server gibt, die man modulweise aktualisieren kann: Welcher Betrieb eines Großunternehmens ist dazu bereit und unterstützt ein solches selektives Vorgehen? Hier herrscht nur die Devise: Ganz oder gar nicht und wenn, dann Augen zu und irgendwie durch!
Für die Praxis ist dieses Vorgehen mit einem erheblichen Risiko verbunden, weswegen es leider Großunternehmen gibt, die nach wie vor auf der Basis veralteter (JEE-)Standards und alter Applikationserver weiterentwickeln. Das Risiko des Umstiegs auf neuere Versionen will niemand tragen bzw. will sich niemand leisten.
In meinem aktuellen Projekt war unsere Präferenz klar: Updaten, sowie es irgendwie geht, um die Lücke zu den zukünftigen Aktualisierungen nicht zu groß werden zu lassen. Aber die Menge an Schweiß, die uns dieses eigentlich kleine Update gekostet hat, habe ich bisher noch in keinem Spring-Projekt für eine ähnliche Aufgabe vergossen!
Immerhin: Das Deployment geht jetzt viel schneller als zuvor, und weitere Fehler sind nicht aufgetaucht. Die Anwendung läuft stabil und schnell, auch unter Last!
Trotzdem: Erwarte ich von JEE-Applikationsservern zu viel? Was sind eure Erfahrungen?
Nachtrag: Die hier beschriebenen Probleme habe ich in Artikel JEE 6 und die Applikationsserver – ein Blick aus zwei Winkeln tiefergehend analysiert.