Université Virtuelle de Tunis Développement Orienté Services Services Web avec JAX-RS
Ministère de l’enseignement Supérieur, de la Recherche scientifiqu e et de la Technologie
Université Virtuelle de Tunis
Développement Orienté Services
Développement des Services Web REST avec Java : JAX-RS
1
Université Virtuelle de Tunis Développement Orienté Services Services Web avec JAX-RS
1. Introduction
Nous nous intéressons dans ce cours au développement des services Web de type REST. Côté Serveur, nous montrons le code pour le traitement du service Web et côté Client nous dévoilons le code qui permet d’appeler un service Web. La majorité des langages de programmation orientés Web supportent le développement de services Web REST : Java, PHP, C#, C++, … Nous nous limitons au langage Java dans ce cours. Comme il existe différents frameworks de développement de Services Web, ceux qui respectent la spécification JAX-RS (détailler après) et d’autres comme Apache Axis2, …
2. JAX-RS
1. Spécification
JAX-RS est l’acronyme Java API for RESTful Web Services. Elle est décrite par la JSR 311 (jcp.org/en/jsr/summary?id=311) et la version courante de la spécification est la 2.0. Depuis la version 1.1, JAX-RS fait partie intégrante de la spécification Java EE 6 au niveau de la pile des Services Web. Cette spécification décrit uniquement la mise en oeuvre des services Web REST côté serveur. Le développement des Services Web REST repose sur l’utilisation de classes Java et d’annotations.
2. Implémentation
Il existe différentes implémentations de la spécification JAX-RS :
- JERSEY : implémentation de référence fournie par Oracle (jersey.java.net) - CXF : fournie par Apache, la fusion entre XFire et Celtix (cxf.apache.org) - Spring REST : une API REST fournie par le framework Spring
(https://spring.io/guides/gs/rest-service/)
- RESTEasy : fournie par JBoss (www.jboss.org/resteasy) - RESTlet : un des premiers framework implémentant REST pour Java
(www.restlet.org)
Une étude comparative sur les performances des implémentations peut être trouvée dans spécification www.java.dzone.com/articles/jax-rs-vendor-comparisons-part. Comme JAXRS ne décrit pas la couche cliente, chaque implémentation fournit une API spécifique. Dans la suite du cours nous utiliserons l’implémentation de référence JERSEY. Sa version actuelle est la 2.5.1 qui respecte la spécification JAX-RS 2.0. Jersey est incluse dans le serveur d’application Glassfish.
la
3. Fonctionnement
Les clients peuvent être développés en des différents langages. En ce qui concerne Java, il existe différentes APIs qui gèrent la partie client. Dans l’architecture REST les services web sont utilisés en envoyant et recevant du contenu HTTP. Les requêtes sont reçus par des servlets qui vont la transmettre par la suite à l’implémentation JAX-RS. Cette dernière intègre des classes annotées implémentant le service web.
2
Université Virtuelle de Tunis Développement Orienté Services Services Web avec JAX-RS
4. Développement
Le développement de Services Web avec JAX-RS est basé sur des POJO (Plain Old Java Object) en utilisant des annotations spécifiques à JAX-RS. Il n’y a une pas une de description requise dans des fichiers de configuration, seule la configuration de la Servlet « JAX-RS » est requise pour réaliser le pont entre les requêtes HTTP et les classes Java annotées.
Un Service Web REST est déployé dans une application Web. Contrairement aux Services Web étendus il n’y a pas de possibilité de développer un service REST à partir du fichier de description WADL. Seule l’approche Bottom / Up qui est disponible : créer et annoter un POJO, compiler, déployer et tester.
Exemple : Service Web REST « HelloWorld »
package isi.rest.service; import ….
@Path("hello") public class HelloWorldRessource { @GET @Produces(MediaType.TEXT_HTML) public String sayHello() { return "Hello World"; } } L’URI @Path(“/hello”) permet de définir le chemin de la ressource hello. La lecture de cette ressource se fait grâce à une méthode de type GET de la requête HTTP. Le type MIME de la réponse est de type text/html.
3
Université Virtuelle de Tunis Développement Orienté Services Services Web avec JAX-RS
Le résultat de la requête est illustré dans la figure ci-dessus. Le retour est directement interprétable depuis le navigateur puisqu’il s’agit d’un type MIME reconnu.
La ressource en lecture au service hello est accessible via une requête HTTP à travers la méthode GET. Le retour est directement interprétable depuis un navigateur puisqu’il s’agit d’un type MIME.
<?xml version="1.0" encoding="UTF-8"?> <web-app …> <display-name>HelloWorldRessource</display-name> <servlet> <servlet-name>Jersey REST Service</servlet-name> <servletclass> com.sun.jersey.spi.container.servlet.ServletContainer </servlet-class> <init-param> <param-name>com.sun.jersey.config.property.packages</param-name> <param-value>isi.rest.service</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>Jersey REST Service</servlet-name> <url-pattern>/*</url-pattern> </servlet-mapping> </web-app> Structure d’une application web
La structure illustrée ci-dessous est celle d’une application web dynamique Java. Le répertoire « WEB- INF » comporte l’ensemble des classe java compilées .class, le répertoire « lib » contient les bibliothèques, dans le cas où le serveur d’application les intègre il suffit d’avoir le serveur dans le classpath de l’application.
3. JAX-RS par l’exemple
1. Système RESTFUL pour la gestion d’une bibliothèque
Nous allons illustrer dans ce qui suit la mise en place d’un système RESTFUL d’une bibliothèque, reposant sur un système CRUD.
4
Université Virtuelle de Tunis Développement Orienté Services Services Web avec JAX-RS
Le système dispose de deux ressources qui sont la bibliothèque et le livre. Une bibliothèque possède des livres, où il est possible d’ajouter, de mettre à jour ou de supprimer un livre. La recherche d’un livre est établie selon différents critères (JSBN, nom, …). Les données récupérées sont de types simples (String, Long, …) ou structurées. Enfin, différents formats de données peuvent être utilisés comme JSON, XML, …
2. Rappel sur le protocole HTTP
HTTP signifie Hyper Text Transfer Protocol, qui est un protocole de communication de type client/serveur sans état. Il est donc impossible de conserver des informations issues du client. La conversion est initialisée par le client via une requête HTTP, grâce à une URL qui est saisie dans le navigateur. Requête
Voici un la structure d’une requête HTTP envoyée par le client (navigateur) au serveur WWW :
<Méthode> <URI> HTTP/<Version> [<Champ d’en-tête>:<Valeur>] ... Ligne blanche [corps de la requête pour la méthode Post]
- Méthode : le type de méthode de la requête qui peut être GET, POST, …
- URI : l’adresse du document demandé qui peut être un fichier HTML, une image, … -
Version : la version du protocole HTTP utilisé qui est 1.0 ou 1.1
- Champ d’entête : dans lequel figure différentes informations, comme le navigateur,
l’utilisateur, …
- La ligne blanche est obligatoire - Corps de la requête : uniquement si la méthode est de type POST, dans lequel sont
fournies les valeurs des paramètres envoyées par un formulaire.
Entête
L’entête correspond aux formats de documents et aux paramètres pour le serveur :
- Accept : types MIME acceptés par le cilent (text/html, text/plain, …) - Accpet-Encoding : le codage accepté (compress, x-gzip, x-zip) - Accpet-Charset : le jeu de caractères préféré du client
- Accept-Language : la liste de langues (fr, en, de, …) - Authorization : le type d’autorisation : BASIC (nom :mot de passe en base 64), il est
transmis en clair et facile à décrypter
- Cookie : cookie retourné
- From : adresse email de l’utilisateur - ….
Type de méthodes
Lorsqu’un client se connecte à un serveur et envoie une requête, cette requête peut-être de plusieurs types, appelés méthodes. Deux des méthodes les plus utilisées sont GET et POST.
La requête de type GET permet d’extraire des informations comme les documents, les graphiques, … Elle intègre les données dans l’URL, qui est la chaine de l’interrogation, exemple : www.biblio/book?title=SOA&author=Xavier Fournier
5
Université Virtuelle de Tunis Développement Orienté Services Services Web avec JAX-RS
La requête de type POST permet de poster des informations secrètes, des données graphiques,… Elle est transmise dans le corps de la requête.
POST /book.php HTTP/1.1 Host: www.biblio.com User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; fr; rv:1.9.0.9) Gecko/2009040821 Firefox/3.0.9 (.NET CLR 3.5.30729) FirePHP/0.2.4 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Accept-Language: fr,fr-fr;q=0.8,en-us;q=0.5,en;q=0.3 Accept-Encoding: gzip,deflate Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7 Content-Type: application/x-www-form-urlencoded Content-Length: 40 Keep-Alive: 300 Connection: keep-alive title=SOA&author=Xavier Fournier Voici la structure réponse qui peut être envoyée par le serveur WWW au client :
HTTP/<Version><Status><Commentaire Status> Content-Type:<Type MIME du contenu> [<Champ d’en-tête>:<Valeur>] ... Ligne blanche Document Nous remarquons la présence de la version du protocole HTTP, avec cette fois le statut de la réponse liée à une erreur ou une réussite (200) et des informations (Commentaire) sur le statut OK. Le type de contenu retourné est notamment spécifié (text/html, text/plain, application/octet-stream) et enfin le document qui peut être formaté d’un code HTML ou autre.
3. Annotations de JAX-RS
@Path
Une classe Java doit être annotée par l’annotation @path pour qu’elle puisse être traitée par des requêtes HTTP. Cette annotation définit des ressources appelées racines (Root Resource Class). La valeur donnée à @path correspond à une expression URI relative au contexte de l’application web.
Exemple : http://localhost:8080/libraryrestwebservice/books
- localhost : adresse du serveur - 8080 : port
- libraryrestwebservice : contexte de l’application web - books : URI de la ressource
L’annotation @path peut également annoter des méthodes de la classe. L’URI résultante est la concaténation de l’expression du @path de la classe avec l’expression du @path de la méthode.
6
Université Virtuelle de Tunis Développement Orienté Services Services Web avec JAX-RS
Par exemple, on peut invoquer les livres prêtés d’une bibliothèque à travers l’URI /books/borrowed, comme le montre l’exemple ci-dessous :
@Path("/books") public class BookResource {
@GET public String getBooks() {
...
} @GET @Path("/borrowed")
String getBorrowedBooks() { ...
}
} Template parameters
public
La valeur définie dans @path ne se limite pas seulement aux expressions constantes. Il est aussi possibile de définir des expressions plus complexes appelées Template Parameters. Pour distinguer une expression complexe dans la valeur du @path, son contenu est délimité par {…}. Il est possible également de mixer dans la valeur de @path des expressions constantes et des expressions complexes. Les Template Parameters peuvent également utiliser des expressions régulières Exemple :
@Path("/books/") public class BookResource {
Publicité
@GET @Path("{id}") public String getBookById(@PathParam("id") int id) {
return "SOA " + id;
} @GET @Path("name-{name}-editor-{editor}") public String getBookByNameAndEditor(@PathParam("name") String name, @PathParam("editor") String editor)
return "JAX-RS (Name:" + name + " - Editor:" + editor + ")";
}
} Ainsi, on peut récupérer un livre grâce à son identifiant (méthode getBookById) avec l’URI /books/123 par exemple. Comme il est possible de récupérer un livre avec son nom et celui de son éditeur (méthode getBookByNameAndEditor) grâce à /books/name-SOA- editorDUNOD par exemple.
l’URI
Sub-resource locator
Une sub-resource locator est une méthode qui doit respecter les exigences suivantes :
- Annotée avec @Path - Non annotée avec @GET, @POST, @PUT, @DELETE
7
Université Virtuelle de Tunis Développement Orienté Services Services Web avec JAX-RS
- Retourne une sous ressource (un type Object)
L’intérêt d’utiliser une méthode sub-resource locator est de pouvoir déléguer vers une autre classe ressource. Le développement d’une sous ressource suit un schéma classique, pas d’obligation de placer une ressource racine. Une méthode sub-resource locator supporte le polymorphisme (retourne des sous types).
L’exemple suivant montre une méthode sub-resource locator (méthode getSpecificBook) dont la sous ressource est définie par SpecificBookResource. Il est donc possible de récupérer un livre de type SpecificBookResource avec son id, exemple à travers l’URI /books/specific/123.
@Path("/books/") public class BookResource {
@Path("specific") public SpecificBookResource
getSpecificBook() { SpecificBookResource();
return new
}
}
public class SpecificBookResource {
@GET @Path("{id}") public String getSpecificBookById(@PathParam("id") int id) { return ".NET platform is Bad"; }
} Traitement des données en tant qu’objets complexes
JAX-RS peut traiter des objets complexes selon deux formats XML et JSON
JAX-RS et JAXB
JAX-RS permet l’utilisation d’objets JAXB afin de manipuler des données en XML. Voici un exemple :
Méthodes HTTP à travers les annotations
L’annotation des méthodes Java permet de traiter des requêtes HTTP suivant le type de méthode (GET, POST, …). Les annotations disponibles par JAX-RS sont les suivantes : @GET, @POST, @PUT, @DELETE et @HEAD. Ces annotations ne sont utilisables que sur des méthodes Java. Le nom des méthodes Java n’a pas d’importance puisque c’est l’annotation employée qui précise où se fera le traitement. Il est possible aussi d’étendre les annotations disponibles pour gérer différents type de méthode HTTP à travers par exemple le protocole WebDav (extension au protocole HTTP pour la gestion de documents) et les méthodes supportées : PROPFIND, COPY, MOVE, LOCK, UNLOCK, …
La spécification JAX-RS, n’impose pas de respecter les conventions définies par le style REST. Il est possible donc d’utiliser une requête HTTP de type GET pour effectuer une suppression
8
Université Virtuelle de Tunis Développement Orienté Services Services Web avec JAX-RS
d’une ressource. Des opérations CRUD sur des ressources sont réalisées au travers des méthodes HTTP. Exemple : CRUD sur la ressource Livre
@Path("/books/") public class BookResource { @GET public String getBooks() {
"Cuisine et moi / JavaEE 18";
return
} @POST public String createBook(String livre) {
return livre;
} @GET @Path("{id}") public String getBookById(@PathParam("id") int id) { return "Java For Life " + id; } @PUT @Path("{id}") public void updateBookById(@PathParam("id") int id) {
...
} @DELETE @Path("{id}") public void deleteBookById(@PathParam("id") int id) {
...
}
} Dans cet exemple, getBooks permet de récupérer la liste de tous les livres, createBook crée un nouveau livre, getBooksById récupère un livre, updateBooksById permet de mettre à jour u livre et deleteBookById supprime un livre.
4. Paramètres de requêtes
JAX-RS fournit des annotations pour extraire des paramètres d’une requête. Elles sont utilisées sur les paramètres des méthodes des ressources pour réaliser l’injection du contenu. La liste des différentes annotations disponibles est la suivante :
- @PathParam : extraire les valeurs des Template Parameters - @QueryParam : extraire les valeurs des paramètres de requête - @FormParam : extraire les valeurs des paramètres de formulaire
- @HeaderParam : extraire les paramètres de l’en-tête - @CookieParam : extraire les paramètres des cookies - @Context : extraire les informations liées aux ressources de contexte
Une valeur par défaut peut être spécifiée en utilisant l’annotation @DefaultValue. Par défaut, JAX-RS décode tous les paramètres, la résolution de l’encodage se fait par l’annotation @Encoded.
9
Université Virtuelle de Tunis Développement Orienté Services Services Web avec JAX-RS
Les annotations peuvent être utilisées sur les types Java suivants :
- Les types primitifs sauf char et les classes qui les encapsulent - Toutes classes ayant un constructeur avec paramètre de type String - Toutes classes ayant la méthode statique valueOf(String) - List<T>, Set<T> et SortedSet<T>
L’annotation @PathParam est utilisée pour extraire les valeurs des paramètres contenues dans les Template Parameters.
Exemple :
Dans cet exemple, la valeur de id est injectée dans la méthode getBookId pour récupérer le livre selon cet id.
@Path("/books/") public class BookResource {
@GET @Path("{id}") public String getBookById(@PathParam("id") int id) { return "Java For Life " + id; } @GET @Path("name-{name}-editor-{editor}") public String
getBookByNameAndEditor(@PathParam("name") String name,
@PathParam("editor") String editor) return "Name:" + name + " - Editor:" + editor;
}
} L’annotation @QueryParam est utilisée pour extraire les valeurs des paramètres contenues d’une requête quel que soit son type de méthode HTTP.
Exemple :
Dans cet exemple, des valeurs par défaut peuvent être injectées si les valeurs des paramètres ne sont pas fournies. Un exemple d’URI de cette ressource : /books/queryparameters?name=SOA&isbn=1-111111-11&isExtended=false
@Path("/books/") public class BookResource {
@GET @Path("queryparameters") public String getQueryParameterBook(
@DefaultValue("all") @QueryParam("name") String name, @DefaultValue("?-???????-?") @QueryParam("isbn") String isbn, @DefaultValue("false") @QueryParam("isExtended") boolean isExtented)
return name + " " + isbn + " " + isExtented;
{
}
10
Université Virtuelle de Tunis Développement Orienté Services Services Web avec JAX-RS
} L’annotation @FormParam est utilisée pour extraire les valeurs des paramètres contenues dans un formulaire. Le type de contenu doit être application/x-www-form-urlencoded. Cette annotation est très utile pour extraire les informations d’une requête POST d’un formulaire HTML.
Exemple :
@Path("/books/") public class BookResource { @POST @Path("createfromform") @Consumes("application/x-www-form-urlencoded") public String createBookFromForm(@FormParam("name") String name) { System.out.println("BookResource.createBookFromForm()");
return name;
}
} L’annotation @HeaderParam est utilisée pour extraire les valeurs des paramètres contenues dans l’en-tête d’une requête.
Exemple :
@Path("/books/") public class BookResource {
@GET @Path("headerparameters") public String getHeaderParameterBook(
@DefaultValue("all") @HeaderParam("name") String name, @DefaultValue("?-???????-?") @HeaderParam("isbn") String isbn, @DefaultValue("false") @HeaderParam("isExtended") Boolean
return name + " " + isbn + " " + isExtented;
isExtented) { }
} L’annotation @Context permet d’injecter des objets liés au contexte de l’application. Les types d’objets supportés sont les suivants :
- UriInfo : informations liées aux URIs - Request : informations liées au traitement de la requête
- HttpHeaders : informations liées à l’en-tête - SecurityContext : informations liées à la sécurité
Certains de ces objets permettent d’obtenir les mêmes informations que les précédentes annotations liées aux paramètres.
Publicité
Un objet de type UriInfo permet d’extraire les informations « brutes » d’une requête HTTP. Les principales méthodes sont les suivantes :
11
Université Virtuelle de Tunis Développement Orienté Services Services Web avec JAX-RS
- String getPath() : chemin relatif de la requête - MultivaluedMap<String, String> getPathParameters() : valeurs des paramètres de la
requête contenues dans Template Parameters.
- MultivaluedMap<String, String> getQueryParameters() : valeurs des paramètres de la
requête
- URI getBaseUri() : chemin de l’application - URI getAbsolutePath() : chemin absolu (base + chemins) - URI getRequestUri() : chemin absolu incluant les paramètres
Exemple : accéder aux informations d’une requête via UriInfo http://localhost:8080/restws/books/informationfromuriinfo/test?toto=ddd
@Path("/books/") public class BookResource {
@GET @Path("informationfromuriinfo/{name}") getInformationFromUriInfo(@Context UriInfo uriInfo,
public String
@PathParam("name") String name) {
System.out.println("getPath(): " + uriInfo.getPath()); List<PathSegment> pathSegments = uriInfo.getPathSegments();
MultivaluedMap<String, String> pathParameters =
uriInfo.getPathParameters();
MultivaluedMap<String, String> queryParameters =
uriInfo.getQueryParameters();
System.out.println("getAbsolutePath(): " +uriInfo.getAbsolutePath()); System.out.println("getBaseUri(): " + uriInfo.getBaseUri()); System.out.println("getRequestUri(): " + uriInfo.getRequestUri()); return ""; }
}
Un objet de type HttpHeader permet d’extraire les informations contenues dans l’en-tête d’une requête. Les principales méthodes sont les suivantes :
- Map<String, Cookie> getCookies() : les cookies de la requête - Locale getLanguage() : le langue de la requête - MultivaluedMap<String, String> getRequestHeaders() : valeurs des paramètres de
l’entête de la requête
- MediaType getMediaType() : le type MIME de la requête
A noter que ces méthodes permettent d’obtenir le même résultat que les annotations @HeaderParam et @CookieParam.
12
Université Virtuelle de Tunis Développement Orienté Services Services Web avec JAX-RS
@Path("/books/") public class BookResource {
@GET @Path("informationfromhttpheaders/{name}") public String
getInformationFromHttpHeaders(@Context HttpHeaders httpheaders) {
Map<String, Cookie> cookies = httpheaders.getCookies(); Set<String> currentKeySet = cookies.keySet();
for (String currentCookie : currentKeySet) { System.out.println(currentCookie); }
httpheaders.getRequestHeaders();
MultivaluedMap<String, String> requestHeaders =
Set<String> requestHeadersSet = requestHeaders.keySet(); for (String currentHeader : requestHeadersSet) {
System. out .println(currentHeader);
} return "" ;
}
}
L’annotation @Consumes est utilisée pour spécifier le ou les d’une ressource peut accepter. L’annotation @Produces est utilisée pour spécifier le ou les types MIME qu’une méthode d’une ressource peut produire. Il est possible de définir un ou plusieurs types MIME. Ces annotations peuvent être portées sur une classe ou sur une méthode. L’annotation sur la méthode surcharge celle de la classe. Si ces annotations ne sont pas utilisées tous types MIME pourront être acceptés ou produits. La liste des constantes des différents types MIME est disponible dans la classe MediaType. Exemple : Gestion de type MIME
types MIME qu’une méthode
Type MIME accepté par le client :
GET /books/details/12 HTTP/1.1 Host: localhost Accept: text/html
Type MIME du contenu retourné s’accorde par rapport à ce qui est supporté par le client :
HTTP/1.1 200 OK Date: Wed, 05 January 2010 14:44:55 GMT Server: Jetty(6.1.14) Content-Type: text/html <html> <title>Details</title> <body>
13
Université Virtuelle de Tunis Développement Orienté Services Services Web avec JAX-RS
<h1>Ce livre est une introduction sur la vie</h1> </body> </html>
Dans le code qui suit, le chemin des trois méthodes est identique. Le choix de la méthode déclenchée dépend du type MIME supporté par le client :
@Path("/books/") public class BookResource {
@GET @Path("details/{id}") @Produces(MediaType.TEXT_PLAIN) public String getDetailTextBookId(@PathParam("id") String id) { return "Ce livre est une introduction sur la vie";
}
@GET
@Path("details/{id}") @Produces(MediaType.TEXT_XML) public return
String getDetailXMLBookId(@PathParam("id") String id) { "<?xml version=\"1.0\"?>" + "<details>Ce livre est une
"; > < introduction sur la v ie " + " /details
} @ GET @ Path( "details/{id}" ) @ Produces(M ediaType.TEXT_HTML) public String getDetailHTMLBookId( @ PathParam( "id" ) String id) {
return "<html> " + "<title>" + "Details" + "</title>" + "<body><h1>"
+ "Ce livre e st une introduction sur la vie " + " /body></h 1> " < /h tml> ";
<
" +
}
}
5. Gestion du contenu Précédemment nous sommes focalisés sur les informations contenues dans l’en-tête d’une requête. JAX-RS permet également de manipuler le contenu du corps d’une requête et d’une réponse. Il peut ainsi automatiquement effectuer des opérations de sérialisation et désérialisation vers un type Java spécifique :
- */* : byte[] - text/* : String - text/xml, application/xml, application/*+xml : JAXBElement - application/x-www-form-urlencoded : MultivalueMap<String,String>
Dans la suite nous montrerons des exemples côté serveur qui illustrent la manipulation des types Java.
Exemple : Requête et réponse avec un flux d’entrée
14
Université Virtuelle de Tunis Développement Orienté Services Services Web avec JAX-RS
@Path("/contentbooks/") public class BookResource {
@PUT @Path("inputstream") public void updateContentBooksWithInputStream(InputStream is) throws IOException {
byte[] bytes = readFromStream(is); String input = new String(bytes); System.out.println(input);
} private byte[] readFromStream(InputStream stream) throws IOException { ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buffer = new byte[1000]; int wasRead = 0;
(wasRead > 0) { baos.write(buffer, 0, wasRead); }
wasRead = stream.read(buffer);
do {
if
} while (wasRead > -1);
return baos.toByteArray();
}
@Path("inputstream") @GET @Produces(MediaType.TEXT_XML) public InputStream
getContentBooksWithInputStream() throws FileNotFoundException {
return new FileInputStream( "c: \ \ example.xml" ) ;
}
} Exemple : Requête et réponse avec un fichier
Dans cet exemple, JAX-RS crée un fichier temporaire à partir du fichier donné (méthode getContentBooksWithFile)
@Path("/contentbooks/") public class BookResource { @Path("file") @PUT
Publicité
public void updateContentBooksWithFile(File file) throws IOException {
byte[] bytes = readFromStream(new FileInputStream(file));
String input = new String(bytes); System.out.println(input);
} @Path("file") @GET @Produces(MediaType.TEXT_XML) public File getContentBooksWithFile() { File file = new File("c:\\example.xml");
return file;
15
Université Virtuelle de Tunis Développement Orienté Services Services Web avec JAX-RS
}
} Exemple : Requête et réponse avec un String
@Path("/contentbooks/") public class BookResource {
@PUT public void updateContentBooksWithString(String current) throws IOException
{
System.out.println(current);
} @Path("string") @GET @Produces(MediaType.TEXT_XML) public String getContentBooksWithString() {
return "<?xml version=\"1.0\"?>" + "<details>Ce livre est une
introduction sur la vie" + "</details>";
}
}
Actuellement nous avons employé les types disponibles fournis par Java. JAX-RS offre la possibilité d’utiliser directement des types personnalisés en s’appuyant sur la spécification JAXB. Ce dernier JAXB est défini par la JSR 222, qui est une spécification permettant de mapper des classes Java en XML et en XML Schema. L’avantage est de pouvoir manipuler directement des objets Java sans passer par une représentation abstraite XML. Chaque classe est annotée pour décrire la mapping entre l’XML Schema et les informations de la classe : XmlRootElement, XmlElement, XmlType, …
JAX-RS supporte la sérialisation et la dé-sérialisation de classes qui sont annotées par @XmlRootElement, @XmlType ou « enveloppées » par un objet JAXBElement. Le format du contenu d’une requête et d’une réponse peut être représenté par de l’XML ou du JSON. Ces formes de contenu sont définies par les annotations @Produces et @Consumes, qui peuvent être de type :
- XML : text/xml, application/xml, application/*+xml - JSON : application/json
La manipulation de types personnalisés oblige de préciser dans le service le type MIME à traiter et à retourner.
JAX-RS et JAXB
Exemple : mise à jour d’un livre (format XML)
Dans cet exemple, l’annotation JAXB XmlRootElement(name = "book") définit l’élément racine de l’arbre XML.
16
Université Virtuelle de Tunis Développement Orienté Services Services Web avec JAX-RS
@XmlRootElement(name = "book") @XmlAccessorType(XmlAccessType.FIELD) public class Book {
@XmlElement protected String name;
@XmlElement protected String isbn; public String getName() { return name;
} public void setName(String name) { this.name = name; } public String getIsbn() { return isbn;
} public void setIsbn(String isbn) { this.isbn = isbn; } public String toString() { return name;
}
} Dans la ressource BookResource, le type MIME retourné par le service (méthode getContentBookWithJAXBXML) permet de connaître le format à traiter.
@Path("/contentbooks/") public class BookResource {
@Path("jaxbxml") @Consumes("application/xml") @PUT public void updateContentBooksWithJAXBXML(Book current) throws IOException
{ current.getIsbn());
System.out.println("Name: " + current.getName() + ", ISBN: " +
} @Path("jaxbxml") @GET @Produces("application/xml") public Book getContentBooksWithJAXBXML() {
Book current = new Book();
current.setIsbn("123-456-789"); current.setName("SOA");
return
current; }
} L’URL /contentbooks/jaxbxml rend le résultat suivant :
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <xs:schema version="1.0" xmlns:xs="http://www.w3.org/2001/XMLSchema">
17
Université Virtuelle de Tunis Développement Orienté Services Services Web avec JAX-RS
<xs:element name="book" type="book"/>
<xs:complexType name="book"> <xs:sequence> <xs:element name="name" type="xs:string"/> <xs:element name="isbn" type="xs:string"/> </xs:sequence> <xs:complexType> </xs:schema>
Exemple : Mise à jour d’un livre (JAXBElement et format XML)
Dans cet exemple, un objet JAXBElement est utilisé pour envelopper le type Book. L’accès direct à l’objet Book se fait par la méthode getValue().
@Path("/contentbooks") public class BookResource {
@Path("jaxbxml") @Consumes("application/xml") @POST public void updateContentBooksWithJAXBElementXML(JAXBElement<Book>
currentJAXBElemnt) {
Book current = currentJAXBElemnt.getValue(); System.out.println("Name: " + current.getName() + ", ISBN: " +
current.getIsbn());
}
} Lors de l’envoie de la réponse au client un code statut est retourné. Les statuts des réponses sans erreur s’échelonnent de 200 à 399. Le code est 200 « OK » pour les services retournant un contenu non vide. Le code est 204 « No Content » pour les services retournant un contenu vide. Pour les réponses avec erreur, leurs statuts s’échelonnent de 400 à 599. Pour une ressource non trouvée, le code de retour est 404 « Not Found ». Pour un type MIME en retour non supporté, le code retourné est 406 « Not Acceptable ». Enfin pour une méthode HTTP non supportée, le code retourné est 405 « Method Not Allowed ».
JAX-RS et JSON
JAX-RS permet de manipuler des données en JSON grâce à des bibliothèques tierces telles JAXB, Jackson,… Dans l’exemple qui suit nous exploitons le même objet Book qui est mappé un XSD grâce à JAXB. Cette approche permet donc d’utiliser le même objet pour un rendu en XML ou JSON :
@Path("/contentbooks/") public class BookResource {@Path("jaxbxml")
@Path("jaxbjson") @GET
18
Université Virtuelle de Tunis Développement Orienté Services Services Web avec JAX-RS
@Produces("application/json") public Book getContentBooksWithJAXBJSON() {
Book current = new Book();
current.setIsbn("123-456-789"); current.setName("SOA");
return
current; }
} Le rendu JSON est le suivant :
{ "name":"JAX-RS", "isbn":"Queen size mattress" }
Pour utiliser à la fois XML et JSON : @Produces({"application/json","application/xml"). Sachant que par défaut c’est le rendu XML qui sera utilisé, dans ce cas il faut que le client spécifie dans le header de la requête http le format JSON : Accept: application/json
6. Réponse
Actuellement, tous les services développés retournaient soit un type void soit un type Java défini par le développeur. JAX-RS facilite la construction de réponses en permettant de choisir un code de retour, de fournir des paramètres dans l’en-tête, de retourner une URI, … Les réponses complexes sont définies par la classe Response, qui dispose de méthodes abstraites non utilisables directement. La méthode Object getEntity() retourne le corps de la réponse, la méthode int getStatus() retourne un code et la méthode MultivalueMap<String, Object> getMetaData() retourne les données de l’en-tête. Les informations de ces méthodes sont obtenues par des méthodes statiques retournant des ResponseBuilder. Il est aussi possible d’utiliser un patron de conception Builder.
Les principales méthodes de la classe Response sont les suivantes :
- ResponseBuilder created(URI location) : Modifie la valeur de Location dans l’en-tête, à
utiliser pour une nouvelle ressource créée
- ResponseBuilder notModified() : Statut à « Not Modified » - ResponseBuilder ok() : Statut à « Ok » - ResponseBuilder serverError() : Statut à « Server Error » - ResponseBuilder status(Response.Status) : défini un statut particulier défini dans
Response.Status
- …
Les principales méthodes de la classe ReponseBuilder sont les suivantes :
- Response build() : crée une instance - ResponseBuilder entity(Object value) : modifie le contenu du corps - ResponseBuilder header(String, Object) : modifie un paramètre de l’en-tête
19
Université Virtuelle de Tunis Développement Orienté Services Services Web avec JAX-RS
Exemple : Préciser le code de retour et ajouter des informations dans l’entête de la réponse.
@Path("/contentbooks") public class BookResource {
@Path("response") @GET public Response getBooks() { return Response .status(Response.Status.OK) .header("param1", "Bonjour") .header("param2", "Hello") .entity(new Book("JAX-RS","1-1111-11