Dieses Blog durchsuchen

Sonntag, 22. Oktober 2017

Autocomplete im IntelliJ

Diese Woche bekam ich eine interessante Mail vom JRebel Produkt-Manager Sander Sõnajalg. Darin wird beschrieben, wie man im Intellij autocompile nutzen kann. Dies war bisher meines Wissens nicht möglich, man musste entweder Strg-F9 (Projekt bauen) oder Strg-Shift-F9 (aktuelle Klasse compilieren) nutzen, um Änderungen wirksam werden zu lassen.

Das Autocompile Feature lässt sich wie folgt nutzen:

  1. Im Intellij die Einstellungen öffnen, Compiler in den Filter eingeben, "Build project automatically" anwählen (obwohl hier die Warnung steht, dass dies nicht beim laufenden oder im Debug-Modus befindlichen Projekt funktioniert)
  2. Strg-Schift-A drücken im Intellij, "Registry..." in den Filter tippen, auswählen und "compiler.automake.allow.when.app.running" Checkbox aktivieren

Mit diesen Einstellungen werden Änderungen am Quelltext sofort im laufenden Programm wirksam, ohne dass eine zusätzliche Eingabe erfolgen muss. Die bisherigen Tests sahen sehr gut aus, sodass ich mich weiterhin über das geniale, wenn auch relativ teure JRebel freue. (gibt es Leute, die nach Nutzung von JRebel noch ohne dieses Tool leben können?)

Freitag, 6. Januar 2017

REST Service Exception Handling


Bei der Implementierung von REST-Services stellt sich der Entwickler früher oder später die Frage, wie eigentlich mit Exceptions umgegangen werden soll. Denn anders als bei der Implementierung von SOAP Services muss sich der Entwickler hier eine eigene Strategie überlegen, wie aufgetretene Fehler dem nutzenden System übermittelt werden sollen.

An dieser Stelle möchte ich eine Variante aus der Praxis vorstellen, die sich bewährt hat. Und zwar wird dazu die Klasse javax.ws.rs.ext.ExceptionMapper implementiert und mit der @Provider Annotation versehen. Eine weitergehende Konfiguration oder Aktivierung des Mappers ist nicht notwendig. Indem nun die Methode toResponse überschrieben wird, kann definiert werden, wie die Antwort des Servers im Falle einer Exception konkret aussehen soll.

Das Beispiel zeigt den Aufbau einer Nachricht bestehend aus einleitendem Text "An Error occured!", dem aktuellen Datum, dem Namen der Exception-Klasse und der Nachricht. Falls die Exception einen root cause hat, wird auch dieser noch mit ausgegeben.
Zusätzlich ist es sinnvoll, einen passenden HTTP-Status-Code mitzugeben, in diesem Fall Status Code 500 für "Internal Server Error", denn viele Clients fragen diesen Status-Code ab, um entsprechend reagieren zu können.

@Provider
public class RestThrowableExceptionMapper implements ExceptionMapper<Throwable> {

    @Context
    private HttpHeaders headers;

    @Override
    public Response toResponse(Throwable throwable) {
        int status = Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(); //defaults to internal server error 500;
        StringBuilder messageBuilder = new StringBuilder();
        messageBuilder.append("An Error occured! ");
        messageBuilder.append(new Date());
        messageBuilder.append(": ");
        messageBuilder.append("Cause -> ");
        messageBuilder.append(throwable.getClass().getName());
        messageBuilder.append(": ");
        messageBuilder.append(throwable.getMessage());
        // also append root cause of exception if present
        Throwable rootCause = ExceptionUtils.getRootCause(throwable);
        if (rootCause != null) {
            messageBuilder.append("; Root Cause -> ");
            messageBuilder.append(rootCause.getClass().getName());
            messageBuilder.append(": ");
            messageBuilder.append(rootCause.getMessage());
        }
        return Response.status(status).
               entity(messageBuilder.toString()).
               type(headers.getMediaType()).build();
    }
}

Dienstag, 27. Dezember 2016

Eigener Authenticator für Basic Authentication in Kombination mit Proxy-Authentifizierung

Sofern ein REST-Client (oder auch SOAP-Client) sich zunächst über einen Proxy authentifizieren soll und anschließend eine Basic-Authentifizierung durchführen muss, bietet es sich an, einen eigenen Authenticator zu schreiben. 
Dieser Authenticator muss von der Klasse java.net.Authenticator ableiten und die Methode getPasswordAuthentication() überschreiben. Erwähnenswert ist nun, wie die implementierung dieser Methode konkret aussieht:

@Override
protected PasswordAuthentication getPasswordAuthentication() {
    String requestingHost = getRequestingHost();
    if (proxyEnabled && requestingHost.equals(proxyHost))    {
        return new PasswordAuthentication(proxyUserName, proxyPassword);
    }   else    {
        return new PasswordAuthentication(serverUserName, serverPassword);
    }
}

Zu sehen ist, wie mithilfe der geerbten Methode getRequestingHost()zunächst der Host ermittelt wird, der eine Authentifizierungsanfrage stellt. Sofern dies der Proxy ist, wird entsprechend der Nutzer und das Passwort des Proxys gesetzt. Handelt es sich jedoch um den Zielserver, werden die Credentials entsprechend für diesen verwendet.

Über die statische Methode Authenticator.setDefault(new MyAuthenticator()); wird der Authenticator schließlich JVM-weit gesetzt und beginnt mit der Arbeit, sobald die ersten Authentifizierungsanfragen eintreffen.

Mittwoch, 15. Juni 2016

REST Service Dokumentation mit Swagger


REST Services erfreuen sich seit einigen Jahren immer größerer Beliebtheit. Während SOAP Services als unflexibel und unnötig kompliziert gelten, sind REST Services schlank und bestehende Schnittstellen sind nicht so "empflindlich" gegenüber Änderungen. Z. B. lassen sich Services und Query-Parameter hinzufügen, ohne dass der Schnittstellenvertrag gebrochen wird.

Einen großen Nachteil haben REST Services jedoch gegenüber SOAP mit WSDL: die WSDL definiert die Schnittstelle und ermöglicht es einem Client so, das Interface sehr einfach mit wenigen Zusatzinformationen anzusprechen. Es gibt Tools die anhand einer WSDL Stubs (Client Klassen) erstellen können, sodass schnell klar wird, wie die Schnittstelle zu bedienen ist. Zwar gibt es auch bei REST vergleichbare Ansätze (siehe z. B. WADL), diese konnten sich jedoch in der Praxis nicht wirklich durchsetzen.

Umso wichtiger ist es daher, die REST Services umfassend zu dokumentieren und die Doku auch aktuell zu halten. Genau hier setzt Swagger an. Mit Swagger lassen sich die Services sehr komfortabel über Annotations beschreiben. Dadurch dass zur Erstellung der Doku Informationen direkt aus dem Quelltext zu Rate gezogen werden, ist stets Aktualität gewährleistet. Des weiteren bietet Swagger eine HTML Oberfläche, um die Doku zu präsentieren und die Services direkt auszuprobieren.

Im Folgenden werden die wenigen notwendigen Schritte beschrieben, um Swagger im eigenen Projekt nutzen zu können. (basierend auf einem JAX-RS und Maven Projekt)

Zunächst muss die Swagger-Lib als Dependency aufgenommen werden: 

<dependency>
    <groupId>io.swagger</groupId>
    <artifactId>swagger-jaxrs
    <version>1.5.9</version>
    <scope>compile</scope>
</dependency>

Anschließend muss die Rest- "Application" Klasse um folgenden Konstruktor ergänzt werden (falls JAX-RS verwendet wird): 

//Swagger initialization
public Application() {
    BeanConfig beanConfig = new BeanConfig();
    beanConfig.setVersion("1.0");
    beanConfig.setTitle("Meine REST services");
    beanConfig.setBasePath("/anwendung/service");
    //Hier befinden sich die Rest-Services
    beanConfig.setResourcePackage
    ("de.stahl.restservice");
    beanConfig.setScan(true);
}

Swagger UI wird benötigt, um die Dokumentation als Web-App bereitzustellen. Dazu muss die Web-Anwendung heruntergeladen werden (http://swagger.io/swagger-ui/) und der Inhalt des "dist" Ordners (swagger-ui/dist) in den Ordner "src/main/webapp" bereitgestellt werden. 

Damit die Dokumentation schließlich auch erstellt wird, müssen die Services noch mit den passenden Annotations versehen werden, die eine Beschreibung der Funktionalität beinhalten, zum Beispiel:


  •  Auf Klassenebene: 
@Api(value="Mein REST Service")
  •  Auf Methodenebene: 
@ApiOperation(value = "Beschreibung der API Operation") @ApiParam(value = "ein wichtiger Parameter", required = true) 


Diese Schritt reichen aus, um die Dokumentation zu erstellen. Detailliertere Informationen zu den verfügbaren Annotations gibt es hier: https://github.com/swagger-api/swagger-core/wiki/Annotations

Offline-Nutzung

Es könnte notwendig sein, eine Schnittstellendokumentation zu erstellen, obwohl der Quelltext noch gar nicht fertiggestellt wurde, z.B. wenn ein externe Komponente frühzeitig Informationen über eine sich in der Entwicklung befindliche Schnittstelle benötigt. Auch dies ist mit Swagger möglich.

Dazu wird zunächst die Swagger-Definition (JSON-File) erstellt. Dies kann mit Hilfe des Swagger Editors geschehen: http://editor.swagger.io/#/
Dann wird das resultierende JSON in die index.html der Swagger-Web-Anwendung eingefügt und das SwaggerUi-Objekt initialisiert: 

var spec = {"json":"test"};

window.swaggerUi = new SwaggerUi({
   url:url,
   spec: spec,
   ...
Da die Swagger Web-App ausschließlich auf HTML und Javascript basiert, kann diese anschließend, z.B. als Zip-Archiv and die potenziellen Clients verteilt werden.

JRebel Support

Als JRebel Fan möchte ich noch erwähnen, dass Swagger nun auch von JRebel wunderbar unterstützt wird. (siehe https://zeroturnaround.com/forums/topic/support-for-swagger/) Dies ist sehr hilfreich, um die Änderungen an der Dokumentation und den Annotations direkt in der Oberfläche nachvollziehen zu können.

Sonntag, 21. Juni 2015

Responsive Web Design

Anfang des Jahres bekam ich eine Nachricht von Google, dass meine Web Seite Probleme mit der mobilen Nutzerfreundlichkeit aufweist. Dies hat mich nicht sonderlich überrascht, da ich die Seite gar nicht für die Smartphone-Nutzung optimiert hatte. Da ich die Problematik aber sehr spannend finde und die mobile Nutzung von Web-Seiten auch beruflich immer mehr in den Vordergrund rückt, habe ich die Mail zum Anlass genommen, mich näher mit der Thematik zu befassen.

Also habe ich mir das Buch "The responsive web" meines Lieblingsverlags Manning bestellt (The responsive web) und mir das erste Kapitel durchgelesen.

Kurze Zusammenfassung:

Man sollte seine Web-Seite zunächst für die mobile Nutzung optimieren (leider zu spät für meine Seite) und dann Schritt für Schritt für größere Screens optimieren. Da meine Seite nun einmal schon da war, habe ich angefangen, sie für kleinere Auflösungen anzupassen. Das zentrale Element für das Design auf Basis von CSS sind die sogenannten Media queries und Breakpoints. Ich habe mich dazu entschlossen, die Seite sowohl für Screens ab 1200px zu optimieren, als auch für alles darunter bis zu einer minimalen Größe von 200px. Die zentrale Anweisung im CSS sieht so aus:

@media only screen and (min-width: 1200px) {
     div#Mainpage {
         width: 1024px;
     }
}

Dies bedeutet, dass das div-Element mit der ID "Mainpage" eine breite von 1024px haben soll; aber nur dann, wenn der Screen 1200px breit ist. (Bei den 1200px handelt es sich um einen sogenannten Breakpoint) Für alle Bildschirme unter 1200px soll die Web-Seite die komplette Bildschirmbreite einnehmen:

div#Mainpage {
    width: 100%;
}

Auf diese Weise nimmt die Seite auf kleineren Bildschirmen die größtmögliche Bildschirmfläche ein, während sie auf großen Wide-Screens zentriert auf 1024px begrenzt wird. Die Media queries sind sehr mächtig, denn nun ist es möglich, alle möglichen Designs auf die entsprechenden Bildschirmgrößen hin zu optimieren. So ist es z.B. auf einem Smartphone sinnvoll, die Navigation mit großen Links zu präsentieren, die nicht zu nahe zusammenstehen. Auch horizontales Scrollen sollte möglichst vermieden werden.

In nächster Zeit gibt es noch einiges zu tun, bis sich meine Web-Seite vollständig "responsive" verhält. Leider ist es nämlich so, dass die CSS Optimierungen viel Zeit kosten und ein perfektes Ergebnis extrem aufwändig ist. Daher ist es durchaus eine Überlegung wert, Content-Management Systeme wie Joomla einzusetzen, die diesen Mechanismus über Plugins bereits mitbringen - dies kann eine Menge Arbeit ersparen.

Abschließend möchte ich noch auf die Google Webmaster Tools hinweisen. Sie geben viele wertvolle Tipps und analysieren die Web-Seite auf Knopfdruck.
Im Hinblick auf die mobile Optimierung der Seite gibt es unter folgendem Link konkrete Hinweise und Lösungsvorschläge:
Mobile Usability 

Wer sich stärker auf die Performance der eigenen Web-Seite konzentrieren möchte, ist hier gut aufgehoben:
Pagespeed insight 

Die Tipps sind umfassend und wertvoll, sodass man sich eine teure, intellektuelle Analyse der eigenen Website sparen kann und sich sofort auf die Beseitigung der Problemfelder konzentrieren kann. Google vergibt einen Score für die Seite, bei 100 Punkten ist die Seite optimal implementiert.

Zulange sollte man die Thematik "Responsive Design" auf die lange Bank schieben, denn Google könnte nicht optimierte Seiten abstrafen. In der Hinweismail von Google klingt das so: "Diese Seiten werden von der Google-Suche als nicht für Mobilgeräte optimiert eingestuft, und werden entsprechend in den Suchergebnissen für Smartphone-Nutzer dargestellt."

 
 

Dienstag, 30. Dezember 2014

Debugging Ausgabe bei Richfaces erhöhen

In den letzten Monaten war ich mit der Migration einer Anwendung von Richfaces 3.3 auf Richfaces 4.5 beschäftigt. Diese Migration war dringend notwendig, da neuere Browser die alte Richfaces Version nicht mehr unterstützt haben (z. B. gingen keine Klappboxen mehr auf oder Ajax-Requests liefen ins Leere) Die Migration hat mich dann einiges an Nerven gekostet, vor allem deshalb, weil sich die Richfaces-Entwickler dazu entschlossen haben, sämtliche CSS-Benennungen zu ändern und auch viele Tag-Namen und Attribute zum Teil völlig unnötigerweise abzuändern. Dies führte dazu, dass nach Umstellung auf die neue Richfaces Version zunächst einmal nichts mehr funktionierte und auch das Design kaum noch wiederzuerkennen war.

Im Rahmen dieser Migration gab es auch immer wieder den Fall, dass eine in der Managed-Bean definierte Action-Methode nicht aufgerufen wurde. Hier war ich dann komplett ratlos, da es weder auf dem Server (JBoss-Log) noch auf dem Client (Java-Script Konsole) eine Fehlermeldung oder Warnung gab. Hier zeigt sich sehr schön der Nachteil einer komplexen Komponentenbibliothek: Es ist im Prinzip eine Black-Box und wenn die Standard-Komponenten nicht funktionieren, hat man erstmal Pech gehabt. Ein Kollege gab mir dann den Tipp, wie das Logging (sehr schön zu sehen in der Firebug-Konsole) auf Debug-Level erhöht werden kann, um Fehler leichter identifizieren zu können:

<a4j:log mode="console" level="DEBUG" />

Platziert man dieses Tag auf die XHTML-Seite, werden detaillierte Informationen in die Browser-Console gegeben, z. B. wie folgt:

RichFaces: New request added to queue. Queue requestGroupingId changed to Form:subviewTable:infoTable:6:checkbox
RichFaces: Queue will wait 0ms before submit
RichFaces: richfaces.queue: will submit request NOW
RichFaces: Received 'begin' event from <input id=form:subviewTable:infoTable:6:checkbox class=rowCheckbox ...>
POST http://localhost:8080/app/views/shortInfo/shortInfo.jsf
200 OK
RichFaces: Received 'beforedomupdate' event from <input id=form:subviewTable:InfoTable:6:checkbox class=rowCheckbox ...>
RichFaces: [object Object]
RichFaces: [object Object]
RichFaces: richfaces.queue: ajax submit successfull
RichFaces: richfaces.queue: Nothing to submit
RichFaces: Received 'success' event from <input id=form:subviewTable:infoTable:6:checkbox class=rowCheckbox ...>
RichFaces: Received 'complete' event from <input id=form:subviewTable:infoTable:6:checkbox class=eventRowCheckbox ...> 


Mit Hilfe dieser Ausgabe lässt sich besser erkennen, was RichFaces Java-Script-seitig tut und wo Probleme (z.B. beim nicht-Aufruf einer Action-Methode) herrühren können.
Weitere Informationen zum Thema Debugging gibt es hier:


https://github.com/richfaces/richfaces/wiki/Debugging-RichFaces

Freitag, 28. Februar 2014

Rückgabewerte bei einem JAX-WS Client-Aufruf werden nicht gesetzt

Die letzten Tage hat mich ein Problem mit Jax-WS sehr beschäftigt (=fast in den Wahnsinn getrieben)

Ich habe einen Jax-WS Client (Metro-Implementierung) geschrieben, der einen Web Service aufruft und Rückgabeparameter erwartet. Leider waren diese Rückgabewerte allesamt auf "Null" gesetzt. Ich hatte jedoch mit SoapUI die Soap-Antwort des Servers überprüft und habe gesehen, dass die Rückgabewerte korrekt übertragen werden. Auch mit Axis2 hat es wie gewünscht funktioniert.

Die Schwierigkeit war, dass es keinerlei Fehlermeldung oder Warnhinweis gab. Von daher half nur stundenlanges googeln und sich von Kollegen inspirieren zu lassen.

Nach und nach konnte ich das Problem auf JAXB eingrenzen. JAXB ist dafür verantwortlich, die SOAP Nachricht zu parsen und daraus Java Objekte zu machen und genau das ging ja offensichtlich schief.

Im Endeffekt hat sich dann (nach vielen weiteren Tests) herausgestellt, dass es ein Problem mit den Namensräumen gab. In de WSDL (Schema-Abschnitt) war folgendes definiert:

elementFormDefault="qualified"

In den Client-Stubs, die über wsimport erstellt wurden, war jedoch in der Datei package-info.java kein entsprechender Eintrag zu finden. Und dies bedeutet, dass der Default, nämlich unqualified verwendet wird. Das ganze lässt sich über folgende Annotation richtigstellen:

@javax.xml.bind.annotation.XmlSchema(namespace = "http://www.namespace.de",elementFormDefault = XmlNsForm.QUALIFIED)

-und schon wurden die Rückgabewerte wie erwartet gefüllt.

Ob dies nun ein Fehler im JAXB ist (was mich bei der Verbreitung von JAXB wundern würde) oder ob irgendein Detail in der WSDL bzw. in der Schema Datei fehlerhaft ist, kann ich nicht mit Sicherheit sagen.