RESTful Web Services mit JAX-RS

Abschließend noch ein paar Worte zu Exceptions. Jede Methode eines RESTful Web Service kann während ihrer Ausführung eine WebApplicationException oder eine davon abgeleitete Ausnahme werfen, ohne diese explizit im Methodenkopf deklarieren zu müssen. Der beim Instanziieren einer solchen Exception übergebene Statuscode wird dann als HTTP-Statuscode an den Aufrufer zurückgeliefert. Das Werfen einer davon abweichenden Ausnahme ist ebenfalls möglich, erfordert aber deren Deklaration im Methodenkopf sowie die Bereitstellung eines so genannten Exception Mappers. Es handelt sich dabei um eine mit @Provider annotierte Klasse, die das Interface ExceptionMapper implementiert. Beim Bücherdienst kann z. B. innerhalb der Methode delete()eine BookNotFoundException auftreten. Nachdem diese nach außen gereicht wird, müssen wir einen Exception Mapper bereitstellen. Wir nennen ihn BookNotFoundExceptionMapper und sorgen dafür, dass er den HTTP-Statuscode 404 (Not Found) zurückliefert (Listing 5).

Listing 5
@Provider
public class BookNotFoundExceptionMapper implements ExceptionMapper {
  public Response toResponse(BookNotFoundException bnfe) {
    return Response.status(Status.NOT_FOUND)
                   .entity(bnfe.getMessage())
                   .type(MediaType.TEXT_PLAIN)
                   .build();
  }
}
Deployment eines Web Service

Das Deployment eines RESTful Web Service erfolgt in Form einer so genannten JAX-RS-Applikation. Mittels einer solchen Applikation definiert der Entwickler die anwendungsspezifischen Komponenten und Metadaten. Er erstellt hierzu eine von Application abgeleitete Klasse und überschreibt in dieser die Methode getClasses(),sodass diese alle JAX-RS-spezifischen Klassen in Form eines Set-Objekts zurückliefert (Listing 6). Zu beachten ist, dass wir dort tatsächlich nur die Klassen anführen müssen. Das Interface mit den eigentlichen JAX-RS-Annotationen können wir uns ersparen.

Listing 6
public class BookResourceApplication extends Application {
  public Set> getClasses() {
    Set> classes = new HashSet>();
    
    classes.add(BookResourceImpl.class);
    classes.add(BookCollectionProvider.class);
    classes.add(BookNotFoundExceptionMapper.class);
    
    return classes;
  }
}

Zum Starten einer solchen Applikation lässt sich der von Java SE 6 bereitgestellte HTTP-Server nutzen (Listing 7). Unser Bücherdienst wird dadurch unter http://localhost:8080/resources/… hochgezogen. Da die Standardedition keine JAX-RS-Implementierung enthält, darf nicht vergessen werden, die erforderlichen Bibliotheken dem Klassenpfad hinzuzufügen.

Listing 7
HttpServer server = HttpServer.create(new InetSocketAddress(8080), 25);
HttpContext context = server.createContext("/resources");

HttpHandler handler = RuntimeDelegate.getInstance().createEndpoint
                        (new BookResourceApplication(), HttpHandler.class);
context.setHandler(handler);

server.start();

Produktiv werden solche JAX-RS-Anwendungen jedoch meist als Webapplikationen ausgerollt. Bei Jersey muss hierzu in web.xml ein bestimmtes Servlet registriert werden und diesem der Name der Applikationsklasse als Parameter übergeben werden (Listing 8). Alle Klassen sind wie üblich in WEB-INF/classes und alle Bibliotheken in WEB-INF/lib einzuspielen.

Listing 8
RESTServletcom.sun.jersey.spi.container.servlet.
            ServletContainerjavax.ws.rs.Applicationde.javamagazin.books.BookResourceApplication
          1RESTServlet/resources/*

Zum Deployment des Webarchivs bedarf es dann eines Webservers mit integriertem Jersey. GlassFish 3.1 stellt einen solchen dar. An die Schnittstellenbeschreibung eines Web Service gelangt man, indem man application.wadl unter dem Pfad des Web Service Deployments aufruft. Im Fall der Bücherverwaltung liefert http://localhost:8080/resources/application.wadl die gewünschte Information.

Nutzung eines Web Service

Nachdem sich die Funktionalitäten eines RESTful Web Service ganz einfach per HTTP nutzen lassen, kann natürlich jeder HTTP-Client zum Abfragen und Manipulieren der Ressourcen eingesetzt werden. Hardcore-Java-Fans können auch via HttpURLConnection auf einen solchen Webdienst zugreifen (Listing 9). Konkret führt die Ausführung dieser Codezeilen zum serverseitigen Aufruf der Methode create()und dadurch zum Anlegen des transferierten Buches.

Listing 9
URL url = new URL("http://localhost:8080/resources/books");

HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("POST");
connection.setRequestProperty("Content-Type", "application/xml");
connection.setDoOutput(true);
connection.connect();

PrintStream out = new PrintStream(connection.getOutputStream());
out.print(Book.getBookExample().toXMLString());
out.close();

System.out.println("Status Code: " + connection.getResponseCode());
System.out.println("Location: " + connection.getHeaderField("Location"));

connection.disconnect();

Dass diese Vorgehensweise nur in Ausnahmefällen glücklich macht, ist wohl verständlich. In den meisten Fällen greift der Entwickler deshalb auf Frameworks zurück, die den Dienstzugriff auf wesentlich höherem Abstraktionslevel erlauben. Dank Ajax (Asynchronous JavaScript and XML) lassen sich RESTful Web Services mithilfe von JavaScript auch direkt aus dem Browser heraus abfragen [8]. Das wird dann aber die Aufgabe des nächsten Ferienpraktikanten sein.

Fazit und Ausblick

Nach einer kurzen theoretischen Einführung über RESTful Web Services und JAX-RS haben wir uns am Beispiel eines Bücherdienstes angesehen, wie man konkret einen solchen RESTful Web Service implementiert, ausrollt und schlussendlich nutzt. Dank des seit der Java-Version 5 eingeschlagenen Weges, bei der Komponentenentwicklung auf annotierte POJOs zu setzen, lassen sich derartige Services einfach umsetzen. Besondere Eleganz verleihen dem Ganzen die Entity-Provider und die Exception Mapper. Im dritten Teil dieser Serie werden wir uns Werkzeuge anschauen, die die Realisierung von SOAP Web Services weiter vereinfachen. Speziell in Bezug auf weiterführende Themen bieten sie ihrem Nutzer viele nützliche Features.

Bernhard Löwenstein (b.loewenstein[at]gmx.at) arbeitet als Projektleiter und Softwareentwickler für die Potsdamer Intervista AG. Er ist außerdem als IT-Trainer tätig und schreibt laufend Artikel für verschiedene deutsche Fachmagazine.
Kommentare

Schreibe einen Kommentar

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