Java SE Embedded Compact Profiles

Hat ein System beschränkten Speicher, wird es knapp für das gesamte Java SE. Daher gibt es Teilmengen der Java SE, genannt Profiles, die nur knapp über 11 MiB benötigen. Im Moment gibt es drei Profile, also drei Teilmengen der Java SE, die jeweils immer etwas mehr können. compact1 ist die kleinste Teilmenge, wo zum Beispiel die Kernklassen enthalten sind, compact2 nimmt die API für Datenbankverbindungen mit auf, compact3 kann dann XML-Verarbeitung und die komplette Java SE grafische Oberflächen darstellen oder Web-Services deklarieren.

Jede Klasse ist einem Profil zugeordnet, was der Compiler statisch prüfen kann – solange der Klassenname nicht dynamisch bestimmt wird. So lässt sich sicherstellen, dass nur Typen referenziert werden, die auch in einem Profil sind.

Oracle Java Standard Edition Embedded (Oracle Java SE Embedded)

Oracle unterstützt mit der Oracle Java Standard Edition Embedded (Oracle Java SE Embedded)[1] Profile. Allerdings laden Entwickler keine einfache Laufzeiumgebung herunter, sondern ein Werkzeug JRECreate, mit dem eine Laufzeitumgebung mit unseren eigenen Paketen für ein bestimmtes System generiert wird. Drei Laufzeitumgebungen sind wählbar: eine Minimal-JVM, Client JVM (schnelle Reaktionsfähigkeit), Server JVM (verbraucht mehr Hauptspeicher). Sehr fein kann dann bestimmt werden, ob JavaFX mit dabei sein soll, oder welche Sprachübersetzungen mit ausgeliefert werden. Am Ende steht ein Verzeichnis mit bin und JRE, lib und Java-Archiven, Konfigurationen und – das darf bei Oracle auf keinen Fall fehlen – Copyright- und Lizenz-Dokumenten.

[1]      http://www.oracle.com/technetwork/java/embedded/embedded-se/downloads/index.html

Eclipse 4.6 (Neon) News

Alle News unter https://www.eclipse.org/eclipse/news/4.6/platform.php.

Interessant sind:

Commands and shortcuts to zoom in text editors In text editors, you can now use Zoom In (Ctrl++ or Ctrl+=) and Zoom Out (Ctrl+-) commands to increase and decrease the font size.Like a change in the General > Appearance > Colors and Fonts preference page, the commands persistently change the font size in all editors of the same type. If the editor type’s font is configured to use a default font, then that default font will be zoomed.
Full Screen The Full Screen feature is now also available on Windows and Linux. You can toggle the mode via shortcut (Alt+F11) or menu (Window > Appearance > Toggle Full Screen).When Full Screen is activated, you’ll see a dialog which tells you how to turn it off again.

On the Mac, Window > Toggle Full Screen (Control+Command+F) still works as before.

Substring code completion Content Assist now supports substring patterns. Enter any part of the desired proposal’s text, and Content Assist will find it! For example, completing on selection proposes all results containing selection as a substring.Popup with proposals like addSelectionListener(..), getSelection(), etc.

This feature can be disabled using the Show substring matches option on the Java > Editor > Content Assist preference page.

Clean Up to remove redundant type arguments A new option to remove redundant type arguments has been added under the „Unnecessary Code“ group of the Clean Up profile.
Create new fields from method parameters You can now assign all parameters of a method or constructor to new fields at once using a newQuick Assist (Ctrl+1):Assign all parameters to new fields
Quick Fix to configure problem severity You can now configure the severity of a compiler problem by invoking the new Quick Fix (Ctrl+1) which opens the Java > Compiler > Errors/Warnings preference page and highlights the configurable problem.

The Quick Fix icon may look familiar to you. In older Eclipse versions, this was a toolbar button in enriched hovers (i.e., you had to press F2 or move the mouse into the hover to see it).

 

 

 

JAX-RS mit Jersey, Teil 2

Content-Handler, Marshaller und verschiedene MIME-Typen

JAX-RS erlaubt grundsätzlich alle MIME-Typen, und die Daten selbst können auf verschiedene Java-Datentypen übertragen werden. So ist es egal, ob bei Textdokumenten zum Beispiel der Rückgabetyp String oder OutputStream ist; selbst ein File-Objekt lässt sich zurückgeben. Für einen Parametertyp – Parameter werden gleich vorgestellt – gilt das Gleiche: JAX-RS ist hier recht flexibel und kann über einen InputStream oder Writer einen String entgegennehmen. (Reicht das nicht, können so genannte Provider angemeldet werden.)

Bei XML-Dokumenten kommt hinzu, dass JAX-RS wunderbar mit JAXB zusammenspielt.

XML mit JAXB

Dazu ein Beispiel für einen Dienst hinter der URL http://localhost:8080/rest/message/serverinfo, der eine Serverinformation im XML-Format liefert. Das XML wird automatisch von JAXB generiert.

@Path( "message" )
 public class MessageResource {
 
   @GET
   @Path( "serverinfo" )
   @Produces( MediaType.TEXT_XML )
   public ServerInfo serverinfo() {
     ServerInfo info = new ServerInfo();
     info.server = System.getProperty( "os.name" )+" "
                                       + System.getProperty( "os.version" );
     return info;
   }
 }
 
 @XmlRootElement
 class ServerInfo {
   public String server;
 }

Die Klasse ServerInfo ist eine JAXB-annotierte Klasse. In der eigenen JAX-RS-Methode serverinfo() wird dieses ServerInfo-Objekt aufgebaut, das Attribut gesetzt und dann zurückgegeben; der Rückgabetyp ist also nicht String wie vorher, sondern explizit ServerInfo. Dass der MIME-Typ XML ist, sagt @Produces(MediaType.TEXT_XML). Und noch eine Annotation nutzt das Beispiel: @Path. Lokal an der Methode bedeutet es, dass der angegebene Pfad zusätzlich zur Pfadgabe an der Klasse gilt. Also ergibt sich der komplette Pfad aus:

Basispfad + "message" + "/" + "serverinfo"

Unter http://localhost:8080/rest/message/serverinfo ist im Browser die Ausgabe zu sehen.

JSON-Serialisierung *

Ist der Client eines REST-Aufrufs ein JavaScript-Programm in einem Webbrowser, ist es in der Regel praktischer, statt XML das Datenformat JSON zu verwenden. JAX-RS bindet dabei die JABX-annotierten Objekte nicht nur an XML, sondern auch an JSON. Dazu muss lediglich eine kleine Änderung bei den MIME-Typen vorgenommen werden:

@Produces( MediaType.APPLICATION_JSON )

Das reicht schon aus, und der Server sendet ein JSON-serialisiertes Objekt. Jersey bringt – in den späteren Versionen – einen eigenen JSON-Formatter mit, sodass keine Extra-Bibliotheken nötig sind.

Jersey-Client

Die JAX-RS-API bietet mit dem MIME-Typ noch eine Besonderheit, dass der Server unterschiedliche Formate liefern kann, je nachdem, was der Client verarbeiten möchte oder kann. Der Server macht das mit @Produces klar, denn dort kann eine Liste von MIME-Typen stehen. Soll der Server XML und JSON generieren können, schreiben wir:

@GET
@Path( "serverinfo" )
@Produces( { MediaType.TEXT_XML, MediaType.APPLICATION_JSON } )
public ServerInfo serverinfo()

Kommt der Client mit dem Wunsch nach XML, bekommt er XML, möchte er JSON, bekommt er JSON. Die Jersey-Client-API teilt über request(String) bzw. request(MediaType… types) mit, was ihr Wunschtyp ist. (Dieser Typwunsch ist eine Eigenschaft von HTTP und nennt sich Content Negotiation.)

WebTarget wr1 = ClientBuilder.newClient().target( "http://localhost:8080/rest" );
 Builder b1 = wr1.path( "message" ).path( "serverinfo" )
                                   .request( MediaType.APPLICATION_JSON );
 System.out.println( b1.get( ServerInfo.class ).server );  // Windows Vista 6.0

 WebTarget wr2 = ClientBuilder.newClient().target( "http://localhost:8080/rest" );
 Builder b2 = wr2.path( "message" ).path( "serverinfo" ).request( MediaType.TEXT_XML );
 System.out.println( b2.get( ServerInfo.class ).server );  // Windows Vista 6.0
 
 WebTarget wr3 = ClientBuilder.newClient().target( "http://localhost:8080/rest" );
 Builder b3 = wr3.path( "message" ).path( "serverinfo" ).request( MediaType.TEXT_PLAIN );
 System.out.println( b3.get( ServerInfo.class ).server );  // NotAcceptableException

Passt die Anfrage auf den Typ von @Produces, ist alles prima, ohne Übereinstimmung gibt es einen Fehler. Bei der letzten Zeile gibt es eine Ausnahme („javax.ws.rs.NotAcceptableException: HTTP 406 Not Acceptable“), da JSON und XML eben nicht purer Text sind.

REST-Parameter

Im Abschnitt „Wie sieht ein REST-URI aus?“ in Abschnitt 15.2.1 wurde ein Beispiel vorgestellt, wie Pfadangaben aussehen, wenn sie einen RESTful Service bilden:

http://www.tutego.de/blog/javainsel/category/java-7/page/2/

Als Schlüssel-Wert-Paar lassen sich festhalten: category=java-7 und page=2. Der Server wird die URL auseinanderpflücken und genau die Blog-Einträge liefern, die zur Kategorie „java-7“ gehören und sich auf der zweiten Seite befinden.

Bisher sah unser REST-Service auf dem Endpunkt /rest/message/ so aus, dass einfach ein String zurückgegeben wird. Üblicherweise gibt es aber unterschiedliche URLs, die Operationen wie „finde alle“ oder „finde alle mit der Einschränkung X“ abbilden. Bei unseren Nachrichten wollen wir dem Client drei Varianten zur Abfrage anbieten (mit Beispiel):

  • /rest/message/: alle Nachrichten aller Nutzer
  • /rest/message/user/chris: alle Nachrichten von Benutzer „chris“
  • /rest/message/user/chris/search/kitesurfing: alle Nachrichten von Benutzer „chris“ mit dem Betreff „kitesurfing“

Das erste Beispiel macht deutlich, dass hier ohne explizite Angabe weiterer Einschränkungskriterien alle Nachrichten erfragt werden sollen, während mit zunehmend längerer URL weitere Einschränkungen dazukommen.

Parameter in JAX-RS kennzeichnen

Die JAX-RS-API erlaubt es, dass diese Parameter (wie Benutzername oder Such-String) leicht eingefangen werden können. Für die drei möglichen URLs entstehen drei überladene Methoden:

@GET @Produces( MediaType.TEXT_PLAIN )
public String message() ...
 
@GET @Produces( MediaType.TEXT_PLAIN )
@Path("user/{user}")
public String message( @PathParam("user") String user ) {
  return String.format( "Benutzer = %s", user );
}
 
@GET
@Produces( MediaType.TEXT_PLAIN )
@Path("user/{user}/search/{search}")
public String message( @PathParam("user") String user,
                       @PathParam("search") String search ) {
  return String.format( "Benutzer = %s, Suchstring = %s", user, search );
}

Die bekannte @Path-Annotation enthält nicht einfach nur einen statischen Pfad, sondern beliebig viele Platzhalter in geschweiften Klammern. Der Name des Platzhalters taucht in der Methode wieder auf, nämlich dann, wenn er mit @PathParam an einen Parameter gebunden wird. Jersey parst für uns die URL und füllt die Parametervariablen passend auf bzw. ruft die richtige Methode auf. Da die JAX-RS-Implementierung den Wert füllt, nennt sich das auch JAX-RS-Injizierung.

URL-Endung Aufgerufene Methode
/rest/message/ message()
/rest/message/user/chris message( String user )
/rest/message/user/chris/search/kitesurfing message( String user, String search )

Tabelle: Welche URL zu welcher Methode führt

Die Implementierungen der Methoden würden jetzt an einen Daten-Service gehen und die selektierten Datensätze zurückgeben. Das zeigt das Beispiel nicht, da dies eine andere Baustelle ist.

Tipp: Wenn Parameter falsch sind, kann eine Methode eine besondere Ausnahme vom Typ javax.ws.rs.WebApplicationException (dies ist eine RuntimeException) erzeugen. Im Konstruktor von javax.ws.rs.WebApplicationException lässt sich ein Statuscode als int oder als Aufzählung vom Typ Response.Status übergeben, etwa new WebApplicationException(Response.Status. EXPECTATION_FAILED).

REST-Services mit Parametern über die Jersey-Client-API aufrufen

Wenn die URLs in dem Format schlüssel1/wert1/schlüssel2/wert2 aufgebaut sind, dann ist ein Aufbau einfach durch Kaskadierung der path(…)-Methoden umzusetzen:

System.out.println( ClientBuilder.newClient()
   .target( "http://localhost:8080/rest" )
   .path( "message" ).path( "user" ).path("chris")
   .request().get(String.class) ); // Benutzer = chris
 
System.out.println( ClientBuilder.newClient()
   .target( "http://localhost:8080/rest" )
   .path( "message" ).path( "user" )
   .path("chris").path("search").path("kitesurfing")
   .request().get(String.class) ); // Benutzer = chris, Suchstring = kitesurfing

Multiwerte

Schlüssel-Wert-Paare lassen sich auch auf anderen Wegen übermitteln statt nur auf dem Weg über schlüssel1/wert1/schlüssel2/wert2. Besonders im Web und für Formularparameter ist die Kodierung über schlüssel1=wert1&schlüssel2=wert2 üblich. Auch das kann in JAX-RS und mit der Jersey-Client-API abgebildet werden:

  • Anstatt Parameter mit @PathParam zu annotieren, kommt bei Multiwerten @QueryParam zum Einsatz.
  • Anstatt mit path(String) zu arbeiten, wird bei dem Jersey-Client mit queryParam(String key, Object… value)

Hinweis: Es gibt eine Reihe von Dingen, die in Methoden per Annotation übermittelt werden können, und nicht nur @PathParam und @QueryParam. Dazu kommen noch Dinge wie @HeaderParam für den HTTP-Request-Header, @CookieParam für Cookies, @Context für Informationsobjekte und weitere Objekte.

PUT-Anforderungen und das Senden von Daten

Zum Senden von Daten an einen REST-Service ist die HTTP-PUT-Methode gedacht. Die Implementierung einer Java-Methode kann so aussehen:

@PUT
@Path( "user/{user}" )
@Consumes( MediaType.TEXT_PLAIN )
public Response postMessage( @PathParam( "user" ) String user, String message ) {
  System.out.printf( "%s sendet '%s'%n", user, message );
  return Response.noContent().build();
}

Zunächst gilt, dass statt @GET ein @PUT die Methode annotiert. @Consumes hält den MIME-Typ dieser gesendeten Daten fest. Ein zusätzlicher @PathParam fängt die Benutzerkennung ein, die dann mit der gesendeten PUT-Nachricht auf der Konsole ausgegeben wird.

Diese beiden Annotationen @PUT und @Consumes sind also nötig. Eine Rückgabe in dem Sinne hat die Methode nicht, und es ist umstritten, ob ein REST-PUT überhaupt neben dem Statuscode etwas zurückgeben soll. Daher ist die Rückgabe ein spezielles JAX-RS-Objekt vom Typ Response, das hier für 204 steht.

PUT/POST/DELETE-Sendungen mit der Jersey-Client-API absetzen

Ein Invocation.Builder bietet neben get(…) auch die anderen Java-Methoden für HTTP-Methoden, also delete(…), post(…), options(…), … und eben auch put(…) zum Schreiben.

Client client = ClientBuilder.newClient();
 WebTarget target = client.target( "http://localhost:8080/rest" );
 Response response = target.path( "message" ).path( "user" ).path( "chris" )
                           .request().put( Entity.text("Hey Chris") );
 System.out.println( response );

Die put(…)-Methode erwartet als Argument den Typ Entity, und zum Objektaufbau gibt es diverse statische Methoden in Entity. Entity.text(„Hey Chris“) ist eine Abkürzung für Entity.entity(„Hey Chris“, MediaType.TEXT_PLAIN).

JAX-RS mit Jersey

Für Java gibt es mit JAX-RS einen Standard zum Deklarieren von REST-basierten Web-Services. Er wurde in der JSR-311, „JAX-RS: The Java API for RESTful Web Services“, definiert. Es fehlt noch eine Implementierung, damit wir Beispiele ausprobieren können:

  • Jeder Applikationsserver ab Java EE 6 enthält standardmäßig eine JAX-RS-Implementierung.
  • Da wir JAX-RS ohne einen Applikationsserver mit der normalen Java SE ausprobieren möchten, ist eine JAX-RS-Implementierung nötig, da das JDK die JAX-RS-API nicht mitbringt. Es ist naheliegend, die JAX-RS-Referenzimplementierung Jersey (http://jersey.java.net/) zu nutzen, die auch intern von Applikationsservern verwendet wird.

Mit Jersey lässt sich entweder ein Servlet-Endpunkt definieren, sodass der RESTful Web-Service in einem Servlet-Container wie Tomcat läuft, oder der eingebaute Mini-HTTP-Server nutzen. Wir werden im Folgenden mit dem eingebauten Server arbeiten.

Download und JAR-Dateien

Beginnen wir mit dem Download der nötigen Java-Archive. Die Jersey-Homepage http://jersey.java.net/ verweist für den Download auf http://jersey.java.net/download.html. Klicken wir dort auf

Jersey JAX-RS 2.0 RI bundle […] contains the JAX-RS 2.0 API jar, all the core Jersey module jars as well as all the required 3rd-party dependencies.

dann startet der Download von jaxrs-ri-2.23.1.zip. (Die Datei liegt physikalisch auf den Servern vom Maven Central Repository und könnte neuer sein.) Das ZIP-Archiv können wir auspacken und dann folgende Archive in den Klassenpfad aufnehmen:

  • ws.rs-api-2.0.jar (aus dem Ordner api): Enthält die JAX-RS-API, aber keine Implemen-tierung.
  • jersey-client.jar, jersey-common.jar, jersey-server.jar (aus dem Ordner lib): Jersey-Implementierung
  • *.jar (aus dem Ordner ext): Enthält das, worauf die Jersey-Implementierung selbst zurückgreift; um es einfach zu machen: einfach alles.
  • Ein JAR müssen wir noch in den Klassenpfad dazusetzen, damit der Jersey-Server den im JDK eingebauten HTTP-Server nutzen kann, und zwar das jersey-container-jdk-http-2.x.y.jar von http://central.maven.org/maven2/org/glassfish/jersey/containers/jersey-container-jdk-http/

JAX-RS-Annotationen für den ersten REST-Service

JAX-RS definiert einige Annotationen, die zentrale Konfigurationen bei RESTful Web-Services vornehmen, etwa Pfadangaben, Ausgabeformate oder Parameter. In den nächsten Abschnitten werden diese Annotationen an unterschiedlichen Beispielen vorgestellt.

Die Annotationen @Path und @GET

Beginnen wir mit einem REST-Service, der einen simplen Text-String liefert:

package com.tutego.insel.rest;
 
 import javax.ws.rs.*;
 
 @Path( "message" )
 public class MessageResource {
 
   @GET
   @Produces( MediaType.TEXT_PLAIN )
   public String message() {
     return "Yea! ";
   }
 }

Die ersten beiden Annotationen sind zentral:

  • @Path: Ist die Pfadangabe, die auf den Basispfad gesetzt wird.
  • @GET: Die HTTP-Methode. Hier gibt es auch die Annotationen für die anderen HTTP-Methoden POST, PUT, …
  • @Produces: Die Annotation kann zwar grundsätzlich auch entfallen, aber besser ist es, deutlich einen MIME-Typ zu setzen. Es gibt String-Konstanten für die wichtigsten MIME-Typen, wie TEXT_XML oder MediaType.TEXT_HTML, und auch Strings wie „application/pdf“ können direkt gesetzt werden.

Test-Server starten

Ein Java EE-Application-Server würde die Klasse aufgrund der Annotationen gleich einlesen und als REST-Service anmelden. Da wir die Klasse jedoch in der Java SE ohne Application-Server verwenden, muss ein REST-Server von Hand aufgebaut werden. Das ist aber problemlos, denn dafür haben wir jersey-container-jdk-http-2.x.y.jar eingebunden, sodass sich Jersey in den im JDK eingebauten HTTP-Server integriert.

ResourceConfig rc = new ResourceConfig().packages( "com.tutego.insel.rest" );
 HttpServer server = JdkHttpServerFactory.createHttpServer( 
    URI.create( "http://localhost:8080/rest" ), rc);
 JOptionPane.showMessageDialog( null, "Ende" );
 server.stop( 0 );

Nach dem Start des Servers scannt Jersey die Klassen im genannten Paket daraufhin ab, ob sie passende JAX-RS-Annotationen tragen. Das Class-Scanning wurde aktiviert mit der package(…)-Methode beim ResourceConfig, es ist aber auch möglich, im Konstruktor von ResourceConfig direkt die JAW-RS-Klassen aufzuzählen.

Die statische Methode JdkHttpServerFactory.createHttpServer(…) liefert ein Objekt vom Typ com.sun.net.httpserver.HttpServer, das Teil der internen Java-API ist. Die JdkHttpServerFactory stammt aus dem internen Jersey-Paket.

REST-Services konsumieren

Bei createHttpServer(…) ist als Testpfad „http://localhost:8080/rest“ eingetragen, und zusammen mit @Path(„message“) an der Klasse ergibt sich der Pfad http://localhost:8080/rest/message für die Ressource.

REST im Browser

Wird die URL http://localhost:8080/rest/message in den Webbrowser eingesetzt, führt das zur Ausgabe von „Yea!“.

REST-Client mit Jersey

Sich im Browser das Ergebnis eines REST-Aufrufs anzuschauen, ist nicht das übliche Szenario. Oft sind es JavaScript-Programme im Browser, die REST-Aufrufe starten und die konsumierten Daten auf einer Webseite integrieren.

Ist es ein Java-Programm, das einen REST-Dienst anzapft, so kann dies mit einer einfachen URLConnection erledigt werden, was eine Standardklasse aus dem java.net-Paket ist, um auf HTTP-Ressourcen zuzugreifen. Doch Jersey definiert eine standardisierte API zum einfachen Zugriff. Es reicht ein Einzeiler:

System.out.println(
   ClientBuilder.newClient().target( "http://localhost:8080/rest" )
                .path( "message" ).request().get( String.class )
 );

Der API-Stil, den die Bibliotheksautoren hier verwenden, nennt sich Fluent-API; Methodenauf-rufe werden verkettet, um alle Parameter wie URL, MIME-Typ für die Anfrage zu setzen. Das ist eine Alternative zu den bekannten Settern auf JavaBeans bzw. einen überladenen Konstruktor mit unübersichtlichen Parameterlisten.

Um die Typen bei der javax.ws.rs.client-API etwas besser zu verstehen, schreiben wir alles ganz vollständig aus:

Client client = ClientBuilder.newClient();
 WebTarget target = client.target( "http://localhost:8080/rest" );
 WebTarget resourceTarget = target.path( "message" );
 Invocation.Builder request = resourceTarget.request( MediaType.TEXT_PLAIN );
 Response response = request.get();
 System.out.println( response.readEntity( String.class ) );

Das Response-Objekt hat auch eine Methode getStatus() für den HTTP-Statuscode und hasEntry(), um zu prüfen, ob es ein Ergebnis gibt und keinen Fehler. Apropos Fehler: Ist die URL falsch, gibt es eine RuntimeExcection – geprüfte Ausnahmen verwendet Jersey nicht.

Exception in thread "main" javax.ws.rs.NotFoundException: HTTP 404 Not Found

RESTful Web-Services

Dieser Abschnitt stellt das Architekturprinzip REST vor und anschließend den Java-Standard JAX-RS. Es folgen Beispiele mit der JAX-RS-Referenzimplementierung Jersey.

Aus Prinzip REST

Bei RESTful Services liegt das Konzept zugrunde, dass eine Ressource über einen Webserver verfügbar ist und eindeutig über einen URI identifiziert wird.

Da unterschieden werden muss, ob eine Ressource neu angelegt, gelesen, aktualisiert, gelöscht oder aufgelistet werden soll, werden dafür die bekannten HTTP-Methoden verwendet:

HTTP-Methode Operation
GET Listet Ressourcen auf oder holt eine konkrete Ressource.
PUT Aktualisiert eine Ressource.
DELETE Löscht eine Ressource oder eine Sammlung von Ressourcen.
POST Semantik kann variieren, in der Regel aber geht es um das Anlegen einer neuen Ressource.

Tabelle: HTTP-Methoden und übliche assoziierte Operationen

Anders als SOAP ist REST kein entfernter Methodenaufruf, sondern eher vergleichbar mit den Kommandos INSERT, UPDATE, DELETE und SELECT in SQL.

Ursprung von REST

Die Idee, Web-Services mit dem Webstandard HTTP aufzubauen, beschrieb Roy Thomas Fielding im Jahr 2000 in seiner Dissertation „Architectural Styles and the Design of Network-based Software Architectures“. Das Architekturprinzip nennt er Representational State Transfer (REST) – der Begriff ist neu, aber das Konzept ist alt. Aber so wie im richtigen Leben setzen sich manche Dinge erst spät durch. Das liegt auch daran, dass SOAP-basierte Web-Services immer komplizierter wurden und sich die Welt nach etwas anderem sehnte. (Daher beginnen wir im Buch auch mit REST, und dann wird SOAP folgen.)

Wie sieht ein REST-URI aus?

Auf der einen Seite stehen die HTTP-Methoden GET, PUT, POST, DELETE und auf der anderen Seite die URIs, die die Ressource kennzeichnen. Ein gutes Beispiel einer REST-URL ist der Blog vom Java-Buch:

http://www.tutego.de/blog/javainsel/category/java-8/page/2/

Das Ergebnis ist ein HTML-Dokument mit ausschließlich den Beiträgen aus der Kategorie Java 8 und nur denen auf der zweiten Seite.

Fast daneben ist auch vorbei

Da auf den ersten Blick jede HTTP-Anfrage an einen Webserver wie ein REST-Aufruf aussieht, sollten wir uns im Klaren darüber sein, was denn kein REST-Aufruf ist. Eine URL wie http://www.bing.com/search?q=tutego ist erst einmal nicht der typische REST-Stil, da es keine Ressource gibt, die angesprochen wird. Da REST als leichtgewichtig und cool gilt, geben sich natürlich gerne Dienste ein bisschen RESTig. Ein Beispiel ist Flickr, ein Fotoservice von Yahoo. Das Unternehmen wirbt mit einer REST-API, aber es ist alles andere als REST und kein gutes Beispiel.[1] Das Gleiche gilt auch für Twitter, das lediglich einen rudimentären REST-Ansatz hat, aber in der Öffentlichkeit als REST pur wahrgenommen wird.

[1]   Roy Fielding meint dazu nur: „Flickr obviously don’t have a clue what REST means since they just use it as an alias for HTTP. Perhaps that is because the Wikipedia entry is also confused. I don’t know.“ (Quelle: http://roy.gbiv.com/untangled/2008/no-rest-in-cmis).

Boolean nach Ganzzahl konvertieren

Der primitive Typ boolean lässt sich nicht über eine Typumwandlung in einen anderen primitiven Typ konvertieren. Doch in der Praxis kommt es vor, dass true auf 1 und false auf 0 abgebildet werden muss; der übliche Weg ist:

int val = aBoolean ? 1 : 0;

Exotischer ist:

int val = Boolean.compare( aBoolean, false );

Noch exotischer folgendes:

int val = 1 & Boolean.hashCode( true ) >> 1;

Interessante Open-Source-Libs Mai

Update von Retrolambda

Java 8 Sprachfeatures für Java 7, 6, 5 VM. Details unter https://github.com/orfjackal/retrolambda. Die Updates von letzter Woche:

Retrolambda 2.3.0 (2016-04-30)

  • Optimize generated code to reduce method count (Issue #81)
  • Fix method reference to protected method in base class in other package failing with IllegalAccessError (Issue #89)

Retrolambda 2.2.0 (2016-04-29)

  • Backports calls to Objects.requireNonNull, improving JDK 9 support (Issue #75)
  • Optimize generated code to reduce method count (Issue #81)

Nachträgliches Implementieren von Schnittstellen

Implementiert eine Klasse eine bestimmte Schnittstelle nicht, so kann sich auch nicht am dynamischen Binden über diese Schnittstelle teilnehmen, auch wenn sie eine Methoden hat, über die eine Schnittstelle abstrahiert. Besitzt zum Beispiel die nicht-finale Klasse FIFA eine öffentliche Methode price(), implementiert aber Buyable mit einer gleich benannten Methoden nicht, so lässt sich zu einem Trick greifen, sodass eine Implementierung geschaffen wird, die die existierende Methode aus der Klasse und die der Schnittstelle in die Typhierarchie bringt.

class FIFA {
  public double price() { … }
}

interface Buyable {
   double price();
 }

class FIFAisBuyable extends FIFA implements Buyable { }

Eine neue Unterklasse FIFAisBuyable erbt von der Klasse FIFA und implementiert die Schnittstelle Buyable, sodass der Compiler die existierende price()-Methode mit Vorgabe der Schnittstelle vereinigt. Nun lässt sich FIFAisBuyable als Buyable nutzen und dahinter steckt die Implementierung von FIFA. Als Unterklasse bleiben auch alle sichtbaren Eigenschaften der Oberklasse erhalten.

Java SE 8u9[1|2] Update

Alles News unter http://www.oracle.com/technetwork/java/javase/8all-relnotes-2226344.html.

Interessanbt finde ich unter anderem:

New JVM Options added: ExitOnOutOfMemoryError and CrashOnOutOfMemoryError
Two new JVM flags have been added:

  • ExitOnOutOfMemoryError – When you enable this option, the JVM exits on the first occurrence of an out-of-memory error. It can be used if you prefer restarting an instance of the JVM rather than handling out of memory errors.
  • CrashOnOutOfMemoryError – If this option is enabled, when an out-of-memory error occurs, the JVM crashes and produces text and binary crash files (if core files are enabled).

See JDK-8138745.

Und:

Disable MD5withRSA signature algorithm in the JSSE provider
The MD5withRSA signature algorithm is now considered insecure and should no longer be used. Accordingly, MD5withRSA has been deactivated by default in the Oracle JSSE implementation by adding „MD5withRSA“ to the „jdk.tls.disabledAlgorithms“ security property. Now, both TLS handshake messages and X.509 certificates signed with MD5withRSA algorithm are no longer acceptable by default. This change extends the previous MD5-based certificate restriction („jdk.certpath.disabledAlgorithms“) to also include handshake messages in TLS version 1.2. If required, this algorithm can be reactivated by removing „MD5withRSA“ from the „jdk.tls.disabledAlgorithms“ security property.

JDK-8144773 (not public)

 

Bouncing ball in a Swing window

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class BouncingBall {

  private static class BallPanel extends JPanel {

    // http://www.iconfinder.com/icondetails/8839/64/beach_ball_tourism_toys_icon
    private final Icon ball = new ImageIcon( BouncingBall.class.getResource( "ball.png" ) );
    private int ballX, ballY, dx = 2, dy = 2;

    public BallPanel() {
      new Timer( 10, new ActionListener() {
        @Override
        public void actionPerformed( ActionEvent evt ) {
          ballX += dx;
          ballY += dy;

          if ( ballX <= 0 || ballX + ball.getIconWidth() >= getWidth() )
            dx = -dx;
          if ( ballY <= 0 || ballY + ball.getIconHeight() >= getHeight() )
            dy = -dy;

          repaint();
        }
      } ).start();
    }

    @Override
    public void paint( Graphics g ) {
      Graphics2D g2d = (Graphics2D) g;
      g2d.setRenderingHint( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON );
      g2d.setRenderingHint( RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY );

      g2d.setColor( Color.WHITE );
      g2d.fill( getBounds() );

      ball.paintIcon( null, g2d, ballX, ballY );
    }
  }

  public static void main( String[] args ) {
    JFrame f = new JFrame();
    f.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
    f.setIgnoreRepaint( true );
    f.add( new BallPanel() );
    f.setBounds( 200, 100, 400, 200 );
    f.setVisible( true );
  }
}