<?xml version="1.0" encoding="UTF-8"?> <rss
version="2.0"
xmlns:content="http://purl.org/rss/1.0/modules/content/"
xmlns:wfw="http://wellformedweb.org/CommentAPI/"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:atom="http://www.w3.org/2005/Atom"
xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
> <channel><title>knallisworld &#187; build</title> <atom:link href="http://www.knallisworld.de/blog/tag/build/feed/" rel="self" type="application/rss+xml" /><link>http://www.knallisworld.de/blog</link> <description>Where is the beef?</description> <lastBuildDate>Thu, 02 Feb 2012 23:10:07 +0000</lastBuildDate> <language>en</language> <sy:updatePeriod>hourly</sy:updatePeriod> <sy:updateFrequency>1</sy:updateFrequency> <item><title>Automatischer Deploy von Java-Applikationen oder: Ant Builds mit externen Jars und SVN-Informationen</title><link>http://www.knallisworld.de/blog/2009/04/21/automatischer-deploy-von-java-applikationen-oder-ant-builds-mit-externen-jars-und-svn-informationen/</link> <comments>http://www.knallisworld.de/blog/2009/04/21/automatischer-deploy-von-java-applikationen-oder-ant-builds-mit-externen-jars-und-svn-informationen/#comments</comments> <pubDate>Tue, 21 Apr 2009 16:59:23 +0000</pubDate> <dc:creator>knalli</dc:creator> <category><![CDATA[Diplomarbeit.. ftw!]]></category> <category><![CDATA[Java]]></category> <category><![CDATA[ant]]></category> <category><![CDATA[build]]></category> <category><![CDATA[deploy]]></category> <category><![CDATA[mac os x]]></category> <guid
isPermaLink="false">http://www.knallisworld.de/blog/?p=670</guid> <description><![CDATA[Der Eclipse-verwÃ¶hnte Programmierer braucht sich eigentlich nicht mit Begriffen wie Deploy oder Build herumschlagen &#8212; zumindestens nicht so lange man in der Entwicklung ist. Ein Knopfdruck auf &#8220;Run&#8221; und das Programm lÃ¤uft. Sowohl alle lokalen Resourcen als auch alle externen Jars werden gefunden, eingebunden und kÃ¶nnen einwandfrei genutzt werden. MÃ¶chte man die Applikation nun fertigstellen [...]]]></description> <content:encoded><![CDATA[<p>Der Eclipse-verwÃ¶hnte Programmierer braucht sich eigentlich nicht mit Begriffen wie Deploy oder Build herumschlagen &#8212; zumindestens nicht so lange man in der Entwicklung ist. Ein Knopfdruck auf &#8220;Run&#8221; und das Programm lÃ¤uft. Sowohl alle lokalen Resourcen als auch alle externen Jars werden gefunden, eingebunden und kÃ¶nnen einwandfrei genutzt werden.</p><p>MÃ¶chte man die Applikation nun fertigstellen und anderen zur VerfÃ¼gung stellen &#8211; oder frÃ¼her im Rahmen von Enduser-Tests &#8211; so kommt es bei groÃŸen Projekten unweigerlich zu Problemen.</p><p>Im Folgenden beschÃ¤ftige ich mit den Problemen:</p><ol><li>Wie exportiere ich ein Projekt mit vielen externen Jars einfach und sicher in ein lauffÃ¤higes Jar?</li><li>Wie stelle ich sicher, das sowohl in der Entwicklungsumgebung als auch in der Jar alle Bilder o.Ã¤. Ressourcen wiedergefunden werden?</li><li>Wie nutze ich einen eigenen Buildprozess und</li><li>Wie kann ich im Buildprozess Zusatzinformationen wie die aktuelle SVN-Revision oder das Build-Datum verwerten?</li></ol><p><span
id="more-670"></span></p><h2>Problem 1: Viele externe Jars</h2><p>Eclipse bietet im Exportwizard die MÃ¶glichkeit, eine Jar-Datei zu erstellen. Dabei wird das aktuelle Projekt <span
style="text-decoration: underline;">ohne</span> die verlinkten Jar-Dateien erstellt. UnschÃ¶n, wenn man die Applikation als ganzes anbieten will. So eine Art Out-of-the-Box.</p><p>Seit Eclipse 3.4 bietet der Exportwizard eine Neuerung, die so genannte <em>Runnable Jar</em>. Dabei werden neben den eigenen Class-Dateien alle verlinkten Jars (sind ja in Wirklichkeit Zip-Dateien) extrahiert und alles, wirklich <em>alles</em>, in eine Jar geschmissen. Das funktioniert in einigen FÃ¤llen, ist aber erstens unschÃ¶n, kann zweitens zu Problemen bei Namenskonflikten fÃ¼hren (bsp. Resourcenodner) und ist drittens u.U. nicht mit der Lizenz vereinbar. Auch nicht so ganz schÃ¶n.</p><p>Das zuletzt angesprochene neue Feature ist eine Art &#8220;Light-Version&#8221; des <a
href="http://fjep.sourceforge.net/">Fat Jar Eclipse-Plugin</a>. Das eigentliche Plugin selber hingehen bietet mehr: Das angegebene Projekt wird in ein Jar zusammengefÃ¼gt (vgl. Option 1). Dazu kommen alle externen Jars &#8211; und entgegen der zweiten Option so wie sie sind. Das ganze wird in eine groÃŸe Jar (quasi ein Wrapper) gepackt und mit einem kleinen &#8220;Bootloader&#8221; versehen. Perfekt!</p><h2>Problem 2: Lokale Ressourcen wie Bilder o.Ã¤.</h2><p>Im lokalen Projekt funktioniert natÃ¼rlich immer der Zugriff auf alleÂ Ressourcen. MÃ¶chte man spÃ¤ter jedoch ein Jar anbieten, muss man sich an einige Spielregeln halten. Erstens mÃ¼ssen sich dieÂ RessourcenÂ im (lokalen) Classpath befinden oder diesem bekannt gemacht werden. Zweitens kÃ¶nnen die Ressourcen selber nicht normal via File-Konstruktor geÃ¶ffnet werden, sondern mÃ¼ssen Ã¼ber den ClassLoader geladen werden. In der Regel reicht der Standard Classloader.</p><p>Um also in einem Ressourcen sowohl im Entwicklungsprojekt als auch in der Jar spÃ¤ter (und vor allem: in dem Multi-Jar-Archiv mit FatJar, siehe oben) zu benutzen, bedarf es folgender Konfiguration:</p><ul><li>In der Regel existiert ein Verzeichnis <em>src</em>, wo die Packages der eigenen Java-Dateien liegen. Dort einen Verzeichnis (Beispiel: <em>images</em>) anlegen, oder natÃ¼rlich auch als Unterverzeichnis in einem Package (ist ja auch ein Verzeichnis).</li><li>Um in Java nun beispielsweise das Bild <em>image.png </em>aus dem Verzeichnis <em>images</em> zu laden, macht man folgendes: <code>URL url = classLoader.getResource("images/image.png")</code>. classLoader ist dabei der Standard-Classloader; diesen erhÃ¤lt man beispielsweise durch <code>classLoader = MyClass.class.getClassLoader()</code> wobei MyClass eine Klasse aus dem eigenen Projekt ist, die den Standard-ClassLoader verwendet. Das vollstÃ¤ndige Beispiel zum Laden eines Bildes wÃ¤re also: <code>Icon icon = new ImageIcon(MyClass.class.getClassLoader().getResource("images/image.png"));</code></li></ul><h2>Problem 3a: Der Buildprozess</h2><p>Jedes Mal, wenn man eine Ã„nderung am Projekt gemacht hat, muss das gesamte Projekt exportiert werden. Zum Beispiel mit dem o.g. FatJar-Plugin. Jedes Mal mÃ¼ssen die Einstellungen geprÃ¼ft werden und eventuelle Sonderkonfigurationen wie Classpath-Erweiterungen oder Zusatzressourcen eingestellt werden. Dies ist nicht nur lÃ¤stig und zeitaufreibend &#8211; es ist auch schlichtweg unnÃ¶tig. Die Antwort darauf ist: Ant.</p><p>Ant ist die moderne Antwort auf make. WÃ¤hrend make auf Kommandozeilen orientierte Konfiguration und auf Basis von Leerzeichen/Tabulatoren arbeitet, wird ant mittels XML &#8220;geschrieben&#8221;. Nicht ohne Grund verwendet Eclipse fÃ¼r die internen Buildprozesse &#8220;rein zufÃ¤llig&#8221; auch Ant &#8211; und es lÃ¤sst nicht Ã¼berraschen, dass Eclipse auch externe Ant-Tasks ausfÃ¼hren kann. FÃ¼r Unwissende: Das ist das dritte Icon oben, Debug, Run und eben Run Tool/External Tools wie etwa Ant-Tasks.</p><p>Im Wesentlichen Ã¤hnelt Ant dabei sehr an make. Es werden einzelne Targets und untereinander zusammenhÃ¤ngende AbhÃ¤ngigkeiten definiert. Auch kÃ¶nnen wahlweise Systemkommandos ausgefÃ¼hrt werden (ggf. plattformabhÃ¤ngig). Des Weiteren gibt es zu der groÃŸen Ant-Befehls-Library zahlreiche Plugins.</p><p>Das FatJar Plugin bietet beim Export-Prozess die MÃ¶glichkeit, die Konfiguration auch als build.xml zu speichern. Damit erhÃ¤lt man eine Grundlage. Wie man auch ohne Kenntnisse von FatJar und Ant leicht erkennen kann, werden alle Jars zusammengepackt und an das Plugin geschickt. Der fertige Name wie auch die Main-Klasse kÃ¶nnen theoretisch noch angepasst werden. Kleiner Test? Einfach auf diese <em>build.xml &gt; Rechte Maustaste &gt; Ru</em><em>n</em> ausfÃ¼hren. VoilÃ¡. Damit hat man mit einem Knopfdruck immer ein aktuelles Jar.</p><h2>Problem 3b: Keine TestfÃ¤lle</h2><p>In das exportierte Jar gehÃ¶ren in Regel mindestens keine TestfÃ¤lle &#8211; und zusÃ¤tzlich kann man sich eine Jar (nÃ¤mlich junit) sparen. DafÃ¼r mÃ¼ssen alle Class-Dateien in ein seperates Verzeichnis kopiert werden. In einem Standardprojekt liegen im Verzeichnis <em>src</em> die Codedateien, im Verzeichnis <em>bin </em>die kompilierten Bytecodedateien. Als Zwischenschritt kopieren wir alle Dateien von <em>bin</em> nach <em>build</em> &#8211; aber mit einer definierten Ausnahmeliste: Alle Pfade, die mit tests beginnen oder mit test(s) aufhÃ¶ren, werden ignoriert. Die Muster mit Wildcards sind leicht zu verstehen und selber anpassbar.</p><p><code><span><span> </span></span>&lt;mkdir<span> </span>dir=<span>"build"</span><span> </span>/&gt;<br
/> &lt;copy<span> </span>todir=<span>"build"</span>&gt;<br
/> &lt;fileset<span> </span>dir=<span>"bin"</span>&gt;<br
/> &lt;exclude<span> </span>name=<span>"tests/**/*.*"</span>/&gt;<br
/> &lt;exclude<span> </span>name=<span>"**/test/*.*"</span>/&gt;<br
/> &lt;exclude<span> </span>name=<span>"**/tests/*.*"</span>/&gt;<br
/> &lt;/fileset&gt;<br
/> <span
style="font-family: Georgia;"><code>&lt;/copy&gt;</code>;</span></code></p><p><span
style="font-family: Georgia;">ZunÃ¤chst wird das Verzeichnis gelÃ¶scht (von einem vorherigen Task) und anschlieÃŸend werden die Dateien kopiert. Die Ant-API sei auch an dieser Stelle erwÃ¤hnt.</span></p><p><span
style="font-family: Georgia;">Das XML von FatJar muss nun natÃ¼rlich geringfÃ¼gig angepasst werden: Statt aus bin muss jetzt build im Filesourcepath stehen:Â <code>&lt;fatjar.filesource<span> </span>path=<span>"build"</span><span> </span>relpath=<span>""</span>/&gt;</code></span></p><h2>Problem 4: Zusatzinformationen im Buildprozess</h2><p>Vor allem in der Entwicklungszeit &#8211; vielleicht aber auch danach fÃ¼r den Support &#8211; mÃ¶chte man im Buildprozess einige Zusatzinformationen speichern. In dieser Kurzvorstellung gehe ich auf zwei interessante MÃ¶glichkeiten ein: Das Datum und die Uhrzeit des Buildens und die aktuelle Revision des Subvision-Repository.</p><p>Als Ausgabe definieren wir eine<em> Pr</em><em>operty-Datei</em>, die von Java aus sehr einfach mit der Klasse Properties oder ResourceBundle gelesen werden kann. Ant kann ebenfalls Property-Dateien lesen und schreiben.</p><p>Mit dem XML-SchnippselÂ <code>&lt;tstamp&gt;<span>&lt;format</span><span> </span><span>property=</span>"TSTAMP"<span> </span><span>pattern=</span>"MM/dd/yyyy HH:MM:SS z"<span>/&gt;&lt;/tstamp&gt;</span></code> legen wir eine lokale Variable namens &#8220;<em>TSTAMP</em>&#8221; an, die durch den Ant-Befehl <em>tstamp</em> und format ausgefÃ¼llt wird. Hier ein standardisiertes Format, damit es Java auch entsprechend einfach wieder lesen kann. Wer mag, kann dies (beidseitig) auch Ã¤ndern.</p><p>FÃ¼r die aktuelle SVN-Revision bedarf es einem Plugin. Theoretisch kÃ¶nnte man auch ein Ant-Befehl &#8220;svn info&#8221; losschicken, aber dafÃ¼r muss sowohl das Programm svn lokal verfÃ¼gbar sein (in Eclipse nicht zwingend notwendig) als auch in der richtigen Version. Stichwort hier: SVN 1.4 != 1.5. GlÃ¼cklicherweise bietet <a
href="http://subclipse.tigris.org/svnant.html">tigris.org</a> ein entsprechendes Plugin unter dem Namen <em>SvnAnt</em> an. Entweder man kopiert sich das Plugin in den Ant-Classpath.. oder einfach in das Projekt selber. Im Beispiel gehe ich davon aus, dass das Plugin unter <em>build_data/svndata-1.2.1</em> extrahiert wurde.<br
/> Das Plugin muss zunÃ¤chst im project bekannt gemacht werden, das sieht in Ant etwa so aus:</p><p><code><span><span> </span></span>&lt;!-- svn ant integration --&gt;<br
/> <span>&lt;path</span><span> </span><span>id=</span><span> </span>"svnant.classpath"<span> </span><span>&gt;<br
/> <span> &lt;fileset</span><span> </span><span>dir=</span><span> </span>"build_data/svnant-1.2.1/lib"<span> </span><span>&gt;<br
/> &lt;include<span> </span>name=<span> </span><span>"*.jar"</span><span> </span>/&gt;<br
/> &lt;/fileset&gt;<br
/> &lt;/path&gt;<br
/> <span
style="font-family: Georgia;"><code><span>&lt;typedef</span><span> </span><span>resource=</span>"org/tigris/subversion/svnant/svnantlib.xml"<span> </span><span>classpathref=</span>"svnant.classpath"<span> </span><span>/&gt;</span></code></span></span></span></code></p><p><span><span><span
style="font-family: Georgia;">An der Stelle (sprich: in dem Task) wo man die Information braucht, reicht dann beispielsweise folgender Aufruf, um eine Ant-Variable <em>svn.revision</em> zu setzen.</span></span></span></p><p><code><span><span> </span></span>&lt;svn&gt;<br
/> &lt;status<span> </span>path=<span>"src/"</span><span> </span>revisionProperty=<span>"svn.revision"</span><span> </span>/&gt;<br
/> &lt;/svn&gt;</code></p><p><code><span><span>Es stehen natÃ¼rlich auch andere Informationen zur VerfÃ¼gung, dazu bitte die entsprechenden Dokumentationen von Subversion oder SvnAnt konsultieren.</span></span></code></p><p>Die nun zwei gewonnen Informationen &#8211; TSTAMP und svn.revision &#8211; kÃ¶nnen nun in eine Property-Datei geschrieben werden. Dies geht wirklich sehr einfach mit:</p><p><code><span><span> </span></span><span>&lt;propertyfile</span><span> </span><span>file=</span>"build/configuration.properties"<span>&gt;<br
/> <span> &lt;entry</span><span> </span><span>key=</span>"buildRevision"<span> </span><span>value=</span>"${svn.revision}"<span>/&gt;<br
/> <span> &lt;entry</span><span> </span><span>key=</span>"buildDatetime"<span> </span><span>value=</span>"${TSTAMP}"<span>/&gt;<br
/> &lt;/propertyfile&gt;</span></span></span></code></p><p>Das war&#8217;s schon. Idealerweise sollte das Java-Programm diese Informationen natÃ¼rlich nur optional verwenden, da sie ja zur Laufzeit in Eclipse selber nicht vorhanden sind. Erst nach dem Export werden diese Informationen gesetzt. Wie bereits in <span
style="text-decoration: underline;">3b</span> erwÃ¤hnt, werden die Dateien im Verzeichnis <em>build</em> zusammengefÃ¼gt. NatÃ¼rlich muss das Schreiben der Property-Datei <span
style="text-decoration: underline;">vor dem ZusammenfÃ¼gen</span> mittels FatJar passieren.</p> ]]></content:encoded> <wfw:commentRss>http://www.knallisworld.de/blog/2009/04/21/automatischer-deploy-von-java-applikationen-oder-ant-builds-mit-externen-jars-und-svn-informationen/feed/</wfw:commentRss> <slash:comments>4</slash:comments> </item> </channel> </rss>
