Montag, 22. September 2014

Jersey 2 und JAXB - Deutliche Performance-Steigerung durch Wechsel von EclipseLink MOXy auf Jackson

In der vergangenen Woche haben wir ein Performance-Problem in einem unserer REST Services analysiert. Der Service dient einer Reihe von internen Applikationen als zentrales Gateway zu einem externen Provider von Patentinformationen. Auf Anfrage ruft er bei dem externen Dienstleister Patentinformationen in Form von JSON Objekten ab um diese dann in ein internes XML-Format umzuwandeln und an die aufrufenden Applikationen zurückzugeben.
Sowohl die Server-Seite für die internen Applikationen, als auch die Client-Seite zur Anbindung des externen Dienstleisters ist mit Jersey 2 realisiert. Für die Umwandlung werden die vom Dienstleister abgerufenen JSON-Strings mit Hilfe von JAXB in ein Objektmodell überführt. Dieses wird anschließend in ein Zielmodell transformiert und wiederum über JAXB in das auszuliefernde XML-Format serialisiert. Als JAXB-Implementierung verwendeten wir bisher EclipseLink MOXy, die Standard-Implementierung bei Jersey.

Das Problem

Die grundsätzliche Verarbeitungsgeschwindigkeit unseres Services war in Ordnung. Er war in der Lage ein bis zwei Patent-Dokumente pro Sekunde zu verarbeiten, was uns nicht in Jubel verfallen lies, für unsere angebundenen Applikationen jedoch vermutlich ausreichend war. Während der abschließenden Tests fiel uns allerdings auf, dass einige der Anfragen z.T. mehrere Sekunden dauerten. Bei steigender Last stieg auch die Häufigkeit dieser lang laufenden Anfragen, sowie die Zeit die diese benötigten. In Ausnahmefällen konnten wir Antwortzeiten von 45 Minuten (!) und mehr beobachten. Außerdem war auffällig, dass die CPU-Nutzung während dieser lang laufenden Anfragen kontinuierlich hoch war. Bei Batch-Abfragen mit mehreren parallelen Threads ließ sich anhand der CPU-Auslastung deutlich erkennen, wieviele Threads gerade mit einer solchen lang laufenden Anfrage beschäftigt waren.

Die Suche nach der Ursache

Zunächst vermuteten wir die Ursache beim externen Dienstleister, dessen Service auch an anderen Stellen bereits einige Schwächen gezeigt hatte. Wir analysierten das System mit Hilfe von VisualVM und fanden heraus, dass die lang laufenden Prozesse immer an derselben Stelle hingen, nämlich in der javax.ws.rs.core.Response bei der Ausführung der Methode readEntity(Class entityType). Das schien unseren Verdacht zunächst zu bestätigen. Wenn das Auslesen des Inhalts der Response lange dauert, liegt das vermutlich daran, dass dieser vom Server zu langsam geliefert wird. Zwei Punkte gab es jedoch, die uns komisch vorkamen:

  1. Wir hatten in Jersey eine Timeout-Zeit von 90 Sek. konfiguriert, deren prinzipielles Funktionieren wir auch beobachten konnten. Warum griff diese in den von uns beobachteten Fällen nicht?
  2. Wenn unser Service lediglich auf die Übertragung der Antwortdaten vom Dienstleister wartet, woher kommt dann die hohe CPU-Auslastung?

Ein genauerer Blick in die "hängenden" Threads führte uns dann auf die richtige Spur. Die Threads warteten nicht sondern waren eifrig damit beschäftigt die z.T. recht großen JSON-Strings vom Dienstleister (bis zu 30 MB!) zu parsen. Knackpunkt war dabei eine Methode des org.eclipse.persistence.internal.oxm.record.json.JSONReaders (EclipseLink MOXy):


Neben der etwas gewöhnungsbedürftigen Signatur fiel uns vor allem der interne Umgang mit der übergebenen Zeichenkette auf. Zum einen wird der Ergebnis String per einfacher String-Konketenation (+=) zusammengebaut, wofür in Java extra effizientere Methoden (StringBuffer bzw. StringBuilder) vorhanden sind. Zum anderen arbeitet die Methode an einigen Stellen mit Substrings. 
Über die geänderte Implementierung der Substring-Methode ab der Java-Version 7 waren wir bereits an einer anderen Stelle gestolpert. Im Gegensatz zur Java 6 Implementierung, bei der ein Substring weiterhin auf das selbe Character-Array im Speicher verwiesen und lediglich das relevante Fenster (Offset, Count) neu definiert hat, legt die Java 7 Implementierung eine Kopie des Character-Arrays im Speicher an (siehe dazu auch http://www.programcreek.com/2013/09/the-substring-method-in-jdk-6-and-jdk-7/). 
Diese geänderte Vorgehensweise macht an vielen Stellen Sinn (wenn aus einem großen Strings nur einige wenige Zeichen zur weiteren Verarbeitung benötigt werden und der Rest verworfen werden kann). In unserem speziellen Fall führte sie jedoch dazu, dass die ohnehin schon speicherintensiven Strings noch mehrere Male kopiert wurden.

Der Wechsel zu Jackson

Auf der Suche nach Lösungen kamen wir zu der Idee, dass der Austausch einer JAXB Implementierung in der Theorie doch eigentlich problemlos möglich sein sollte. Schließlich definiert das JDK die verwendete API und ich muss lediglich die Implementierung meiner Wahl hinzufügen. In früheren Projekten hatten wir recht gute Erfahrungen mit Jackson gesammelt. Wir entschieden uns es einfach zu versuchen, auch wenn wir nicht sicher waren, ob eine andere Implementierung unser Problem wirklich beheben würde. 

Im ersten Ansatz suchten wir uns die zu unserer Jersey Version 2.5.1 passende Jackson Bridge jersey-media-json-jackson 2.5.1 heraus. Leider mussten wir beim Aktualisieren der Dependencies feststellen, dass diese noch auf die Jackson Version 1.9.13 verwies. Eine Jackson 2.x Version hatten wir uns allerdings schon vorgestellt. Ein Versuch der vorhandenen Jersey Version einfach ein Jackson 2 unterzujubeln funktionierte erwartungsgemäß nicht. Also galt es auch Jersey zu aktualisieren. 

Die jersey-media-json-jackson Bridge verwendet Jackson 2 ab der Version 2.9. Da war der Weg bis zur aktuellen Version Jersey Version 2.11 auch nicht mehr weit. Wir aktualisierten Jersey 2.5.1 also auf die Version 2.11, entfernten die jersey-media-moxy Bridge sowie EclipseLink MOXy selbst und fügten statt dessen die jersey-media-json-jackson Bridge und die aktuelle Jackson Version 2.4.1 hinzu. Neben den von der Jersey-Bridge referenzierten Jackson-Bibliotheken fügten wir außerdem noch die jackson-dataformat-xml Bibliothek hinzu, um auch die Serialisierung ins interne XML-Format über Jackson abbilden zu können. Nach dem Update der Dependencies galt es dann ein paar Compiler-Fehler zu beheben.

Das bisher im Jersey Client registrierte MoxyJsonFeature wurde durch das JacksonFeature ersetzt. Anstelle eines custom MoxyJsonContextResolver erstellten wir einen analogen JacksonObjectMapperContextResolver (nach Vorbild der Jersey Dokumentation) und registrierten ihn ebenfalls im Jersey Client.

In einigen Beans unseres internen Objektmodells hatten wir einzelne Properties mit der MOXy-Annotation @XmlCDATA versehen um für diese bei der Serialisierung nach XML die Kapselung in einer CDATA-Sektion zu erzwingen. Dies ist notwendig, da die enthaltenen Textdaten HTML-Tags zur Formatierung enthalten, die für die Anzeige in den angebundenen Applikationen verwendet werden und somit nicht maskiert werden sollen. In Jackson gibt es keine Möglichkeit eine CDATA-Kapselung gezielt für einzelne Properties zu erzwingen. Als Alternativlösung erstellten wir einen CDataContentHandler (gefunden bei stackoverflow), der für alle serialisierten Properties angewendet wird und die Werte in eine CDATA-Sektion kapselt wenn es notwendig ist. Den Content Handler geben wir dem JAXB Marshaller in der marshal-Methode mit.

In dem Bean Package für das Objektmodell des Dienstleister-Formats hatten wir in einer jaxb.properties Datei noch die MOXy JAXBContextFactory registriert. Welche Context Factory hier für Jackson einzutragen ist haben wir nicht herausgefunden, also haben wir die Datei kurzer Hand gelöscht. Die anschließenden grünen Integrationstests haben uns recht gegeben. Der Umstieg war geschafft. Fairerweise muss gesagt werden, dass wir uns die hier aufgeführten Punkte mühsam zusammensuchen mussten und somit insgesamt gut einen Tag mit dem Umstieg zugebracht haben. Nicht zuletzt war das auch der Grund das alles in einem Blog-Artikel festzuhalten.

Das Resultat

Nachdem alle Tests wieder grün waren blieb die spannende Frage: Was hat die ganze Aktion jetzt gebracht.

Wie deployten den umgestellten REST Service in unsere Testumgebung, ließen die ersten kleinen Lasttests dagegen laufen und beobachteten gebannt die CPU- und Memory-Auslastung in VisualVM bzw. der JConsole. Die Verbesserung war enorm, viel größer als wir sie erwartet hatten. Hatte unser Service zuvor unter Last im Durchschnitt zwischen 500 und 800 MB Speicher und zwischen 40 und 60 % CPU-Last (mit Peeks bis zu 90 %) verursacht, so kommt er jetzt mit um die 250 - 300 MB Speicher und halbwegs konstant unter 10 % CPU-Last aus. Das spürt man auch an den Antwortzeiten und der Stabilität. Lang laufende Anfragen kommen nicht mehr vor. 

Fazit: Die umfangreiche Analyse sowie die Umstellung auf Jackson haben sich für uns auf jeden Fall gelohnt. Für zukünftige Projekte werden wir vermutlich gleich auf Jackson setzen.

Mittwoch, 19. Juni 2013

Zum Beispiel ein Test

Beispiele als Grundlage für Anforderungsanalyse, Spezifikation und Implementierung



Einleitung


Beispiele helfen in der Analysephase die Kundenanforderungen zu verstehen und zu diskutieren. Später können diese dann als Basis für automatisierte Akzeptanztests verwendet werden. Klingt ganz einfach? In der Praxis kann es jedoch einige Hürden geben, insbesondere wenn es um die Erweiterung einer existierenden Anwendung geht. Im Vortrag werden Erfahrungen aus einem Kundenprojekt geschildert und unterschiedliche Möglichkeiten aufgezeigt um aus definierten Beispielen automatisierte Akzeptanztests abzuleiten.


Analyse und Spezifikation von Anforderungen


In dem zugrundeliegenden Kundenprojekt wurde ein bestehendes Softwaresystem um eine Reihe von neuen Features erweitert. Grundlage für die Entwicklung jedes einzelnen Features war die Aufnahme und Analyse der Kundenanforderungen und deren Überführung in eine Spezifikation des zu entwickelnden Features. Dazu wurden Anforderungsworkshops durchgeführt, in denen der Auftraggeber seine Anforderungen präsentierte und mit dem Analysten diskutierte. Die Aufgabe des Analysten bestand darin die Ergebnisse der Diskussionen in eine „Prosa“-Spezifikation zu überführen. Diese wurde anschließend vom Auftraggeber geprüft, freigegeben und schließlich zur Umsetzung an den Entwickler übergeben.

Das Ergebnis dieser Vorgehensweise war häufig ernüchternd. Das fertige Feature entsprach nur zum Teil den Erwartungen des Kunden. Missverständnisse und Spezifikationslücken wurden oft erst im Rahmen der Abnahmetests des Kunden aufgedeckt.

Ursache: Auftraggeber, Analyst und Entwickler haben die Spezifikation jeweils nach ihren eigenen Vorstellungen interpretiert und sie „so verstanden, wie sie sie verstehen wollten“. Jeder hat nach bestem Wissen und Gewissen das spezifiziert, geprüft und entwickelt was er verstanden hat. Ein gemeinsames Verständnis fehlte, es fiel über diesen Weg jedoch nicht auf.


Beispiele


Beispiele veranschaulichen und verdeutlichen Zusammenhänge und helfen dadurch Missverständnisse und Lücken aufzudecken. Im Gegensatz zu einer allgemeingültig formulierten “Prosa”-Spezifikation verlangen Beispiele konkrete Angaben.

Nachfolgend exemplarisch eine Spezifikation sowie ein dazu formuliertes Beispiel um dies zu verdeutlichen:

Spezifikation:
Die Afa einer Investition ergibt sich 
aus dem Investitionswert, 
linear verteilt über den Abschreibungszeitraum, 
beginnend mit dem Monat der Aktivierung

Beispiel:
Wenn wir eine Investition im Wert von 600 TEUR getätigt haben, 
die am 01.01.2012 aktiviert wurde, 
dann folgt daraus, 
bei einer Abschreibungsdauer von 60 Monaten, 
eine monatliche AfA in Höhe von 10 TEUR 
und zwar von Jan/2012 bis Dez/2016

Die Verwendung von konkreten Werten führt oft dazu, dass Fachbegriffe verdeutlicht und weitere Details aufgedeckt werden. So war in der vorliegenden Spezifikation zunächst nur von einem  „Abschreibungszeitraum“ die Rede. Dass es eine definierte „Abschreibungsdauer von 60 Monaten“ gibt wurde erst durch das Beispiel offensichtlich. Außerdem werfen konkrete Beispiele häufig weitere Fragen auf, wie: „Erfolgt die Aktivierung immer am Anfang eines Monats?“ oder „Ist auch eine andere Abschreibungsdauer als 60 Monate möglich?“

Im dem zugrundeliegenden Kundenprojekt wurden in Anforderungsworkshops bis dahin nur vereinzelt Beispiele verwendet um schwierige Sachverhalte zu erklären oder um als Diskussionsgrundlage zu dienen. In den meisten Fällen wurden sie jedoch spätestens nach der Überführung in die Spezifikation verworfen. Dabei können solche Beispiele im Laufe der Diskussion wachsen und weitere Details aufdecken. Angefangen von einem grundlegenden Standardfall werden schnell Spezialfälle identifiziert und können wiederum durch entsprechende Beispiele festgehalten werden. Schnell entsteht auf diesem Weg eine Sammlung von Beispielen, die ein Feature nachvollziehbar und überprüfbar beschreibt. Beispiele, die vom Kunden oder mit dem Kunden zusammen erarbeitet werden geben auch Aufschluss darüber, was dem Kunden wichtig ist und was er vom System erwartet. Damit werden neben den Details eines Features auch die „Akzeptanzkriterien“ deutlich die der Kunde an das System stellt.

Noch wichtiger als die resultierenden Beispiele ist die Diskussion die diese auslösen. Sie führt dazu dass die Teilnehmer das Feature wesentlich intensiver beleuchten und detaillierter verstehen als dies sonst der Fall wäre.

Allerdings sind Beispiele nicht immer so einfach aufzustellen, wie es auf den ersten Blick aussieht. Es verlangt einige Übung innerhalb einer Diskussion die richtigen Beispiele zu identifizieren und sich auf die relevanten Inhalte zu beschränken. Oft existieren umfangreiche Einflussfaktoren die zu einem konkreten Ergebnis führen. Es kann mühsam sein, all diese Einflussfaktoren zu identifizieren und mit geeigneten Werten zu belegen. Das Ziel sollte daher immer sein sich auf das Wesentliche zu beschränken.

Zusammenfassend lässt sich folgende Aussage festhalten:

Durch die Verwendung von Beispielen 
wird die Spezifikation eines Features nicht einfacher 
sondern besser.

Sollen bestehende Features eines Systems geändert werden enthalten die entsprechenden Beispiele oft umfangreiche Teile des vorhandenen Systems. Werden diese innerhalb eines Anforderungsworkshops „nachspezifiziert“ entstehen häufig Aussagen wie „Das haben wir doch alles schon mal besprochen. Alles soll bleiben wie bisher, nur…“ Es hat sich in diesen Fällen als hilfreich erwiesen bereits im Vorfeld aus dem bestehenden System grundlegende Beispiele abzuleiten und diese im Workshop dem Kunden als Ausgangspunkt für die Spezifikation der Änderungen zu präsentieren.


Specification by Example


In seinem Buch „Bridging the Communication Gap“ beschreibt Gojko Adzic unter dem Begriff „Specification by Example“ die Verwendung von Beispielen als alleinige Spezifikation. Alle relevanten Aspekte eines Features werden dabei vollständig in Form von Beispielen beschrieben. Eine Prosa-Spezifikation ist demnach nicht mehr notwendig und entfällt.

Führt man diesen Ansatz konsequent weiter können alle definierten Beispiele in Summe als die Kriterien festgehalten werden bei deren Erfüllung das entwickelte Feature als akzeptiert (= abgenommen) gilt. Die Entwicklung erfolgt dann von Anfang an basierend auf diesen „Akzeptanzkriterien“ und der Entwickler hat sehr konkrete Vorgaben was das neue Feature leisten muss. Man spricht in diesem Fall von „Acceptance-Test Driven Development“  (ATDD).

In vielen Fällen ist der Kunde jedoch eine Prosa-Spezifikation gewohnt und sieht sie oft sogar explizit in seinem Vorgehensmodell und entsprechenden Dokumentvorlagen als Artefakt vor. Darüber hinaus lassen sich nicht alle Sachverhalte in Form von Beispielen beschreiben. Insbesondere Zusammenhänge zwischen einzelnen Sachverhalten sowie Begründungen für das geforderte Verhalten können damit nicht ausreichend beschrieben werden. Insofern wurde im konkreten Projekt ein Mittelweg eingeschlagen bei dem die Prosa-Spezifikation umfangreich durch entsprechende Beispiele ergänzt wurde. Im Prosa-Teil werden dabei neben der Problemstellung, den Rahmenbedingungen und dem Lösungsentwurf i.W. die Kern-Features sowie die wichtigsten Sonderfälle beschrieben. Die Beispiele verdeutlichen die Prosa-Beschreibung und ergänzen weitere Sonder- und Randfälle.


Vom Beispiel zum Test


Neben den bereits beschriebenen Eigenschaften von Beispielen bieten diese einen weiteren Vorteil:

Beispiele können nachgestellt bzw. ausgeführt werden

Damit sind die Beispiele aus der Analyse/Spezifikation eine optimale Grundlage zur Definition von Akzeptanztests.

Formuliert man das zuvor genannte Beispiel etwas um wird dies deutlich:

Wir haben eine Investition im Wert von 600 TEUR
aktiviert am 01.01.2012
und eine Abschreibungsdauer von 60 Monaten

Ausgangssituation/-zustand
Wenn wir die Afa berechnen

Aktion/Ereignis
dann folgt daraus
eine monatliche AfA
in Höhe von 10 TEUR
in der Zeit von Jan/2012 bis Dez/2016
Erwartetes Ergebnis

Damit können die definierten Beispiele eines Features als „Definition of Done“ für die Entwicklung festgelegt werden. Wenn alle definierten Beispiele (= Akzeptanztests) erfolgreich durchlaufen werden ist das Feature vollständig umgesetzt.

Es ist nicht zwingend erforderlich diese Akzeptanztests automatisiert umzusetzen. Sie können auch manuell ausgeführt werden. Spätestens wenn zum betreffenden Feature weitere Änderungsanforderungen vom Kunden gemeldet werden zahlt sich eine Automatisierung jedoch aus. Darüber hinaus können automatisierte Akzeptanztests regelmäßig ausgeführt werden um sich gegen Regressionsfehler zu schützen.

Im Falle einer Java-Anwendung können die automatisierten Akzeptanztests entweder mit Hilfe von Spezifikationsframeworks wie z.B. JBehave, Cucumber oder Concordion oder ausschließlich auf Basis von JUnit umgesetzt werden. In allen Fällen sollten die Tests wie eine Spezifikation lesbar sein Im Optimalfall entsteht dadurch eine „Lebenden Spezifikation“, d.h. eine Beschreibung der vollständigen Funktionalität einer Anwendung in Form von automatisierten Akzeptanztests.

Donnerstag, 8. November 2012

Configurable entities with fluent interface

When testing Java applications you usually have to create entities with some kind of test data. Beside the content you need for your tests you often have to define some more stuff, just to get a valid entity. Particularly, in an integration test scenario, where you want to store your test entity in a database, you may additionally have to create some master or referenced entities to be able to save your data. Doing this in every single test (class) will pollute your tests very soon and makes it hard to read and maintain them.

In my last projects I used "configurable" entity extensions in combination with factory classes that create fully configured default entities to get my test code clean and readable. The API of the configurable entities mainly provides a "fluent interface" as described some years ago by Martin Fowler and Eric Evans (see http://martinfowler.com/bliki/FluentInterface.html).

Let's have a look at an example to illustrate the idea.

Imagine you have a Customer entity like the following.


public class Customer {

    private String firstName;
    private String lastName;
    private String eMail;
    private String phoneNumber;

    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    ...
}

If you just use its Java Bean API to create a new customer in your test setup you would have to write something like this:

@Test
public void doSomethingWithACustomer() {
    Customer aCustomer = new Customer();
    aCustomer.setFirstName("Donald");
    aCustomer.setLastName("Duck");
    aCustomer.setEMail("donald@ent.net");
    aCustomer.setPhoneNumber("001-1234-98765");
    ...
}


A ConfigurableCustomer that I usually would write looks as follows:


public class ConfigurableCustomer extends Customer {

    public ConfigurableCustomer withFirstName(String firstName) {
        this.setFirstName(firstName);
        return this;
    }

    public ConfigurableCustomer withLastName(String lastName) {
        this.setLastName(lastName);
        return this;
    }

    public ConfigurableCustomer withEMail(String eMail) {
        this.setEMail(eMail);
        return this;
    }

    public ConfigurableCustomer withPhoneNumber(String phoneNumber) {
        this.setPhoneNumber(phoneNumber);
        return this;
    }
}

It extends the regular Customer and provides one with-method for each property of the entity. Each of these methods just does two things:
  • it calls the regulary setter for this property to set the given value
  • it returns the ConfigurableCustomer instance itself
With this test extension, we can refactor the formerly written test:

@Test
public void doSomethingWithACustomer() {
    ConfigurableCustomer aCustomer = new ConfigurableCustomer()
        .withFirstName("Donald")
        .withLastName("Duck")
        .withEMail("donald@ent.net")
        .withPhoneNumber("001-1234-98765");
    ...
}

This code is a little slighter and there's less duplication in it. But that's not the most important change. It is also much more readable. It can almost be read like a part of a specification.

Now, what if all we need to configure in our test is the E-Mail address of our customer? Of course, we  need to define all the other data somewhere to get a valid customer to work with. But in our test setup we only want to describe the parts that are necessary for the test.

That is when I usually introduce a CustomerFactory in my test sources.


public class CustomerFactory {

    /**
     * Creates a ConfigurableCustomer using some default data.
     * @return the Customer.
     */
    public static ConfigurableCustomer create() {
        return new ConfigurableCustomer()
                .withFirstName("Donald")
                .withLastName("Duck")
                .withEMail("donald@ent.net")
                .withPhoneNumber("001-1234-98765");
    }
}

It provides one - or sometimes more - methods to create a customer fully configured with some default data. It returns a ConfigurableCustomer instance, so we can easily overwrite individual properties as needed. If we only need an arbitrary customer, not interrested in its details, we can just take the default entity as it is. Therefore, the factory always should return a fully filled and valid entity.

In our test, where we just need a customer with a known email, we can now use the following code:


@Test
public void doSomethingWithACustomer() {
    Customer aCustomer = CustomerFactory.create()
                .withEMail("a.known@email.com");
    ...
}

We will get a valid customer named "Donald Duck" with the default phone number and the defined email. All done with a tiny and readable piece of code in our test.

If we later on have to modify the Customer entity, e.g. add some more properties, all we have to do is to also extend the CustomerFactory to set reasonable default values for the new fields. By extending the ConfigurableCustomer class, too, we also make this new fields configurable.

That's it. No magic, no rocket science, just some simple patterns that make your testing life easier. Feel free to use it in your code and let me know your experiences with it.

Remark: If you're using JPA or another object-relational mapping technology you may experience some limitations extending your entity classes. In this case we have to find another way to implement our ConfigurableCustomer, e.g. let it wrap the Customer entity instead of extending it. Doing this, we no longer can easily use the ConfigurableCustomer everywhere a "normal" Customer is expected. Instead, we have to call a toCustomer() method to get the wrapped entity.


public class ConfigurableCustomer {

    private Customer customer;

    public ConfigurableCustomer withFirstName(String firstName) {
        customer.setFirstName(firstName);
        return this;
    }

    public ConfigurableCustomer withLastName(String lastName) {
        customer.setLastName(lastName);
        return this;
    }

    ...

    public Customer toCustomer() {
        return customer;
    }
}


Our test case using this wrapping ConfigurableCustomer would look like this.


@Test
public void doSomethingWithACustomer() {
    Customer aCustomer = CustomerFactory.create()
                .withEMail("a.known@email.com")
                .toCustomer();
    ...
}

That's a few more characters to type and a little cumbersome to read, but still much better than our example at the beginning of this post.


(Thanks to @stefanscheidt for review!)

Dienstag, 9. Oktober 2012

Build grails-app on travis-ci.org

I've just successfully configured a continous integration build of my new grails web application using travis-ci.org.

Travis-ci.org is a continuous integration service for the open source community. It is free to use and fully integrated with github. It supports multiple programming languages including java and groovy.

To get started with travis-ci.org log in using your github account and read the getting started tutorial.

Once you have activated your github repository you need to add a .travis.yml file to your repository containing the necessary build configuration settings for travis-ci to build your application.

For a grails application you can use the following .travis.yml configuration:

language: groovy

jdk:
- oraclejdk7

before_install:
- sudo add-apt-repository -y ppa:groovy-dev/grails
- sudo apt-get update
- sudo apt-get install grails-2.1.1


script: grails test-app

Thanks to berngp@github for publishing gist!

Mittwoch, 16. Mai 2012

Simple Java DateBuilder

I took some time on a trip to Munich to extract a little DateBuilder class from one of my latest Java projects and publish it to github. It's no big thing, just a simple wrapper around the default GregorianCalendar with a nice and simple interface. Check it out and feel free to use it in your projects.


Usage:

// two days ago from today
DateBuilder.today().daysAgo(2);

// 15-09-2012
DateBuilder.givenDate(15, 5, 2012).monthsAhead(4);

// first day of next month
DateBuilder.today().day(1).monthAhead(1);

// or
DateBuilder.today().firstDay().nextMonth();

Freitag, 27. Mai 2011

Testen von erwarteten Exceptions mit JUnit4

Beim durcharbeiten einer Schulung zum Thema "Microtesting" werde ich gerade auf ein Feature in JUnit4 aufmerksam, welches ich bisher noch nicht kannte.

Wollte ich bisher testen, dass eine Methode eine Exception auslöst, so habe ich in etwa folgenden Test-Code geschrieben:

@Test
public void methodCallShouldThrowNumberFormatException() {
  try {
    objectUnterTest.methodUnderTest();
    fail("Should throw NumberFormatException");
  } catch ( NumberFormatException exc ) {
    // everything is fine
  }
}

Das ist zum einen etwas umständlich, zum anderen kann es je nach verwendeten Code-Analyse Einstellungen auch zu Warnungen führen, dass der Catch-Block nichts tut.

Schöner und deutlich einfacher ist die Verwendung des Attributes "expected" der @Test Annotation.

@Test ( expected = NumberFormatException.class )
public void methodCallShouldThrowNumberFormatException() {
  objectUnterTest.methodUnderTest();
}

Damit teile ich JUnit mit, dass ich bei der Durchführung dieses Tests erwarte dass eine NumberFormatException ausgelöst wird. Ist dies der Fall bewertet JUnit den Test als erfolgreich (grün). Fliegt keine oder eine andere Exception schlägt der Test fehl.

Als weiteres Attribut der @Test Annotation kann eine "timeout" Zeitspanne angegeben werden, nach welcher der Test mit einem Fehler abgebrochen wird. Sicherlich auch nicht falsch, das zu wissen.

Donnerstag, 14. April 2011

Android BallApp - Iteration 3 - Einlesen der Ballliste aus einer JSON-Datei

Ziel der dritten Iteration meiner Android BallApp:

Einlesen der vollständigen Ball-Liste in die SQLite Datenbank

Die vollständige Ball-Liste verwalte ich bisher in einer Excel-Datei.

Frage 1: Wie bekomme ich die Excel-Daten in die Android-Applikation?

Die Antwort ist einfach: Die Excel-Daten werden in eine ASCII-Datei exportiert und als Resource (in Android als Assets bezeichnet) ins Projekt kopiert.

Zur Speicherung der strukturierten Ball-Daten in einer ASCII-Datei wähle ich ein JSON-Datenformat. JSON erlaubt genau wie XML die Definition einer eigenen Datenstruktur, benötigt dafür aber deutlich weniger Platz, da die schließenden Tags wegfallen. Außerdem ist JSON ein gängiges Datei-Format und kann von Android gut verarbeitet werden (wie wir später noch sehen werden).

Die JSON-Daten lassen sich mit Excel gut generieren. Auf Details dazu will ich jetzt hier nicht näher eingehen, wen das interessiert, der möge sich bitte melden. Als kleiner Tipp: die Excel-Funktion VERKETTEN hilft.

Die ASCII-Datei ballliste.json sieht wie folgt aus:

[{hrst: "3D", name: "BOF BM 2007 Catherine Massem", gr: "k", 
         l: "l", s: "7", h: "", gw: "41"},
    {hrst: "3D", name: "BOF BMM 2008 GSP Malonne", gr: "k", 
        l: "l", s: "15", h: "", gw: "42"},
    {hrst: "3D", name: "BOF DkJM 2005 Oliver Rex Wiile", gr: "k", 
        l: "l", s: "1", h: "", gw: "40"},
    ...
    {hrst: "", name: "T4", gr: "k", 
        l: "r", s: "79", h: "", gw: "30"}]

Jedes Ball-Element besteht aus einer komma-separierten Liste von Name-Wert-Paaren und wird begrenzt von geschweiften Klammern. Die eckigen Klammern am Anfang und Ende definieren die Liste der einzelnen Ball-Elemente, ebenfalls komma-separiert.

Die JSON-Datei lege ich im Verzeichnis /assets in meinem Android-Projekt ab.

Frage 2: Wie lese ich die ASCII-Datei ein?

Das Einlesen von Dateien erfolgt in Android weitestgehend analog zu Java - genauso umständlich.

Den Zugriff auf die Asset-Datei erhalte ich über den Android Context, also über meine Activity. Über die Methode getAssets() bekomme ich den AssetManager. Dieser stellt mir wiederum die Methode open(String fileName) zur Verfügung, über die ich Asset-Dateien, also Dateien die im Verzeichnis /assets abgelegt sind, öffnen kann. Ich erhalte als Ergebnis einen InputStream.

Um die Datei einigermaßen komfortabel einlesen zu können, kapsele ich den InputStream in einen InputStreamReader und diesen wiederum in einen BufferedReader. So kann ich den Inhalt der Datei zeilenweise einlesen und in einem StringBuffer sammeln.

private String readJSONBallliste(Context context) {
      StringBuffer content = new StringBuffer();
      try {
        BufferedReader reader = new BufferedReader(
            new InputStreamReader(
                context.getAssets().open("ballliste.json")));
        String nextLine = reader.readLine();
        while (nextLine != null) {
          content.append(nextLine).append("\n");
          nextLine = reader.readLine();
        }
      } catch (IOException e) {
        Log.e(LOG_TAG, "Fehler beim Lesen der JSON-Datei", e);
      }
      return content.toString();
    }

Frage 3: Wie parse ich die JSON-Daten?

Das parsen der JSON-Daten ist in Android relativ einfach. Android bringt dafür die notwendigen Klassen im Package org.json bereits mit.

Bei meinem JSON-String handelt es sich um eine Liste von Objekten. Daher verwende ich ein JSONArray welches ich mit dem JSON-String initialisiere. Das JSONArray stellt die üblichen Methoden eines Arrays bzw. einer Liste bereit, die ich verwenden kann um durch die Liste zu iterieren. Leider implementiert das JSONArray kein Iterator oder Iterable Interface, so dass ich den altmodischen Weg gehen muss.

JSONArray jsonArray = new JSONArray(
        readJSONBallliste(context));
    for (int i = 0; i < jsonArray.length(); i++) {
      JSONObject nextBall = (JSONObject) 
          jsonArray.getJSONObject(i);
      Log.d(LOG_TAG, nextBall.getString("hrst") + 
          " - " + nextBall.getString("name"));
      ...
    }
Über die Methode getJSONObject(int index) lassen sich die einzelnen Ball-Elemente aus dem JSONArray auslesen. Das JSONObject stellt eine Reihe von Methoden zur Verfügung um zu einem Attribut-Namen den entsprechenden Wert abzufragen. Schreiben der JSON-Daten in die SQLite Datenbank Jetzt muss ich nur noch die Daten aus den JSONObjecten verarbeiten indem ich sie in die Datenbank schreibe. Die vollständige Methode dazu sieht wie folgt aus:
private void createData(SQLiteDatabase db) {
      Log.d(LOG_TAG, "Create Data...");
      String insert = "INSERT INTO " + TABLE_BAELLE + 
          " ( " + KEY_BALL_ID + ", " + KEY_BALL_FULL_NAME + 
          " ) VALUES ( ?, ? );";
      try {
        JSONArray jsonArray = new JSONArray(
            readJSONBallliste(context));
        for (int i = 0; i < jsonArray.length(); i++) {
          JSONObject nextBall = (JSONObject) 
              jsonArray.getJSONObject(i);
          Log.d(LOG_TAG, nextBall.getString("hrst") + 
              " - " + nextBall.getString("name"));
          db.execSQL(insert, new String[] { 
              String.valueOf(i), 
              nextBall.getString("name") });
        }
      } catch (JSONException e) {
        Log.e(LOG_TAG, "Fehler beim parsen der JSON-Daten", e);
      }
    }
Ich rufe die Methode beim initialen Anlegen der Datenbank auf, also in der onCreate(SQLiteDatabase db) Methode meines BallDbAdapters. Dem muss ich beim Initialisieren aus der Activity noch die Activity als Context mitgeben, damit ich Zugriff auf den AssetManager habe. Nach dem starten meiner App wird die Ball-Liste in die Datenbank geschrieben und die Liste mit den Ball-Namen wird angezeigt. Der vollständige Source Code kann wieder unter https://github.com/bobbyout/ballapp-android abgerufen werden.