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