Archiv für die Kategorie ‘Softwaredesign’

Quelle aller Worte: Internationalisierung mit GWT UIBinder

Samstag, 10. September 2011

Als Gott sah was beim Turmbau zu Babel passierte, entschied er sich die Sprache der Menschen zu teilen, so dass der Eine den Anderen nicht mehr verstehen möge. Ähnlich scheinen sich die Designer von GWT gefühlt haben, als sie die Internationalisierung von GWT 2.x entworfen haben.

Das Internationalisierungsprinzip von GWT ist an sich sehr einfach. Man nehme ein paar properties-Dateien, inkludiere das I18N-Modul und lasse sich die entsprechende Java-Datei generieren. Soweit so gut. Leider nicht so, wenn man den UIBinder verwenden möchte. Hier haben sich bereits diverse verzweifelte Entwickler einen “abgebrochen” um herauszufinden, welche Dateien an welcher Stelle liegen müssen, damit man in den jeweiligen ui.xml-Dateien eine vernünftige Übersetzung erhält.

Grundsätzlich gilt, dass Übersetzungs-Keys aus ui.xml-Dateien immer im Package com.google.gwt.i18n.client mit dem Namen LocalizableResource_[locale].properties liegen müssen. Diesen Umstand verdanken wir dem MessagesWriter, der vom UiBinderGenerator herangezogen wird. Dieser generiert zwar die Messages-Dateien für alle Keys, die sich in einer ui.xml-Datei befinden in eine eigene Messages-Klasse, importiert aber mittels

writer.write("import static com.google.gwt.i18n.client.LocalizableResource.*;");

ausschließlich die Übersetzungen aus LocalizableResource. Soweit so gut, möchte man meinen. Problematisch wird es dann, wenn man Übersetzungen sowohl im Java-Code, als auch im in den ui-Dateien haben will. Typisches Beispiel sind hier Tabellen vom Typ CellTable, deren Spalten normalerweise in den View-Klassen definiert werden. Will man hier nun sprachlich angepasste Spaltenköpfe haben, muss man diese mittels einer Messages-Klasse übersetzen.
In diesem Moment steht man vor der Frage: Möchte ich zwei Übersetzungsdateien verwalten, in denen ggf. Keys doppelt auftreten, oder möchte ich nur Eine für beide Mechanismen haben?

Ohne auf die unsäglichen und teilweise unpraktikablen Varianten einzugehen, die sich im Netz bereits befinden bzw. die kryptisch auf der GWT-Dokumentation vorgestellt wurde, stelle ich hier meine zwei Lösungen vor, die ich für sinnvoll erachte.

Variante 1: ui:with

Die klassische Variante. Man nehme einfach die existierenden Messages und importiere sie wie folgt in die ui.xml-Datei.

 <ui:with field='messages' type='com.my.app.MyMessages'/>
 <g:Label text="{messages.hello}" />

Diese Variante hat den Vorteil, dass man auf die komplette Bandbreite der Erweiterungen, die für Messages-Klassen bereitgestellt wurde zugreifen kann.

Variante 2: Customized Messages-Interface

In Variante 1 nutzen wir den Mechanismus, der eigentlich für den Java-Code gedacht war für den UiBinder. Natürlich geht es auch anders herum. Wir “kopieren” einfach den Mechanismus der MessagesWriter-Klasse. Wir bauen uns ein Interface und importieren die LocalizableResources.

package com.mypackage;

import static com.google.gwt.i18n.client.LocalizableResource.*;

public interface MyAppMessages extends Messages {
  @DefaultMessage("Example")
  String example();
}

Der GWT-Compiler greift hier einfach das Interface und verknüpft die Methoden mit den Keys aus der jeweiligen LocalizableResource-Datei. Damit lassen sich auch innerhalb von Java-Dateien die Keys aus LocalizableResource verwenden.

Achtung:
Leider gibt es nicht die Möglichkeit LocalizableResource-Dateien über den i18n-Aufruf in eine Klasse übersetzen zu lassen, da LocalizableResource bereits ein existierendes Interface ist und die Übersetzung zu einer sogenannten zyklischen Vererbung führt.

Fazit

Mit beiden Varianten lässt sich das bestehende Problem lösen, dass man nur einen Mechanismus zur Internationalisierung innerhalb von GWT-Projekten nutzen möchte. Welche der beiden zum Einsatz kommt hängt stark von der bestehenden Struktur und – was nicht zur vernachlässigen ist – von der Motivation des Entwicklers, welche Schritte für ihn in seinem Arbeitsprozess besser integriert werden können, ab. Während Variante 1 den Aufruf des i18n-Creators erfordert, muss in Variante 2 das Interface immer manuell angepasst werden.

Trügerische Sicherheit: Fehlermeldungen ohne Kontextinformationen

Donnerstag, 30. Juni 2011

Die Applikation. Unendliche Weiten. … *räusper*

So schlimm ist die Welt der Software-Entwicklung nun zwar auch nicht, dass man blindlinks irgendwo im Dunkeln rumdüst und völlig überraschend in einer Raumzeit-Anomalie hängt oder von Q für die Verbrechen der Menschheit angeklagt wird.
Aber gerade bei neuen Software-System hat man viele Stellen noch nicht berührt und muss sich häufig auf bereits existierendes Verhalten verlassen, wenn man neue Komponenten integriert. So geschehen heute. Ich möchte kurz vorstellen – die Aufgabe:

Rufe via REST eine URL auf, die den Status einer Entity umsetzt.

Zusätzliche Herausforderung:

Kann sein, dass die Gegenstelle nicht da ist.

Es folgte eine Odyssee von URL-Anpassungen. So mach ich mich daran, den Aufruf zu implementieren und bekomme ein einziges Wort zurück “StatusNotAllowedException“. Ich denk mir so: “Ok, das hört sich doch gut an.”, da ich wusste, dass die Status-Änderung mit dem aktuellen Objekt wirklich nicht erlaubt bzw. sein sollte. Witzigerweise war aber das Problem, dass ich einen völlig falschen Call getriggert hatte – klassisch Copy&Paste. Erst nachdem ich verkündete, dass der Aufruf implementiert sei, wurde ich aufgeklärt, dass die Antwort nicht korrekt ist. Nach kurzem scharfen Hinsehen war die URL dann auch schnell ausgetauscht.

Also ging es auf zu Runde 2. Wie bereits angesprochen gab es ja noch die Nebeninfo, dass evtl. die Empfängerstelle noch nicht implementiert ist. Darum gab es auch hier wieder für mich keinen wirklichen Argwohn als die Schnittstelle eine 404 zurücklieferte. Vorgewärmt von der ersten Fehlermeldung hab ich gleich gefragt, ob die Empfängerseite wirklich nicht da sei, worauf ein “doch, ist da” zurückgeliefert wurde.
Also schauen wir uns die URL an und entdecken: Oha, die Ports sind falsch. Aber nicht nur bei einer URL, sondern bei allen. Auch das geändert und somit kommen wir zu …

… Runde 3: Fehlermeldungen, die nicht da auftauchen wo sie erwartet werden. Leider gab es dann noch einen Schusselfehler bei der Änderung der Ports, so dass ein Aufruf an den alten Port geschickt wurde und mit unverhohlener Freundlichkeit meinte “kenn ich nicht”. In meinem Gesicht manifestierte sich die Frage “was kennst du nicht?”. In meinem Log tauchte nichts auf, im Log auf dem Server, auf dem wir dachten, dass der Aufruf aufschlagen sollte kam auch nichts. Zwar lag das eigentliche Problem am falschen Port, aber die Fehlermeldung sagte nichts darüber aus, was gerade schief gelaufen ist. Auch im Log des eigentlichen Servers – da wo der Call gelandet war – stand nichts aussagekräftiges. Am Ende half nur das 4-Augen-Prinzip um den Fehler zu finden.

Fazit: Ein Hoch auf die verbale Kommunikation und ein leidiges Buh auf schlecht formulierte Fehlermeldungen. Zwar muss man schon einiges an Schusseligkeit an den Tag legen um so viele Fehler hintereinander zu machen – und es passiert doch – aber gerade beim Ersten hätte es beinahe dazu geführt, dass ich mich in einer Sicherheit wähnte, die gar nicht da war und die uns im Produktiv-System ggf. teuer hätte zu stehen kommen können. Darum sollten Fehlermeldungen stets so viel Informationen enthalten, dass sie das eigentliche Problem konkret darstellen und entweder mögliche Ursachen oder mögliche Lösungen vorschlagen.

Clean Code und Refactoring – Das riecht aber komisch

Mittwoch, 08. Dezember 2010

Wer sich schonmal über einen Quellcode gesetzt hat – über den eigenen oder eines anderen Entwicklers – und sich nach kurzer Zeit gefühlt hat, er müsse mal kurz weggehen um frische Luft zu bekommen, der hat es wohl mit einem sogenannten “Code smell” zu tun.

“Code smell” ist ein Begriff, dem man – je länger man in der Softwareentwicklung unterwegs ist – mittlerweile kaum noch entgehen kann. Sei es nun im eigenen Projekt oder in der aktuellen Fachpresse. Für jene, denen dieser Begriff noch nicht geläufig ist, sei er wie folgt kurz erklärt:

Ein “Code smell” ist ein Symptom für tieferliegende Programm-Probleme, das sich durch unschönen bzw. verwirrenden Quellcode zeigt.

Eine konkretere Herleitung findet sich u.a. auf der entsprechenden Wikipedia-Seite. Wie die obige Erklärung bereits andeutet sind Code smells eine relativ subjektive Angelegenheit. Wann ist eine Methode zu lang, oder was sind angemessene Variablenbezeichner? Eine Antwort auf diese Fragen muss man am Ende vor allem für sich selbst finden.
Was ist jedoch zu tun, wenn man über solchen Code stolpert? Wie schreibe ich z.B. eine Methodensignatur so um, dass ich sicher sein kann, dass mein Code immer noch wie vorher funktioniert? Hierfür möchte ich 3 Bücher vorstellen, die man getrost als Leitfaden für Erkennung, Vermeidung und Aufräumaktionen verwenden kann.

Clean Code stellt viele Aspekte für unschönen Code vor, die man bei der täglichen Arbeit zwar bemerkt, aber bei denen man unter Umständen nicht immer weiss wie man sie besser machen kann. Der Autor gibt sich hierbei nicht den Anspruch des allumfassenden “So-und-nicht-anders”, sondern lässt Freiraum für eigene Entscheidungen.
Martin Fowler stellt verschiedene Refactoring-Mechanismen vor und erklärt die Motivation, die hinter jedem einzelnen steckt. Da es sich hierbei u.a. auch um sehr triviale Refactorings handelt, wirkt das Buch zeitweise etwas trocken.
Dies wird jedoch durch die Darstellung verschiedener “Bad smells in Code” teilweise wieder ausgeglichen.
Ähnlich aufgebaut wie das “Refactoring”-Buch werden hier verschiedene Code-Probleme anhand einfacher Beispiele erklärt und deren Lösung durch Patterns oder in Richtung eines Patterns vorgestellt.

Wer sich etwas konkreter mit Refactoring an sich beschäftigten will, seien diese Bücher ans Herz gelegt. Wer meint, er schreibe bereits guten Code, möge folgenden Auszug aus “Clean Code” selbst prüfen: Guten Code erkennt man an der Metrik der WTF/minute eines Code Reviewers.