Interopérabilité des services Web de type « Contract First

publicité
Interopérabilité des services Web de type « Contract First »
(priorité au contrat) entre Microsoft .NET et IBM WebSphere
Eric Cherng
Vertigo Software, Inc.
James Duff
Vertigo Software, Inc.
Dino Chiesa
Microsoft Corporation
28 octobre 2004
Public visé :
Architectes et développeurs
Télécharger l'exemple d'interopérabilité pour cet article
Introduction
Contenu de l'exemple d'interopérabilité
Conditions et configuration logicielle requises
Pourquoi des services Web ?
Service Web 1 : Addition
Service Web 2 : Évaluations et rapports
Service Web 3 : Recherche de produits
Conclusion
Recommandations/Astuces
Remerciements
Liens utiles
Introduction
Tandis que les services Web attirent de plus en plus d’adeptes, les fournisseurs s’efforcent
d’ajouter de nouveaux standards et fonctionnalités dans leurs infrastructures pour enrichir et
consolider la communication entre systèmes. Les organisations, qui investissent de plus en plus de
temps et d’argent dans la recherche de l’optimisation des services Web et de leurs technologies de
base, doivent connaître les forces et les limites de ces technologies, notamment en matière de
savoir-faire du développeur, de facilité de maintenance et d’interopérabilité. Le présent article est
consacré à l’interopérabilité ; il définit comme point de départ l’interopérabilité entre Microsoft .NET
Framework et IBM WebSphere via les services Web. L’objectif de ce livre blanc et de l’exemple qui
l’accompagne est d’expliquer aux développeurs de chaque plate-forme comment intégrer l’autre
plate-forme. Bien que notre étude soit focalisée sur les plates-formes .NET de Microsoft et
WebSphere d’IBM, les exemples illustrent des techniques et principes de base pouvant être
réutilisés dans tous les projets exigeant une interopérabilité entre plates-formes via des services
Web.
Contenu de l’exemple d’interopérabilité
Cet exemple contient :
1. L’exemple de code de service Web :

Il s'agit de services Web et de clients des services Web écrits en Java et en C#
pour trois interfaces de service différentes :
1. Service Web 1 : il s'agit d'un service Web effectuant une simple addition dont
l’objectif est de vous familiariser avec les outils et les plates-formes utilisés.
2. Service Web 2 : ce service montre comment faire passer des types d’objet
complexes d’une plate-forme à l’autre.
3. Service Web 3 : ce service met en évidence un modèle de conception pour créer
des services Web extensibles, capables de gérer les modifications dans les
mappages de données.

Chaque service Web fait preuve d’une interopérabilité hétérogène. Il suffit de
modifier un paramètre sur le client du service Web pour que celui-ci puisse
communiquer soit avec le service Web .NET, soit avec le service WebSphere.

Dans cet exemple simple, il n’est pas nécessaire d’avoir accès à la base de données
pour exécuter un service Web et un client.

Dans ce document, nous étudierons en détail la création du service Web 1 à partir
des principes de base. Nous n’approfondirons pas autant l’analyse des services Web 2
et 3.
2. Ce livre blanc, qui décrit en détail les différents services Web et leur conception.
Les techniques et concepts abordés dans cet article sont généraux et peuvent donc s’appliquer à
toute connexion entre deux plates-formes de fournisseurs différents. Néanmoins, les exemples ont
été développés et testés uniquement avec les kits de développement (SDK) de services Web de
.NET Framework et d’IBM WebSphere.
Conditions et configuration logicielle requises
Cet article sur l’interopérabilité (et l’exemple associé) considère que le lecteur connaît les bases de
.NET Framework, d’ASP.NET et du serveur d’application WebSphere d’IBM. Le service Web 1
s’adresse aux développeurs qui n’ont jamais créé de services Web ou qui ne connaissent pas l’une
des deux plates-formes. Même si vous êtes familiarisé avec les services Web et avec les deux
plates-formes en question, nous vous conseillons de consulter au moins la partie concernant le
service Web 1 car elle contient des trucs et astuces utiles qui ne figurent pas dans les sections
traitant des services Web 2 et 3.
Configuration logicielle requise
Nous avons utilisé les logiciels suivants pour créer et tester les exemples.

Microsoft Windows XP Professionnel SP1

Microsoft Internet Information Services 5.1

Kit de développement (SDK) de Microsoft .NET Framework, version 1.1

SDK de Sun Java, version 1.4

Kit de développement pour services Web de WebSphere (WSDK), version 5.1

Kit des développeurs de services Web Java (JWSDP), version 1.3
Si vous n’avez aucun logiciel Java, vous devez impérativement installer les logiciels requis dans
l’ordre suivant :
1. SDK Sun Java 1.4
2. Eclipse 2.1.3
3. WSDK 5.1
4. JWSDP 1.3
5. Eclipse nécessite le SDK Java, lequel est inclus dans le WSDK mais pour installer le WSDK,
Eclipse doit déjà être installé !
6. Vous trouverez des informations complémentaires sur l’installation dans le fichier README
à télécharger avec les exemples d’interopérabilité.
Pourquoi des services Web ?
Les services Web en tant que technologie applicative existent depuis de nombreuses années. Bien
avant que les organisations et les sociétés ne créent les standards de services Web, les analystes
d’entreprise, les architectes et les ingénieurs en logiciel avaient compris que les données
d’entreprise étant dispersées sur de nombreux systèmes, il fallait que ces derniers puissent
communiquer entre eux. Les premières tentatives de liaison des applications entre elles à l’aide des
technologies d’appel de procédure distante (RPC), comme RMI, DCOM et d’autres mécanismes
d’interconnexion spécifiques à certaines plates-formes, ont généralement échoué en raison de la
diversité des fournisseurs et des plates-formes utilisées dans les organisations. Ces approches ont
aussi avorté parce que leur usage n’était pas adapté à Internet, d’où la lenteur, voire l’absence de
réponses sur ce réseau. D’autres solutions, qui utilisaient les files d’attente de messages, les
verbes PUT/GET et l’ordonnancement manuel des messages, posaient des problèmes de
maintenance et de productivité en matière de programmation. C’est pourquoi les développeurs ont
opté pour des standards et protocoles courants : XML et HTTP.
Lorsque les ingénieurs se sont lancés dans la création d’applications capables de communiquer
entre elles, ils ont choisi XML parce que ce langage est utilisé et pris en charge par toutes les
plates-formes. Le protocole HTTP a été retenu en raison de son utilisation étendue et de sa
capacité à traverser les pare-feu. Les fournisseurs comme Microsoft ou IBM ont commencé à créer
des infrastructures pour soutenir ces efforts de développement et faciliter le travail des
développeurs. Pour cela, ces infrastructures éliminaient la charge que représentent la sérialisation
et la désérialisation du XML et fournissaient des éléments structurels communs comme le code
requis pour établir des connexions entre systèmes. La naissance des services Web a été accueillie
comme une promesse de simplification de l’intégration entre systèmes et applications hétérogènes.
Les fournisseurs, utilisant les services Web comme catalyseur, se rallient maintenant autour du
concept d’architecture orientée services qui permet d’interconnecter des solutions individuelles,
éventuellement créées (en toute légitimité) à l’aide de protocoles RPC propriétaires comme RMI ou
DCOM. Les données peuvent ainsi circuler en temps réel dans toute l’entreprise.
Les bénéfices et le potentiel en termes d’intégration sont donc réels, mais dans la pratique, les
développeurs estiment pour la plupart que la création de services Web interopérables est plutôt
complexe. La création de services Web même très simples recèle de nombreux risques comme les
conflits de types ou l’absence de prise en charge de certaines fonctions. Notre premier exemple
présente un service Web interopérable. Nous vous expliquerons les différentes étapes du processus
de conception et de création d’un tel service.
Service Web 1 : Addition
Le premier exemple explique les bases d’un service Web interopérable. Il s’agit d’un service qui
effectue une simple addition. Il accepte deux entiers de la part du client et renvoie la somme de
ces deux nombres.
Le schéma fonctionnel suivant décrit l’architecture de ce service Web :
Figure 1. Architecture fonctionnelle du service Web 1.
Qu’est-ce qui vient en premier, l’implémentation ou l’interface ?
La technique de création de services Web la plus courante, la plus avérée et la mieux prise en
charge par les outils consiste à « inférer » une interface de service Web à partir d’une
implémentation existante. Le développeur rédigera le code suivant :
public int Ajouter(int x, int y) { return x+y; }
Dans ASP.NET, il est très simple d'exposer ce code en tant que service Web : il suffit d’ajouter au
code l’attribut WebMethod. Cette technique est souvent appelée « Implementation First »
(priorité à l’implémentation) ou « Code First » (priorité au code) car l’interface de service Web,
décrite de façon formelle dans un document WSDL (Web Service Description Language), est
dérivée de l’implémentation.
Figure 2. Développement d’un service Web par la méthode « Implementation First »
(priorité à l’implémentation).
La technique de développement de services Web appelée « Implementation First » consiste à écrire
d’abord le code du service Web (voir étape nº 1 de la figure 2). Après compilation, l’infrastructure
des services Web utilise ce code pour générer de façon dynamique un fichier WSDL (étape nº 2).
Lorsque les clients demandent la définition du service Web, ils récupèrent le fichier WSDL généré et
créent le proxy client à partir de cette définition (étape nº 3).
Par exemple, dans ASP.NET, le fichier WSDL peut être généré de façon dynamique à partir d’une
implémentation avec URL comme celle-ci :
http://hostname:port/Service1.asmx?WSDL
Lorsque le runtime de .NET détecte le paramètre WSDL dans la requête, il effectue une réflexion
sur le code doté de l’attribut WebMethod pour générer de façon dynamique une opération de
description du service sous-jacent dans le fichier WSDL.
Cette technique d’implémentation est très simple et fonctionne très bien, mais elle génère quelques
problèmes, notamment en cas de services Web utilisés pour connecter des systèmes hétérogènes.
Par exemple, si l’on commence par l’implémentation, il n'est pas possible d’inclure dans le service
Web des types spécifiques à une plate-forme. Les types de jeux de données .NET et de vecteurs
Java sont spécifiques à une plate-forme et sont difficiles à représenter sur d’autres plates-formes.
En effet, il n’existe pas encore de mappage unique et bien défini entre ces types spécifiques et
XML. Ce n’est pas parce qu’un client .NET est capable de reconnaître dans un Blob de XML un jeu
de données, qu’un client de service Web écrit en Java peut faire la même chose. Nous sommes
donc confrontés à des problèmes d’interopérabilité.
Le schéma XML standard W3C définit des types de données intégrés dont, entre autres, les chaînes
(string), les entiers de différentes tailles (integer), les variables booléennes (Boolean), les variables
représentées en virgule flottante (float) en simple et double précision ou les données de date et
d’heure (DateTime). Chaque plate-forme applicative prend également en charge son propre jeu de
types de données. L’intersection entre ces jeux de types de données définit les types qui offriront la
meilleure interopérabilité entre plates-formes. Si vous commencez par des types appartenant au
schéma XML, il est facile de les mapper vers les types spécifiques à la plate-forme, mais si vous
commencez par ces derniers, le mappage inverse n’est pas forcément possible. Par exemple le
mappage des types integer, string, Boolean et float du schéma XML vers les types de données
correspondant dans .NET ou Java est facile. En revanche, les types de vecteurs (Vector) ou de
tables de hachage (Hashtable) sont natifs des différentes plates-formes et ne font donc pas partie
des types officiels du schéma XML. Pour plus d’informations sur les types de données pris en
charge, consultez les spécifications (en anglais) des types de données du schéma XML.
Les runtimes de la plupart des services Web (y compris le runtime intégré à .NET Framework et à
WSDK) sont capables d’effectuer un mappage entre les primitives du schéma XML et les primitives
spécifiques à la plate-forme. Ainsi, une chaîne du schéma XML sera mappée vers le type
System.String dans .NET et vers le type java.lang.String dans Java. Avec les primitives du schéma XML
et les structures et tableaux générés à partir de ces primitives, il est possible de créer des types de
données plus complexes, décrits dans le schéma XML, qui pourront être mappés de façon très
fidèle d’une plate-forme à l’autre. Ces types de données peuvent alors figurer dans les documents
WSDL à utiliser dans le service Web.
Cela représente l’essence de la méthode appelée « WSDL first » (priorité au WSDL) : si vous
utilisez les types du schéma XML pour définir les types de données utilisés dans le service Web,
vous avez plus de chance de pouvoir mapper ces types de données entre plates-formes.
Cette technique qui consiste à créer d’abord le fichier WSDL est aussi appelée parfois « Schema
First » (priorité au schéma). Si ces deux appellations sont généralement employées
indifféremment, elles renvoient en fait à deux concepts légèrement différents. Dans cet article,
nous souhaitons amener les architectes et les développeurs à créer le contrat à partir des
définitions WSDL, avant de créer le code sous-jacent du service. Pour créer le fichier WSDL, les
développeurs peuvent soit créer des définitions de schéma XML spécifiques à l’interface avec
laquelle elles seront utilisées (« WSDL First »), soit utiliser les schémas XML déjà définis dans leur
domaine d’application (« Schema First »). Cet article utilisera la terminologie du concept « WSDL
First ».
Figure 3. Développement d’un service Web par la méthode « WSDL First » (priorité au
WSDL).
Le problème avec la technique consistant à créer d’abord le fichier WSDL, c’est que les outils
produits aujourd’hui ne favorisent pas cette pratique. Elle reste néanmoins faisable, mais difficile à
implémenter. Visual Studio comporte un éditeur de schéma et un éditeur XML mais pas d’éditeur
WSDL. Eclipse ne propose pas non plus d’éditeur WSDL. Heureusement, ces deux environnements
offrent une fonctionnalité permettant de générer le squelette de code du service Web en plus du
code du proxy client à partir d'un fichier WSDL.
Vous pouvez utiliser l’outil de votre choix pour créer votre propre fichier WSDL, y compris VI ou le
Bloc-notes. Plutôt que d’éditer directement le texte, vous pouvez aussi utiliser pour plus de
commodité des outils spécialisés comme XmlSpy d’Altova, dotés de concepteurs WSDL. Toutefois,
cette solution n’est pas forcément appropriée dans la mesure où de nombreux développeurs
restent incapables de « penser en WSDL ».
Il existe une solution à ce problème : concevoir rapidement un prototype d’interface de service
Web en utilisant la technique « Implementaiton First ». Il suffit d’utiliser les fonctions de génération
dynamique de fichier WSDL d’ASP.NET ou du WSDK d’IBM pour créer un fichier modèle WSDL. Il
devient alors possible d'entreprendre le développement selon la méthode « WSDL First » et de
peaufiner l’interface à sa guise. Ce processus s’exécute de façon itérative jusqu’à obtention du
fichier WSDL final.
Comme l’illustre la figure 3 ci-dessus, la création de services Web à partir de la méthode « WSDL
First » comporte trois étapes majeures :
1. Créer le fichier WSDL.
2. Implémenter le document WSDL.
a.
Créer le service Web.
b.
Créer le client du service Web.
Vous pouvez effectuer les sous-étapes A et B de l’étape 2 dans l’ordre de votre choix. Comme elles
dépendent toutes deux du document WSDL, ce qui importe, c’est de créer le document WSDL
avant d’entreprendre A ou B.
Dans la suite de cette section, nous étudierons les étapes du développement du premier exemple
de service Web selon le processus « WSDL First ». Vous trouverez le code source complet de cet
exemple dans le téléchargement qui accompagne cet article.
Création du fichier WSDL à partir de Visual Studio .NET (« Implementation First »)
Avant d’aborder la création de services Web à partir de l'option « WSDL First », qui consiste à
générer d’abord le fichier WSDL, nous étudierons le modèle de développement « Implementation
First » de Visual Studio .NET, qui accorde la priorité à l’implémentation. Nous avons choisi de
commencer par cette méthode car la création d'un fichier WSDL en partant de zéro est une tâche
difficile. Il est beaucoup plus simple avec les outils dont vous disposez de prendre un raccourci et
d’utiliser le fichier WSDL généré par l’approche « Implementation First ». Imaginez qu’il vous faille
entrer manuellement le code suivant :
<?xml version="1.0" encoding="utf-8"?> <definitions xmlns:http="http://schemas.xmlsoap.org/wsdl/http/"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:s="http://www.w3.org/2001/XMLSchema"
xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:tm="http://microsoft.com/wsdl/mime/textMatching/"
xmlns:mime="http://schemas.xmlsoap.org/wsdl/mime/" xmlns:s0="http://example.org/"
targetNamespace="http://example.org/" xmlns="http://schemas.xmlsoap.org/wsdl/"> <types> <s:schema
elementFormDefault="qualified" targetNamespace="http://example.org/"> <s:element name="Add"> <s:complexType>
<s:sequence> <s:element minOccurs="1" maxOccurs="1" name="x" type="s:int" /> <s:element minOccurs="1"
maxOccurs="1" name="y" type="s:int" /> </s:sequence> </s:complexType> </s:element> <s:element
name="AddResponse"> <s:complexType> <s:sequence> <s:element minOccurs="1" maxOccurs="1" name="AddResult"
type="s:int" /> </s:sequence> </s:complexType> </s:element> </s:schema> </types> <message name="AddSoapIn"> <part
name="parameters" element="s0:Add" /> </message> <message name="AddSoapOut"> <part name="parameters"
element="s0:AddResponse" /> </message> <portType name="Service1Port"> <operation name="Add"> <input
message="s0:AddSoapIn" /> <output message="s0:AddSoapOut" /> </operation> </portType> <binding
name="Service1Soap" type="s0:Service1Port"> <soap:binding transport="http://schemas.xmlsoap.org/soap/http"
style="document" /> <operation name="Add"> <soap:operation soapAction="http://example.org/Add" style="document" />
<input> <soap:body use="literal" /> </input> <output> <soap:body use="literal" /> </output> </operation> </binding>
<service name="Service1"> <port name="Service1Soap" binding="s0:Service1Soap"> <soap:address
location="http://localhost/NetWSServer/Service1.asmx" /> </port> </service> </definitions>
Si pour le développeur WSDL chevronné, cet exercice peut sembler banal, pour le reste d’entre
nous, il supposerait d’ingurgiter les 51 pages de spécifications WSDL. C’est pourquoi nous
utiliserons au démarrage la méthode commençant par l’implémentation. Pour les développeurs, la
création d'un service Web en rédigeant du code est en effet la technique la plus simple.
Commencez par créer un projet de service Web ASP.NET en C# dans Visual Studio .NET. Appelez
ce projet WebService1WSDL.
Figure 4. Boîte de dialogue Nouveau projet dans Visual Studio .NET.
Ensuite, ouvrez le code du fichier Service1.asmx. Supprimez les commentaires de la méthode
HelloWorld et remplacez cette méthode par la suivante :
[WebMethod] public int Ajouter(int x, int y) { return -1; }
Figure 5. Code d’un service Web selon la méthode « Implementation First ».
Créez le projet et vérifiez qu’il n’y a pas d’erreur. Puis, cliquez avec le bouton droit sur
Service1.asmx et configurez cette page comme page de démarrage. Vous pouvez maintenant
appuyer sur F5 pour afficher la page de test du service Web (figure 6).
Figure 6. Page de test du service Web.
Soulignons que ce soit le fichier ASMX qui constitue le véritable service Web. La page que vous
visionnez est générée par l’infrastructure pour documenter le service Web et pour permettre au
développeur de tester ce service sans avoir à créer manuellement une application cliente. La
fonctionnalité de test n’est accessible que si vous affichez cette page localement ; elle ne
fonctionne pas pour les services Web utilisant comme paramètres d’entrée des types de données
complexes. Pour des raisons de sécurité, pensez à désactiver cette page de test après avoir
déployé votre service Web sur une machine de production.
Cliquez ensuite sur le lien Service Description (Description du service) situé en haut à droite de
la page. Cette action ouvre une autre page Web affichant le WSDL généré pour votre service. La
fonction de génération du WSDL reste disponible tant que vous ne la désactivez pas délibérément.
Enregistrez le fichier WSDL sur votre disque local et nommez-le WebService1.wsdl.
Figure 7. Fichier WSDL généré par Framework.
Vous venez de créer un fichier WSDL sans avoir eu à apprendre les spécifications WSDL !
Autre conseil important ici : étant donné que nous avons généré le fichier WSDL à partir de ce
projet temporaire, l’emplacement du service Web dans le document WSDL est préprogrammé de
façon à pointer vers ce projet temporaire. Cela n’a aucune incidence sur le service Web en luimême, mais les clients qui utilisent ce fichier WSDL utiliseront cette référence comme
emplacement du service Web. Il est donc important de modifier cette valeur avant de déployer le
fichier WSDL sur le site Web. Nous changerons cette référence plus tard, dès que nous connaîtrons
l’emplacement réel de notre service Web.
Si vous avez oublié de modifier cet emplacement et que les applications clientes ont déjà été
créées, vous pouvez toujours corriger la référence en modifiant l’emplacement vers lequel pointent
les références des clients. Dans .NET et dans WebSphere, les proxys clients de services Web
permettent de configurer correctement la propriété d’URL. Cela s’avère également utile lorsque
vous passez du développement à la production et que vous ne souhaitez modifier que le point de
terminaison du service.
Comme le service Web que nous créons est simple, le fichier WSDL n’a pas besoin d’être peaufiné.
L’étape suivante consiste à créer le service Web .NET.
Implémentation du service Web .NET (« WSDL First »)
À partir du fichier WSDL généré à l’étape précédente, nous allons maintenant créer un nouveau
service Web .NET.
Pour passer du fichier WSDL à un fichier de code source, nous utiliserons une application console
appelée wsdl.exe pour générer le code. Cet outil analyse le fichier WSDL et tous les autres fichiers
externes pour créer un fichier de code source unique contenant toutes les classes, méthodes et
types requis pour implémenter le service Web. Wsdl.exe est installé avec Visual Studio .NET 2003
(et le SDK de .NET). Pour utiliser cet outil, vous devez ouvrir l’invite de commande de Visual
Studio .NET 2003, située par défaut dans le menu Démarrer, sous Tous les
programmes/Microsoft Visual Studio .NET 2003/Outils Visual Studio .NET, puis aller à
l’endroit ou vous avez enregistré le fichier WSDL.
Pour que wsdl.exe génère le fichier source du service Web, exécutez la commande suivante.
wsdl.exe /server WebService1.wsdl
Figure 8. Invite de commande de Visual Studio .NET 2003 exécutant wsdl.exe.
Notez que l’utilitaire génère un message indiquant que le fichier Service1.cs a été créé. Ce fichier
sera le point de départ de notre service Web.
Le fichier généré par wsdl.exe n’est qu’un modèle de la méthode que nous souhaitons
implémenter ; il doit donc être modifié pour fonctionner correctement. La commande wsdl.exe
active génère toujours une classe abstraite pour le service Web lorsqu’elle est exécutée avec
l’option /server. Nous la convertirons en classe concrète en éliminant le mot clé abstract et en
fournissant une implémentation pour la méthode Add. Nous placerons également la classe dans
l’espace de noms WebService1. Nous obtiendrons le code suivant :
namespace WebService1 { /// <remarks/>
[System.Web.Services.WebServiceBindingAttribute(Name="Service1Soap",
Namespace="http://tempuri.org/")] public class Service1 :
System.Web.Services.WebService { /// <remarks/>
[System.Web.Services.WebMethodAttribute()]
[System.Web.Services.Protocols.SoapDocumentMethodAttribute(
"http://tempuri.org/Add", RequestNamespace="http://tempuri.org/",
ResponseNamespace="http://tempuri.org/",
Use=System.Web.Services.Description.SoapBindingUse.Literal, ParameterStyle=
System.Web.Services.Protocols.SoapParameterStyle.Wrapped)] public int Add(int x,
int y) { int result; result = x + y; return result; } } }
Peut-être vous demandez-vous pourquoi ne pas simplement sous-classer la classe abstraite
générée au lieu de la convertir en classe concrète ? Il existe une bonne raison. L’outil wsdl.exe
génère du code doté d’attributs utilisés par le sérialiseur XML de .NET et le runtime des services
Web pour effectuer le mappage des objets vers le XML et inversement. Dans notre exemple, un
attribut utilise l’espace de noms http://tempuri.org pour le document XML généré. Ce type
d'attribut n'est pas hérité par les sous-classes. Par conséquent, si nous sous-classions la classe
abstraite, il nous faudrait couper-coller tous ces attributs dans notre classe concrète.
Plutôt que de dupliquer manuellement tous ces attributs, il est plus simple de modifier le fichier
directement. Bien sûr, cela signifie que si le fichier WSDL est modifié, il vous faudra générer à
nouveau le code source du fichier WSDL. Il convient d’effectuer cette tâche avec soin pour éviter
d’effacer tout le code existant. Vous devrez copier manuellement dans le nouveau fichier le code
d’implémentation du service Web figurant dans l’ancien fichier.
Maintenant que le code source de notre service Web est prêt, nous pouvons créer notre solution
Visual Studio. Créez dans Visual Studio .NET un nouveau projet de service Web ASP.NET en C#
et appelez le WebService1.
Cela fait, copiez le fichier Service1.cs créé précédemment et contenant le code d’implémentation
du service Web dans le répertoire contenant les fichiers du service Web générés par Visual Studio.
Il s’agit généralement de c:\inetpub\wwwroot\WebService1.
Figure 9. Affichage dans l’Explorateur Windows des fichiers du projet de service Web.
L’exemple de service Web Service1.asmx est généré en tant qu’élément de l’Assistant de projet
Visual Studio. Puisque la commande wsdl.exe ne génère que le code d’implémentation du modèle
et non le fichier d’entrée (ASMX), nous préférons réutiliser le fichier Service1.asmx généré par
Visual Studio plutôt que d’en créer un nous-même. Cependant, il existe déjà un fichier source
correspondant au fichier Service1.asmx. Pour associer le fichier ASMX à notre code
d’implémentation, il suffit d’effacer le code d’implémentation généré par Visual Studio
(Service1.asmx.cs) et d’attribuer le nom de ce fichier à notre code d’implémentation. Pour cela,
vérifiez que les fichiers Service1.asmx et Service1.asmx.cs ne sont pas ouverts dans Visual Studio,
puis supprimez le fichier Service1.asmx.cs et attribuez le nom Service1.asmx.cs au fichier
Service1.cs.
Pour vérifier que la transplantation du code a bien fonctionné, sélectionnez Service1.asmx dans
l’Explorateur de solutions de Visual Studio et cliquez sur le menu Afficher, puis sur Code. Le code
du modèle doit s’afficher et l’implémentation de la méthode Add que nous avons modifiée
précédemment doit apparaître dans Visual Studio .NET.
Enfin, pour tester le bon fonctionnement du service, cliquez avec le bouton droit sur
Service1.asmx puis cliquez sur Définir comme page de démarrage. Allez ensuite dans le menu
Déboguer et cliquez sur Démarrer pour créer le projet et ouvrir Service1.asmx dans votre
navigateur. Vérifiez que le service Web fonctionne correctement à l’aide de la page de test.
Figure 10. Page de test du service Web d’addition.
Figure 11. Réponse du service Web à la requête en ajoutant 12 et 45.
Pour empaqueter le service Web .NET, il nous faut maintenant retourner au document WSDL
d'origine pour effectuer quelques modifications mineures. Comme nous l’avons déjà remarqué, le
document WSDL pointe actuellement vers notre service Web de projet temporaire. Maintenant que
nous avons terminé la création du service Web réel, nous pouvons modifier la référence de
localisation de façon à ce qu’elle pointe vers ce service. Pour cela, ouvrez le document WSDL
d'origine et remplacez l’attribut location de l’élément soap:address pour qu’il pointe vers ce
nouveau service Web. Le nouvel élément soap:address doit se présenter comme suit :
<port name="Service1Soap" binding="s0:Service1Soap"> <soap:address
location="http://localhost/NetWSServer/Service1.asmx" /> </port>
Figure 12. Modification de l’emplacement du service Web dans le fichier WSDL.
La création d’un service Web .NET selon la méthode « WSDL First » est maintenant terminée. Nous
allons à présent créer le client de service Web WSDK qui utilisera ce service Web .NET.
Implémentation du client de service Web WSDK
Maintenant que notre service Web est implémenté, nous allons créer un client pour l’utiliser.
Comme le sujet de cet article est l’interopérabilité, nous utiliserons le service Web .NET en
exécutant une page JSP sur le serveur d’application WebSphere d’IBM.
Démarrez Eclipse et créez un nouveau projet Web Dynamique (Dynamic Web Project). Appelez
ce projet WebServiceClient1 et le fichier de projet EAR WebServiceClient1Ear.
Figure 13. Affichage dans Eclipse du projet WebServiceClient1.
Ensuite, ajoutez le fichier WSDL au projet WebServiceClient1. Pour cela, il suffit de faire glisser le
fichier directement dans le projet situé dans le volet de navigation d’Eclipse.
Nous souhaitons maintenant générer un proxy Java pour le service Web. De même que Visual
Studio .NET crée une classe proxy lorsqu’une référence Web est ajoutée à un projet, les outils
WSDK vont créer un jeu de fichiers Java qui permettront d’appeler le service Web plus facilement
que s’il fallait intervenir directement sur les échanges réseau. Puisque nous avons déjà ajouté le
document WSDL au projet, vous pouvez créer le proxy en cliquant avec le bouton droit sur le
document WSDL, en pointant sur le menu Web Services (Services Web) et en cliquant sur
Generate Client (Générer le client). Cela ouvrira l’Assistant WSDL to Java Bean Proxy (WSDL
vers proxy Java Bean) illustré à la figure 14.
Figure 14. Assistant WSDL to Java Bean Proxy.
Dans la première boîte de dialogue, pensez à cocher la case Test the generated proxy (Tester le
proxy généré). Cela ouvrira la page JSP (similaire à la page de test générée à partir de
Service1.asmx à la création d’un service Web dans Visual Studio) qui servira à vérifier les classes
proxy générées. Pour les autres options de l’Assistant, les valeurs par défaut conviennent. Vous
pouvez donc cliquer sur Finish (Terminer).
Dès qu’Eclipse a terminé de générer tous les fichiers nécessaires, la page de test suivante s’affiche
dans la fenêtre principale d’Eclipse.
Figure 15. Affichage dans l’IDE d’Eclipse de la page de test JSP.
Cliquez sur add(int,int) dans le volet Methods (Méthodes) et indiquez deux nombres à additionner
dans le volet Inputs (Entrées). La somme de ces deux nombres doit s’afficher dans le volet du bas
Result (Résultat). En arrière-plan, la page JSP appelle la classe proxy du service Web, générée
précédemment par l’Assistant WSDK du service Web, pour appeler notre service Web .NET. Étant
donné que nous avons modifié la référence d'emplacement du service Web dans le document
WSDL, notre client Java sait exactement où trouver le service Web que nous avons créé lors de
l’étape 2. Si la somme de vos deux nombres est correcte, cela démontre que le proxy Java a été
généré correctement et qu'il communique déjà avec le service Web .NET.
Il existe une autre méthode pour vérifier la connexion entre un service et un client. Elle consiste à
définir un point d’arrêt dans le code du service Web de Visual Studio .NET puis à exécuter le
service Web en mode de débogage. Côté Java, si vous exécutez la fonction d’addition dans la page
JSP, Visual Studio .NET devrait normalement interrompre l’exécution du service et s’arrêter au
point que vous avez défini.
À ce stade, nous avons vérifié le bon fonctionnement de la classe de proxy Java générée par le
WSDL. Nous avons également utilisé la page de test générée par l’Assistant WSDK pour tester les
appels vers le service Web .NET à partir de Java. Puisque ces deux points fonctionnent bien, la
démo concernant le service Web .NET et le client de service Web Java pour le service Web 1 est
maintenant terminée. Si nous n’avons pas décrit en détail le processus de création d’un client de
service web .NET et du serveur de service Web Java, l’exemple de code accompagnant cet article
inclut des implémentations de serveurs et de clients dans .NET et dans Java. Si vous souhaitez les
générer vous-même, vous trouverez d’autres didacticiels concernant ces procédures.
Service Web 2 : Évaluations et rapports
Le deuxième exemple repose sur le premier pour créer un service Web utilisant un schéma de
données beaucoup plus complexe. Notre objectif dans cet exemple est de montrer l’interopérabilité
entre .NET et WebSphere avec des types de données complexes. Dans le schéma de cet exemple
figurent, en plus des types standards du schéma XML, des types complexes, des types simples, des
énumérations, des restrictions, des tableaux et des types qui héritent d’autres types. Les
différences entre les deux plates-formes et les problèmes d’interopérabilité que pose le
développement de services Web sont plus manifestes dans cet exemple en raison de sa plus
grande complexité.
Scénario
La société Stuff Sellers vend une gamme variée de produits à différents types d’entreprises.
Chaque entreprise est autorisée à soumettre des appréciations des produits qu’elle achète. Le
scénario étudié dans le service Web 2 est celui d’un service d’évaluation permettant aux
utilisateurs de soumettre un rapport pour une évaluation donnée ou d’obtenir une liste de tous les
rapports pour une évaluation donnée identifiée par un nombre. Dans cet exemple, nous pouvons
considérer que les rapports correspondent aux appréciations et que la somme de ces appréciations
permet de générer l’évaluation finale.
Schéma de données
La figure suivante illustre les éléments majeurs du schéma de données.
Figure 16. Principaux éléments du schéma de données du service Web 2.
L’élément de plus haut niveau est Ratings (évaluations). Chaque élément Ratings contient un
tableau d’éléments ReportSet (jeu de rapports) encapsulé dans le type ReportSetArray (tableau
de jeux de rapports). Chaque élément ReportSet contient un tableau d’éléments Report (rapport)
encapsulé dans le type ReportArray (tableau de rapports). Ces trois éléments de niveau supérieur
sont des types de données qui héritent du type MyBase (ma base). Pour consulter la vraie
définition de schéma XML pour ce schéma de données, reportez-vous au fichier
WebService2SharedTypes.xsd à télécharger avec les exemples.
Comme avec WSDL, il existe deux façons de générer un schéma XML. La première consiste à
concevoir manuellement le schéma en utilisant un concepteur de schéma (dans le cas d’un schéma
XML, Visual Studio inclut un concepteur). La deuxième consiste à inférer le schéma à partir d’un
type .NET existant à l’aide de l’utilitaire xsd.exe. Comme avec WSDL, ces deux techniques
peuvent être associées au sein d’un processus itératif permettant de peaufiner le schéma jusqu’à
ce qu’il soit parfait.
Dans certains cas, le schéma de données a déjà été défini et existe indépendamment de
l’implémentation ou de l’interface du service Web. Cette situation peut se présenter si vous devez
créer un service Web à partir du modèle de conception d’un système existant. Que vous le
conceviez vous-même ou qu’il vous soit fourni, le schéma de données doit être décrit dans un
fichier XSD de schéma XML autonome pour que sa modularité et sa réutilisation puissent être
garanties.
La figure suivante est un diagramme de classes illustrant le schéma XML défini dans le schéma de
données pour ce service Web. Ce schéma est défini dans le même code dans le fichier
WebService2SharedTypes.xsd. Le code généré à partir du schéma de données (fichier de
schéma XML) sur chaque plate-forme doit suivre une structure de classes similaire à celle décrite
dans ce diagramme.
Figure 17. Diagramme de classes du schéma de données dans le service Web 2.
Définition du service Web
Dans le premier service Web, nous avons utilisé .NET Framework pour générer le fichier modèle
WSDL initial. Comme notre service Web était simple, cette étape suffisait à créer le WSDL.
Malheureusement, le présent service Web n’est pas aussi simple et une intervention manuelle
s’impose. Pour garantir l’interopérabilité, nous avons dû apporter des modifications au fichier
modèle WSDL, entre autres inclure des références à notre schéma de données, changer l’espace de
noms et créer les messages appropriés aux portTypes (types de ports).
Le document WSDL de ce service Web définit deux opérations : GetRatings et SubmitReport.
GetRatings renvoie un type Ratings si l’ID fourni correspond à une évaluation donnée.
L’opération SubmitReport permet aux clients de soumettre un nouveau rapport associé à des
éléments Ratings et ReportSet spécifiques. Vous pouvez consulter la définition WSDL de ces
opérations dans le fichier WebService2.wsdl à télécharger avec les exemples.
Il existe deux façons d’inclure les définitions de schéma XML dans des fichiers WSDL : vous pouvez
définir le schéma inline dans le fichier WSDL ou référencer vos fichiers de schéma XML dans votre
fichier WSDL à l’aide de l’élément xsd:import. Les définitions inline faisant partie du fichier WSDL,
leur maintenance est simple. Cependant, comme le schéma de données décrit les données et non
l’interface du service Web et qu’il est parfois utilisé indépendamment de cette interface, il est plus
logique de séparer ces deux définitions dans deux fichiers distincts. Si votre service Web est simple
et n’utilise pas trop de types de données complexes, le schéma inline fonctionnera très bien (dans
le cas du service Web 1, par exemple), mais il est généralement recommandé de séparer le
schéma de données de la définition du service web.
Dans ce service Web, nous avons décidé d’utiliser la méthode xsd:import pour référencer le fichier
de schéma XML externe (WebService2SharedTypes.xsd) à partir du fichier WSDL
(WebService2.wsdl). Dans le fichier WSDL, cette méthode se présente comme suit :
<types> <xs:schema targetNamespace="http://example.org/"> <!-- Import the Shared
Types --> <xs:import namespace="http://example.org/sharedtypes"
schemaLocation="WebService2SharedTypes.xsd"/> <!-- Import the Message Parameters -> <xs:import namespace="http://example.org/interface"
schemaLocation="WebService2Interface.xsd"/> </xs:schema> </types> . . .
Implémentation
Pour créer l’exemple de code du service Web dans .NET, exécutez wsdl.exe en définissant les
paramètres suivants :
wsdl.exe WebService2.wsdl WebService2Interface.xsd WebService2SharedTypes.xsd
Notez que pour exécuter wsdl.exe, il faut que le fichier WSDL d’entrée et tous les fichiers XSD
inclus soient spécifiés ensemble dans la ligne de commande. Lors de l'importation d'un XSD dans
un WSDL, il est possible de fournir un attribut schemaLocation. Selon la spécification WSDL, cet
attribut sert à indiquer l’emplacement du schéma mais cette indication n’est pas toujours suivie par
les outils qui interprètent le fichier WSDL. Dans notre exemple, wsdl.exe n’utilise pas l’indication
schemaLocation et il est donc nécessaire de spécifier les fichiers de schéma externes dans la ligne
de commande. En revanche, les outils WSDK d’IBM reconnaissent l’indication schemaLocation et
chargeront les fichiers directement le moment venu.
Soulignons un détail important : l’élément ID de MyBase porte le type xsd:int et inclut l’attribut
minOccurs=0. La définition de schéma XML de MyBase se présente comme suit :
<xs:complexType name="MyBase"> <xs:sequence> <xs:element minOccurs="0"
maxOccurs="1" name="ID" type="xs:int" nillable="true" /> </xs:sequence>
</xs:complexType>
Si minOccurs=0, la propriété ID peut être exclue du document XML produit. Cela pose un
problème sur la plate-forme .NET. Dans .NET, le type xsd:int est mappé vers le type Int32 qui est
un type de valeur ; or les types de valeur ne peuvent pas être NULL. Cela signifie principalement
qu’il est impossible de déterminer si la propriété ID a été définie puisque toutes les valeurs de
Int32 sont valides. La plate-forme .NET Framework résout ce problème en créant une autre
variable appelée IDSpecified de type Boolean. Cette variable est vérifiée par la logique de
sérialisation XML de .NET qui détermine si la variable ID a été définie, essentiellement en attribuant
à la variable ID la valeur NULL/not NULL. Ainsi, lorsque vous tentez d’accéder à la variable ID,
vous devez toujours vérifier ou définir d’abord la variable IDSpecified. Pour plus d’informations
sur ce modèle d’utilisation, consultez la documentation MSDN sur la classe XmlIgnoreAttribute (en
anglais).
Une fois traduit en code C#, le type MyBase se présente comme suit :
[System.Xml.Serialization.XmlTypeAttribute(Namespace=
"http://example.org/sharedtypes")]
[System.Xml.Serialization.XmlIncludeAttribute(typeof(Report))]
[System.Xml.Serialization.XmlIncludeAttribute(typeof(ReportSet))]
[System.Xml.Serialization.XmlIncludeAttribute(typeof(Ratings))] public class MyBase
{ public int ID; [System.Xml.Serialization.XmlIgnoreAttribute()] public bool
IDSpecified; }
Ce problème ne se pose pas si l’on utilise l’exemple WebSphere. En effet, lorsqu’un xsd:int est
utilisé avec minOccurs=0, les outils WSDK génèrent une variable de type java.lang.Integer au
lieu de la variable native Java de type int. Le type java.lang.Integer est un type de référence et
il est possible pour une variable de ce type de prendre la valeur NULL pour indiquer qu’elle n’a pas
été définie. Voici la traduction du type MyBase en code Java obtenue avec les outils WSDK :
public class MyBase implements java.io.Serializable { private java.lang.Integer ID;
public MyBase() { } public java.lang.Integer getID() { return ID; } public void
setID(java.lang.Integer ID) { this.ID = ID; } }
La comparaison entre le code C# généré à partir du schéma XML et le code Java généré pour le
même schéma met en exergue une autre différence relative à l’inclusion des attributs de code dans
le code C#. Comme nous l’avons déjà dit, ces attributs sont utilisés par le sérialiseur XML dans
.NET pour faciliter le mappage depuis l’instance de classe vers XML. Java nécessite également des
« métadonnées » similaires. Dans le cas du runtime des services Web dans WSDK, ces
métadonnées sont stockées de façon indépendante dans un fichier XML qui définit les mappages de
types. Pour plus d’informations, consultez la documentation WSDK.
Autre point intéressant : si vous examinez les classes générées par les outils .NET ou WSDK, vous
constaterez peut-être que les types de données générés sont différents de ceux que vous auriez
écrits en tant que développeur, sans tenir compte des questions d’interopérabilité. Regardez la
classe Ratings.java générée par WSDK. Si nous enlevons les éléments de gestion interne du code,
elle se présente comme suit :
public class Ratings extends org.example.MyBase implements java.io.Serializable {
private java.lang.String description; private int confidenceLevel; private
java.util.Calendar expiration; private org.example.ReportSetArray allReports;
public Ratings() { } public java.lang.String getDescription() { return description;
} public void setDescription(java.lang.String description) { this.description =
description; } public int getConfidenceLevel() { return confidenceLevel; } public
void setConfidenceLevel(int confidenceLevel) { this.confidenceLevel =
confidenceLevel; } public java.util.Calendar getExpiration() { return expiration; }
public void setExpiration(java.util.Calendar expiration) { this.expiration =
expiration; } public org.example.ReportSetArray getAllReports() { return
allReports; } public void setAllReports(org.example.ReportSetArray allReports) {
this.allReports = allReports; } }
Il se peut que les membres des données primitives de type int et string correspondent à ce que
n’importe quel développeur aurait rédigé : application des conventions JavaBean et empaquetage
des méthodes d’obtention et de réglage autour d’un membre privé. Mais c’est alors qu’apparaissent
les différences. La valeur de date est gérée par un élément Calendar, et non par un java.util.Date.
Et un élément Array est empaqueté par une classe personnalisée, également accessible via une
paire de méthodes d’obtention/réglage. Cette classe générée est probablement différente de celle
que vous auriez écrite mais elle fonctionne et présente en outre l’avantage d’être interopérable.
Vous pourriez faire le même constat concernant le code généré par les outils .NET.
Nous avons suivi les grandes étapes du processus décrit dans la section précédente concernant le
service Web 1 pour créer deux projets Visual Studio : l’un pour le client et l’autre pour le serveur.
De même, nous avons créé deux projets WebSphere avec le WSDK. Tous ces clients et serveurs
sont interopérables. Pour voir comment tout cela fonctionne, compilez et exécutez les projets
Visual Studio et Eclipse à télécharger avec les exemples. Avant cela, n’oubliez pas de lire le fichier
Readme concernant ce téléchargement.
Dans cet exemple, nous avons étudié l’utilisation des types de données complexes dans un service
Web interopérable. Le schéma XML W3C joue un rôle fondamental dans la définition des types de
message et des éléments de données à échanger. Cette section a expliqué comment développer un
XSD pour un type de données complexe et comment employer ce schéma XML dans un document
WSDL. Elle a également souligné quelques questions adjacentes à connaître, notamment la
différence entre les types de valeur et les types de référence dans .NET et les conséquences de
cette différence sur la sérialisation XML.
Dans l’exemple suivant, nous allons développer davantage les idées abordées dans cet exemple et
étudier l’interopérabilité avec les éléments de données extensibles.
Service Web 3 : Recherche de produits
La plupart des services Web emploient un schéma de données fixe, ce qui signifie que les types de
données transmis sur le réseau sont déjà connus au moment de la conception. Mais parfois, les
schémas de données statiques ne permettent pas de répondre aux besoins de l’application.
Considérons le scénario d’entreprise suivant.
Scénario
Comme nous l’avons déjà dit dans le service Web 2, Stuff Sellers vend différents produits aussi
bien aux consommateurs qu’à d’autres entreprises. Comme cette gamme de produits est très
vaste, le directeur nous a demandé de créer un service Web qui faciliterait la recherche de produits
dans la base de données. Cet outil sera utilisé indirectement par les consommateurs par le biais de
la vitrine Internet du magasin et directement par les autres entreprises.
Le directeur souhaiterait que ce service Web satisfasse aux trois exigences suivantes :
1. Il doit accepter les données dynamiques. Dans la mesure où la société Stuff Sellers ajoute
souvent de nouveaux types de produits à sa base de données, la définition du service Web
doit être suffisamment flexible pour prendre en charge les nouveaux types sans que cela
n’interrompe le fonctionnement des clients existants.
2. Il doit prendre en charge les opérations de base. Les entreprises qui recherchent des
produits Stuff Sellers seront probablement intéressées par des produits présentant une
propriété spécifique. Un magasin de vente au rabais recherchera des produits coûtant
moins de 50 centimes, par exemple. Le service Web doit donc être capable de renvoyer des
données et permettre au client d’effectuer sans difficulté une simple vérification des
résultats obtenus pour cette propriété.
3. Il doit être extensible. La société Stuff Sellers aura probablement besoin de modifier le
service Web à l’avenir et souhaite donc que ce service puisse prendre en charge facilement
de nouvelles fonctions, avec une incidence minimum sur les clients existants.
Outre ces exigences, le directeur souhaiterait que l’interface du service Web soit aussi simple que
possible. Plus précisément, le service Web ne devra avoir qu’un seul point d’entrée.
Si nous analysons les produits vendus actuellement par Stuff Sellers, nous constatons que tous les
produits sont dotés au minimum d’un nom (Name), d’une description (Description), d’un prix
(Price) et d’une unité de gestion des stocks (SKU). Ces attributs constituent les propriétés de base
de tous nos produits. De plus, chaque produit est doté de son propre jeu d’attributs unique. Par
exemple, un film DVD est doté d’un code région (Region), d’un format de vidéo (Format), d’une
langue (Language) et d’une date de parution (ReleaseDate). Un livre est doté d’une liste d’auteurs
(Authors), d’un éditeur (Publisher), d’un code ISBN (ISBN) et d’une date de publication
(PublishedDate). Par conséquent, le type de produit de base exposera toutes les propriétés
communes à tous les types de produits et acceptera les extensions pour les propriétés spécifiques
à certains types de produits.
Schéma de données
Voici un diagramme de classes du schéma de données défini pour le service Web 3.
Figure 18. Diagramme de classes du schéma de données du service Web 3.
La définition de type de schéma XML pour SearchResult (résultat de la recherche) dans
WebService3SharedTypes.xsd se présente comme suit :
<xs:complexType name="SearchResult"> <xs:sequence> <xs:element minOccurs="1"
maxOccurs="1" name="SKU" type="xs:string"/> <xs:element minOccurs="1" maxOccurs="1"
name="Price" type="xs:decimal" /> <xs:element minOccurs="1" maxOccurs="1"
name="Name" type="xs:string"/> <xs:element minOccurs="1" maxOccurs="1"
name="Description" type="xs:string" /> <xs:element minOccurs="1" maxOccurs="1"
name="ProductType" type="xs:string" /> <xs:any minOccurs="1" maxOccurs="1"
processContents="lax" /> </xs:sequence> </xs:complexType>
Comme vous pouvez le constater, SearchResult est le type parent qui représente tous les
produits trouvés par le service Web. SearchResult contient les propriétés communes à tous les
types de produits. De plus, SearchResult contient également un élément xsd:any qui sert de
caractère générique. Un fichier XML conforme à ce schéma peut inclure n’importe quel élément
XML à cet emplacement. Dans le cas qui nous intéresse, l’élément xsd:any contiendra l’un des
types de propriété de produit que le service Web peut renvoyer. Nous en avons défini trois dans le
fichier WebService3ExtendedProperties.xsd : DvdProperties, BookProperties et CDProperties
(respectivement, propriétés du DVD, du livre et du CD). Dans la pratique, l’application cliente
accèdera aux propriétés communes en vérifiant SearchResult et accèdera aux propriétés étendues
de ce produit en vérifiant la variable du membre contenant les types de propriétés spécifiques du
produit.
Plutôt que d’utiliser xsd:any dans le schéma pour le mappage des éléments XML, il est également
possible d’employer un élément string (chaîne) dans le schema qui contiendra le XML généré de
façon dynamique. L’utilisation d’une chaîne est similaire à celle du caractère générique. La
différence, c’est que le XML contenu dans la chaîne est défini pour échapper à la transmission et
sera donc opaque pour les analyseurs XML, ce qui n’est pas ce que nous souhaitons. Il est plus sain
d’intégrer le XML dynamique dans le document XML que de définir son échappement dans un
élément de chaîne. Dans les deux cas, du travail supplémentaire s’impose, qu’il s’agisse de générer
le XML côté expédition ou de l’analyser côté réception.
Définition du service Web
Comme dans le deuxième exemple, le document WSDL de ce service Web est composé de deux
fichiers : WebService3.wsdl et WebService3SharedTypes.xsd. WebService3.wsdl contient
les déclarations définissant le service Web et WebService3SharedTypes.xsd est un fichier de
schéma XML contenant les types de données utilisés par le service Web. Voici un exemple de
capture de message SOAP renvoyé par le service Web au client.
<?xml version="1.0" encoding="utf-8" ?> <soap:Envelope
xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <soap:Body> <SearchResponse
xmlns="http://example.org/sharedtypes"> <SearchResult> <SearchResult>
<SKU>B05502HB9I</SKU> <Price>14.99</Price> <Name>Spain's History</Name>
<Description>Short documentary on the history of the Spain.</Description>
<ProductType>DVD</ProductType> <DvdProperties
xmlns="http://example.org/resulttypes"> <Region>EUROPE</Region>
<Format>PAL</Format> <Language>Spanish</Language> <ReleaseDate>2000-0514</ReleaseDate> </DvdProperties> </SearchResult> <SearchResult>
<SKU>A04D5E87RJ</SKU> <Price>20.00</Price> <Name>Spain's History</Name>
<Description>Companion coffee table book to the documentary "Spain's
History".</Description> <ProductType>Book</ProductType> <BookProperties
xmlns="http://example.org/resulttypes"> <Authors> <Author>Mark Person</Author>
</Authors> <Publisher>BookPub Central</Publisher> <ISBN>0459756212</ISBN>
<PublishedDate>2003-08-08</PublishedDate> </BookProperties> </SearchResult>
</SearchResult> </SearchResponse> </soap:Body> </soap:Envelope>
Il existe également un troisième fichier, WebService3ExtendedProperties.xsd, qui n’est pas
importé dans le WSDL mais qui est essentiel pour que le service Web puisse générer une réponse
et que les clients puissent interpréter cette réponse. Ce fichier contient les définitions de la partie
dynamique des données : les propriétés étendues des types de produits.
La séparation des types de produits des types utilisés par le service Web présente un avantage : la
possibilité d’étendre les types de produits sans modifier l’interface. À l’avenir, Stuff Sellers sera
amené à étendre son activité et à vendre d’autres types de produits. Comme les résultats de
recherche contiendront ces nouveaux produits, il est indispensable que l’ajout de nouveaux
produits puisse s’effectuer sans difficulté. Dans notre modèle de conception, la prise en charge de
nouveaux types est simple : il suffit d’étendre l’implémentation du service Web pour qu’il renvoie
ces nouveaux types et d’ étendre le schéma de données défini dans
WebService3ExtendedProperties.xsd. Dernières étapes : publier le nouveau fichier XSD sur le
Web et informer les utilisateurs de cette modification. Il n’est pas nécessaire de modifier le fichier
WSDL.
Les clients du service Web qui ne souhaitent pas utiliser les propriétés étendues ou se contentent
de les transmettre aux autres services peuvent choisir d’ignorer ces propriétés. À l’exécution, ces
clients n’ont pas besoin de désérialiser le Blob XML dans des objets. Par exemple, si une application
est écrite pour filtrer des produits uniquement en fonction du prix, le type de produit renvoyé n’a
aucune importance. Dans ce cas, le client doit vérifier uniquement la propriété de base Price du
type SearchResult et peut ignorer en toute sécurité les propriétés étendues.
Grâce à la flexibilité qu’offre l’élément xsd:any, le service Web peut ajouter de nouvelles fonctions
sans que cela n’interrompe le fonctionnement des clients existants. Les nouveaux clients du service
Web pourront utiliser les nouveaux types de produits tandis que les applications existantes se
contenteront de les ignorer. Même si des types de produits sont éliminés, les clients existants du
service Web continueront de fonctionner correctement : il leur suffira de ne pas exécuter le code
appartenant aux anciens produits. Cette méthode de conception offre un compromis idéal : les
messages de réponse peuvent être étendus pour les nouvelles applications sans que cela n’altère le
bon fonctionnement des applications existantes.
Implémentation
On appelle « sérialisation XML » ou « liaison XML » la conversion entre le XML et les instances
correspondantes des objets. En général, la conversion de paramètres en appel de service Web et
du XML transmis en demande ou en réponse du service Web est effectuée automatiquement par le
runtime des services Web. Cependant, si l’on utilise des extensions de schéma non définies dans le
WSDL (ou les XSD correspondants importés), cette sérialisation et cette désérialisation XML
doivent être effectuées manuellement. La plate-forme .NET propose des outils et des interfaces de
programmation permettant cela.
IBM WebSphere ne propose pas d’interface publique permettant d’effectuer manuellement une
sérialisation XML. Le client et le serveur Java doivent être dotés d’une capacité complémentaire
pour effectuer la liaison entre Java et XML. Il s’agit de l’API JAXB qui fait partie du kit des
développeurs de services Web Java de de Sun (JWSDP). Le kit JWSDP vous fournira le compilateur
JAXB, capable de générer la classe Java à partir du schéma XML de façon similaire à l’utilitaire
xsd.exe dans .NET. Il est beaucoup plus simple d’utiliser les classes pour référencer les types de
données que de manipuler directement les éléments XML. Dans JAXB, les classes de type de
données générées sont également responsables des opérations d’ordonnancement (ou
« marshalling ») et de « sérialisation » vers les fichiers XML (qui doivent être conformes au XSD),
ainsi que des opérations inverses.
Le SDK de .NET Framework inclut également des outils et une infrastructure de liaison des données
XML avec les classes .NET. L’outil Xsd.exe analyse les fichiers XSD pour créer un fichier de code
source correspondant contenant les classes de définition des types de données. À l’exécution, les
applications peuvent utiliser l’espace de noms System.Xml.Serialization des classes pour créer un
graphique d’objet à partir du XML transmis et inversement.
Par exemple, à la compilation, pour générer des classes C# à partir du schéma
WebService3ExtendedProperties.xsd, utilisez la commande suivante :
xsd.exe /classes WebService3ExtendedProperties.xsd
Ensuite, à l’exécution, pour créer le graphique d’objet à partir d’un fichier, utilisez ces quelques
lignes de code :
FileStream fs = new FileStream(path, FileMode.Open); XmlSerializer s= new
XmlSerializer(typeof(BookPropertiesType)); BookPropertiesType props; try { props=
(BookPropertiesType) s.Deserialize(fs); } finally { fs.Close(); }
Normalement, pour les types de données utilisés dans les interfaces des services Web, la création
des classes de type de données et la sérialisation sont exécutées automatiquement : la création
des classes est exécutée par l’outil wsdl.exe ou par l’utilitaire « Ajouter une référence Web » dans
Visual Studio et la sérialisation, par le runtime des services Web dans .NET. Lorsque vous
transmettez un paramètre à un service Web, ce paramètre est automatiquement sérialisé en XML
pour pouvoir être transmis sur le réseau. Il est nécessaire ici d’utiliser xsd.exe pour créer les
classes car le schéma WebService3ExtendedProperties.xsd n’est pas explicitement inclus dans
la définition de l’interface du service Web.
Qu’en est-il de la sérialisation à l’exécution ? Lorsque l’outil xsd.exe analyse un schéma, il mappe
les éléments xsd:any vers des champs de type XmlElement. En modifiant les classes générées,
en changeant le type de champ par System.Object plutôt que par System.Xml.XmlElement et en
associant au champ les attributs XmlElementAttribute, nous pouvons demander à l’infrastructure
de mapper les données XML vers des types de données .NET spécifiques plutôt que vers un
élément générique XmlElement. Par exemple, dans cet extrait de code, le champ Any sera mappé
vers l’une des trois propriétés étendues.
[System.Xml.Serialization.XmlElementAttribute(ElementName="DvdProperties",
Type=typeof(NetWSServer3.DvdPropertiesType),
Namespace="http://example.org/resulttypes")]
[System.Xml.Serialization.XmlElementAttribute(ElementName="BookProperties",
Type=typeof(NetWSServer3.BookPropertiesType),
Namespace="http://example.org/resulttypes")]
[System.Xml.Serialization.XmlElementAttribute(ElementName="CDProperties",
Type=typeof(NetWSServer3.CDPropertiesType),
Namespace="http://example.org/resulttypes")] public object Any;
Ces modifications des classes générées permettront au runtime de .NET de sérialiser de façon
automatique et implicite les types d’objet de propriété Product depuis et vers XML. Sans cette
fonctionnalité, le développeur devrait effectuer une sérialisation explicite de ces classes vers XML.
C’est d’ailleurs la solution retenue par WebSphere pour les types de propriété étendus. (Pour plus
de détails, voir l’exemple de code).
Lors du développement de cet exemple, il a fallu déterminer entre autres s’il fallait faire de
ProductType une énumération ou une simple chaîne. L’avantage avec l’énumération, c’est que les
différents types sont indiqués de façon explicite et qu’il n’y a donc pas de confusion possible.
Cependant, nous avons finalement décidé de ne pas utiliser d’énumération à cause des exigences
d’extensibilité : notre solution doit être suffisamment flexible pour qu’il soit possible de créer des
types de produits supplémentaires ou d’en éliminer sans que cela n’interrompe le fonctionnement
des clients existants. Si ProductType est défini en tant qu’énumération, la transmission de
nouveaux types de produits à d’anciens clients entraînerait un dysfonctionnement. C’est pourquoi
nous avons utilisé une chaîne, celle-ci offrant la possibilité d’étendre les lignes de produits sans
interrompre le fonctionnement des clients du service Web existants.
Comme dans le service Web 2, les types de données générés ici par les outils seront probablement
différents de ceux qu’aurait rédigés un développeur tentant de modéliser le problème sous forme
de code. Cependant, là encore, le principal avantage de commencer par le WSDL et de générer le
code réside dans l’interopérabilité.
Comme dans le service Web 2, nous avons suivi les grandes étapes du processus présenté dans
l’introduction de cet article pour produire des projets de clients et de serveurs pour Visual Studio et
le kit de développement (SDK) de services Web WebSphere. Une fois encore, les clients et serveurs
obtenus offrent une interopérabilité totale. Bien, arrêtons les discussions et passez à l'action.
Compilez et exécutez les projets Visual Studio et Eclipse dans les exemples à télécharger pour
vérifier l’extensibilité de ce service Web 3 en action.
Le service Web 3 illustre l’utilisation de types de données complexes inclus de façon statique ou
dynamique dans un document WSDL. La prise en charge des types extensibles permet d’étendre et
de modifier l’interface en réduisant au minimum les modifications à apporter au service Web et aux
clients de ce service.
Conclusion
Les trois services Web décrits dans cet article démontrent qu’il est tout à fait possible de créer des
services Web interopérables en utilisant des types de données complexes. La méthode la plus
simple avec les outils de développement, qui commence par l’implémentation (« Implementation
First »), pose souvent des problèmes d’interopérabilité. En revanche, si l’on définit d’abord
l’interface du service Web (WSDL) et que l’on génère ensuite les clients et les serveurs à partir de
cette définition d’interface, il est possible d’éviter de nombreux pièges pouvant compromettre
l’interopérabilité. Bien que nous ayons limité notre approche « WSDL First » à .NET et à
WebSphere, les concepts illustrés s’appliquent aux problèmes d’interopérabilité entre n’importe
quelles plates-formes.
La rédaction manuellement du WSDL n’est pas simple. Cet article a aussi décrit la méthode
consistant à développer sur le mode itératif et à peaufiner les fichiers WSDL et les définitions de
schéma XML W3C en utilisant les outils de prototype inclus dans Visual Studio .NET et WSDK.
Enfin, cet article fournit des conseils et des indications sur les pièges possibles que recèle la
création de services Web interopérables et extensibles. Si nous poursuivons cet idéal
d’interopérabilité des services Web, peut-être le rêve d’un accès omniprésent à n’importe quel
système, indépendamment de la plate-forme ou de l’architecture, deviendra-t-il réalité.
Voici la liste des recommandations et astuces mentionnées dans cet article.
Recommandations/Astuces
Utilisation des documents WSDL
1. Lors de la rédaction manuelle du code WSDL, prenez garde au contexte de l’espace de
noms actuel. La chaîne de l’espace de noms doit être rigoureusement identique dans toutes
les références au même espace de noms.
2. Si votre service Web doit retourner un type de données complexe pour lequel le mappage
vers et depuis XML n’est pas bien défini, envisagez de créer votre propre type de données
pour représenter les mêmes données. Cela optimise la portabilité sans empêcher la
transmission des données nécessaires.
3. Pour garantir la modularité et la réutilisation, utilisez l’importation XSD plutôt que les
définitions de schéma inline. Reportez-vous aux exemples du service Web 2 et du service
Web 3.
4. Prenez garde aux attributs optionnels des types de données dans la définition de schéma
XML. Certains d’entre eux peuvent altérer le mode de génération du code
d’implémentation. Reportez-vous à l’exemple du service Web 2 sur la définition de la
variable ID.
5. WS-I (Web Services Interoperability Organization), organisation pour l’interopérabilité des
services Web, est un organisme officiel créé pour promouvoir l’interopérabilité des services
Web entre toutes les plates-formes et langues. Cette organisation a créé une spécification
pour l’interopérabilité appelée WS-I Basic Profile et fournit des outils permettant de vérifier
si les fichiers WSDL respectent cette spécification.
6. Lorsqu’une exception se produit dans le service Web, un élément SOAPFault est renvoyé
dans le message SOAP. En principe, l’infrastructure du service Web s’empare de l’élément
SOAPFault et renvoie sa propre classe d’exception. Dans .NET Framework, SOAPFault est
pris et empaqueté dans la classe System.Web.Services.Protocols.SoapException. Dans WebSphere,
l’exception renvoyée est com.ibm.ws.webservices.engine.WebServicesFault. Vous trouverez des
informations complémentaires sur les erreurs dans les propriétés des classes d’exceptions
mentionnées.
7. Tous les types complexes XSD sont convertis en classes. Comme Java ne prend pas
actuellement en charge les énumérations, les valeurs d’énumérations sont traduites en
constantes public static string dans la classe Java correspondante.
8. Certaines fonctionnalités du schéma XSD ne sont pas bien représentées sur certaines
plates-formes actuelles, par exemple les types d’entiers non signés (unsigned integer) dans
XSD. Si .NET fournit des types non signés, ce n’est pas le cas de Java. C’est pourquoi il
n’est pas recommandé de les utiliser dans des services Web interopérables.
9. L’utilisation des tableaux est risquée si l’on utilise des conditions de limitation. Dans .NET,
si un tableau vide est sérialisé, seul l’élément d’en-tête XML est créé. Si un tableau NULL
est sérialisé, il est totalement ignoré et aucune sérialisation en XML ne sera effectuée. En
revanche, Java sérialise l’élément XML dans les deux cas. La différence, c’est que pour la
case NULL, le runtime des services Web dans WebSphere balise l’élément XML à l’aide d’un
attribut xsd:nil=true pour indiquer que le tableau porte la valeur Null.
10. Lorsqu’un client WebSphere désérialise un tableau .NET vide, il crée un tableau avec une
référence NULL. Si un client WebSphere désérialise un tableau .NET Null, une exception
java.lang.NullPointerException est insérée.
11. Lorsqu’un client .NET désérialise un tableau WebSphere vide, il crée un tableau d’une
longueur égale à zéro. Lorsqu’un client .NET désérialise un tableau WebSphere Null, il
attribue la valeur NULL à la référence du tableau.
Utilisation de Wsdl.exe
Lorsqu’un fichier WSDL référence d’autres fichiers (par exemple, des fichiers XSD), vous devez
fournir de façon explicite l’emplacement de ces fichiers sous forme d’arguments dans wsdl.exe.
Pour des raisons de sécurité, wsdl.exe ne charge pas automatiquement les fichiers indiqués par
des références schemaLocation dans le fichier WSDL. Par exemple, si vous générez le code pour le
service Web 2, vous devrez exécuter la commande suivante :
wsdl.exe WebService2.wsdl WebService2Interface.xsd WebService2SharedTypes.xsd
Reportez-vous au service Web 2 pour plus d’informations sur ce point.
Wsdl.exe génère par défaut un exemple de code en C#. Si vous souhaitez utiliser un autre
langage (ex. : VB.NET), insérez l’option /l dans la commande :
wsdl.exe /l:vb WebService1.wsdl
Lorsque l’outil génère un fichier source pour le service Web (à l’aide de l’option /server), il crée un
modèle de classe abstraite associé au document WSDL. Il est recommandé de modifier ce fichier
directement plutôt que de sous-classer le fichier généré. Pour plus d’informations à ce sujet,
reportez-vous au service Web 2.
Visual Studio .NET 2005 prendra en charge la génération de fichiers source modèles directement à
partir du WSDL dans l’IDE. De plus, si le document WSDL est modifié et qu’il faut générer à
nouveau le fichier modèle, c’est l’IDE qui s’en chargera en s’assurant que tout le code existant dans
l’ancien modèle a bien été reporté dans le nouveau modèle.
Utilisation de Visual Studio .NET
Lors de la création d’une application de service Web dans Visual Studio .NET, le projet généré
fournit automatiquement une page de test qui s’affiche à l’exécution du service Web à partir du
navigateur. Comme nous développons des services Web en utilisant la méthode qui consiste à
créer d’abord le fichier WSDL (« WSDL First »), nous vous recommandons de désactiver ce WSDL
généré automatiquement et de publier votre propre WSDL dans un emplacement public. Pour
désactiver la page de test et le WSDL générés automatiquement, ajoutez les éléments suivants
dans l’élément <system.web> du fichier web.config de votre projet :
<webServices> <protocols> <remove name="Documentation" /> </protocols>
</webServices>
Outre la technique consistant à utiliser wsdl.exe pour créer une classe proxy de service Web, vous
pouvez aussi opter pour une méthode plus conviviale grâce aux outils intégrés dans Visual
Studio.NET. La boîte de dialogue Ajouter une référence Web vous demandera d’effectuer le
pointage vers un fichier WSDL, qui sera utilisé pour générer une classe proxy. L’opération effectuée
en coulisses par la boîte de dialogue consiste essentiellement à appeler wsdl.exe à l’arrière plan
pour traiter le fichier WSDL.
Malheureusement, si vous tentez de créer un proxy client en utilisant la commande Ajouter une
référence Web de Visual Studio .NET 2002 pour effectuer un pointage vers un document WSDL
utilisant xsd:import, vous obtiendrez un bogue. Si c’est le cas, utilisez toujours la commande
wsdl.exe pour générer le proxy client. Ce bogue a été corrigé dans Visual Studio .NET 2003,
désormais capable de récupérer tous les fichiers importés et de générer ensuite la classe de proxy
client.
Création du WSDL avec Eclipse
Grâce aux outils fournis dans le WSDK, Eclipse offre une fonctionnalité des services Web similaire à
celle de Visual Studio. Dans Eclipse, vous devez créer un fichier Java Bean ou EJB qui servira de
modèle pour le service Web. Vous pouvez également recourir à des utilitaires de ligne de
commande pour générer les fichiers modèles du service Web. Pour plus d’informations sur cette
procédure, consultez la documentation d’Eclipse.
Remerciements
Nous remercions Simon Guest de Microsoft pour ses comptes-rendus et commentaires techniques
excellents, ainsi que Neil Chopra et Mike Hanley de Vertigo Software pour avoir testé et enrichi
certaines idées développées dans cet article.
Liens utiles
Titre
Emplacement
Page d’accueil des
http://msdn.microsoft.com/webservices/ (en anglais)
services Web de Microsoft
Page d’accueil des
services Web d’IBM
http://www.ibm.com/webservices (en anglais)
Page d’accueil des
services Web de BEA
http://www.bea.com/webservices/ (en anglais)
Organisation WS-I
http://www.ws-i.org
Version finale de la
spécification WS-I Basic
Profile
http://www.ws-i.org/Profiles/BasicProfile-1.0.html (en anglais)
Yasser Shohoud
http://msdn.microsoft.com/msdnmag/issues/02/12/WebServicesDesign/ (en
anglais)
Will Provost
http://webservices.xml.com/pub/a/ws/2003/07/22/wsdlfirst.html (en anglais)
Téléchargement