Using grails projects in Hudson

Being an agile software development company we use a continuous integration (CI) server like Hudson.
For our grails projects we wrote a simple ant target -call-grails to call the batch or the shell scripts:

    <condition property="grails" value="${grails.home}/bin/grails.bat">
        <os family="windows"/>
    </condition>
    <property name="grails" value="${grails.home}/bin/grails"/>

    <target name="-call-grails">
		<chmod file="${grails}" perm="u+x"/>
        <exec dir="${basedir}" executable="${grails}" failonerror="true">
            <arg value="${grails.task}"/>
            <arg value="${grails.file.path}"/>
            <env key="GRAILS_HOME" value="${grails.home}"/>
        </exec>
    </target>

Calling it is as easy as calling any ant target:

  <target name="war" description="--> Creates a WAR of a Grails application">
        <antcall target="-call-grails">
            <param name="grails.task" value="war"/>
            <param name="grails.file.path" value="${target.directory}/${artifact.name}"/>
        </antcall>
    </target>

One pitfall exists though, if your target takes no argument(s) after the task you have to use a different call:

	<target name="-call-grails-without-filepath">
		<chmod file="${grails}" perm="u+x"/>
        <exec dir="${basedir}" executable="${grails}" failonerror="true">
            <arg value="${grails.task}"/>
            <env key="GRAILS_HOME" value="${grails.home}"/>
        </exec>
    </target>

== or equals with Java enum

When you compare objects in Java you should prefer the equals()-method to == in general. The reason is that you get reference equality (like with ==) by default but you are able to change that behaviour. You can override equals() (DO NOT FORGET TO OVERRIDE hashCode() too because otherwise you will break the general class contract) to reflect logical equality which is often what you want, e.g when comparing some string constant with user input.

With primitive types like double and int you are more or less limited to == which is fine for those immutable value types.

But what is the right thing to to with the enum type introduced in Java 5?
Since enums look like a class with methods, fields and the like you might want to use equals() instead of ==. Now this is a special case where using reference equality is actually safer and thus better than logical equality.

Above (please mind the stupid example) we can see that comparing the EState enum with an ILamp using equals() is accepted perfectly by the compiler even though the condition never can be true in practice. Using == the compiler screams and tells us that we are comparing apples with oranges.

Using Flying Saucer PDF offline

Flying saucer is a nice tool for quick PDF generation from a (X)HTML page. Everything worked fine when we tested it at home but when we had a demo at a client’s site, no PDF could be generated. The problem was caused by a little snippet in the header of the HTML:

<!DOCTYPE html PUBLIC
"-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

The DTD declaration! So we took a look at the flying saucer issue database and found someone who had the same problem.
But another solution is even simpler:
Xerces has a default setting which tells the parser to load external dtds.
Turning this off solved our offline problems:

  DocumentBuilderFactory builderFactory =
    DocumentBuilderFactory.newInstance();
  builderFactory.setFeature(
    "http://apache.org/xml/features/nonvalidating/load-external-dtd",
    false);

When as Set is not what you want

When you want to filter out duplicates in a list in groovy you normally do something like:

        def list = [2, 1, 2, 3]
        def filtered = list as Set
        assertEquals([1, 2, 3], filtered as List)

This kicks out all duplicates in a one-liner. But what if the list is sorted (e.g. in reverseOrder)?

        def list = [3, 2, 2, 1]
        def filtered = list as Set
        assertEquals([3, 2, 1], filtered as List) // this fails!

One solution would be to use a small closure:

        def list = [3, 2, 2, 1]
        def filteredList = []
        list.each {
            if (!filteredList.contains(it)) {
                filteredList << it
            }
        }
        assertEquals([3, 2, 1], filteredList)

This closures preserves the order and filters out the duplicates.

Multipage Flows with Grails Part One – The traditional way

When you are developing web apps once in a while you need a flow of multiple pages just like a shopping cart at Amazon. Since there are different ways to implement such a multipage flow in Grails I thought of recording our experiences here.

The scenario

We model the process of submission of a proposal for different kinds of technologies. First you have to decide which type of proposal (once, long-term, in house) you want to submit. On the second page you give your personal infos and a general description of your intentions using the chosen technologies. Here you also choose the technologies you want. This has an impact on the following page which holds the information for the different technologies. Last but not least you save the proposal and show it to the user so he can see what data he entered. This flow could look like this:

-> chooseType -> enterGeneralInfo -> enterSpecificInfo -> save -> show

In each of the steps we need different parts of the full proposal. So we decided to use Command Objects in Grails. These objects are filled and are validated automatically by Grails. To use them you just have to delare them as parameter to the closure:

def enterSpecificInfo = {GeneralPartCommand cmd ->
  if (!cmd.hasErrors()) {
    Proposal proposal = new Proposal(cmd.properties)
    proposal.specificInfos = determineSpecificInfos(proposal.technologies)
    render(view: 'enterSpecificInfo', model: ['proposal': proposal])
  }
  else {
    render(view: 'enterGeneralInfo', model: ['proposal': cmd])
  }
}

You can then bind the properties in the command object to the proposal itself. The GeneralPartCommand has the properties and constraints which are needed for the general information of the proposal. This imposes a violation of the DRY (Don’t Repeat Yourself) principle as the properties and the constraints are duplicated in the proposal and the command object but more about this in another post.

How to collect all the data needed

Since you can only and possibly want to save the proposal at the end of the flow when it is complete you have to track the information entered in the previous steps. Here hidden form fields come in handy. These have to store the information of all previous steps taken so far. This can be a burden to do by hand and is a place where bugs arise.

How to go from one state to another

To transition from one state to the next you just validate the command object (or the proposal in the final state) to see if all data required is there. If you have no errors you render the view of the next state. When something is missing you just re-render the current view:

if (!cmd.hasErrors()) {
  Proposal proposal = new Proposal(cmd.properties)
  proposal.specificInfos = determineSpecificInfos(proposal.technologies)
  render(view: 'enterSpecificInfo', model: ['proposal': proposal])
}
else {
  render(view: 'enterGeneralInfo', model: ['proposal': cmd])
}

The errors section in the view takes the errors and prints them out. The forms in the views then use the next state for the action parameter.

Summary

An advantage of this solution is you can use it without any add-ons or plugins. Also the URLs are simple and clean. You can use the back button and jump to almost every page in the flow without doing any harm. Almost – the transition which saves the proposal causes the same proposal to be saved again. This duplicates the proposal because the only key is the internal id which is set in the save action.
Besides the DRY violation sketched above another problem arises from the fact that the logic of the states and the transitions are scattered between several controller actions and the action parameters in the forms. Also you have to remember to track every data in hidden form fields.
In a future post we take a look at how Spring (WebFlow) solves some of these problems and also introduces others.

Give your project a voice

We are all very into Extreme Feedback Devices (XFD), so we decided to use all our senses to gather feedback from our projects. This becomes a real challenge once you think about it, because we are naturally very focused on (and limited to) visual feedback.

So we decided to put audible feedback to work.

All our projects get continuously built by two servers in parallel. The first server checks for compilation and test errors, just like a good CI server should. The second server applies every quality metric we found helpful to the code and spits out huge amounts of numbers for every single build.

We identified the numbers that really matter to us and established a simple mechanism to scrape them from the result web pages. Then we associated a sound sample with all possible changes and plugged some speakers to our feedback server.

So now, expect our projects to clearly articulate their news.

To give you an idea of how it sounds, here’s a short list of possible audio samples:

  • Fixed an important bug: “Impressive”
  • Reduced code crap: “Excellent”
  • Introduced a bug: “Humiliation”

Imagine the words spoken like in an old Quake game. Now you can have an eventful build and be yelled at like “Impressive Excellent Humiliation”.

We reserved the biggest coding failure we can imagine happening here to a special audio sample. If somebody introduces new code crap (as determined by Crap4J), he gets ordered to “CUT THE CRAP!” at incredible volume. We used the voice of the inventor of XFDs, Alberto Savoia, taken from his delightful training video for management by numbers (position 2:03ff). The audio quality isn’t convincing, his command surely is.

If you wonder what it’s like to be suddenly interrupted by different voices rebuking or praising you – it’s healthy. You get used to it very quickly, yet the information always catches on. And the information is always relevant.

We call it our “audible remorse”.


Read more about our Extreme Feedback Devices:

We are changing our locale

This is a bilingual posting:

We decided to continue this blog in english language, though we are no way of native speakers. Old postings may be translated in the future, if worth the effort.

The Softwareschneiderei remains a german-speaking company.

—-

Dies ist ein zweisprachiger Eintrag:

Wir haben uns entschieden, diesen Blog auf englisch weiterzuführen, obwohl wir keine englischen Muttersprachler sind. Alte Einträge könnten zukünftig übersetzt werden, wenn sie den Aufwand wert sind.

Die Softwareschneiderei bleibt eine deutschsprachige Firma.

Der blinde Fleck von Continuous Integration

Blinder FleckAn english version of this blog entry can be found at https://schneide.wordpress.com/2009/12/28/a-blind-spot-of-continuous-integration/

Am Montag früh brach nach einem Routine-Update unseres CI-Servers und anschließendem manuell gestartetem Baudurchgang plötzlich ein Test in einem Projekt, das seit Wochen keine Änderung mehr erfahren hatte.

Die Analyse des Tests zeigte relativ schnell, dass offensichtlich die A-TRIP Regeln für gute Unit Tests verletzt wurden und der Test unter Verwendung des aktuellen Zeitpunkts Datumsberechnungen in die Zukunft und die Vergangenheit testet. Am Sonntag früh wurde auf Sommerzeit umgestellt, so dass dem Test plötzlich eine Stunde in der Vergangenheitsberechnung fehlte.

Den Test zu beheben war einfach, allerdings war er bereits seit Mai 2006 im Projekt enthalten. Seitdem muss dieses Projekt immer um die Zeitumstellung herum inaktiv gewesen sein. Unser Continuous Integration hat das Problem jedenfalls erst jetzt (und auch da nur durch den Zufall einer CI-Aktualisierung zum richtigen Zeitpunkt) gefunden.

Die Phänomene, die durch ungeschickte Unit Tests hervorgerufen werden, sind mit einem verdachtunabhängigen Nightly Build scheinbar zuverlässiger zu finden als mit Continuous Integration. Eine Kombination beider Techniken scheint uns nach dieser Erfahrung lohnenswert zu sein.

Unabhängig von CI oder Nightly Builds gilt: Ein Unit Test, der den Konstruktor von Date, DateTime, Calendar oder einer ähnlichen Klasse aufruft, ist vermutlich ein schlechter Unit Test.

Gedanken zu Online-Hilfen

Eine gute moderne Software hat eine kontextbezogene Online-Hilfe. Bei einer sehr guten modernen Software wird diese nie benutzt, aber das ist ein anderes Thema.

Wir haben in der Schneide verschiedene Ansätze ausprobiert, die alle einen großen Nachteil hatten: Die eingestellten Inhalte waren an manchen Stellen unverständlich oder zu knapp und der Benutzer konnte nicht viel dagegen tun. Daher befassen wir uns gerade mit der Idee, eine Online-Hilfe mit einem Wiki zu koppeln. Auf diese Weise könnten die Benutzer ihre Online-Hilfe selbst verbessern und fortschreiben, so dass die Inhalte hilfreicher werden.

Um die Vor- und Nachteile dieses Ansatzes nicht erst durch den Kunden testen zu lassen, haben wir einen Selbstversuch gestartet. Die Online-Hilfe betrifft allerdings keine Software, sondern die Schneide selbst.

Eine Online-Hilfe für den Arbeitsplatz

Wir haben uns das Konzept der “WikiTags” ausgedacht, kleine Aufkleber mit einer Wiki-Adresse und einem Symbol, das auf diesen Umstand hinweist.

Thumb Spoon WikiTag 

Mit diesen WikiTags rüsten wir gerade alle Räume und Gegenstände in Reichweite aus, so dass sie einem Eintrag in unserer Firmen-Online-Hilfe (unserem Wiki) zugeordnet sind. Über ein kleines Eingabefeld in unserem Intranetportal gelangt man direkt auf die betreffende Wiki-Seite (der sogenannte WikiWarp), kann dort die Informationen zum Gegenstand (z.B. Bedienhinweise oder Bezugsquellen) einsehen und auch gleich verbessern und ergänzen.

Spoon Wiki Page

Wir haben also eine halbautomatisch aktivierbare Online-Hilfe für unsere Firma implementiert und die ersten Erfahrungen damit gesammelt. Was ist die einprägsamste Erfahrung? Wir brauchen kleine, portable Anzeigegeräte für spezielle Anwendungsfälle (siehe Bild).

Special WikiTag

“Poor man’s reporting” oder Ultraleichtgewichtiges Java Reporting

In den meisten unserer Softwarelösungen wird irgendwann irgendeine Form von Reportdokument erstellt. Meist fällt dem Kunden ein, dass er gerne ein hübsches Pdf mit Messwerten und ein paar Diagrammen hätte. Im Laufe der Zeit haben wir diverse freie (sowohl wie in Bier als auch Rede) als auch kommerzielle Lösungen ausprobiert und eingesetzt. Darunter sind u.a. Jasper Reports und RReport, jedoch hatten sie alle ihre mehr oder weniger großen Haken und Ösen. Die meisten teureren kommerziellen Lösungen (wie z.B. Big Faceless PDF) kamen wegen des jeweiligen Projektumfangs und den wenigen benötigten Features nicht in Betracht.

Nach der Lektüre von iText in Action zur Pdf-Erzeugung und -Manipulation und der Suche nach einem Werkzeug, mit dem man einfach und bequem Acroforms erzeugen kann kristallisierte sich eine neue, extrem leichtgewichtige Lösung für das Reporting-Problem heraus: OpenOffice und iText. Carl Young beschreibt in seinem Blog die Benutzung von OpenOffice als Designwerkzeug für Acrobat Forms.OpenOffice Form

Solche Pdf-Formulare lassen sich relativ einfach mit iText ausfüllen und mit Bildern versehen und dann abspeichern.

Form in AcrobatFilled Form

Damit hat man eine pure Java-Lösung in der Software und ein mächtiges, plattformübergreifendes Designwerkzeug für die Reports.

Die augenscheinlichsten Vorteile dieser Lösung sind:

  • Reportvorlagen liegen in Pdf vor, sind somit von jedem einsehbar und kontrollierbar
  • Die Quellen für die Reportvorlagen können mit OpenOffice erstellt und geändert werden und einfach per Knopfdruck als Pdf exportiert werden
  • Die Reportdaten können in beliebigen Formaten abgelegt oder im Programm generiert werden und dann mithilfe von iText in das Pdf-Formular übertragen werden

Die bisher festgestellten Nachteile sind je nach Einsatzzweck vernachlässigbar, können aber auch zum Showstopper werden:

  • Man muss immer feste Bereiche für die Daten festlegen, sind diese zu groß, werden sie wahlweise skaliert oder abgeschnitten (geht sowohl mit Bildern als auch mit Text!). Hat man also einen langen Fließtext und der Platz im Formularfeld reicht nicht aus, so hat man ein ziemliches Problem.
  • Man bekommt kein Format für die Formulardaten selbst geschenkt, sondern muss sich selbst um deren Speicherung kümmern, um den selben Report zu einem späteren Zeitpunkt neu zu erstellen oder die Reportdaten zur weiteren Verarbeitung bereitstellen zu können. Die programmatische Extraktion der Reportdaten aus dem fertigen Pdf-Report ist extrem aufwändig bis unmöglich.

Die bisherigen Erfahrungen mit dieser OpenOffice+iText-Lösung sind sehr positiv und helfen auch sehr bei der Kommunikation mit dem Kunden. In manchen Fällen fühlt sich dieser aufgrund der Textverarbeitung sogar in der Lage, selbst Layoutänderung auszuprobieren und durchzuführen. Selbst ungeschickte Entwickler bekommen durch OpenOffice mit vergleichsweise geringem Einarbeitungsaufwand ansehnliche Reports hin oder können die Sekretärin damit beauftragen. Kostenlos ist die ganze Lösung noch dazu.