Falls nicht alle Dateien unterhalb des Wurzelverzeichnisses in ein FileSet gewählt werden sollen, kann man die Menge der Dateien einschränken, in dem man eine Pattern-Definition angibt. Ein solcher Pattern wird - ähnlich wie Wildcards in einer Shell - auf den Dateinamen angewandt. In Listing 1, Zeile 4 wählt zum Beispiel der Pattern "**/*.class" in der Definition des Attributs <include> alle Java-Class Files unterhalb des Verzeichnisses "src" in das Fileset.
Zusätzlich zur Auswahl von Dateien über ihren Namen gibt es in Ant die Möglichkeit, Dateien mit Hilfe von Selektoren über weitere ganz spezifische Eigenschaften wie die Größe oder das Datum der letzten Änderung auszuwählen. Ein Selektor bezieht sich immer auf eine bestimmte Dateieigenschaft und wird im Ant-Buildfile als zusätzliches Attribut in der XML-Definition des FileSet angegeben. Listing 1 enthält in Zeile 7-9 ein Beispiel, in dessen FileSet alle Dateien enthalten sind, die den String 'image' enthalten. Ant bietet vordefinierte Selektoren für alle allgemein üblichen Dateiatribute an wie <size>, <contains>, <depend>, usw. Diese Selektoren werden in der Online-Hilfe von Ant auch als "Core Selectors" bezeichnet. Darüber hinaus existieren noch Selektor-Container, die es erlauben, Selektoren in logischen Kombinationen anzuwenden (z.B. um alle Dateien zu selektieren, die einen bestimmten String enthalten und die in den letzten zwei Wochen verändert wurden).
================================================= Listing 1: FileSet Definitionen 1 <fileset dir="src"/> 2 3 <fileset dir="src"> 4 <include name = "**/*.class"/> 5 </fileset> 6 7 <fileset dir="src"> 8 <contains text="image"/> 9 </fileset> =================================================
Eine ausführliche Beschreibung des FileSet-Mechanismus inklusive der "Core Selectors" und der Selektor-Container findet sich in der Online-Hilfe zu Ant[2]. Dort sind auch weiterführende Konzepte wie der Datentyp PatternSet und Referenzen auf File- und PatternSets erläutert.
================================================= Listing 2: FileSet mit Custom Selector 1 <fileset dir="src"> 2 <custom classname="MySelectorClass"> 3 <param name="aName" value="aValue"/> 4 </custom> 5 </fileset> =================================================
Bei der Erstellung eigener Selektoren kann man auf verschiedene Klassen im Ant-API zurückgreifen. Die Struktur dieser API wird im Folgenden erläutert. Wie auch zum Erstellen eigener Tasks (vgl. [1]) existieren in Ant ein Interface und eine abstrakte Klasse zum Erstellen eigener Selektoren. Das Interface und die abstrakte Klasse heißen "FileSelector" respektive "BaseSelector", beide liegen im Package org.apache.tools.ant.types.selectors (siehe Bild 1). Das Interface enthält - analog zur Methode execute() bei den Tasks - nur die Methode boolean isSelected(...). Die abstrakte Klasse implementiert das Interface und ermöglicht ferner den Zugriff auf Ressourcen des Ant-Projekts, in dem sie von der Klasse "DataType" ableitet (Diese Ressourcen können unter anderem verwendet werden, um Log-Ausgaben zu schreiben oder um Properties auszulesen).
Eine Ebene tiefer im Ableitungsbaum existieren das Interface "ExtendFileSelector" und die abstrakte Klasse "BaseExtendSelector". Diese erweitern die oben genannte Kombination aus Klasse und Interface um Mechanismen zur Behandlung von Parametern, wobei die angesprochene Methode isSelected(...) und damit die Klasse "BaseExtendSelector" selbst weiterhin abstrakt bleibt. Die Behandlung von Parametern erfolgt dadurch, dass diese zur Laufzeit vom XML-Parser ausgelesen werden und über die Methode setParameters(...), die im Interface Parameterizable (siehe Bild 1) deklariert ist, dem Selektor übergeben werden. Die Klasse BaseExtendSelector definiert analog zur Methode setParameters(...) eine Methode getParameters(), die man in eigenen Selektorklassen zum Auslesen der Parameter verwenden kann.
Eine Überprüfung, ob alle erforderlichen Parameter korrekt übergeben wurden, kann in der Methode verifySettings() implementiert werden. In der Default-Implementierung der Klasse "BaseSelector" wird diese Methode von der Methode validate() aus aufgerufen. Die Methode validate() dient zur Überprüfung der Konsistenz der Selektor-Instanz und sollte immer als Erstes in der Implementierung der Methode isSelected(...) aufgerufen werden. Eine ausführliche Beschreibung aller erwähnten Klassen und Methoden findet sich in der Online-Hilfe zu Ant [2].
Das Erstellen eigener Selektoren ist mit diesem Grundlagenwissen über FileSets und das Ant-API ist relativ einfach: Ausgehend von Bild 1 definiert man eine Klasse für den neuen Selektor, die von "BaseExtendSelector" ableitet (Falls der neue Selektor keine Parameter zu verarbeiten braucht, kann auch von BaseSelector abgeleitet werden). Anschliessend definiert man in der neuen Klasse für jeden Parameter, der verwendet werden soll, eine Instanzvariable entsprechenden Typs. Die Methode setParameters(...) muss daraufhin neu implementiert werden, so dass diese Instanzvariablen mit Werten belegt werden. Die Funktionalität des Selektors wird wie oben beschrieben in der Methode isSelected(...) implementiert. Die Einbindung in die XML-Datei erfolgt über das <custom>-Tag des FileSets.
Da für den neuen Selektor Parameter verwendet werden sollen, muss die
neue Selektorklasse von "BaseExtendSelector" abgeleitet
werden (s. Bild 2). Um die Implementierung möglichst flexibel zu halten und
eine spätere Erweiterung auf andere Systeme zur Versionskontrolle
zu ermöglichen, erfolgt diese
Ableitung in zwei Schritten gemäß dem Strategie-Entwurfsmuster.
Es wird zunächst eine Klasse "AbstractVCSSelector" abgeleitet.
In dieser Klasse wird die Übergabe der VCS-Parameter (Branch, Tag, usw.)
implementiert. Die Klasse implementiert außerdem bereits die
Funktionalität des Selektors in der Methode isSelected(...),
verwendet hierfür aber eigens definierte abstrakte Methoden, die
in den konkreten Subklassen von
AbstractVCSSelector ausprogrammiert werden müssen (z.B. die
Methode checkTag()).
Die Übergabe der Parameter findet wie beschrieben in der Methode
setParameters(...) statt. Es können folgende Parameternamen
verwendet werden: Branch, Label und Version.
Wird kein Parameter übergeben, prüft der Selektor nur, ob es sich um
eine Datei unter Versionkontrolle handelt. Falls ein Parameter oder eine
Kombination der genannten Parameter übergeben wird, geht der Selektor von einer
VCS-Datei aus und prüft, ob die Bedingungen aus den Parametern auf die
Datei zutreffen.
Für Verzeichnisse prüft dieser abstrakte Selektor keine Versionsinformation,
sondern gibt immer true zurück. Diese Voreinstellung kann für eine
konkrete Subklasse des Selektors geändert werden, indem dort die
Instanzvariable "checkDirs" mit dem Wert true belegt wird.
hier Listing 3: Datei AbstractVCSSelector.java
Von der Klasse "AbstractVCSSelector" leitet dann der konkrete Selektor fürs
CVS ab ("CVSSelector"). Die Funktionalität wird dort wie oben beschrieben
durch Implementierung der geerbten abstrakten Methoden bereitgestellt.
Dazu werden noch zwei Hilfsmethoden definiert, die über das
Kommandozeileninterface von CVS den CVS-Status einer Datei abfragen können.
hier Listing 4: Datei CVSSelector.java
Als Anwendungsbeispiel ist in Listing 3 die XML-Definition eines FileSets angegeben, das alle Java-Dateien unterhalb des Wurzelverzeichnisses "src" enthält, sofern es sich dabei um CVS-Dateien handelt. Weitere Java-Dateien, zum Beispiel solche, die mittels idl2java oder castor automatisch generiert werden, werden von dem FileSet nicht erfasst.
================================================= Listing 5: Anwendungsbeispiel 1 <fileset dir="src"> 2 <custom classname="VCSSelectors.CVSSelector"> 4 </custom> 5 </fileset> =================================================
Eine Erweiterung der vorgestellten Selektoren auf andere Systeme zur Versionsverwaltung ist durch die Verwendung des Strategie-Musters einfach möglich. So wäre zum Beispiel für ClearCase lediglich folgendes tun: Analog zur Klasse CVSSelector müßte eine Klasse "CCSelector" entwickelt werden, in deren Implementierung anstatt des Befehls "cvs status ..." der Befehl "cleartool ls ..." verwendet und das Ergebnis entsprechend ausgewertet wird. Da ClearCase mit versionierten Verzeichnissen arbeitet, muss außerdem die Instanzvariable "checkIsDir" im Konstruktor mit dem Wert "true" initialisiert werden. Weiterhin ist auch eine Erweiterung des CVS-Selektors selbst denkbar, z.B. zur Unterscheidung zwischen Text- und Binärdateien. Dies kann auf einfache Weise durch Vererbung geschehen: Die Implementierung der Methode setParameters(...) muss einen weiteren Parameter verarbeiten können: CVSType. Dieser Parameter kann die Werte "text" oder "binary" haben. Die Methode setParameters(...) muss in der Klasse "CVSSelector" überschrieben werden und prüfen, ob der Parameter CVSType gesetzt wurde. Die übrigen Parameter werden wie gewohnt durch Aufruf von super() mit der Default-Implementierung verarbeitet. Analog dazu wird die Methode isSelected(...) ebenfalls überschrieben, um bei vorhandenem Parameter CVSType die erforderliche Überprüfung durchzuführen und den Rest an die Superklasse zu delegieren.
Referenzen [1] Fühlersprache, iX 02/2004 [2] Online-Manual zu Ant