Good Webservice Design

Was zeichnet eigentlich einen guten Webservice aus ? Hier eine Liste von Anforderungen, die ein guter Webservice erfüllen muss.

1. Versioniert
Webservices ändern sich. Im Idealfall kommen nur neue, optionale Attribute hinzu, ohne dass ältere Clients kaputt gehen. Bei umfangreicheren Umstrukturierungen ist dies aber nicht der Fall: Die neue Version ist nicht mehr rückwertskompatibel. Hier muss sowohl die alte und die neue Version des Webservice eine Zeit lang parallel betrieben werden. Von daher muss die Version in die URL des Webservice mit aufgenommen werden (z.B. http://api.stackexchange.com/2.0/tags?order=desc&sort=popular&site=stackoverflow).

2. Zustandslos
Ein Webservice darf keine Session-Ids oder gar Cookies einsetzen. Lesende Requests sollten unabhängig von einander ausgeführt werden können; eine bestimmte Reihenfolge darf nicht vorgegeben sein.

3. Verständlich auch ohne Dokumentation
Methodennamen und Parameter sollten nicht kryptisch sein. Beispiel: Die Bedeutung von totalHits ist verständlicher als die von th. Zwar werden bei der kürzeren Variante ein paar Bytes eingespart, aber auf Kosten der Verständlichkeit. Wie Bytes besser eingespart werden, zeigt der nächste Punkt.

4. Kompakt
Der Webserver sollte die GZIP-Komprimierung anbieten. Des weiteren sollte der Webservice auch JSON zurückgeben, um das Parsen für die Clients zu erleichtern und die Datenmenge zusätzlich zu reduzieren.

5. Im Browser testbar
Jeder Webservice muss eine Testseite zur Verfügung stellen, auf der alle Requests und Parameter ausprobiert werden können. Beispiele dafür:

Testseiten erleichtern das Erlernen des Webservices und ermöglichen den schnellen Test unabhängig von irgendwelchen Tools. Gute Testseiten können sogar eine separate Dokumentation ersetzen.

6. Auskunftsfreudig bei Fehlern
Es ist ziemlich frustrierend, wenn der Webservice bei syntaktisch falschen oder fehlenden Parametern einfach nur eine Standardfehlermeldung zurückgibt. Wichtig ist, dass der Webservice mitteilt, was genau schief gelaufen ist. Beispiel:

{
  "error" : {
    "name" : "missing_parameter",
    "description" : "parameter 'lang' is missing"
  }
}

Des Weiteren muss der Server bei einem Fehlerfall auch den HTTP-Code korrekt setzen. Bei clientseitigen Fehlern sollte der Server einen HTTP 400 (Bad Request) schicken, bei serverseitigen Fehlern dagegen einen HTTP 500 (Internal Server Error). Dies erleichtert das Cachingverhalten und die Fehlerbehandlung auf Seiten des Clients.

7. Konsistent
Parameternamen sollten über alle Methoden hinweg identisch benannt werden. Ansonsten muss der Client diese Inkonsistenzen abfangen, was die Client-Implementierung wieder aufwändiger macht.

Posted in Webapplikation Workout | 4 Comments

Pimp up your VIM – NERDTree

Heute bin ich auf die VIM-Erweiterung “NERDTree” gestossen. Diese Erweiterung platziert einen navigierbaren Verzeichnisbaum an die linke Seite des Editors. Zwar hat VIM einen eigenen Editor-Browser (Aufrufbar mit :E), aber mit NERDTree arbeitet es sich deutlich effizienter.

Die wichtigsten Shortcuts

  • j – Runter
  • k – Hoch
  • SHIFT-C – Wechsel in das aktuell selektierte Verzeichnis
  • u – Wechsel in das Parent-Verzeichnis.
  • m – Aktionsmenü (add, move, delete, copy)
  • ENTER – Editiern der ausgewählten Datei

Installation

Die Installation erfolgt sehr einfach über Pathogen-Bundle. Damit NERDTree automatisch gestartet wird, muss folgendes Autocommand in die ~/.vimrc eingetragen werden:

autocmd vimenter * NERDTree

Etwas unschön ist, dass beim Öffnen von vim nun immer das NERDTree-Window fokussiert ist. Um in das andere Window zu springen, muss man die Tastenkombination CTRL-W w drücken .

Dies lässt sich jedoch durch einen Eintrag in die ~/.vimrc automatisieren:

autocmd vimenter * wincmd w
Posted in IDE | Leave a comment

Spass mit Websphere 6 – Teil 1

Wie ich diese Woche feststellen durfte, implementiert der IBM Websphere 6.x (und sogar der Websphere 7.x) die Servlet Specification nicht richtig. Konkret geht es um das Verhalten von getServletPath() und getPathInfo() des HttpServletRequests.

Wie es sein muss:

Angenommen, wir haben eine Webapp unter dem Context mystore und mappen genau ein Servlet auf den Defaultpath “/”. Wenn wir folgende URL aufrufen: /mystore/search/de, dann sehen wir im Apache Tomcat folgendes:

getServletPath(): "/search/de"
getPathInfo(): null""

Editiert am 1.4.2012: Der Tomcat liefert einen leereren String, anstelle von null zurück.

Dieses Verhalten wird in der Servlet Api 2.4 unter dem Kapitel “Specification of Mappings” festgelegt:

A string containing only the ’/’ character indicates the “default” servlet of the application. In this case the servlet path is the request URI minus the context path and the path info is null.

Wie es der Websphere macht:

Der Websphere behandelt das Default-Mapping genauso wie ein Directory-Mapping also “/*”. Somit gibt er folgendes zurück:

getServletPath(): ""
getPathInfo(): "/search/de"

Das ist natürlich unglücklich, da man im Code nun eine Fallunterscheidung machen muss.

Spring hilft:

Die Methode getPathWithinServletMapping aus der UrlPathHelper-Klasse des Spring Frameworks schafft hier Abhilfe. Wenn getServletPath() null ist, liefert die Methode stattdessen die Antwort von getPathInfo() zurück. Wichtig: Dies funktioniert aber erst seit dem Spring Release 3.0.3.
Editiert am 1.4.2012: getPathWithinServletMapping() gibt natürlich nicht den ServletPath zurück, sondern nur die Pfadangabe hinter dem ServletPath. Von daher ist dies nicht die korrekte Lösung. Nachfolgend stelle ich stattdessen die Methode getPathWithinApplication vor, die mir in meinem Anwendungsfall die Fallunterscheidung zwischen Websphere und Tomcat erspart hat:

Die Methode getPathWithinApplication aus der UrlPathHelper-Klasse des Spring Frameworks liefert den restlichten Pfad nach dem Context, unabhängig ob er vom ServletPath oder PathInfo kommt.

Weitere Lektüre:

http://www-01.ibm.com/support/docview.wss?uid=swg1PK39337
https://issues.apache.org/jira/browse/TAPESTRY-1713

Posted in Webapplikation Workout | Leave a comment

Meine Terminal-Konfiguration

Hier eine kleine Liste von praktischen Konfigurationseinstellungen für die Arbeit mit dem Terminal unter Mac OS X.

1. “IR_Black”-Theme

Das wohl schönste Theme für das Mac Terminal ist “IR_Black”, zu finden unter http://blog.toddwerth.com/entries/13. Das einzige, was ich an diesem Theme noch nachträglich ändern musste, war der Cursor-Typ: Ein Blockcursor muss einfach sein.

2. Alias für ls

Folgende Optionen möchte ich bei jedem “ls” haben:

  • -G Ausgabe in Farbe
  • -F Fügt Suffixes hinter jedes ausgegebene Objekt. Bsp. ein Slash (/) für ein Verzeichnis
  • -h Gibt Dateigrössen in einer lesbareren Form zurück

Damit diese Optionen immer verwendet werden, setze ich ein alias auf ls in ~/.bash_profile.

alias ls='ls -GFh'

3. Syntax highlighting in VIM

Nächster Schritt: Ich möchte in VIM immer ein Syntax-Highlighting verwenden. Dazu lege ich die Datei ~/.vimrc mit folgenden Inhalt an:

syntax on

4. Bash completion für GIT

(Danke Johannes für diesen Tipp). Es gibt für Git ein “completion support”-Skript für Bash. So kann man kontextabhängig die zur Verfügung stehenden Git-Befehle mit Tab anzeigen lassen.

Erst lade ich das Bashskript von Github runter:

cd ~
curl -o .git-completion.bash https://raw.github.com/git/git/master/contrib/completion/git-completion.bash

Anschliessend braucht es noch folgenden zusätzlichen Eintrag in die ~/.bash_profile:

source .git-completion.bash

Fertig.

Posted in IDE | Leave a comment

HMLauncherView

Letzte Woche habe ich meine iOS-Komponente “HMLauncherView” auf Github hochgeladen. Die Komponente bietet eine Alternative zu der TTLauncherView aus dem Three20-Framework. Das Besondere ist, dass auch mehrere “LauncherViews” interagieren können. So können Buttons von einer Liste zur anderen verschoben werden (Siehe Video).

Ausbaufähig ist noch die Dokumentation, aber das kommt auch noch.

Link zu Github: https://github.com/heikomaass/HMLauncherView

Posted in iOS | Leave a comment

Erzeuge keine UIViews in der init-Methode eines UIViewControllers

Vor einiger Zeit hatte ich bei einem Code-Review folgende init-Methode eines UIViewControllers gesehen:

- (id) init {
	if (self = [super init]) {
		UIImage *image = [UIImage imageNamed:@"someimage.png"];
		UIImageView *imageView = [[UIImageView alloc] initWithImage:image];
		self.logoImageView = imageView;
		[imageView release];
	}
	return self;
}

- (void) dealloc {
   [logoImageView release];
   [super dealloc];
}

Was ist daran falsch ? Bevor ich die Frage beantworte, möchte ich den Lifecycle eines UIViewController-Objekts stark vereinfacht vorstellen:

Lifecycle

Der Lifecycle eines UIViewControllers umfasst folgende Methodenaufrufe:

  1. init
  2. loadView
  3. viewDidLoad
  4. (Optional) viewDidUnload
  5. dealloc

Die init-Methode (1.) wird nur 1x im Leben eines UIViewController-Objekts aufgerufen. Sobald das erste Mal auf das view-Property eines UIViewControllers zugegriffen wird, ruft UIKit die loadView-Methode (2.) auf, um die View für den Controller zu erzeugen. Anschliessend ruft UIKit die Methode viewDidLoad (3.) auf. Bei Speichermangel (Memory Warning) reagiert der UIViewController und führt die Methode viewDidUnload (4.) aus, sofern die View nicht gerade sichtbar ist. Zitat:

When a low-memory condition occurs and the current view controller’s views are not needed, the system may opt to remove those views from memory.
[...] If your view controller stores references to the view or its subviews, you should use this method to release those references
Dokumentation viewDidUnload

Wenn die View des Controllers durch viewDidUnload abgeräumt wurde, dann muss sie natürlich beim nächsten Zugriff wieder erstellt werden. In diesem Fall wird loadView und viewDidLoad erneut aufgerufen. Dieser Zyklus kann sich beliebig häufig wiederholen.

Keine UIViews in init erzeugen

So. Zurück zur Anfangsfrage: Warum sollte man keine UIViews in der init-Methode anlegen ? Der Grund ist einfach: Sie können so bei Speichermangel nicht abgeräumt werden. Wenn man beispielsweise eine UIImageView im init erzeugen und in viewDidUnload releasen würde, dann würde sie nie wieder neu erzeugt werden, da init ja nur 1x aufgerufen wird. Das Resultat sind fehlende Views bei Speichermangel.

Kurzum: UIViews sollen nie im init, sondern immer nur im loadView oder in viewDidLoad angelegt werden.

Posted in iOS | Comments Off

Das iOS-Framework “Three20″ in der Retrospektive

Vor fast zwei Jahren stand ich vor dem Problem, eine Schnellstart-Buttonleiste in eine iPhone-App einzubauen. Die Buttons in der Leiste sollten analog zum iPhone-Homescreen mittels Drag&Drop sortiert werden können.

Bei der Recherche nach einer bereits fertigen Open Source-Komponente bin ich auf das
Three20-Framework gestossen, dass damals von vielen Projekten eingesetzt wurde. Auf der Webseite von Three20 wird der eigene Launcher als wichtiges Feature angeteasert.

Three20 wurde von Joe Hewitt ursprünglich für die Facebook-iPhone-App entwickelt. Joe kommt aus der “Webwelt” und hat vorher den äusserst nützlichen Firebug ins Leben gerufen. Mit Three20 hat er versucht, Ideen aus der Webentwicklung in die iOS-Welt zu übertragen. Beispielsweise werden die UIViewController mit URLs versehen, die über einen eigenen Dispatcher (TTNavigator) aufgerufen werden. Zudem können UI-Elemente ähnlich wie CSS über einen Stylesheet (TTStyleSheet) angepasst werden.

Mir waren die (zugegeben interessanten) Ansätze jedoch viel zu invasiv.
Der TTNavigator und die Stylesheets verstehen sich quasi als “Gegen”-Framework zum UIKit,
und das kann auf Dauer nicht funktionieren. Ich hatte damals lediglich die TTLauncherView-Klasse in mein Projekt integriert.

Soweit so schön. Jetzt kommt jedoch der Haken der Story:

Bei jedem iOS-Update hatte ich Probleme mit Three20, da das Framework erst an Apple’s Änderungen angepasst werden musste. Auch beim Upgrade von XCode 3 auf XCode 4 kam es aufgrund des komplexen, intranparenten Buildsystems von Three20 zu Komplikationen. Joe Hewitt hatte mittlerweile Facebook verlassen, und der neue Projektverantwortliche musste dann einen Monat später leider folgendes Fazit über Three20 ziehen:

  • Poor documentation.
  • Spaghetti dependencies.
  • Suffering from a “kitchen sink” complex.
  • A complex build structure.
  • An enormous number of difficult-to-solve bugs.
  • Next-to-zero test coverage.

Quelle: https://github.com/jverkoey/nimbus (Absatz “Nimbus Background”)

Aus diesen Gründen gibts nun das Nachfolger-Framework “Nimbus” (https://github.com/jverkoey/nimbus). Leider wird dies auf der Three20-Projektwebseite nicht erwähnt. Von daher die Warnung auf dieser Seite: Don’t use it!

Vor knapp vier Monaten stand ich erneut vor dem “Problem”, eine Schnellstart-Buttonleiste in eine iOS-App zu integrieren. Diesmal habe ich es komplett selber programmiert.

Posted in iOS | 2 Comments

Und jetzt: Aus javablog.ch wird heikomaass.de

So: Die RewriteRules sind konfiguriert und die Name-Server bei der SWITCH ausgetauscht. Google ist dank der Webmaster Tools ebenfalls über den Umzug informiert. Jetzt kann ja nichts mehr schiefgehen ;)

Wie im Kommentar meines vorherigen Blogeintrag angekündigt, wird aus dem eher themenbezogenen JavaBlog nun ein persönliches Blog (Danke nochmals an Jürg für seine Anregung). Diese Entscheidung zieht natürlich einen Domain-Wechsel mit sich.

Alle alten URLs funktionieren weiterhin, sie werden über einen HTTP 301 auf die entsprechende Seite hier weitergeleitet.

(Im Übrigen danke noch an das Supportteam von All-Inkl, die sogar noch Sonntag Nacht sehr zügig antworten).

Posted in Uncategorized | 1 Comment

Und jetzt ?

Knapp ein Jahr habe ich auf diesem Blog nichts mehr geschrieben. Mit meiner zunehmenden Fokussierung auf das “Mobile”-Thema kann ich mich mit JavaBlog auch nicht mehr wirklich anfreunden, da ich in letzter Zeit viel in Objective-C programmiert habe.

Natürlich spielt Java für mich weiterhin eine große Rolle (Stichwort Android), aber es ist eben nur ein Ausschnitt aus meiner Tätigkeit.

Ob ich jetzt mein Blog umbenenne oder einfach ein Neues aufmache, weiss ich noch nicht. Aber das Bloggen möchte ich definitiv nicht aufgeben.

Posted in Uncategorized | 2 Comments

JDK7 Testfahrt – Teil 2

“Catching Multiple Exception Types”

Ein praktisches Feature, um doppelte catch-Blöcke zu vermeiden. Es ist nun möglich mit einem Catch-Block mehrere Exception-Typen gleichzeitig zu fangen. Die Exception-Typen werden dabei mit dem ODER-Operator (|) getrennt. Im Catch-Block kann die gefangene Exception unter gemeinsamen Supertyp angesprochen werden.

   static class AException extends Exception {
        public void fooFromA() {}
    }
    static class BException extends AException {
        public void fooFromB() {}
    }
    static class CException extends AException {
        public void fooFromC() {}
    }

    public void x() {
        try {
            z();
        } catch (BException | CException e) {
            e.fooFromA();
            // e.fooFromB(); Compiler-Fehler
            // e.fooFromC(); Compiler-Fehler
        }
    }

    public void z() throws BException, CException {
        // heavy lifting
    }

“Improved Checking for Rethrown Exceptions”

Des Weiteren “merkt” sich der Compiler nun, welche Exception-Typen im try-Block geworfen können. So kann im Catch-Block ein Supertyp gefangen und weiter geworfen werden, auch wenn dieser Supertyp gar nicht in der throws-Deklaration vorkommt.

 public void y() throws BException, CException {
        try {
            z();
        } catch (Throwable e) {
            // e darf ohne Cast weiter geworfen werden, da
            // der Compiler sich "gemerkt hat", dass im
            // try-Block nur BException und CException
            // vorkommen können.
            throw e;
        }
    }
Posted in Java | 2 Comments