{"id":3923,"date":"2017-07-15T12:48:38","date_gmt":"2017-07-15T10:48:38","guid":{"rendered":"http:\/\/www.tutego.de\/blog\/javainsel\/?p=3923"},"modified":"2017-07-15T12:48:38","modified_gmt":"2017-07-15T10:48:38","slug":"jax-rs-mit-jersey-teil-1-restful-web-services","status":"publish","type":"post","link":"https:\/\/www.tutego.de\/blog\/javainsel\/2017\/07\/jax-rs-mit-jersey-teil-1-restful-web-services\/","title":{"rendered":"JAX-RS mit Jersey, Teil 1 (RESTful Web-Services)"},"content":{"rendered":"<p>Dieser Abschnitt stellt das Architekturprinzip REST vor und anschlie\u00dfend den Java-Standard JAX-RS. Es folgen Beispiele mit der JAX-RS-Referenzimplementierung Jersey.<\/p>\n<h3>Aus Prinzip REST<\/h3>\n<p>Bei RESTful Services liegt das Konzept zugrunde, dass eine Ressource \u00fcber einen Webserver verf\u00fcgbar ist und eindeutig \u00fcber einen URI identifiziert wird.<\/p>\n<p>Da unterschieden werden muss, ob eine Ressource neu angelegt, gelesen, aktualisiert, gel\u00f6scht oder aufgelistet werden soll, werden daf\u00fcr die bekannten HTTP-Methoden verwendet:<\/p>\n<table>\n<tbody>\n<tr>\n<td width=\"206\">HTTP-Methode<\/td>\n<td width=\"558\">Operation<\/td>\n<\/tr>\n<tr>\n<td width=\"206\">GET<\/td>\n<td width=\"558\">Listet Ressourcen auf oder holt eine konkrete Ressource.<\/td>\n<\/tr>\n<tr>\n<td width=\"206\">PUT<\/td>\n<td width=\"558\">Aktualisiert eine Ressource.<\/td>\n<\/tr>\n<tr>\n<td width=\"206\">DELETE<\/td>\n<td width=\"558\">L\u00f6scht eine Ressource oder eine Sammlung von Ressourcen.<\/td>\n<\/tr>\n<tr>\n<td width=\"206\">POST<\/td>\n<td width=\"558\">Semantik kann variieren, in der Regel aber geht es um das Anlegen einer neuen Ressource.<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>HTTP-Methoden und \u00fcbliche assoziierte Operationen<\/p>\n<p>Anders als SOAP ist REST kein entfernter Methodenaufruf, sondern eher vergleichbar mit den Kommandos INSERT, UPDATE, DELETE und SELECT in SQL.<\/p>\n<h4>Ursprung von REST<\/h4>\n<p>Die Idee, Web-Services mit dem Webstandard HTTP aufzubauen, beschrieb Roy Thomas Fielding im Jahr 2000 in seiner Dissertation \u201eArchitectural Styles and the Design of Network-based Software Architectures\u201c. Das Architekturprinzip nennt er Representational State Transfer (REST) \u2013 der Begriff ist neu, aber das Konzept ist alt. Aber so wie im richtigen Leben setzen sich manche Dinge erst sp\u00e4t 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.)<\/p>\n<h4>Wie sieht ein REST-URI aus?<\/h4>\n<p>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:<\/p>\n<p>http:\/\/www.tutego.de\/blog\/javainsel\/category\/java-9\/page\/2\/<\/p>\n<p>Das Ergebnis ist ein HTML-Dokument mit ausschlie\u00dflich den Beitr\u00e4gen aus der Kategorie Java 9 und nur denen auf der zweiten Seite.<\/p>\n<h4>Fast daneben ist auch vorbei<\/h4>\n<p>Da auf den ersten Blick jede HTTP-Anfrage an einen Webserver wie ein REST-Aufruf aussieht, sollten wir uns im Klaren dar\u00fcber 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\u00fcrlich 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.<a href=\"#_ftn1\" name=\"_ftnref1\">[1]<\/a> Das Gleiche gilt auch f\u00fcr Twitter, das lediglich einen rudiment\u00e4ren REST-Ansatz hat, aber in der \u00d6ffentlichkeit als REST pur wahrgenommen wird.<\/p>\n<h3>Jersey<\/h3>\n<p>F\u00fcr Java gibt es mit JAX-RS einen Standard zum Deklarieren von REST-basierten Web-Services. Er wurde in der JSR-311, \u201eJAX-RS: The Java API for RESTful Web Services\u201c, definiert. Es fehlt noch eine Implementierung, damit wir Beispiele ausprobieren k\u00f6nnen:<\/p>\n<ul>\n<li>Jeder Applikationsserver ab Java EE 6 enth\u00e4lt standardm\u00e4\u00dfig eine JAX-RS-Implementierung.<\/li>\n<li>Da wir JAX-RS ohne einen Applikationsserver mit der normalen Java SE ausprobieren m\u00f6chten, ist eine JAX-RS-Implementierung n\u00f6tig, da das JDK die JAX-RS-API nicht mitbringt. Es ist naheliegend, die JAX-RS-Referenzimplementierung Jersey (https:\/\/jersey.github.io\/) zu nutzen, die auch intern von Applikationsservern verwendet wird. Jersey x implementiert die JAX-RS 2.0 API.<\/li>\n<\/ul>\n<p>Mit Jersey l\u00e4sst sich entweder ein Servlet-Endpunkt definieren, sodass der RESTful Web-Service in einem Servlet-Container wie Tomcat l\u00e4uft, oder der eingebaute Mini-HTTP-Server genutzt wird. Wir werden im Folgenden mit dem eingebauten Server arbeiten.<\/p>\n<h4>Download und JAR-Dateien<\/h4>\n<ul>\n<li>Beginnen wir mit dem Download der n\u00f6tigen Java-Archive. Diese k\u00f6nnen wir einfach per Maven einbinden, oder per Hand laden. In Maven muss f\u00fcr unser Beispiel folgendes in die xml-Datei:\n<ul>\n<li>&lt;dependency&gt;<\/li>\n<li>&lt;groupId&gt;org.glassfish.jersey.containers&lt;\/groupId&gt;<\/li>\n<li>&lt;artifactId&gt;jersey-container-jdk-http&lt;\/artifactId&gt;<\/li>\n<li>&lt;version&gt;2.25&lt;\/version&gt;<\/li>\n<li>&lt;\/dependency&gt;<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<p>Wenn wir den h\u00e4ndischen Weg nehmen ist es etwas aufw\u00e4ndiger, die einzelnen Jar-Dateien zusammenzusammeln. Die Jersey-Homepage https:\/\/jersey.github.io\/ verweist f\u00fcr den Download auf https:\/\/jersey.github.io\/download.html. Klicken wir dort auf<\/p>\n<p><strong>Jersey<\/strong> <strong>JAX-RS<\/strong> <strong>2.0<\/strong> <strong>RI<\/strong> <strong>bundle<\/strong> [\u2026] contains the JAX-RS 2.0 API jar, all the core Jersey module jars as well as all the required 3rd-party dependencies.<\/p>\n<p>dann startet der Download von jaxrs-ri-2.x.y.zip. Das ZIP-Archiv k\u00f6nnen wir auspacken und dann folgende Archive in den Klassenpfad aufnehmen:<\/p>\n<ul>\n<li>Aus dem Ordner api: ws.rs-api-2.0.1.jar. Enth\u00e4lt die JAX-RS-API (im Wesentlichen die Annotationen), aber keine Implementierung.<\/li>\n<li>Aus dem Ordner lib: jersey-client.jar, jersey-common.jar, jersey-server.jar. Das ist die Jersey-Implementierung. jersey-media-jaxb.jar kann mit eingebunden werden, damit Jersey automatisch JAXB f\u00fcr die Konvertierung zwischen XML und Objekten verwendet.<\/li>\n<li>Aus dem Ordner ext: Alle jar-Dateien. Enth\u00e4lt das, worauf die Jersey-Implementierung selbst zur\u00fcckgreift; um es einfach zu machen: einfach alles.<\/li>\n<li>Ein JAR m\u00fcssen wir noch in den Klassenpfad dazusetzen, damit der Jersey-Server den im JDK eingebauten HTTP-Server nutzen kann, und zwar von http:\/\/central.maven.org\/maven2\/org\/glassfish\/jersey\/containers\/jersey-container-jdk-http\/ das jersey-container-jdk-http-2.x.y.jar.<\/li>\n<\/ul>\n<h3>JAX-RS-Annotationen f\u00fcr den ersten REST-Service<\/h3>\n<p>JAX-RS definiert einige Annotationen, die zentrale Konfigurationen bei RESTful Web-Services vornehmen, etwa Pfadangaben, Ausgabeformate oder Parameter. In den n\u00e4chsten Abschnitten werden diese Annotationen an unterschiedlichen Beispielen vorgestellt.<\/p>\n<h4>Die Annotationen @Path und @GET<\/h4>\n<p>Beginnen wir mit einem REST-Service, der einen simplen Text-String liefert:<\/p>\n<pre>package\u00a0com.tutego.insel.rest;\n \n import\u00a0javax.ws.rs.*;\n import javax.ws.rs.core.MediaType;\n\n\n @Path(\u00a0\"dating\"\u00a0)\n public\u00a0class\u00a0DatingResource\u00a0{\n \n \u00a0\u00a0@GET\n \u00a0\u00a0@Produces(\u00a0MediaType.TEXT_PLAIN\u00a0)\n \u00a0\u00a0public\u00a0String\u00a0meet()\u00a0{\n \u00a0\u00a0\u00a0 return \"Afra, Ange, Ceri, Dara, Ha, Jun, Sevan\";\n\n\u00a0\u00a0}\n }<\/pre>\n<p>Die ersten beiden Annotationen sind zentral:<\/p>\n<ul>\n<li>@Path: Ist die Pfadangabe, die auf den Basispfad gesetzt wird. Die Annotation ist g\u00fcltig f\u00fcr einen Typ und eine Methode.<\/li>\n<li>@GET: Die HTTP-Methode. Hier gibt es auch die Annotationen f\u00fcr die anderen HTTP-Methoden POST, PUT, \u2026<\/li>\n<li>@Produces: Die Annotation kann zwar grunds\u00e4tzlich auch entfallen, aber besser ist es, deutlich einen MIME-Typ zu setzen. Es gibt String-Konstanten f\u00fcr die wichtigsten MIME-Typen, wie TEXT_XML oder MediaType.TEXT_HTML, und auch Strings wie \u201eapplication\/pdf\u201c k\u00f6nnen direkt gesetzt werden. Neben @Produces gibt es das symmetrische @Consumes.<\/li>\n<\/ul>\n<h3>Test-Server starten<\/h3>\n<p>Ein Java\u00a0EE-Application-Server w\u00fcrde 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\u00fcr haben wir jersey-container-jdk-http-2.x.y.jar eingebunden, sodass sich Jersey in den im JDK eingebauten HTTP-Server integriert.<\/p>\n<pre>ResourceConfig\u00a0rc\u00a0=\u00a0new\u00a0ResourceConfig().packages(\u00a0\"com.tutego.insel.rest\"\u00a0);\n HttpServer\u00a0server\u00a0=\u00a0JdkHttpServerFactory.createHttpServer(\u00a0\n \u00a0\u00a0\u00a0URI.create(\u00a0\"http:\/\/localhost:8080\/api\"\u00a0),\u00a0rc );\n JOptionPane.showMessageDialog(\u00a0null,\u00a0\"Ende\"\u00a0);\n server.stop(\u00a00\u00a0);<\/pre>\n<p>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(\u2026)-Methode beim ResourceConfig, es ist aber auch m\u00f6glich, im Konstruktor von ResourceConfig direkt die JAW-RS-Ressourcen \u00fcber ihre Class-Objekte aufzuz\u00e4hlen.<\/p>\n<p>Die statische Methode JdkHttpServerFactory.createHttpServer(\u2026) 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.<\/p>\n<h3>REST-Services konsumieren<\/h3>\n<p>Bei createHttpServer(\u2026) ist als Testpfad &#8222;http:\/\/localhost:8080\/api&#8220; eingetragen, und zusammen mit @Path(&#8222;dating&#8220;) an der Klasse ergibt sich der Pfad http:\/\/localhost:8080\/api\/dating f\u00fcr die Ressource. Wir k\u00f6nnen die URL im Browser eintragen.<\/p>\n<h4>REST-Client mit Jersey<\/h4>\n<p>Sich im Browser das Ergebnis eines REST-Aufrufs anzuschauen, ist nicht das \u00fcbliche Szenario. Oft sind es JavaScript-Programme im Browser, die REST-Aufrufe starten und die konsumierten Daten auf einer Webseite integrieren.<\/p>\n<p>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:<\/p>\n<pre>System.out.println(\n \u00a0\u00a0ClientBuilder.newClient().target( \"http:\/\/localhost:8080\/api\" )\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 .path( \"dating\" ).request().get( String.class )\n );<\/pre>\n<p>Der API-Stil, den die Bibliotheksautoren hier verwenden, nennt sich Fluent-API; Methodenaufrufe werden verkettet, um alle Parameter wie URL oder MIME-Typ f\u00fcr die Anfrage zu setzen. Das ist eine Alternative zu den bekannten Settern auf JavaBeans bzw. einen \u00fcberladenen Konstruktor mit un\u00fcbersichtlichen Parameterlisten.<\/p>\n<p>Um die Typen bei der javax.ws.rs.client-API etwas besser zu verstehen, schreiben wir alles ganz vollst\u00e4ndig aus:<\/p>\n<pre>Client\u00a0client\u00a0=\u00a0ClientBuilder.newClient();\n WebTarget\u00a0target\u00a0=\u00a0client.target(\u00a0\"http:\/\/localhost:8080\/api\"\u00a0);\n WebTarget\u00a0resourceTarget\u00a0=\u00a0target.path(\u00a0\"dating\"\u00a0);\n Invocation.Builder\u00a0request\u00a0=\u00a0resourceTarget.request(\u00a0MediaType.TEXT_PLAIN\u00a0);\n Response\u00a0response\u00a0=\u00a0request.get();\n System.out.println(\u00a0response.readEntity(\u00a0String.class\u00a0)\u00a0);<\/pre>\n<p>Das Response-Objekt hat auch eine Methode getStatus() f\u00fcr den HTTP-Statuscode und hasEntry(), um zu pr\u00fcfen, ob es ein Ergebnis gibt und keinen Fehler. Apropos Fehler: Ist die URL falsch, gibt es eine RuntimeException \u2013 gepr\u00fcfte Ausnahmen verwendet Jersey nicht.<\/p>\n<p>Exception\u00a0in\u00a0thread\u00a0&#8222;main&#8220;\u00a0javax.ws.rs.NotFoundException:\u00a0HTTP\u00a0404\u00a0Not\u00a0Found<\/p>\n<p><a href=\"#_ftnref1\" name=\"_ftn1\"><sup>[1]<\/sup><\/a>\u00a0\u00a0 Roy Fielding meint dazu nur: \u201eFlickr obviously don\u2019t 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\u2019t know.\u201c (Quelle: http:\/\/roy.gbiv.com\/untangled\/2008\/no-rest-in-cmis).<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Dieser Abschnitt stellt das Architekturprinzip REST vor und anschlie\u00dfend 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 \u00fcber einen Webserver verf\u00fcgbar ist und eindeutig \u00fcber einen URI identifiziert wird. Da unterschieden werden muss, ob eine Ressource neu angelegt, gelesen, aktualisiert, [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_jetpack_memberships_contains_paid_content":false,"footnotes":"","_links_to":"","_links_to_target":""},"categories":[1,11],"tags":[],"class_list":["post-3923","post","type-post","status-publish","format-standard","hentry","category-allgemein","category-insel"],"jetpack_featured_media_url":"","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/posts\/3923","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/comments?post=3923"}],"version-history":[{"count":9,"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/posts\/3923\/revisions"}],"predecessor-version":[{"id":3932,"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/posts\/3923\/revisions\/3932"}],"wp:attachment":[{"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/media?parent=3923"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/categories?post=3923"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/tags?post=3923"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}