05.11.2013 Aufrufe

Laufzeitanalysen mit Hilfe der Aspektorientierten ... - Feldschmid

Laufzeitanalysen mit Hilfe der Aspektorientierten ... - Feldschmid

Laufzeitanalysen mit Hilfe der Aspektorientierten ... - Feldschmid

MEHR ANZEIGEN
WENIGER ANZEIGEN

Sie wollen auch ein ePaper? Erhöhen Sie die Reichweite Ihrer Titel.

YUMPU macht aus Druck-PDFs automatisch weboptimierte ePaper, die Google liebt.

Abschlussdokument<br />

Aspektorientierte Programmierung<br />

Alexan<strong>der</strong> Burgess<br />

Andreas <strong>Feldschmid</strong><br />

Henrik Kemmesies<br />

Abdol Gholam<br />

Pierre Chavaroche<br />

21. Januar 2008


Name<br />

Arbeitspaket<br />

Pierre Chavaroche Kapitel 1 + 2<br />

Henrik Kemmesies Kapitel 3 + Abschlussdokument<br />

Andreas <strong>Feldschmid</strong> Kapitel 4 + 6.5<br />

Abdol Gholam Kapitel 5<br />

Alexan<strong>der</strong> Burgess Kapitel 6<br />

2


Inhaltsverzeichnis<br />

1 Was ist die Motivation AOP zu nutzen? 5<br />

2 Grundlagen AOP 7<br />

2.1 Begriffe, Grundkonzepte . . . . . . . . . . . . . . . . . . . . . . . . . . . 7<br />

2.1.1 Das allgemeine Prinzip von AOP . . . . . . . . . . . . . . . . . . 7<br />

2.1.2 Was genau ist ein Aspekt? . . . . . . . . . . . . . . . . . . . . . . 8<br />

2.1.3 Was ist ein Join-Point? . . . . . . . . . . . . . . . . . . . . . . . . 9<br />

2.1.4 Was ist ein Pointcut? . . . . . . . . . . . . . . . . . . . . . . . . . 9<br />

2.1.5 Wo kann ich den nun den zusätzlichen Code definieren? (Advice) 11<br />

2.1.6 Was ist eine Intertype-Declaration? . . . . . . . . . . . . . . . . . 12<br />

2.2 Vorstellung und Einrichtung <strong>der</strong> Entwicklungstools . . . . . . . . . . . . 12<br />

3 Laufzeitanalyse <strong>mit</strong> <strong>Hilfe</strong> von AOP 13<br />

3.1 Laufzeitinformationen über Programme . . . . . . . . . . . . . . . . . . . 14<br />

3.2 Erhebung von Laufzeitinformationen über Aspekte . . . . . . . . . . . . 16<br />

3.3 Grenzen <strong>der</strong> Laufzeitinformationsbeschaffung . . . . . . . . . . . . . . . . 18<br />

3.4 Anwendungsmöglichkeiten <strong>mit</strong> Laufzeitinformationen . . . . . . . . . . . 19<br />

4 Entwurf eines prototypischen Werkzeugs 20<br />

4.1 Architektur des Programms / Entscheidungen . . . . . . . . . . . . . . . 20<br />

4.2 Implementierung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23<br />

4.3 Implementierte Aspekte . . . . . . . . . . . . . . . . . . . . . . . . . . . 26<br />

5 Einbinden benutzerspezifischer Aspekte in den Prototyp 29<br />

6 Benutzung des Prototyps 34<br />

6.1 Was leistet das Programm? . . . . . . . . . . . . . . . . . . . . . . . . . 34<br />

6.2 Wie benutzt man das Programm? . . . . . . . . . . . . . . . . . . . . . . 34<br />

6.3 Einbinden in ein Eclipseprojek . . . . . . . . . . . . . . . . . . . . . . . . 35<br />

6.4 Einbinden über den Codeweaver . . . . . . . . . . . . . . . . . . . . . . . 36<br />

6.5 Ausführen <strong>mit</strong>tels Ant-Task . . . . . . . . . . . . . . . . . . . . . . . . . 39<br />

6.6 Was wird Ausgegeben? . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41<br />

Literaturverzeichnis 42<br />

Abbildungsverzeichnis 43<br />

3


Listings 44<br />

4


1 Was ist die Motivation AOP zu<br />

nutzen?<br />

Eine <strong>der</strong> großen Herausfor<strong>der</strong>ungen <strong>der</strong> heutigen Softwareentwicklung ist es, die verschiedenen<br />

Verantwortlichkeiten und Aufgaben einer Software und ihrer Komponenten sauber<br />

zu trennen (Das Prinzip des ”<br />

Seperation of Concerns“). Nur so können Abhängigkeiten<br />

minimiert und eine lange Lebensdauer <strong>der</strong> Software garantiert werden.<br />

Allerdings gibt es oft nicht-funktionale Anfor<strong>der</strong>ungen, die über den gesamten Code<br />

verteilt sind (Die sogenannten ”<br />

Crosscutting Concerns“).<br />

Abbildung 1.1: Logging in Apache Tomcat<br />

Wie man in Abbildung 1.1 sehen kann, ist das Logging über fast alle Module <strong>der</strong> Software<br />

verstreut, da es offensichtlich oft benötigt wird. Obwohl es nur eine einzige Anfor<strong>der</strong>ung<br />

(bzw. Aufgabe) ist, muss für die Realisierung Code in vielen Moduln geän<strong>der</strong>t werden.<br />

Weitere Beispiele für Crosscutting Concerns sind:<br />

• Exception-Handling<br />

• Performance<br />

• Security<br />

5


Abbildung 1.2: XML-Parsing in Apache Tomcat<br />

Das Gegenbeispiel stellt Abbildung 1.2 dar: Das XML-Parsing ist nur in einem einzigen<br />

Modul vertreten.<br />

Eine Verteilung über den gesamten Softwarecode (wie in Abbildung 1.1) hat zur Folge,<br />

dass<br />

1. eine Än<strong>der</strong>ung jener Anfor<strong>der</strong>ungen gleichzeitig eine Än<strong>der</strong>ung an mehreren Stellen<br />

im Code nach sich zieht (Fehlergefahr und geringe Produktivität).<br />

2. <strong>der</strong> eigentliche, fachliche Code kaum mehr zu lesen ist, da er völlig von nichtfunktionalen<br />

Anfor<strong>der</strong>ungen durchsetzt ist.<br />

Doch wie kann AOP hier jetzt helfen? Die Aspektorientierte Programmierung hat es sich<br />

zur Aufgabe gemacht, diese Crosscutting Concerns vom eigentlichen, fachlichen Code zu<br />

kapseln und so<strong>mit</strong> zu trennen.<br />

6


2 Grundlagen AOP<br />

AOP ist ein mächtiges Werkzeug. Wir möchten hier noch einmal kurz die wichtigsten<br />

Konzepte von AspectJ vorstellen. Für genauere Einblicke können wir unsere Einführungs-<br />

Präsentation zum Thema AOP bzw. die AspectJ-Dokumentation unter [Aspb] sowie die<br />

Bücher [Lad03] und [Böh06] empfehlen.<br />

2.1 Begriffe, Grundkonzepte<br />

Wie in den meisten an<strong>der</strong>en Techniken, gibt es auch in AOP einige Begriffe und Konzepte<br />

zu erklären, bevor man <strong>mit</strong> AOP wirklich arbeiten kann. Die Prinzipien sind aber, einmal<br />

verstanden, sehr schlüssig.<br />

2.1.1 Das allgemeine Prinzip von AOP<br />

Eine grobe Übersicht wie AOP funktioniert, sieht man in Abbildung 2.1. Links ist <strong>mit</strong><br />

Methode1 und Methode2 ein sequentieller Programmablauf vorzufinden, wie man es aus<br />

Programmen gewöhnt ist. Nun gibt es in diesem Programmablauf bestimmte Punkte<br />

(Die Join-Points) an denen jetzt zusätzlicher Code hinzugefügt werden könnte. Dieser<br />

zusätzliche Code ist in sogenannten Aspekten gekapselt. Der Pointcut wählt diejenigen<br />

Joinpoints aus, an welchen zusätzlicher Code hinzugefügt werden soll. Das Hinzufügen<br />

des Codes an die ausgewählten Joinpoints wird in AOP ”<br />

Weaving“ (Verweben) genannt.<br />

7


Abbildung 2.1: Wo AOP angreift<br />

2.1.2 Was genau ist ein Aspekt?<br />

Ein Aspekt ist einer Java-Klasse sehr ähnlich: Er besitzt genauso Methoden und Variablen<br />

wie man es von einer Java-Klasse gewöhnt ist. Ein Aspekt enthält aber zusätzliche<br />

Komponenten um die AOP-Techniken anwenden zu können. In AspectJ kann einem die<br />

Syntax für einen Aspekt sehr bekannt vorkommen:<br />

Listing 2.1: Ein Aspekt in AspectJ<br />

public aspect KontoAspekt<br />

[ implements ] [ extends ][ per−Klausel ]<br />

{<br />

//Code<br />

}<br />

Zusätzlich hat er die Endung .aj um sich von den normalen Java-Klassen zu unterscheiden.<br />

8


2.1.3 Was ist ein Join-Point?<br />

Join-Points sind über den gesamten Code verteilt. Es sind die Punkte, an denen <strong>der</strong><br />

Compiler die zusätzlichen Anweisungen aus den Aspekten ”<br />

einweben“ kann. Aufgrund<br />

dessen werden sie oft auch als ”<br />

Webepunkte“ bezeichnet. Ein Java-Beispiel:<br />

Abbildung 2.2: Eine Java-Klasse Konto <strong>mit</strong> Beispiel-Join-Points<br />

Abbildung 2.2 zeigt, wo ein Join-Point sein kann: Bei <strong>der</strong> Instanziierung einer Klasse,<br />

beim Ausführen einer Methode, beim Beschreiben einer Variablen, usw.<br />

2.1.4 Was ist ein Pointcut?<br />

Ein Pointcut fasst nun eine Menge von Join-Points zusammen und wird im jeweiligen<br />

Aspekt definiert. Er kann keinen Join-Point enthalten, alle Join-Points o<strong>der</strong> im Idealfall<br />

9


genau diejenigen, die man für seinen Aspekt benötigt. Sollte <strong>der</strong> Programmablauf zu<br />

einem Join-Point kommen und <strong>der</strong> Pointcut enthält diesen, wird <strong>der</strong> zusätzliche Code<br />

im Aspekt an dieser Stelle ausgeführt. Ein paar Java-Beispiele:<br />

Allgemeine Syntax:<br />

pointcut [ pointCutName ( ) ] : [ Auswahlmethode ] ;<br />

Ein konkreter Pointcut, <strong>der</strong> einen Join-Point aus <strong>der</strong> Java-Klasse ”<br />

Konto“ enthält:<br />

pointcut KontoSelect ( ) : c a l l ( void Konto . abheben ( int ) ) ;<br />

Sobald die Methode abheben aus <strong>der</strong> Klasse Konto ”<br />

gecallt“ (d.h. aufgerufen) wird, setzt<br />

<strong>der</strong> dafür vorgesehene zusätzliche Code ein.<br />

Der selbe Pointcut <strong>mit</strong> einem logischen Operator, um einen zusätzlichen Join-Point zu<br />

selektieren:<br />

pointcut KontoSelect ( ) : c a l l ( void Konto . abheben ( int ) )<br />

| | c a l l ( void Konto . e i n z a h l e n ( int ) ) ;<br />

Der Pointcut trifft dann zu, wenn entwe<strong>der</strong> die Methode abheben o<strong>der</strong> einzahlen aufgerufen<br />

wird.<br />

Diesmal <strong>mit</strong> Wildcards:<br />

pointcut KontoSelect ( ) : c a l l (∗ Konto . abheben ( . . ) ) ;<br />

Sollte es Überladungen <strong>der</strong> Methode abheben geben, sind diese durch den Pointcut ebenfalls<br />

betroffen.<br />

Anmerkung: Logische Operatoren und Wildcards können kombiniert werden.<br />

Neben <strong>der</strong> Pointcut-Methode call gibt es noch viele weitere Methoden:<br />

• execution(Methoden- o<strong>der</strong> Konstruktorname)<br />

• handler(Exception)<br />

• args(Parameter)<br />

• this(Instanzenname)<br />

• get(Variable)<br />

• set(Variable)<br />

Dies sind nur Einige unter vielen Weiteren, worauf hier aber nicht näher eingegangen<br />

wird.<br />

10


2.1.5 Wo kann ich den nun den zusätzlichen Code definieren?<br />

(Advice)<br />

Abbildung 2.3: Der Advice<br />

In Abbildung 2.3 sieht man die Rolle des Advice: Er befindet sich ebenfalls im Aspekt<br />

und enthält den Code, <strong>der</strong> an einem Join-Point ausgeführt werden soll.<br />

Beispiel für einen Advice:<br />

b e f o r e ( ) : KontoSelect ( )<br />

{<br />

System . out . p r i n t l n ( ”Es wird etwas von einem Konto abgehoben ! ” ) ;<br />

}<br />

Bevor die Methode Konto.abheben (definierter Join-Point im Pointcut KontoSelect() )<br />

aufgerufen wird, wird eine System.out.-Meldung ausgegeben. Neben before gibt es auch<br />

11


noch after (Ausführung nach einem Join-Point) und around (umschließt einen Join-<br />

Point, bzw. kann ihn auch ersetzen).<br />

2.1.6 Was ist eine Intertype-Declaration?<br />

Mit <strong>der</strong> Intertype-Declaration ist es sogar möglich, zusätzliche Variablen und Methoden<br />

bestehenden Klassen hinzuzufügen.<br />

Beispiel:<br />

public aspect ZusatzAttribut {<br />

private int Konto . z i n s s a t z = 2 ;<br />

}<br />

public int Konto . g e t Z i n s s a t z ( ) {<br />

return z i n s s a t z ;<br />

}<br />

Im Aspekt ZusatzAttribut wird <strong>der</strong> Klasse Konto ein weitere Variable hinzugefugt: Der<br />

Zinssatz. Zusätzlich erhält sie eine Get-Methode.<br />

2.2 Vorstellung und Einrichtung <strong>der</strong><br />

Entwicklungstools<br />

Wie auch schon für die Beispiele aus Kapitel 1, haben wir für unser Projekt den Standard<br />

AspectJ gewählt. Der Vorteil von AspectJ ist die Aufwärtskompatibilität: So<strong>mit</strong><br />

ist korrekter Java-Code auch korrekter AspectJ-Code. Zusätzlich erzeugt <strong>der</strong> AspectJ-<br />

Compiler ganz normalen Java-Bytecode, d.h. es läuft auf unverän<strong>der</strong>ter JVM und ist<br />

so<strong>mit</strong> auch Plattformkompatibel.<br />

Die Integration in Eclipse funktioniert wie folgt: Unter http://www.eclipse.org/ajdt/<br />

findet man die neusten AJDT (AspectJ Development Tools). Laden Sie sich hier die<br />

entsprechende .zip-Datei herunter. Anschließend kopieren Sie die Dateien aus dem Ordner<br />

”<br />

features“ und ”<br />

plugins“ in die gleichnamigen Ordner aus Ihrem Eclipse-Verzeichnis.<br />

Natürlich ist eine Installation auch automatisch über die Eclipse Update-Funktion möglich<br />

(die Update-Site URL ist auch auf <strong>der</strong> AJDT-Seite zu finden).<br />

12


3 Laufzeitanalyse <strong>mit</strong> <strong>Hilfe</strong> von<br />

AOP<br />

Wo gehobelt wird, da fallen Späne“ - und wo programmiert wird, da entstehen Fehler.<br />

”<br />

Dieser schlaue Satz dürfte jedem heranwachsenden Informatiker wohlbekannt sein. Bei<br />

Unachtsamkeit können Fehler zu finanziellen o<strong>der</strong> sogar im schlimmsten Fall zu menschlichen<br />

Verlusten führen. Allein im Jahr 1999 verlor die NASA 125 Millionen Dollar durch<br />

einen verpassten Mars-Landeanflug <strong>der</strong> Sonde Mars Climate Orbiter“. Der Grund: Eine<br />

”<br />

fehlerhafte Implementierung des Maßsystems [Mar].<br />

Zur Vermeidung solcher Programmierfehler kennt die Informatik eine Antwort: Softwaretests.<br />

Noch bevor eine Software den Unit-Tests unterzogen wird, sollte sie einer<br />

statischen Code-Analyse unterzogen werden. Die statische Code-Analyse überprüft den<br />

Quelltext eines Programms auf eine Reihe vorher festgelegter Regeln. Dadurch können<br />

beispielsweise folgende Fälle identifiziert und behoben werden:<br />

• fehlende Vorinitialisierung von Variablen<br />

• Identifikation von unerreichbaren Codestellen<br />

• Überschreitung <strong>der</strong> maximalen Lines of Code (LoC) einer Methode<br />

• fehlendes Implementieren eines Interfaces<br />

Teilweise wird die statische Analyse durch die Zuhilfenahme von Stylecheckern ergänzt,<br />

die den Quelltext auf vorher festgelegte Programmierstile überprüfen.<br />

Die statische Code-Analyse hat allerdings den Nachteil, dass Programmierfehler, die<br />

erst während <strong>der</strong> Programmausführung ”<br />

entstehen“, nicht als solche identifiziert werden<br />

können. Um diesem Problem entgegenzuwirken muss das Programm zur Laufzeit, durch<br />

eine dynamische Code-Analyse, analysiert werden. Hierbei sind nicht nur Informationen<br />

an einer ganz bestimmten Programmstelle von Interesse, son<strong>der</strong>n auch <strong>der</strong> allgemeine<br />

Zustand und <strong>der</strong> Ablauf des Programms können eine wichtige Rolle spielen.<br />

Dynamische Informationen über ein laufendes Programm nennt man Laufzeitinformationen.<br />

Sie können <strong>mit</strong> <strong>Hilfe</strong> <strong>der</strong> <strong>Aspektorientierten</strong> Programmierung aus dem Programmfluss<br />

herausgeholt werden. Bevor dies allerdings durchgeführt werden kann, muss<br />

zunächst überlegt werden, welche Laufzeitinformationen eines Programms überhaupt<br />

von Interesse sind.<br />

13


3.1 Laufzeitinformationen über Programme<br />

Generell muss bei dem Begriff ”<br />

Laufzeitinformationen“ von einem unbekannten Projekt<br />

ausgegangen werden. Ein Projekt ist dann unbekannt, wenn <strong>der</strong> innere Aufbau und die<br />

innere Funktionsweise unbekannt sind. Man spricht in diesem Fall von einer Blackbox.<br />

Abbildung 3.1: Blackbox-Ansicht eines Projekts<br />

Betrachtet man nun eine Blackbox, wie sie in <strong>der</strong> Abbildung 3.1 zu sehen ist, so beginnt<br />

man automatisch, sich Fragen über das vorliegende Objekt und dessen Laufzeitinformationen<br />

zu stellen. Mögliche Laufzeitinformationen werden nun im Folgenden strukturiert<br />

erfasst. Als Grobeinteilung wurden die Strukturierungspunkte Klassen, Methoden, Einund<br />

Ausgaben, Threads, Exceptions, Technisches und Sonstiges gewählt:<br />

• Klassen<br />

– Zählen <strong>der</strong> Instanzen einer bestimmten Klasse<br />

– Einhalten vorgeschriebener Schnittstellen<br />

– Korrektes Abbilden <strong>der</strong> Instanzen auf die vorgegebene Softwarearchitektur<br />

– Rekonstruierung <strong>der</strong> Softwarearchitektur<br />

– Er<strong>mit</strong>tlung <strong>der</strong> Aufrufreihenfolge <strong>der</strong> Komponenten<br />

(ruft obere immer untere auf?)<br />

• Methoden<br />

– Er<strong>mit</strong>tlung <strong>der</strong> Anzahl <strong>der</strong> Aufrufe einer Methode<br />

– Messung <strong>der</strong> Ausführungszeit einer Methode<br />

– Auffinden von ungenutzten Methoden<br />

– Auffinden von ungenutzten Argumente<br />

14


• Threads<br />

– Auffinden vorhandener Threads<br />

– Er<strong>mit</strong>tlung <strong>der</strong> Anzahl von Threads zu einem gewissen Zeitpunkt<br />

– Er<strong>mit</strong>tlung <strong>der</strong> Anzahl von Threads über den Gesamtablauf des Programms<br />

– Messen <strong>der</strong> Ausführungszeit eines Threads<br />

– Er<strong>mit</strong>tlung <strong>der</strong> Größe <strong>der</strong> zugeteilten Zeitscheibe an einen Thread<br />

– Auffinden von ungenutzten Threads<br />

– Auffinden von Deadlocks<br />

– Messen <strong>der</strong> Wartezeit eines Threads auf gesperrte Objekte<br />

• Ein- und Ausgaben<br />

– Erfassen von Logging-Informationen<br />

– Erfassen von gelesenen und geschriebenen Inhalten einer Datei<br />

– Verwendung von Dateien<br />

– Erfassen <strong>der</strong> Anzahl von verwendeten Dateien<br />

– Korrektes Öffnen und Schließen <strong>der</strong> Dateien<br />

– Verwendung von Netzwerkverbindungen<br />

– Er<strong>mit</strong>tlung <strong>der</strong> Verwendungsdauer externer Quellen<br />

(Dateien und Netzwerkverbindungen)<br />

– Er<strong>mit</strong>tlung von verschickten Informationen über das Netzwerk<br />

– Erfassung von Benutzereingaben (Konsole / GUI)<br />

• Exceptions<br />

– Er<strong>mit</strong>tlung aller geworfenen Exceptions<br />

– Auffinden unvorhergesehener Exceptions<br />

– Er<strong>mit</strong>tlung <strong>der</strong> Anzahl und des Zeitpunkts von geworfenen Exceptions<br />

• Technisches<br />

– Er<strong>mit</strong>tlung <strong>der</strong> Anzahl benötigter Speicherzellen<br />

(im Hinblick auf Embedded Systems)<br />

– Er<strong>mit</strong>tlung <strong>der</strong> Speicheradresse von Variable X<br />

– Messen <strong>der</strong> Speicherbelastung des Programms<br />

– Er<strong>mit</strong>tlung <strong>der</strong> Belegungenen <strong>der</strong> Prozessorregister<br />

– Auffinden von fehlenden Speicherfreigaben<br />

• Sonstiges<br />

– Protokollierung des detaillierten Programmablaufs<br />

(Wann hat Benutzer was gedrückt?)<br />

15


– Erkennung von Endlosschleifen<br />

– Ausgeben von SQL-Statements<br />

– Er<strong>mit</strong>tlung des Ausführungsorts des Programms<br />

– Er<strong>mit</strong>tlung des Bauplans <strong>der</strong> GUI<br />

– Zählen von GUI-Elementen<br />

Hinweis: Diese Liste hat nicht die Anfor<strong>der</strong>ung vollständig zu sein. Sie soll lediglich<br />

einen Überblick über mögliche Laufzeitinformationen geben.<br />

3.2 Erhebung von Laufzeitinformationen über<br />

Aspekte<br />

Nachdem nun ein Überblick über mögliche Laufzeitinformationen erstellt wurde, sind<br />

diese nun aus dem Black-Box-Programm zu extrahieren. Dazu wird im Weiteren die<br />

Laufzeitinformation ”<br />

Ausgeben von SQL-Statements“ als Erklärungsbeispiel herangezogen.<br />

Wie bereits zu Beginn dieses Kapitels erwähnt, verschafft AOP <strong>mit</strong> <strong>Hilfe</strong> <strong>der</strong> Aspekte die<br />

Möglichkeit, dieses Vorhaben umzusetzen. Das Black-Box-Projekt muss dazu im ersten<br />

Schritt in ein AspectJ-Projekt umgewandelt werden. Dies geschieht durch die Auswahl<br />

des Menüpunktes ”<br />

Rechtsklick auf den Black-Box-Projektordner“ −→ ”<br />

AspectJ Tools“<br />

−→ ”<br />

Convert to AspectJ Project“.<br />

Abbildung 3.2: Umwandlung eines Java-Projekts in ein AspectJ-Project<br />

Anschließend sollte in dem Black-Box-Projektordner ein neues Package für die zu implementierenden<br />

Aspekte erstellt werden. In dem Aspekt-Package muss nun im zweiten<br />

Schritt ein Aspekt erstellt werden. Der Aspekt wird über ”<br />

Rechtsklick auf Aspekt-<br />

Package −→ New −→ Other. . . −→ AspectJ −→ Aspect“ <strong>mit</strong> einem beliebigen Namen<br />

16


erstellt. Ein Aspekt ist grundsätzlich ein Behälter für AOP-Code und am ehesten <strong>mit</strong><br />

einer Java-Klasse zu vergleichen – nur dass <strong>der</strong> Aspekt statt *.java auf *.aj endet.<br />

Nach <strong>der</strong> Erstellung des Aspekts, ist in <strong>der</strong> *.aj-Datei bereits folgen<strong>der</strong> Code enthalten:<br />

Listing 3.1: Die erste Codezeile vom Aspekt SqlStatementsAspect<br />

public aspect SqlStatementsAspect { . . . }<br />

Nun muss geklärt werden, welche Sprachkonstrukte die gesuchte Laufzeitinformation<br />

charakterisieren. Diese Charakterisierung ist beson<strong>der</strong>s für die Erstellung <strong>der</strong> Join-Points<br />

wichtig. Im Fall <strong>der</strong> ”<br />

Ausgeben von SQL-Statements“-Laufzeitinformation genügt ein<br />

kurzer Blick in die Java-Dokumentation im Abschnitt java.sql.Statement unter http://<br />

java.sun.com/javase/6/docs/api/java/sql/Statement.html , woraus hervor geht,<br />

dass jedes SQL-Statement unter JDBC über eine <strong>der</strong> execute*-Methoden des Statement-<br />

Interfaces abgesetzt wird (die addBatch()-Methode wird hier ignoriert).<br />

Anschließend kann durch diese Charaktereigenschaften <strong>der</strong> Pointcut <strong>mit</strong> seinen Join-<br />

Points definiert werden (in unserem Laufzeitinformationen-Fall existiert nur ein Join-<br />

Point!). Dazu wird zuerst <strong>der</strong> Pointcut <strong>mit</strong> einem Namen und einem abschließenden ”<br />

:“<br />

angegeben, gefolgt von den Join-Point-Definitionen:<br />

Listing 3.2: Pointcut- und Join-Point-Definition im Aspekt SqlStatementsAspect<br />

pointcut getSqlStatement ( S t r i n g s q l ) :<br />

c a l l (∗ java . s q l . Statement . execute ∗ ( . . ) ) && args ( sql , . . ) ;<br />

Sollen weitere Join-Points angegeben werden, so geschieht dies durch das Anhängen des<br />

O<strong>der</strong>-Operators ‖ an den ersten Join-Point (nach ”<br />

&& args(sql, ..)“ ), gefolgt von den<br />

weiteren Join-Point-Definitionen. Um nicht jede <strong>der</strong> execute*-Methoden einzeln angeben<br />

zu müssen, können Wildcards eingesetzt werden. Ein Stern * steht für eine beliebige<br />

Anzahl beliebiger Zeichen (außer dem Punkt). Zwei Punkte .. stehen für eine beliebige<br />

Anzahl von beliebigen Zeichen (einschließlich dem Punkt) und ein Plus + steht für<br />

sämtliche Unterklassen bzw. Interfaces des vorgegebenen Typs. Ist zusätzlich <strong>der</strong> Zugriff<br />

auf die Argumente einer Methode nötig, so muss das Schlüsselwort ”<br />

args“ <strong>mit</strong> Angabe<br />

<strong>der</strong> erwarteten Argumente dem Join-Point hinzugefügt werden.<br />

Nachdem <strong>der</strong> Einstiegspunkt unserer Interessen definiert ist, muss nun <strong>der</strong> auszuführende<br />

Code an dieser Stelle definiert werden. Dies wird in einem before-, after- o<strong>der</strong> einem<br />

around-Advice <strong>mit</strong> Angabe des Pointcuts festgelegt:<br />

Listing 3.3: Advice-Definition im Aspekt SqlStatementsAspect<br />

b e f o r e ( S t r i n g s q l ) : getSqlStatement ( s q l ) {<br />

System . out . p r i n t l n ( ”SQL−Statement ’ ”+s q l+” ’ was c a l l e d ” ) ;<br />

}<br />

17


Wurde alles richtig implementiert, so wird in <strong>der</strong> Entwicklungsumgebung ”<br />

Eclipse“ links<br />

neben dem Aspekt ein kleiner Pfeil angezeigt, <strong>der</strong> beim Herüberfahren <strong>mit</strong> <strong>der</strong> Maus die<br />

anvisierte Stelle im Black-Box-Projekt anzeigt.<br />

Abbildung 3.3: Der Aspekt Ausgeben von SQL-Statements zeigt auf eine Codestelle<br />

Umgekehrt wird bei <strong>der</strong> anvisierten Stelle im Black-Box-Projekt ein entgegengesetzter<br />

Pfeil angezeigt:<br />

Abbildung 3.4: Main-Methode, auf die <strong>der</strong> Aspekt Ausgeben von SQL-Statements zeigt<br />

Allerdings sind <strong>der</strong> Erfassung <strong>der</strong> Laufzeitinformationen gewisse Grenzen gesetzt, die im<br />

Folgenden untersucht werden.<br />

3.3 Grenzen <strong>der</strong> Laufzeitinformationsbeschaffung<br />

Mit AOP können keine echten Ausführungszeiten einer Methode o<strong>der</strong> eines Teilbereichs<br />

des Programms er<strong>mit</strong>telt werden. Für eine Zeitmessung muss vor und nach dem zu<br />

messenden Code zusätzlicher AOP-Code eingefügt werden. Dieser zusätzliche Code ist,<br />

zusätzlich zum normalen Code, während <strong>der</strong> Programmausführung auszuführen, wodurch<br />

wie<strong>der</strong>um zusätzliche Zeit beansprucht wird und so<strong>mit</strong> das Gesamtergebnis einer<br />

gemessenen Ausführungszeit verfälscht wird. Die <strong>mit</strong> AOP er<strong>mit</strong>telten Ausführungszeiten<br />

18


sind daher immer nur Näherungswerte, die allerdings trotzdem gut für Vergleichszwecke<br />

herangezogen werden können.<br />

Mit AOP ist es zudem nicht möglich, den Code zwischen einem try- und catch-Block o<strong>der</strong><br />

den Ausführungsort eines Threads in einem Join-Point anzugeben. Auch das Erfassen<br />

von technischen Informationen, wie z.B. die Anzahl <strong>der</strong> benötigten Speicherzellen für ein<br />

Objekt, ist beim managed Code, wie z.B. Java, nicht möglich, da AOP ebenfalls wie <strong>der</strong><br />

managed Code nur in einer Sandbox abläuft und keinen Zugriff auf die Arbeitsweisen<br />

<strong>der</strong> Virtual Machine hat.<br />

3.4 Anwendungsmöglichkeiten <strong>mit</strong><br />

Laufzeitinformationen<br />

Wurden einige Laufzeitinformationen aus einem Programm <strong>mit</strong> den entsprechenden<br />

Aspekten extrahiert, so stellt sich nun die Frage, wie diese weiterzuverarbeiten sind.<br />

Ein beson<strong>der</strong>s wichtiger Anwendungsfall ist die Grundlagenbildung für einen Profiler.<br />

Dieser müsste allerdings zusätzlich <strong>mit</strong> statischen Analysen ergänzt werden, um das<br />

volle Spektrum <strong>der</strong> Softwareanalyse nutzen zu können.<br />

Weiter könnten die Laufzeitinformationen für die Erstellung von Sequenzdiagrammen<br />

o<strong>der</strong> zum Kennenlernen eines Programms verwendet werden. Unter Kennenlernen ist<br />

hier zu verstehen, dass ein Entwickler die Aufgabe bekommen hat, eine ihm unbekannte<br />

Software um gewisse Funktionalitäten zu erweitern / abzuän<strong>der</strong>n ohne dabei auf Softwaredokumentation<br />

o<strong>der</strong> früher beteiligte Entwickler zurückzugreifen. Durch entsprechende<br />

Laufzeitinformationen könnten hier schnell und einfach die vorhandene Softwarearchitektur,<br />

die dafür benötigt wird, und <strong>der</strong> Programmablauf <strong>der</strong> zu erweiternden Software<br />

er<strong>mit</strong>telt werden.<br />

Soll darüber hinaus ein Programm aufgrund schlechter Performance optimiert werden,<br />

so könnte es sehr wichtig sein, die Methode <strong>mit</strong> <strong>der</strong> längsten Ausführungsdauer zu identifizieren.<br />

Auch dies ist <strong>mit</strong> Laufzeitinformationen möglich.<br />

Ebenfalls sind Laufzeitinformationen für das Testen von Software geeignet: Es wird vor<br />

<strong>der</strong> Ausführung eines Programms abgeschätzt, wie lange eine gewisse Methode benötigen<br />

darf. Danach wird die Software ausgeführt und die theoretisch er<strong>mit</strong>telte Zeit <strong>mit</strong> <strong>der</strong><br />

tatsächlichen Ausführungszeit verglichen. Unterscheiden sich diese Werte gravierend, so<br />

könnte dies bereits ein Indiz für fehlerhaften Code sein. Eine weitere Möglichkeit des<br />

Testens wäre eine eigene Implementierung, auf Basis von Laufzeitinformationen, für das<br />

Tracen eines Programms.<br />

Auch können Laufzeitinformationen zur Softwarequalitätsicherung <strong>der</strong> Softwarearchitektur<br />

eingesetzt werden, indem die Soll-Architektur <strong>mit</strong> <strong>der</strong> tatsächlichen Implementierung<br />

abgeglichen wird und dabei Komponenten überführt werden, die an<strong>der</strong>e Komponenten<br />

verbotener Weise übergehen.<br />

19


4 Entwurf eines prototypischen<br />

Werkzeugs<br />

In diesem Abschnitt wird das von uns implementierte prototypische Werkzeug kurz<br />

vorgestellt.<br />

Gerade beim Profiling beziehungsweise <strong>der</strong> Analyse von Methodenlaufzeiten, muss viel<br />

Wert auf Performance und Skalierbarkeit gelegt werden, da<strong>mit</strong> ein möglichst unverfälschtes<br />

Ergebnis erzielt werden kann. Die von uns implementierten Aspekte sowie die erstellte<br />

Architektur wurden nicht speziell auf einen performanceorientierten o<strong>der</strong> gar skalierbaren<br />

Ansatz optimiert. Die erstellte Lösung ist also eher als ”<br />

Prototyp“ o<strong>der</strong> ”<br />

Proof of<br />

Concept“ zu verstehen – die generierten Ausgaben können als Richtwerte herangezogen<br />

werden, sind aber auf jeden Fall kritisch zu hinterfragen.<br />

4.1 Architektur des Programms / Entscheidungen<br />

Um die Architektur des Programms verstehen zu können, ist es wichtig die Grundzüge<br />

Aspektorientierter Programmierung zu verstehen – siehe dazu Kapitel 1.<br />

Bei <strong>der</strong> Analyse <strong>der</strong> Architekturanfor<strong>der</strong>ungen an unser Programm wurde schnell klar,<br />

dass eine <strong>der</strong> Hauptanfor<strong>der</strong>ungen die Interoperabilität ist, also die Möglichkeit unser<br />

Werkzeug ohne große Anpassungen auf ein beliebiges Java-Projekt anwenden zu können.<br />

Diese Funktion wird durch AspectJ und so<strong>mit</strong> von den verschiedenen Aspekten von<br />

Haus aus bestens Unterstützt – wir mussten also nur noch darauf achten beim Sammeln,<br />

Verwalten und Ausgeben <strong>der</strong> Daten keine projektspezifischen Abhängigkeiten zu<br />

generieren.<br />

Um die Informationen, die von den einzelnen Aspekten gesammelt werden auswerten<br />

und eventuell darstellen zu können, muss eine Ausgabe stattfinden. Dies könnte nun<br />

per Ausgabe in eine Datei, Schreiben in eine Datenbank, auf einen Socket o<strong>der</strong> eine<br />

MessageQueue realisiert werden. Wir entschieden uns <strong>der</strong> Einfachheit halber für eine<br />

simple Ausgabe in eine Datei. Dabei entschieden wir uns für eine Ausgabe im XML-<br />

Format. Auch dafür musste eine – soweit überhaupt möglich – gemeinsame Struktur<br />

festgelegt werden. Einer <strong>der</strong> Gründe für das XML-Ausgabeformat war die Möglichkeit<br />

das erzeugte XML-Dokument <strong>mit</strong>tels XSLT in verschiedene an<strong>der</strong>e Formate (z.B. CSV,<br />

SVG, XHTML usw.) transformieren zu können.<br />

20


Ein zusätzlicher wichtiger Punkt in diesem Kontext ist die Fragestellung, wann die Ausgabe<br />

erfolgt. Bei jedem Funktionsaufruf mehrere XML-Java-Elemente zu erstellen und<br />

diese dann in eine XML-Datei korrekt einzuschachteln würde eine – selbst für einen Prototypen<br />

– zu hohe Beeinflussung <strong>der</strong> Laufzeit bedeuten. Selbst Ausgaben per System.out<br />

beeinflussen die Laufzeit schon deutlich (Programmlaufzeit Normal: 6.5Sec, <strong>mit</strong> Aspekten<br />

7Sec, <strong>mit</strong> System.out-Ausgabe bei jedem Methodenaufruf: 25Sec). Daher wurde<br />

von uns eine Möglichkeit angestrebt die Informationen während dem Programmablauf<br />

lediglich <strong>mit</strong>tels Java-Objekten vorzuhalten und nach dem Beenden des eigentlichen<br />

Programmablaufs die Transformation in XML-Objekte und die Datei-Ausgabe in eine<br />

XML-Datei zu starten. Natürlich ergeben sich dadurch gewisse Constraints und Nachteile.<br />

So steigt die Speichernutzung stärker an und sehr lang laufende Programme <strong>mit</strong><br />

vielen Methodenaufrufen führen letztendlich zum Speicherüberlauf. Um diese Nachteile<br />

zu beseitigen, müsste eine ausgereiftere Logging-Methode benutzt werden, welche ständig<br />

o<strong>der</strong> auch in bestimmten Intervallen die Log-Ausgabe in eine Logdatei startet und dabei<br />

die Ausführung des zu profilenden Programms möglichst wenig beeinflusst.<br />

Eine weitere Entscheidung, die wir Treffen mussten, war die Wahl <strong>der</strong> Zeitabstände<br />

zur Methodenmessung. Sehr viele Methoden laufen äußerst kurz, so dass eine Messung<br />

<strong>mit</strong> System.getCurrentTimeMillis jedes Mal 0ms ergeben hätte. System.nanoTime liefert<br />

zwar nicht wirklich Zeiten <strong>mit</strong> Nanosekunden-Granularität, schafft aber durchaus<br />

genauere Messungen als Millisekunden, zusätzlich ist System.nanoTime() nicht von <strong>der</strong><br />

Betriebssystem-Zeit abhängig, kann also nicht durch den User beeinflusst werden.<br />

Eine sehr wichtige Funktion ist die Beschränkung des Profilings auf einen Teilbereich <strong>der</strong><br />

Applikation. So möchte man vielleicht nur die Datenbank-Schicht profilen, während <strong>der</strong><br />

Anwendungskern und die GUI nicht von Interesse sind. Dies konnte ebenfalls <strong>mit</strong> einer<br />

AspectJ-Funktion gelöst werden: Pointcuts die within(Packagename..*) beinhalten treffen<br />

nur auf Joinpoints zu, die im jeweiligen Package o<strong>der</strong> Unterpackage liegen (Wildcard-<br />

Bedeutung siehe [Syn]). So konnten wir <strong>mit</strong> dem Pointcut excluded: !within(cleverTec..*)<br />

auch festlegen, dass sich unsere Aspekte nicht gegenseitig profilen.<br />

21


Abbildung 4.1: Die excluded-/included-Anweisung<br />

Eine weitere Anfor<strong>der</strong>ung war es, die einzelnen Aspekte modular zu entwickeln, um<br />

wenig o<strong>der</strong> keine Abhängigkeiten zwischen den Aspekten untereinan<strong>der</strong> zu generieren.<br />

So<strong>mit</strong> konnte die Implementierung unabhängig voneinan<strong>der</strong> vonstatten gehen und so die<br />

Arbeit besser aufgeteilt werden. Zusätzlich können die Aspekte dann auch einzeln zuo<strong>der</strong><br />

abgeschaltet werden und neue Aspekte ohne große Probleme hinzugefügt werden.<br />

Letztendlich konnten wir die Architektur sehr simpel und überschaubar halten:<br />

22


Abbildung 4.2: Die technische Architektur des Prototyps<br />

Wie man sieht, wird ein AbstractAspect verwendet, in welchem zum einen die angesprochenen<br />

Pointcuts excluded()/included() vorhanden sind – welche von jedem konkreten<br />

Aspekt später auch für jeden Pointcut benutzt werden müssen – und zum an<strong>der</strong>en<br />

die Verbindung zum Logger, also <strong>der</strong> Datenhaltung hergestellt wird. Dabei wird keine<br />

Schnittstelle verwendet, son<strong>der</strong>n <strong>der</strong> Logger direkt übernommen. Dies bedeutet, dass bei<br />

einem Austausch des Loggers sämtliche Aspekte an den neuen Logger angepasst werden<br />

müssen – dies nehmen wir aber in Kauf, da bei einer Umstellung des Loggers auf eine<br />

an<strong>der</strong>e, performantere Methode gleich die gesamte Architektur sowie die Struktur <strong>der</strong><br />

ValueObjects neu überdacht werden muss.<br />

4.2 Implementierung<br />

Bei <strong>der</strong> Implementierung starteten wir da<strong>mit</strong>, unsere Loggerklasse zu schreiben. Der Logger<br />

ist dafür zuständig die Logdaten aufzunehmen und nach Ablauf des zu überwachenden<br />

Programms auszugeben.<br />

23


Zur Datenhaltung innerhalb des Loggers entschlossen wir uns parametrisierte, geschachtelte<br />

Maps zu verwenden. Im Logger selbst finden wir eine Map, welche aus String/-<br />

Map als Key/Value Paaren besteht (Map). Je<strong>der</strong><br />

Aspekt nutzt die vom Logger angebotene Methode ”<br />

Map<br />

getHashMap(String)“, um eine exklusive Map zu bekommen – dafür übergibt man als<br />

String-Parameter Aspektname.class.getName(), um so eine Eindeutigkeit zu erlangen.<br />

In diese Map schreibt <strong>der</strong> Aspekt dann seine Daten als String, ValueObject-Paare.<br />

Die nächste Funktionalität die wir implementieren wollten, war die Ausgabe <strong>der</strong> Daten<br />

nach Ablauf des zu überwachenden Programms. Dies gestaltete sich als etwas schwieriger.<br />

Wie wird man darüber Informiert, dass das Programm beendet ist? Wenn es sich um ein<br />

rein linear ablaufendes Programm handelt, könnte man am Ende eine Benachrichtigung<br />

einbauen, dazu müsste das Programm aber geän<strong>der</strong>t werden, was wir eigentlich vermeiden<br />

wollten. Eine an<strong>der</strong>e Möglichkeit wäre die finalize()-Methode unseres Loggers zu<br />

überschreiben. Jedes Objekt in Java besitzt die von Object geerbte finalize()-Methode.<br />

Diese wird aufgerufen, sobald das jeweilige Objekt explizit - also vom GarbageCollector<br />

- entfernt wird. Wann und ob Objekte vom GarbageCollector entfernt werden, ist aber<br />

nicht festgelegt. Wird das Programm beendet, werden alle noch existierenden Objekte<br />

zerstört, wobei die finalize()-Methode aber nicht ausgeführt wird.<br />

Eine bessere Möglichkeit bietet ein sogenannter ShutdownHook.<br />

Per Runtime.getRuntime().addShutdownHook(Thread t); wird ein Thread in <strong>der</strong> Runtime<br />

registriert, welcher ausgeführt wird, sobald exit(int) aufgerufen wird, keine weiteren<br />

aktiven Threads mehr am Leben sind o<strong>der</strong> die VirtualMachine vom Benutzer terminiert<br />

wird (Logoff z.B.), also kurz gesagt das Programm beendet wird. Durch diesen Mechanismus<br />

ist es uns möglich relativ einfach und zuverlässig die Ausgabe erst nach Beenden<br />

des Hauptprogramms zu starten.<br />

Die verschiedenen Aspekte erheben unterschiedliche Daten, teilweise werden nur String/Integer<br />

Paare gespeichert (z.B. zählen <strong>der</strong> Instanzen einer Klasse), an<strong>der</strong>e speichern zu<br />

einem String mehrere Zeit-Intervalle <strong>mit</strong> Anfangs und Endzeit sowie zusätzliche Informationen,<br />

wie etwa die <strong>mit</strong>tlere Ausführungszeit. Die Struktur und das Ausgabeformat<br />

<strong>der</strong> einzelnen Aspektdaten unterscheiden sich also stark untereinan<strong>der</strong> und sind auch<br />

nicht generisch darstellbar. Daher entwickelten wir unser ValueObject Interface. Das<br />

Interface beinhaltet zwei Methoden:<br />

• public void printValues();<br />

//Gibt die Daten auf System.out aus – Debugging Zwecke etc.<br />

• public Element toXMLElement();<br />

//Gibt ein JDOM-Element zurück, in welchem die gesamten Daten als XML-<br />

Sturktur vorhanden sind<br />

Wie die Implementierung des ValueObjects ansonsten aufgebaut ist und wie es seine<br />

mehr o<strong>der</strong> weniger komplexen Daten hält, interessiert uns hier nicht – das ist <strong>der</strong> Aufgabenbereich<br />

des einzelnen Aspekts.<br />

24


Unser Logger, welcher ja als ShutdownHook-Thread registriert ist, kann nun zunächst<br />

über die erste Map iterieren und erhält so für jeden Key – welcher ja einem Aspekt-<br />

Package entspricht – als Value eine Map aus String, ValueObjects. Nun kann er über diese<br />

String-, ValueObjects-Paare iterieren und die toXMLElement()-Methode des ValueObjects<br />

benutzen, um so die gesamten Daten für einen Aspekt zu aggregieren. So entsteht<br />

dann aus den einzelnen XML-Teilbäumen ein großes XML Gesamtdokument. Dieses liegt<br />

nun als JDOM-Document vor und kann direkt <strong>mit</strong> dem org.jdom.output.Outputter in<br />

eine Datei geschrieben werden.<br />

Nachdem dies geschehen ist, wird überprüft, ob in <strong>der</strong> clevertec.properties XSLT-Stylesheets<br />

angegeben wurden. Gegebenenfalls wird das JDOM-Document dann <strong>mit</strong> diesen<br />

XSLT-Stylesheets transformiert und die Ausgaben in die entsprechenden Dateien geschrieben.<br />

So liefern wir z.B. Templates für das erzeugen von CSV-Dateien, welche dann<br />

in Excel weiterverarbeitet werden können, z.B. sortieren, selektieren und erstellen von<br />

Charts, etc. Eine dieser CSVs wurde auch vom Team RoQuMod weiterverwendet: Es<br />

wurde ein conqat-Prozessor geschrieben, welcher das CSV einliest und so die Werte in<br />

Conqat als Daten verfügbar macht und letztendlich als Tabelle ausgibt. Die erzeugten<br />

SVG-Grafiken sind eine Art ”<br />

Proof of Concept“, dass man unsere Daten auch visualisieren<br />

kann.<br />

Der gesamte Ablauf kann etwa folgen<strong>der</strong>maßen dargestellt werden:<br />

25


Abbildung 4.3: Programmablauf des Prototypen<br />

4.3 Implementierte Aspekte<br />

Folgende Aspekte haben wir in <strong>der</strong> aktuellen Version des Programms implementiert:<br />

• ExceptionsAspect: Zählt wie oft Exceptions aufgetreten sind und wann<br />

26


• Instances: Zählt wie viele Objektinstanzen erzeugt werden<br />

• MethodExecutionNumber: Zählt wie oft Methoden aufgerufen wurden<br />

• MethodExecutionRuntime: Misst die Zeit, die für eine Methodenausführung benötigt<br />

wird<br />

• MethodCallRuntime: Wie MethodExecutionRuntime, nur für Calls, standardmäßig<br />

disabled da sonst sehr viele Logdaten entstehen!<br />

• ThreadCreation: Zählt wieviele Threads erzeugt wurden<br />

• ThreadStartup: Zählt wie viele Threads ausgeführt / beendet wurden<br />

Die Aspekte konnten alle unabhängig voneinan<strong>der</strong> implementiert werden. Teilweise wurden<br />

gemeinsame Utility-Klassen benutzt, z.B. für das Festhalten von Zeitintervallen.<br />

Beispielsaspekt: Instances.aj<br />

Exemplarisch möchten wir hier nun einen Aspekt noch mal etwas genauer betrachten:<br />

Der Aspekt Instances.aj zählt alle Objektinstanzen.<br />

Der Aspekt leitet sich – wie je<strong>der</strong> Aspekt – von unserem AbstractAspect ab. Dadurch<br />

erbt er die beiden Pointcuts included && excluded und den Logger log. Nun wird aus<br />

dem Logger eine für diesen Aspekt exklusive Map geholt. Dafür wird <strong>der</strong> Aspektname.class.getName()<br />

– also die Packagestruktur und <strong>der</strong> eigentliche Aspektname – an<br />

die Methode Logger.getHashMap(String) übergeben. In <strong>der</strong> Ausgabe wird später eine<br />

Section für diesen Aspekt erstellt, die <strong>der</strong> Packagestruktur entspricht – hier also ”<br />

clever-<br />

Tec.aspects.instanceCounter“. Dies bedeutet, wenn man zwei Aspekte in einem Package<br />

implementieren würde, würden ihre Daten in <strong>der</strong> gleichen Sektion <strong>der</strong> XML-Ausgabe<br />

landen.<br />

Als nächstes muss ein o<strong>der</strong> mehrere Pointcuts definiert werden. Dabei ist darauf zu achten<br />

”<br />

&& excluded() && included()“ an den Pointcut anzufügen, da<strong>mit</strong> sich die Aspekte<br />

nicht gegenseitig ”<br />

matchen“ und zusätzlich eine Einschränkung <strong>der</strong> Analyse auf Teilbereiche<br />

möglich ist. In unserem Fall sieht <strong>der</strong> Pointcut so aus:<br />

pointcut InstancesCount ( ) :<br />

execution ( new ( . . ) ) && excluded ( ) && included ( ) ;<br />

Es werden also alle Joinpoints gematched, welche ein neues Objekt erzeugen. Nun muss<br />

noch definiert werden was dann zu tun ist, dies geschieht im Advice. Hier benutzten wir<br />

einen before() : InstancesCount() Advice. Als nächstes wird geprüft welches Objekt gerade<br />

instanziert wurde. Dies geschieht <strong>mit</strong>tels thisJoinPoint.getTarget().getClass().getCanonicalName();<br />

- was uns die Packagestruktur sowie den Klassennamen liefert. Sollte<br />

gerade eine lokale o<strong>der</strong> anonyme Instanz erzeugt worden sein – liefert die Methode Null.<br />

Diese Ausnahme wird manuell behandelt und <strong>der</strong> Objektname dann auf ”<br />

localOrAnonymous“<br />

gesetzt.<br />

27


Nun kann begonnen werden diese Logdaten in die Map zu schreiben. Zunächst wird geprüft,<br />

ob zu dem Key – also hier dem Namen des instanzierten Objekts – bereits ein ValueObject<br />

vorliegt. Falls dies nicht <strong>der</strong> Fall ist, wird ein neues angelegt und in die Map geschrieben.<br />

Sollte bereits ein ValueObject zum jeweiligen Key vorliegen, wird dieses durch<br />

seine Methoden um den Wert 1 inkrementiert. Hier also c.setCount(c.getCount()+1).<br />

Nun sind die Daten in <strong>der</strong> Map gespeichert und so<strong>mit</strong> die Aufgabe dieses Aspekts fertiggestellt.<br />

28


5 Einbinden benutzerspezifischer<br />

Aspekte in den Prototyp<br />

Nachdem die Architektur und <strong>der</strong> allgemeine Ablauf des Programms vorgestellt worden<br />

sind, wird in diesem Abschnitt ein detailliertes Beispiel für das Einbinden eines benutzerspezifischen<br />

Aspekts im Programm beschrieben. Eine unaufwändige und schnelle<br />

Erweiterung von Aspekten im Programm war eines <strong>der</strong> Hauptziele bei <strong>der</strong> Entwicklung<br />

des Programms. Der Einbindevorgang wird anhand eines Beispielaspekts erläutert. Der<br />

Beispielaspekt, <strong>der</strong> in diesen Kapitel vorgestellt wird, ist für die Zählung von Getterund<br />

Setter-Methoden zuständig. Die nötigen Testklassen sowie die Aspektklasse befinden<br />

sich im GetterSetter.jar auf <strong>der</strong> CD.<br />

Bevor <strong>mit</strong> dem Erstellen des neuen Aspekts begonnen werden kann, ist es nötig, das<br />

Projekt in Eclipse zu importieren. Zum Einbinden des Programms in Eclipse ist lediglich<br />

die Installation von AJDT(AspectJ Development Tools) notwendig (siehe HowTo<br />

Dokument). Im einzelnen muss zunächst ein neues Projekt erstellt werden, indem in<br />

Eclipse das CLeverTec Programm importiert wird. Anschliessend muss das Projekt <strong>mit</strong><br />

<strong>Hilfe</strong> von AJDT in ein AspectJ Projekt konvertiert werden, falls dies nicht automatisch<br />

geschehen ist. Das Projekt kann auch problemlos in Maven eingebunden werden, dazu<br />

ist lediglich die Einbindung von JDOM-Bibliothek in <strong>der</strong> pom.xml notwendig.<br />

Nun werden drei Testklassenn zu Testzwecken erstellt. Dazu muss zunächst ein neues<br />

Package test.all.aspects im Verzeichnis src/test angelegt werden. Die Klasse Konto<br />

enthält zwei Variablen guthaben und kontoNummer, zwei Methoden abheben(int Betrag)<br />

und einzahlen(int betrag), sowie die für den neu einzubindenden Aspekt relevanten<br />

Getter- und Setter-Methoden. Die Methode abheben() verursacht eine OverLi<strong>mit</strong>Exception,<br />

falls versucht wird, mehr Geld abzuheben als vorhanden ist. Die OverLi<strong>mit</strong>Exception<br />

Klasse ist eine selbstgeschriebene Exception, die von <strong>der</strong> Basisklasse Exception<br />

abgeleitet ist. In <strong>der</strong> Mainmethode <strong>der</strong> Klasse TestKonto wird ein Konto erstellt und<br />

eine Einzahlung sowie eine Abhebung wird ausgeführt, am Schluss wird dann die für relevante<br />

getter-Methode getGuthaben() aufgerufen. Schließlich muss die Klasse Testkonto<br />

als Java Application o<strong>der</strong> AspectJ/Java kompiliert und ausgeführt werden. Dabei wird<br />

ein neues Verzeichnis out erstellt, die unter an<strong>der</strong>em eine XML-Datei out.xml enthält, in<br />

<strong>der</strong> alle ausgewerteten Daten zusammengefasst sind. Um die Än<strong>der</strong>ungen festzustellen,<br />

die durch die Aspekterweiterung aufgetreten sind, sollte diese generierte XML Datei in<br />

ein beliebiges Verzeichnis gespeichert werden. Der Aufbau und die Struktur <strong>der</strong> XML<br />

Datei wird in Kapitel 5 näher erläutert.<br />

29


Innerhalb des Projekts wird nun ein Package namens cleverTec.aspects.getterandsetter<br />

und anschließend eine Klasse CountGetterAndSetter erstellt. Die Klasse CountGetterAndSetter<br />

ist eine Implementierung des Interface ValueObject. Diese enthält zwei Methoden<br />

zur Speicherung bzw. Ausgabe <strong>der</strong> entsprechenden Ergebnisse.<br />

Dabei entspricht die Methode printValue() <strong>der</strong> Konsolenausgabe und die Methode toXM-<br />

LElement() ist für das Festhalten <strong>der</strong> Daten in einer XML Datei verantwortlich. Des<br />

Weiteren besitzt die Klasse zwei Variablen getSetNumber die für die Zählung <strong>der</strong> aufgerufenen<br />

Getter- und Setter-Methoden zuständig ist und eine Variable getSet die die<br />

Methodennamen <strong>der</strong> Getter- und Setter-Methoden speichert. Die Methode increment()<br />

zählt die Anzahl <strong>der</strong> Getter- und Setter-Aufrufe um eins hoch.<br />

30


Abbildung 5.1: ValueObject-Klasse CountGetterUndSetter<br />

Innerhalb desselben Packages muss nun eine Aspektklasse GetterAndSetter erstellt werden,<br />

die von AbstractAspect abgeleitet ist. In <strong>der</strong> GetterAndSetter Aspektklasse ist ein<br />

pointcut definiert, <strong>der</strong> alle Getter und Setter-Methoden durchläuft. Nach jedem Aufruf<br />

einer Getter- o<strong>der</strong> Setter-Methode, wird <strong>der</strong> Name <strong>der</strong> Methode sowie die aktualisierte<br />

Anzahl <strong>der</strong> bisherigen Aufrufe <strong>mit</strong> <strong>Hilfe</strong> des Advice in eine HashMap gespeichert, die<br />

ein Attribut <strong>der</strong> Aspektklasse ist.<br />

31


Abbildung 5.2: Benutzerspezifisches Aspekt GetterAndSetter.aj<br />

Nachdem nun erneut die Klasse TestKonto kompiliert und ausgeführt wurde, kann die<br />

neu generierte XML <strong>mit</strong> <strong>der</strong> ursprünglichen XML Datei vor <strong>der</strong> Aspekterweiterung verglichen<br />

werden. Innerhalb <strong>der</strong> neuen XML Datei ist ein weiterer Tag zu<br />

finden, <strong>der</strong> in <strong>der</strong> alten XML Datei nicht vorhanden war.<br />

Abbildung 5.3: Loggerausgabe <strong>mit</strong> dem Ergebnis des benutzerspezifischen Aspekts<br />

Zusammenfassend sind folgende Punkte bei <strong>der</strong> Erstellung eigener Aspekte zu beach-<br />

32


ten:<br />

• Je<strong>der</strong> neue Aspekt muss in einem eigenen Package erstellt werden (siehe Kapitel<br />

4.3)<br />

• Die Javaklasse implementiert das Interface ValueObject und da<strong>mit</strong> dessen zwei<br />

Methoden printValues() und toXMLElement(). Dabei gibt erstere die Daten auf<br />

<strong>der</strong> Konsole aus und letztere speichert die Daten in geeignetem Format in die XML<br />

Datei.<br />

• Die Aspektklasse ist von <strong>der</strong> Klasse AbstracLogger abgeleitet. Von dieser erbt sie<br />

das Attribut vom Typ Logger.<br />

33


6 Benutzung des Prototyps<br />

Die Idee <strong>der</strong> Anwendung war, dass man jeglichen Quellcode <strong>mit</strong> Aspekten versehen kann<br />

und dadurch Messungen am Programm vornehmen kann. Dies kann auf zwei verschiedene<br />

Arten erreicht werden. Zum einen kann man dies über Eclipse erreichen, zum an<strong>der</strong>en hat<br />

man die Möglichkeit den Quellcode über einen Kommandozeilenaufruf <strong>mit</strong> den Aspekten<br />

versehen zu lassen und kann das Kompilat daraus wie jedes an<strong>der</strong>e Javaprogramm laufen<br />

lassen. Als Ausgabe entsteht dadurch eine XML-Datei sowie eine CSV-Datei.<br />

6.1 Was leistet das Programm?<br />

Die Aspekte sind so gedacht, dass sie jedem beliebigem Quellcode hinzugefügt werden<br />

können und den Quellcode so erweitern, dass statistische Informationen ausgelesen<br />

werden können. In <strong>der</strong> vorliegenden Version können bereits folgende Daten ausgelesen<br />

werden, sind also als Aspekt vorhanden:<br />

• Instanzen zählen<br />

• Methodenaufrufe zählen, sowohl gesamt wie zu je<strong>der</strong> spezifisch<br />

• Methodenlaufzeiten<br />

• Threadinstanzen zählen<br />

• Threadausführungen (Aufrufe von start())<br />

• Exceptions die geworfen werden<br />

Das Programm führt eine Statistik während <strong>der</strong> zu untersuchende Quellcode ausgeführt<br />

wird und schreibt die erworbenen Daten, bei Beendigung <strong>der</strong> virtuellen Maschine, in eine<br />

XML-Datei, welche zusätzlich noch gleich <strong>mit</strong> beliebigen XSLT-Templates transformiert<br />

werden kann (z.B. CSV o<strong>der</strong> SVG-Ausgaben). Die Sammlung <strong>der</strong> Daten passiert so<strong>mit</strong> in<br />

Java nativem Code und kann daher auf je<strong>der</strong> zur Javaspezifikation kompatiblen virtuellen<br />

Maschine laufen, wenn es entsprechend ihrer Version kompiliert wurde.<br />

6.2 Wie benutzt man das Programm?<br />

Das Programm kann auf zwei verschiedene Arten benutzt werden. Zum einen durch einbinden<br />

in ein Eclipseprojekt (6.3) und zum an<strong>der</strong>en durch Aufruf des Codeweavers (6.4),<br />

34


welchem <strong>der</strong> zu untersuchende Quellcode und die Aspekte gegeben werden und dieser<br />

dann die Aspekte an den Quellcode knüpft und ein lauffähiges Programm ausgibt.<br />

6.3 Einbinden in ein Eclipseprojek<br />

Der einfachste Weg ist den Quellcode, den Quellcode <strong>der</strong> Aspekte und dazugehöriger<br />

Klassen in ein AspectJ-Projekt in Eclipse zu kopieren. Dann kann man die ganze Anwendung<br />

direkt aus Eclipse heraus laufen lassen und muss sich um nichts weiter kümmern.<br />

Das Anlegen eines AspectJ-Projekts funktioniert gleich wie das Anlegen eines normalen<br />

Javaprojekts, nur das man eben als Projektbasis ein AspectJ-Projekt auswählt. Danach<br />

ist zu empfehlen zwei Quellverzeichnisse anzulegen, eins für das Programm welches untersucht<br />

werden soll und eins für die Aspekte. Jetzt wäre bereits alles lauffähig, aber<br />

es können noch weitere Konfigurationen vorgenommen werden. In <strong>der</strong> Datei AbstractAspect.aj<br />

können ausgeschlossene Bereiche, sowie explizit eingeschlossene Bereiche bestimmt<br />

werden. Die hierbei relevanten Pointcuts sind folgende:<br />

Listing 6.1: relevante Pointcuts bei <strong>der</strong> Verwendung des Prototyps<br />

public pointcut excluded ( ) :<br />

! within ( cleverTec . . ∗ ) ;<br />

public pointcut included ( ) :<br />

within ( ∗ ) ;<br />

Alle Dateien o<strong>der</strong> Pakete die explizit ausgeschlossen werden sollen können in den Klammern<br />

des ”<br />

excluded“-Pointcut definiert werden. Die einzelnen Teilbereiche werden über<br />

&& (logisches Und) o<strong>der</strong> ‖ (logisches O<strong>der</strong>) verbunden. Wichtig hierbei ist, dass das<br />

Paket des Prototypen sowie alle seine Unterpakete und Klassen (cleverTec..*) ausgeschlossen<br />

werden. Als Beispiel für die Benutzung:<br />

Listing 6.2: Benutzungsbeispiel des within() im Prototyp<br />

! within ( cleverTec . . ∗ && java . s q l . . ∗<br />

Diese Definition würde nun das Paket des Prototypen und dessen Unterpakete ausschließen<br />

sowie zusätzlich alle Pakete von java.sql sowie dessen Unterpakete. Ähnlich läuft dies<br />

nun <strong>mit</strong> den explizit zu inkludierenden Paketen und Dateien. Die Än<strong>der</strong>ung die man hier<br />

vornehmen muss, passiert in den Klammern des within-Fragments <strong>mit</strong> den gleichen Regeln<br />

wie oben bei den für die explizit ausgeschlossenen Pakete. Die Grundeinstellung *<br />

bedeutet, dass alle Pakete eingeschlossen werden (außer natürlich diejenigen die ausgeschlossen<br />

sind). Das ist eigentlich auch schon alles was es zu konfigurieren gibt.<br />

Man kann zusätzlich seine eigenen Aspekte schreiben aber sollte sie über den gerade<br />

beschriebenen Mechanismus aus den Statistiken ausschließen.<br />

35


Zuletzt bleibt nur noch zu konfigurieren wohin die Ausgabe geschrieben werden soll und<br />

ob XSLT-Templates auf sie angewendet werden sollen - dies kann man <strong>mit</strong>tels <strong>der</strong> Datei<br />

clevertec.properties im Unterverzeichnis conf erledigen. Die Erklärung zu dieser Datei<br />

ist im nächsten Abschnitt zu finden. Hat man nun dies alles gemacht kann man das zu<br />

untersuchende Programm laufen lassen und am Ende des Laufs sind die gesammelten<br />

Daten in den in <strong>der</strong> clevertec.properties definierten Ausgabedateien zu finden.<br />

Das ganze wurde auch nochmal als Video beigelegt, in dem gezeigt wird wie man alles<br />

in diesem Punkt beschriebene macht.<br />

6.4 Einbinden über den Codeweaver<br />

Der Codeweaver ist ein Javaprogramm, das einem letztlich den Aufruf für den acj<br />

(AspectJ Compiler) von AspectJ abnimmt. Er baut den kompletten Aufruf auf, führt<br />

ihn aus und fügt dem kompilierten Projekt noch Konfigurationsdateien hinzu. Was man<br />

braucht um ihn zu benutzen sind die Aspect Tools von AspectJ, die unter an<strong>der</strong>em die<br />

Laufzeitumgebung von AspectJ enthalten. Diese findet man hier:<br />

http://www.eclipse.org/aspectj/downloads.php<br />

Der Codeweaver selbst baut eigentlich nur eine Zeichenfolge auf <strong>mit</strong> welcher <strong>der</strong> ajc<br />

von AspectJ aufgerufen wird, indem er einen Prozess dafür startet und die Zeichenfolge<br />

ausführt. Die ganzen Daten, die benutzt werden um diesen Aufruf aufzubauen kommen<br />

aus <strong>der</strong> config.properties-Datei. Das nachfolgende Bild sollte dies etwas deutlicher<br />

machen.<br />

36


Abbildung 6.1: Die Verwendung <strong>der</strong> config.properties-Datei<br />

Anhand <strong>der</strong> Informationen aus dieser Datei wird dann ein <strong>mit</strong> den Aspekten versehener<br />

Bytecode erzeugt, welchen man wie jedes an<strong>der</strong>e Javaprogramm laufen lassen kann.<br />

Zusätzlich werden noch die Verzeichnisse libs und conf, die im Arbeitsverzeichnis des<br />

CodeWeavers liegen, kopiert. Zum Schluss wird noch eine Batch- o<strong>der</strong> Skriptdatei erzeugt,<br />

je nach unterliegen<strong>der</strong> Plattform, die dann <strong>mit</strong>tels Parameterübergabe des Pfades<br />

zur main-Methode den kompilierten Code ausführt.<br />

Der Codeweaver kann sowohl unter Linux als auch unter Windows benutzt werden,<br />

dabei ist natürlich zu beachten das die Pfadangaben für Dateien <strong>der</strong> jeweiligen Umgebung<br />

angepasst werden müssen. Gehen wir nun Schritt für Schritt durch, wie man ihn<br />

benutzt.<br />

1. Als erstes brauchen wir eine Konfigurationsdatei <strong>mit</strong> dem Namen config.properties<br />

im Arbeitsverzeichnis. Da man diese wahrscheinlich noch nicht hat kann man sie<br />

<strong>mit</strong>tels dem Aufruf ”<br />

java cleverTec.ajc.Codeweaver -makeconfig“ erzeugen lassen.<br />

Der Aufbau <strong>der</strong> Konfigurationsdatei wird am Ende des Abschnittes erklärt<br />

2. Nachdem man die Konfigurationen gesetzt hat, u.a. wo die AspectJ-Bibliotheken<br />

sowie <strong>der</strong> zu untersuchende Quellcode liegt, kann man dann den Codeweaver aufrufen<br />

<strong>mit</strong>tels: java cleverTec.ajc.Codeweaver. Dieser generiert einem dann den By-<br />

37


tecode verwoben <strong>mit</strong> den Aspekten und zusätzlich, in dem in <strong>der</strong> Konfigurationsdatei<br />

definiertem Ausgabeverzeichnis, ein Unterverzeichnis <strong>mit</strong> dem Namen conf,<br />

in welchem eine Konfigurationsdatei namens clevertec.properties für das gerade<br />

kompilierte Programm liegt.<br />

3. Danach können wir in das Ausgabeverzeichnis wechseln und noch Einstellungen in<br />

<strong>der</strong> Konfigurationsdatei clevertec.properties vornehmen:<br />

xmloutfile definiert den Namen <strong>der</strong> xml-Ausgabedatei<br />

xsltfiles p10cm definiert die Namen <strong>der</strong> Umwandlungsdateien, die die xml-<br />

Ausgabe weitertransformieren. Durch ;“ getrennt – Reihenfolge<br />

”<br />

analog zu xsltouts<br />

xsltouts definiert die Namen <strong>der</strong> zusätzlichen Ausgabedateien, die <strong>mit</strong>tels<br />

XSLT-Files generiert werden. Durch ;“ getrennt – Reihenfolge analog<br />

zu ”<br />

xsltfiles.<br />

4. Im letzten Schritt muss man nur noch die main-Methode des kompilierten Programms<br />

aufrufen und nach Beendigung ist sowohl die xml- als auch die csv-Ausgabe<br />

vorhanden in den jeweils im clevertec.properties definierten Dateien.<br />

Das war eigentlich schon alles was man machen muss. Es fehlt nur noch die Konfiguration<br />

des Codeweaver über dessen Konfigurationsdatei config.properties in dessen Arbeitsverzeichnis.<br />

Hier ein Beispiel für eine Windowskonfiguration und danach die Erklärungen<br />

<strong>der</strong> einzelnen Variablen:<br />

Listing 6.3: Die Konfigurationsdatei config.properties<br />

FURTHERJAVAARGS=<br />

ACJCLASSPATH=Libs \\jdom −1.1\\jdom . j a r ;<br />

C:\\ Programme\\ Java \\ a s p e c t j \\ l i b \\ a s p e c t j r t . j a r<br />

DESTINATION=t e s t B i n<br />

FURTHERACJARGS=−source 1 . 5<br />

ASPECT LIB PATH=C:\\ Programme\\ Java \\ a s p e c t j \\ l i b<br />

JAVAFILES=t e s t s S o u r c e s \\ t e s t S o u r c e s \\ threads \\;\<br />

s r c \\ cleverTec \\ l o g g e r \\;\<br />

s r c \\ cleverTec \\ a s p e c t s \\methodExecutionNumber \\;\<br />

s r c \\ cleverTec \\ a s p e c t s \\ threads \\;\<br />

s r c \\ cleverTec \\ u t i l \\<br />

ACJ=org . a s p e c t j . t o o l s . a j c . Main<br />

CLASSFILESGIVEN=0<br />

Die Konfigurationsdatei ist eine Java konforme properties-Datei. Deswegen auch die ”<br />

\\“<br />

in den Windowspfadangaben. Ansonsten gelten die üblichen Regeln für solche Dateien.<br />

Die Erklärung <strong>der</strong> einzelnen Variablen:<br />

38


FURTHERJAVAARGS<br />

ACJCLASSPATH<br />

DESTINATION<br />

FURTHERACJARGS<br />

ASPECT LIB PATH<br />

JAVAFILES<br />

ACJ<br />

CLASSFILESGIVEN<br />

Hier können zusätzliche Argumente für den Javakompilierer<br />

(javac) übergeben werden, z.B. -Xstdout<br />

Hier müssen die vom acj zur Übersetzung benötigten Bibliotheken<br />

übergeben werden. Mehrere Angaben sollten <strong>mit</strong>tels<br />

Strichpunkt getrennt werden<br />

Diese Variable bestimmt das Ausgabeverzeichnis wo die kompilierten<br />

Dateien hingeschrieben werden sollen<br />

Dies sind Argumente die dem acj zusätzlich übergeben werden<br />

sollen, ähnlich wie FURTHERJAVAARGS<br />

Hier müssen die AspectJ-Bibliotheken angegeben werden die<br />

u.a. den acj enthalten <strong>der</strong> zum übersetzen <strong>der</strong> Javadateien<br />

benutzt werden soll<br />

Hier werden sowohl <strong>der</strong> zu untersuchende Quellcode angegeben<br />

als auch die Aspekte die man benutzen möchte. Wenn die<br />

Angaben <strong>mit</strong> einem systemspezifischen Pfadseparator, z.B.<br />

Unix /“, enden, werden die verschiedenen Pfade passend umgebaut<br />

und als Argument an den acj übergeben. Falls sie nicht<br />

”<br />

so enden werden dem acj nur die angegebenen Dateien, welche<br />

<strong>mit</strong> .java o<strong>der</strong> .aj enden sollten, übergeben. Wenn man mehrere<br />

Pfade angeben will kann man diese <strong>mit</strong>tels Strichpunkt<br />

trennen.<br />

Der eigentliche Aufruf des acj selbst. Falls sich irgendwann<br />

einmal die Struktur <strong>der</strong> AspectJ-Bibliotheken än<strong>der</strong>t kann das<br />

hier angepasst werden<br />

Entwe<strong>der</strong> 0 o<strong>der</strong> 1. Bei 0 bedeutet dies, dass man nur Quellcode<br />

kompilieren will (.java), bei 1, dass man nur kompilierte<br />

Dateien verweben will (.class-Dateien)<br />

6.5 Ausführen <strong>mit</strong>tels Ant-Task<br />

AspectJ liefert standardmäßig Taskdefinitions für das weitverbreitete Build-Tool Ant<br />

(siehe [The]) <strong>mit</strong> jedem Release in <strong>der</strong> ”<br />

aspectjtools.jar“ aus. Da<strong>mit</strong> ist die Integration<br />

des AspektJ-Compilers ”<br />

ajc“ in Ant ein leichtes. Ein sehr einfaches compile Target für<br />

Ant könnte in etwa so aussehen:<br />

Listing 6.4: Ein einfaches compile Target für Ant<br />

<br />

<br />

<br />

<br />

39


<br />

<br />

<br />

Der Aufruf kompiliert hier sämtliche .java und .aj Dateien im Verzeichnis src<br />

und testsSrouces und schreibt die kompilierten .class Dateien in das destDir-Verzeichnis<br />

– ajc agiert hier also ebenso wie javac, nur eben <strong>mit</strong> AspectJ Unterstützung. Je nachdem<br />

welches Projekt hier kompiliert werden soll, müssen weitere Bibliotheken im Classpath<br />

angegeben werden, o<strong>der</strong> Konfigurationsdateien <strong>mit</strong> in das destDir kopiert werden.<br />

Hierfür kann die Build-Datei beliebig erweitert werden. Für einfache Aufgaben sind hier<br />

kaum Kentnisse <strong>der</strong> Ant-Syntax notwendig, die Build-Datei ist ausserdem für sich sehr<br />

sprechend.<br />

Um das volle potential des AspektJ-Compilers auszunutzen und sich so die Möglichkeit<br />

zu erschließen inkrementell zu kompilieren o<strong>der</strong> ByteCode-Weaving, also das anwenden<br />

eines Aspekts auf bereits kompilierte .class Dateien (bzw. .jar-Dateien), zu betreiben –<br />

verweisen wir auf die sehr gute Dokumentation <strong>der</strong> AspectJ-Ant-Tasks unter [Aspa].<br />

Das aktuell enthaltene Buildfile aspectj build.xml kompiliert sämtliche Aspekte und<br />

Tests. Um es zu verwenden sind folgende Schritte vorzunehmen:<br />

Zunächst sollte Ant in einer möglichst neuen Version installiert werden und das ant/bin-<br />

Verzeichnis gegebenenfalls zur Path-Variable hinzugefügt werden, um Ant direkt als<br />

Kommando in <strong>der</strong> Konsole verwenden zu können. Nun müssen noch die Properties im<br />

aspectj build.xml angepasst werden. Beson<strong>der</strong>s wichtig sind hier die beiden Properties<br />

<br />

<br />

Diese müssen auf den jeweiligen Pfad zeigen, in dem AspectJ installiert wurde.<br />

Um unsere Beispiele auszuführen ist sonst keine weitere Einstellung nötig. Sollen aber<br />

an<strong>der</strong>e Projekte kompiliert werden, muss das classpath compile bzw. classpath execution<br />

Property gegebenenfalls um weitere librarys erweitert werden.<br />

Nun kann das default-target, hier compile, per Kommandozeile gestartet werden: ”<br />

ant<br />

–f aspectj build.xml“ führt das default-target aus. Zunächst, wird falls vorhanden <strong>der</strong><br />

Ordner ant-out im relativen Verzeichnis gelöscht (clean). Dann werden Librarys und<br />

Konfigurationsdateien ins ant-out Verzeichnis kopiert(copy). Zu guter letzt werden noch<br />

sämtliche java/aj Dateien compiliert, sowie alle weiteren Dateien die sich in den beiden<br />

sourceroot-Verzeichnissen befinden ins jeweilige ant-out Unterverzeichnis kopiert<br />

(ausgeschlossen von diesem Kopieren sind .java und .aj Dateien – siehe SourceRootCopyFilter).<br />

Nun können die kompilierten .class Dateien ganz normal per java Aufruf ausgeführt werden<br />

(zusätzlich muss man nur die aspectjrt.jar im Classpath angeben) – auch dies kann<br />

40


per Ant erledigt werden. ant –f aspectj build.xml run“ startet z.B. die Main-Methode<br />

”<br />

<strong>der</strong> Klasse testSources.threads.Restaurant – dies kann durch setzen des mainclass-properties<br />

in <strong>der</strong> XML-Datei auch geän<strong>der</strong>t werden, o<strong>der</strong> per Kommandozeile direkt überschrieben<br />

werden:<br />

ant –f aspectj build.xml run -Dmainclass=testSources.svg.SVG Coordinate System Test“<br />

”<br />

führt also die Klasse testScources.svg.SVG Coordinate System Test aus.<br />

Ant bietet noch viele weitere Möglichkeiten automatische Builds und weitere Aufgaben<br />

zu realsieren. Hierfür verweisen wir auf die Ant-Dokumentation unter [Apa].<br />

Durch die Verwendung des Ant-Build-Files ist eine nahtlose Integration in eventuell bereits<br />

bestehende automatische Buildprozesse möglich. So könnten beispielsweise Tests<br />

<strong>mit</strong>hilfe von AspectJ geschrieben werden und die von uns dabei erzeugten Ausgaben direkt<br />

ausgewertet, o<strong>der</strong> die SVG-Grafiken in Report-Seiten <strong>mit</strong>eingebunden werden. Hier<br />

ist auch zu beachten, dass weitere XSL-Transformationen direkt per clevertec.properties<br />

eingebunden werden können. So könnte aus den Ergebnissen direkt eine passende XHTML-<br />

Seite generiert werden, welche dann automatisch generiert wird und weiterverarbeitet<br />

werden kann (Maven, CruiseControl, Wikis etc..).<br />

6.6 Was wird Ausgegeben?<br />

Hat man nun den einen o<strong>der</strong> an<strong>der</strong>en Weg genommen um sein Programm <strong>mit</strong> den<br />

Aspekten zu verweben und laufen zu lassen bekommt man als Ergebnis sowohl eine<br />

XML-Datei, sowie eventuell weitere durch XSLT erzeugte Dateien. Die XML-Datei ist<br />

so aufgeteilt, dass zu jedem Unterpaket von clevertec.aspects eine Sektion aufgemacht<br />

wird und die Werte die über die dort enthaltenen Aspekte gesammelt wurden in diese<br />

Sektion geschrieben werden. Die einzelnen Aspekte in jedem Paket wie<strong>der</strong>um bekommen<br />

eine eigene Sektion <strong>mit</strong> dem Namen ëntry”welcher den Aspekt definiert. In dieser<br />

ëntrySektion sind dann die Werte die von dem jeweiligen Aspekt gesammelt wurden,<br />

als Kindelemente <strong>mit</strong> Attributen enthalten. Die Formatdefinition dieser Kindelemente,<br />

also welcher Wert ist ein Attribut o<strong>der</strong> sollen Werte als Elemente eingefügt werden, wird<br />

von dem zum Aspekt gehörigen ValueObject bestimmt. Präziser, die Methode toXM-<br />

LElement (implementiert von dem Interface ValueObject) bestimmt wie die Ausgabe<br />

innerhalb jedes ëntryäussieht.<br />

Die CSV-Datei wird eigentlich in mehreren Teilen ausgegeben (momentan). Die Idee<br />

hierbei ist, dass die CSV-Datei(en) <strong>mit</strong> <strong>Hilfe</strong> von XSLT-Datei(en) aus <strong>der</strong> produzierten<br />

XML-Datei erzeugt wird. Die XSLT-Datei(en) die benutzt werden soll, wird einfach<br />

in <strong>der</strong> clevertec.properties unter xsltfiles vermerkt (siehe 6.4 ). Diese Dateien werden<br />

dann zusätzlich nach Beendigung <strong>der</strong> XML-Ausgabe verarbeitet. Über diesen Mechanismus<br />

kann man leicht beliebige CSV-Ausgaben erzeugen, die immer an die Umstände<br />

angepasst werden können sowohl <strong>mit</strong> den Daten als auch <strong>mit</strong> dem Format.<br />

41


Literaturverzeichnis<br />

[Apa]<br />

Apache Ant 1.7.0 Manual. http://ant.apache.org/manual/index.html, Abruf:<br />

20. Januar 2008<br />

[Aspa] AspectJ Ant Tasks. http://www.eclipse.org/aspectj/doc/released/<br />

devguide/antTasks.html, Abruf: 20. Januar 2008<br />

[Aspb] The AspectJ Programming Guide. http://www.eclipse.org/aspectj/doc/<br />

released/progguide/index.html, Abruf: 20. Januar 2008<br />

[Böh06] Böhm, Oliver: Aspekorientierte Programmierung <strong>mit</strong> AspectJ 5. Dpunkt-<br />

Verlag, 2006. – ISBN 3–89864–330–1<br />

[Lad03] Laddad, Ramnivas: AspectJ In Action. Manning, 2003. – ISBN 1–930110–93–6<br />

[Mar] Metric mishap caused loss of NASA orbiter. http://www.cnn.com/TECH/<br />

space/9909/30/mars.metric.02/, Abruf: 20. Januar 2008<br />

[Syn] Syntax <strong>der</strong> TypePatterns. http://www.eclipse.org/aspectj/doc/<br />

released/progguide/semantics-pointcuts.html#type-patterns, Abruf:<br />

20. Januar 2008<br />

[The] The Apache Ant Project. http://ant.apache.org/, Abruf: 20. Januar 2008<br />

42


Abbildungsverzeichnis<br />

1.1 Logging in Apache Tomcat . . . . . . . . . . . . . . . . . . . . . . . . . . 5<br />

1.2 XML-Parsing in Apache Tomcat . . . . . . . . . . . . . . . . . . . . . . . 6<br />

2.1 Wo AOP angreift . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8<br />

2.2 Eine Java-Klasse Konto <strong>mit</strong> Beispiel-Join-Points . . . . . . . . . . . . . . 9<br />

2.3 Der Advice . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11<br />

3.1 Blackbox-Ansicht eines Projekts . . . . . . . . . . . . . . . . . . . . . . . 14<br />

3.2 Umwandlung eines Java-Projekts in ein AspectJ-Project . . . . . . . . . 16<br />

3.3 Der Aspekt Ausgeben von SQL-Statements zeigt auf eine Codestelle . . . 18<br />

3.4 Main-Methode, auf die <strong>der</strong> Aspekt Ausgeben von SQL-Statements zeigt . 18<br />

4.1 Die excluded-/included-Anweisung . . . . . . . . . . . . . . . . . . . . . 22<br />

4.2 Die technische Architektur des Prototyps . . . . . . . . . . . . . . . . . . 23<br />

4.3 Programmablauf des Prototypen . . . . . . . . . . . . . . . . . . . . . . . 26<br />

5.1 ValueObject-Klasse CountGetterUndSetter . . . . . . . . . . . . . . . . . 31<br />

5.2 Benutzerspezifisches Aspekt GetterAndSetter.aj . . . . . . . . . . . . . . 32<br />

5.3 Loggerausgabe <strong>mit</strong> dem Ergebnis des benutzerspezifischen Aspekts . . . . 32<br />

6.1 Die Verwendung <strong>der</strong> config.properties-Datei . . . . . . . . . . . . . . . . 37<br />

43


Listings<br />

2.1 Ein Aspekt in AspectJ . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8<br />

3.1 Die erste Codezeile vom Aspekt SqlStatementsAspect . . . . . . . . . . . 17<br />

3.2 Pointcut- und Join-Point-Definition im Aspekt SqlStatementsAspect . . . 17<br />

3.3 Advice-Definition im Aspekt SqlStatementsAspect . . . . . . . . . . . . . 17<br />

6.1 relevante Pointcuts bei <strong>der</strong> Verwendung des Prototyps . . . . . . . . . . . 35<br />

6.2 Benutzungsbeispiel des within() im Prototyp . . . . . . . . . . . . . . . . 35<br />

6.3 Die Konfigurationsdatei config.properties . . . . . . . . . . . . . . . . . . 38<br />

6.4 Ein einfaches compile Target für Ant . . . . . . . . . . . . . . . . . . . . 39<br />

44

Hurra! Ihre Datei wurde hochgeladen und ist bereit für die Veröffentlichung.

Erfolgreich gespeichert!

Leider ist etwas schief gelaufen!