Accueil > Java > WebService "Contract First" avec Spring-WS

WebService "Contract First" avec Spring-WS

Contract First vs Code First

La méthode la plus répandue pour exposer des WebServices en Java est la méthode dite « Code First » : on développe un composant Java et on s’appuie ensuite sur un outil pour exposer ce composant sous forme de WebService. Cet outil va analyser les signatures des méthodes de votre composant et va générer un descripteur de WebService : le WSDL. Le développeur jette ensuite un oeil (ou pas) à ce descripteur et se félicite de ne pas avoir eu à écrire ce fichier très verbeux.

Un autre développeur récupère le fameux descripteur WSDL et utilise un autre langage ou un autre outil pour générer le code client et c’est la que les choses se compliquent souvent.

Je vais prendre comme exemple un service de recherche dans un annuaire, ce service permet de rechercher des personnes à partir de leur nom, prénom et ville de résidence. Deux critères sont obligatoires : le nom et la ville de résidence.

Voici l’interface Java qui correspond au service :

@WebService
public interface AnnuaireService {
	List<Personne> rechercher(String nom, String prenom, String ville);
}

Une fois ce service déployé à l’aide de la pile de WebService CXF, le WSDL généré déclare le type suivant pour représenter le message de recherche :

<xs:complexType name="rechercher">
	<xs:sequence>
		<xs:element minOccurs="0" name="arg0" type="xs:string" />
		<xs:element minOccurs="0" name="arg1" type="xs:string" />
		<xs:element minOccurs="0" name="arg2" type="xs:string" />
	</xs:sequence>
</xs:complexType>

Cette déclaration n’est pas utilisable dans l’état, les critères de recherche ne sont pas nommés et tous les critères sont marqués comme optionnels (minOccurs=0).
Il est donc necessaire d’annoter l’interface Java pour permettre à CXF de générer un WSDL plus significatif.

@WebService
public interface AnnuaireService {
	List<Personne> rechercher(
		@WebParam(name="nom") String nom,
		@WebParam(name="prenom") String prenom,
		@WebParam(name="ville") String ville);
}

Les critères de recherche sont maintenant nommés dans le WSDL :

<xs:complexType name="rechercher">
	<xs:sequence>
		<xs:element minOccurs="0" name="nom" type="xs:string" />
		<xs:element minOccurs="0" name="prenom" type="xs:string" />
		<xs:element minOccurs="0" name="ville" type="xs:string" />
	</xs:sequence>
</xs:complexType>

La spécification JAX-WS ne permet pas de rendre les paramètres obligatoires.

Il existe bien sur des solutions de contournement pour que le WSDL soit plus conforme au contrat du service, mais cet exemple trivial nous montre que la génération du WSDL à partir du code Java necessite d’enrichir le code avec des méta données et que le résultat ne permet pas d’utiliser le WSDL généré comme contrat solide entre le producteur et les consommateurs.

Pour que le WSDL reprenne son rôle de contrat, il est important qu’il spécifie le plus précisément possible les messages d’entrée et de sortie du WebService. La démarche « Contract First » met justement l’accent sur la contractualisation des messages échangés.

Contract First avec Spring WS

Le descripteur WSDL reste très verbeux et la perspective de gérer ces fichiers à la main est assez effrayante. Rassurez vous, nous allons nous contenter de décrire les messages en entrée et sortie à l’aide de XML Schema (XSD).
Une fois ces messages décrits, nous allons nous appuyer sur JAXB et Spring-WS pour implémenter le contrat.

Voici le schéma décrivant le message de recherche dans l’annuaire :

<element name="rechercherRequest">
	<complexType>
		<annotation>
			<documentation>
				Message d'entrée pour la recherche dans l'annuaire
   		</documentation>
		</annotation>
		<sequence>
			<element name="nom" type="string" />
			<element name="prenom" type="string" minOccurs="0" />
			<element name="ville" type="string" />
		</sequence>
	</complexType>
</element>

Une configuration rapide de la génération des objets Java à partir du XML Schema à l’aide de maven 2 :

<plugin>
	<groupId>org.codehaus.mojo</groupId>
	<artifactId>jaxb2-maven-plugin</artifactId>
	<version>1.3</version>
	<executions>
		<execution>
			<goals>
				<goal>xjc</goal>
			</goals>
		</execution>
	</executions>
</plugin>

Et voici l’objet qui correspond à la requête :

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "", propOrder = {
    "nom",
    "prenom",
    "ville"
})
@XmlRootElement(name = "rechercherRequest")
public class RechercherRequest {

    @XmlElement(required = true)
    protected String nom;
    protected String prenom;
    @XmlElement(required = true)
    protected String ville;
 ....
 

Certes c’est verbeux mais c’est du codé généré donc pas de couts d’intégration ou de maintenance.

Voici maintenant la signature du composant Java qui prend en charge la requête :

@Endpoint
public class AnnuaireEndpoint {
	@PayloadRoot(localPart = "rechercherRequest", namespace = "http://yellowpages.tartachuc.org")
	public RechercherResponse rechercher(RechercherRequest request) {

....

Une première annotation @Endpoint marque la classe et la rend detectable par le « component-scan » de spring. La seconde annotation @PayloadRoot indique quel message est traité par la méthode.

Cette mise en oeuvre de Spring-WS permet d’assurer un contrat WSDL soigné tout en ne pénalisant pas la productivité des développements. Dernier avantage, cette démarche impose de concevoir les messages échangés ce qui favorisera des messages plus expressifs et simples  qui facilitent l’utilisation des services tout en évitant d’exposer directement les objets « privés ».

Étiquettes : ,
  1. Aucun commentaire pour l’instant.
  1. No trackbacks yet.

Laisser un commentaire

Entrez vos coordonnées ci-dessous ou cliquez sur une icône pour vous connecter:

Logo WordPress.com

Vous commentez à l'aide de votre compte WordPress.com. Déconnexion / Changer )

Image Twitter

Vous commentez à l'aide de votre compte Twitter. Déconnexion / Changer )

Photo Facebook

Vous commentez à l'aide de votre compte Facebook. Déconnexion / Changer )

Photo Google+

Vous commentez à l'aide de votre compte Google+. Déconnexion / Changer )

Connexion à %s

%d blogueurs aiment cette page :