Vous êtes sur la page 1sur 18

Developpez.

com

Java Web
Rubrique Java Web Forum Java

Accueil Forums Rubriques

Tu t o r i e l s u r l a m i s e e n p l a c e d e s e r v i c e s W e b S O A P a v e c A p a c h e C X F

Mise en œuvre côté serveur et client

Table des matières

I. Introduction

II. Projet
II-A. Objectif
II-B. Création dans Eclipse

III. Approche Java first


III-A. Le contrat
III-B. Le WebService
III-C. Tests
III-D. Résumé

IV. Approche WSDL First


IV-A. Le fichier WSDL
IV-A-1. Création
IV-A-2. Utilisation d'un fichier type
IV-A-3. Les autres méthodes
IV-B. Génération du code
IV-B-1. Plug-in CXF pour Maven
IV-B-2. Classes générées
IV-B-3. Générer un serveur
IV-C. Résumé

V. Le client
V-A. Génération des classes
V-A-1. Télécharger le WSDL
V-A-2. Génération des classes
V-A-3. Remarques à propos des classes générées
V-B. Création du client
V-C. Test du client

VI. Conclusion

VII. Liens

VIII. Remerciements

Nous allons voir dans cet article comment mettre en œuvre un service web, à l'aide du framework Apache CXF et de Spring. Nous commencerons par voir le côté serveur, selon deux approches, pour finir par
voir comment créer un client pour interroger notre serveur.

1 commentaire

Article lu 68539 fois.

L'auteur

Denis Thomas

L'article

Publié le 7 septembre 2013

Version PDF

ePub, Azw et Mobi

Liens sociaux

Partager

I. Introduction ▲

Selon Wikipédia, un web service est un programme informatique permettant la communication et l'échange de données entre applications et systèmes hétérogènes dans des environnements distribués. Cette
communication consiste simplement à demander à un serveur d'exécuter une requête, et de nous en envoyer le résultat. Il peut s'agir d'interroger une base de données à partir de plusieurs critères, ou à l'inverse de
mettre à jour la base à distance. Un web service s'appuie sur plusieurs protocoles, tant pour la requête que pour le transport de celle-ci.

CXF est ainsi un framework Java, capable de parler plusieurs de ces protocoles, mais dans ce tutoriel, nous ne parlerons que du protocole SOAP pour les requêtes et de HTTP pour transporter ces dernières. Sachez
cependant que CXF est capable d'utiliser plusieurs autres protocoles de requêtes (RESTful HTTP, CORBA par exemple) et de transport (notamment JMS). Je vous renvoie à la documentation officielle de CXF, dont
vous trouverez le lien en fin d'article, pour ceci.

Comme indiqué dans les exemples fournis avec CXF, Spring est un invité de première classe du framework. En permettant la déclaration et l'injection de beans dans nos objets, il facilite grandement la création de
notre futur web service, tant pour le côté serveur que le côté client. C'est donc tout naturellement que nous l'utiliserons.

II. Projet ▲

II-A. Objectif ▲

Le projet de web service qui servira de support à notre article est un petit service de gestion de bibliothèque. Notre cahier des charges sommaire est :

un livre se caractérise par un identifiant, son titre, son genre (policier, science-fiction…), son année de publication et ses auteurs ;
un auteur se caractérise par un identifiant, ses prénom et nom, sa nationalité et ses dates de naissance et de mort ;
la recherche de livres peut se faire par identifiant, titre ou auteur ;
la recherche d'auteurs peut se faire par identifiant, titre ou livre ;
pour créer un livre, on doit fournir son titre et la liste de ses auteurs. En retour, nous obtenons son identifiant ;
pour créer un auteur, seul le nom est obligatoire. En retour, nous obtenons son identifiant.

II-B. Création dans Eclipse ▲

Maven simplifie considérablement la vie du développeur Java, grâce notamment à sa gestion des dépendances, aussi c'est tout naturellement que nous allons l'utiliser. Mais vous pouvez aussi télécharger CXF
directement depuis le site officiel, ce qui vous permettra de bénéficier des nombreux exemples d'utilisation du framework.

Créons un projet Maven BookService dans Eclipse, en choisissant comme archetype maven-archetype-webapp. Nous devrons ajouter les dépendances vers CXF et Spring. Les versions que nous utiliserons sont
respectivement 2.7.3 et 3.1.3.RELEASE. Dans le cadre de ce tutoriel, nous utiliserons Java 7, même si à partir de sa version 2.7, CXF se contente de Java 6.

Voici la liste des dépendances à ajouter concernant notre projet :

pom BookService Sélectionnez

<properties>
<cxf.version>2.7.3</cxf.version>
<spring.version>3.1.3.RELEASE</spring.version>
</properties>

<dependencies>
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-web-api</artifactId>
<version>6.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-databinding-jaxb</artifactId>
<version>${cxf.version}</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-frontend-jaxws</artifactId>
<version>${cxf.version}</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-transports-http</artifactId>
<version>${cxf.version}</version>
</dependency>
</dependencies>

Outre les liens vers diverses ressources sur internet à propos de CXF et des services web, vous trouverez en fin d'article les projets Eclipse complets, ce qui vous permettra de disposer d'implémentations pour tester
les services web que nous allons créer.

Nous sommes maintenant prêts à nous attaquer à une première façon de créer un service web.

III. Approche Java first ▲

Dans l'approche Java first, nous commençons par définir l'interface de notre WebService en créant une interface Java. Nous pourrions nous passer de l'interface Java et créer directement une classe, mais cette
solution est déconseillée. Un WebService étant destiné à être utilisé par de nombreux clients, une interface garantit plus surement qu'il ne variera pas dans le temps, même si nous changeons son implémentation.

III-A. Le contrat ▲

Le contrat de notre service, c'est-à-dire son interface, sera double : un BookService et un AuthorService. Le premier concernera des livres, le second les auteurs de ces livres. Nous aurons donc deux web services.
Voici ces interfaces :

Interface BookService Sélectionnez


public interface BookService {

Book getBook(Long id) throws BookNotFoundException;

List<Book> getBooksByTitle(String title);

List<Author> getAuthorsFromBook(String bookTitle) throws BookNotFoundException;

Long createBook(String title, BookType type, int year, Long... authorsId);


}

et :

interface AuthorService Sélectionnez

public interface AuthorService {

List<Book> getBooksFromAuthor(String authorFirstName, String authorLastName) throws AuthorNotFoundException;

Author getAuthor(Long id) throws AuthorNotFoundException;

List<Author> getAuthorsByName(String firstName, String lastName) throws AuthorNotFoundException;

Long createAuthor(String firstName, String lastName, String nationality, Date dateOfBirth, Date dateOfDeath);
}

Nous avons besoin des classes Book et Author. Ce sont ces objets qui transiteront sur le réseau, entre le(s) client(s) et le serveur. Les voici :

class Book Sélectionnez

public class Book {


private Long id;
private String title;
private BookType type;
private int year;
private List<Author> authors;
// getters et setters
}

et

class AUthor Sélectionnez

public class Author {


private Long id;
private String firstName;
private String lastName;
private String nationality;
private Date dateOfBirth;
private Date dateOfDeath;
private List<Book> books;
// getters et setters
}

BookType est une simple énumération :

enum BookType Sélectionnez

public enum BookType {


SCIENCE_FICTION, POLICIER, ROMAN_HISTORIQUE, HEROIC_FANTASY
}

Nous avons aussi besoin d'exceptions en cas d'erreur :

class BookNotFoundException Sélectionnez

public class BookNotFoundException extends Exception {


private Long id;
private String title;
// getters et setters
}

et :

class AuthorNotFoundException Sélectionnez

public class AuthorNotFoundException extends Exception {


private Long id;
private String author;
// getters et setters
}

Pour l'instant, nous ne sommes en présence que de classes Java classiques. Il est temps de passer au WebService proprement dit.

III-B. Le WebService ▲

Sa création est extrêmement simple : nul besoin de la moindre ligne de code, tout se fera par annotations et configuration.
La première annotation est @WebService, qui sert à marquer nos interfaces :

Sélectionnez

@WebService(name="BookService", serviceName="BookService")
public interface BookService {
...
}

Nous annotons AuthorService de la même manière, en adaptant les noms. Les attributs Name et ServiceName ne sont pas obligatoires, des valeurs par défaut sont fournies. Nous verrons plus tard à quoi ils servent.
@WebService permet à CXF de connaitre les interfaces à instrumenter en tant que web service. C'est la seule qui soit obligatoire.

Pour décrire le service à nos clients, CXF va nous générer un fichier WSDL. Cependant, sa génération se fait à partir des classes Java compilées, et non depuis le code source. Nous perdons malheureusement ainsi
le nom des paramètres, et donc leur signification. Pour les retrouver, nous utilisons une autre annotation, @WebParam. Elle se place sur les paramètres des méthodes de nos interfaces :

Sélectionnez

List<Author> getAuthorsFromBook(@WebParam(name="bookTitle") String bookTitle) throws BookNotFoundException;

C'est son attribut Name qui nous permet de retrouver les noms originaux, plus parlant que arg0, arg1…

Nous n'avons besoin de rien de plus concernant notre code. Passons maintenant à notre fichier web.xml. Comme pour toute application Web, il contient la définition des servlets. Ici, nous n'en utiliserons qu'une seule,
CXFServlet. Nous avons aussi besoin d'un ContextLoaderListener de Spring, à qui nous dirons où se trouve le fichier de configuration de l'application :

Sélectionnez

<?xml version="1.0" encoding="UTF-8"?>


<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">

<display-name>Tutoriel CXF</display-name>

<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>WEB-INF/app-context.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<servlet>
<servlet-name>cxf</servlet-name>
<servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
<servlet-name>cxf</servlet-name>
<url-pattern>/services/*</url-pattern>
</servlet-mapping>

<session-config>
<session-timeout>60</session-timeout>
</session-config>
</web-app>

Et voici le fichier de configuration de Spring :

Sélectionnez

<?xml version="1.0" encoding="UTF-8"?>


<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:beans="http://cxf.apache.org/configuration/beans"
xmlns:jaxws="http://cxf.apache.org/jaxws" xmlns:soap="http://cxf.apache.org/bindings/soap"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.cxf.apache.org/bindings/soap
http://www.cxf.apache.org/bindings/schemas/configuration/soap.xsd
http://cxf.apache.org/jaxws
http://cxf.apache.org/schemas/jaxws.xsd">

<jaxws:server
serviceClass="fr.atatorus.bookservice.services.BookService" address="/book"
serviceBean="#book">
</jaxws:server>
<jaxws:server
serviceClass="fr.atatorus.bookservice.services.AuthorService" address="/author"
serviceBean="#author">
</jaxws:server>
<bean id="book" class="fr.atatorus.bookservice.services.BookServiceImpl" />
<bean id="author" class="fr.atatorus.bookservice.services.AuthorServiceImpl"/>
</beans>

Notez l'utilisation du namespace jaxws dans l'en-tête du fichier. Il nous permet d'utiliser l'élément <jaxws:server>. On précise simplement l'interface et l'implémentation à utiliser.

Et voilà, tout est en place, nous pouvons lancer notre web service grâce au menu contextuel Run As > Run on server (auparavant, vous devrez avoir installé un serveur J2EE, Tomcat par exemple, et configuré
Eclipse en conséquence). Une fois le serveur et l'application lancés, rendez-vous à l'URL http://localhost:8080/bookservice/services. Vous devez voir alors la liste des services disponibles :
Nous voyons bien ici nos deux services, identifiés par la valeur de l'attribut Name de l'annotation @WebService. Sans cet attribut, notre service se serait appelé du nom de l'interface, postfixé par Service :
AuthorServiceService par exemple. Nous avons également un lien vers les fichiers WSDL qui décrivent nos services, dont voici un petit extrait :

extrait wsdl Sélectionnez

<?xml version='1.0' encoding='UTF-8'?>


<wsdl:definitions xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:tns="http://services.bookservice.atatorus.fr/"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:ns1="http://schemas.xmlsoap.org/soap/http"
name="AuthorService" targetNamespace="http://services.bookservice.atatorus.fr/">
<wsdl:types>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:tns="http://services.bookservice.atatorus.fr/"
attributeFormDefault="unqualified" elementFormDefault="unqualified"
targetNamespace="http://services.bookservice.atatorus.fr/">
<xs:element name="createAuthor" type="tns:createAuthor" />
<xs:element name="createAuthorResponse" type="tns:createAuthorResponse" />

...

Ce fichier au format XML décrit notre web service. On y trouve bien entendu le nom de notre web service, tel qu'indiqué par notre annotation @WebService, mais aussi l'URL à laquelle il peut être atteint, ainsi que la
liste des opérations disponibles, leurs paramètres et valeur de retour. Quant à l'espace de nom, il s'agit du nom du package puisque nous n'en avons pas donné dans l'annotation.

Ce fichier a été généré par CXF, à partir de nos interfaces annotées. Nous verrons aux chapitres suivants que CXF est aussi capable du cheminement inverse : partir du fichier WSDL pour aboutir à du code Java,
client ou serveur.

Il est assez complexe à interpréter, mais il est inutile d'entrer dans les détails pour créer notre service. CXF se chargera de tout. Nous en parlerons cependant au chapitre suivant, quand nous créerons le web service
en commençant par ce fichier.

Et maintenant que notre service fonctionne, nous devons le tester.

III-C. Tests ▲

Comme nous n'avons pas de programmes clients pour l'instant, nous allons utiliser SoapUI, un logiciel open source simple d'utilisation. Téléchargez-le, installez-le et lancez-le. Bien que vous puissiez considérer
ce qui suit comme un microtutoriel SoapUI, je vous encourage à vous rendre sur le site officiel pour approfondir l'utilisation de cet outil. Vous trouverez le lien en fin d'article.

Dans la fenêtre qui s'ouvre, vous voyez à gauche la liste des projets, actuellement vide. Cliquons avec le bouton droit pour ajouter le nôtre. Dans Project Name, entrons BookService, et dans Initial WSDL/WADL,
mettons l'URL d'un de nos WSDL :

Sous Projects, nous voyons notre projet BookService et son premier WebService, AuthorService et ses quatre méthodes, chacune avec une requête initiale vide. Avec le menu contextuel sur BookService, ajoutons le
WSDL de notre second service pour avoir notre projet complet. Quand ceci sera fait, dépliez l'arborescence de la méthode createAuthor, et double-cliquez sur la requête :
Nous découvrons sa description et ses paramètres. C'est grâce à l'annotation @WebParam que nous voyons leur nom. Sans elle, ils s'appelleraient simplement arg0, arg1, etc.

C'est parti, nous allons créer notre premier livre. Il s'agira de « La poussière dans l'œil de Dieu », de Larry Niven et Jerry Pournelle. Commençons par créer l'auteur Larry Niven :

Larry Nivean étant toujours vivant, nous ne mettons pas sa date de mort. En réponse, nous récupérons son identifiant, que nous notons, nous en aurons besoin lors de la création du livre. Il peut aussi nous servir à
vérifier notre requête en faisant un getAuthor. Passons maintenant à son collègue, né le 7 aout 1933 et lui aussi toujours vivant. Ceci fait, créons le livre :

Je vous laisse essayer la requête getBook pour vérifier que tout est correct. Vous pouvez créer votre propre implémentation, ou utiliser celle des projets Eclipse en fin d'article.

III-D. Résumé ▲

La réalisation de nos deux WebService nous permet de voir la grande simplicité de l'approche Java first, puisque seules quelques annotations suffisent. Cette approche est d'autant plus intéressante si le service
existe sans qu'il soit déjà exposé en tant que web service. Il nous suffit d'annoter l'interface, d'ajouter quelques lignes dans nos fichiers de configuration, et le tour est joué. Il est bien entendu possible de créer un web
service sans utiliser Spring. Nous verrons comment dans le chapitre suivant, quand CXF génèrera nos classes.

Cependant, l'approche Java First souffre d'un défaut : comme soapUI nous l'indique, tous les paramètres sont optionnels. Malheureusement, l'annotation @WebParam ne permet pas de préciser ceci. Si on peut le
deviner pour certains, la date de décès d'un auteur par exemple, ce n'est pas le cas de tous, cela dépend du créateur du web service. Bien qu'il s'agisse davantage d'une règle métier, et donc à mettre en œuvre dans
une couche supérieure, il ne fait pas de mal de l'indiquer à nos clients dans le contrat du web service.

De plus, cette approche lie de façon irréversible notre service à la technologie, et nous force à penser à notre service comme une interface Java, alors qu'il devrait s'agir d'une entité totalement abstraite, et
indépendante de la technologie utilisée.

Passons à la seconde approche pour créer un web service, l'approche WSDL first.

IV. Approche WSDL First ▲

WSDL signifie Web Services Description Language. Un fichier WSDL est donc simplement un fichier XML qui décrit l'ensemble des méthodes du web service que nous allons publier. Nous en avons vu
précédemment, écrit par CXF à partir de nos interfaces annotées.

Commencer par ce fichier nous permet d'être complètement agnostiques concernant la technologie à utiliser. Nous pouvons nous concentrer exclusivement sur le WebService en tant que service, sans avoir à y
penser de suite en tant qu'interface Java. Car si ici nous utiliserons Java, rien ne nous interdit de changer de technologie. Le fichier WSDL nous garantira la continuité du contrat de notre service Web si on doit en
changer. N'ayant pas défini à cette étape une technologie précise, nous ne penserons qu'au contrat du service à exposer, sans être perturbé par les limites et/ou avantages de la future implémentation. Nous verrons
qu'il apporte également une souplesse que ne permet pas Java dans le choix des paramètres.

IV-A. Le fichier WSDL ▲

IV-A-1. Création ▲

Une fois le projet créé comme précédemment (appelons le BookService2), allons dans src/main/resources et créons un répertoire wsdl qui contiendra notre fichier. Eclipse dispose d'un éditeur graphique de fichier
wsdl, ce qui va bien nous aider pour la création de ce fichier sans que nous ayons trop à plonger dans sa complexité. On y accède, comme pour la plupart des assistants d'Eclipse, avec un Ctrl-N ou un New > Other
depuis le menu Fichier ou contextuel. Il fait partie des assistants Web Services :
Cliquons sur Next, nommons le BookService.wsdl dans l'écran suivant, et cliquons à nouveau sur Next :

Renseignons les divers champs comme ci-dessus, en mettant abs comme préfixe, abs signifiant atatorus book service. En cliquant sur Finish, on se retrouve avec l'éditeur en mode Design :

On trouve à gauche notre service, nommé BookService, au centre un petit carré qui est le binding vers un portType, nommé lui aussi BookService.

Le portType est le support d'un ensemble d'opérations qui seront exposées à nos clients. Quand on génèrera nos interfaces et classes, si on veut comparer au chapitre précédent, le portType est l'équivalent de
l'interface de notre service. Pour l'instant, il ne comprend qu'une seule opération, NewOperation. Bien entendu, un portType peut comprendre plusieurs opérations.

Le service est ce qui sera exposé aux clients. Il comprend un ou plusieurs ports. Pour l'instant, il n'en comprend qu'un seul, nommé BookServiceSoap. Chaque port est relié, grâce au binding (le petit carré) à un
portType. Un port ne peut être relié qu'à un seul binding, mais un binding peut être relié à plusieurs ports. De l'autre côté, un binding ne conduit qu'à un portType, mais un portType peut être atteint par plusieurs
bindings. Il est cependant préférable, pour plus de clarté, d'utiliser la règle suivante : un port, un binding, un portType. Je ne vois pas de cas où on serait obligé d'y déroger. Nous reviendrons plus en détail sur ces
notions quand nous génèrerons nos interfaces et classes.

Pour l'instant, nous avons un squelette de WSDL. Cliquez sur l'onglet source en bas de l'éditeur pour en avoir un bref aperçu. Retournons dans l'éditeur graphique, et complétons-le en commençant par le port.
Sélectionnons ce dernier, et ouvrons la vue des propriétés d'Eclipse :

L'adresse est celle où le service sera accessible, nous la remplaçons donc par la valeur correcte. Cette adresse servira à CXF pour la génération des classes, en créant notamment un serveur stand alone à l'écoute
sur cette URL. En mettant en œuvre notre service à l'aide Spring, nous pouvons bien entendu utiliser une adresse différente.
La prochaine étape sera de créer les méthodes exposées par notre Web Service. Au niveau de l'éditeur, nous les retrouvons dans le portType. Cliquons sur la méthode NewOperation, et renommons-la getBooks. La
récupération de livres se fera selon plusieurs critères :

un identifiant ;
un titre, ou une partie du titre ;
l'identifiant d'un auteur ;
le nom d'un auteur.

Bien entendu, ces paramètres sont mutuellement exclusifs. De plus, si on fournit l'identifiant d'un livre, nous ne récupérons pas une liste de livres, mais un seul, ou aucun. Dans ce dernier cas, nous lancerons
l'exception BookNotFound. De même, on peut lancer l'exception AuthorNotFound si on cherche un livre à partir d'un auteur inconnu.

Commençons donc par les paramètres de la méthode. Passez la souris sur la flèche à droite du paramètre d'entrée input :

Une popup apparait, et nous voyons que notre paramètre d'entrée s'appelle in, et est de type String. Cliquons sur cette flèche, et un nouvel éditeur, Inline Schema of BookService.wsdl, apparait. Il ne s'agit que d'un
« zoom » sur notre fichier WSDL que nous sommes en train d'éditer, et qui concerne le paramètre d'entrée de notre méthode. Supprimons le paramètre in en sélectionnant Delete dans le menu contextuel (bouton
droit). Il nous reste une petite icône carrée, qui si on regarde dans les propriétés est une séquence. Toujours avec le bouton droit, cliquons dessus et choisissons AddChoice. La nouvelle icône que nous voyons
représente un choix, c'est-à-dire une liste d'éléments mutuellement exclusifs. Ajoutons les éléments suivants, toujours avec le bouton droit et en sélectionnant AddElement :

Il nous reste à modifier le type de nos identifiants. Ceci se fait dans la vue des propriétés, ou en cliquant sur le type associé à l'identifiant dans l'éditeur. Dans la liste déroulante, les principaux types sont
immédiatement accessibles, mais aucun ne nous intéresse. Sélectionnons Browse, et dans la popup prenons long. Nous devons ensuite mettre la cardinalité de ces paramètres, pour indiquer que si nous n'en
choisissons qu'un parmi eux, celui choisi est obligatoire. Dans la vue propriétés, nous mettons 1 dans Minimum Occurrence. Il est inutile de renseigner le maximum, la cardinalité sera [1..1]. Cliquez sur l'onglet Source
en bas de l'éditeur, et vérifiez que vous voyez quelque chose qui ressemble à ça :

Sélectionnez

<xsd:element name="getBooks">
<xsd:complexType>
<xsd:sequence>
<xsd:choice>
<xsd:element name="bookId" type="xsd:long"
minOccurs="1"></xsd:element>
<xsd:element name="bookTitle" type="xsd:string"
minOccurs="1"></xsd:element>
<xsd:element name="authorId" type="xsd:long"
minOccurs="1"></xsd:element>
<xsd:element name="authorName" type="xsd:string"
minOccurs="1"></xsd:element>
</xsd:choice>
</xsd:sequence>
</xsd:complexType>
</xsd:element>

Occupons-nous maintenant de la réponse de notre méthode. Elle nous renvoie, nous l'avons dit, un livre, ou une liste de livres. Dans la fenêtre de l'éditeur des paramètres de notre méthode, nous voyons une petite
icône carrée tout en haut à gauche : . Double-cliquons dessus, nous nous retrouvons avec la liste des éléments de notre fichier WSDL :
Nous avons actuellement deux éléments : getBooks et getBooksResponse. Le premier représente les paramètres d'entrée que nous venons d'éditer, le second la réponse que nous allons adapter à nos besoins.
Cliquons dessus, et nous nous retrouvons avec la même interface que précédemment. Commençons par ajouter un choix entre deux éléments : le premier nous décrit un livre complet, le second ne contenant que le
minimum pour reconnaitre un livre. Voici donc ce que nous voulons :

livre complet : identifiant, titre, année de publication, genre, liste des noms auteurs avec leur identifiant ;
livre « basique » : identifiant, titre, liste des noms auteurs avec leur identifiant.

Commençons par supprimer le paramètre existant de la Sequence, et substituons-lui un Choice. Ajoutons à ce dernier une nouvelle Sequence comprenant quatre éléments (bookId, title, year, genre) et une autre
Sequence, qui contiendra deux éléments (authorId et authorName). Tous les éléments sont de type string, sauf les identifiants qui sont des long et l'année de publication qui est un int. Au Choice, ajoutons une
nouvelle Sequence, qui contiendra la même chose que la précédente à l'exception de l'année de publication et le genre.

Quant aux cardinalités, tous les éléments sont obligatoires (minimum 1, maximum non renseigné ou 1), sauf pour la séquence des auteurs. Il y a au moins un auteur, mais il peut y en avoir plusieurs. Mettons donc *
dans le maximum. Quant à nos deux Sequences, elles ont chacune une cardinalité respectivement de 1 et 1 à l'infini.

Il nous reste un dernier petit détail à régler : le genre des livres est une chaine de caractères, mais restreinte à une liste de valeurs. Sélectionnons cet élément, et dans la vue des propriétés, allons dans l'onglet
Constraints et ajoutons les valeurs désirées :

Voilà, nous avons fini la description de notre première méthode. Regardons le résultat :

Et en XML :

Sélectionnez

<xsd:element name="getBooksResponse">
<xsd:complexType>
<xsd:sequence>
<xsd:choice>
<xsd:sequence minOccurs="1">
<xsd:element name="bookId" type="xsd:long" minOccurs="1">
</xsd:element>
<xsd:element name="title" type="xsd:string" minOccurs="1">
</xsd:element>
<xsd:element name="year" type="xsd:int" minOccurs="1">
</xsd:element>
<xsd:element name="genre" minOccurs="1">
<xsd:simpleType>
<xsd:restriction base="xsd:string">
<xsd:enumeration value="SCIENCE_FICTION">
</xsd:enumeration>
<xsd:enumeration value="POLICIER">
</xsd:enumeration>
<xsd:enumeration value="HEROIC_FANTASY">
</xsd:enumeration>
<xsd:enumeration value="ROMAN_HISTORIQUE">
</xsd:enumeration>
</xsd:restriction>
</xsd:simpleType>
</xsd:element>
<xsd:sequence minOccurs="1" maxOccurs="unbounded">
<xsd:element name="authorId" type="xsd:long" minOccurs="1">
</xsd:element>
<xsd:element name="authorName" type="xsd:string" minOccurs="1">
</xsd:element>
</xsd:sequence>
</xsd:sequence>
<xsd:sequence minOccurs="1" maxOccurs="unbounded">
<xsd:element name="bookId" type="xsd:long" minOccurs="1">
</xsd:element>
<xsd:element name="bookTitle" type="xsd:string" minOccurs="1">
</xsd:element>
<xsd:sequence minOccurs="1" maxOccurs="unbounded">
<xsd:element name="authorId" type="xsd:long" minOccurs="1">
</xsd:element>
<xsd:element name="authorName" type="xsd:string" minOccurs="1">
</xsd:element>
</xsd:sequence>
</xsd:sequence>
</xsd:choice>
</xsd:sequence>
</xsd:complexType>
</xsd:element>

Et ce n'est pas fini : il nous faut nous occuper des exceptions. Pour ceci, retournons dans l'éditeur de notre fichier WSDL, et en cliquant avec le bouton droit sur la méthode que nous venons de créer, sélectionnons
Add Fault pour ajouter une exception :

Renommons-la, dans la vue Properties, en BookNotFoundFault, puis comme pour les paramètres d'entrée et sortie de notre méthode, éditons-la. La recherche d'un livre se faisant par son identifiant ou son titre, nous
remplaçons l'actuel élément par un choix entre ces deux éléments :

Recommençons pour AuthorNotFoundFault.

Ouf, nous avons terminé la première méthode de notre service. C'est quelque peu fastidieux. L'éditeur graphique nous aide à comprendre les différentes étapes plus simplement, mais reste moins facile d'accès que
l'écriture d'une simple interface Java. En contrepartie, nous avons plus de possibilités, avec des paramètres optionnels, puisque la recherche d'un livre peut se faire selon quatre critères au choix. Pour faire la même
chose avec une interface Java, nous aurions dû utiliser quatre méthodes différentes.

Pour se simplifier la vie, on pourrait commencer par écrire une interface Java, l'annoter, mettre en œuvre rapidement un petit service qui va nous générer un fichier WSDL, qu'il nous suffira d'adapter. Ceci est aussi
quelque peu fastidieux pour un aussi petit service que le nôtre. Pour un plus complexe, c'est à voir.

Il y a une autre technique qui va nous alléger un peu le travail. Les paramètres des méthodes se ressemblent, notamment la liste des auteurs dans les livres, et nous allons très certainement les réutiliser ailleurs.
Créer des types communs permet ainsi de factoriser une partie du fichier WSDL, ce qui est toujours une bonne chose en programmation.

IV-A-2. Utilisation d'un fichier type ▲

Le fichier type nous permet de définir les types d'objets qui vont être manipulés par notre web service. On y trouve donc les auteurs, les livres, ainsi que les exceptions. Il n'est pas nécessaire que les types de notre
service soient créés dans un fichier séparé, mais c'est une pratique qui clarifie le développement. Le fichier WSDL est plus léger, et nous gardons la possibilité de réutiliser nos mêmes types ailleurs.

Nos types sont décrits par un fichier de schéma XML. Créons un fichier types.xsd à l'aide de l'assistant d'Eclipse :
Nous voyons s'ouvrir l'éditeur XSD d'Eclipse, en mode design :

Il est très proche de ce que nous avons déjà vu lors de la création de notre fichier WSDL. Dans notre cas, tout ce qui nous intéresse est la partie Types. Commençons par remplacer le namespace utilisé
(http://www.example.org/types) par le nôtre. Cliquez sur Schema, et renseignez la vue Properties comme ci-dessous :

Le préfixe abst signifie atatorus book service type. Ceci fait, retournons dans la partie Types de l'éditeur et avec le menu contextuel, ajoutons quelques types complexes, Book, Author, BasicBook, BasicAuthor,
BookNotFound, AuthorNotFound, et un type simple, BookGenre :

Double-cliquons sur Author, nous nous retrouvons avec l'éditeur que nous connaissons bien pour avoir édité les paramètres de notre méthode précédemment. Modifions donc tous ces éléments pour qu'ils
correspondent à nos besoins :

Author : il est composé d'un identifiant, du prénom, du nom, de sa nationalité, et de ses dates de naissance et de mort. Seuls l'identifiant et le nom sont obligatoires (cardinalité mini à 1, maxi à 1ou non
renseignée), les autres sont optionnels (mini 0, maxi 1). L'identifiant est de type long, les dates de type date, les autres sont des strings ;
BasicAuthor : un identifiant et un nom, qui sera la concaténation du prénom et du nom. Les deux sont obligatoires ;
Book : l'identifiant, le titre, le genre, l'année de parution, une liste de BasicAuthor. L'identifiant est un long, l'année un int, le genre est de type BookGenre. Tous sont obligatoires ;
BasicBook : l'identifiant, le titre, la liste des BasicAuthor. Tous sont obligatoires ;
BookNotFound : choix entre l'identifiant du livre ou son titre. Les deux sont obligatoires ;
AuthorNotFound : choix entre l'identifiant et le nom de l'auteur, les deux sont obligatoires ;
BookGenre : c'est un type simple à base de string, réduit aux choix que nous avons vu précédemment.

Ceci ne devrait pas vous poser de problèmes. Enregistrons nos modifications, et retournons à notre fichier WSDL, et plus particulièrement à la réponse de notre méthode. Zoomons dessus en cliquant sur la flèche à
sa droite, et effaçons tout son contenu. Elle nous retournera désormais soit un Book, soit une liste de BasicBook. Pour sélectionner ces types, choisissez Browse dans la liste déroulante du type, et cochez la case
Enclosing Project de la fenêtre de sélection :

Si dans la liste vous avez comme moi les types qui apparaissent en double, ceci provient du fait qu'Eclipse a généré les classes, sans doute parce que vous avez Build automatically coché dans le menu Project, ou
parce que comme moi, vous avez lancé une génération des classes pour voir si votre fichier types.xsd produisait les classes attendues. Si c'est votre cas, prenez garde à sélectionner le type correspondant au fichier
des types que nous venons de créer, pas celui de la classe Java générée. On peut faire la distinction en regardant dans Declaration Location.

Et voici ce que nous devons obtenir pour la réponse de notre service :


C'est nettement plus compréhensible qu'auparavant. Modifions ensuite le type de nos exceptions. Supprimons-les (ce sera plus facile), et recréons-les. Nommons la première BookNotFoundFault, avec un message
du même nom, et de type BookNotFound. Même chose pour la seconde concernant les auteurs.

IV-A-3. Les autres méthodes ▲

Nous procèderons de même pour les autres méthodes. Maintenant que nos types existent, ce sera beaucoup plus rapide. Voici les méthodes qui nous restent :

createBook : elle prend en paramètres un titre, un genre, une année de publication, une liste d'auteurs. Cette liste peut se présenter sous la forme d'une liste d'identifiants ou de noms. En retour, on a
l'identifiant du livre créé, ou une exception AuthorNotFound si un des auteurs de la liste n'existe pas ;
getAuthors : elle prend en paramètres, au choix, un identifiant d'auteur, un nom, un identifiant de livre, un titre de livre. Dans le premier cas, elle nous retourne un Author, dans les autres une liste de
BasicAuthor. En cas d'erreur, elle lance l'exception AuthorNotFound ou BookNotFound ;
createAuthor : en paramètres, on trouve un prénom, un nom, une date de naissance et de décès, une nationalité. En retour, nous avons l'identifiant de l'auteur créé.

Une fois ces méthodes créées dans notre éditeur, nous devons les lier à notre binding. Sans ça, notre service ne propose tout simplement aucune méthode. Rappelez-vous notre petit carré entre le service et le
portType qui contient les méthodes que nous voulons exposer. Pour que le lien se fasse entre nos méthodes et le service, nous devons mettre à jour le binding. Utilisons le menu contextuel sur le carré du binding, et
choisissons Generate Binding Content. Comparez avant et après le code source du fichier, au niveau de la balise <wsd:binding /> pour voir la différence. Seules les opérations recensées dans cette balise seront
exposées.

Vous trouverez à la fin de l'article les liens vers les projets Eclipse complets, avec les fichiers WSDL et de types.

Le fichier WSDL doit être un peu nettoyé. En effet, à peine avions-nous cliqué sur Add Fault dans le menu, qu'Eclipse nous génère des balises <wsdl:message /> pour par exemple createAuthorFault. Nous les avons
remplacées dans l'éditeur graphique par BookNotFoundFault et AuthorNotFoundFault, mais Eclipse ne les supprime pas du code source.

Maintenant nous avons enfin tout ce dont nous avons besoin, nous pouvons passer à la génération des classes Java à l'aide de Maven.

IV-B. Génération du code ▲

La génération de code Java par CXF nous donnera l'interface de notre service, ainsi que toutes les classes manipulées par notre service, qu'il s'agisse des paramètres, des valeurs de retour ou des exceptions. Une
fois le code généré, nous n'avons besoin que de créer l'implémentation de notre service à partir de l'interface, et à le configurer comme dans l'approche Java First.

Pour générer ces classes, nous utiliserons le plug-in CXF pour Maven, mais il existe également un outil en ligne de commande, que vous trouverez en téléchargeant CXF depuis le site officiel.

IV-B-1. Plug-in CXF pour Maven ▲

Comme nous utilisons Maven pour notre projet, l'emploi de ce plug-in est tout naturel. Voici sa configuration minimale :

Sélectionnez

<build>
...
<plugins>
...
<plugin>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-codegen-plugin</artifactId>
<version>${cxf.version}</version>
<executions>
<execution>
<id>generate-sources</id>
<phase>generate-sources</phase>
<configuration>
<wsdlRoot>${basedir}/src/main/resources/wsdl</wsdlRoot>
<wsdlOptions>
<wsdlOption>
<wsdl>${basedir}/src/main/resources/wsdl/BookService.wsdl</wsdl>
</wsdlOption>
</wsdlOptions>
</configuration>
<goals>
<goal>wsdl2java</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>

Les paramètres sont équivalents à ceux de l'utilitaire en ligne de commande. Ici, nous disons dans quel répertoire se trouvent les fichiers à analyser avec balise <wsdlRoot>, et quel est le fichier WSDL à utiliser avec
la balise <wsdl>. La génération des sources se fera à la phase Maven generate-sources, en exécutant mvn clean generate-sources.

IV-B-2. Classes générées ▲

Les classes se trouvent dans le répertoire target/generated-sources/cxf. Pensez à rafraichir le projet Eclipse si vous avez exécuté la commande depuis l'extérieur de l'IDE. Vous aurez certainement à ajouter ce
répertoire parmi les sources du projet. Allez dans le menu contextuel Build Path > Use as Source Folder pour ceci.
Leurs packages sont fr.atatorus.bookservice et fr.atatorus.bookservice.types, et correspondent aux Target namespaces que nous avions définis au moment de la création de nos fichiers WSDL et XSD. Nous avons
une classe pour chaque type, ainsi que pour chaque opération et sa réponse. Le plug-in nous a également généré d'autres classes.

La principale est BookService. Il s'agit tout simplement de l'interface de notre service. Nous pouvons donc dès à présent mettre en œuvre ce service, exactement comme nous l'avons vu au chapitre précédent. Nous
devrons prendre garde lors de l'implémentation à gérer les paramètres optionnels, mais ceci mis à part, il n'y aura aucune différence.

Ensuite, nous trouvons une factory, qui nous permet d'instancier nos objets sans passer par l'opérateur new. Elle me semble avoir peu d'utilité en elle-même, mais il est possible d'en hériter ou de s'en inspirer si
nécessaire.

Nous avons également des classes du genre GetBooks et GetBooksResponse. Il s'agit de classes container pour les paramètres et les valeurs de retour de nos méthodes.

IV-B-3. Générer un serveur ▲

Le plugin cxf peut générer un serveur fonctionnel. Il nous suffit d'ajouter ces quelques lignes dans notre pom :

Sélectionnez

<configuration>
<wsdlRoot>${basedir}/src/main/resources/wsdl</wsdlRoot>
<wsdlOptions>
<wsdlOption>
<wsdl>${basedir}/src/main/resources/wsdl/BookService.wsdl</wsdl>
<extraargs>
<extraarg>-impl</extraarg>
<extraarg>-server</extraarg>
</extraargs>
</wsdlOption>
</wsdlOptions>
</configuration>

Nous obtenons deux classes supplémentaires : BookServiceImpl, une implémentation minimale de notre service, qui ne fait rien, et BookService_BookServiceSOAP_Server, un mini serveur stand alone, qui s'exécute
simplement via sa méthode main(). Son implémentation est vraiment minimale, pour ne pas dire fruste, mais elle nous permet de voir comment créer un serveur qui pourra servir aux tests. Intéressons-nous
cependant un peu à lui, et plus précisément à l'annotation sur la classe :

Sélectionnez

@javax.jws.WebService(serviceName = "BookService",
portName = "BookServiceSOAP",
targetNamespace = "http://www.atatorus.fr/BookService/",
wsdlLocation = "file:/home/denis/dev/workspace/bookservice2/src/main/resources/wsdl/BookService.wsdl",
endpointInterface = "fr.atatorus.bookservice.BookService")
public class BookServiceImpl implements BookService {
...
}

Cette annotation possède un attribut particulièrement important, wsdlLocation. Comme son nom l'indique, elle signale où se trouve notre fichier WSDL. Elle n'est pas indispensable pour que notre service fonctionne,
on peut même se passer de toute l'annotation, puisqu'on la trouve réduite à son minimum sur l'interface. Mais si nous n'indiquons pas où se trouve notre fichier, quand nous le demanderons à partir de l'URL
http://localhost:8080/BookService/services?wsdl nous obtenons bien notre fichier WSDL, mais sans la déclaration des types du fichier types.xsd. Pour avoir les types, nous devons utiliser l'URL
http://localhost:8080/BookService/services?wsdl=BookService.wsdl Nous obtenons alors un fichier WSDL intégrant les types au début. En implémentant notre service à l'aide de la servlet CXFServlet comme dans le
tutoriel précédent, cette annotation est inutile, et est remplacée par l'attribut wsdlLocation dans le fichier contexte de Spring :

Sélectionnez

<jaxws:server serviceClass="fr.atatorus.bookservice.BookService"
address="/book" serviceBean="#book" wsdlLocation="wsdl/BookService.wsdl" >
</jaxws:server>
<bean id="book" class="fr.atatorus.services.BookServiceImpl" />

Nous verrons plus en détail les conséquences de l'utilisation ou non de cet attribut quand nous aborderons le client.

Vous pouvez lancer le serveur, et utiliser SoapUI comme client pour le tester. L'implémentation générée par CXF se contente de renvoyer des objets null, nous aurons donc besoin d'une implémentation un peu plus
complète pour ceci. Vous pouvez la réaliser vous-même, ou prendre celle dans les projets en fin d'article.

IV-C. Résumé ▲

La création d'un WebService à partir d'un fichier WSDL est un peu plus complexe comme nous venons de le voir. Mais cette complexité nous apporte davantage de souplesse qu'une simple interface Java. Nous
avons besoin d'être un peu plus soigneux côté implémentation, à cause de la complexité de méthodes qui possèdent des paramètres optionnels, donc null. Quant à nos clients, la technologie utilisée par le serveur
leur importe peu, tout ce qu'ils demandent c'est qu'il fonctionne. Et donc si pour une raison ou une autre nous avons besoin de changer de technologie, nous leur apportons ainsi la garantie que le service ne changera
pas.

Mais avoir des web services c'est bien, avoir des clients capables de les utiliser c'est mieux.

V. Le client ▲

Cette fois, nous n'avons pas à décider des possibilités du web service, nous sommes ses utilisateurs. Tout ce que nous avons, c'est l'URL où on peut le joindre, et du WSDL. Et nous avons vu que CXF peut tout à fait
générer les classes nécessaires à l'appel du service. Outre l'interface du service, nous avons évidemment besoin des objets manipulés par le service. Et plutôt que de les écrire à la main, utilisons CXF pour ceci.

V-A. Génération des classes ▲

Comme d'habitude, commençons par créer un projet Maven dans Eclipse, avec comme archetype maven-archetype-quickstart. Mais cette fois, comme nous ne disposons pas forcément du fichier WSDL, nous le
téléchargerons.

V-A-1. Télécharger le WSDL ▲

Pour ceci, nous nous servirons du plugin maven-download-plugin(1) :

Sélectionnez

<plugins>
...
<plugin>
<groupId>com.googlecode.maven-download-plugin</groupId>
<artifactId>maven-download-plugin</artifactId>
<version>1.0.0</version>
<executions>
<execution>
<id>Download wsdl</id>
<goals>
<goal>wget</goal>
</goals>
<phase>validate</phase>
<configuration>
<url>http://localhost:8080/bookservice2/services/book?wsdl</url>
<outputDirectory>${basedir}/target/resources/wsdl</outputDirectory>
<outputFileName>BookService.wsdl</outputFileName>
</configuration>
</execution>
</executions>
</plugin>
...
</plugins>

Pour voir toutes les options de ce plugin, utilisez la commande :

Sélectionnez

mvn help:describe -Dplugin=com.googlecode.maven-download-plugin:maven-download-plugin -Ddetail=true

Dans notre cas, nous n'avons besoin que de l'URL du fichier, du répertoire où nous souhaitons l'enregistrer, ainsi que son nom. Quant à la phase choisie, il s'agit de validate, qui prend place juste avant generate-
sources, et qui valide que les ressources de notre projet sont disponibles.

V-A-2. Génération des classes ▲

Pour la génération des classes, nous reprenons le plugin CXF et le configurons comme dans le tutoriel précédent, sauf que nous n'avons plus besoin des options pour générer une implémentation et un serveur. À la
place, nous allons générer un client :

Sélectionnez

<plugins>
...
<plugin>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-codegen-plugin</artifactId>
<version>${cxf.version}</version>
<executions>
<execution>
<id>generate-sources</id>
<phase>generate-sources</phase>
<configuration>
<wsdlRoot>${basedir}/target/resources/wsdl</wsdlRoot>
<wsdlOptions>
<wsdlOption>
<wsdl>${basedir}/target/resources/wsdl/BookService.wsdl</wsdl>
<extraargs>
<extraarg>-client</extraarg>
</extraargs>
</wsdlOption>
</wsdlOptions>
</configuration>
<goals>
<goal>wsdl2java</goal>
</goals>
</execution>
</executions>
</plugin>
...
</plugins>

Lançons notre projet bookservice2 créé précédemment. Ensuite, exécutons la commande :

Sélectionnez

mvn clean validate generate-sources

Et voilà, nous avons notre fichier WSDL et nos classes. Je vous laisse examiner BookService_BookServicePort_Client qui montre comment générer un client. Nous utiliserons cependant une autre méthode, plus
simple, avec Spring.

V-A-3. Remarques à propos des classes générées ▲

Regardons l'interface de notre web service. Selon la manière dont elle est publiée par le site, elle correspond à celle que nous avions (j'ai supprimé les annotations pour plus de clarté) :

Sélectionnez

public interface BookService {

public GetBooksResponse getBooks(GetBooks parameters) throws BookNotFoundFault, AuthorNotFoundFault;

public long createAuthor(String firstName, String lastName, XMLGregorianCalendar dateOfBirth, XMLGregorianCalendar dateOfDeath, String nat

public CreateBookResponse createBook(CreateBook parameters) throws AuthorNotFoundFault;

public GetAuthorsResponse getAuthors(GetAuthors parameters) throws BookNotFoundFault, AuthorNotFoundFault;


}

ou ressemble à ça :

Sélectionnez

public interface BookService {

public void getBooks(Long bookId, String bookTitle, Long authorId, String authorName,
javax.xml.ws.Holder<fr.atatorus.bookservice.types.Book> book,
javax.xml.ws.Holder<java.util.List<fr.atatorus.bookservice.types.BasicBook>> books) throws BookNotFoundFault, AuthorN

public long createAuthor(String firstName, String lastName, XMLGregorianCalendar dateOfBirth, XMLGregorianCalendar dateOfDeath, String nat

public long createBook(String title, BookGenre genre, int year, List<java.lang.Long> authorIds, List<java.lang.String> authorsName) throws

public void getAuthors(Long authorId, String authorName, Long bookId, String bookTitle,
javax.xml.ws.Holder<fr.atatorus.bookservice.types.Author> author,
javax.xml.ws.Holder<java.util.List<fr.atatorus.bookservice.types.BasicAuthor>> basicAuthors) throws BookNotFoundFau
}

Dans le premier cas, nous avons indiqué explicitement à notre serveur où se trouvait le fichier WSDL, soit en annotant notre implémentation avec @WebService et en indiquant l'attribut wsdlLocation, soit en
l'indiquant dans le fichier de configuration de Spring, comme nous l'avons vu au chapitre précédent.

Si nous ne précisons pas où se trouve le fichier WSDL, la servlet CXF doit se débrouiller pour le générer. Elle produit un fichier WSDL parfaitement valide, mais quand notre client l'analyse pour obtenir les valeurs de
retour, pour les objets complexes il utilise un Holder(2). Nous devons fournir cet objet en paramètre lors de l'appel, et au retour, nous récupérons notre objet Book à l'intérieur. Si nous nous retrouvons avec ce genre
d'interface, un petit adaptateur nous facilitera la vie. Ce n'est guère différent que d'utiliser un objet GetAuthors comme paramètre dans le premier cas. Nous avons toujours besoin d'un adaptateur.

V-B. Création du client ▲

Une factory fournie par CXF nous suffit : JaxWsProxyFactoryBean. Elle va nous créer le client très simplement, à partir de deux paramètres : l'interface et l'adresse du web service.

Sélectionnez

public class BookClient {

public static void main(String args[]) throws Exception {

JaxWsProxyFactoryBean factory = new JaxWsProxyFactoryBean();


factory.setAddress("http://localhost:8080/bookservice/services/book");
factory.setServiceClass(BookService.class);

BookService client = (BookService) factory.create();


...
}

Et voilà, c'est aussi simple que ça, nous avons un client de web service prêt à l'emploi, il suffit de lui passer les paramètres qu'il attend. On peut aussi créer notre client en tant que bean Spring tout aussi facilement :

Sélectionnez

<bean id="client" class="fr.atatorus.bookservice.BookService"


factory-bean="clientFactory" factory-method="create"/>

<bean id="clientFactory" class="org.apache.cxf.jaxws.JaxWsProxyFactoryBean">


<property name="serviceClass" value="fr.atatorus.bookservice.BookService"/>
<property name="address" value="http://localhost:8080/bookservice2/services/book"/>
</bean>

Il ne vous reste plus qu'à tester le client.

V-C. Test du client ▲

Pour tester le client, nous avons la possibilité d'interroger directement le web service. Mais outre le fait qu'il n'est pas forcément bien vu d'utiliser un serveur de production pour des tests, il est parfois nécessaire qu'il
nous retourne des valeurs spécifiques à certains traitements particuliers. Là encore, SoapUI va nous aider, en permettant, simplement à partir du fichier WSDL, de générer un serveur, plus exactement un stub, un
morceau de serveur en quelque sorte, qui nous répond exactement ce dont nous avons besoin.

Dans SoapUI, créons un nouveau projet, et après avoir ajouté le fichier WSDL, cochons la case Create MockService :

Nous devons choisir ensuite quelle méthode il implémentera. Choisissons-les toutes :

Dans la fenêtre suivante, donnons-lui un nom ou laissons le nom par défaut :

Et c'est (presque) tout, nous disposons d'un web service opérationnel :


Il ne nous reste plus qu'à définir la réponse de notre serveur à nos interrogations. Commençons par la méthode getBooks, et double-cliquons dessus. Une nouvelle fenêtre s'ouvre :

Vous avez deviné, double-cliquons sur Response 1. SoapUI nous présente alors un éditeur avec le type de réponse envoyé par le serveur, qu'il ne nous reste qu'à remplir :

Un dernier petit effort : configurons l'URL et le port d'écoute de notre stub. Cliquons sur l'icône en forme d'outils dans la fenêtre du MockService, et remplaçons le chemin par défaut
(/mockBookServiceServiceSoapBinding) par quelque chose de plus simple :

Et maintenant, lançons notre MockService grâce à la petite flèche verte à gauche.

Écrivons quelques lignes de code pour vérifier que notre client (et notre stub…) fonctionne :

Sélectionnez

@SuppressWarnings("restriction")
public static void main(String args[]) throws Exception {

JaxWsProxyFactoryBean factory = new JaxWsProxyFactoryBean();


factory.setAddress("http://localhost:8088/mock");
factory.setServiceClass(BookService.class);
BookService client = (BookService) factory.create();

Holder<Book> bookHolder = new Holder<Book>();


client.getBooks(1L, null, null, null, bookHolder, null);
Book book = bookHolder.value;
System.out.println("Titre : " + book.getTitle());
System.out.println("Auteurs :");
for (BasicAuthor author : book.getAuthors()) {
System.out.println(author.getAuthorName());
}
}
Et voilà le résultat :

Sélectionnez

Titre : La poussière dans l'œil de dieu


Auteurs :
Larry Nivean
Jerry Pournelle

SoapUI vous permet également de créer des réponses intelligemment, en tenant compte des paramètres de la requête. Allez sur le site officiel pour un tutoriel un peu plus poussé.

VI. Conclusion ▲

Nous avons vu comment créer un serveur, selon deux approches, ainsi qu'un client, en nous appuyant sur le framework Apache CXF, ainsi que la manière tester l'ensemble avec SoapUI. Nous nous sommes limités
aux web services utilisant le protocole SOAP avec le front-end JAX-WS. CXF nous propose d'autres protocoles et front-end, qui nécessiteraient chacun une série d'articles. Je vous renvoie donc au site officiel
d'Apache CXF si vous voulez en apprendre davantage. Mais avec cet article, vous devriez posséder des bases suffisantes pour commencer à implémenter vos propres web services.

VII. Liens ▲

Voici quelques liens qui pourront vous aider à aller plus loin :

Apache CXF : c'est le site officiel. Vous y trouverez la documentation complète du framework, ainsi que pas mal d'exemples en téléchargeant le framework lui-même ;
SoapUI : le site officiel de cet outil, pour le télécharger et apprendre à le maitriser ;
maven-download-plugin : un plug-in Maven pour télécharger des fichiers lors du build ;
wagon : un autre plug-in Maven qui peut remplacer le précédent si vous rencontrez des problèmes avec.

CXF n'est pas le seul framework pour développer des services web :

Apache Axis : il s'agit d'une implémentation du protocole SOAP, comme nous avons utilisé avec CXF ici. Mais contrairement à ce dernier, il est limité à ce protocole. Il a évolué jusqu'à une version 2 ;
XFire : Comme indiqué sur la page d'accueil du site, il s'agit de l'ancêtre de CXF. CXF peut même être considéré comme XFire 2.

Je ne connais pas ces frameworks. Si j'ai réussi à écrire des erreurs en aussi peu de mots, que les spécialistes me pardonnent (et me corrigent).

Quelques ressources disponibles sur DVP à propos des services Web :

Développer un Web Service avec JAX-WS et le tester avec SOAPUI, en 5 minutes, un tutoriel de Thierry Leriche-Dessirier ;
Architectures Orientées Services : une série de cours de Mickael Baron.

Et pour les plus paresseux d'entre vous, voici les projets Eclipse prêts à l'import :

Projet Java First ;


Projet WSDL First ;
Projet client.

Enfin, les articles originaux sur mon blog personnel :

Tutoriel CXF - Service Web : approche Java First ;


Tutoriel CXF - Service Web : approche WSDL First ;
Tutoriel CXF - Service Web : lecôté client ;

VIII. Remerciements ▲

Je remercie pour leur aide Robin56, thierryler et Mickael Baron, ainsi que ClaudeLeloup pour la correction orthographique.

Vous avez aimé ce tutoriel ? Alors partagez-le en cliquant sur les boutons suivants : Partager

(1) Je n'ai rencontré aucun problème avec ce plugin, mais quelques commentaires sur mon blog m'ont fait remarquer qu'il souffrait de quelques bogues, notamment celui d'ajouter des « / » à la fin des chemins, et m'ont
recommandé d'utiliser le plug-in Wagon. Je ne connais pas ce plug-in, je vous laisse seul juge de celui qui est le mieux adapté à votre besoin.

(2) Dans ce cas, Eclipse vous présentera peut-être un warning :Access restriction: The type Holder<Book> is not accessible due to restriction on required library /…/jre/lib/rt.jar. J'avoue ne pas savoir comment me débarrasser
de ce warning autrement qu'avec l'annotation @SuppressWarnings("restriction").

Le contenu de cet article est rédigé par Denis Thomas et est mis à disposition selon les termes de la Licence Creative Commons Attribution 3.0 non transposé.
Les logos Developpez.com, en-tête, pied de page, css, et look & feel de l'article sont Copyright © 2013 Developpez.com.

Copyright © 2000-2019 - www.developpez.com

Partenaire : Hébergement Web

Vous aimerez peut-être aussi