I. Exercice 1 - Développer un Service Web REST : Bonjour ENSMA▲
II-A. But▲
- Développer un service web REST à partir d'une classe Java.
- Déployer sous Glashfish, tester le service web avec CURL.
- Tester le service web avec SOAP-UI.
- Tester le service web avec les outils de Netbeans.
II-B. Description▲
Le service Web de type REST de ce premier exercice consiste à fournir un accès à la ressource Bonjour ENSMA qui permet de donner des messages différents selon la date du jour. Les services proposés n'autorisent que la lecture à la ressource (GET). Ces services de récupération peuvent posséder des paramètres. Enfin, le format de récupération de la ressource est du XML.
II-C. Étapes à suivre▲
- Démarrer l'environnement de développement NetBeans.
- Créer un nouveau projet File -> New Project... puis Java Web et choisir Web Application, faire Next.
- Dans le nom du projet choisir le nom HelloENSMAWebServiceRestExercice1.
- Comme type de serveur d'application, choisir GlassFish 3 et comme version Java EE choisir la version 7 puis faire Next.
- Ne rien choisir dans les options de Frameworks, puis faire Finish. L'initialisation d'un projet Web est en cours.
- Créer un service Web REST à partir de l'assistant de création Web Services -> RESTful Web Services from Patterns puis faire Next.
- Choisir par la suite, l'option Simple Root Resource puis faire Next. Dans le champ Resource Package définir la valeur soa.jaxrslabs.helloensmawebservicerestexercice1, dans le champ Path saisir la valeur helloensma, dans le champ Class Name saisir la valeur HelloEnsma, puis faire Finish.
- Par la suite, il est demandé la manière dont les ressources sont gérées par l'application, choisir la troisième proposition (Create default REST servlet adaptor in web.xml). Notez que pour accéder à toutes les ressources il faudra préfixer les chemins par /resources.
- Depuis la nouvelle classe créée HelloEnsma, supprimer la méthode correspondant à la création d'une ressource HelloEnsma (méthode putXml).
- Dans le corps de la méthode String getXml() utilisée pour récupérer la ressource Bonjour ENSMA, recopier le contenu suivant
2.
3.
4.
5.
@GET
@Produces
(
"application/xml"
)
public
String getXml
(
) {
return
"<bonjour>Bonjour ENSMA</bonjour>"
;
}
- Faire un Clean and Build à partir du projet HelloENSMAWebServiceRestExercice1 et s'assurer que le projet se construit correctement.
- Faire un Deploy à partir du projet. Le serveur Glassfish doit démarrer. Sur la console de Glassfish, un Web Service doit avoir été découvert, un message similaire doit être présent :
- Ouvrir une fenêtre d'un navigateur Web et tester la récupération de la ressource Bonjour ENSMA (requête GET via l'URL http://localhost:8080/HelloENSMAWebServiceRestExercice1/resources/helloensma).
Ce premier service REST n'est pas parfait puisque le type de retour est une simple chaîne de caractères. Comment pourrions-nous retourner à la fois une information, un code de statut ou des informations dans l'en-tête de la réponse ? Pour cela, nous allons utiliser un objet Response pour le retour des prochains services REST.
- Ajouter une nouvelle méthode Java getXmlWithParams dans la classe HelloEnsma qui permet de récupérer la ressource Bonjour ENSMA via des paramètres de requêtes. Ces paramètres seront donnés via le chemin et via l'en-tête de la requête en utilisant respectivement les annotations @PathParam et @HeaderParam. Recopier le code suivant :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
@GET
@Path
(
"{id}"
)
@Produces
(
"application/xml"
)
public
Response getXmlWithParams
(
@PathParam
(
"id"
) String id, @DefaultValue
(
"all"
) @HeaderParam
(
"name"
) String name) {
System.out.println
(
id);
System.out.println
(
name);
return
Response
.status
(
Status.OK)
.entity
(
"<bonjour>Bonjour ENSMA de la part de "
+
name +
"</bonjour>"
)
.build
(
);
}
- Saisir l'URL suivante depuis votre navigateur Web pour tester la récupération de la ressource Bonjour ENSMA : http://localhost:8080/HelloENSMAWebServiceRestExercice1/resources/helloensma/27022011. Le message au format XML est retourné au navigateur.
- Pour envoyer des paramètres au niveau de l'en-tête de la requête, nous utiliserons l'outil CURL qui est un outil en ligne de commande pour construire des requêtes HTTP. Télécharger et installer l'outil à partir de l'URL suivante : http://curl.haxx.se/.
- Depuis une invite de commande saisir la commande suivante :
- À partir d'un navigateur Web, afficher le contrat de description WADL via l'URL suivante : http://localhost:8080/HelloENSMAWebServiceRestExercice1/resources/application.wadl.
- Utiliser l'outil de test de service Rest fourni par Netbeans (menu contextuel du projet et choisir l'élément Test RESTful Web Services).
- Envoyer une requête HTTP de type GET pour récupérer une ressource Bonjour ENSMA. Noter qu'il n'est pas possible avec cet outil de modifier l'en-tête d'une requête HTTP.
- Télécharger et installer l'outil SOAP-UI à partir de l'URL suivante : http://www.soapui.org/. Ouvrir SOAP-UI et créer un projet à partir de ce contrat de description.
- Envoyer une requête HTTP de type GET pour récupérer une ressource Bonjour ENSMA. Modifier cette requête de façon à transmettre également les paramètres via le chemin et via l'en-tête de la requête HTTP.
II. Exercice 2 - Développer un Service Web REST : Interrogation et réservation de train▲
III-A. But▲
- Développer un service web REST à partir d'une classe Java.
- Utilisation d'un Sub-Resource Locator.
- Mise en place d'un CRUD complet.
- Déployer sous GlassFish et tester le service web avec CURL.
III-B. Description▲
Le service Web REST de ce deuxième exercice consiste à créer un système CRUD pour l'interrogation et la réservation de trains. Les ressources manipulées par les services sont donc un train et une réservation. Le service Web REST doit pouvoir lister l'ensemble des trains, lister les trains qui satisfont un critère de recherche (ville de départ, ville d'arrivée, jour de départ et un intervalle de temps), de créer, de modifier, de supprimer et de lister une réservation pour un client donné. Nous insisterons sur l'accessibilité des services et non sur le code métier.
III-C. Étapes à suivre▲
- Créer un nouveau projet File -> New Project... puis Java Web et choisir Web Application, faire Next.
- Dans le nom du projet choisir le nom BookTrainWebServiceRestExercice2.
- Comme type de serveur d'application, choisir GlassFish 3 et comme version Java EE choisir la version 6 puis faire Next.
- Ne rien choisir dans les options de Frameworks, puis faire Finish. L'initialisation d'un projet Web est en cours.
- Créer la classe Train (dans le package soa.jaxrslabs.booktrainwebservicerestexercice2) qui modélise le concept de Train et qui contient un attribut String numTrain (identifiant fonctionnel d'un train), un attribut String villeDepart (la ville de départ du train), un attribut String villeArrivee (la ville d'arrivée du train), un attribut int heureDepart (heure de départ depuis la ville de départ). Ajouter des modificateurs et des accesseurs sur tous les attributs. Voir le code suivant
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
@XmlRootElement
(
name =
"train"
)
public
class
Train {
private
String numTrain;
private
String villeDepart;
private
String villeArrivee;
private
int
heureDepart; // Format : 1230 = 12h30
public
Train
(
) {
}
public
Train
(
String numTrain, String villeDepart, String villeArrivee, int
heureDepart) {
this
.numTrain =
numTrain;
this
.villeDepart =
villeDepart;
this
.villeArrivee =
villeArrivee;
this
.heureDepart =
heureDepart;
}
public
int
getHeureDepart
(
) {
return
heureDepart;
}
public
void
setHeureDepart
(
int
heureDepart) {
this
.heureDepart =
heureDepart;
}
... // A compléter...
}
- Créer la classe BookTrainBD qui sert à persister toutes les informations concernant le service Web de cet exercice.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
public
class
BookTrainBD {
private
static
List<
Train>
trains =
new
ArrayList<
Train>(
);
static
{
trains.add
(
new
Train
(
"TR123"
, "Poitiers"
, "Paris"
, 1250
));
trains.add
(
new
Train
(
"TR127"
, "Poitiers"
, "Paris"
, 1420
));
trains.add
(
new
Train
(
"TR129"
, "Poitiers"
, "Paris"
, 1710
));
}
public
static
List<
Train>
getTrains
(
) {
return
trains;
}
}
- Créer la classe TrainResource (dans le package soa.jaxrslabs.booktrainwebservicerestexercice2) permettant l'accès aux services Web REST de la ressource Train. Définir comme chemin de ressource racine la valeur /trains (utilisation de l'annotation @Path) puis ajouter trois méthodes qui permettent respectivement de retourner la liste des trains, un train en fonction de son identifiant fonctionnel et une recherche de trains par critères passés en paramètre de la requête (ville de départ, ville d'arrivée et heure de départ). Pour cette dernière méthode, le sous-chemin associé est /search. Noter que le format de retour des services est du XML.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
@Path
(
"/trains"
)
@Produces
(
"application/xml"
)
public
class
TrainResource {
public
TrainResource
(
) {
}
@GET
// Méthode HTTP utilisée pour déclencher cette méthode
public
Response getTrains
(
) {
System.out.println
(
"getTrains"
);
return
Response
.status
(
Status.OK)
.entity
(
BookTrainBD.getTrains
(
))
.build
(
);
}
@GET
// Méthode HTTP utilisée pour déclencher cette méthode
@Path
(
"numTrain-{id}"
) // Chemin suivant ce template numTrain-TR123 où TR123 est une chaine variable
public
Response getTrain
(
@PathParam
(
"id"
) String numTrain) {
System.out.println
(
"getTrain"
);
for
(
Train current : BookTrainBD.getTrains
(
)) {
if
(
numTrain.equals
(
current.getNumTrain
(
))) {
return
Response
.status
(
Status.OK)
.entity
(
current)
.build
(
);
}
}
return
Response
.status
(
Status.NO_CONTENT)
.build
(
);
}
@GET
// Méthode HTTP utilisée pour déclencher cette méthode
@Path
(
"/search"
) // Chemin suivant /trains/search pour invoquer cette méthode
public
List<
Train>
searchTrainsByCriteria
(
@QueryParam
(
"departure"
) String departure, @QueryParam
(
"arrival"
)
String arrival, @QueryParam
(
"arrivalhour"
) String arrivalHour) {
System.out.println
(
"searchTrainsByCriteria"
);
return
Response
.status
(
Status.OK)
.entity
(
BookTrainBD.getTrains
(
).subList
(
0
, 2
))
.build
(
);
}
}
À partir d'un navigateur Web invoquer les trois services définis ci-dessus.
- http://localhost:8080/BookTrainWebServiceRestExercice2/resources/trains.
- http://localhost:8080/BookTrainWebServiceRestExercice2/resources/trains/numTrain-TR123
- http://localhost:8080/BookTrainWebServiceRestExercice2/resources/trains/search?departure=poitiers&arrival=paris&arrivalhour=1250
Créer la classe BookTrain (toujours dans le package soa.jaxrslabs.booktrainwebservicerestexercice2) qui modélise le concept de réservation de Train et qui contient un attribut String numBook (identifiant fonctionnel de la réservation d'un train), un attribut Train currentTrain (association sur le train de la réservation), un attribut int numberPlaces (nombre de places réservées). Ajouter des modificateurs et des accesseurs sur tous les attributs.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
@XmlRootElement
(
name =
"booktrain"
)
public
class
BookTrain {
private
String bookNumber;
private
Train currentTrain;
private
int
numberPlaces;
public
String getBookNumber
(
) {
return
bookNumber;
}
public
void
setBookNumber
(
String bookNumber) {
this
.bookNumber =
bookNumber;
}
public
Train getCurrentTrain
(
) {
return
currentTrain;
}
...
}
Compléter la classe BookTrainBD de façon à persister les informations concernant les services liés à la réservation de train.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
public
class
BookTrainBD {
private
static
List<
Train>
trains =
new
ArrayList<
Train>(
);
private
static
List<
BookTrain>
bookTrains =
new
ArrayList<
BookTrain>(
);
static
{
trains.add
(
new
Train
(
"TR123"
, "Poitiers"
, "Paris"
, 1250
));
trains.add
(
new
Train
(
"TR127"
, "Poitiers"
, "Paris"
, 1420
));
trains.add
(
new
Train
(
"TR129"
, "Poitiers"
, "Paris"
, 1710
));
}
public
static
List<
Train>
getTrains
(
) {
return
trains;
}
public
static
List<
BookTrain>
getBookTrains
(
) {
return
bookTrains;
}
}
Créer la classe BookTrainResource (dans le package soa.jaxrslabs.booktrainwebservicerestexercice2) permettant l'accès aux services Web REST de la ressource Réservation. Quatre méthodes sont à définir. La première createBookTrain est invoquée pour la création d'une ressource Réservation (méthode POST). La deuxième getBookTrains (méthode GET) est utilisée pour lister l'ensemble des réservations. La troisième getBookTrain (méthode GET) permet de retourner les informations d'une réservation à partir d'un numéro de réservation. Finalement removeBookTrain (méthode DELETE) permet de supprimer une réservation.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
public
class
BookTrainResource {
@POST
// Méthode HTTP utilisée pour déclencher cette méthode
public
Response createBookTrain
(
String numTrain, int
numberPlaces) {
Train currentTrain =
null
;
for
(
Train current : BookTrainBD.getTrains
(
)) {
if
(
current.getNumTrain
(
).equals
(
numTrain)) {
currentTrain =
current;
}
}
if
(
currentTrain ==
null
) {
return
Response
.status
(
Status.NO_CONTENT)
.build
(
);
}
BookTrain newBookTrain =
new
BookTrain
(
);
newBookTrain.setNumberPlaces
(
numberPlaces);
newBookTrain.setCurrentTrain
(
currentTrain);
newBookTrain.setNumBook
(
Long.toString
(
System.currentTimeMillis
(
)));
BookTrainBD.getBookTrains
(
).add
(
newBookTrain);
return
Response
.status
(
Status.OK)
.entity
(
newBookTrain.getNumBook
(
))
.build
(
);
}
@GET
// Méthode HTTP utilisée pour déclencher cette méthode
public
Response getBookTrains
(
) {
System.out.println
(
"getBookTrains"
);
return
Response
.status
(
Status.OK)
.entity
(
BookTrainBD.getBookTrains
(
))
.build
(
);
}
@GET
// Méthode HTTP utilisée pour déclencher cette méthode
@Path
(
"{id}"
) // Chemin de façon à intégrer un template parameter (id)
public
Response getBookTrain
(
@PathParam
(
"id"
) String bookNumber) {
List<
BookTrain>
bookTrains =
BookTrainBD.getBookTrains
(
);
for
(
BookTrain current : bookTrains) {
if
(
current.getNumBook
(
).equals
(
bookNumber)) {
return
Response
.status
(
Status.OK)
.entity
(
current)
.build
(
);
}
}
return
Response
.status
(
Status.NO_CONTENT)
.build
(
);
}
@DELETE
// Méthode HTTP utilisée pour déclencher cette méthode
@Path
(
"{id}"
) // Chemin de façon à intégrer un template parameter (id)
public
Response removeBookTrain
(
@PathParam
(
"id"
) String bookNumber) {
BookTrain currentBookTrain =
null
;
for
(
BookTrain current : BookTrainBD.getBookTrains
(
)) {
if
(
current.getNumBook
(
).equals
(
bookNumber)) {
currentBookTrain =
current;
}
}
return
Response.status
(
Status.ACCEPTED).build
(
);
}
}
L'accès à la ressource Réservation (via la classe BookTrainRessource) est obtenu via l'utilisation d'un sub-resource locator. Compléter la classe TrainResource.
2.
3.
4.
5.
6.
7.
8.
9.
10.
public
class
TrainResource {
public
TrainResource
(
) {
}
@Path
(
"/booktrains"
) // Chemin suivant /trains/booktrains pour invoquer cette méthode
public
BookTrainResource getBookTrainResource
(
) {
return
new
BookTrainResource
(
);
}
}
À partir de CURL et SOAP-UI, invoquer chaque service (création d'une réservation, liste les réservations, obtenir les informations d'une réservation et supprimer la réservation créée).
III. Exercice 3 - Développer un client de Service Web REST : Interrogation et réservation de train▲
IV-A. But▲
- Développer un client de service web REST à partir de l'API JERSEY.
- Création des requêtes via le patron Builder.
IV-B. Description▲
Ce troisième exercice se propose de fournir un client pour l'accès au service Web REST défini dans l'exercice 2. Une interface graphique en Java/Swing permet de contrôler les appels aux différents services.
IV-C. Étapes à suivre▲
Créer un nouveau projet File -> New Project... puis Java et choisir Java Application, faire Next.
Pour le nom du projet choisir le nom BookTrainClientWebServiceRestExercice3.
Pour le nom de la classe principale choisir soa.jaxrslabs.booktrainclientwebservicerestexercice3.BookTrainClientMain (Create Main Class).
Ajouter les bibliothèques Jersey (JAX-RS RI) et la bibliothèque décrivant l'API JAX-RS.
Compléter la classe BookTrainClientMain à partir du code ci-dessous. Noter que les traitements des trois boutons seront à compléter par la suite (callGetTrains(), callGetBookTrains(), createBookTrains())
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
public
class
BookTrainClientMain extends
JFrame {
public
BookTrainClientMain
(
) {
super
(
"Client Réservation Train"
);
this
.initializeService
(
);
this
.setLayout
(
new
GridLayout
(
3
,1
,10
,10
));
JPanel panelTrains =
new
JPanel
(
);
panelTrains.setLayout
(
new
GridLayout
(
1
,1
));
JButton getTrains =
new
JButton
(
"GetTrains"
);
panelTrains.add
(
getTrains);
getTrains.addActionListener
(
new
ActionListener
(
) {
public
void
actionPerformed
(
ActionEvent e) {
callGetTrains
(
);
}
}
);
JPanel panelBookTrains =
new
JPanel
(
);
panelBookTrains.setLayout
(
new
GridLayout
(
1
,1
));
JButton getBookTrains =
new
JButton
(
"GetBookTrains"
);
panelBookTrains.add
(
getBookTrains);
getBookTrains.addActionListener
(
new
ActionListener
(
) {
public
void
actionPerformed
(
ActionEvent e) {
callGetBookTrains
(
);
}
}
);
JPanel createBookTrainFormPanel =
new
JPanel
(
);
createBookTrainFormPanel.setLayout
(
new
GridLayout
(
1
,3
));
final
JTextField numTrain =
new
JTextField
(
);
final
JTextField numberPlaces =
new
JTextField
(
);
JButton createBookTrains =
new
JButton
(
"CreateBookTrain"
);
createBookTrains.addActionListener
(
new
ActionListener
(
) {
public
void
actionPerformed
(
ActionEvent e) {
createBookTrains
(
numTrain.getText
(
), numberPlaces.getText
(
));
}
}
);
createBookTrainFormPanel.add
(
createBookTrains);
createBookTrainFormPanel.add
(
numTrain);
createBookTrainFormPanel.add
(
numberPlaces);
this
.add
(
panelTrains);
this
.add
(
panelBookTrains);
this
.add
(
createBookTrainFormPanel);
this
.pack
(
);
this
.setVisible
(
true
);
}
public
static
void
main
(
String[] args) {
new
BookTrainClientMain
(
);
}
}
Ajouter une méthode initializeService() qui permet d'initialiser l'accès au service Web REST. Adapter l'URL selon l'emplacement déterminé par l'exercice 2.
2.
3.
4.
5.
private
void
initializeService
(
) {
ClientConfig config =
new
DefaultClientConfig
(
);
Client client =
Client.create
(
config);
service =
client.resource
(
UriBuilder.fromUri
(
"http://localhost:8080/BookTrainWebServiceRestExercice2/resources/"
).build
(
));
}
Ajouter également un attribut service de type WebResource.
Ajouter une méthode callGetTrains() qui permet de lister l'ensemble des trains.
2.
3.
4.
5.
6.
7.
8.
9.
10.
private
void
callGetTrains
(
) {
List<
Train>
result =
service
.path
(
"trains"
)
.accept
(
MediaType.APPLICATION_XML)
.get
(
new
GenericType<
List<
Train>>(
) {}
);
for
(
Train current : result) {
System.out.println
(
current.getNumTrain
(
) +
" - "
+
current.getVilleDepart
(
) +
" - "
+
current.getVilleArrivee
(
) +
" - "
+
current.getHeureDepart
(
));
}
}
Recopier dans le projet BookTrainClientWebServiceRestExercice3 la classe Train depuis le projet BookTrainWebServiceRestExercice2 en respectant le package (soa.jaxrslabs.booktrainclientwebservicerestexercice3).
Ajouter une méthode createBookTrains(String numTrain, String numberPlaces) qui permet de créer une réservation d'un train. Compléter le code ci-dessous de façon à prendre en compte les paramètres de requêtes et l'appel à une méthode HTTP POST. Pour rappel, le service de création retourne un numéro de réservation (type String).
2.
3.
4.
5.
6.
7.
8.
9.
private
void
createBookTrains
(
String numTrain, String numberPlaces) {
String numBook =
service.path
(
"trains"
)
.path
(
"booktrains"
)
.queryParam
(
"numTrain"
, numTrain)
.queryParam
(
"numberPlaces"
, numberPlaces).request
(
)
.post
(
null
, String.class
);
System.out.println
(
numBook);
}
Ajouter une méthode callGetBookTrains() qui permet de récupérer l'ensemble des réservations qui ont été créées. Compléter le code ci-dessous.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
private
void
callGetBookTrains
(
) {
List<
BookTrain>
result =
service
.path
(
"trains"
)
.path
(
"booktrains"
)
.accept
(
MediaType.APPLICATION_XML)
.get
(
new
GenericType<
List<
BookTrain>>(
) {}
);
for
(
BookTrain current : result) {
System.out.println
(
current.getNumBook
(
) +
" - "
+
current.getCurrentTrain
(
).getNumTrain
(
) +
" - "
+
current.getNumberPlaces
(
));
}
}
Recopier dans le projet BookTrainClientWebServiceRestExercice3 la classe BookTrain depuis le projet BookTrainWebServiceRestExercice2 en respectant le package (soa.jaxrslabs.booktrainclientwebservicerestexercice3).
Exécuter le programme en vous assurant que le service Web REST défini dans l'exercice 2 est déployé et opérationnel.
IV. Remerciements▲
- https://mbaron.developpez.com/soa/soapuilesson1: Tests fonctionnels de Web Services avec SOAP-UI.
- https://mbaron.developpez.com/soa/jaxwslesson2: Développement de Web Services avec JAX-WS et Netbeans.
Je tiens à remercier Claude Leloup pour sa relecture orthographique attentive de cet article.