Système Repartis
Système Repartis
Système Repartis
SMB111, 6 ECTS
Chapitre 7 – Séance 12
Services Web (suite)
Le plus grand soin a été apporté à la réalisation de ce support pédagogique afin de vous fournir une information
complète et fiable. Cependant, le Cnam Champagne-Ardenne n'assume de responsabilités, ni pour son utilisation,
ni pour les contrefaçons de brevets ou atteintes aux droits de tierces personnes qui pourraient résulter de cette
utilisation.
Les exemples ou programmes présents dans cet ouvrage sont fournis pour illustrer les descriptions théoriques. Ils
ne sont en aucun cas destinés à une utilisation commerciale ou professionnelle.
Le Cnam ne pourra en aucun cas être tenu pour responsable des préjudices ou dommages de quelque nature que
ce soit pouvant résulter de l'utilisation de ces exemples ou programmes.
Tous les noms de produits ou autres marques cités dans ce support sont des marques déposées par leurs
propriétaires respectifs.
Ce support pédagogique a été rédigé par Cyril Rabat, enseignant vacataire au Cnam Champagne-Ardenne.
L'utilisation du support pédagogique est réservée aux formations du Cnam Champagne-Ardenne. Tout autre usage
suppose l'autorisation préalable écrite du Cnam Champagne-Ardenne.
Toute utilisation, diffusion ou reproduction du support, même partielle, par quelque procédé que ce soit, est
interdite sans autorisation préalable écrite du Cnam Champagne-Ardenne. Une copie par xérographie,
photographie, film, support magnétique ou autre, constitue une contrefaçon passible des peines prévues par la loi,
du 11 mars 1957 et du 3 juillet 1995, sur la protection des droits d'auteur.
Les différents exemples proposés dans ce cours ont été écrits courant 01/2013. Pour les
compiler, la dernière version en date de Java Enterprise Edition (J2EE), a été installée
(version de javac 1.7.0). Il est possible que certains exemples nécessitent des corrections
mineures pour fonctionner avec des versions ultérieures de Java.
Pour NetBeans, il s’agit de la version 7.2.1.
Le développement d’un service Web, son déploiement et son utilisation passent par un
ensemble d’étapes, aussi bien du côté du serveur que de celui du client. Du côté du serveur, nous
avons les étapes suivantes :
- Développement du service Web : en Java, cela correspond à l’écriture d’une ou plusieurs
classes qui correspondent au code associé aux opérations ;
- Déploiement du service Web : une fois écrit, il est compilé puis déployé sur un serveur Web
qui gère les services Web tels que Glassfish ou Axis2 ;
- Enregistrement auprès d’un annuaire : cette étape n’est pas obligatoire, mais elle permet à
des clients de découvrir le service.
Du côté du client, une fois le service Web déployé, il n’est pas possible de l’utiliser
immédiatement. Il faut passer par les étapes suivantes :
- Récupération de la description WSDL : elle est réalisée via un annuaire ou directement
depuis le serveur Web ;
- Génération de la souche associée à la description : en Java, cela correspond à la génération
d’une souche, sous la forme d’une classe ;
- Utilisation du service : à partir de la souche, il est possible d’intégrer les opérations du
service Web dans une application cliente.
Depuis Java 6, une API complète ainsi que des outils sont disponibles dans le JDK J2EE,
permettant la création et le déploiement simplifié d’un service Web. Il est même possible de mettre à
disposition un mini-serveur Web pour réaliser des tests en local.
1.2. JAX-WS
Java propose une API appelée JAX-WS (pour Java API for XML Web Services) qui est à la fois un
standard et une implémentation. Elle est intégrée à la JRE depuis plusieurs versions. METRO fournie
une implémentation de référence de JAX-WS1. Grâce à cette API, Java fournit ainsi la possibilité de
développer des services Web en dehors d’un serveur d’applications (ce terme sera abordé dans la suite
de ce cours).
Le service Web correspond à un ensemble d’opérations qui seront possibles pour le client. En
Java, cela implique le développement d’une classe qui contient des annotations JAX-WS. Ce dernier est
basé sur le concept de POJO (pour Plain Old Java Object), c’est-à-dire l’utilisation d’une simple classe,
sans héritage, ni surcharge de méthode. L’intérêt est qu’aucun fichier autre que la classe n’est
nécessaire pour le déploiement.
Dans cette classe Java « ordinaire », le programmeur peut insérer des annotations qui
correspondent à des mots-clés, associés à des paramètres, qui sont spécifiés en commençant par un
arobase (@) puis le mot-clé. Ainsi, d’une manière générale, nous avons la notation suivante :
1
Voir le site officiel http://metro.java.net/.
Services Web (suite) 1 / 15
@MotClef(paramètre1="valeur1", paramètre2="valeur2", …)
Pour commencer, il faut spécifier les informations générales concernant le service. Elles sont
précisées avant la définition de la classe comme suit :
@WebService(name="MonService")
@SOAPBinding(style=Style.RPC,use=Use.LITERAL)
public class MonService { … }
Dans la classe MonService, nous pouvons maintenant écrire les méthodes qui seront
accessibles par les clients. Pour chaque, nous devons spécifier son nom, le nom de ses paramètres
éventuels et le nom du résultat si c’est une fonction. Pour réaliser des appels asynchrones (comme
avec CORBA), il est possible d’utiliser la directive @OneWay. Voici un exemple de méthode2 :
@WebMethod(operationName="hello")
@WebResult(name="un-hello")
public String hello(@WebParam(name="nom") String nom) {
return "Bonjour " + nom;
}
Avant la méthode, nous spécifions son nom à l’aide de la directive @WebMethod et le résultat à
l’aide de @WebResult. À noter que ces choix influenceront le contenu de la description WSDL. Pour
chaque paramètre, nous utilisons la directive @WebParam qui prend uniquement le nom. Cette directive
est à utiliser autant de fois qu’il y a de paramètres.
Normalement, les classes qui contiennent des services Web doivent être compilées, puis
déployées en utilisant différents outils et nécessitent la mise en place d’un serveur Web particulier. Or,
dans le JDK, il est possible de déployer facilement un service Web à l’aide d’un mini-serveur Web.
Nous utilisons pour cela une classe nommée Endpoint (qui correspond au point d’attache du service
Web). La commande suivante permet de l’exécuter :
Tant que le endPoint est en cours d’exécution, le service est accessible. Pour arrêter le service,
il suffit de stopper le endPoint :
2
Pour le code complet de la classe MonService, reportez-vous aux annexes 6.1.
SMB111 2 / 15
endpoint.stop();
Nous plaçons les instructions précédentes dans un main d’une classe appelée Publieur3. Entre
ces deux commandes, il est nécessaire d’ajouter une boucle infinie ou de mettre en attente le serveur.
Lorsque ce programme sera exécuté, le service Web deviendra accessible.
Pour vérifier le bon fonctionnement du service Web, nous pouvons utiliser un navigateur Web
classique. En tapant l’URL http://localhost:8080/services, nous devons obtenir l’affichage
représenté sur la Figure 1.
Pour s’assurer que le service Web correspond bien aux annotations spécifiées dans la classe
Java, il est possible de cliquer sur le lien vers le WSDL : celui-ci s’affichera dans le navigateur. Voici un
extrait de cette description WSDL4 :
…
<message name="hello">
<part name="nom" type="xsd:string"/>
</message>
<message name="helloResponse">
<part name="un-hello" type="xsd:string"/>
</message>
<portType name="MonService">
<operation name="hello">
<input wsam:Action="http://messervices/MonService/helloRequest"
message="tns:hello"/>
<output wsam:Action="http://messervices/MonService/helloResponse"
message="tns:helloResponse"/>
</operation>
</portType>
…
Maintenant que le service Web est déployé, nous allons écrire un client qui va exploiter ce
service. Pour cela, nous devons récupérer la description WSDL et générer une souche à partir de celle-
ci. Nous utilisons un outil présent dans le JDK appelé wsimport (pour Web Services import) qui réalise
ses opérations automatiquement.
3
Pour le code complet de la classe Publieur, reportez-vous aux annexes 6.2.
4
Pour la description complète, reportez-vous aux annexes 6.3.
Services Web (suite) 3 / 15
Nous créons un répertoire client (qui n’est pas lié au serveur) dans lequel nous tapons la
commande suivante (à taper uniquement lorsque le serveur est en fonctionnement) :
L’option –keep permet de conserver les sources Java qui sont générées automatiquement et
compilées (ces fichiers ne sont pas nécessaires, mais permettent de comprendre le fonctionnement du
service Web). L’option –p permet de spécifier le package Java dans lequel les classes seront placées.
Ici, nous avons choisi de garder le même nom (messervices). Enfin, nous devons spécifier l’URL à
laquelle la description WSDL est accessible.
MonService.java
MonService.class
MonServiceService.java
MonServiceService.class
L’interface MonService correspond aux opérations décrites dans le WSDL et elles doivent être
identiques à celles de la classe que nous avons écrite dans la section 1.3. Le code la méthode hello
est la suivante :
@WebMethod
@WebResult(name = "un-hello", partName = "un-hello")
@Action(input = "http://messervices/MonService/helloRequest",
output = "http://messervices/MonService/helloResponse")
public String hello(
@WebParam(name = "nom", partName = "nom")
String nom);
Pour obtenir une instance de la classe MonService, nous utilisons la classe MonServiceService
générée par l’outil wsimport. Elle contient un ensemble de méthodes dont getMonServicePort qui
permet de construire un objet de type MonService. Sur cet objet, nous pourrons ensuite appeler les
différentes opérations (ici, uniquement un appel à la méthode hello).
Pour interagir avec le service Web, nous développons une classe Client5. Dans le main, nous
ajoutons les instructions suivantes pour récupérer une instance et lancer des opérations :
Pour exécuter le client (après avoir compilé la classe), il faut que le serveur soit en cours de
fonctionnement (pour rappel, la méthode sera exécutée du côté serveur). Le message « Bonjour
Toto » doit s’afficher dans la console.
Une architecture Web dynamique est basée généralement sur la notion de serveur
d’applications : la Figure 2 en donne une vision simplifiée. Le but d’un serveur d’applications est de
faciliter la gestion des applications dans une entreprise. Généralement, celles-ci sont distribuées et le
serveur d’applications facilite leur développement, leur déploiement et leur contrôle. L’intérêt est
d’avoir une séparation entre la logique présentation et la logique métier, avec généralement une
5
Pour le code complet de la classe Client, reportez-vous aux annexes 6.4.
SMB111 4 / 15
séparation avec les bases de données. Comme nous l’avons vu dans le cours 1, cette architecture est
de type 3-tiers, voire n-tiers (si les données sont séparées).
client
Internet
client SGBD
serveur Web serveur
d'applications
client
Figure 2 : architecture Web dynamique
La plupart du temps, les communications sont réalisées à l’aide du protocole HTTP (ou HTTPs).
Ainsi, la plupart des serveurs d’applications possèdent les fonctionnalités d’un serveur Web, qui est
généralement intégré. Dans ce cas, on parle de serveur d’applications Web. Depuis les dernières
versions du JDK J2EE, le serveur d’applications Glassfish est fourni. C’est ce serveur que nous allons
utiliser pour déployer un service Web.
2.2. Glassfish
Lorsque Glassfish est installé, il doit être démarré. Normalement, il fonctionne sur le port 8080.
Ainsi, en le démarrant (dans le Menu Démarrer, « Java EE 6 SDK » puis « Démarrer le serveur
d’applications ») puis en ouvrant un navigateur et en tapant l’adresse http://localhost:8080, nous
obtenons un message d’accueil dont un extrait est représenté sur la Figure 3.
Comme on peut le voir sur l’interface d’administration, il est possible de déployer une
application directement en ligne et de visualiser celles qui sont déjà installées. En cliquant sur le
bouton « Déployer une application », il est possible de sélectionner une application sur le poste qui est
au format WAR. Ce type de format est similaire au format JAR traditionnel. Cependant, il possède une
structure particulière représentée sur la Figure 4. Des informations sont nécessaires pour spécifier la
configuration de l’application. Concernant les services Web, nous devons indiquer le point d’attache,
par exemple.
La création de l’application est assez complexe, notamment l’écriture des différents fichiers XML
nécessaires pour le déploiement. Pour cette raison, il est conseillé de passer par des IDE tels
qu’Eclipse ou NetBeans. Ces derniers créent la structure de l’application et permettent de générer les
fichiers nécessaires automatiquement. Ils permettent aussi de les déployer facilement. Dans ce cours,
nous allons nous intéresser à NetBeans qui peut fonctionner avec Glassfish (Eclipse propose des
fonctionnalités similaires).
NetBeans, comme Eclipse, fonctionne sous la forme de projets. Pour en créer un qui utilise des
services Web, il faut créer un projet de type « Java Web », puis « Web Application » (dans le menu
« File » puis « New Project »). Après avoir spécifié un nom au projet, l’assistant demande de
sélectionner le serveur à utiliser avec le projet6 : en installant J2EE avec Glassfish, celui-ci doit être
présent dans la liste (et sélectionné par défaut) 7. Les autres options peuvent rester avec les valeurs
par défaut : il faut cliquer simplement sur le bouton « Finish ».
Une fois le projet créé, nous obtenons l’arborescence présentée sur la Figure 6. Les sources des
services Web (les classes Java) seront ajoutées dans le répertoire « Source Packages ». Pour en
ajouter un, allez dans le menu « File » puis « New File », dans la catégorie « Web services »,
sélectionnez « Web service ». Ensuite, il vous ait demandé de saisir un nom pour le service Web et de
spécifier un package (par exemple messervices). Puis en laissant les autres options par défaut, cliquez
sur le bouton « Finish ». Le package est créé (correspondant à un répertoire « messervices » dans la
partie « Source Packages ») et le fichier Java correspondant est créé.
Le fichier source du service Web peut être accessible depuis le package ou depuis la branche
« Web Services » de l’arborescence. Depuis cette dernière, il est possible d’accéder aux fonctionnalités
automatiques de NetBeans (cf. section 2.5).
Par défaut, un service Web créé par NetBeans contient uniquement une méthode « hello ». Le
contenu est le suivant :
/**
* This is a sample web service operation
*/
@WebMethod(operationName = "hello")
public String hello(@WebParam(name = "name") String txt) {
return "Hello " + txt + " !";
}
6
NetBeans est prévu pour fonctionner avec plusieurs serveurs. Ceux-ci doivent être installés au préalable sur la
machine.
7
Au moment de la rédaction de ce support, la version de Glassfish est 3.1.2.
Services Web (suite) 7 / 15
Nous allons maintenant pouvoir tester le service Web. Pour cela, il faut allez dans le menu
« Run », puis « Run project ». Cette action prend un certain temps : NetBeans va exécuter Glassfish8
et déployer le service. Une fois terminée, une page est automatiquement ouverte dans le navigateur :
il s’agit en fait de « index.jsp ». Pour tester le service, il suffit de tapez l’adresse suivante :
http://localhost:8080/NomProjet/NomService
Nous obtenons alors le même affichage que sur la Figure 1. Une autre solution est de cliquer
avec le bouton droit sur le projet et sélectionner « deploy ».
Maintenant, à chaque modification du service Web et dès que l’on enregistre les modifications,
NetBeans réalise automatiquement le déploiement : le service Web est recompilé et redéployé.
Attention cependant, pendant cette opération, il se peut qu’il ne soit plus disponible.
L’intérêt d’exploiter NetBeans est de pouvoir utiliser un ensemble d’assistants. Par exemple, il
est possible d’ajouter automatiquement une nouvelle opération. Pour cela, cliquez avec le bouton droit
sur le service Web créé : un menu contextuel est affiché comme le montre la Figure 7 (a). En cliquant
sur « Add Operation… », une nouvelle fenêtre s’affiche (voir Figure 7 (b)) permettant de saisir le nom
de l’opération, les paramètres avec leur type et leur nom et les exceptions possibles générées.
(a) (b)
Figure 7 : menu contextuel sur le service Web (a) et ajout d’une nouvelle opération (b)
Depuis le même menu, il est possible de tester le service Web en sélectionnant « Test Web
Service ». Une fenêtre s’ouvre dans le navigateur comme représenté sur la Figure 8. Chaque opération
est représentée et il est possible de saisir des valeurs pour les paramètres.
8
Attention, si le serveur est déjà démarré en dehors de NetBeans, le démarrage ne pourra être réalisé. Il faut
donc fermer Glassfish et laisser NetBeans le démarrer lui-même.
SMB111 8 / 15
Figure 8 : test d'un service Web
Par exemple, supposons que nous testions la méthode setCompteur. Nous saisissons la valeur
10 dans le champ et nous validons. Une nouvelle fenêtre s’ouvre affichant le résultat de l’opération,
ainsi que les requêtes SOAP envoyées et reçues. Voici par exemple, la requête :
Pour développer un client qui exploitera le service Web, nous créons un nouveau projet avec
NetBeans. Ce projet est une application Java (catégorie « Java », puis « Java Application »). Une
classe contenant un main est créée.
Nous ajoutons un nouveau document appelé « Web Service Client ». L’assistant nous propose
de choisir parmi les services Web créés : nous sélectionnons ici le service « MonService ». Cette action
a pour conséquences de produire les fichiers générés manuellement par l’outil wsimport. Nous
obtenons l’arborescence représentée sur la Figure 9.
On peut remarquer que les deux fichiers générés sont situés dans la section « Generated
Sources ». La description WSDL est placée dans la section « Sources Packages ». Nous avons une
section spécifique nommée « Web Service References » dans laquelle nous avons le MonServicePort
qui peut être utilisé pour écrire le client.
Comme les noms des fichiers sont un peu différents de ceux produits par wsimport, le client
doit être un peu modifié par rapport à celui présenté dans la section 1.5. Nous ajoutons les lignes
suivantes dans le main du fichier ClientMonService9 :
Pour exécuter le programme, il suffit alors d’aller dans le menu « Run », puis « Run project ».
L’exécution est réalisée dans un onglet dans la partie output. Il est possible ainsi de saisir du texte au
clavier. Si l’exécution se réalise correctement, nous obtenons l’affichage suivant :
run:
Votre nom : Toto
Hello Toto !
BUILD SUCCESSFUL (total time: 13 seconds)
3. Conclusion
Dans ce cours, nous avons vu comment créer un service Web à l’aide de Java. Nous avons
survolé deux méthodes : l’utilisation d’un mini-serveur Web intégré au JDK et l’utilisation du serveur
d’applications Glassfish.
9
Pour le code complet du client, reportez-vous aux annexes 6.5.
SMB111 10 / 15
Le développement de la classe est basé sur un développement Java standard, avec des
annotations JAX-WS qui permettent de donner des informations supplémentaires pour le service Web.
Nous avons vu comment exploiter l’IDE NetBeans pour développer un service Web, le déployer
automatiquement dans Glassfish et le tester. L’utilisation d’un service Web dans une application cliente
est aussi automatisée avec l’importation automatique de la description WSDL et la génération des
classes nécessaires.
4. Références/bibliographie
[1] George Coulouris, Jean Dollimore, Tim Kindberg et Gordon Blair, Distributed Systems : Concepts
and Design 5th Edition, Addison-Wesley Publishing Company, 2011 (en anglais).
[2] Annick Fron, Architectures Réparties en JAVA 2ème édition, Dunod (présentation de nombreuses
fonctionnalités de Java telles que SOAP, RMI, CORBA, JMS…).
[3] Martin Kalin, Java Web Services : Up and Running, O’Reilly, 2009 (en anglais).
5.1. QCM
1. Lorsqu’on n’utilise pas d’IDE, il est nécessaire d’écrire manuellement la description WSDL lors
du développement d’un service Web :
Vrai Faux
2. Avec du Java pur, il n’est pas nécessaire de mettre en place un serveur Web :
Vrai Faux
3. L’écriture d’un client impose de récupérer la description WSDL :
Vrai Faux
4. Les annotations JAX-WS n’ont pas d’influence sur le comportement du service Web :
Vrai Faux
5. Un serveur d’applications est nécessaire pour mettre en place un service Web :
Vrai Faux
5.2. Exercices
a. Reprenez les sources fournies avec le cours et compilez-les. Testez la mise en place du
service Web.
SMB111 12 / 15
6. Annexes
package messervices;
import javax.jws.*;
import javax.jws.soap.*;
import javax.jws.soap.SOAPBinding.*;
@WebService(name="MonService")
@SOAPBinding(style=Style.RPC,use=Use.LITERAL)
public class MonService {
@WebMethod(operationName="hello")
@WebResult(name="un-hello")
public String hello(@WebParam(name="nom") String nom) {
return "Bonjour " + nom;
}
@WebMethod(operationName="getSomme")
@WebResult(name = "la-somme")
public double getSomme(@WebParam(name="op1") double op1,
@WebParam(name="op2") double op2) {
return op1 + op2;
}
import javax.xml.ws.Endpoint;
import javax.swing.JOptionPane;
import messervices.*;
<definitions xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-
wssecurity-utility-1.0.xsd"
xmlns:wsp="http://www.w3.org/ns/ws-policy"
xmlns:wsp1_2="http://schemas.xmlsoap.org/ws/2004/09/policy"
xmlns:wsam="http://www.w3.org/2007/05/addressing/metadata"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:tns="http://messervices/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns="http://schemas.xmlsoap.org/wsdl/"
targetNamespace="http://messervices/"
name="MonServiceService">
<types/>
Services Web (suite) 13 / 15
<message name="hello">
<part name="nom" type="xsd:string"/>
</message>
<message name="helloResponse">
<part name="un-hello" type="xsd:string"/>
</message>
<portType name="MonService">
<operation name="hello">
<input wsam:Action="http://messervices/MonService/helloRequest"
message="tns:hello"/>
<output wsam:Action="http://messervices/MonService/helloResponse"
message="tns:helloResponse"/>
</operation>
</portType>
<binding name="MonServicePortBinding" type="tns:MonService">
<soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="rpc"/>
<operation name="hello">
<soap:operation soapAction=""/>
<input>
<soap:body use="literal" namespace="http://messervices/"/>
</input>
<output>
<soap:body use="literal" namespace="http://messervices/"/>
</output>
</operation>
</binding>
<service name="MonServiceService">
<port name="MonServicePort" binding="tns:MonServicePortBinding">
<soap:address location="http://localhost:8080/services"/>
</port>
</service>
</definitions>
import messervices.*;
import java.util.Scanner;
package clientmonservice;
import java.util.Scanner;
import messervices.MonService;
import messervices.MonService_Service;
/**
*
* @author Cyril
SMB111 14 / 15
*/
public class ClientMonService {
/**
* @param args the command line arguments
*/
public static void main(String[] args) {
MonService port = new MonService_Service().getMonServicePort();
Scanner sc = new Scanner(System.in);
System.out.print("Votre nom : ");
String nom = sc.nextLine();
System.out.println(port.hello(nom));
}
}