Configuration

Eine elegante Möglichkeit flexibel verschiedene Konfigurationsvarianten zu kombinieren ist das Commons Configuration Framework. Es ermöglicht Configs aus Propertie-Files, DBs, JNDI, XMLs, ... zu kombinieren.

Eine Integration in Maven braucht:
<dependencies>
    <dependency>
        <groupId>commons-configuration</groupId>
        <artifactId>commons-configuration</artifactId>
        <version>1.10</version>
    </dependency>
</dependencies>

Dabei lassen sich verschiedene Config-Quellen auch in eine gemeinsame Struktur kombinieren, der man die Herkunft nicht mehr ansieht.
Ausserdem lassen sich Teilbäume ausgeben:

CompositeConfiguration cc = new CompositeConfiguration();
cc.addConfiguration(new SystemConfiguration()); // first
cc.addConfiguration(new PropertiesConfiguration("config2.properties")); // second
cc.addConfiguration(new PropertiesConfiguration("config1.properties")); // last
Iterator<String> keys = cc.getKeys("java.vm");
while(keys.hasNext()){
    String k = keys.next();
    System.out.println(k+" = "+cc.getProperty(k));
}

GIT - Einleitung

Nach über acht Jahren mit Subversion in internen und externen Projekten, hatte ich im letzten Jahr Gelegenheit Erfahrungen mit GIT zu sammeln. Während es auf den ersten Blick wie ein weiteres Sourcecode Verwaltungs-Tool aussieht, merkt man bald, dass es sich doch deutlich unterscheidet. 
Wer sich also mit Perforce, CVS oder Subversion auskennt, tut gut daran Bekanntes erst einmal zu vergessen.

Die Unterschiede sind im Wesentlichen:

GIT speichert nicht eine Liste von Veränderungen auf File-Basis, sondern sieht das verwaltete Verzeichnis (das mit dem .git-Verzeichnis) als Root eines Filesystems. Beim Commit wird ein Snapshot des gesamten Verzeichnisses gespeichert (wobei natürlich unveränderte Files nicht gespeichert werden).

Jeder Entwickler hat das komplette Repository lokal. Das Design von GIT braucht kein zentrales Repository. Die Suche in der History geschieht lokal, was es ungewohnt schnell macht. Ausserdem kann man überall (fast) ohne Einschränkungen am Projekt weiterarbeiten.
Im Firmen-Einsatz ist ein zentrales Repository unablässig, um nightly builds&co durchführen zu können. Hier wird üblicherweise ein stationäres Repository für diesen Zweck angelegt.

Ein Commit ist quasi zweistufig: Der erste Schritt findet lokal mittels commit statt. Der zweite Schritt ist ein push auf ein anderes Repository (z.B. das Zentrale der Firma/des Projekts), das System eines Kollegen oder github. Wobei man dazwischen noch Reihenfolge der lokalen Commits, ... noch ändern kann.
 

Initialer Setup

GIT kann kostenlos von http://git-scm.com/download heruntergeladen und dann installiert werden. Nach der Installation muss einmalig Benutzername und EMail-Adresse definiert werden. Dies wird bei den Commits bzw Pushs verwendet.

git config –global user.name „Vorname Nachname“
git config –global user.email „email@thetop.ch

Mit --system gibt es noch einen höheren Level für diese Konfiguration-Optionen, den man in der Praxis eigentlich nicht verwendet, da er zu weitreichend wirkt.
 

Erste Schritte

Um ein Verzeichnis von GIT verwalten zu lassen, muss im obersten betroffenen Verzeichnis folgendes eingegeben werden:

git init

Dies gilt kaskadierend für sämtliche enthaltenen Verzeichnisse, wobei - anders als bei Subversion - nur auf dem obersten Level ein .git-Verzeichnis angelegt, welches sämtliche GIT-spezifischen Informationen enthält.
Damit GIT die Dateien in den Verzeichnisse verwaltet werden müssen Dateien einzeln, über Wildcards oder verzeichnisweise hinzugefügt werden:

git add read.txt
git add *.java

In folgenden Blog-Einträgen sammle ich meine Erfahrungen zur internen Funktionsweise von GIT.

API Design

Ich möchte hier meine Erfahrungen und Erkenntnisse zum Thema API Design zusammenfassen. Dieses Thema hat soviele Facetten und Stolperfallen, die einem später manchmal sehr teuer zu stehen kommen können. Es ist wichtig, möglichst viel in die Überlegungen beim API-Bau einzubeziehen.

Was verstehe ich unter API?

Ein API ist ein Programmierschnittstelle für die Verwendung einer Komponente von "Aussen". Heute werden Applikationen in Module unterteilt und man definiert üblicherweise APIs bei einem Modul für die Kommunikation mit anderen Modulen. Aber auch innerhalb von Modulen finden sich viele API, die die einzelnen Applikationsteile von einander abtrennen ("teile und herrsche").

Wieso ist es wichtig ein gutes API zu bauen?

Dafür gibt es massenweise Gründe die verschiedene Personenkreise betreffen:

1. Entwickler in anderen Teams verwenden unser API und binden sich damit an unseren Vorschlag ein Problem zu lösen.

2. Entwickler im eigenen Team sind durch das API schlimmstenfalls an gewisse Strukturen und Lösungsansätze gebunden. Weiterentwicklung kann behindert werden, das Schreiben von Testklassen kann schwierig werden und die Innovationszyklen werden länger.

Dies hat natürlich direkten Einfluss auf die Attraktivität der Gesamtlösung. Je schneller und zuverlässiger Anpassungen gemacht werden können, umso besser ist das API.

Was macht ein API zu einem guten API?

Damit ein API gut ist, muss es folgende Kriterien erfüllen:

  1. es ist leicht zu verstehen und zu verwenden
  2. es ist konsistent
  3. es hat einen klar definierten Verwendungszweck
  4. es "zwingt" den Entwickler es richtig zu verwenden
  5. es versteckt Internas
  6. es ist gut dokumentiert

Wie stelle ich sicher, dass das API leicht zu verstehen ist?
Grundsätzlich macht es Sinn, sich wo möglich an APIs von JavaSE zu orientieren. Die kennt Jeder und wenn man sein API so strukturiert, wie es von Java bekannt ist, finden sich Entwickler schneller damit zurecht. Wichtig ist hier die Erkenntnis für wen es einfach zu verstehen sein soll. Kunde eines API ist immer der Entwickler, der mit dem API arbeiten wird. Ich muss mir also überlegen, was will er von diesem Modul, in welchem Kontext bewegt er sich und welche einzelnen Arbeitsschritte sind notwendig.

Da ich nicht immer einen solchen Entwickler zur Hand habe, muss ich mir selbst dessen Hut aufsetzen und den Code schreiben, der notwendig ist, mein API zu verwenden. Dieses Vorgehen führt quasi automatisch zu TDD (test driven design) bzw. BDD (behavior driven design). Ein weiterer wichtiger Aspekt sind die verwendeten Begriffe: wie heissen die Klassen, wie die Methoden und die Parameter? Eine präzise Wahl der Namen kann das API klar und verständlich machen. Wenn es schwierig ist einen Namen für etwas zu finden, dann ist üblicherweise konzeptionell der Wurm drin.

Da alle Klassen, Interfaces und Methoden im JDK englische Namen haben und sich Jeder an diese Namen gewöhnt hat, ist es absolut sinnvoll die eigene API auch komplett auf englisch zu entwickeln und zu benamsen. Wenn man unsicher ist, wie etwas auf englisch heisst, macht es sicher sein einen muttersprachlichen Entwickler zu fragen.

Was macht ein API Konsistenz und wieso ist das wichtig?
Der Programmierer, der mein API verwendet, tut dies um Zeit zu sparen. Er möchte sich auf seine Aufgabe konzentrieren und verwendet deshalb meinen Code, um nicht abgelenkt zu werden. Wenn sich nun mein API an unterschiedlichen Stellen unterschiedlich verhält oder ich verschiedene Namen für die gleichen Dinge verwende, verwirre ich den Kollegen und er wird wahrscheinlich aufgeben mein API zu verwenden.
Also: wichtig ist, dass alle Methoden, die mein API nach aussen zur Verfügung stellt eine ähnliche "Flughöhe" haben und, dass ich gleichen Dingen den gleichen Namen gebe. Ausserdem sollte ich darauf achten, dass ich möglichst wenig Ballast mitbringen, So ist es beispielsweise hinderlich, wenn ich eine lange Liste von Abhängigkeiten (JAR's und/oder AppServer mit spezifischen Versionen) voraussetze, damit mein API funktioniert. Dies schränkt die Einsatzmöglichkeiten deutlich ein.

Wieso ist es wichtig den Verwendungszweck klar zu definieren und daran festzuhalten?
Mit einem klar definierten Funktionsumfang, weiss der Entwickler, was er erwarten kann und was nicht. Es wird immer Wünsche für Erweiterungen und Generalisierungen geben. Diesen nur nachzugeben, wenn es ins Gesamtbild des API passt, ist Aufgabe des API-Leaders. Im Zweifelsfall sollte auf zusätzliche Funktionen verzichtet werden. Funktionen, die sich später als fälschlicherweise hinzugefügt herausstellen, können nicht mehr entfernt werden, weil irgendjemand sie braucht.

Wie zwinge ich den Entwickler mein API richtig zu verwenden?
Diese Frage generell zu beantworten ist kaum möglich. Üblicherweise führt TDD dazu, dass ein API klare Vorgaben über dessen Verwendung macht. Damit dies allerdings gelingt, braucht es viel Disziplin beim Schreiben der Tests. Wenn man beim TDD über Unzulänglichkeiten des APIs stolpert reicht Jammern nicht. TDD kann zu einem kompletten Umbau des API und gegebenenfalls der dahinterliegenden Strukturen führen. Da muss man durch!

Ein wichtiger Aspekt in diesem Zusammenhang ist "den Benutzer des API nicht zu überraschen". Damit meine ich, dass das API das macht, was erwartet wird und dass alle Parameter sinnvoll Initialwerte haben (Convention-over-Configuration).

Wieso soll ich Internas verstecken und wie mache ich das?
Bei den Internas, die versteckt werden sollen, handelt es sich häufig um Datenstrukturen. Beispielsweise wird intern eine HashMap oder eine LinkedList verwendet, um einen Datensammlung zu verwalten. Der Entwickler, der mein API verwendet, soll es nicht wissen. Seine Erwartungen wie die Daten aufgebaut sind, sind wahrscheinlich ganz anders als die bestgeeignetste Struktur, die ich intern verwenden möchte. Und genau das muss das Ziel sein. Es schadet nichts, die Daten von einem Collection-Typ in einen anderen umzukopieren, wenn man dafür die Freiheit hat in einer zukünftigen Version intern alles auf den Kopf zu stellen.

Es sollten keine Klassen, die Serializable implementieren im API verwendet werden, da durch Serializable sämtliche Attribute (auch die privaten) nach aussen weitergegeben werden.

Ein weiteres, klassisches Thema sind Exceptions. Wenn ich hinter meinem API eine SQL-basierte DB verwende, sollte ich dies nicht dem User mitteilen, indem ich ihm SQLExceptions entgegen werfe.

Ausserdem können auch Strings zu Problemen in diesem Zusammenhang führen. Wenn ich in einem String Daten in einem bestimmten Format aufbereite, dann schaffe ich hiermit eine gewisse Abhängigkeit, denn Benutzer meines API werden den String auslesen und parsen. Damit breche ich ihren Code, wenn ich meinen String anders formatiere.

Ein sehr wichtiges Thema in diesem Zusammenhang sind Verantwortlichkeiten. Wenn also mein Modul für die Richtigkeit und Vollständigkeit von gewissen Daten verantwortlich ist, dann gebe ich eine unveränderbare Liste dieser Daten zurück. Am Besten ist es anstatt Klassen Interfaces zu empfangen und zurück zu geben.

Wieso sollte ich eine Dokumentation schreiben?
Nicht alle Funktionalitäten eines API gehen aus dem Interface hervor: Wann werden welche Fehler ausgelöst? Was wird zurückgegeben, wenn keine Daten vorhanden sind (null / leere Liste / Exception)? Der Entwickler, der unser API verwendet, soll solche Dinge direkt aus dem API (bzw. dessen Dokumentation) erfahren. Das macht unser API attraktiv. Ausserdem ist es wichtig, dass gute Verwendungsbeispiele verfügbar sind. Oft werden diese per Copy-Paste von anderen Entwicklern übernommen und auf die eigenen Bedürfnisse angepasst.

SCJP & OCA/OCP Java Programmer

Die Übernahme von Sun durch Oracle schlägt sich nun in den Zertifizierungen nieder. Oracle bringt hier eine gewisse Umstrukturierung. Als Einstieg für die Zertifizierung mit Java hat Oracle den OCA (Oracle Certified Associate) definiert.

Bei dieser Zertifizierung werden Java Basics abgefragt. Hier stehen also der Java Befehlsatz und die Standard-Datentypen (inkl deren Wrapper) zur Verfügung. Ausserdem kommen Fragen zu Arrays und ArrayList, StringBuilder und Exceptions. Der überwiegende Teil der Fragen beinhaltet Sourcecode-Blöcke, die man verstehen muss. Hierbei können Fehler bei der Syntax, beim Scoping (Sichtbarkeit der Variablen) und beim korrekten Aufbau von Java-Konstrukten vorliegen. Ausserdem muss man von einzelnen Klassen, die wichtigsten Methoden (bspw. Methode zur Bestimmung der Länge bei ArrayList) und bei gewissen Methoden die möglichen Parameter kennen. Auch wenn sich hier viel mit Übung machen lässt, sind einzelne Fragen doch eher gesucht. Der Test dauert neu 150 Minuten und beinhaltet 90 Fragen. Im Vergleich zum SCJP ist dies ein etwas kleinerer Themenbereich mit mehr Fragen in weniger Zeit pro Frage. Ausserdem müssen nun 75% der Fragen korrekt beantwortet werden. Beim SCJP waren es noch 59%.

Seit Kurzem ist der "OCA SE 7 Java Programme I" freigegeben, nachdem er in einer zehn-wöchigen Testphase an Programmierern ausprobiert wurde. Nach meiner Erfahrung mit der Test-Zertifizerung, dem ursprünglichen SCJP und meiner Erfahrung mit Umschulungs- und Zertifizierungs-Vorbereitungs-Kursen in den letzten zehn Jahren ist der Test etwas schwieriger als der bisherige SCJP. Dies liegt vor allem an der Konzentration auf den Java-Kern und dem höheren Druck (mehr Fragen pro Minute, weniger Fehler).

JavaFX2.0: Control

Unter Control werden all die GUI-Komponenten verstanden, die Benutzereingaben entgegennehmen. Dabei decken die Controls ein breites Spektrum von Funktionalität ab. Alle Controls lassen sich umfassend über CSS konfigurieren. Da gibt es zum Einen Controls, die dem Benutzer erlauben Eingaben bzw. Selektionen zu machen: ChoiceBox, ListView, Slider, TableView, TextInputControl und TreeView.

Andere Controls dienen der reinen Anordnung und Darstellung von anderen Controls (nicht zu verwechseln mit Layout-Klassen!): Accordion, MenuBar, Separater, ScrollBar und ScrollPane, SplitPane, TabPane und ToolBar.

Dann gibt es noch ein paar spezielle Controls, wie z.B. die Super-Klasse Labeled, die für Cells, Buttons, Labels und den Titel bei TitledPane zuständig ist. In diese Kategorie fällt auch der ProgressIndicator.

Die erste Gruppe rund um TreeView und ChoiceBox, umfasst altbekannte Komponenten, wie sie jedes GUI-Framework kennt. Wer aus dem Swing-Umfeld kennt, kann sich an die vielen dedizierten Klassen, für alle möglichen Details erinnern. Dies wurde von Vielen als Over-Design gewertet und in JavaFX entrümpelt. Als Basis für alle JavaFX-Controls, die eine Sammlung von Optionen enthalten, dient eine ObservableList als Container. Sobald sich die, mit dem Control verknüpfte ObservableList ändert, wie dessen Inhalt entsprechend aktualisiert. Wichtig ist hier, dass man die ursprünglich hinterlegte ObservableList verwendet und nicht permanent neue Listen anlegt und zuweist.

Beim zweiten Block gibt es kaum Überraschungen. Einzig das Accordion ist vielen neu. Dabei handelt es sich um eine Gruppe von TitlePanes, die untereinander dargestellt werden und von denen der Benutzer immer nur eins aufgeklappt haben kann.

Bei der letzten Gruppe ist vor allem die Klasse Cell (Subklasse von Labeled) interessant. Diese Klasse ist - im Vergleich zu Swing - konzeptionell neu, denn sie stellt eine einheitliche Komponente für ein Element in einer ListView, einer TableView oder einer TreeView dar. Da eine Cell normalerweise Text enthält, gibt es dafür ein entsprechendes Attribut. Wenn man aber eine grafische Darstellung innerhalb einer Cell benötigt, lässt sich ein beliebiger Node anhängen.

JavaFX2.0: Parent, Group und Region

Diesmal möchte ich die Subklassen von Parent anschauen, die für das Management von Darstellung und Layout verwendet werden. Zuerst ein kurzer Ausflug hinter die Kulissen: JavaFX2.0 arbeitet mit einem sogenannten Pulse. Dabei handelt es sich um einen Takt des Grafik-Subsystem, der 60 Mal in der Sekunde prüft, ob sich am scene graph etwas geändert hat, ob Bereiche des GUIs neu gezeichnet werden müssen und so weiter. Änderungen an Instanzen, die im Scene Graph abgelegt sind, werden also umgehend umgesetzt. Auf den Pulse hat man keinen Einfluss. Mehr oder weniger Durchläufe pro Sekunde sind also nicht möglich. (Stichwort Games, die gerne mit FPS werben: 60 Vollbilder pro Sekunde sind aber absolut ausreichend, denn das menschliche Auge gibt schon viel früher auf).

Von Parent sind vier Klassen abgeleitet: Control, Group, Region und WebView. Control umfasst sämtliche grafischen GUI-Elemente, während WebView das macht, was der Name suggeriert. Zum diesen beiden in separatem Blog mehr...

Mich interessieren jetzt erst mal Group und Region. Eine Group enthält eine ObservableList mit GUI-Elemente, die der Reihe nach gezeichnet werden, wenn die Group gezeichnet wird. Dabei wird die Grösse einer Group durch die enthaltenen GUI-Elemente bestimmt. Dabei wird jeweils die preferred size herangezogen. Die automatische Grössenanpassung unterbindet Group. Die Group ist eher für rein grafische Dinge geeignet und weniger für Controls. Dies vor allem wegen dem fehlenden CSS Styling Support.

Eine Region kann durch CSS gestyled werden. Details zum CSS3-Background Support finden sich hier. Eine Region ändert nichts am Layout der enthaltenen Komponenten. Dieses wird von den Subklassen von Region übernommen: StackPane, HBox, VBox, TilePane, FlowPane, BorderPane, GridPane, or AnchorPane.