Diese virtuellen Baugerüste geben dem Nutzer den Rest

Frameworks zur Entwicklung von RESTful Web Services

Bernhard Löwenstein

REST ist derzeit in aller Munde, und so ist es nicht verwunderlich, dass für die Realisierung von Webdiensten und Clients, die diesem Architekturstil folgen, diverse Frameworks und Tools verfügbar sind. Drei bedeutende Vertreter aus der Java-Welt stellt dieser Artikel vor.

Enterprise Java Web Services

Die Serie „Enterprise Java Web Services“ ist erstmals im Java Magazin erschienen und umfasst vier Teile:

Im dritten Teil dieser Artikelserie beschäftigten wir uns mit Frameworks zur Entwicklung von SOAP Web Services und Clients. Dieses Mal werden wir uns ebenfalls solche Systeme ansehen, nun aber für das REST-Umfeld. Der Fokus liegt auch dieses Mal vorrangig auf den Funktionen, die über den Java-Web-Service-Standard hinausgehen. Nachdem die Spezifikation für JAX-RS (Java API for RESTful Web Services) [1] kein Client-API vorschreibt, sind hier verschiedene Lösungsansätze zu erwarten. Tabelle 1 listet die wichtigsten Vertreter auf und ermöglicht einen schnellen Überblick. Für jedes dieser Frameworks werden wir uns nach einer kurzen Vorstellung ansehen, mit welchem Mehrwert es in Sachen Dienstentwicklung aufwarten kann. Weitere Themen sind das Ausrollen der implementierten Services sowie die Umsetzung von REST-Clients damit. Da alle Services JAX-RS-konform sind, gelten uneingeschränkt auch die Punkte aus dem zweiten Artikel, selbst wenn wir nicht immer extra darauf hinweisen.

Tabelle 1: Die Eckdaten der bedeutendsten Java-Frameworks zur Entwicklung von RESTful Web Services

Name Version Community Website Lizenz JAX-RS
Apache CXF 2.4.2 Apache http://cxf.apache.org ASL 2.0 Ja
Jersey 1.9 Java.net http://jersey.java.net CDDL 1.1, GPL 2 with CPE Ja
RESTEasy 2.2.2 GA JBoss http://www.jboss.org/resteasy/ ASL 2.0 Ja
Begleitendes Beispiel: Distributed Books

Im zweiten Artikel diente uns ein RESTful Web Service, der alle Bücher inklusive ihrer Standorte im Unternehmen verwaltet, zur Vertiefung der Theorie. Auch dieses Mal greifen wir auf ihn zurück. Zwecks Wiederholung folgt die Kurzspezifikation für diesen sowie das JAX-RS-annotierte Interface, das wir damals schrieben (Listing 1): Der Bücherdienst soll die typischen CRUD-Operationen bereitstellen, die formatmäßig mit XML verarbeitet werden können. Zusätzlich soll der Benutzer alle Bücher im JSON-Format abrufen und über einen Suchbegriff nach bestimmten Werken suchen können. In Verbindung mit den Suchergebnissen kann er zwischen dem JSON- und XML-Format wählen. Hinweis: Sollten im Anschluss Codebeispiele für Handler beziehungsweise Interceptors angeführt werden, geben diese stets die HTTP-Methode und den Request URL zur empfangenen Anfrage auf der Standardkonsole aus.

Listing 1
...
@Path("/books")
@Consumes(MediaType.APPLICATION_XML)
@Produces(MediaType.APPLICATION_XML)
public interface BookResource {
  @POST
  public Response create(@Context UriInfo uriInfo, Book book);
  
  @GET
  @Path("/{bookid}")
  public Book read(@PathParam("bookid") long bookID);
  
  @PUT
  @Path("/{bookid}")
  public void update(@PathParam("bookid") long bookID, Book book);

  @DELETE
  @Path("/{bookid}")
  public void delete(@PathParam("bookid") long bookID) throws 
                                           BookNotFoundException;

  @GET
  @Path("/all")
  @Produces(MediaType.APPLICATION_JSON)
  public String getAll(@QueryParam("maxnumber") @DefaultValue("50") 
                                                       int maxNumber);
  
  @GET
  @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
  @Path("/search")
  public Collection search(@QueryParam("query") String query);
}
Apache CXF

Wie sich mit dem Apache-CXF-Framework sowohl SOAP als auch RESTful Web Services realisieren lassen, lernten wir bereits im letzten Artikel kennen. Das Wichtigste hiermit nochmals in Kürze: CXF entstand durch Fusion von Celtix und XFire, besticht durch vielfältige Integrationsmöglichkeiten und eine große Zahl an Werkzeugen zur Codegenerierung. Von der Architektur her bildet der auf Spring basierende CXF-Bus das Rückgrat des Systems, weshalb an vielen Stellen Spring-Konfigurationsdateien anzutreffen sind.

Die Entwicklung von REST-Diensten kann auf verschiedene Arten erfolgen. In den meisten Fällen wird der Programmierer wohl auf JAX-RS zurückgreifen. Bei Bedarf kann er sie aber auch auf Basis der Provider- und Dispatch APIs von JAX-WS (Java API for XML-based Web Services) [2] erstellen. Eine dritte Möglichkeit via HTTP Binding gibt es außerdem noch, sie wird allerdings zukünftig nicht mehr unterstützt werden.

Zur Wandlung der übermittelten Daten von ihrer Textrepräsentation in ein Java-Objekt bietet CXF neben XML auch direkte Unterstützung für JSON. Es greift dabei auf Jettison zurück. In Verbindung mit unserem Bücherdienst können wir dadurch auf den selbst geschriebenen Entity-Provider BookCollectionProvider verzichten. Durch geringfügige Adaption des JAX-RS-Interface inklusive Anpassung der zugehörigen Ressourcenklasse können wir unsere Sourcen überhaupt von jeglichem JSON-lastigen Code befreien:

@GET
@Path("/all")
@Produces(MediaType.APPLICATION_JSON)
public Collection getAll(@QueryParam("maxnumber")
                         @DefaultValue("50") int maxNumber);

Indem wir die Bücherdaten nun anstatt in textueller JSON-Darstellung in Form eines Collection-Objekts zurückliefern, übertragen wir die Verantwortung für die Transformation von Java nach JSON an die JAX-RS Runtime, die dieser erfreulicherweise gewissenhaft nachkommt. Mit Atom und Fastinfoset unterstützt CXF noch weitere Medientypen. In Sachen Data Binding kann der Nutzer neben JAXB auch auf Aegis, SDO und XML Beans zurückgreifen.

Das CXF-eigene Interceptor-Konzept begegnete uns bereits im letzten Artikel. Durch die dem System zugrunde liegende Bus-Architektur lassen sich solche Komponenten in absolut identischer Weise in Verbindung mit RESTful Web Services einsetzen, das heißt der damals implementierte MyLoggingInInterceptor funktioniert auch problemlos mit unserem Bücherdienst. Darüber hinaus hat der Entwickler die Möglichkeit, via Filter auf den Request beziehungsweise den Response einzuwirken. Abhängig vom Typ handelt es sich bei jenen um Klassen, die entweder das Interface org.apache.cxf.jaxrs.ext.RequestHandler oder org.apache.cxf.jaxrs.ext.ResponseHandler implementieren (Listing 2). Der Unterschied zwischen Interceptors und Filter wird unter [3] erklärt. Zusätzlich kann auch über so genannte Invoker vor und nach dem eigentlichen Dienstaufruf in den Verarbeitungsprozess eingegriffen werden.

Listing 2
...
public class MyLoggingRequestHandler implements RequestHandler {
  @Override
  public Response handleRequest(Message message, ClassResourceInfo 
                                                       resourceClass) {
    String requestURL = (String) message.get(Message.REQUEST_URL);
    if (message.get(Message.QUERY_STRING) != null) {
      requestURL += "?" + message.get(Message.QUERY_STRING);
    }
    System.out.println("HTTP Method: " + 
            message.get(Message.HTTP_REQUEST_METHOD));
    System.out.println("Request URL: " + requestURL);
    return null;
  }
}
Geschrieben von
Bernhard Löwenstein
Kommentare

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht.