tag:blogger.com,1999:blog-22815131223005251182024-03-08T16:29:26.296+01:00subjektive IT - Bemerkenswertes aus Projekten, aus Neugier oder aus beidem...Anonymoushttp://www.blogger.com/profile/01068308481291529801noreply@blogger.comBlogger5125tag:blogger.com,1999:blog-2281513122300525118.post-29422763142003802092013-12-03T11:53:00.000+01:002013-12-03T11:53:00.181+01:00Echte Programmierer lieben CLIs<span style="background-color: white; color: #404040; font-family: Roboto, arial, sans-serif; font-size: 13px; line-height: 18px;">Wer schon mal mit der <a href="https://github.com/spring-projects/spring-roo">Spring Roo </a>Shell gearbeitet hat oder mit der <a href="https://github.com/spring-projects/rest-shell">Spring rest-shell</a> will bestimmt auch seine eigene Software so präsentieren. Zu diesem Zweck hat Spring die <a href="https://github.com/spring-projects/spring-shell">Shell </a>herausgelöst und zur Verfügung gestellt. </span>Anonymoushttp://www.blogger.com/profile/01068308481291529801noreply@blogger.com0tag:blogger.com,1999:blog-2281513122300525118.post-49557562822690243312012-09-17T07:00:00.000+02:002012-09-17T09:47:51.953+02:00Groovy: Script mit Bibliotheken verknüpfen: (at)Grap<br />
Ein Script hat den Charme das es klein und handlich weitergegeben werden kann. Doch das hat schnell ein Ende, wenn man auf 3rd Party Bibliotheken zugreifen muss. Doch auch dafür hat groovy eine Lösung: Die Annotation<span style="font-family: Courier New, Courier, monospace;"> @Grab</span>. Die Annotation gibt der Laufzeitumgebung die Information welche Jars aus Maven Repositories geladen werden sollen, bevor das Script ausgeführt werden kann.<br />
Hier nur kurz das offizielle Beispiel direkt zitiert:<br />
<span style="background-color: #cccccc; font-family: Courier New, Courier, monospace;">@Grab('commons-lang:commons-lang:2.4')</span><br />
<span style="background-color: #cccccc; font-family: Courier New, Courier, monospace;">import org.apache.commons.lang.WordUtils</span><br />
<span style="background-color: #cccccc; font-family: Courier New, Courier, monospace;">println "Hello ${WordUtils.capitalize('world')}"</span><br />
<span style="background-color: #cccccc; font-family: Courier New, Courier, monospace;"><br /></span>
<span style="background-color: white; font-family: inherit;">Die Synthax im Beispiel ist die Kurzschreibweise. Es ist auch eine etwas textlastigere Variante möglich. Diese liefert dann auch direkt die Erklärung was die einzelnen Elemente bedeuten:</span><br />
<span style="background-color: #cccccc;"><span style="font-family: Courier New, Courier, monospace;">@Grab(group='org.apache.solr', module='solr-commons-csv', version='3.5.0')</span><span style="font-family: inherit;"> </span></span>Anonymoushttp://www.blogger.com/profile/01068308481291529801noreply@blogger.com0tag:blogger.com,1999:blog-2281513122300525118.post-76584722035575179842012-09-14T07:00:00.000+02:002012-09-17T09:46:45.727+02:00Spring Roo: sortierte ListenUm mit <a href="http://www.springsource.org/spring-roo">Spring Roo</a> per Konsolenbefehle sortierten Listen zu erstellen, erfordert es einen kleinen manuellen Eingriff.<br />
Hier eine Anleitung anhand eines Beispiels:<br />
<br />
<div style="background-color: #cccccc; font-family: 'Courier New', Courier, monospace;">
<span style="font-family: 'Courier New', Courier, monospace;">field set --fieldName orderedBs --type ~.domain.ClassB </span><br />
<span style="font-family: 'Courier New', Courier, monospace;">--cardinality ONE_TO_MANY --class ~.domain.ClassA</span><br />
<div>
<span style="font-family: 'Courier New', Courier, monospace;">field number --fieldName orderNo --type java.lang.Integer --min 0</span></div>
<div>
<br /></div>
</div>
<div>
Damit erhält man ein unsortiertes Set generiert, es gibt aber kein Schlüsselwort um eine sortierte Liste zu erzeugen. Alle Attribute für die Sortierung sind aber bereits angelegt.<br />
Das Gute: Die Views und Controller funktionieren jetzt schon ohne Anpassung für sortierte Listen. </div>
<div>
Die einzige Änderung die man nun per Hand machen muss, ist die Umstellung in der generierten Modell - Klasse (~domain.ClassA): </div>
<div>
<br /></div>
<div style="background-color: #cccccc; font-family: 'Courier New', Courier, monospace;">
<div>
@OneToMany(mappedBy = "classA", cascade = CascadeType.ALL)</div>
<div>
@OrderBy("orderNo")</div>
<div>
<span style="font-family: 'Courier New', Courier, monospace;"> private List<ClassB>
orderedBs = new LinkedList<ClassB>();</span></div>
</div>
<div>
<br /></div>
<div>
Das war es, es werden sortierte Ergebnisse in den Views ohne weitere Anpassungen geliefert.<br />
Kleiner Pferdefuß: da "order" ein Schlüsselwort in SQL ist, kann es nicht als Attributname für die Annotation OrderBy verwendet werden. D.h. <span style="font-family: Courier New, Courier, monospace;">@OrderBy("order")</span> ist durch SQL verboten! Das merkt aber erst die Datenbank, die Annotation, Hibernate und Spring halten sich hier völlig ruhig.</div>
Anonymoushttp://www.blogger.com/profile/01068308481291529801noreply@blogger.com0tag:blogger.com,1999:blog-2281513122300525118.post-53241960188671228492012-09-13T10:52:00.000+02:002012-09-13T11:01:24.752+02:00Groovy: Mit Files und Texten effizient arbeitenMit groovy kann man sehr schon mit Files und Texten arbeiten. Aber es gibt auch ein paar Stolpersteine. Damit man sicher zum Ziel kommt, hier ein kleines Tutorial.<br />
In diesem Script wird eine Datei geladen und die doppelten Zeilen gelöscht, sowie der Inhalt sortiert. Das Ergebnis wird dann in eine andere Datei mit gleichem Namen aber einer anderen Endung (.txt) gespeichert.<br />
<div style="background-color: #cccccc;">
<div style="font-family: 'Courier New', Courier, monospace;">
fName = "xyz.csv"</div>
<div style="font-family: 'Courier New', Courier, monospace;">
infile = new File(fName)</div>
<div style="font-family: 'Courier New', Courier, monospace;">
outfile = new File(fName[0..-4]+"txt")</div>
<div style="font-family: 'Courier New', Courier, monospace;">
erg = new HashSet()</div>
<div style="font-family: 'Courier New', Courier, monospace;">
infile.eachLine { erg.add(it) }</div>
<div style="font-family: 'Courier New', Courier, monospace;">
outfile.write('');</div>
<div style="font-family: 'Courier New', Courier, monospace;">
erg.sort().each{</div>
<div style="font-family: 'Courier New', Courier, monospace;">
outfile.append(it+"\n", "UTF-8");</div>
<div style="font-family: 'Courier New', Courier, monospace;">
}</div>
</div>
<span style="font-size: large;"><br /></span>
<span style="font-size: large;">java.io und java.util sind schon drin</span><br />
Das Script ist vollständig. Auch ohne imports. Der Grund: Groovy hat in groovy.lang die Pakete io und util dabei. Eine Kleinigkeit, aber für Java Umsteiger doch bemerkenswert.<br />
<br />
<span style="font-size: large;">File ist nicht gleich java.io.File
</span><br />
Eigentlich doch, nur das <a href="http://groovy.codehaus.org/groovy-jdk/java/io/File.html">groovy die Klasse File</a> erweitert hat um einige Convience Funktionen. Im Beispiel sieht man z.B. <span style="font-family: 'Courier New', Courier, monospace;">eachLine </span>was eine Closure erwartet, die dann auf jede Zeile angewendet wird. Was in Java lästig ist: Das recht alte java.io Paket wirft mit Exceptions um sich. Gerade für ein Script ist das lästig. Da hat groovy ganz einfach dafür gesorgt, dass man sich darum nicht kümmern muss. Obwohl es in groovy auch einen sehr guten <a href="http://mrhaki.blogspot.com/2009/09/groovy-goodness-exception-handling.html">try catch Mechanismus</a> gibt, ist per default kein Exception Handling nötig. Zu bemerken ist noch, das ein explizites close auf die Dateien hier nicht notwendig ist, da groovy Files automatisch frei gibt.<br />
<br />
<span style="font-size: large;">Strings sind auch Arrays und Arrays sind anders </span><br />
Viele Java Programmierer werden bei der 3. Zeile stocken. Denn in groovy kann ein String als Array angesehen werden. D.h. man kann die Buchstaben im String einfach per Array Index adressieren. Damit nicht genug, die letzte Stelle in einem Array ist die -1. D.h wenn man den 3 Buchstaben des Datei Suffix adressieren will, findet man den auf der Position -4. Jetzt kommt noch eine weitere Erweiterung von groovy ins Spiel, das bilden von Subarrays. Dazu kann man einfach wie im Beispiel [0..-4] schreiben, um den Substring "alles außer die letzten 3 Zeichen" zu extrahieren.<br />
<br />
<span style="font-size: large;">Encoding raten und die Nebeneffekte</span><br />
Ein wichtiger Designentscheid von Groovy ist Convience: Code less. Nur in selten Fällen kann es in die andere Richtung gehen. Das <a href="http://groovy.codehaus.org/api/groovy/util/CharsetToolkit.html">CharsetToolkit</a> ist so ein Kandidat. Es wird z.B. beim lesen einer Datei das encoding geraten in dem die ersten Zeilen gelesen werden und daraus ein Charset ermittelt wird. Wenn das nicht geht, z.B. bei Ausgabefiles wird der System Default Charset verwendet. Da in meinem Beispiel aber der Text im inFile UTF-8 codiert ist, wird er entsprechend auch so in der Ausgabe erwartet. Darum muss das Charset, wie oben beschrieben, explizit angegeben werden.<br />
<br />
<span style="font-size: large;">Strings und Charakter sind eins, nur für Java nicht</span><br />
Auch und obwohl ich es gut finde, das groovy nicht zwischen chars und Strings unterscheidet, gibt es beim Wechsel von groovy code zu Java Bibliotheken einen Stolperstein. Wird als Parameter einer Java Methode ein char erwartet, dann findet groovy die Methode nicht. Die Folge ist, das man groovy ein wenig auf die Sprünge helfen muss. Kleines Beispiel gefällig:<br />
<br />
<br />
<div style="background-color: #cccccc;">
<span style="font-family: 'Courier New', Courier, monospace;">import org.apache.commons.csv.*</span><br />
<div>
<span style="font-family: 'Courier New', Courier, monospace;"><br /></span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace;">// ok</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace;">new CSVParser(new FileReader(new File(fName)),</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace;"> new CSVStrategy(';' as char,'"' as char,'#' as char))</span></div>
<span style="font-family: 'Courier New', Courier, monospace;"><br /></span>
<span style="font-family: 'Courier New', Courier, monospace;">// geht nicht</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">new CSVParser(new FileReader(new File(fName)),</span><br />
<span style="font-family: 'Courier New', Courier, monospace;"> new CSVStrategy(';','"','#' ))
</span></div>
<br />
Wenn man es weiss, ist es erklärlich. Bis man es gefunden hat, kann man schon an sich Zweifeln...Anonymoushttp://www.blogger.com/profile/01068308481291529801noreply@blogger.com0tag:blogger.com,1999:blog-2281513122300525118.post-41900789076614003002012-09-12T10:15:00.000+02:002012-09-12T10:15:49.409+02:00Groovy: Die Closure - Beispiel findAllFiles<span style="font-size: x-large;">Kernkonzept: Die groovy Closure</span><br />
Ich mag <span style="font-family: inherit;">groovy </span>für kleine Aufgaben, die in Java zumeist unhandlich sind. Nichts gegen Java, aber manches ist gescripted handlicher oder macht zumindest mehr Spass. Einsatzszenarien sind bei mir in der Regel so definiert:<br />
<br />
<ul>
<li><b>kaum Performance Anforderungen</b>. Wenns um die letzten Prozente geht, muss es eben Java sein. Das bedeutet nicht, das in Performance kritischen Anwendungen kein groovy drin sein kann, nur eben nicht an den kritischen Sequenzen.</li>
<li><b>String Manipulation als Kernaufgabe</b>. Ein Script ist ideal, wenn es darum geht automatische Textverarbeitung zu machen. Ob es klassisches Search/Replace oder Textgenerierung/Konvertierung ist, es ist mit groovy leichter. </li>
<li><b>Klein genug um wartbar zu sein</b>. Jedes einzelne Script sollte nicht mehr als eine A4 Seite lang sein. Da groovy sehr kompakt ist, passt auch viel Logik in wenig Zeilen. Darum ist Modularisierung extrem wichtig, sonst landet man schnell bei "write only" Programmen. </li>
</ul>
<br />
<br />
Damit <span style="font-family: inherit;">groovy </span>Spaß macht, braucht man etwas Handwerkszeug um richtig loszulegen. Eines der wichtigsten: Die Closure.<br />
<br />
Die Sourcen, an denen ich entlang hangeln werde (am Ende des Postings ist der gleiche Quellcode ohne Zeilennummern als Kopiervorlage):<br />
<br />
<br />
<div style="background-color: #cccccc; font-family: 'Courier New', Courier, monospace;">
1: findAllFiles = { File folder, Closure fileMatch -><br />
2: def fileNameFilter = {dir, name -> fileMatch(name)} as FilenameFilter<br />
3: def dirFilter = {File file -> !file.isFile()} as FileFilter<br />
4: def files = []<br />
5: def innerFindAll<br />
6: innerFindAll = {File lFolder -><br />
7: if(!lFolder.isFile()){<br />
8: lFolder.listFiles(dirFilter).each{ innerFindAll(it)}<br />
9: files.addAll(lFolder.listFiles(fileNameFilter ))<br />
10: }<br />
11: return files<br />
12: }<br />
13: return innerFindAll(folder)<br />
14: }<br />
<br /></div>
<br />
Und so wirds benutzt:<span style="font-family: 'Courier New', Courier, monospace;"><br /></span><br />
<span style="font-family: 'Courier New', Courier, monospace;"><br /></span>
<span style="font-family: 'Courier New', Courier, monospace;"><span style="background-color: #cccccc;">println findAllFiles(new File("."), {it.endsWith(".xml")})</span> </span><br />
<div>
<br /></div>
<div>
Das Ergebnis ist ein kleines Helferlein, ich verwende es etwa um in allen gelieferten xml Dateien Platzhalter für Autor, Erzeugungsdatum und Version einheitlich zu setzen. Aber das ist ein anderes Thema. Zuerst mal sind die Grundlagen für die Closure zu beschreiben.<br />
<br />
<span style="font-size: large;">Eine erste Closure</span></div>
<div>
Der Code definiert eine Closure <span style="font-family: 'Courier New', Courier, monospace;">findAllFiles</span>. Eine Closure ist ein first Class Bewohner des groovy Ökosystems. Formal ist eine <a href="http://groovy.codehaus.org/Closures">Closure in den groovy Docs definiert</a>, informell ist es ein Methoden-Pointer und das erklärt es für mich auch ausreichend um es benutzen zu können. Etwas ungewohnt für Java Nutzer ist die Definition der Parameter. Dazu hat auch die offizielle Dokumentation eine gute Beschreibung. Von Interesse ist der zweite Parameter: <span style="font-family: 'Courier New', Courier, monospace;">Closure fileMatch</span>. Hiermit wird das Einsatzgebiet flexibler. Die Closure <span style="font-family: 'Courier New', Courier, monospace;">findAllFiles </span>bekommt auf diesen Weg mitgeteilt nach welchen Kriterien die Dateien gefiltert werden sollen. Es ist auch beim Aufruf gut lesbar, was die Closure macht, im Beispielaufruf: finde alle Dateien die mit .xml aufhören. Ich denke, die Aufrufzeile kommt gut ohne Kommentar aus.<br />
Eine weitere Info zu Parametern: Es ist in <span style="font-family: inherit;">groovy </span>nicht notwendig, dass man Variablen explizit definiert, ich tue es aber immer, wenn es der Lesbarkeit dient. Es ist z.B. die Variable <span style="font-family: 'Courier New', Courier, monospace;">folder </span>besser dokumentiert, wenn ich den Typ (<span style="font-family: 'Courier New', Courier, monospace;">java.io.File</span>) aufführe. Woher soll der Nutzer sonst wissen, ob ich den Folder als File oder als String erwarte? Alternativ kann man die Variable entsprechen benennen, in der Art fileFolder, nach kurzer Überlegung, ist es aber keine gute Alternative. Ich müsste ja überall den sperrigen Namen tippen und so eindeutig wie eine explizite Deklaration ist es immer noch nicht.<br />
<br />
<span style="font-size: large;">Closure um Interfaces zu implementieren</span><br />
Die nächsten zwei interessanten Closures sind <span style="font-family: 'Courier New', Courier, monospace;">fileNameFilter </span>und <span style="font-family: 'Courier New', Courier, monospace;">dirFilter</span>. Es ist eine besondere Nutzung, in dem hier jeweils das Interface<span style="font-family: 'Courier New', Courier, monospace;"> java.io.FilenameFilter </span>bzw. <span style="font-family: 'Courier New', Courier, monospace;">java.io.FileFilter </span>implementiert wird. Das funktioniert so einfach für Interfaces mit genau einer Methode. Für Interfaces mit vielen Methoden gibt es in <span style="font-family: inherit;">groovy </span>auch viele Möglichkeiten, ich verwende dann aber ausschliesslich die original Java Art ein Interface zu implementieren. Die speziellen <span style="font-family: 'Courier New', Courier, monospace;">groovy </span>Spielarten verschlechtern IMHO die Lesbarkeit ohne dafür echten Mehrwert zu bieten. Findet eure bevorzugte Methode am besten selbst raus, nur alle durcheinander zu benutzen erschwert die Lesbarkeit enorm.<br />
<br />
<span style="font-size: large;">Closures und Rekursion</span><br />
Mit Closures können Rekursionen, <i>fast </i>so wie in Methoden programmiert werden. Aber nur fast: Es muss zuerst die Closure definiert werden, dann kann sie in der Implementierung selbst referenziert und damit rekursiv benutzt werden. Darum erstreckt sich die Definition der Closure <span style="font-family: 'Courier New', Courier, monospace;">innerFindAll </span>über zwei Zeilen (Zeile 5+6).<br />
<br />
<span style="font-size: large;">Implizites Return</span><br />
Die Zeile 13<span style="font-family: 'Courier New', Courier, monospace;"> return innerFindAll(folder)</span> kann auf das Schlüsselwort <span style="font-family: 'Courier New', Courier, monospace;">return </span>verzichten. Der letzte Wert in einem Codeblock wird in <span style="font-family: inherit;">groovy </span>immer zurückgeliefert. Ich finde aber, dass ein explizites return das lesen erleichtert.<br />
<br />
<span style="font-size: large;">Unbenannte Closure</span><br />
Die letzte Closure im Codebeispiel ist eine unnamed Closure. Es ist die Sequenz: <span style="font-family: 'Courier New', Courier, monospace;">{it.endsWith(".xml")} </span>Die geschweiften Klammern dienen dazu, eine Closure zu definieren. Da die Closure hier als Aufrufparameter genutzt wird und danach obsolet ist, kann sie unnamed definiert werden.<br />
<span style="font-size: large;"><br /></span>
<span style="font-size: large;">Unbenannte Variable it</span><br />
Wer kennt nicht <a href="http://en.wikipedia.org/wiki/The_Addams_Family">Cousin It</a>? Es gibt auch ein interessantes "Es" in <span style="font-family: inherit;">groovy</span>. Jede Closure hat implizit <span style="font-family: 'Courier New', Courier, monospace;">it</span> als Variable und kann ohne zutun benutzt werden. Es spart einiges an Codierarbeit, aber ich nutze es nur in Closures die nicht an verschiedenen Stellen aufgerufen werden, da mit der Automatik auch das Mittel der Dokumentation flöten geht. Als Faustregel: In unbenannten Closures ist <span style="font-family: 'Courier New', Courier, monospace;">it</span> ok, ansonsten ist ein Namen vorzuziehen, sorry Cousin It.<br />
<br />
<span style="font-size: large;">Lesekurs für Java Programmierer</span><br />
Es ist für Java Programmierer, die wohl das Gros der <span style="font-family: inherit;">groovy </span>Nutzer ausmachen, der schwierige Teil, aufmerksam die Klammern zu lesen. Die {} haben in <span style="font-family: inherit;">groovy </span>wie zuvor beschrieben, eine neue Wertigkeit. Die Zeile 8 definiert z.B. die Closure <span style="font-family: 'Courier New', Courier, monospace;">each</span>, es wird schnell mal () verwendet, was einem Methodenaufruf entspricht.<span style="font-family: 'Courier New', Courier, monospace;"> </span><br />
Achtung auch bei []. Es ist keine Array Definition, sondern die Definition einer Liste. So auch im Beispiel auf Zeile 4.<br />
Die resultierenden Fehlermeldungen deuten nicht immer direkt auf die Ursache.<br />
Es ist eine systematische Schwäche von Scriptsprachen, da es nicht zu so deutlichen Fehlermeldungen kommt wie etwa in Java. Aber durchhalten, nach kurzer Zeit hat man die Sematiken der Fehlermeldungen verstanden und ist so schnell mit der Korrektur wie in Java.<br />
<br />
<br />
<span style="font-size: large;">Keine Semikolons wo es geht</span><br />
Es ist dort wo ein Zeilenumbruch vorkommt, nur sehr selten nötig Semikolons zu setzen. Wenn man beim Umstieg auf groovy Konsequent die Semi's weglässt, schränkt man sich kaum ein, zwingt sich aber das man ein Mindestmaß an guter Formatierung beibehält.<br />
<br />
<span style="font-size: large;">Sourcecode</span><br />
Der Quellcode als Kopiervorlage, d.h. ohne die Zeilennummern.<br />
<br />
<div style="background-color: #cccccc; font-family: 'Courier New', Courier, monospace; font-size: x-small;">
findAllFiles = { File folder, Closure fileMatch -><br />
def fileNameFilter = {dir, name -> fileMatch(name)} as FilenameFilter<br />
def dirFilter = {File file -> !file.isFile()} as FileFilter<br />
def files = []<br />
def innerFindAll<br />
innerFindAll = {File lFolder -><br />
if(!lFolder.isFile()){<br />
lFolder.listFiles(dirFilter).each{ innerFindAll(it)}<br />
files.addAll(lFolder.listFiles(fileNameFilter ))<br />
}<br />
return files<br />
}<br />
return innerFindAll(folder)<br />
}</div>
</div>
Anonymoushttp://www.blogger.com/profile/01068308481291529801noreply@blogger.com2Dortmund, Deutschland51.5040988 7.483599551.3459588 7.1677425 51.662238800000004 7.7994565000000007