Build - Tools - Department of Information Systems

Transcrição

Build - Tools - Department of Information Systems
Westfälische Wilhelms-Universität Münster
Ausarbeitung
Build - Tools
im Rahmen des Seminars „Softwaretechnik“
Carsten Fiedler
Themensteller: Prof. Dr. Herbert Kuchen
Betreuer: Christoph Lembeck
Institut für Wirtschaftsinformatik
Praktische Informatik in der Wirtschaft
Inhaltsverzeichnis
1
Grundlegende Aufgaben von Build-Tools.................................................................. 3
2
make............................................................................................................................ 4
2.1
Anwendungsbereiche von make ....................................................................... 4
2.2
Grundlegende Kommandos und Aufbau eines Makefiles ................................ 5
2.2.1
2.2.2
2.2.3
3
4
5
Explizite Regeln............................................................................................ 5
Variablen in Makefiles.................................................................................. 6
Implizite Regeln............................................................................................ 7
Ant .............................................................................................................................. 8
3.1
Grundlegende Aufgaben von Ant ..................................................................... 8
3.2
Installation von Ant, grundlegende Kommandos und Aufbau eines XMLBuildfiles........................................................................................................... 9
3.3
Beispiel für ein Buildfile................................................................................. 15
3.4
Entwicklungstendenzen .................................................................................. 16
XDoclet..................................................................................................................... 16
4.1
Grundlegende Entwicklungsidee von XDoclet............................................... 16
4.2
Einführung in Javadoc .................................................................................... 17
4.3
Installation von XDoclet und grundlegende Befehlstruktur, Tasks und
Subtasks .......................................................................................................... 18
4.4
XDoclet Tag Definitionen .............................................................................. 19
4.5
Templates........................................................................................................ 22
4.6
XDoclet-Integration in einem Ant Buildfile................................................... 23
Gegenüberstellung von make, Ant und XDoclet ...................................................... 24
A Ant-Buildfile mit paralleler Abarbeitung ................................................................. 25
B Kommentierte Java-Klasse zur Berechnung von Quadratzahlen ............................. 26
Literaturverzeichnis ........................................................................................................ 27
II
Kapitel 1: Grundlegende Aufgaben von Build-Tools
1 Grundlegende Aufgaben von Build-Tools
Heutige Anwendungen bestehen aus Abhängigkeitsbäumen mit hunderten von
Modulen. Diese werden sehr schnell unübersichtlich und insb. schwer erweiterbar und
wartbar. Wird ferner bei einer Re-Kompilierung nur ein einzelnes Moduls vergessen,
kann dies eine falsche Generierung des gesamten Programms zur Folge haben. Aus der
Überlegung heraus, diese Übersetzungsprozesse automatisch und vor allen Dingen
möglichst effizient durchzuführen, haben die Entwicklung von Build-Tools ins Leben
gerufen.
Die zunächst grundlegende Aufgabe von Build-Tools ist eine vollständige Übersetzung
einer Anwendung bzw. deren Module, wobei im Falle einer Code-Erweiterung unter
„vollständig“ nicht immer eine vollständige Re-Kompilierung aller Module zu
verstehen ist, sondern vielmehr die Eigenschaft, nach jedem Build-Vorgang eine
komplette Anwendung zu erzeugen. Nach einer Änderung sollen bei der anschließenden
Übersetzung nur die relevanten, d.h. die durch die Änderung betroffenen Module,
übersetzt werden. Dies spart Zeit und Kosten. Hier setzt das ursprünglich von Stuart I.
Feldman für das Betriebssystem UNIX entwickelte make an, dessen grundlegende
Funktionsweise in Kapitel 2 näher erörtert wird. Grundbestandteil eines Build-Tools
bildet
das
so
genannte
Buildfile.
In
einem
Buildfile
wird
mit
einer
Beschreibungssprache festgelegt, in welchen Schritten eine Anwendung generiert
werden soll. In aller Regel werden diese Strukturierungsschritte als Targets oder Rules
bezeichnet. Innerhalb dieser Targets/Rules werden die eigentlichen Kommandos
ausgeführt. Ferner können globale und lokale Variablen für Parameterübergaben
(Umgebungsvariablen, Verzeichnisse, Dateinamen etc.) innerhalb eines Buildfiles
definiert werden.
Die Weiterentwicklung der höheren Programmiersprachen, insb. die Idee der
Entwicklung Plattform unabhängiger (objektorientierter) Programmiersprachen, die sich
auch für die Programmierung von verteilten Anwendungen eigenen (z.B. Java) haben
neben dem Build-Tool make und seinen Derivaten für andere Betriebssysteme, die
Entwicklung alternativer Build-Tools gefördert. Ein besserer Ansatz für die
Generierung plattformunabhängiger Java-Anwendungen bildet z.B. das Build Tool Ant,
welches selbst in Java programmiert ist und als Basis lediglich die Installation des
plattformspezifischen Java Development Toolkits voraussetzt, dessen Arbeitsweise in
3
Kapitel 2: make
Kapitel 3 ausführlich dargestellt werden soll. Allerdings eignet sich Ant für die
Generierung von verteilten Anwendungen (z.B. einer J2EE Anwendung) auch nur im
begrenztem Maße, da bei verteilten Anwendungen neben dem eigentlichen Quellcode
weitere Konfigurationsdateien (z.B. der EJB Deployment Descriptor) erforderlich sind.
Hier wird Ant durch das ebenfalls in Java generierte Code-Generierungstool XDoclet
erweitert, auf dieses in Kapitel 4 näher eingegangen wird.
2 make
2.1 Anwendungsbereiche von make
make wurde in den 70er Jahren von Stuart I. Feldman für die automatische Generierung
von Programmen unter UNIX entwickelt. Inzwischen gibt es verschiedene Arten von
make für alle bekannten Plattformen mit einigen individuellen Besonderheiten. Tabelle
1 listet einige Varianten auf. Im weiteren Verlauf wird exemplarisch auf die GNU Make
- Variante eingegangen. Von GNU Make (im weiteren nur noch make genannt)
existieren Portierungen für verschiedene Plattformen, die mit unterschiedlichen
Aufrufen erfolgen, um Konflikte mit anderen make-Varianten zu vermeiden.
Name
Einsatzbereich
make
GNUmake der Free Software Foundation
Pmake
BSD-Variante, Solaris
Nmake
MS-DOS, Windows, Microsoft
make
MS-DOS, Windows, Borland
Tabelle 1: Einige make - Varianten
Zwei grundsätzliche Anforderungen sollen durch make erfüllt werden. Zum einen soll
eine Flexibilität bei der Übersetzung von Quelltexten, insb. Automatisierung der
Übersetzung erreicht werden, zum anderen eine einfache Anpassung an unterschiedliche
Umgebungen realisiert werden, z.B. Lokalisierung von Bibliotheken im Dateisystem
der jeweiligen Maschine. Darüber hinaus existieren weitere Anwendungsbereiche für
make, z.B.
zur automatischen Generierung von Dokumenten, Manuals oder die
automatische Aktualisierung von Datenbeständen jeglicher Art (z.B. für eine
Adressverwaltung).
4
Kapitel 2: make
2.2 Grundlegende Kommandos und Aufbau eines Makefiles
Wie bereits einleitend erwähnt, bildet das so genannte Buildfile, bei make klassisch
Makefile genannt, die Grundlage für die Code-Generierung. Im weiteren Verlauf von
Kapitel 2 wird deshalb ausschließlich der Begriff Makefile verwendet. Makefiles
werden in einer eigenen Beschreibungssprache formuliert, auf deren grundlegenden
Bestandteile im weiteren Verlauf genauer eingegangen wird. Makefiles werden
sequentiell abgearbeitet. Der Aufruf eines Makefiles erfolg über:
make [ -f || --file || --makefile ] myMakefile
2.2.1 Explizite Regeln
Ein Makefile besteht aus einer Reihe so genannter Regeln, innerhalb derer beschrieben
wird, wie aus einer oder mehren Quellen ein Ziel/Target erzeugt wird. Der Aufbau einer
Regel lässt sich wie folgt darstellen.
Ziel: Quelle_1 ; Quelle_2
<tab> Befehl_1
<tab> Befehl_2
Hier wird das Ziel aus den Quellen 1 und 2 durch eine sequenzielle Ausführung der
Befehle 1 und 2 erzeugt. Es ist anzumerken, dass jede Befehlszeile durch einen
Tabulator eingerückt werden muss, damit make diese auch als Kommandozeile
interpretiert. Jeder Befehl entspricht einem eigenen Shell-Kommando, z.B. der Aufruf
des C-Compilers gcc.
Ein Einfaches Beispiel eines Makefiles:
# Einfaches Makefile
# Das Programm foobar setzt sich aus den
# beiden Objektdateien foo.o und bar.o zusammen
foobar: foo.o bar.o
gcc foo.o bar.o –o foobar
# Regel zur Erzeugung von foo.o
# durch Aufruf des C-Compilers gcc
foo.o: foo.c
gcc foo.c –c
# Regel zur Erzeugung von bar.o
# durch Aufruf des C-Compilers gcc
bar.o: bar.c
gcc bar.c –c
Bei einem Aufruf von make foobar findet eine komplette Erzeugung des Programms
foobar statt, indem zunächst die beiden C-Programme foo.c und bar.c kompiliert
werden und anschließend die daraus generierten Objektdateien foo.o und bar.o durch
5
Kapitel 2: make
den Linker zusammengebunden werden. Der dazugehörige Syntaxbaum zur
Beschreibung der Abhängigkeiten ist in Abbildung 1 dargestellt. Dieser wird durch
make von unten nach oben bearbeitet.
Abbildung 1: Syntaxbaum für ein einfaches Makefile
Innerhalb eines Syntaxbaumes dürfen keine Zyklen existieren. Die darin erhaltenen
Abhängigkeiten könnten zu einer fehlerhaften Codegenerierung durch make führen.
Mit einem Aufruf von make bar.o kann alternativ auch nur eine Generierung der
Objektdatei bar.o veranlasst werden.
Ferner bevorzugt make bei der Übersetzung stets Dateien mit aktuelleren Zeitstempeln.
Dies bedeutet, dass z.B. eine Objektdatei nur dann neu erzeugt wird, wenn die
Quelldatei neuren Datums ist.
2.2.2 Variablen in Makefiles
Grundsätzlich werden Variablen in aller Regel als Bezeichner definiert, die eine
Zeichenkette repräsentieren. Diese Zeichenkette (Wert der Variablen) wird durch das
Programm make anstelle der Variablen eingesetzt, wenn die Variable innerhalb eines
Makefiles auftritt. Innerhalb der Makefiles können eigene Variablen gesetzt oder
Umgebungsvariablen verwendet werden. Ferner existieren vordefinierte Variablen,
sowie Variablen zur Steuerung des Build-Vorganges.
Der Zugriff auf Variablen erfolgt über den Operator $, wobei dies in den Varianten
$(Variable), ${Variable} oder $Variable erfolgen kann. Bei der letzteren Variante
sind entsprechend keine Leerzeichen zulässig.
make unterscheidet zwischen verschiedenen Arten von Variablendeklarationen, die im
weiteren Verlauf kurz dargestellt werden sollen.
• Einfache Variablenzuweisung, z.B.
W := Welt
6
Kapitel 2: make
• Variablen erweitern, z.B.
PATH:=$PATH:/usr/local/bin
PATH+=/usr/local/bin
• Bedingte Zuweisung, z.B.
Name?=Wert
• Mit Hilfe des Ausdrucks define (GNU make Besonderheit), z.B.
define Name
Wert_1
Wert_2
endef
Mit define werden Folgen von einzelnen Werten einem Bezeichner zugeordnet.
Darüber hinaus kann mit diesem Ausdruck auch eine Folge von Befehlen definiert
werden.
Mit Hilfe der Direktive override können Variablen erzwungen werden, d.h. die beim
Kommandozeilenaufruf make gesetzten Variablen überschrieben werden, z.B.
override BIN_PATH := /usr/local/bin
Im obigen Beispiel enthält die Variable BIN_PATH zwingend das Verzeichnis
/usr/local/bin, auch wenn diese zuvor beim Kommandozeilenaufruf mit einem anderen
Verzeichnis initialisiert wurde.
Eine Variablenzuweisung kann ferner mit einer Zuweisung eines leeren Wertes wieder
aufgehoben werden. Umgebungsvariablen können durch -e explizit oder automatisch
durch lokale Variablen überschrieben werden.
Es existieren zahlreiche vordefinierte Variablen für die unterschiedlichen make
Plattformen auf die aber im weiteren Verlauf nicht näher eingegangen werden soll.
Ferner existieren spezielle Variablen für Ziele / Abhängigkeiten.
2.2.3 Implizite Regeln
Für eine Reihe von Standardfällen kennt make vordefinierte Regeln, d.h. für immer
wiederkehrende Fälle (Muster), die sich lediglich durch eine andere Vorbedingung
unterscheiden. So kann z.B. der Aufruf gcc in zahlreichen Fällen erfolgen, die sich
lediglich durch ihre vorherigen Abläufe unterscheiden.
%.o: %.c
${CC} -c $<
7
Kapitel 3: Ant
Das obige Beispiel bewirkt ein Kompilieren beliebiger .c-Dateien zu Objekt-Dateien .o.
% ist ein Platzhalter für beliebige Zeichenketten. Wenn das Ziel verfolgt wird, nur
bestimmte C-Dateien zu übersetzten, muss der entsprechende String vor dem %-Zeichen
bekannt gemacht werden, z.B. main% für alle Dateien, deren Name mit dem String
„main“ beginnt. $(CC) ist ferner ein Makro für den Aufruf des C-Compilers gcc. $< ist
ein Makro dafür, dass der erste Teil der aktuellen Regel verwendet wird. Im obigen
Beispiel sind dies alle C-Quelldateien.
Für eine Reihe von Dateitypen gibt es bereits vordefinierte implizite Regeln, diese
können mit make -p angezeigt werden. Ferner sei noch angemerkt, dass implizite
Regeln überschrieben werden können.
3 Ant
3.1 Grundlegende Aufgaben von Ant
Ant ist ein Java basiertes Build-Tool mit dem primären Ziel der Generierung einer
vollständigen Java-Anwendung. Die grundsätzliche Aufgabe von Ant, ist die
Spezifizierung von Gruppen von Dateien, auf diese anschließend Build-Kommandos
ausgeführt werden, z.B. das Zusammenfassen aller Java-Klassen zu einem Package und
der Aufruf der Java-Compiler, Kopierkommandos oder das Erstellen von jar-/zipDateien zur Generierung einer vollständig installierbaren Anwendung. Ferner ist das
Erstellen von verteilten Anwendungen möglich (z.B. das Erstellen/Deployment von
Komponenten einer J2EE Anwendung). Darüber hinaus lassen sich Java-Tools wie z.B.
JUnit oder das spätere erläuterte XDoclet integrieren.
Entwickler von Ant ist die Apache Software Foundation, eine Open Source Initiative,
welche ihre Bekanntheit durch das Apache HTTP Server Project erlangt hat.
Ursprünglich war Ant Bestandteil des Apache Jakarta Projektes. Auf Grund der
steigenden Bedeutung und Komplexität wurde Ant zu einem eigenständigen Apache
Projekt umstrukturiert (The Apache Ant Project, seit 18. November 2002). Diese
Änderung hat ferner dazu beigetragen, dass die Package-Bezeichnungen entsprechend
der neuen Namensgebung seit Vers. 1.5.2. von jakarta-ant-versNr in apache-ant-versNr
umbenannt wurden. Die aktuelle Version kann auf http://ant.apache.org/index.html
eingesehen werden.
8
Kapitel 3: Ant
3.2 Installation von Ant, grundlegende Kommandos und Aufbau
eines XML-Buildfiles
Zur Installation von Ant genügt ein einfaches Entpacken der zip- bzw. tar.gz-Datei und
setzen von Umgebungsvariablen beginnend mit ANT_HOME=Wurzelpfad auf das AntVerzeichnis (z.B. ANT_HOME=C:\apache-ant-1.6.2). Der Aufruf von Ant erfolgt über
das Kommando: ant –Parameter
Die Tabelle 2 listet einige wichtige Übergabeparameter auf.
Parameter
Bedeutung
f || file || buildfile <file>
angegebe Datei (standardmäßig build.xml) ausführen
D<property>=<value>
einem Property einen Wert zuweisen
propertyfile <file>
aus einer Datei Properties einlesen
find
Build-Datei im Verzeichnisbaum aufwärts suchen und ausführen
diagnostics
Diagnose-Infos ausgeben (Systemdaten, jdk-Version etc.)
debug
Debug-Infos ausgeben
f || logfile <file>
Log-Ausgaben in eine bestimmte Datei schreiben
Tabelle 2: Grundlegende Parameter für den "ant"-Aufruf
Beispielaufruf:
ant –f EJBExample.xml –logfile log.txt
Wie der obige Beispielaufruf zeigt, wird das Ant-Buildfile (auch Ant-Datei oder BuildDatei genannt) in XML Notation formuliert. Im weiteren Verlauf soll auf deren
grundlegende Bestandteile eingegangen werden und an Hand eines Beispiel-Buildfiles
verdeutlicht werden.
1. Root Tag <project>:
Jedes Buildfile besteht aus genau einem Projekt, welches wiederum rekursiv das
Kommando ant und somit weitere Projekte aufrufen kann. Das Root Tag definiert das
Ant Arbeitsverzeichnis und ermöglicht die Angabe eines Standard-Targets, falls keine
Parameter beim Aufruf von ant übergeben werden. Im folgenden Beispiel ist dies das
Target „main“.
<project name="AntExample" default="main" basedir=".">
Im obigen Beispiel wird ein Projekt namens „AntExample“ angelegt, das StandardTarget „main“ definiert und als Ausgangsverzeichnis das aktuelle Arbeitsverzeichnis
festgelegt.
9
Kapitel 3: Ant
2. Variablen, Properties <property>:
Properties werden dazu verwendet, die durch Ant bearbeitete Anwendung auf die realen
Gegebenheiten des eigenen Rechners anzupassen. In diesen Variablen können
beispielsweise Verzeichnisse, Dateinamen oder Versionsnummern abgelegt werden.
Ferner besteht die Möglichkeit der Definition eigener Property-Dateien, welche
dynamisch in das Buildfile eingebunden werden oder beim Start von Ant als Parameter
übergeben werden können. Die Referenzierung auf eine Property erfolgt über den
Ausdruck ${Property_Name}. Neben der Möglichkeit System-Properties aufzurufen
(z.B. Java Classpath) können eigene Properties gesetzt werden und zur bedingten
Abarbeitung von Targets herangezogen werden (Properties als Flag). Hier ist jedoch zu
beachten, dass einmal gesetzte Variablen innerhalb eines Buildfiles nicht mehr
überschrieben werden können. Sie sind während des gesamten Buildfiles gültig.
Innerhalb des Ant-Kommandozeilenaufrufs definierte Properties sind ferner auch für
alle weiter aufgerufenen Buildfiles gültig. Diesbezüglich wird in 4. (Tasks, insb. <ant>
und <antcall>) genauer eingegangen. Mit dem Ant-Kommando <echoproperties>
können die zum aktuellen Zeitpunkt definierten Properties angezeigt werden. Ferner
besteht die Möglichkeit, Zeitwerte in Properties zu speichern, die in verschiedene
Darstellungen formatiert werden können, z.B. Tageszeit, Tagesdatum etc. Im unten
aufgeführten Beispiel werden 3 Verzeichnisse für das Quell-, Klassen- und
Dokumentationsverzeichnis angelegt.
<property name="src" value="./sources"/>
<property name="cls" value="./classes"/>
<property name="doc" value="./doc"/>
3. Targets <target>:
Zu einem Projekt gibt es eines oder mehrere so genannter Targets. Diese dienen zur
Strukturierung/Modularisierung
der
Buildfiles
und
ermöglichen
festgelegte
Abarbeitungsreihenfolgen. Abhängigkeiten zwischen Targets werden durch das
Schlüsselwort depends signalisiert, z.B. depends=“Target1,Target2“. Mit Hilfe der
Attribute if und unless können Verfügbarkeiten von Properties überprüft werden,
bevor eine Durchführung eines Targets erfolgt. Hierbei ist jedoch zu beachten, dass
deren konkreten Inhalte nicht überprüft werden. Bei der Verwendung des Attributes if
muss eine Property vorhanden sein, bei unless wird nur dann fortgesetzt, wenn die
Property nicht existiert. Beide Attribute können nacheinander in einer Target-Definition
angegeben werden. Es wird dann automatisch ein „logisches UND“ zwischen ihnen
10
Kapitel 3: Ant
gesetzt. Kommandos innerhalb eines Targets werden in der Regel sequentiell
abgearbeitet. Andere Abarbeitungsverfahren sind aber möglich und werden unter 4.
(Kommandos) weiter erläutert.
<target name="compile" if="src">
<echo message="Starting Compiling."/>
<javacsrcdir="${src}" excludes="**/*old" destdir="${cls}"/>
<echo message="Finished Compiling."/>
</target>
Im obigen Beispiel werden nur Java-Quelldateien, deren Dateiname nicht auf „old“
endet, übersetzt und in einem Klassenverzeichnis abgelegt.
4. Kommandos <task>:
Ein Target besteht aus einem oder mehreren eigentlichen Kommandos, den so
genannten Tasks (z.B. Verzeichnisse erstellen über mkdir oder der Java Compileraufruf
javac). Momentan existieren mehr als Hundert von diesen Kommandos. Im weiteren
Verlauf wird auf einige wesentliche Kommandos eingegangen. Zunächst unterscheidet
man zwischen Built-in-Tasks und Optional Tasks. Im Gegensatz zu den Build-in-Tasks
benötigen die Optional Tasks zusätzliche externe Libraries. Eine Übersicht über diese
externen Libraries ist unter http://ant.apache.org/external.html verfügbar. Insb. sind hier
auch Tasks für die Manipulation der Deployment-Descriptoren der einzelnen Java
Enterprise Beans Application Server zu finden. Diese Tasks werden jedoch nicht von
der Apache Software Foundation entwickelt. Tasks können innere Tags beinhalten, z.B.
zur Spezifikation von Verzeichnissen für Bibliotheken. Ferner können Tasks und deren
innere Tags mit eigenen ID’s versehen werden (durch id=“myIdentifier“), um
gezielte Zugriffe zu ermöglichen. Die Tabelle 3 zeigt einige wesentliche Ant
Kommandos.
Task
Bedeutung
ant
neuer Aufuruf eines Buildfiles
antcall
Aufruf eines Targets
echo
Ausgabe einer Meldung während des Buildvorganges
copy
Kopieren von Dateien oder Verzeichnisse
delete
löscht Dateien oder Verzeichnisse
mkdir
Verzeichnis erstellen
property
Property setzen
java
Java-Klasse starten
javac
Java-Compileraufruf
javadoc
Programmdokumentation generieren
ear
J2EE Enterprise Application-Archive erstellen
Tabelle 3: Einige grundlegende Ant-Tasks
11
Kapitel 3: Ant
Wie bereits einleitend erwähnt, kann innerhalb des Buildfiles ein rekursiver ant-Aufruf
erfolgen. Es wird dann ein weiteres Buildfile aufgerufen. Darüber hinaus besteht die
Möglichkeit mit Hilfe des Kommandos <antcall target=“Targtename“> weitere
Targets innerhalb des gleichen Buildfiles aufzurufen, ohne Abhängigkeiten über
depends definieren zu müssen. Bei dem Aufruf von <ant> oder <antcall> können
ferner Properties und Referenzen an das aufzurufende Target übergeben werden.
Tabelle 4 zeigt diese Möglichkeiten und die Default-Belegungen auf.
Attribut
Bedeutung
target
das aufzurufende Target
Default
inheritall
Properties an das aufzurufende Target weitergeben
True
inheritrefs
Referenzen an das aufzurufende Target weitergeben
False
-
Tabelle 4: Spezielle Attribute für <ant> - bzw. <antcall> - Aufruf
Die obige Tabelle zeigt, dass die Übergabe von Properties an die aufgerufenen Targets
eingeschränkt werden kann, indem das Attribut inheritall auf false gesetzt wird. Im
Gegensatz zu depends, bietet <antcall> durch diese Einschränkung die Möglichkeit,
dass in den untergeordneten Targets neue Properties definiert werden können. Ferner ist
der Aufruf von <antcall> innerhalb eines Targets möglich, während bei der
Verwendung von depends das abhängige Target erst dann ausgeführt wird, wenn das
vorherige Target abgearbeitet wurde. Ähnlich wie bei <ant> über das Target
<property>, besteht bei <antcall> die Möglichkeit, mit dem Target <param> (als
eingebettetes Tag) eigene Properties zu definieren. Beide Targets bieten die Möglichkeit
über <references> Referenzen zu definieren, die anderen Targets übergeben bzw. als
Verweis in andere Kommandos eingefügt werden können.
Wie schon zuvor erläutert, können Tasks auch parallel abgearbeitet werden. Ferner
können Wartezeiten definiert werden, um auf das Eintreffen spezieller Ereignisse zu
warten. Tabelle 5 zeigt die dazu notwendigen Tasks auf.
Task
Aufgabe
parallel
alle enthaltenen Kommandos werden als parallel laufende Tasks gestartet
sequentiell
alle enthaltenen Kommandos werden als sequentielle Tasks gestartet
waitfor
Warten auf das Eintreffen eines bestimmten Ereignisses
sleep
Unterbrechung der Programmausführung um eine angegebene Zeit
Tabelle 5: Spezielle Tasks für die Abarbeitungsreihenfolge
Bei der Verwendung des <waitfor> - Tasks können max. Wartezeiten über die
Attribute maxwait und maxwaitunit oder Timeouts über <timeoutproperty> gesetzt
12
Kapitel 3: Ant
werden, um lange bzw. unendliche Wartezeiten bei nicht eintreffenden Ereignissen zu
unterbinden. Ferner kann genau festgelegt werden, in welchen Zeitabständen die
Bedingung überprüft werden soll. <sleep> und <waitfor> können genauer über die
Attribute hours, minutes, seconds oder milliseconds spezifiziert werden.
Das folgende Beispiel zeigt eine parallele Abarbeitung zweier Targets. Das Target
compile kompiliert alle Java-Quelldateien.. Target documentation erzeugt die Java
Dokumentation. Es wird maximal 60 Sekunden auf die Kompilierung gewartet, bevor
versucht wird, die generierte Klasse HelloPeople zu starten. Wird diese Klasse
innerhalb der 60 Sekunden nicht generiert, erfolgt automatisch bei dem anschließenden
Java-Aufruf eine Fehlermeldung durch Ant, weil die Klasse nicht im Classpath
gefunden werden kann. Die maximale Wartezeit wird nicht ausgeführt, wenn die Klasse
HelloPeople vorzeitig generiert worden ist, weil alle 500 Millisekunden eine
Überprüfung auf die Existenz der Datei im Klassenverzeichnis durchgeführt wird.
<target name="main">
<!-- run compile and documentation -->
<parallel>
<antcall target="compile"/>
<antcall target="documentation"/>
</parallel>
<!-- wait for compiling -->
<waitfor maxwait="60" maxwaitunit="second" checkevery="500">
<available file="${cls}/HelloPeople.class"/>
</waitfor>
<!-- start application -->
<java classname="HelloPeople">
<classpath> <pathelement path="${cls}"/> </classpath>
</java>
</target>
Ein vollständiges, lauffähiges Beispiel ist im Anhang A aufgeführt.
5. Filesets <fileset>:
Mit Filesets werden Listen von einer oder mehreren Dateien spezifiziert. Dieses Tag ist
kein eigenständig ausführbares Kommando, sondern wird als inneres Tag von anderen
Kommandos, z.B. bei der Definition von PATH- oder CLASSPATH-Variablen
verwendet. Es ist jedoch möglich, Filesets als eigenständiges Tag zu definieren, welches
als Referenz in andere Kommandos eingebunden wird. Ferner besteht die Möglichkeit,
Fileset-Eigenschaften an andere ausführbare Kommandos zu vererben, damit diese
Kommandos auf den gleichen Dateistrukturen arbeiten können.
<copy todir=“src_new“>
<fileset dir=“src“ includes=“**/*.java“ excludes=”**/*old”/>
</copy>
13
Kapitel 3: Ant
Im obigen Beispiel werden alle Java-Quelldateien, deren Dateiname nicht auf „old”
endet, in das Verzeichnis „src_new“ kopiert. **/*
bedeutet hier, dass alle
Unterverzeichnisse mit einbezogen werden. Das Attribut dir ist Pflichtbestandteil für
jedes Fileset. Es definiert das Ausgangverzeichnis für weitere Operationen. Zur
Auswahl bestimmter Dateien können die Attribute includes und excludes verwendet
werden. Diese Variante kann aber bei größeren Dateiangaben schnell recht
unübersichtlich werden. Alternativ besteht deshalb die Möglichkeit, eigenständige
<includes> - bzw. <excludes> - Tags zu verwenden. Das obige Beispiel würde sich
dann wie folgt verändern.
<copy todir=“src_new“>
<fileset dir=“src“>
<includes=“**/*.java“/>
<excludes=”**/*old”/>
</copy>
Die alternative Variante ermöglicht ferner die Angabe von Bedingungen. So könnte
beispielsweise das <includes> - Tag wie folgt erweitert werden:
<includes=“**/*.java“ if=”myProperty”/>
6. Patternsets <patternset>:
Patternsets sind eine Erweiterung für Filesets. Sie vereinen die schon bekannten Tags
zur Bildung von Mustern, die sich auf Verzeichnis- und Dateinamen beziehen.
Patternsets können als innere Tags von Filesets, sowie als eigenständige Tags formuliert
werden. Im letzteren Fall ist eine Referenzierung über eine zuvor definierte ID möglich.
(1) Beispiel für ein Patternset:
<patternset id=“pset“>
<includes name=”*.java” if=”myProperty”>
</patternset>
(2) Beispiel für die Integration des Patternsets in einem Fileset:
<target name=“main“>
<copy todir=“targetDirectory“>
<fileset dir=“.“> <patternset refid=“pset“> </fileset>
</copy>
</target>
Das obige Beispiel (1) definiert ein Patternset, welches über die ID „pset“ referenziert
werden kann, wie dies im Beispiel (2) über das Attribut refid illustriert wird.
14
Kapitel 3: Ant
Neben den Filesets und Patternsets gibt es noch die so genannten Filelists zur expliziten
Angabe fester Dateinamen. Darüber hinaus existieren Directory-based Tasks, welche
ein automatisches Durchsuchen ganzer Verzeichnisbäume auslösen können.
3.3 Beispiel für ein Buildfile
Das nachfolgende Beispiel soll einen kleinen Überblick über das Zusammenspiel obiger
Komponenten geben. Zunächst wird das „main“ – Target aufgerufen und das aktuelle
Arbeitsverzeichnis festgelegt. Das „main“ – Target jedoch ist abhängig von dem Target
„documentation“, welches wiederum vom Target „compile“ abhängt. Das „compile“ –
Target ist von keinem weiteren Target abhängig und wird somit als erstes ausgeführt.
Dort werden alle Java-Quelldateien, außer Dateien, deren Namen auf „old“ enden, aus
dem Verzeichnis „sources“
übersetzt und die generierten Klassendateien in das
Verzeichnis „classes“ abgelegt. Die Verzeichnisnamen sind durch Properties am
Anfang des Buildfiles definiert worden. Wenn das Target „compile“ abgearbeitet
worden ist wird das Target „documentation“ ausgeführt, welches die Java
Dokumentation für alle Java-Quelldateien generiert. Die zu selektierenden JavaQuelldateien werden über ein Fileset definiert. In diesem Fall werden alle Dateien und
Unterverzeichnisse ausgewählt (**/* als Wildcard und <includes> für die
Spezifikation der Dateiendung). Alle Dateien, deren Namen auf „old“ enden, werden
nicht dokumentiert (festgelegt durch <excludes>). Die HTML-Dokumentation wird in
dem Verzeichnis „doc“ abgelegt. Auf Details der Java Dokumentation wird in Kapitel 4
eingegangen. Nachdem die Dokumentation erstellt wurde, können die generierten
Klassen in dem „main“ – Target über das java – Kommando ausgeführt werden. Dazu
wird mit dem <classpath> - Tag der Java Classpath mit den generierten Klassen
definiert.
<?xml version="1.0" encoding="UTF-8"?>
<project name="AntExample" default="main" basedir=".">
<!-- set properties for source- and class-folders -->
<property name="src" value="./sources"/>
<property name="cls" value="./classes"/>
<property name="doc" value="./doc"/>
<!-- main (first) target -->
<target name="main" depends="documentation">
<java classname="HelloPeople">
<!-- set classpath-->
<classpath>
<pathelement path="${cls}"/>
</classpath>
</java>
</target>
15
Kapitel 4: XDoclet
<!-- create javadoc -->
<target name="documentation" depends="compile">
<javadoc version="true" private="true" author="true" destdir="${doc}">
<fileset dir="${src}" includes="**/*.java" excludes="**/*old"/>
</javadoc>
</target>
<!-- compile all Java-Classes -->
<target name="compile" if="src">
<javac srcdir="${src}" excludes="**/*old destdir="${cls}"/>
</target>
</project>
3.4 Entwicklungstendenzen
Die Entwicklungsmöglichkeiten sind bei Ant aufgrund des noch jungen Alters recht
vielseitig vorhanden. Zum einen sind eine Erweiterung der Namensräume und weitere
Vererbungsmöglichkeiten bei Properties geplant. Darüber hinaus sollen redundante AntTasks mit gleicher Bedeutung zukünftig zusammengefasst werden, z.B. die Parameter f,
file und filename. Ferner sollen alternative Beschreibungssprachen zu XML realisiert
werden. Eine einfachere Installation mit einem selbst extrahierenden Installer, sowie
eine Verbesserung der Ant Dokumentation sind ebenfalls angedacht. Seit einiger Zeit
existiert bereist eine erste GUI-Lösung für Ant namens antidote mit der eine
einheitliche Oberfläche geschaffen wurde, welche sich unter anderem auch für eine
einheitliche Integration in die zahlreichen Java Entwicklungsumgebungen eignen
würde.
Nähere
Information
sind
unter
http://cvs.apache.org/viewcvs.cgi/ant-
antidote/#dirlist zu finden.
4 XDoclet
4.1 Grundlegende Entwicklungsidee von XDoclet
Wie bereits in Kapitel 3 erläutert, bietet Ant eine gute Möglichkeit größere (Java-)
Applikationen effizient zu generieren. Neben Ant existieren für fast jede der zahlreichen
und teilweise recht unterschiedlichen Java-Anwendungen Tools zur Erstellung der
notwendigen Konfigurationsdateien bzw. Verarbeitung der in der Regel Sourcecode
unabhängig gespeicherten Metadaten mit Konfigurationsinformationen, z.B. die
automatische Generierung eines notwendigen XML-Descriptors einer Enterprise Java
Beans (EJB) Applikation mit dem J2EE Deployment-Tool. Gerade im Hinblick auf die
Skalierbarkeit großer Systeme und die Kosten für deren Entwicklung ist jedoch ein
Einsatz dieser Tools begrenzt. Eine möglichst einfache Editierung der erzeugten
Konfigurationsdateien ist in der Regel recht komplex zu handhaben. Im Sinne der
16
Kapitel 4: XDoclet
Erweiterbarkeit und Überschaubarkeit versucht XDoclet mit einer neuen Strukturierung
der Quelldateien und Metadaten eine effiziente Codegenerierung zu ermöglichen. Die
Quelltexte werden dabei einfach um zusätzliche Javadoc tags erweitert, welche
Informationen für die Codegenerierung (Metadaten) beinhalten. Initiator von XDoclet,
damals noch EJBDoclet genannt, war der Schwede Rickard Öberg, Mitglied im
Entwickler-Team des freien J2EE-Servers JBoss, der für die Entwicklung einer EJB
Applikation seine Quelldateien mit Javadoc tags versehen hat, welche mit dem bereits
existierenden Javadoc-Tool und einem selbst geschriebenen Doclet in den
erforderlichen XML Descriptor Code für die Metadaten übersetzt werden konnten, in
dem von ihm bezeichneten EJB Bean Code. Dieser Ansatz wurde zur Grundlage für die
Entwicklung eines eigenständigen Tools, welches inzwischen auch in anderen
Bereichen, wie. z.B. die Erstellung von Servlets bei Web-Anwendungen gefunden hat.
XDoclet wird in Ant integriert und stellt zahlreiche Tasks und Substasks zur Verfügung.
Im Laufe der Zeit entstand ein eigener Parser (XJavadoc), der ein Objektmodell aller
eingelesenen Klassen erzeugt, auf dieses über so genannte Templates (CodeSchablonen) zugegriffen wird.
4.2 Einführung in Javadoc
Javadoc ist ein Java Dokumentationsgenerator und bildet die Grundlage für XDoclet. Er
erzeugt anhand von Java-Quelltexten eine Dokumentation, in der die jeweiligen
Klassen, Interfaces, Methoden, Konstruktoren und Datenelemente aufgeführt sind. Als
Argument bekommt Javadoc einen Paketnamen oder die Namen von Java-Quelltexten
übergeben. Seit Version 1.2 ist es möglich, die Dokumentation mit selbst definierten
Klassen, den so genannten Doclets, zu generieren. Die unten aufgelistete Beispielklasse
zeigt die klassische Java-Kommentierung (Javadoc tags).
/**
* Diese Klasse berechnet Quadratzahlen von 1 bis maxIndex
* @author Carsten Fiedler
* @version 0.1
*/
public class FirstJavaDocExample {
/** Diese Variable gibt die Anzahl der zu berechnenden Quadrate an.*/
private static int maxIndex = 5;
/** Dieses Array speichert die Ergebnisse.*/
private static int[] squareArray;
/**
* Diese Methode berechnet die Quadrate und
* speichert sie im Array squareArray.
*/
private static void square () {
for (int i = 0; i < maxIndex; i++) {
squareArray[i] = (i+1)*(i+1);
}
17
Kapitel 4: XDoclet
}
/**
* Die Startroutine des Programms.
* @param args Kommandozeilenparameter.
* Diese werden hier nicht verwendet.
*/
public static void main (String[] args) {
…
}
…
}
Der vollständige Java-Quellcode zur Berechnung der Quadratzahlen von 1 bis
maxIndex ist im Anhang aufgeführt. Durch den Kommandozeilenaufruf
javadoc -version -private -author FirstJavaDocExample.java
wird nun die Standard-HTML-Dokumentation aus diesen Dokumentationstexten
erzeugt.
Mit
dem
Parameteraufrufen
–doclet
<class>
und
-docletpath
<classpathlist> kann der Entwickler ferner eigene Doclets spezifizieren. Für das
obige Beispiel wurden diese Parameter nicht angegeben, was den Aufruf eines
Standard-Doclets zur Folge hat.
4.3 Installation von XDoclet und grundlegende Befehlstruktur, Tasks
und Subtasks
Zur Installation von XDoclet genügt ein einfaches Entpacken der Binaries (xdoclet-binversNr.zip). Da XDoclet als Ant-Task intergriert wird, muss zum einen der Pfad zu
XDoclet im Ant-Buildfile gesetzt werden und zum anderen der eigentliche Task-Aufruf
erfolgen. Diesbezüglich kann zwischen mehreren Task-Aufrufen gewählt werden.
Tabelle 6 zeigt einige grundlegende Typen auf.
Task
Einsatzbereich
<ejbdoclet>
EJB, EJBs, Utiltity-Klassen und Deployment-Descriptoren
<webdoclet>
Web-Entwicklung, Servlets, Filters, Taglibs, Web Frameworks
<hibernatedoclet>
Hibernate Persitance, Configuration, Mbeans
<doclet>
Eigene Templates für die ad hoc Codegenerierung
<documentdoclet>
Project Documentation wie z.B. to do tags
Tabelle 6: Beispiele fur XDoclet-Tasks
Im weiteren Verlauf wird exemplarisch auf <ejbdoclet> genauer eingegangen werden,
weil dieser Task durch die ursprüngliche Entwicklungsidee von EJBDoclet am
weitesten entwickelt ist und dadurch am meisten an Popularität gewonnen hat. Natürlich
sind die Tasks in Praxis kombinierbar, z.B. <ejbdoclet> und <webdoclet> für die
Entwicklung einer vollständigen EJB Web-Applikation. Zu einem Task existieren
18
Kapitel 4: XDoclet
weitere Subtasks. Diese bilden die einzelnen Code-Generierungsschritte. In der
folgenden Tabelle 7 sind einige wesentliche zu <ebjdoclet> gehörende Subtasks im
Detail aufgelistet, auf diese exemplarisch in einem späteren Beispiel eingegangen wird.
Subtask
Beschreibung
<deploymentdescriptor>
generiert den ejb-jar.xml Deployment Descriptor
<entitybmp>
<entitycmp>
generieren die konkreten Klassen der BMP, CMP und Session Beans
<session>
<entitypk>
generiert die Primary Key Klassen für Entity Beans
<localhomeinterface>
generieret das Home Interface
<localinterface>
generieret das Remote Interface
Tabelle 7: Beispiele für Subtasks von <ejbdoclet>
4.4 XDoclet Tag Definitionen
Wie bereits einleitend erwähnt, werden die XDoclet-Tags mit den Metainformationen
direkt in den Quellcode als Java Dokumentation integriert. Diese Tags bestehen jeweils
aus 4 wesentlichen Elementen, dem Namespace, Tagname, dem Attributnamen und den
Attributwerten. Eine allgemeine syntaktische Formulierung lautet wie folgt:
</**
* @Namespace:Tagname Attributname=“Attributwert“
*/
1. Namenspace
Der Namespace gibt den grundsätzlichen Typ der Codegnerierung an. Tabelle 8 gibt
einen Überblick über einige verfügbare Namespaces.
Subtask
Beschreibung
ejb
Standard EJB Information (herstellerunabhängig)
jboss
Information für den JBoss Application Server
weblogic
Information für den BEA WebLogic Application Server
websphere
Information für den IBM WebSphere Application Server
web
generiert eine web.xml Konfigurationsdatei für Web Applications
jsp
generiert Tag Library Extension Descriptor -Information
Tabelle 8: Auflistung einiger Namespaces
2. Tagname
Der Tagname bildet den Überbegriff für die zu realisierende Konfiguration, z.B.
@ejb.bean, der eine konkrete Enterprise Java Bean spezifiziert. Dieser Tag gehört zu
den EJB-Tags auf Klassenebene. Durch diese Tags werden allgemeine Informationen
19
Kapitel 4: XDoclet
wie der Bean-Name und Bean-Typ oder Details über Relationen, Primary Keys und
Finder-Methoden definiert. Eine Übersicht über einige wesentliche EJB-Tags ist der
Tabelle 9 zu entnehmen.
Tag
Beschreibung
@ejb.bean
allgemeine Informationen über Beans
@ejb.dao
definiert das Data Access Object einer Entitiy Bean
@ejb.ejb-external-ref
defniert eine Referenz auf eine externe Bean (ejb.jar Datei)
@ejb.finder
definiert eine Finder-Methode für das Home-Interface
@ejb.home
Spezifikation des Home-Interfaces einer Bean
@ejb.persistence
Information über die Persitenz (Tabellenname, Zugriffsrechte)
@ejb.pk
legt den Primärschlüssel einer Bean fest
Tabelle 9: Beispiele für Tags auf Klassenebene
Darüber hinaus existieren noch Tags auf Methodenebene, mit denen z.B. Sichtbarkeiten
der Schnittstellen, Transaktionstypen oder Persistenzinformation definiert werden
können. Tabelle 10 gibt einen Überblick über einige wesentliche EJB-Tags.
Tag
Beschreibung
@ejb.persistence
Information über die Persistenz (Tabellenname, Zugriffsrechte)
@ejb.pk-field
markiert das aktuelle Feld als Primärschlüssel
@ejb.interface-method
defniert, in welcher Schnittstelle die Methode erscheint
@ejb.transaction
definiert das Tranksaktionverhalten
@ejb.create-method
markiert ejbCreate Methode
Tabelle 10: Beispiele für Tags auf Methodenebene
3. Attributname und Attributwerte
Zu jedem Tag gehören zahlreiche Attribute, deren Werte die eigentliche Konfiguration
bilden. Einige exemplarische Typen werden in dem folgenden Beispiel dargestellt.
/**
* Bean-Klasse der Entity Bean (mit CMP) Artikel.
* @author Carsten Fiedler
* @version 0.1
*
XDoclet-Attributerläuterungen:
* @ejb.bean name="Artikel"
Name im ejb-jar.xml Deployment Descriptor benutzt wird
*
display-name="ArtikelBean"
Anzeigenname der Bean
*
view-typ="local"
view-types, die durch die Bean unterstützt werden
*
type="CMP"
definiert den Bean Typen
*
primary-field="artikelNr"
Festlegung des Primary Keys für die Bean
*
cmp-version = "2.0"
Angabe der Versionsnummer für CMP
*/
public abstract class ArtikelBean implements EntityBean {
protected EntityContext ctx;
/**
* Methode liefert die Artikelnummer (Primary Key).
* @return liefert einen String zurück, der den Primary Key darstellt.
* @ejb.pk-field
* @ejb.interface-method view-type="local"
* @ejb.transaction type="Required"
*/
20
Kapitel 4: XDoclet
public abstract String getArtikelNr();
/**
* Metode setzt Artikelnummer (Primary Key)
* @ejb.persistent-field
*/
public abstract void setArtikelNr(String artikelNr);
/**
* Methode liefert den Artikelpreis.
* @return Liefert einen String zurück, der den Primary Key darstellt.
*/
public abstract double getPreis();
/**
* Methode setzt den Artikelpreis.
*/
public abstract void setPreis(double preis);
/**
* Callback-Methoden (werden bei Bedarf vom Container aufgerufen).
* Diese Methoden sind grösstenteils leer implementiert, da
* container-managed persistence verwendet wird.
*/
public void ejbActivate() { }
public void ejbRemove() { }
public void ejbPassivate() { }
public void ejbLoad() { }
public void ejbStore() { }
public void setEntityContext(EntityContext ctx) {
this.ctx = ctx;
}
public void unsetEntityContext() {
this.ctx = null;
}
/**
* @param id
* @param price
* @throws CreateException
*/
public void ejbPostCreate(String artikelNr, double preis) throws
EJBException { }
/**
* Diese Methode legt einen neuen Artikel an. Die Methode wird vom
* Container automatisch aufgerufen, sobald create(.) auf dem
* Home-Object aufgerufen wird. Die Argumente (Typen) von
* create(.) und ejbCreate(.) muessen folglich ueberbereinstimmen.
* @ejb.create-method
*/
public String ejbCreate(String artikelNr, double preis) throws
CreateException, EJBException {
setArtikelNr(artikelNr);
setPreis(preis);
return null;
}
}
Die obige Java Klasse repräsentiert eine Entitiy Bean für ein Objekt vom Typ Artikel.
Sie beinhaltet Methoden zum Setzen und Auslesen der Artikelnummer (Primary Key),
zum Festlegen und Auslesen des Preises, sowie die durch die Realisierung des
Interfaces EntitiyBean zu überschreibenden Methoden für das Erzeugen der
EntitiyBean-Objekte und die Callback Methoden. Die obigen EJB-Tags auf
Klassenebene definieren die Bean-Namen, die Version des CMP’s, die durch die Entity
Bean unterstützten View Types und die Artikelnummer als Primary Key. Auf der
21
Kapitel 4: XDoclet
Methodenebene wird zum einem das @ejb.create-method - Tag zum Kennzeichnen
der ejbCreate-Methode zum Erzeugen von Bean Objekten verwendet, zum anderen wird
die Methode zum Auslesen des Primary Keys näher beschrieben, indem festgelegt wird,
wie der Zugriff bzw. Transaktionsverhalten zu gewährleisten ist.
Die Beispielkonfiguration einer Session Bean lässt sich auf ähnliche Art und Weise
generieren, wobei es weitere Attribute für die Implementierung der Geschäftslogik gibt.
4.5 Templates
XDoclet wird als ein so genanntes Template basiertes Codegenerierungstool bezeichnet,
wobei
der
generierte
Output
auf
Code
Templates
(Code-Schablonen)
in
unterschiedlichen Kontexten basiert, d.h. Templates speichern die reinen Inputdaten,
sowie die Metadaten der Konfiguration, d.h. sie legen fest, was aus den Tags der
Javaklasse generiert werden muss. Sie bilden eine Art Prototyp der zu generierenden
Files bzw. Descriptoren, bei denen konkrete Parameter noch festgelegt werden müssen.
An Hand der Templates entscheidet die so genannte Template Engine, wie der
generierte Code angepasst werden muss. Templates haben die Endung „.xdt“. Ihr
Aufbau ähnelt JSP-Dateien. Sie bestehen aus Text und XML tags (beginnend mit XDt).
Neben den zahlreich von XDoclet mitgelieferten Templates besteht die Möglichkeit,
eigene zu programmieren.
Ein einfaches Beispiel für ein selbst definiertes Template könnte wie folgt aussehen:
<!-- short Template for XDoclet -->
log for deprecated classes
-------------------------<XDtClass:forAllClasses>
<XDtClass:fullClassName/>
<XDtClass:ifHasClassTag tagName="deprecated">
WARNING: This class is deprecated.
NOTE: <XDtClass:classTagValue tagName="deprecated"/>
</XDtClass:ifHasClassTag>
</XDtClass:forAllClasses>
Das obige Template gibt für alle übergebenen Klassen den jeweiligen Klassennamen
aus. Ferner prüft es jeweils, ob die aktuelle Klasse mit einem Tag als “deprecated”
markiert worden ist. Wenn dies der Fall ist, wird eine Warnung mit den entsprechenden
Attributswerten
ausgegeben.
Das
folgende
Bsp.
zeigt
die
entsprechende
Kommentierung in der Java-Klasse.
/**
* deprecated class
* @deprecated
*
value1
22
Kapitel 4: XDoclet
*
*/
value2
4.6 XDoclet-Integration in einem Ant Buildfile
Das folgende Beispiel zeigt die Integration von XDoclet Tasks in einem Ant-Buildfile.
Das Projekt „AntDocletExample“ hat die Aufgabe, zu allen Beanklassen aus einem
Quellverzeichnis die Remote/Local- und Home-Interfaces, sowie den J2EEDeploymentdescriptor zu generieren und in einem „build“ – Verzeichnis zu speichern.
Dazu werden zunächst die Variablen für die Arbeitsverzeichnisse und den J2EE-, sowie
XDoclet-Classpath definiert. Anschließend werden im init-Target die genauen Pfade für
den ejbdoclet-Aufruf festgelegt. Der ejbdoclet-Aufurf erfolgt schließlich im Traget
generateEJB. Dort wird über die entsprechenden Subtasks die Generierung des
Deploymentdescriptors und Interfaces veranlasst. Die Quelldateien werden über das
Fileset-Target spezifiziert. Die generierten Dateien werden im Verzeichnis „Build“
abgelegt.
<?xml version="1.0" encoding="UTF-8"?>
<project name="AntXDocletExample" default="main" basedir=".">
<!-- properties for src- and build-directory-->
<property name="src" value="./sources"/>
<property name="build" value="./build"/>
<!-- add XDoclet and J2EE path -->
<property name="xdoclet.lib.dir" value="./xdoclet-1.2.2/lib/"/>
<property name="lib.dir" value="C:/j2sdkee1.3.1/lib"/>
<path id="xdoclet.lib.path">
<fileset dir="${xdoclet.lib.dir}" includes="*.jar"/>
</path>
<!-- main (first) target -->
<target name="main" depends="generateEjb">
<echo message="Build seems to be okay! ;-)"/>
</target>
<!-- path-,taskdefinition for "ejbdoclet"-call in generateEJB -->
<target name="init">
<taskdef name="ejbdoclet"
classname="xdoclet.modules.ejb.EjbDocletTask">
<classpath>
<fileset dir="${xdoclet.lib.dir}">
<include name="**/*.jar"/>
</fileset>
<fileset dir="${lib.dir}">
<include name="**/*.jar"/>
</fileset>
</classpath>
</taskdef>
</target>
<!-- get Bean-Classes, create deploymentdescriptor and interfaces -->
<target name="generateEjb" depends="init">
<ejbdoclet destdir="${build}">
<fileset dir="${src}">
<include name="**/*Bean.java"/>
</fileset>
<!-- create deploymentdescriptor and interfaces -->
<deploymentdescriptor destdir="${build}"/>
<localinterface/>
<localhomeinterface/>
23
Kapitel 5: Gegenüberstellung von make, Ant und XDoclet
</ejbdoclet>
</target>
</project>
5 Gegenüberstellung von make, Ant und XDoclet
Moderne Anwendungen, die nicht nur durch lange und zahlreiche Quellcodes
gekennzeichnet sind, sondern auch durch eine Vielzahl von Metadaten für
Konfigurationseinstellungen geprägt sind, können mit einfachen Werkzeugen nicht
mehr effizient generiert, erweitert und gewartet werden. Diese Komplexität wird auch
nicht mehr allein durch das etablierte make realisiert, sondern
Bedarf weiterer
Werkzeuge wie das Build-Tool Ant in Kombination mit dem Code-Generierungstool
XDoclet. Obwohl alle vorgestellten Build-Tools den gleichen grundlegenden Zweck,
der automatischen Codegenerierung, erfüllen wollen, existieren doch einige wesentliche
Unterschiede. So ist make dadurch gekennzeichnet, dass es Plattform abhängig arbeitet
und die Buildfiles in einer eigenen Beschreibungssprache definiert. Ant hingegen ist
Plattform unabhängig und durch seine XML-Notation der Buildfiles als „quasistandardisiert“ anzusehen. XDoclet kann wiederum als eine Erweiterung von Ant und
dem Javadoc-Tool angesehen werden. Durch die Meta-Kommentare der Quelldateien
bietet XDoclet ferner einen besseren Überblick über die Aufgabe der gerade
bearbeiteten Komponente und ihre Kommunikation mit anderen Modulen innerhalb der
Anwendung. Dies erleichtert zum einen die Adaption der Module und zum anderen
werden keine weiteren Tools für die Visualisierung der Abhängigkeiten benötigt.
Darüber hinaus werden alle weiteren Konfigurationsdateien automatisch durch XDoclet
erzeugt. So muss beispielsweise nur das Grundgerüst einer Entity- oder SessionBeanKlasse
einmal
definiert
werden.
Die
erforderlichen
Interfaces
und
Deploymentdescriptoren werden jeweils automatisch bei einem erneuten Aufruf von
XDoclet generiert. Abschließend soll festgehalten werden, dass Ant und XDoclet,
make nicht ersetzen können (insb. bei der Generierung einer C / C++ - Anwendung),
aber für eine Java-Anwendung die bessere Code-Generierungs-Lösung bilden.
24
Anhang A: Ant-Buildfile mit paralleler Abarbeitung
A Ant-Buildfile mit paralleler Abarbeitung
<?xml version="1.0" encoding="UTF-8"?>
<project name="AntExample" default="main" basedir=".">
<!-- set properties for source- and class-folders -->
<property name="src" value="./sources"/>
<property name="cls" value="./classes"/>
<property name="doc" value="./doc"/>
<!-- main (first) target -->
<target name="main">
<echo message="Main Target has been started."/>
<!-- run compile and documentation -->
<parallel>
<antcall target="compile"/>
<antcall target="documentation"/>
</parallel>
<!-- wait for compiling -->
<waitfor maxwait="60" maxwaitunit="second" checkevery="500">
<available file="${cls}/HelloPeople.class"/>
</waitfor>
<!-- start application -->
<java classname="HelloPeople">
<!-- set classpath-->
<classpath>
<pathelement path="${cls}"/>
</classpath>
</java>
</target>
<!-- create javadoc -->
<target name="documentation">
<echo message="Building Java Documentation."/>
<javadoc version="true" private="true" author="true" destdir="${doc}">
<fileset dir="${src}" includes="**/*.java" excludes="**/*old"/>
</javadoc>
</target>
<!-- compile all Java-Classes -->
<target name="compile" if="src">
<echo message="Starting Compiling."/>
<javac srcdir="${src}" excludes="**/*old" destdir="${cls}"/>
<echo message="Finished Compiling."/>
</target>
</project>
25
Anhang B: Kommentierte Java-Klasse zur Berechnung von Quadratzahlen
B Kommentierte Java-Klasse zur Berechnung von
Quadratzahlen
/**
* Diese Klasse berechnet Quadratzahlen von 1 bis
* zu einem spezifizierten maxIndex.
*
* @author Carsten Fiedler
* @version 0.1
*/
public class FirstJavaDocExample {
/**
* Diese Variable gibt die Anzahl der zu berechnenden Quadrate an.
*/
private static int maxIndex = 5;
/**
* Dieses Array speichert die Ergebnisse.
*/
private static int[] squareArray;
/**
* Die Startroutine des Programms.
*
* @param args Kommandozeilenparameter. Diese werden hier nicht verwendet.
*/
public static void main (String[] args) {
squareArray = new int [maxIndex];
System.out.println("Das Array fuer die Speicherung der Ergebnisse wurde
mit der Groesse " + maxIndex + " initialisiert.");
square();
System.out.println("Die Ergebnisse lauten:");
printArray(squareArray);
}
/**
* Diese Methode berechnet die Quadrate und
* speichert sie im Array squareArray.
*
*/
private static void square () {
for (int i = 0; i < maxIndex; i++) {
squareArray[i] = (i+1)*(i+1);
}
}
/**
* Diese Methode gibt den Inhalt des Arrays (Quadratzahlen)
* in der Konsole aus.
* @param sArray Das Array mit den Ergebnissen (Quadratzahlen).
*/
private static void printArray (int[] sArray) {
for (int i = 0; i < maxIndex; i++) {
System.out.println("squareArray[" + i + "] == " + squareArray[i] +
" ist die Quadratzahl von " + (i+1) + ".");
}
}
26
Literaturverzeichnis
Literaturverzeichnis
[1]
Helmut Herold: make – Das Profitool zur automatischen Generierung von
Programmen, Addison-Wesley, 2003
[2]
Craig Walls, Norman Richards: XDoclet in Action, Manning, 2004
[3]
GNU Make – GNU Project – Free Software Foundation
http://www.gnu.org/software/make/
[4]
The Apache Ant Project
http://ant.apache.org/
[5]
XDoclet - Attribute Oriented Programming
http://xdoclet.sourceforge.net/xdoclet/index.html
27