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.

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.

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

Caching mit OSCache Teil 3

Mit der Taglibrary von OSCache können einzelne Segmente innerhalb einer JSP gecached werden. Das “cache”-Tag führt den Tag-Body aus und cached anschliessend das Ergebnis (also den Output). Dabei kann der JSP-Entwickler entscheiden, wie lange und in welchem Scope (Application oder Session) der Body Content gecached werden soll.

Die Konfiguration des Algorithmus sowie die maximale Anzahl Objekte im Cache werden in einem property-File (oscache.properties) vorgenommen, welches in das WEB-INF/classes Verzeichnis platziert wird.

Das nachfolgende Beispiel cached zwei Datumsobjekte. Das erste wird für 5 Sekunden im Application Scope gecached. Das zweite für 10 Sekunden im Session Scope (jede Session bekommt also ein eigenes gecachetes Objekt).

  1. <html>
  2.     <head>
  3.         <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
  4.         <title>Cache Test</title>
  5.     </head>
  6.     <body>
  7.  
  8.     <h1>Cached Object 1</h1>
  9.         <cache:cache time="5" >
  10.             <jsp:useBean id="now" scope="page" class="java.util.Date"/>  
  11.             <c:out value="${now}"/>
  12.          </cache:cache>
  13.  
  14.         <-- now ist nur dann vorhanden, wenn der Body des obigen Cache Tag ausgewertet wird (beim ersten Mal und nach Ablauf der Cache Zeit) -->
  15.         <c:out value="${now}"/>    
  16.  
  17.     <h1>Cached Object 2</h1>
  18.         <cache:cache time="10" scope="session" >
  19.             <jsp:useBean id="nowInSession" scope="page" class="java.util.Date"/>  
  20.             <c:out value="${nowInSession}"/>
  21.          </cache:cache>
  22.  
  23.     </body>
  24. </html>

Die (sehr gute Dokumentation) der Taglibrary gibts unter
http://www.opensymphony.com/oscache/wiki/JSP%20Tags.html#JSPTags-cache

Caching mit OSCache Teil 2

Zentrales Element im OSCache sind die konkreten Implementierungen der AbstractCacheAdministrators Klasse. OSCache bietet zwei Implementierungen:

Die Funktionsweise eines CacheAdministrators ähnelt einer HashMap, geht aber weit darüber hinaus. So können Cache-Einträge ablaufen und müssen bei erneuten Zugriffsversuch erneuert werden. Auch die Anzahl von Cache-Einträgen soll limitiert werden. Mit der Methode setAlgorithmClass(String clazz) kann der Algorithmus konfiguriert werden.
Zur Verfügung stehen:

  • FIFOCache
  • LRUCache
  • UnlimitedCache

Beispiel:
Das nachfolgende Beispiel verwendetet einen FIFOCache mit max. 20 Cache-Einträgen. Unter dem Key “myKey” wird ein String in den Cache abgelegt und anschliessend 2x darauf zugegriffen. Beim zweiten Mal ist dieses Objekt “abgelaufen” und muss erneuert werden.

  1. GeneralCacheAdministrator cAdmin = new GeneralCacheAdministrator();
  2.         cAdmin.setAlgorithmClass("com.opensymphony.oscache.base.algorithm.FIFOCache");
  3.         cAdmin.setCacheCapacity(20);
  4.  
  5.         cAdmin.putInCache("myKey", "myObject");
  6.        
  7.         // first attempt to access the cache object
  8.         try {
  9.             System.out.println(cAdmin.getFromCache("myKey", 2));
  10.             // OK, object is not "older" than 2 seconds
  11.         } catch (NeedsRefreshException nre) {
  12.             System.out.println("expired object:=" + nre.getCacheContent());
  13.         }
  14.  
  15.         // Sleep 3 seconds
  16.         Thread.currentThread().sleep(3000);
  17.         try {
  18.             System.out.println(cAdmin.getFromCache("myKey", 2));
  19.             // NOT OK, object is definately older than 2 seconds
  20.             // --> NeedsRefreshException will be thrown
  21.         } catch (NeedsRefreshException nre) {
  22.             System.out.println("expired object:=" + nre.getCacheContent());
  23.         }
  24.        
  25.         cAdmin.destroy();

Ausgabe:
myObject
expired object:=myObject

Das Cachingframework bietet zusätzlich eine eigene JSP-Taglibrary und einen Servlet-Filter, der transparent für die Applikation ganze Seiten oder binäre Dateien cachen kann. Mehr dazu im 3. Teil.

Caching mit OSCache Teil 1

Caching spielt bei der Entwicklung von Webapplikation eine entscheidende Rolle. Nicht jedes Element auf einer Seite soll bei einem Request komplett neu generiert werden. Beispiel dafür sind RSS-Feeds, die in Form einer Newsliste auf einer Homepage dargestellt werden. Hier soll nicht bei jedem Request der RSS-Feed vom anderen Server abgeholt und neu geparst werden, da jedes Einlesen mit erheblichen Zeitaufwand verbunden ist.

Eine typische Lösung ist, den RSS-Feed nach dem ersten eintreffenden Request für eine Zeitspanne (z.B. für eine Stunde) im RAM zu halten. Jeder weitere Request innerhalb dieser Zeitspanne verwendet das gecachte Objekt. Nach Ablauf dieser Zeitspanne verfällt der Cacheeintrag und der nächste Request sorgt dafür, dass der RSS-Feed neu generiert wird und wieder in den Cache abgelegt wird.

Man kann sich so einen Cachingmechanismus mit Hilfe einer HashMap und einem Object-Wrapper selber bauen. Oder man spart sich die Arbeit und nutzt ein vorhandenes Caching-Framework wie OSCache von OpenSymphony.

OSCache bietet generische Caching-Klassen mit unterschiedlichen Cache-Algorithmen wie Least Recently Used oder FIFO. Die Cache-Einträge können zudem persistiert werden, damit sie einen Webserver Neustart überleben und nicht neu generiert werden müssen.

Mehr zum Einsatz dieser Cache-Bibliothek gibts im zweiten Teil.

JavaScript Verifier: JSLint

Manchmal darf man als Java-Entwickler auch JavaScript programmieren. Dabei stört mich der fehlende Compiler, der den Code auf Tippfehler oder versehentlich definierte globale Variablen überprüft. Abhilfe schafft JSLint, ein JavaScript Programm, das JavaScript validiert. Neben Fehlern werden auch Coderichtlinien wie Klammersetzung oder Variablennamen berücksichtigt.

Abschliessend noch ein Zitat von Douglas Crockford (Entwickler von JSLint):

JSLint will hurt your feelings

Yahoo! User Interface Library(YUI)

Mit dem Web 2.0 Hype kam eine Vielzahl von Javascript Frameworks. Neben der obligatorischen Unterstützung für asynchrone Requests bieten diese Frameworks auch zusätzliche Features wie Animation oder eigene UI-Controls. Diese Woche habe ich mir die Yahoo! User Interface Library (YUI) genauer angeschaut und bin bis jetzt begeistert.

Typischerweise zeichnen sich die bisherigen JavaScript Frameworks wie Dojo oder Rico durch eine eher dürftige Dokumentation aus. Dagegen ist die YUI Library von Yahoo vorbildlich dokumentiert. Besonders gefällt mir die hohe Anzahl an Code-Beispielen und die “Cheat Sheets”, die eine sehr schnelle Einarbeitung in die Library ermöglichen.

Beispiele:
Cheat Sheet für das “Animation” Modul
Dokumentation für das “Autocomplete” Modul

Ach ja: Die Library ist Open Source (unter der BSD Lizenz).