Implementation of runtime debugging tools for the enterprise application environments Java EE and Spring

Java EE & Spring Runtime Debugging

Dokumentinformationen

Autor

Patrick Kleindienst

instructor Prof. Dr. Simon Wiest
Schule

Stuttgart Media University

Fachrichtung Audiovisual Media
Dokumenttyp Bachelor Thesis
Ort Stuttgart
Sprache German
Format | PDF
Größe 0.98 MB

Zusammenfassung

I.Laufzeit Debugging in Java EE und Spring Umgebungen

Diese Bachelorarbeit befasst sich mit der Implementierung von Laufzeit-Debugging-Tools für Java Enterprise Anwendungen (Java EE) und Spring Anwendungen. Das Hauptproblem, welches adressiert wird, ist der aufwändige Prozess des wiederholten Deployens und Neustartens des Servers bei der Fehlersuche. Die Lösung besteht in der Instrumentierung laufender Anwendungen, d.h. dem dynamischen Einfügen von Logging-Statements in den Anwendungscode mittels Bytecode Engineering. Zwei verschiedene Ansätze werden vorgestellt: Watson, basierend auf Spring AOP (Aspect-Oriented Programming), und SherLog, welches die Instrumentation API und Javassist nutzt und plattformunabhängig ist. Beide Tools nutzen JMX (Java Management Extensions) für die Remote-Überwachung und -Steuerung. Ein wesentlicher Aspekt ist die Granularität der Instrumentierung, wobei der Fokus auf Methoden liegt, um die Ursachen von Fehlern besser nachvollziehen zu können. Die Arbeit vergleicht die Möglichkeiten und Einschränkungen der verschiedenen Ansätze und Plattformen.

1. Das Problem Ineffizientes Debugging in Java EE und Spring

Der Hauptfokus der Arbeit liegt auf der Verbesserung des Debugging-Prozesses in Java Enterprise Anwendungen (Java EE) und Spring-basierten Anwendungen. Das bestehende Problem wird als ineffizient beschrieben: ständiges Re-Deployen des Quellcodes und Neustarten des Servers bei der Fehlersuche. Diese wiederholten Schritte verursachen erheblichen Zeitaufwand und reduzieren die Produktivität. Die Arbeit argumentiert, dass diese Ineffizienz durch die Implementierung eines Laufzeit-Debugging-Tools signifikant reduziert werden kann. Diese Tools sollen es ermöglichen, den laufenden Anwendungscode mit Logging-Statements anzureichern, ohne den Serverbetrieb zu unterbrechen. Dadurch soll der Debugging-Zyklus beschleunigt und die Notwendigkeit für wiederholte Serverneustarts eliminiert werden. Die generierten Log-Daten sollen in separaten Log-Dateien gesammelt und zur einfachen Überwachung bereitgestellt werden. Die zentrale Idee ist die „Instrumentierung“ laufender Anwendungen, um zusätzliche Informationen über den aktuellen Zustand der Anwendung, wie z.B. die Dauer von Datenbankabfragen oder Parameterwerte von Methodenaufrufen, zu erhalten.

2. Zwei Lösungsansätze Watson Spring basiert und SherLog Plattformunabhängig

Die Arbeit präsentiert zwei unterschiedliche Lösungen für das Laufzeit-Debugging: Watson und SherLog. Watson ist spezifisch für Spring-basierte Anwendungen konzipiert und nutzt Aspect-Oriented Programming (AOP) zur Integration von Logging-Statements. Im Gegensatz dazu ist SherLog plattformunabhängig und funktioniert in verschiedenen Java-Umgebungen, ohne auf spezifische Frameworks angewiesen zu sein. SherLog nutzt stattdessen die Java Instrumentation API und die Javassist-Bibliothek für die Bytecode-Manipulation. Die Wahl der unterschiedlichen Ansätze ermöglicht einen Vergleich der jeweiligen Möglichkeiten und Einschränkungen bei der Entwicklung von Lösungen für verschiedene Plattformen. Der Vergleich betont die Stärken und Schwächen von Spring AOP (insbesondere die Einschränkung auf Method Interception) im Vergleich zur flexibleren, aber auch komplexeren, Instrumentation API. Beide Ansätze liefern zusätzliche Logging-Informationen, zielen aber auf unterschiedliche Anwendungsfälle und Umgebungen ab.

3. Anforderungen an ein effektives Laufzeit Debugging Tool

Die Arbeit definiert implizit und explizit Anforderungen an ein effektives Laufzeit-Debugging-Tool. Ein wichtiger Aspekt ist die Granularität: Das Tool soll in der Lage sein, Methoden zu instrumentieren, da die Aufrufstacks von Methoden bei der Fehlersuche oft entscheidend sind. Die Benutzerfreundlichkeit spielt ebenfalls eine wichtige Rolle: Eine einfache Integration und intuitive Bedienung sind unerlässlich. Im Kontext der Arbeit wird das Beispiel Byteman erwähnt, das zwar die Funktionalität erfüllt, aber aufgrund seiner komplexen DSL (Domain Specific Language) eine steile Lernkurve aufweist und somit die Anforderung an schnelle und einfache Nutzung nicht erfüllt. Zusätzliche Anforderungen betreffen die Remote-Fähigkeit, um das Tool von einem entfernten Host aus bedienen zu können, sowie die Erweiterbarkeit, um benutzerdefinierte Anpassungen und Erweiterungen zu ermöglichen. Diese Anforderungen werden im Laufe der Arbeit an den beiden Implementierungen, Watson und SherLog, evaluiert und bewertet.

4. Technologien und Werkzeuge Java EE Spring JMX Javassist ASM und Log4j

Die Arbeit beschreibt die verwendeten Technologien und Werkzeuge, die für die Implementierung der Laufzeit-Debugging-Tools essentiell sind. Auf der Java EE-Seite werden Technologien wie JPA, EJB und JSF genannt. Spring AOP spielt eine zentrale Rolle im Watson-Tool, wobei die Arbeit Spring’s BeanPostProcessor und die damit verbundenen Lifecycle-Callbacks erläutert. JMX (Java Management Extensions) dient als Kommunikationsmechanismus zwischen dem Debugging-Tool und der Anwendung, wobei die Arbeit auch auf JMX-Sicherheitsmechanismen eingeht. Die Bytecode-Manipulation wird mit Javassist und ASM umgesetzt. Die Arbeit hebt die Unterschiede zwischen diesen beiden Bibliotheken hinsichtlich ihrer Leistungsfähigkeit und Komplexität hervor. Schließlich wird Log4j als Logging-Framework verwendet, und es wird die Notwendigkeit eines Upgrades auf Log4j 2.x angesprochen.

II.Das Watson Debugging Tool Spring basiert

Watson nutzt Spring AOP zur Integration von Logging-Statements. Es verwendet Proxies, um den Anwendungscode dynamisch zu erweitern, ohne einen separaten Compiler zu benötigen. Ein zentraler Aspekt ist die Vermeidung von Compile-Zeit Modifikationen und die Nutzung von Runtime Weaving (RTW). Die Arbeit beschreibt die Herausforderungen im Umgang mit Spring's Proxifizierungsmechanismus und die Implementierung einer Lösung zur dynamischen Anpassung von AOP Proxies. Die Erweiterungsmöglichkeiten von Watson werden durch die Bereitstellung von Schnittstellen und einen Auto-Discovery Mechanismus sichergestellt. ASM (ASM Bytecode Engineering Library) wird für die Bytecode-Manipulation eingesetzt.

1. Funktionsweise von Watson Spring AOP und Runtime Weaving

Das Watson Debugging Tool nutzt die Möglichkeiten von Spring AOP (Aspect-Oriented Programming) für die dynamische Instrumentierung von Spring-basierten Anwendungen. Kern der Funktionalität ist das Konzept des Runtime Weaving (RTW). Anstatt den Code zur Compile-Zeit zu modifizieren, verwendet Watson Proxies, die zur Laufzeit erzeugt werden. Diese Proxies fangen Methodenaufrufe ab und fügen den ursprünglichen Code zusätzliche Logging-Statements hinzu. Der eigentliche Geschäftslogik-Code wird dabei an die ursprünglichen Spring Beans weitergeleitet. Der Vorteil dieses Ansatzes liegt darin, dass kein separater Compiler benötigt wird und Modifikationen des Bytecodes erst zur Laufzeit erfolgen. Ein Proxy wird erst dann erzeugt, wenn eine betroffene Bean zum ersten Mal vom Spring Context angefordert wird. Dies wird durch die Verwendung von Spring’s BeanPostProcessor und dessen Callbacks erreicht, die direkt vor und nach der Initialisierung einer Bean aufgerufen werden. Die Arbeit beschreibt den Mechanismus, wie Beans durch Proxies ersetzt werden, bevor sie im Application Context verfügbar sind. Die Nutzung von Spring AOP ermöglicht die Fokussierung auf die Methode-Interception, die für das Einfügen von Logging-Statements ausreichend ist. Die Wahl von Spring AOP gegenüber AspectJ wird mit der fehlenden RTW-Unterstützung von AspectJ begründet, wodurch die notwendige Flexibilität zur Modifikation laufender Anwendungen eingeschränkt wäre.

2. Herausforderungen und Lösungsansätze bei der Proxy Erstellung

Die Implementierung von Watson stößt auf Herausforderungen im Umgang mit Spring's Mechanismen zur Proxy-Erstellung und -Verwaltung. Anfangs wird ein Ansatz verfolgt, der auf der Manipulation des Bean-Lifecycle basiert. Dieser Ansatz erweist sich jedoch als zu restriktiv und wird verworfen. Ein kritischer Punkt ist die Behandlung des Caching-Mechanismus von Spring für Proxifizierungsentscheidungen. Tests mit dem PrototypeScopeBeanFactoryPostProcessor zeigen, dass die Methode nur bei bereits proxifizierten Beans funktioniert. Die Arbeit beschreibt, wie die Konfiguration des Spring Application Context modifiziert werden kann, um das gewünschte Verhalten zu erreichen, ohne den gesamten Context neu zu laden. Dies beinhaltet das detaillierte Auseinandersetzen mit Spring's AOP-Proxies und der AdvisedSupport-Konfiguration. Die completeProxiedInterface Methode der AopProxyUtils Klasse spielt eine wichtige Rolle, da sie nicht nur die Schnittstellen der Bean, sondern auch zusätzliche Interfaces, wie z.B. das Advised Interface, zum Proxy hinzufügt. Dieses Interface ermöglicht den Zugriff auf die registrierten Advisors des Proxies, was für die Verwaltung der AOP-Aspekte essentiell ist. Die Arbeit beschreibt wie, durch die Nutzung des Advised Interfaces, Zugriff auf die registrierten Advisors ermöglicht und somit die Manipulation der AOP Proxies realisiert werden kann.

3. ASM für die Bytecode Manipulation und Auto Discovery

Für die effiziente und ressourcenschonende Bytecode-Manipulation setzt Watson die ASM-Bibliothek ein, im Gegensatz zu Alternativen wie Javassist. ASM ermöglicht es, Class-Dateien ohne sie vollständig in die JVM zu laden zu untersuchen. Dies wird als Vorteil gegenüber Javassist hervorgehoben, da es die Ressourcen des JVMs schont, indem Klassen erst dann geladen werden, wenn sie auch tatsächlich benötigt werden. Die Implementierung nutzt einen Auto-Discovery Mechanismus, um verfügbare Interceptors zu finden. Dieser Mechanismus basiert auf der Definition einer eigenen Marker-Annotation (@WatsonTransformer), die Klassen kennzeichnet, die für die Instrumentierung vorgesehen sind. Der showAvailableAspects- und listAspectsForBean-Operationen ermöglichen es, die verfügbaren Aspekte und die an eine bestimmte Bean angebundenen Aspekte abzufragen. Der Auto-Discovery Mechanismus erspart dem Benutzer den Zugriff auf den Code des Debugging-Tools und erleichtert somit die Verwendung und Erweiterung des Tools. Die Arbeit beschreibt die Implementierung eines Mechanismus zur Sicherstellung der initialen Proxy-Erstellung, da Spring AOP nicht immer Proxies für alle Beans erstellt.

4. Integration und Erweiterbarkeit von Watson

Die Integration von Watson in eine bestehende Spring-Anwendung wird als einfach beschrieben. Das JAR-File muss lediglich zum Classpath hinzugefügt und eine Konfigurations-Bean zum Spring Context hinzugefügt werden. Trotz des einfachen Setups erfordert die Instrumentierung eine Annotation der betroffenen Klassen mit der Watson Marker-Annotation. Dies wird als möglicher Nachteil angesehen, da es zusätzlichen Aufwand bei der Entwicklung bedeutet. Das Tool bietet jedoch durch dedizierte Schnittstellen Erweiterungspunkte für benutzerdefinierte Anpassungen. Der Auto-Discovery-Mechanismus verhindert, dass der Benutzer direkt mit dem Code des Debugging-Tools interagieren muss, was die Benutzerfreundlichkeit und Erweiterbarkeit erhöht. Die Verwendung von JMX für die Remote-Kommunikation ermöglicht die Bedienung des Tools sowohl lokal als auch remote, ohne dass der Benutzer Unterschiede im Umgang feststellen muss. Die Sicherheit von Watson wird durch die Nutzung der integrierten JMX-Sicherheitsmechanismen, einschließlich Authentifizierung und Verschlüsselung, gewährleistet.

III.Das SherLog Debugging Tool Plattformunabhängig

Im Gegensatz zu Watson ist SherLog plattformunabhängig und funktioniert ohne Abhängigkeit von Frameworks wie Spring. Es verwendet die Instrumentation API und die Javassist Bibliothek zum dynamischen Einfügen von Logging-Statements. Ein Java Agent ermöglicht die Integration in die JVM. Die Architektur basiert auf dem Strategy-Pattern, um die Bytecode-Modifikationen zu kapseln. Die Arbeit beschreibt den Umgang mit dem Classloading-Mechanismus von WildFly und die Implementierung eines Auto-Discovery Mechanismus für ClassFileTransformer. Der Einsatz von log4j für die Logging-Funktionalität wird hervorgehoben.

1. Plattformunabhängigkeit und die Java Instrumentation API

Im Gegensatz zu Watson, das auf Spring AOP aufbaut, ist SherLog ein plattformunabhängiges Laufzeit-Debugging-Tool. Es verzichtet bewusst auf die Abhängigkeit von spezifischen Java Enterprise Umgebungen oder Frameworks, um eine möglichst breite Anwendbarkeit zu gewährleisten. Die Grundlage von SherLog bildet die Java Instrumentation API, die die Manipulation von Bytecode zur Laufzeit ermöglicht. Durch die Nutzung dieser API ist SherLog nicht auf Spring oder ein anderes spezifisches Framework angewiesen und kann in einer Vielzahl von Java-Umgebungen eingesetzt werden. Die Integration in die JVM erfolgt über einen Java Agent, der die Instrumentation-Instanz bereitstellt. Ein wichtiger Aspekt ist die Vermeidung des expliziten Neuladens von Klassen nach der Bytecode-Modifikation, da dies zu unerwünschten Nebeneffekten führen würde. Stattdessen wird die von der Instrumentation API bereitgestellte Funktionalität genutzt, um die bestehenden Klassendefinitionen direkt zu modifizieren. Dies ist notwendig, um auch bereits instanziierte Objekte der instrumentierten Klassen zu beeinflussen. Die Arbeit hebt hervor, dass die Instrumentation API selbst für das Update der Klassendefinition sorgt und das Erstellen einer neuen Klasse durch einen neuen Classloader vermieden werden muss.

2. Architektur von SherLog BaseCodeIntegrators und Strategy Pattern

SherLog verwendet das Strategy-Pattern, um die Komplexität der Bytecode-Operationen zu kapseln und den Code übersichtlich und flexibel zu gestalten. Dies wird durch die Einführung von BaseCodeIntegrators erreicht, die als separate Einheiten die Bytecode-Modifikationslogik enthalten. Die BaseTransformer Klasse dient als Schnittstelle zu diesen Integratoren. Jeder BaseTransformer wird mit einer konkreten Implementierung eines BaseCodeIntegrator initialisiert. Dieser Ansatz ermöglicht eine klare Trennung der Verantwortlichkeiten und erleichtert die Erweiterung des Tools durch neue Integrationsmöglichkeiten. Die Arbeit beschreibt die Funktionsweise der transform Methode, die die Parameter an den jeweiligen BaseCodeIntegrator weiterleitet. Es wird betont, dass der modifizierte Bytecode als Array von Byte-Werten zurückgegeben wird und die Aktualisierung der Klassendefinition durch die Instrumentation API selbst erfolgt. Der Einsatz des Strategy Patterns fördert die Wartbarkeit und Erweiterbarkeit des Codes, indem die verschiedenen Bytecode-Modifikationsstrategien separat implementiert und verwaltet werden können.

3. Auto Discovery von ClassFileTransformers und Javassist

Ähnlich wie Watson nutzt SherLog einen Auto-Discovery-Mechanismus, um die Erweiterbarkeit des Tools zu gewährleisten. Es verwendet eine eigene Annotation (@SherlogTransformer), um die BaseTransformer Subklassen zu identifizieren und zu registrieren. Dies ermöglicht es, neue BaseTransformer Implementierungen einfach hinzuzufügen, ohne den Kerncode des Tools zu verändern. Die Arbeit betont die Wichtigkeit, jeden BaseTransformer sofort nach der Anwendung seiner Änderungen aus der Instrumentation-Instanz zu entfernen, um unerwartete Ergebnisse aufgrund additiver Effekte mehrerer gleichzeitig registrierter Transformer zu verhindern. Die Bytecode-Manipulation selbst erfolgt mit Hilfe der Javassist Bibliothek, die sowohl eine High-Level (source-level) als auch eine Low-Level API für die Bearbeitung von Java Bytecode bietet. Die Arbeit erwähnt den Einsatz von Javassist, jedoch ohne detaillierte Erläuterung der konkreten Implementierung. Die gewählte Strategie, nur bestehende Klassendefinitionen zu modifizieren und nicht neue Klassen über einen eigenen Classloader zu erstellen, wird als essentiell für die korrekte Funktion des Tools hervorgehoben.

4. Umgang mit dem WildFly Classloading Mechanismus und JMX

Die Arbeit beschreibt die Herausforderungen beim Umgang mit dem speziellen Classloading-Mechanismus des JBoss WildFly Anwendungsservers. Im Gegensatz zum üblichen hierarchischen Classloading, welches eine parent-first Policy verfolgt, wird vermutet, dass WildFly eine child-first Policy verwendet. Dies erschwert die Ausführung des SherLog Java Agents, da dessen Klassen nicht direkt vom Webapp Classloader geladen werden können. Der Agent wird über den AppClassLoader geladen, der auch als System Classloader fungiert. Um dennoch die Instrumentierung durchzuführen, wird ein MBean registriert, der als Brücke zwischen dem MBean Server und der laufenden Anwendung dient. Dieser MBean stellt die Funktionalität des Tools remote zur Verfügung und ermöglicht den Zugriff auf die deployte Webapplikation und deren Klassen. Über JMX und Tools wie JConsole kann der Benutzer dann die Instrumentierung auslösen. Die Arbeit betont, dass SherLog nicht auf die Konfiguration des Logger des Deployment angewiesen sein sollte und daher die eigene Logger Implementierung verwendet.

IV.Benutzerfreundlichkeit und zukünftige Verbesserungen

Die Arbeit identifiziert die Notwendigkeit einer verbesserten Benutzerfreundlichkeit. Die Verwendung von JConsole wird als unzureichend beschrieben, und die Entwicklung eines benutzerfreundlicheren JMX Clients mit JavaFX wird vorgeschlagen. Zukünftige Verbesserungen umfassen ein Upgrade auf log4j 2.x und weitere Optimierungen der Benutzerfreundlichkeit. Die Sicherheitsaspekte werden durch die Nutzung von JMX’s integrierten Authentifizierungs- und Verschlüsselungsmechanismen adressiert.

1. Mängel der Benutzerfreundlichkeit von JConsole

Die Evaluation der serverseitigen Implementierungen von Watson und SherLog zeigt, dass trotz der Erfüllung der funktionalen Anforderungen, die Benutzerfreundlichkeit verbesserungswürdig ist. Die Standard-Benutzeroberfläche für die Bedienung über JMX, JConsole, wird als unpraktisch und wenig benutzerfreundlich kritisiert. Die Bedienung gestaltet sich umständlich, da das Kopieren und Einfügen von Strings, die Klassennamen, Variablen oder Methoden repräsentieren, notwendig ist. Das Erscheinungsbild von JConsole wird als unübersichtlich und wenig ansprechend beschrieben. Diese Nachteile beeinträchtigen die Effizienz der Fehlersuche und machen die Nutzung der Debugging-Tools weniger komfortabel. Um diese Mängel zu beheben, wird die Entwicklung eines benutzerdefinierten JMX Clients vorgeschlagen, der die Interaktion mit den Tools vereinfacht und die Benutzerfreundlichkeit deutlich verbessert. Ein benutzerfreundlicherer Client soll den Arbeitsablauf optimieren und die Akzeptanz der entwickelten Tools erhöhen.

2. Entwicklung eines benutzerdefinierten JMX Clients mit JavaFX

Zur Verbesserung der Benutzerfreundlichkeit wird die Entwicklung eines eigenen JMX Clients mit JavaFX geplant. JavaFX wird als geeignete Technologie ausgewählt, da es die Implementierung von reichhaltigen Client-Anwendungen auf Basis von Java und CSS ermöglicht. Die Wahl von JavaFX bietet den Vorteil, dass es als Java API auf alle anderen Java-Bibliotheken, inklusive der JMX Remote API, zugreifen kann. Diese API stellt die notwendigen Klassen zur Verfügung, um eigene JMX Client-Anwendungen zu erstellen und diese mit lokalen oder entfernten MBean Servern zu verbinden. Mit einem maßgeschneiderten Client soll der Prozess des Debugging deutlich vereinfacht werden, indem die umständliche Handhabung von JConsole durch eine intuitive und benutzerfreundliche Oberfläche ersetzt wird. Dies wird die Akzeptanz und den praktischen Nutzen der entwickelten Laufzeit-Debugging-Tools signifikant erhöhen und die Effizienz des Debugging-Prozesses verbessern.

3. Weitere geplante Verbesserungen und zukünftige Arbeiten

Neben der Entwicklung des benutzerdefinierten JMX Clients sind weitere Verbesserungen geplant. Eine wichtige Anpassung betrifft das Upgrade der Log4j-Version. Derzeit verwenden sowohl Watson als auch SherLog Log4j 1.2. Ein Upgrade auf Log4j 2.x wird empfohlen, da Apache das Ende des Lebenszyklus für Log4j 1.2 angekündigt hat. Diese Anpassung ist wichtig, um die langfristige Stabilität und Wartbarkeit der Debugging-Tools sicherzustellen. Die Arbeit erwähnt, dass es weitere kleinere Verbesserungen geben wird, ohne diese jedoch im Detail zu spezifizieren. Die Arbeit fokussiert sich hauptsächlich auf die Implementierung und Evaluierung der beiden Kern-Debugging-Tools, wobei die genannten Verbesserungen als zukünftige Erweiterungen und Optimierungen betrachtet werden können. Diese Verbesserungen sollen die Funktionalität, Benutzerfreundlichkeit und Sicherheit der Tools weiter erhöhen.

Dokumentreferenz

  • MethodInterceptor (AOP Alliance)
  • MethodInvocation (AOP Alliance)
  • Log4j Log4j 2 Guide - Apache Log4j 2 (Apache Software Foundation)
  • 1 JavaFX Overview (Release 8) (Oracle Corporation)