Spass mit Websphere – Teil 2: DEBUG-Logs

Ich hatte heute morgen wieder Spass mit einer Besonderheit des Websphere Application Servers 7: In dem standardmässigen SystemOut.log werden nie DEBUG-Messages ausgebeben.

Der Websphere Application Server 7 leitet ALLE Log4j-Logmessages unterhalb von INFO in das trace.log (IBM/WebSphere/AppServer/profiles/AppSrv01/logs/server1/trace.log) um. Man kann in der eigenen log4j.xml konfigurieren, was man möchte (z.B. eigene File-Appender), es wird alles in das trace.log umgeleitet.

Das trace.log kann über die Admin-Konsole zumindestens konfiguriert werden:

Websphere Trace.log Konfiguration

JSP-Tagfiles: Praktische Fragmente

JSP-Tagfiles ermöglichen die Wiederverwendung von Markup-Schnipseln. Sie können einfach mit Attributen parametrisiert werden. Bereits in früheren (JEE)-Projekten habe ich Tagfiles gerne eingesetzt. Was ich aber bis zum aktuellen Projekt noch nicht genutzt habe, waren die sog. Fragmente: Mit ihnen lassen sich mehrere JSP-Schnipsel gleichzeitig an ein Tagfile übergeben. Innerhalb des Tagfiles kann dann über <jsp:invoke> die interpretierte Ausgabe des Fragments eingebettet werden. Das nachfolgende Beispiel zeigt ein vereinfachtes Grid-Tag, das zwei Fragmente (main und sidebar) anbietet.

/WEB-INF/tags/grid.tag

<%@ tag description="The basic two column grid" pageEncoding="utf-8"
%><%@ attribute name="main" fragment="true"
%><%@ attribute name="sidebar" fragment="true"  
%>
<section>
   <div class="size3of4">
      <jsp:invoke fragment="main"/>
   </div>
   <div>
      <jsp:invoke fragment="sidebar"/>
   </div>
</section>

Aufruf des Tagfiles mit Übergabe von Fragmenten
Die zu übergebenden Fragmente werden durch <jsp:attribute/>-Blöcke definiert.

<%@ page contentType="text/html; utf-8"  pageEncoding="utf-8"
%><%@ taglib prefix="t" tagdir="/WEB-INF/tags"
%><%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"
%><%@ taglib prefix="fmt" uri="http://java.sun.com/jstl/fmt"
%>
<t:grid>
   <jsp:attribute name="main">
      <h2><fmt:message key="resultlists"/></h2>
      <%-- further content for main --%>				
   </jsp:attribute>
   <jsp:attribute name="sidebar">
      <h2><fmt:message key="addons"/></h2>
      <%-- further content for addons --%>
   </jsp:attribute>
</t:grid>

Standardmässig werden die übergebenen Fragmente beim Aufruf mit <jsp:invoke> direkt in die Response geschrieben. Alternativ kann der Output des Fragments in eine Variable gespeichert werden. Dazu wird muss das var-Attribut von <jsp:invoke> gesetzt werden.

Gradle direkt von Eclipse debuggen

Momentan stellen wir ein Maven-basiertes Java-Projekt auf das Buildsystem Gradle um. Wichtig dabei ist, dass von Maven bekannte Komfortfunktionen weiterhin genutzt werden können: Dazu gehört auch das schnelle Starten eines lokalen Tomcats ohne vorherige manuelle Installation. Analog zum maven-tomcat-plugin gibt es für Gradle das gradle-tomcat-plugin. Sollte man (trotz hoher Testabdeckung) in die Verlegenheit kommen, einen Debugger verwenden zu müssen, gibt es zwei Möglichkeiten:

  1. Den Debugger an die JVM anhängen, die über den Aufruf von “gradle” auf der Konsole gestartet wurde.
  2. Die JVM mit Gradle direkt aus Eclipse zu starten.

Lösung 2 ist definitiv vorzuziehen, da der Debugger wesentlich performanter agiert. Aber wie starte ich Gradle in einer eigenen JVM direkt aus Eclipse ? Ganz einfach: In dem Jar gradle-launcher-1.2.jar befindet sich die Main-Klasse org.gradle.launcher.GradleMain.

Diese Klasse können wir in Form einer eigenen Launch-Konfiguration verwenden:
Main-Klasse für Eclipse Launch-Konfiguration

Die gewünschten Gradle tasks übergeben wir in den Program arguments:
Liste mit gradle tasks

Bevor wir starten, müssen wir noch zwei JARs zum Classpath hinzufügen:

  • gradle-launcher-1.2.jar
  • gradle-core-1.2.jar

Classpath-Konfiguration

Nach Klicken auf Run können wir die Ausgabe von Gradle in einer Eclipse Console betrachten, und mit dem Debugging beginnen.
Ausgabe von Gradle

Nice.

Apache Tomcat’s URIEncoding

Bei einem Refactoring einer Webapplikation hatte ich ein HTML-Formular von HTTP-POST auf GET umgestellt, da eine idempotente Suchanfrage ausgelöst wird. Allerdings traten bei Suchbegriffen mit Umlauten unschöne Encoding-Fehler auf:

Kaputte Umlaute durch fehlerhaftes Encoding

“Endlich mal wieder ein Encoding-Problem” denke ich, und beginne mit einem UTF-8-Schnellcheck:

UTF-8-Schnellcheck

  • Content-Type HTTP-Header korrekt gesetzt ? Ja.
    Content-Type: text/html; utf-8;charset=utf-8
    
  • Meta-Tag gesetzt ? Ja.
    <meta content="text/html; charset=utf-8" http-equiv="content-type">
    
  • Default-Encoding der JVM ?
    Ausgabe von Charset.defaultCharset() ? Ist UTF-8.

Alles schien in Ordnung zu sein, aber warum wurde der Suchbegriff falsch encodiert ? Bei der Analyse mit dem Debugger zeigte sich, dass bereits der CGI-Parameter vom Tomcat falsch encodiert wurde.

Die Ursache: URIEncoding-Attribut des Apache Tomcats
Wenn im Connector das URIEncoding-Attribut nicht gesetzt wird, dann verwendet der Tomcat eben das ISO-8859-1 Encoding. Normalerweise würde man jetzt die server.xml anpassen. Allerdings starten wir unsere Entwicklungsinstanzen mit dem maven-tomcat-plugin. In diesem Fall trägt man in der pom.xml einfach folgendes Attribut ein:

<maven.tomcat.uriEncoding>UTF-8</maven.tomcat.uriEncoding>

Fertig. Und schon wieder ein Encoding-Problem weniger.

XCode – Nachträgliche Konfiguration von Application Tests

Beim Anlegen eines neuen Projektes kann XCode 4.3 auf Wunsch ein Unit-Test Bundle erzeugen. Die Option dazu heisst Include Unit Tests. Die so angelegten Unit-Tests werden in der Menüleiste unter Product > Test oder alternativ über den Shortcut cmd-U angestossen. XCode startet die zu testende App im Simulator und führt die Unit-Tests aus. Apple nennt diese Form der Tests Application Tests; ich würde sie eher Integration Tests nennen. Um eine ältere App nachträglich mit Application Tests zu versehen, müssen einige manuelle Schritte ausgeführt werden. Als Beispiel soll eine App namens AppWithoutTests dienen, die ich ohne die Include Unit Tests-Option angelegt habe.

1. Erzeugen eines Cocoa Touch Unit Testing Bundles

  • Klick auf File > New > Target ...
  • Auswahl des Cocoa Touch Unit Testing Bundle
  • Eingabe eines Product Names (z.B. Application Tests) und Klick auf Finish

2. Aufräumen der Schemes
XCode hat nun das Testing Bundle erzeugt, jedoch hat es ein weiteres Scheme angelegt. Für die Entwicklung ist das umständlich, da man für die Ausführung der Tests immer das Scheme wechseln muss. Von daher löschen wir das neu angelegte Scheme und erweitern das bisherige Standard-Scheme.

  • Klick auf Product > Manage Schemes ...
  • Löschen des automatisch angelegten Scheme ApplicationTests
  • Hinzufügen des ApplicationTests Bundles im Test-Target des Standard-Schemes (im Beispiel AppWithoutTests).

3. Testing Bundle: Bundle Loader und Testhost
Noch sind wir nicht fertig. Wir müssen die Wirts-Applikation in den Bundle Settings unseres Testing Bundles selbst konfigurieren.

  • Zunächst setzen wir den Pfad des Bundle Loader auf die unsere kompilierte App, also $(BUILT_PRODUCTS_DIR)/<app_name>.app/<app_name>. Für unsere Beispielapp müssen wir den Pfad auf $(BUILT_PRODUCTS_DIR)/AppWithoutTests.app/AppWithoutTests setzen.
  • Anschliessend setzen wir den Test Host auf den soeben konfigurierten Bundle Loaders. Dazu geben verwenden wir die Variable $(BUNDLE_LOADER).

4. Testing Bundle: Setzen der Target Depedencies
Die Abhängigkeit unsers Testing Bundles zu dem Application Target müssen wir selbst hinzufügen, so dass sichergestellt ist, dass immer zuerst die App, und dann die Tests kompiliert werden.

  • Klick auf die Build Phases unseres Testing Bundles
  • Klick auf Plus-Symbol in der Sektion Target Dependencies
  • Auswahl des Application Targets (Im Beispiel AppWithoutTests).

5. Application Target: Symbols hidden by default abschalten
Die zu testende Applikation darf die kompilierten Methoden und Klassen nicht verstecken. Ansonsten scheitert der Linker im Testing Bundle, weil er die importierten Klassen nicht findet.

  • Dazu müssen wir sicherstellen, dass die Option Symbols Hidden by Default in unserem Application Target AppWithoutTests” auf NO gesetzt ist.

Mit diesen (leider sehr umständlichen) Schritten können nun Application Tests für unsere AppWithoutTests ausgeführt werden. Im nächsten Blogeintrag geht es um die so genannten “Logic Tests”, also Tests, die keine Wirts-Applikation benötigen.

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.

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

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

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.