vendredi 17 avril 2009

JSR 311 : écrire des services RESTful avec Jersey - Partie 1

Comme nous l'avons vu dans cet article, la JSR 311 fait maintenant partie de Java EE 6.
Le but de cette spécification est d'offrir une interface de programmation permettant d'écrire et d'exposer des services de type RESTful.
Ce petit tutoriel présente les cas d'usage de cette API et les facilités qu'elle offre aux développeurs Java.
Dans cet exemple nous utiliserons l'implémentation de référence de SUN : Jersey.


Services RESTful

Pour commencer, qu'est ce qu'un service RESTful?
Un service RESTful est un service web basé sur le style d'architecture REST.
REST (Representational State Transfer) est un modèle d'architecture orienté ressources. Ceci signifie qu'au lieu d'exposer des méthodes comme lorsque l'on utilise le protocole SOAP, on va exposer des ressources. Chaque ressource possède une URL qui l'identifie.

Contrairement à SOAP, REST s'appuie uniquement sur le protocole HTTP et ne propose aucune couche au dessus de ce protocole. Tout est faisable à partir des opérations fournies par de base par HTTP : GET, PUT, POST, DELETE, etc.
Pour récupérer une collection, on effectue un GET sur l'URL appropriée.
La réponse contiendra un ensemble d'éléments, décris par exemple en XML ou en JSON. Pour chaque élément, une URL est définie. Il sera donc possible d'effectuer un appel GET sur élément en particulier pour ne récupérer que celui ci. Une opération de type PUT sur le même élement permettra de mettre à jour ses données. De la même façon, une opération DELETE supprimera l'élément.

REST est en fait le modèle sur lequel le web lui même est construit : les sites et les pages web étant des ressources accessibles via des URL, depuis un navigateur grâce à des opérations HTTP.

Pour la sécurité il est possible de s'appuyer sur l'autentification HTTP, ou encore sur le SSL avec HTTPS. Comme vous pouvez le voir, tout est fait pour utiliser au maximum ce que le web nous fournit depuis toujours, sans sur-couche supplémentaire.

Mais revenons plutôt à notre JSR 311 et à Jersey. Grâce à cette librairie, nous allons pouvoir écrire et exposer des services RESTful très facilement en utilisant des annotations.


Premier service de test

Le premier service que nous allons écrire renverra simplement une chaine"Hello World".
Voici le code nécessaire à l'exposition d'un tel service :

@Path("/test")
public class TestResource {

@GET
@Produces("text/html")
public String getMessage( ) {
return "hello";
}
}


En appelant l'URL http://votreServeur/votreProjet/test, vous obtiendrez une page affichant le message hello.

L'annotation @Produces permet de spécifier le type MIME de sortie, c'est à dire le contenu de la réponse HTTP. Ceci peut etre du HTML, du XML, l'url d'une image...

Récupérer une collection

Maintenant nous allons voir comment récupérer une collection d'objets à partir d'une URL.
Imaginons que nous disposions d'un annuaire de personne. Nous allons construire un service permettant de récupérer une liste de personnes à partir d'une ville.

La classe Person est un simple POJO auquel on rajoute l'annotation JAXB @XMLRootElement afin qu'il puisse être sérialisé en XML.

@XmlRootElement
public class Person {

private String name;

private String town;

public Person() {
}

public Person(String name) {
this.name = name;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public String getTown() {
return town;
}

public void setTown(String town) {
this.town= town;
}

}


URLs statiques

Cette première approche permet de définir un contenu en fonction d'une URL que l'on connait à l'avance. On définit ici une méthode renvoyant la liste des habitants de Paris.
@Path("/directory")
public class Directory{

@Path("/Paris")
@Produces("application/xml")
@GET
public List<Person> getPersonsByTown(){
List<Person> persons = new ArrayList<Person>();
persons.add(directoryService.getPersonsByTown("Paris"));
return persons;
}
}
Ce service sera accessible à l'URL http://votreServeur/votreProjet/directory/Paris

L'inconvénient de cette approche est qu'il faudrait créer une méthode pour chaque ville...
Voyons comment remédier à cette limitation.

URLs dynamiques

Cette fois, nous allons donner la possibilité à l'utilisateur d'ajouter la ville de son choix dans l'URL.

@Path("/directory")
public class Directory{

@Path("{town}")
@Produces("application/xml")
@GET
public List<Person> getPersonsByTown(@PathParam("town")String town){
List<Person> persons = new ArrayList<Person>();
persons.add(directoryDAO.getPersonsByTown(town));
return persons;
}
}


L'annotation @Path("{town}") et l'annotation @PathParam("town") permettent de passer un paramètre dans l'URL du service, qui sera redirigé dans la méthode Java.
Dans cet exemple, le nom de la ville sera passé dans l'URL pour filtrer le contenu de la réponse.
On peut donc appeler le service via l'URL http://votreServeur/votreProjet/directory/Paris, http://votreServeur/votreProjet/directory/Marseille, etc.


MAJ : si vous avez des problèmes pour faire fonctionner jersey sur un conteneur de Servlet comme Tomcat ou App Engine, regardez les commentaires (merci à ce lecteur anonyme pour avoir remonté cette difficulté).

Dans le prochain billet, nous verrons comment mettre à jour notre collection en ajoutant de nouveaux éléments et en modifiant des entrées existantes.