Ant Sonderzeichenprobleme

Erst neuchlich hatte ich in einem Ant-Script Probleme mit sonderzeichen. Ich wollte mit dem Tag “replaceregexp” einen Text ersetzen in dem ein < vorkam. Das hat nicht so ganz geklappt. Viele Sonderzeichen muessen in Ant-Scripten durch spezielle Zeichenfolgen ersetzt werden. Hier sind die wichtigesten ersetzungen.

<  =    & l t ;
>  =    & g t ;
"  =    & q u o t ;
&  =    & a m p ;
'  =    & a p o s ;

Die Leerzeichen in der zweiten Spalte gehoeren natuerlich weg.

Kompilieren mit Ant

Beim kompilieren mit dem Build-Tool Ant werden oft drei Tasks definiert, preCompile, executeCompiler und postCompile. In dem Task preCompile werden alle Arbeiten erledigt die vor dem Kompiliervorgang statt finden muessen, wie z.B. das Erstellen eines Zielverzeichnisses.

<property name="projectBuild"   value="${projectHome}/build/classes"   />

<fileset id="classesFiles"   dir="${projectBuild}"   />

<target name="preCompile"  >
   <mkdir dir="${projectBuild}"   />
</target>

Im Task executeCompiler findet das eigentliche Kompilieren statt.

<property name="compileTarget"   value="1.5"   />

<path id="compilerSourceFiles"  >
    <pathelement path="${projectSources}"   />
</path>

<path id="compilerLibraryFiles"  >
    <fileset dir="${libraries}"  >
       <include name="**/*.jar"   />
    </fileset>
    <fileset dir="${j2eeLibraries}"  >
       <include name="**/*.jar"   />
    </fileset>
</path>

<target name="executeCompiler"   depends="preCompile"  >
   <javac destdir="${projectBuild}"   target="${compileTarget}"  >
      <src refid="compilerSourceFiles"   />
      <classpath refid="compilerLibraryFiles"   />
   </javac>
</target>

Im Task postCompile sind alle Arbeiten beschrieben die nach dem kompilieren ausgefuehrt werden sollen, wie z.B. das kopieren aller nicht Quellcode-Dateien vom Source-Verzeichnis in das Build-Verzeichnis.

<target name="postCompile"   depends="executeCompiler"  >
    <copy todir="${projectBuild}"  >
        <fileset dir="${projectSources}"  >
            <exclude name="**/*.java"   />
        </fileset>
    </copy>
</target>

Der Tag “exclude” teilt Ant mit das er rekursiv durch alle Verzeichnisse gehen soll und dabei alle Dateien die mit “.java” enden auslassen soll.

Ant + Tomcat (hotdeploy)

Unter einem hotdeployment versteht man das deployen einer Applikation ohne dabei den Applikationserver/Servletcontainer herunterzufahren. Um mit Ant ein hotdeployment aus einem Tomcat durchfuehren zu koennen, muss vorher Der TCD (Tomcat Client Deployer) heruntergeladen werden, der hier zur Verfuegung steht: http://tomcat.apache.org/download-60.cgi. Die JAR-Files aus dem TCD sollten im Classpath zu finden sein.
Die Nachfolgenden Properties sollten fuer ein hotdeployment deklariert werden:

<property name="path"   value="/myapp"  />
<property name="url"   value="http://localhost:8080/manager"  />
<property name="username"   value="tomcat"  />
<property name="password"   value="tompassword"  />

Mit dem folgenden Eintrag wird der Ant Task fuer den Manager konfiguriert.

 <path id="deployer.classpath"  >
<fileset dir="${basedir}/lib"  >
<include name="*.jar"  />
</fileset>
</path>

<taskdef resource="org/apache/catalina/ant/catalina.tasks"
classpathref="deployer.classpath"  />
 

Mit den folgend Targets kann eine Applikation neu geladen, undeployt und deployt werden.

 <target name="reload"   >
<reload url="${url}"  username="${username}"  password="${password}"  path="${path}"  />
</target>

<target name="undeploy"   >
<undeploy url="${url}"  username="${username}"  password="${password}"  path="${path}"  />
</target>

<target name="hotdeploy"   >
<deploy url="${url}"  username="${username}"  password="${password}"  path="${path}"  war="${projectWARFile}"   />
</target>

Eine ausführliche Beschreibung aller Task ist auf der Homepage von Tomcat zu finden.

http://tomcat.apache.org/tomcat-6.0-doc/deployer-howto.html

Nicht selten endet das hotdeployment in einem OutOfMemmoryException. Mit dem Mechanismus des HotDeployments hatte Tomcat schon immer Probleme. Ich empfehle fuer das deployen auf Tomcats nur harddeployments, die ich im vorherigen Blogeintrag beschrieben habe.

Ant + Tomcat (harddeploy)

Der folgende Eintrag zeigt wie Ant aus einer WebApplikation eine *.war Datei backt und diese auf dem Tomcat deployt. Gehen wir davon aus das vorhergehende Task eine WebApplikation aus einem Svn-Server ausgecheckt und erofolgreich kompiliert haben. Der folgende Task baut eine *.war Datei.

<target name="buildWARFile"  >
 <war destfile="${projectWARFile}"  >
   <fileset refid="webContentFiles"   />
   <lib refid="libFiles"   />
   <classes refid="classesFiles"   />
   <webinf refid="webinfFiles"   />
 </war>
</target>

Die Attribute “refid” verweisen alle auf “filesets” (siehe Ant-docu). Die eben generiert Datei wird mit dem nachfolgenden Task deployt.

<target name="deploy"   >
 <exec executable="net"   >
   <arg line='stop "  Apache Tomcat 5.5.20 Server 3"  ' />
 </exec>
 <delete dir="${appHome}"   />
 <mkdir dir="${appHome}"   />
 <unwar src="${projectWARFile}"   dest="${appHome}"   />
 <exec executable="net"   >
   <arg line='start "  Apache Tomcat 5.5.20 Server 3"  ' />
 </exec>
</target>

Die erste Anweisung ruft das Kommandozeileninterface “net” auf mit den Uebergabeparametern ‘stop “Apache Tomcat 5.5.20 Server 3″‘. Das Kommandozeileninterface “net” steuert auf Windows Servern die Systemdienste. Damit das ganze funktioniert muss Tomcat als Windows Systemdienst installiert sein.
Sobald der Apache Tomcat heruntergefahren ist, werden mit den nachfolgenden Zeilen das Verzeichnis der Zielapplikation geloescht, ein neues Verzeichnis angelegt, die *.war Datei wird in das neu erstellte Verzeichnis entpackt und zum Schluss wird der Apache Tomcat als Systemdienst gestartet.
Der Vorgang kann ein bis zwei Minuten dauern, dannach sollte die Applikation zur Verfuegung stehen.

Auf einem Debian Linux System sieht der Task ähnlich aus.

<target name="deploy"   >
 <exec executable="/opt/tomcat/bin/shutdown.sh"   />
 <delete dir="${appHome}"   />
 <mkdir dir="${appHome}"   />
 <unwar src="${projectWARFile}"   dest="${appHome}"   />
 <exec executable="/opt/tomcat/bin/startup.sh"   />
</target>

BuildNumber ohne SvnAnt

Im vorherigen BlogEintrag “BuildNumber mit SvnAnt” habe ich beschrieben wie mit Hilfe von SvnAnt die Revisionsnummer geholt und eine Proterties Datei geschrieben werden kann. Die build.xml aus der Beschreibung habe ich auf meinem MacBook Pro (Mac OS X Tiger) mit Erfolg laufen lassen. Ein Kollege von mir konnte die Tasks ebenfalls erfolgreich auf seinem Windows XP PC laufen lassen. Auf einem alten Windows 2000 Server konnte die selbe build.xml jedoch nicht erfolgreich durchlaufen werden. Folgende Exception wurde geworfen:

Ant Exception
Ant Exception

Nach dem ich die neuste libjavahl.dll von der Subversion Seite auf den Server kopiert habe, ist beim ausfuehren der build.xml die JVM agestuerzt mit einem Hinweis das eine native Bibliothek den Fehler verursacht hat. Daraufhin habe ich in der build.xml “javahl” auf “false” gesetzt und den neusten Subversion-Client auf dem Server installiert, was leider auch nicht zum Ziel gefuehrt hat.
Da eine schnelle Loesung her musste, habe ich mir folgenden Workaround einfallen lassen um die aktuelle Revisionsnummer auszulesen:

<exec executable="svn"   >
 <arg line="info ${projectName} > ${statusFile}" />
</exec>

Das bewirkt das folgender Befehl auf der Kommandozeile ausgefuehrt wird:

svn info os > status.properties

Der Befehl aus der Kommandozeile funtkioniert wunderbar und leitet die Ausgabe, welche auch die Revisionsnummer enthaelt, in die Datei “status.properties” um. Der Ant-Task fuehrt leider zu einem Fehler:

svn: Fehler beim Ermitteln der Groß-/Kleinschreibung von '>'

Ich habe das “>” Zeichen auf versucht zu escapen mit dem Dollarzeichen, wie bei Ant ueblich, aber leider ohne Erfolg. Auch die Ersetzung durch Unicode-Zeichen hat keinerlei Wirkung gehabt.
Aus dem Grund habe ich eine Batch Datei erstellt mit folgendem Inhalt:

svn info %1 > %2

Diese Batch Datei wird nun mit folgenden Zeilen aus Ant aufgerufen:

 <exec executable="status.bat"   >
 <arg line="${projectName} ${statusFile}"   />
</exec>
 

Der Aufruf funtkioniert und tut genau das was er soll. Anschließend werden in der Ausgabedatei alle “:” durch das “=” Zeichen ersetzt.

 <replaceregexp file="${statusFile}"  match=":"  replace="=" flags="g"   />
 

Somit haben wir wieder eine Propertie-Datei auf die wir mit JSF zugreifen koennen. Das Attribut “flags” hat den Wert “g”, was soviel bedeutet das die Ersetzung in der Datei global geschehen soll.

BuildNumber mit SvnAnt

In dem vorherigen Blogeintrag habe ich beschrieben wie SvnAnt dazu benutzt werden kann die aktuellen Sourcen aus einem Subversion-Server auszuchecken. Vor dem Bauen der Applikation macht es Sinn die aktuelle Revisionsnummer und einen Timestamp in eine Properties Datei zu schreiben. Diese Informationen koennen in einer Web-Applikation im Footer eingeblendet werden. Wenn ein Fehler auftritt, kann der Kunde, im Idealfall, zu der Fehlerbeschreibung die Revisionsnummer und den Timestamp mitschicken. Diese Informationen sind fuer Entwickler bei der Fehlersuche Gold wert.
Der folgende Task in eine build.xml eingefuegt, holt sich die aktuelle Revisionsnummer vom Subversion-Server und schreibt den Wert in die Property “projectStatus.revision”.

<target name="getSVNRevison"  >
 <svn username="${svnUser}"   password="${svnPassword}"  >
 <status path="${projectHome}"   revisionProperty="projectStatus.revision"   />
 </svn>
</target>

Die Variable “projectHome” zeigt auf den Pfad der lokalen Arbeitskopie, der im Idealfall mit einem Ant-Task vorher ausgecheckt wurde.
Der Nachfolgende Task schreibt die Revisionsnummer in eine Properties Datei, erzeugt einen Timestamp und schreibt diesen ebenfalls in eine Prop. Datei.

 <target name="updateBuildNo"   depends="getSVNRevison"  >
 <replaceregexp file="${buildnoFile}"
 match="revision=(.*)"
 replace="revision=${projectStatus.revision}"   />

 <tstamp>
 <format property="buildTimestamp"   pattern="yyyyMMdd-HHmm"   locale="de,DE"   />
 </tstamp>

 <replaceregexp file="${buildnoFile}"
 match="timestamp=(.*)"
 replace="timestamp=${buildTimestamp}"   />

 <echo>Revision: ${projectStatus.revision}</echo>
 <echo>Timestamp: ${buildTimestamp}</echo>
</target>

Auf die Properties Datei kann in einer JSF-Applikation mit der JSF EL zugegriffen werden als Resource-Bundle.

Ant optional Tasks

Die Ant-Task sind in zwei Gruppen aufgeteilt, in die Core-Tasks und in die Optional-Tasks. Eine vollstaendige Auflistung alles Core- und Optional-Task ist in der offiziellen Ant-Docu http://ant.apache.org/manual/ zu finden. Die Optional-Tasks benoetigen alle zusatzliche Bibliotheken wie z.B. ant-apache-oro.jar. Nach der Installtion von Ant auf einem Debian Linux System mit dem Befehl:

apt-get install ant

stehen auf dem System nur die Core-Tasks zur Verfuegung. Wird in einer build.xml ein Optional-Task verwendet wird eine Exception geworfen. Das Problem kann dadurch geloest werden das die optionalen Tasks nachinstalliert werden mit dem Befehl:

apt-get install ant-optional

In dem Archiv das von der offiziellen Homepage http://ant.apache.org/bindownload.cgi heruntergeladen werden kann, sind alle JARs enthalten, auch die fuer die optionalen Tasks.

Ant + Subversion + SvnAnt

SvnAnt ist eine Java-Bibliothek die Ant-Tasks fuer die Vesionsverwaltungs-Software Subversion anbietet. Die Bibliothek kapselt die Moeglichkeiten des Kommandozeilenprogramms “svn” in Ant-Tasks, womit die Kommunikation zwischen Ant und Subversion sehr komfortabel und elegant moeglich ist.
SvnAnt benutzt intern die javahl Bibliothek, ein Java Interface fuer die Subversion API, welche direkt zum Subversion Projekt gehoert. Wenn die Bib. die korrespondierende native Bib. (libsvnant.dll auf Windows) finden kann, dann geschieht die Kommunikation ueber javahl. Wenn die native Implementierung auf dem Betriebssystem nicht gefunden werden kann, gibt es ein FallBack zum Kommandozeilen Interface “svn”. Wenn kein Subversion-Client installiert ist und dementsprechend kein “svn” im PATH eingetragen ist, kann die Verbindung zu Subversion nicht hergestellt werden und es treten Fehler auf.
Es muss also entweder eine native javahl Bibliothek installiert sein oder ein Subversion-Client. Die javahl Bibs werden uebrigens auch im Subclipse Projekt benutzt. Eclipse Benutzer die Subclipse installiert haben, sollten sich hierum keine Sorgen machen. Alle nicht Eclipse Benutzer koennen die Bibliothek von der offiziellen Subversion Homepage runterladen.
SvnAnt kann hier heruntergeladen werden: http://subclipse.tigris.org/svnant.html

In dem entpackten Archiv befindet sich eine vollstaendige Dokumentation im HTML-Format, in dem alle Ant-Tasks mit ihren Attributen beschrieben sind. Die *.jar Dateien aus dem Archiv muessen entweder in die <ANT_HOME>/lib kopiert werden oder die entsprechende build.xml verweist auf die Dateien.
Bei der zweiten Moeglichkeit muss folgender Path in der build.xml eingetragen sein.

<path id="project.classpath"  >
 <fileset dir="../svnant-1.0.0-rc1/lib"  >
 <include name="*.jar"  />
 </fileset>
</path>

Der Pfad zum “svnant-1.0.0-rc1/lib” Verzeichnis muss natuerlich an das jeweilige System angepasst werden.
Der Task wird wie folgt definiert:

 <taskdef name="svn"
 classname="org.tigris.subversion.svnant.SvnTask"
 classpathref="project.classpath"   />

Wenn die *.jar Dateien von SvnAnt in der <ANT_HOME>/lib liegen dann kann das Attribut “classpathref” weggelassen werden. Das auschecken von Sourcecode aus dem Subversion-Server sieht dann so aus:

 <target name="checkout"  >
 <svn username="${svnUser}"   password="${svnPassword}"   javahl="true"  >
 <checkout url="${svnURL}"   destPath="${projectHome}"   />
 </svn>
</target>
 

Mit dem Attribut “javahl” kann hier explizit angegeben werden ob javahl oder das Kommandozeileninterface “svn” zum Gebrauch kommen soll. Standardmaessig hat das Attribut “javahl” den Wert “true”.
Weitere Tasks wie commit, add, delete, copy, createRepository und noch mehr sind in der Dokumentation zu SvnAnt beschrieben.

Ant + Subversion

Subversion ist ein Programm zur Versionskontrolle und der Nachfolger von CVS. Ich habe frueher mit CVS gearbeitet aber seit dem ich Subversion kenne moechte ich nichts mehr von CVS wissen.

Das build-tool ant hat fuer Java Entwickler ungefaehr die gleiche Bedeutung wie “make” fuer die C und C++ Entwickler. Mit ant werden Dateien kopiert, JARs, WARs und EARs gebacken.

Was liegt naeher als die beiden Werkeuzge miteinander zu verbinden. Es gibt verschiedene Moeglichkeiten um mit ant den aktuellen Sourcecode aus einem Subversion-Server auszuchecken. In jedem Fall muss dafuer auf der Maschine auf dem ant laufen soll ein Subversion Client installiert sein.

Auf einem Debian-Linux-System ist das mit folgendem Befehl aus der Kommandozeile erledigt:

apt-get install subversion

Auf einem Mac OS X System empfehle ich die Darwin Ports zu benutzen. Dazu eine Konsole oeffnen und folgenden Befehl absetzen:

port install subversion

Die Windowsbenutzer muessen sich von der Subversion-Homepage ein Programm herunterladen und per Hand installieren. Hier der Link: http://subversion.tigris.org/

Nach der Installation sollte geprueft werden ob der svn-client auch wirklich funktioniert. Der folgende Befehl checkt ein Projekt aus dem Subversion-Server aus:

svn co –-username tom –-password tomtom http://ebtj09.firma.de/svn/fop/trunk zielverzeichnis

Natuerlich muessen die Werte an den eigenen Subversion-Server angepasst werden.
Der selber Aufruf kann auch aus ant heraus abgesetzt werden.

<target name="checkoutBuild"   description="auschecken von Code aus dem Subversion-Server"  >
 <exec executable="svn"  >
 <arg line="co –-username ${svn.username} –-password$ {svn.password} ${svn.projecturl} ${destUrl}"   />
 </exec>
</target>