Top 10 Fehler beim Einsatz von J2EE
Java, EJBs und J2EE sind keine neuen Technologien mehr. Durch ihrer Komplexität sorgen sie dennoch dafür, dass
altbekannte Fehler wiederholt werden.
Die Fehler sind in 2 Kategorien aufgeteilt: Architektur- und Projektfehler. Einige der aufgelisteten Punkte
sind nicht unbedingt J2EE spezifisch und daher allgemein gültig. Die Erklärung baut
immer auf die speziellen Eigenarten von EJBs und J2EE auf.
Die folgende Liste erhebt keinen Anspruch auf
Vollständigkeit, sondern bietet einen Erfahrungsüberblick aus zahllosen J2EE Projekten. Sie ist daher nicht
als allgemeine Anleitung zum Aufbau von J2EE Anwendungen zu verstehen.
J2EE Architekturfehler
1. Alles ist eine EJB
Sobald die grundlegende Arbeitsweise von EJBs vom Entwickler verstanden wird, verfällt er oft in dem Vorgehen,
alles als eine Session Bean oder Entity Bean zu implementieren. Das ist ein großer Irrtum, denn EJBs sind
kein grundsätzliches Implementierungsparadigma, sondern eine Framework zur Remotekommunikation und Verteilung von
Komponenten. Wird dieses Framework überstrapaziert sind die Folgen meist ein unübersichtliches Design und schlechte
Performance. Der überwiegende Teil eines Systems sollte daher aus klassischen POJOs (Plain Old
Java Objects) bestehen.
2. Kein vernünftiges Schichtenmodell
Die gute Strukturierung von Software ist ein grundsätzliches Problem, das nicht nur in der J2EE Welt vernachlässigt
wird. Eine minderwertige Strukturierung bedeutet einen hohen Entwicklungsaufwand und schlechte Wartbarkeit. Typischerweise
werden im J2EE - Bereich folgende Fehler gemacht:
- Keine Trennung zwischen Präsentationsschicht und Businesslogic. Beispiele hierfür sind mit Java-Code überfrachtete
JSP Seiten sowie das Servlet, in der die ganze Anwendungslogik implementiert wird.
- Keine genaue Festlegung, welche Schicht remote aufrufbar ist, und welche nicht.
3. Feingranulare Remoteaufrufe
Verteilte Anwendungen leiden oft unter folgenden Problemen:
- Die Kosten für einen Remoteaufruf werden unterschätzt
- Es ist dem Entwickler nicht klar, was alles einen Remoteaufruf verursacht
In der J2EE-Welt kommt es daher meist zu folgenden Fehlern:
- Es existiert kein Business-Delegate, der die Koordination von EJBs für einen Client übernimmt.
- Entity Beans werden direkt vom Client verwendet
- Heterogene EJB-Verteilung auf unterschiedlichen Rechners eines Clusters.
4. Verletzung des Parallelitätsprinzips
EJBs sind dafür gedacht, dass sie von vielen Benutzern gleichzeitig verwendet werden. Meist bedeutet das,
dass der Application Server viele EJB Instanzen und viele Threads verwalten muss. Daraus schöpft eine J2EE
Anwendung Ihre Kraft, denn (quasi-)parallel laufende Objekte können vom Betriebsystem gut auf vorhandene
CPU-Zeit verteilt werden.
Dies bedeutet, dass Singletons und synchronisierte Komponenten für J2EE Anwendungen mit großer Vorsicht zu verwenden sind: Alle
parallel arbeitenden Benutzer müssen durch dieses Nadelöhr. Benötigt diese Komponente viel Zeit, warten
währenddessen alle Anderen. Wie viel Zeit eine Komponente tatsächlich benötigt, lässt sich in der Regel nur mit
einem realistischen Lasttest genau feststellen.
5. Langläufige Requests und Transaktionen
Klassische J2EE Anwendungen arbeiten mit einem synchronen Aufrufmodell: Eine Benutzeranfrage erreicht das System
und der Benutzer wartet so lange, bis das Ergebnis der Anfrage zurückgeliefert wird. Dieses Modell funktioniert sehr gut,
solange man sich in der Entwicklungsphase befindet und nicht mehr als ein Benutzer (der Entwickler) mit der
Anwendung arbeitet. Problematisch wird es, sobald das System unter Last steht, und man nicht genügend
darauf geachtet hat, dass Prozesse und deren Transaktionen kurzlebig sind. Die Folge: Durch Timeouts ist
praktisch kein Benutzer mehr in der Lage irgend etwas zu tun.
Für dieses Problem gibt es viele mögliche Lösungen: Optimierung der langläufigen Transaktionen, Beschränkung der
gleichzeitig laufenden Prozesse, asynchrone Entkopplung der Komponenten (z.B. durch JMS). Wichtig dabei ist, so früh
wie möglich potentielle Langläufer zu identifizieren. Die Anpassung der Architektur ist dann meist problemlos möglich.
J2EE Projektfehler
1. Kein ausreichendes Architekturdesign durchgeführt
J2EE unterstützt die Anwendungsentwicklung in vielen Bereichen und bringt eine ganze Reihe von Modelllösungen mit.
Dies verleitet zu der Ansicht, dass ein Design nicht mehr nötig ist: J2EE wird als bereits fertige Architektur interpretiert.
Im Grunde genommen ist es genau umgekehrt, denn die Vielfalt an Möglichkeiten und Techniken bedürfen einer ausgewogenen
Auswahl und Orchestrierung.
Der gedankenlose Einsatz von J2EE-Komponenten (EJBs, JSPs, Servlets, JMS, Security, ...) führt zu einem chaotischem System, in dem
sich nach kurzer Zeit niemand mehr auskennt.
2. J2EE Technologie nicht kennen
Der Umgang mit einer komplexen Technologie kann nicht "on the flight" zur Projektzeit erlernt werden. Das gilt insbesondere
für J2EE, welches, wie bereits erwähnt, viele Modelllösungen mitbringt. Wird das vorgegebene Modell nicht
verstanden, hat das schwerwiegende Folgen auf die entstehende Anwendung.
Sicherlich müssen nicht alle Projektmitglieder J2EE Gurus sein, doch der Technologiehintergrund muss
allgemein bekannt sein.
3. Toolmania
Frei nach dem Motto "viel hilft viel" wird die Entwicklungsumgebung in J2EE-Projekten mit Tools überfrachtet. Dabei
wird schnell vergessen, dass jedes Tools meist eine nicht unerhebliche Einarbeitungszeit erfordert. Auch ist kein Tool
fehlerfrei. Im günstigsten Fall kann man diese umgehen, im schlechtesten wird der Entwicklungsprozess massiv gestört.
Ein Alarmsignal für den schlechten Einsatz von Tools ist, dass die Entwickler nicht mehr zu dem kommen, was Sie eigentlich
machen sollen: programmieren. Stattdessen wird ein nicht unerheblicher Teil der Zeit für die Handhabung und Konfiguration
der Tools beansprucht.
4. Entwicklungs- und Laufzeitsystem unterschiedlich
Vor dem Aufkommen von Java, war es eine Selbstverständlichkeit, dass Entwicklungs- und Laufzeitsystem gleich waren.
Betriebssysteme und Programmiersprachen waren für nichts anderes ausgelegt. Java hat diese Notwendigkeit zwar geändert,
aber nicht vollkommen eliminiert.
Es bestehen weiterhin große Unterschiede zwischen Java-Umgebungen auf verschiedenen Betriebssystemen. Dies hat
meist damit zu tun, wie low-level Resourcen implementiert sind. Prominente Beispiele hierfür sind
Threads, I/O und Bildschirmausgabe (AWT). Nicht alle Unterschiede werden von Java gekapselt, und können daher zu
unvorhergesehenem Anwendungsverhalten führen.
5. Keine oder mangelhafte Performance- und Sizinganalyse
In kaum einem J2EE Projekt macht man sich Gedanken darüber, welche realistische Last das Produktivsystem bewältigen
muss. Oft werden nur primitive Lasttests gefahren, die in keinster Weise das tatsächliche Benutzerverhalten nachbilden.
Geschieht das dennoch, dann wird der logisch nächste Schritt nicht mehr vollzogen: Die Berechnung der nötigen
Hardwareresourcen. Typischerweise wird irgend eine Maschine gekauft, von der man einfach glaubt,
dass sie gut dimensioniert ist und noch gut ins Budget passt.
Die Konsequenz daraus ist, dass erst mit dem Produktivstart die benötigte Systemleistung ermittelt wird. Was folgt ist
die hektische Suche nach Engpässen in der Architektur und im Programmcode.
Wolfgang Schaub
2004-02-03