Frameworks zur Entwicklung von RESTful Web Services

Das Thema Sicherheit nimmt sich CXF ebenfalls an. So liefert es verschiedene Interceptors und Filter mit, die unter anderem auf Authentication und Authorization abzielen.

Das Ausrollen der Dienste lässt sich auf verschiedene Arten durchführen. Zum einen kann der Programmierer seine Services mittels der Klasse org.apache.cxf.jaxrs.JAXRSServerFactoryBean auf programmatischem Wege hochziehen (Listing 3). Zum anderen ist das Deployment in Form einer Webapplikation möglich und erfolgt genau so, wie für das Simple Frontend im dritten Artikel. Lediglich die beiden Endpunktkonfigurationen unterscheiden sich. Weiterhin lassen sich die Web Services mithilfe von javax.ws.rs.ext.RuntimeDelegate starten.

Listing 3
List providers = new ArrayList();
providers.add(new BookNotFoundExceptionMapper());
providers.add(new MyLoggingRequestHandler());
    
JAXRSServerFactoryBean serverFactory = new JAXRSServerFactoryBean();
serverFactory.setAddress("http://localhost:8080/resources");
serverFactory.setResourceClasses(BookResourceImpl.class);
serverFactory.setProviders(providers);

Server server = serverFactory.create();
...
server.destroy();

Zur Abfrage von REST-Diensten bietet das Apache-Framework zwei alternative Lösungen. Einerseits kann sich der Entwickler über die Klasse org.apache.cxf.jaxrs.client.JAXRSClientFactory einen dynamischen Proxy zum JAX-RS-Interface erstellen lassen und darüber die Methodenaufrufe absetzen (Listing 4). Nachdem hierbei keine Kontextvariable in einer Methodensignatur enthalten sein darf, müssen wir unser Interface und die zugehörige Ressourcenklasse noch geringfügig adaptieren:

@POST
public Response create(Book book);

Eine andere Möglichkeit wäre gewesen, für den REST-Client überhaupt eine eigene Schnittstelle zu schreiben. Darin hätten wir dann die Methodennamen nach Belieben ändern können. Diese müssen nämlich zwischen Client und Service nicht übereinstimmen. Für die Proxy-Generierung sind lediglich die Annotationen von Relevanz. Anhand derer baut das System intern die Aufruf-URLs zusammen. Andererseits kann man mithilfe der Klasse org.apache.cxf.jaxrs.client.WebClient auch so genannte HTTP-zentrierte Clients erstellen (Listing 5). Egal aber für welche Variante Sie sich entscheiden, beide Vorgehensweisen sind um Welten komfortabler als unser Versuch mit HttpURLConnection damals, oder?

Listing 4
Book book = ...;

BookResource resource = JAXRSClientFactory.create("http://localhost:8080/resources", BookResource.class);
Response response = resource.create(book);

System.out.println("Status Code: " + response.getStatus());
System.out.println("Location: " + response.getMetadata().get("Location"));
Listing 5
Book book = ...;

WebClient client = WebClient.create("http://localhost:8080/resources/books");
Response response = client.post(book);

System.out.println("Status Code: " + response.getStatus());
System.out.println("Location: " + response.getMetadata().get("Location"));
Jersey

Unweigerlich werden beim Namen dieses Frameworks Gedanken an das letzte Springsteen-Konzert in Wien wach („Are you really from Jersey?“). Wer nicht dort war, dem sei [4] empfohlen. Doch widmen wir uns nun der Referenzimplementierung für JAX-RS. Jersey bietet verschiedene, über den Standard hinausgehende Erweiterungen und lässt sich gut in andere Container integrieren. Den Medientyp JSON unterstützt es ebenfalls direkt, wodurch wir uns erneut den Entity-Provider ersparen. Der Programmierer muss lediglich das automatische, auf Jackson basierende POJO Mapping aktivieren. Wie die Aktivierung konkret funktioniert, entnehmen Sie Listings 6 und 7. Praktischerweise wirken dabei die JAXB-Annotationen auf die JSON-Repräsentation des Objekts ein. Gerade in Verbindung mit Web-Service-Operationen, die sowohl XML als auch JSON produzieren und konsumieren können, erspart das Arbeit. Wenn wir unsere Ressourcenklasse auf Basis des bezüglich getAll()adaptierten Interface von vorhin erstellen, ist erneut keine einzige Zeile mit explizitem JSON-Transformationscode erforderlich. Das manuelle Umwandeln mittels Jettison ist außerdem immer möglich. Das Signieren und Verifizieren von Requests gemäß der Spezifikation von OAuth (Open Authentication) unterstützt Jersey ebenfalls. Es lässt sich durch Filter auf der Client- und Serverseite komfortabel zuschalten.

Das Ausrollen der RESTful Web Services kann wiederum als Webanwendung geschehen. Bei Jersey kommt hierbei das Servlet com.sun.jersey.spi.container.servlet.ServletContainer als Front Controller zum Einsatz. Über Initialisierungsparameter nimmt der Nutzer die Konfiguration vor. Wer es einfach liebt, definiert lediglich die Packages, in denen Jersey automatisch nach Ressourcenklassen und Providern suchen soll. Im Fall unseres Bücherdienstes haben wir allerdings Pech. Nachdem wir die JAX-RS-Annotationen in ein Interface gepackt haben, versucht die Runtime fälschlicherweise dieses, anstatt der eigentlichen Ressourcenklasse zu instanziieren. Wenig überraschend misslingt das. Somit bleibt uns nur der Weg, unsere JAX-RS-Applikation BookResourceApplication zu recyceln und ihren Klassennamen über den passenden Parameter bekanntzugeben. Weiterhin können die Dienste mit dem leichtgewichtigen Webcontainer Grizzly hochgezogen werden (Listing 6). Bestens eignet sich diese Variante zum Einstieg und für Testzwecke. Die Konfiguration erfolgt über die gleichen Parameter wie vorhin.

Listing 6
Map initParams = new HashMap();
initParams.put(ServletContainer.APPLICATION_CONFIG_CLASS, 
              "de.javamagazin.books.BookResourceApplication");
initParams.put(JSONConfiguration.FEATURE_POJO_MAPPING, "true");

SelectorThread selectorThread = 
      GrizzlyWebContainerFactory.create("http://localhost:8080/resources/", 
                                                              initParams);
...
selectorThread.stopEndpoint();

Zu klären ist noch, wie sich ein ausgerollter Service verwenden lässt. Nachdem er auf HTTP-Anfragen reagiert, reicht im Fall des Bücherdienstes die Eingabe von http://localhost:8080/resources/books/all?maxnumber=10 in der Adresszeile des Browsers. Schon wird eine Liste mit maximal zehn Büchern in JSON-Darstellung zurückgeliefert:

 [ {
  "author"    : [ "David Dossot", "John D'Emic" ],
  "bookid"    : 1,
  "keyword"   : [ "Mule", "Enterprise Service Bus" ],
  "location"  : "Room 0815",
  "publisher" : "Manning Publications Co.",
  "title"     : "Mule in Action",
  "year"      : 2010
},
... ]

Zur Realisierung von programmatischen REST-Clients liefert Jersey die Klasse com.sun.jersey.api.client.Client mit (Listing 7). Beim Erzeugen des Clients kann man als Argument die gewünschte Konfiguration zusätzlich übergeben. Wir nutzen diese Möglichkeit, um das automatische POJO Mapping auf der Clientseite zu aktivieren. Der Entwickler allokiert anschließend über das Client-Objekt eine Webressource und baut in weiterer Folge über das zurückgelieferte WebResource-Objekt den gewünschten Request zusammen. Die eigentliche Abfrage setzt er dann über eine der Methoden ab, die namentlich mit den HTTP-Methoden korrespondieren. Die Verarbeitung der Antwortdaten geht dank der Jersey-Unterstützung ebenfalls recht komfortabel vonstatten.

Listing 7
Book book = ...;

ClientConfig config = new DefaultClientConfig();
config.getFeatures().put(JSONConfiguration.FEATURE_POJO_MAPPING, true);

Client client = Client.create(config);
WebResource resource = client.resource("http://localhost:8080/resources/books");
ClientResponse response = resource.post(ClientResponse.class, book);

System.out.println("Status Code: " + response.getStatus());
System.out.println("Location: " + response.getLocation());
Kommentare

Schreibe einen Kommentar

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