Développons en Java v 2.40 Copyright (C) 1999-2023 Jean-Michel DOUDOUX. |
|||||||
Niveau : | Confirmé |
Les services web de type Soap permettent l'appel d'une méthode d'un objet distant en utilisant un protocole web pour le transport (http en général) et XML pour formater les échanges. Les services web fonctionnent sur le principe client / serveur :
L'appel de méthodes distantes n'est pas une nouveauté mais la grande force des services web est d'utiliser des standards ouverts et reconnus notamment HTTP et XML. L'utilisation de ces standards permet d'écrire des services web dans plusieurs langages et de les utiliser sur des systèmes d'exploitation différents.
Les services web de type Soap utilisent des messages au format XML pour permettre l'appel de méthodes ou l'échange de messages.
Certaines fonctionnalités complémentaires mais généralement utiles des services web ne sont pas encore complètement matures à cause de la jeunesse des technologies utilisées pour les mettre en oeuvre. Il reste encore de nombreux domaines à enrichir (sécurité, gestion des transactions, workflow, ... ). Des technologies pour répondre à ces besoins sont en cours de développement mais généralement plusieurs solutions sont en concurrence.
Initialement, Sun a proposé un ensemble d'outils et d'API pour permettre le développement de services web avec Java. Cet ensemble se nomme JWSDP (Java Web Services Developer Pack) dont il existe plusieurs versions.
Depuis, Sun a intégré la plupart de ces API permettant le développement de services web dans les spécifications de J2EE version 1.4.
La version 5 de Java EE et la version 6 de Java SE utilisent JAX-WS 2.0 pour faciliter le développement de services web en utilisant des annotations.
Ce chapitre contient plusieurs sections :
Les services web sont des composants distribués qui offrent des fonctionnalités aux applications au travers du réseau en utilisant des standards ouverts. Ils peuvent donc être utilisés par des applications écrites dans différents langages et exécutées dans différentes plates-formes sur différents systèmes.
Les services Web utilisent une architecture distribuée composée de plusieurs ordinateurs et/ou systèmes différents qui communiquent sur le réseau. Ils mettent en oeuvre un ensemble de normes et standards ouverts qui permettent aux développeurs d'implémenter des applications distribuées internes ou externes en utilisant des outils différents fournis par les fournisseurs.
Un service web permet généralement de proposer une ou plusieurs fonctionnalités métiers qui seront invoquées par un ou plusieurs consommateurs.
Il existe deux grandes familles de services web :
Ce chapitre va se concentrer sur les services web de type SOAP.
Il existe plusieurs définitions pour les services web mais la plus simple pourrait être "fonctionnalité utilisable au travers du réseau en mettant en oeuvre un format standard utilisant généralement XML".
Les services web ne sont donc qu'une nouvelle forme d'échanges de type RPC (Remote Procedure Call). Leur grand intérêt est de reposer sur des standards plutôt que sur des protocoles propriétaires. Par exemple, le transport repose généralement sur le protocol HTTP mais il est possible d'utiliser d'autres protocoles tels que JMS, FTP ou SMTP.
Les services web de type Soap font un usage intensif de XML, des namespaces XML et des schémas XML. Ces technologies font la force des services web pour permettre leur utilisation par des clients et des serveurs hétérogènes. XML est notamment utilisé pour stocker et organiser les informations de la requête et de la réponse mais aussi pour décrire le service web. L'utilisation de XML pour le format des messages rend les échanges indépendants du système d'exploitation, de la plate-forme et du langage.
Il est ainsi possible de développer des services web avec une plate-forme (par exemple Java) et d'utiliser ces services web avec une autre plate-forme (par exemple .Net ou PHP) : c'est une des grandes forces des services web même si cela reste parfois quelque peu théorique, essentiellement à cause des implémentations des moteurs utilisés pour mettre en oeuvre les services web.
Un service web est donc une fonctionnalité accessible au travers du réseau grâce à des messages au format XML. Le format de ces messages est généralement SOAP bien que d'autres formats existent (REST, XML-RPC, ...).
Les services web peuvent prendre plusieurs formes :
Lors de la mise en place de services web, plusieurs problématiques interviennent tôt ou tard :
L'appel à un service web de type SOAP suit plusieurs étapes :
Un des intérêts des services web est de masquer aux développeurs la complexité de l'utilisation des standards sous-jacents. Ceci est réalisé grâce aux développements d'API et de moteurs pour la production et la consommation de services web.
Ces API sont dépendantes des plates-formes utilisées (Java, .Net, PHP, Perl, ...) mais elles mettent toutes en oeuvre avec plus ou moins de complétude les standards de l'industrie relatifs aux services web notamment SOAP et WSDL.
Ainsi les développeurs peuvent se concentrer sur l'écriture des traitements proposés par les services et par leur consommation sans se soucier de la tuyauterie sous-jacente. Un minimum de compréhension est cependant nécessaire pour bien appréhender les mécanismes mis en oeuvre.
Les services web proposent un mécanisme facilitant :
L'utilisation de services web peut avoir plusieurs intérêts :
L'intérêt des services web grandissant, des standards ont été développés pour assurer les besoins nécessaires à leur mise en oeuvre.
L'architecture des services web est composée de quatre grandes couches utilisant plusieurs technologies :
La description d'un service web permet à son consommateur de connaître l'interface du service.
La communication permet de formaliser le format des messages échangés.
En plus de SOAP, WSDL et UDDI, il existe de nombreuses autres spécifications plus ou moins standard pour permettre la mise en oeuvre de fonctionnalités manquantes dans ces standards comme la sécurité, la gestion des transactions, l'orchestration des services, ...
Ces spécifications sont en cours de développement ou d'évolution ce qui les rend généralement immatures. De plus, fréquemment, il existe plusieurs spécifications ayant trait à un même sujet qui sont donc concurrentes. La mise en oeuvre de ces spécifications n'est pas requise pour des services web basiques mais elle peut être nécessaire pour des besoins plus spécifiques.
SOAP (acronyme de Simple Object Acces Protocol jusqu'à sa version 1.1) est un standard du W3C qui permet l'échange formaté d'informations entre un client et un serveur. SOAP peut être utilisé pour la requête et la réponse de cet échange.
SOAP assure la partie messaging dans l'architecture des services web : il est utilisé pour normaliser le format des messages échangés entre le consommateur et le fournisseur de services web. SOAP est donc un protocole qui est principalement utilisé pour dialoguer avec des objets distribués comme peut le proposer JRMP utilisé par RMI, DCOM ou IIOP.
Son grand intérêt est d'utiliser XML ce qui le rend ouvert contrairement aux autres protocoles qui sont propriétaires : cela permet la communication entre un client et un serveur utilisant des technologies différentes. SOAP fait un usage intensif des espaces de nommages (namespaces).
SOAP est défini pour être indépendant du protocole de transport utilisé pour véhiculer le message. Cependant, le protocole le plus utilisé avec SOAP est HTTP car c'est un des protocoles le plus répandu et utilisé du fait de sa simplicité. Son utilisation avec SOAP permet de rendre les services web plus interopérables. De plus, cela permet aux services web de facilement traverser les firewalls du côté producteur et consommateur notamment dans le cas d'échanges par internet.
D'autres protocoles peuvent être utilisés (par exemple SMTP ou FTP) mais leur configuration sera plus délicate car elle ne sera pas fournie en standard comme c'est le cas avec HTTP. En fait, tous les protocoles capables de véhiculer un flux d'octets peuvent être utilisés.
SOAP est aussi indépendant de tout système d'exploitation et de tout langage de programmation car il utilise XML. Ceci permet une exposition et une consommation de services web avec des outils et des OS différents.
SOAP peut être utilisé pour :
Un message SOAP est contenu dans une enveloppe, ainsi le tag racine d'un document SOAP est le tag <Envelope>.
La structure d'une enveloppe SOAP se compose de plusieurs parties :
L'en-tête contient des informations sur le traitement du message : ces informations sont contenues dans un tag <Header>. Pour des services web simples, cette partie peut être vide ou absente mais pour des services plus complexes elle peut contenir des informations concernant les transactions, la sécurité, le routage, etc ...
Le corps du message SOAP est contenu dans un tag <Body> obligatoire. Il contient les données échangées entre le client et le service sous la forme d'un fragment de document XML.
Tous ces éléments sont codés dans le message XML avec un tag particulier mettant en oeuvre un espace de nommage particulier défini dans les spécifications de SOAP.
Un message SOAP peut aussi contenir des pièces jointes placées chacune dans une partie optionnelle nommée AttachmentPart. Ces parties appartiennent à la partie SOAP Part.
SOAP définit aussi l'encodage pour les différents types de données qui est basé sur la technologie schéma XML du W3C. Les données peuvent être de type simple (chaine, entier, flottant, ...) ou de type composé.
Les types simples peuvent être
Les types composés
La partie SOAP Fault permet d'indiquer qu'une erreur est survenue lors des traitements du service web. Cette partie peut être composée de 4 éléments :
Deux formats de messages SOAP sont définis :
Les règles d'encodage (Encoding rules) précisent les mécanismes de sérialization des données dans un message. Il existe deux types :
Le style et le type d'encodage permettent de définir comment les données seront sérialisées et désérialisées dans les requêtes et les réponses.
La combinaison du style et du type d'encodage peut prendre plusieurs valeurs :
Le style RPC/Encoded a largement été utilisé au début des services web : actuellement ce style est en cours d'abandon par l'industrie au profit du style Document/Literal. C'est pour cette raison que le style RPC/Encoded n'est pas intégré dans le WS-I Basic Profile 1.1.
Le style et le type d'encodage sont précisés dans le WSDL. L'appel du service web doit obligatoirement se faire dans le style précisé dans le WSDL puisque celui-ci détermine le format des messages échangés.
Les versions de SOAP
Les spécifications de SOAP 1.2 sont composées de plusieurs parties :
La version 1.2 des spécifications de SOAP est plus précise pour réduire les ambigüités qui pouvaient conduire à des problèmes d'interopérabilité entre différentes implémentations.
SOAP 1.2 propose un support pour des protocoles de transport différents de HTTP. La sérialisation de messages n'est pas obligatoirement en XML mais peut utiliser des formats binaires (XML Infoset par exemple).
WSDL (acronyme de Web Service Description Language) est utilisé pour fournir une description d'un service web afin de permettre son utilisation. C'est une recommandation du W3C.
Pour permettre à un client de consommer un service web, ce dernier a besoin d'une description détaillée du service avant de pouvoir interagir avec lui. Un WSDL fournit cette description dans un document XML. WSDL joue un rôle important dans l'architecture des services en assurant la partie description : il contient toutes les informations nécessaires à l'invocation du service qu'il décrit.
La description WSDL d'un service web comprend une définition du service, les types de données utilisés notamment dans le cas de types complexes, les opérations utilisables, le protocole utilisé pour le transport et l'adresse d'appel.
C'est un document XML qui décrit un service web de manière indépendante de tout langage. Il permet l'appel de ses opérations et l'exploitation des réponses (les paramètres, le format des messages, le protocole utilisé, ...).
WSDL est conçu pour être indépendant de tout protocoles. Ceci rend le standard WSDL flexible mais aussi plus complexe à comprendre. Comme SOAP et HTTP sont les deux protocoles les plus couramment utilisés pour implémenter les services web, le standard WSDL intègre un support de ces deux protocoles.
L'utilisation de XML permet à des outils de différents systèmes, plates-formes et langages d'utiliser le contenu d'un WSDL pour générer du code permettant de consommer un service web. Les moteurs SOAP proposent en général un outil qui va lire le WSDL et générer les classes requises pour utiliser un service web avec la technologie du moteur SOAP. Le code généré utilise un moteur SOAP qui masque toute la tuyauterie du protocole utilisé et des messages échangés lors de la consommation de services web en agissant comme un proxy. Par exemple, Axis propose l'outil WSDL2Java pour la génération de ces classes à partir du WSDL.
Pour assurer une meilleure interopérabilité, WS-I Basic Profil 1.0 oblige à utiliser WSDL et les schémas XML pour la description des services web.
Un document WSDL définit plusieurs éléments :
Un WSDL est un document XML dont le tag racine est <definitions> et qui utilise l'espace de nommage "http://schemas.xmlsoap.org/wsdl/".
Un WSDL est virtuellement composé de deux parties :
Le contenu du WSDL d'un service nommé MonService est de la forme :
Exemple : |
<!-Structure d'un WSDL -->
<definitions name="MonService"
targetNamespace="http://fr.jmdoudoux.dej.ws.monservice/"
xmlns="http://schemas.xmlsoap.org/wsdl/">
<!-- Définitions abstraites -->
<types> ... </types>
<message> ... </message>
<portType> ... </portType>
<!-- Définitions concrètes -->
<binding> ... </binding>
<service> ... </service>
</definitions>
L'ordre de définition des informations dans un WSDL facilite les traitements de ce document par une machine. Pour une exploitation par un humain, il est plus facile de lire le WSDL en commençant par la fin.
Exemple : |
<?xml version="1.0" encoding="UTF-8" ?>
<wsdl:definitions targetNamespace="http://axis.test.jmdoudoux.com"
xmlns:apachesoap="http://xml.apache.org/xml-soap"
xmlns:impl="http://axis.test.jmdoudoux.com"
xmlns:intf="http://axis.test.jmdoudoux.com"
xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
xmlns:wsdlsoap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<!--
WSDL created by Apache Axis version: 1.3
Built on Oct 05, 2005 (05:23:37 EDT)
-->
<wsdl:message name="additionnerRequest">
<wsdl:part name="valeur1" type="xsd:int" />
<wsdl:part name="valeur2" type="xsd:int" />
</wsdl:message>
<wsdl:message name="additionnerResponse">
<wsdl:part name="additionnerReturn" type="xsd:long" />
</wsdl:message>
<wsdl:portType name="Calculer">
<wsdl:operation name="additionner" parameterOrder="valeur1 valeur2">
<wsdl:input message="impl:additionnerRequest" name="additionnerRequest" />
<wsdl:output message="impl:additionnerResponse" name="additionnerResponse" />
</wsdl:operation>
</wsdl:portType>
<wsdl:binding name="CalculerSoapBinding" type="impl:Calculer">
<wsdlsoap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http" />
<wsdl:operation name="additionner">
<wsdlsoap:operation soapAction="" />
<wsdl:input name="additionnerRequest">
<wsdlsoap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
namespace="http://axis.test.jmdoudoux.com" use="encoded" />
</wsdl:input>
<wsdl:output name="additionnerResponse">
<wsdlsoap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
namespace="http://axis.test.jmdoudoux.com" use="encoded" />
</wsdl:output>
</wsdl:operation>
</wsdl:binding>
<wsdl:service name="CalculerService">
<wsdl:port binding="impl:CalculerSoapBinding" name="Calculer">
<wsdlsoap:address location="http://localhost:8080/TestWS/services/Calculer" />
</wsdl:port>
</wsdl:service>
</wsdl:definitions>
Un document WSDL est un document XML dont le tag racine est <definitions>. Généralement, ce tag contient la définition des différents espaces de nommages qui seront utilisés dans le document XML.
L'espace de nommage du document WSDL est défini.
Exemple :
xmlns="http://schemas.xmlsoap.org/wsdl/" (déclaration de l'espace de nommage par défaut) xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
L'espace de nommage de la norme Schema XML est défini.
Exemple :
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
Plusieurs autres espaces de nommages standard sont généralement définis selon les protocoles utilisés.
Exemple :
xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:wsdlsoap="http://schemas.xmlsoap.org/wsdl/soap/"
Il est possible de trouver la définition d'espaces de nommages propres à l'implémentation.
Exemple :
xmlns:apachesoap="http://xml.apache.org/xml-soap"
Un ou plusieurs espaces de nommages sont définis pour le service web lui-même.
Remarque : les préfixes utilisés pour ces espaces de nommages peuvent être différents selon l'implémentation des services web mise en oeuvre
Dans un document WSDL, les différentes entités se référencent mutuellement grâce à leur nom complet (espace de nommage et nom).
L'élément racine d'un WSDL est le tag <definitions>.
Le tag <definitions> peut contenir plusieurs tags fils :
Le tag <definitions> ne peut avoir qu'un seul tag fils <types>.
L'élément <types> contient une définition des différents types de données qui seront utilisés.
Cette définition peut être faite sous plusieurs formats mais l'utilisation des schémas XML est recommandée. Le WS-I Basic Profile impose que cette description soit faite avec des schémas XML.
L'élément <types> peut avoir aucun, un ou plusieurs éléments fils <schema> ayant pour espace de nommage "http://www.w3.org/2001/XMLSchema".
Chaque structure de données est décrite en utilisant la norme schéma XML.
L'élément <types> peut donc avoir plusieurs schémas XML comme éléments fils. Ces schémas peuvent décrire des types simples (element) ou complexes (complexType).
Le tag <definitions> peut avoir plusieurs tags fils <message>. Le tag <message> décrit un message qui est utilisé en tant que requête ou réponse lors de l'invocation d'une opération : il contient une définition des paramètres pour un message échangé en entrée ou en sortie..
Le tag <message> peut avoir un ou plusieurs tags <part>. Le tag <part> possède un attribut "name" pour permettre d'y faire référence et utilise un attribut "element" (pour le style document qui représente l'élément XML inséré dans le body) ou un attribut "type" (pour le style RPC qui représente les paramètres de l'opération).
En WSDL, un échange de messages est une opération qui peut donc avoir une requête en entrée et une réponse en sortie.
Le tag <definitions> peut avoir un ou plusieurs tags fils <typePort>. Le tag <portType> décrit l'interface d'un service web.
Le terme typePort est particulièrement ambigu : il correspond à une description de l'interface du service. Le tag <interface> est utilisé à partir de la version 2.0 de WSDL.
Le tag <typePort> possède un attribut name qui permet d'y faire référence.
Il contient un ensemble d'opérations chacune définie dans un tag fils <operation> qui possède un attribut name permettant d'y faire référence.
Le tag <operation> peut avoir les tags fils <input>, <output> et <fault>. La présence et l'ordre des deux premiers tags définissent le mode d'invocation d'une opération nommé MEP (Message Exchance Pattern).
MEP |
Description |
One-way |
L'endpoint reçoit un message sans fournir de réponse : <input> uniquement |
Request-response |
L'endpoint reçoit un message et envoie la réponse correspondante : <input> et <output> |
Notification |
L'endpoint envoie un message sans avoir de réponse : <output> uniquement |
Solicit-response |
L'endpoint envoie un message et reçoit la réponse correspondante : <output> et <input> |
Le support de ces types d'opérations dépend de l'implémentation du moteur SOAP utilisé.
L'attribut message des tags <input> et <output> fait référence à un tag <message> par son nom.
La description du service doit aussi fournir des informations pour invoquer le service :
Un binding permet de fournir des détails sur la façon dont les données sont transportées.
Un service peut avoir plusieurs bindings mais chacun doit se faire sur une URI unique nommée endpoint.
Le tag <definitions> peut avoir un ou plusieurs tags fils <binding>.
Il permet de définir pour un portType le protocole de transport utilisé et le mode d'encodage des messages.
Le tag <binding> possède un attribut name qui permet d'y faire référence et un attribut type qui permet de faire référence au portType concerné de façon nominative.
Le détail des informations sur le protocole et le mode d'encodage sont des extensions spécifiques comme celle fournie en standard relative à SOAP.
Ainsi le tag fils <soap:binding> est utilisé pour préciser que c'est la version 1.1 de SOAP qui sera utilisée. Son attribut style permet de préciser le style du message : les valeurs possibles sont rpc ou document. Son attribut transport permet de préciser le procotole de transport à utiliser, généralement "http://schemas.xmlsoap.org/soap/http" pour le protocole http.
Le tag <binding> possède un tag fils <operation> pour chaque opération. Le tag <operation> peut avoir plusieurs tags fils.
Le tag fils <soap:operation> permet de définir par son attribut "soapAction" la valeur du header http correspondant. Selon les spécifications WS-I BP, l'attribut "soapAction" doit toujours avoir la valeur chaîne vide.
Les tags <input> et <output> permettent de fournir des précisions sur l'encodage du corps des messages.
Le tag fils <soap:body> permet, par son attribut "use", de préciser comment le corps de message sera encodé : les valeurs possibles sont encoded et document.
L'utilisation de l'encodage "rpc" précise que le corps du message sera une représentation XML des paramètres ou de la valeur de retour de la méthode invoquée.
L'utilisation de l'encodage "document" précise que le corps du message sera un message XML.
Le tag <definitions> ne peut avoir qu'un seul tag fils <service>.
Un service possède un nom précisé dans la valeur de son attribut name.
Un service est composé d'un ou plusieurs ports qui en SOAP correspondent à des endpoints. Chaque port est associé à un binding en utilisant l'attribut binding qui a comme valeur le nom d'un binding défini.
Les tags fils du tag <port> sont spécifiques au binding utilisé : ce sont des extensions qui précisent le endpoint selon le binding. Par exemple pour préciser l'url d'un service web utilisant http, il faut utiliser le tag <address> en fournissant l'url du endpoint comme valeur de l'attribut location. Le tag <endpoint> est utilisé à partir de la version 2.0 de WSDL.
Un port permet de décrire la façon d'accéder au service, ce qui correspond généralement à l'url d'un endpoint et à un binding.
UDDI, acronyme de Universal Description, Discovery and Integration, est utilisé pour publier et rechercher des services web. C'est un protocole et un ensemble de services pour utiliser un annuaire afin de stocker les informations concernant les services web et de permettre à un client de les retrouver. Les spécifications sont rédigées par l'Oasis.
UDDI est une spécification pour permettre la publication et la recherche d'informations sur des entreprises et les services web qu'elles proposent. UDDI permet à une entreprise de s'inscrire dans l'annuaire, d'y enregistrer et de publier ses services web. Il est alors possible d'accéder à l'annuaire et de rechercher un service particulier.
Le site web officiel d'UDDI est à l'url : http://uddi.xml.org
Un annuaire UDDI contient une description de services web mais aussi des entreprises qui les proposent. Ainsi, il est possible avec UDDI de faire une recherche par entreprise ou par activité.
Les données incluses dans un annuaire UDDI sont classées dans trois catégories :
Il est possible d'utiliser un annuaire UDDI en interne dans une entreprise mais il existe aussi des annuaires UDDI globaux nommés UBR (UDDI Business Registry).
Il existe plusieurs versions d'UDDI :
UDDI n'est pas un élément indispensable à la mise en oeuvre des services web comme peut l'être XML, WSDL ou SOAP.
UDDI est une spécification d'annuaire accessible sous la forme de services web de type SOAP que ce soit pour les recherches ou les mises à jour.
|
La suite de cette section sera développée dans une version future de ce document
|
Un message SOAP peut être formaté de plusieurs façons en fonction de son style et de son type d'encodage.
Il existe deux styles de services web reposant sur SOAP : RCP et Document.
En plus du style, il existe deux types d'encodages : Encoded et Literal. Cela permet de définir quatre combinaisons mais généralement les combinaisons utilisées sont RPC/Encoded et Document/Literal. La combinaison Document/Encoded n'est supportée par aucune implémentation.
De plus, Microsoft est à l'origine d'un cinquième format qui bien que non standardisé est largement utilisé car il est mis en oeuvre par défaut dans la plate-forme.Net et il offre un bon compromis entre performance et restrictions d'utilisation.
Style / Type d'encodage |
Encoded |
Literal |
RPC |
RPC / Encoded |
RPC / Literal |
Document |
Document / Encoded |
Document / Literal |
Document / Literal wrapped |
Il y a deux façons de structurer un document SOAP : RPC et Document. Initialement, avant la diffusion de sa première version, SOAP ne permettait que le style RPC. La version 1.0 s'est vue ajouter le support pour le style Document.
Le style RPC est parfaitement structuré alors que le type Document n'a pas de structure imposée mais son contenu peut être facilement validé grâce à un schéma XML ou traité puisque c'est un document XML. Avec le style document, il est donc possible de structurer librement le corps du message grâce au schéma XML.
Les différents styles sont :
Style |
Description |
RPC |
Les messages contiennent le nom de l'opération Paramètres en entrée multiples et valeur de retour |
Document |
Les messages ne contiennent pas le nom de l'opération Un document XML en entrée et en retour |
Les deux désignations pour le style d'encodage (RPC et Document) peuvent être trompeuses car elles peuvent induire que le style RPC est utilisé pour l'invocation d'opérations distantes et que le style document est utilisé pour l'échange de messages. En fait, le style n'a rien à voir avec un modèle de programmation mais il permet de préciser comment le message SOAP est encodé.
Dans le style RPC, le corps du message (tag <soap:body>) contient un élément qui est le nom de l'opération du service. Cet élément contient un élément fils pour chaque paramètre.
Dans le style Document, le corps du message (tag <soap:body>) contient directement un document xml dont tous les composants doivent être décrits dans un ou plusieurs schémas XML. Le moteur Soap est alors responsable du mapping entre le contenu du message et les objets du serveur.
Les types d'encodage pour sérialiser les messages en XML sont :
Encodage |
Description |
Encoded |
Aussi appelé SOAP encoding car l'encodage est spécifique à SOAP sans utiliser de schéma XML |
Literal |
L'encodage du message repose sur les schémas XML |
Literal wrapped |
Idem Literal avec en plus l'encapsulation de chaque message dans un tag qui contient le nom de l'opération. Ce format est défini par Microsoft qui l'utilise dans sa plate-forme .Net |
Le type d'encodage Literal propose que le contenu du corps (body) soit validé par un schéma XML donné : chaque élément qui correspond à un paramètre ou à la valeur de retour est décrit dans un schéma XML.
Le type d'encodage Encoded utilise un ensemble de règles reposant sur les types de données des schémas XML pour encoder les données mais le message ne respecte pas de schéma particulier : chaque élément qui correspond à un paramètre ou à la valeur de retour contient la description de la donnée sous la forme d'attributs spécifiés dans la norme Soap (type, null ou pas, ...)
Le type d'encodage Encoded est particulièrement adapté lors de l'utilisation d'un graphe d'objets cyclique car chaque type d'objet ne sera défini qu'une seule fois. Avec l'encodage Literal, chaque élément est répété dans le document.
Le type d'encodage Literal permet une manipulation du document XML qui constitue le message (validation par un schéma XML, parsing du document, transformation à l'aide d'une feuille de style XSLT, ...)
Le format RPC encoded repose sur des types définis par SOAP alors que les formats RPC Literal et Document Literal repose sur les types du schéma XML.
Il existe donc plusieurs formats utilisables pour un message SOAP :
Ces différents styles et types d'encodages sont à l'origine de difficultés d'interopérabilité au début des services web car la plate-forme .Net utilisait le style Document par défaut et les implémentations sur la plate-forme Java utilisaient plutôt le style RPC.
Au début de l'utilisation de SOAP, c'est donc le format RPC encoding qui était utilisé. Depuis, c'est plutôt le format Document/Literal ou RPC/Literal qui est recommandé notamment par les spécifications WS-I Basic Profile.
Les sections suivantes vont utiliser la classe d'implémentation du service web ci-dessous avec Axis.
Exemple : |
package fr.jmdoudoux.dej.axis;
public class Calculer {
public long additionner(int valeur1, int valeur2) {
return valeur1+valeur2;
}
}
Les sections suivantes vont utiliser la classe d'implémentation du service web ci-dessous avec Metro
Exemple : |
package fr.jmdoudoux.dej.ws;
import javax.jws.WebService;
@WebService
public class Calculer {
public long additionner(int valeur1, int valeur2) {
return valeur1+valeur2;
}
}
Ce format de messages est le plus ancien et le plus simple à mettre en oeuvre pour le développeur.
La structure du corps du message avec le style RPC est imposée. Par exemple, pour une requête, il doit contenir le nom de la méthode ainsi que ses paramètres. Cette structure est donc de la forme :
Exemple : |
<soapenv:Body>
<ns0:nom_de_la_methode xmlns:ns0="uri_de_l_espace_de_nommage"
soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<nom_param_1 xsi:type="xsd:type_param_1">valeur_parametre_1</nom_param_1>
<nom_param_2 xsi:type="xsd:type_param_2">valeur_parametre_2</nom_param_2>
</ns0: nom_de_la_methode>
</soapenv:Body>
</soapenv:Envelope>
Le message réponse a une forme similaire.
Le type des paramètres peut être simple ou plus complexe (par exemple un objet qui encapsule des données de types simples ou d'autres objets).
Attention : RPC Encoding n'est pas conforme à la spécification WS-I Basic Profile
Exemple : Le fichier WSDL
Exemple : |
<?xml version="1.0" encoding="UTF-8"?>
<wsdl:definitions targetNamespace="http://axis.test.jmdoudoux.com"
xmlns:apachesoap="http://xml.apache.org/xml-soap"
xmlns:impl="http://axis.test.jmdoudoux.com"
xmlns:intf="http://axis.test.jmdoudoux.com"
xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
xmlns:wsdlsoap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<!--WSDL created by Apache Axis version: 1.3
Built on Oct 05, 2005 (05:23:37 EDT)-->
<wsdl:message name="additionnerRequest">
<wsdl:part name="valeur1" type="xsd:int"/>
<wsdl:part name="valeur2" type="xsd:int"/>
</wsdl:message>
<wsdl:message name="additionnerResponse">
<wsdl:part name="additionnerReturn" type="xsd:long"/>
</wsdl:message>
<wsdl:portType name="Calculer">
<wsdl:operation name="additionner" parameterOrder="valeur1 valeur2">
<wsdl:input message="impl:additionnerRequest" name="additionnerRequest"/>
<wsdl:output message="impl:additionnerResponse" name="additionnerResponse"/>
</wsdl:operation>
</wsdl:portType>
<wsdl:binding name="CalculerSoapBinding" type="impl:Calculer">
<wsdlsoap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http"/>
<wsdl:operation name="additionner">
<wsdlsoap:operation soapAction=""/>
<wsdl:input name="additionnerRequest">
<wsdlsoap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
namespace="http://axis.test.jmdoudoux.com" use="encoded"/>
</wsdl:input>
<wsdl:output name="additionnerResponse">
<wsdlsoap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
namespace="http://axis.test.jmdoudoux.com" use="encoded"/>
</wsdl:output>
</wsdl:operation>
</wsdl:binding>
<wsdl:service name="CalculerService">
<wsdl:port binding="impl:CalculerSoapBinding" name="Calculer">
<wsdlsoap:address location="http://localhost:8080/TestWS/services/Calculer"/>
</wsdl:port>
</wsdl:service>
</wsdl:definitions>
Exemple : descripteur Axis
Exemple : |
<service name="Calculer" provider="java:RPC">
<operation name="additionner" qname="ns1:additionner"
returnQName="additionnerReturn"
returnType="xsd:long"
soapAction=""
xmlns:ns1="http://axis.test.jmdoudoux.com"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<parameter name="valeur1" type="xsd:int"/>
<parameter name="valeur2" type="xsd:int"/>
</operation>
<parameter name="allowedMethods" value="additionner"/>
<parameter name="typeMappingVersion" value="1.2"/>
<parameter name="wsdlPortType" value="Calculer"/>
<parameter name="className" value="fr.jmdoudoux.dej.axis.Calculer"/>
<parameter name="wsdlServicePort" value="Calculer"/>
<parameter name="wsdlTargetNamespace" value="http://axis.test.jmdoudoux.com"/>
<parameter name="wsdlServiceElement" value="CalculerService"/>
</service>
La requête SOAP
Exemple : |
<?xml version="1.0" encoding="UTF-8" ?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soapenv:Body>
<ns0:additionner
xmlns:ns0="http://axis.test.jmdoudoux.com"
soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<valeur1 xsi:type="xsd:int">10</valeur1>
<valeur2 xsi:type="xsd:int">20</valeur2>
</ns0:additionner>
</soapenv:Body>
</soapenv:Envelope>
La réponse SOAP
Exemple : |
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soapenv:Body>
<ns1:additionnerResponse
xmlns:ns1="http://axis.test.jmdoudoux.com"
soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<additionnerReturn href="#id0" />
</ns1:additionnerResponse>
<multiRef xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
id="id0" soapenc:root="0"
soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
xsi:type="xsd:long">30</multiRef>
</soapenv:Body>
</soapenv:Envelope>
Avantages :
Inconvénients :
Les messages de type RPC/Literal sont encodés comme des appels RPC avec une description des paramètres et des valeurs de retour décrites chacune avec son propre schéma XML.
Le moteur Soap utilisé pour l'exemple de cette section est Metro version 1.5.
Exemple : |
import javax.jws.soap.SOAPBinding.Use;
@WebService
@SOAPBinding(style=Style.RPC, use=Use.LITERAL, parameterStyle=ParameterStyle.BARE)
public class Calculer {
public long additionner(Valeurs valeurs) {
return valeurs.getValeur1()+valeurs.getValeur2();
}
}
Exemple : |
<?xml version="1.0" encoding="UTF-8"?>
<endpoints xmlns="http://java.sun.com/xml/ns/jax-ws/ri/runtime"
version="2.0">
<endpoint name="CalculerWS" implementation="fr.jmdoudoux.dej.ws.Calculer"
url-pattern="/services/CalculerWS" />
</endpoints>
Le fichier WSDL
Exemple : |
<?xml version='1.0' encoding='UTF-8'?>
<!--
Published by JAX-WS RI at http://jax-ws.dev.java.net. RI's version is
JAX-WS RI 2.1.7-hudson-48-.
-->
<!--
Generated by JAX-WS RI at http://jax-ws.dev.java.net. RI's version is
JAX-WS RI 2.1.7-hudson-48-.
-->
<definitions
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:tns="http://ws.test.jmdoudoux.com/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://schemas.xmlsoap.org/wsdl/"
targetNamespace="http://ws.test.jmdoudoux.com/" name="CalculerService">
<types>
<xsd:schema>
<xsd:import namespace="http://ws.test.jmdoudoux.com/"
schemaLocation="http://localhost:8089/TestMetro/services/CalculerWS?xsd=1" />
</xsd:schema>
</types>
<message name="additionner">
<part name="additionner" element="tns:additionner" />
</message>
<message name="additionnerResponse">
<part name="additionnerResponse" element="tns:additionnerResponse" />
</message>
<portType name="Calculer">
<operation name="additionner">
<input message="tns:additionner" />
<output message="tns:additionnerResponse" />
</operation>
</portType>
<binding name="CalculerPortBinding" type="tns:Calculer">
<soap:binding transport="http://schemas.xmlsoap.org/soap/http"
style="rpc" />
<operation name="additionner">
<soap:operation soapAction="" />
<input>
<soap:body use="literal" namespace="http://ws.test.jmdoudoux.com/" />
</input>
<output>
<soap:body use="literal" namespace="http://ws.test.jmdoudoux.com/" />
</output>
</operation>
</binding>
<service name="CalculerService">
<port name="CalculerPort" binding="tns:CalculerPortBinding">
<soap:address location="http://localhost:8089/TestMetro/services/CalculerWS" />
</port>
</service>
</definitions>
La description est faite dans un schéma dédié
Exemple : |
<?xml version='1.0' encoding='UTF-8'?>
<xs:schema xmlns:tns="http://ws.test.jmdoudoux.com/"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
version="1.0" targetNamespace="http://ws.test.jmdoudoux.com/">
<xs:element name="additionner" nillable="true" type="tns:valeurs" />
<xs:element name="additionnerResponse" type="xs:long" />
<xs:complexType name="valeurs">
<xs:sequence>
<xs:element name="valeur1" type="xs:int" />
<xs:element name="valeur2" type="xs:int" />
</xs:sequence>
</xs:complexType>
</xs:schema>
Le premier élément du tag <body> du message désigne la méthode à invoquer
La requête SOAP
Exemple : |
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:ws="http://ws.test.jmdoudoux.com/">
<soapenv:Header/>
<soapenv:Body>
<ws:additionner>
<ws:additionner>
<valeur1>20</valeur1>
<valeur2>30</valeur2>
</ws:additionner>
</ws:additionner>
</soapenv:Body>
</soapenv:Envelope>
La réponse SOAP
Exemple : |
<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
<S:Body>
<ns2:additionnerResponse xmlns:ns2="http://ws.test.jmdoudoux.com/">
50</ns2:additionnerResponse>
</S:Body>
</S:Envelope>
Avantages :
Inconvénient :
Ce type d'encodage n'est supporté par aucun moteur de services web.
Pour respecter le WS-I BP, le tag <soap:body> d'un message Soap encodé en document Literal ne peut avoir qu'un seul élément fils.
L'exemple de cette section va donc utiliser une version légèrement différente du service web qui attend en paramètre un bean encapsulant les données à calculer.
Exemple : |
package fr.jmdoudoux.dej.ws.axis;
public class CalculerWS {
public long additionner(Valeurs valeurs) {
return valeurs.getValeur1()+valeurs.getValeur2();
}
}
La classe Valeurs est un POJO qui encapsule les paramètres requis par la méthode.
Exemple : |
package fr.jmdoudoux.dej.ws;
public class Valeurs {
private int valeur1;
private int valeur2;
public Valeurs() {
super();
}
public Valeurs(int valeur1, int valeur2) {
super();
this.valeur1 = valeur1;
this.valeur2 = valeur2;
}
public synchronized int getValeur1() {
return valeur1;
}
public synchronized void setValeur1(int valeur1) {
this.valeur1 = valeur1;
}
public synchronized int getValeur2() {
return valeur2;
}
public synchronized void setValeur2(int valeur2) {
this.valeur2 = valeur2;
}
}
Descripteur Axis 1.x
Exemple : |
<?xml version="1.0" encoding="UTF-8"?>
<deployment xmlns="http://xml.apache.org/axis/wsdd/"
xmlns:java="http://xml.apache.org/axis/wsdd/providers/java">
<service name="CalculerWS" provider="java:RPC" style="document"
use="literal">
<parameter name="wsdlTargetNamespace" value="http://axis.ws.test.jmdoudoux.com" />
<parameter name="wsdlServiceElement" value="CalculerWSService" />
<parameter name="schemaQualified" value="http://axis.ws.test.jmdoudoux.com" />
<parameter name="wsdlServicePort" value="CalculerWS" />
<parameter name="className" value="fr.jmdoudoux.dej.ws.axis.CalculerWS" />
<parameter name="wsdlPortType" value="CalculerWS" />
<parameter name="typeMappingVersion" value="1.2" />
<operation xmlns:retNS="http://axis.ws.test.jmdoudoux.com"
xmlns:rtns="http://www.w3.org/2001/XMLSchema" name="additionner"
qname="additionner" returnQName="retNS:additionnerReturn" returnType="rtns:long"
soapAction="">
<parameter xmlns:pns="http://axis.ws.test.jmdoudoux.com"
xmlns:tns="http://axis.ws.test.jmdoudoux.com" qname="pns:valeurs"
type="tns:Valeurs" />
</operation>
<parameter name="allowedMethods" value="additionner" />
<typeMapping xmlns:ns="http://axis.ws.test.jmdoudoux.com"
qname="ns:Valeurs" type="java:fr.jmdoudoux.dej.ws.axis.Valeurs"
serializer="org.apache.axis.encoding.ser.BeanSerializerFactory"
deserializer="org.apache.axis.encoding.ser.BeanDeserializerFactory"
encodingStyle="" />
</service>
</deployment>
Le fichier WSDL
Exemple : |
<?xml version="1.0" encoding="UTF-8"?>
<wsdl:definitions targetNamespace="http://axis.ws.test.jmdoudoux.com"
xmlns:apachesoap="http://xml.apache.org/xml-soap"
xmlns:impl="http://axis.ws.test.jmdoudoux.com"
xmlns:intf="http://axis.ws.test.jmdoudoux.com"
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
xmlns:wsdlsoap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<!--WSDL created by Apache Axis version: 1.4
Built on Apr 22, 2006 (06:55:48 PDT)-->
<wsdl:types>
<schema elementFormDefault="qualified"
targetNamespace="http://axis.ws.test.jmdoudoux.com"
xmlns="http://www.w3.org/2001/XMLSchema">
<complexType name="Valeurs">
<sequence>
<element name="valeur1" type="xsd:int"/>
<element name="valeur2" type="xsd:int"/>
</sequence>
</complexType>
<element name="valeurs" type="impl:Valeurs"/>
<element name="additionnerReturn" type="xsd:long"/>
</schema>
</wsdl:types>
<wsdl:message name="additionnerResponse">
<wsdl:part element="impl:additionnerReturn" name="additionnerReturn"/>
</wsdl:message>
<wsdl:message name="additionnerRequest">
<wsdl:part element="impl:valeurs" name="valeurs"/>
</wsdl:message>
<wsdl:portType name="CalculerWS">
<wsdl:operation name="additionner" parameterOrder="valeurs">
<wsdl:input message="impl:additionnerRequest" name="additionnerRequest"/>
<wsdl:output message="impl:additionnerResponse" name="additionnerResponse"/>
</wsdl:operation>
</wsdl:portType>
<wsdl:binding name="CalculerWSSoapBinding" type="impl:CalculerWS">
<wsdlsoap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
<wsdl:operation name="additionner">
<wsdlsoap:operation soapAction=""/>
<wsdl:input name="additionnerRequest">
<wsdlsoap:body use="literal"/>
</wsdl:input>
<wsdl:output name="additionnerResponse">
<wsdlsoap:body use="literal"/>
</wsdl:output>
</wsdl:operation>
</wsdl:binding>
<wsdl:service name="CalculerWSService">
<wsdl:port binding="impl:CalculerWSSoapBinding" name="CalculerWS">
<wsdlsoap:address location="http://localhost:8089/TestsAxisWS/services/CalculerWS"/>
</wsdl:port>
</wsdl:service>
</wsdl:definitions>
La requête SOAP
Exemple : |
<soapenv:Envelope
xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:axis="http://axis.ws.test.jmdoudoux.com">
<soapenv:Header/>
<soapenv:Body>
<axis:valeurs>
<axis:valeur1>20</axis:valeur1>
<axis:valeur2>30</axis:valeur2>
</axis:valeurs>
</soapenv:Body>
</soapenv:Envelope>
La réponse SOAP
Exemple : |
<soapenv:Envelope
xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soapenv:Body>
<additionnerReturn
xmlns="http://axis.ws.test.jmdoudoux.com">
50
</additionnerReturn>
</soapenv:Body>
</soapenv:Envelope>
Avantages :
Inconvénients :
Ce format a été défini par Microsoft pour la plate-forme .Net et il n'existe aucune spécification officielle mais c'est un standard de fait. Ce format reprend le format Document Literal mais le corps contient un élément qui précise le nom de l'opération.
Le tag <body> possède plusieurs caractéristiques en Document/Literal wrapped :
Le fichier WSDL
Exemple : |
<?xml version="1.0" encoding="UTF-8"?>
<wsdl:definitions targetNamespace="http://axis.test.jmdoudoux.com"
xmlns:apachesoap="http://xml.apache.org/xml-soap"
xmlns:impl="http://axis.test.jmdoudoux.com"
xmlns:intf="http://axis.test.jmdoudoux.com"
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
xmlns:wsdlsoap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<!--WSDL created by Apache Axis version: 1.3
Built on Oct 05, 2005 (05:23:37 EDT)-->
<wsdl:types>
<schema elementFormDefault="qualified"
targetNamespace="http://axis.test.jmdoudoux.com"
xmlns="http://www.w3.org/2001/XMLSchema">
<element name="additionner">
<complexType>
<sequence>
<element name="valeur1" type="xsd:int"/>
<element name="valeur2" type="xsd:int"/>
</sequence>
</complexType>
</element>
<element name="additionnerResponse">
<complexType>
<sequence>
<element name="additionnerReturn" type="xsd:long"/>
</sequence>
</complexType>
</element>
</schema>
</wsdl:types>
<wsdl:message name="additionnerResponse">
<wsdl:part element="impl:additionnerResponse" name="parameters"/>
</wsdl:message>
<wsdl:message name="additionnerRequest">
<wsdl:part element="impl:additionner" name="parameters"/>
</wsdl:message>
<wsdl:portType name="Calculer">
<wsdl:operation name="additionner">
<wsdl:input message="impl:additionnerRequest" name="additionnerRequest"/>
<wsdl:output message="impl:additionnerResponse" name="additionnerResponse"/>
</wsdl:operation>
</wsdl:portType>
<wsdl:binding name="CalculerSoapBinding" type="impl:Calculer">
<wsdlsoap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
<wsdl:operation name="additionner">
<wsdlsoap:operation soapAction=""/>
<wsdl:input name="additionnerRequest">
<wsdlsoap:body use="literal"/>
</wsdl:input>
<wsdl:output name="additionnerResponse">
<wsdlsoap:body use="literal"/>
</wsdl:output>
</wsdl:operation>
</wsdl:binding>
<wsdl:service name="CalculerService">
<wsdl:port binding="impl:CalculerSoapBinding" name="Calculer">
<wsdlsoap:address location="http://localhost:8080/TestWS/services/Calculer"/>
</wsdl:port>
</wsdl:service>
</wsdl:definitions>
Descripteur Axis
Exemple : |
<service name="Calculer" provider="java:RPC" style="wrapped" use="literal">
<operation name="additionner" qname="ns1:additionner"
returnQName="ns1:additionnerReturn" returnType="xsd:long"
soapAction="" xmlns:ns1="http://axis.test.jmdoudoux.com"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<parameter qname="ns1:valeur1" type="xsd:int"/>
<parameter qname="ns1:valeur2" type="xsd:int"/>
</operation>
<parameter name="allowedMethods" value="additionner"/>
<parameter name="typeMappingVersion" value="1.2"/>
<parameter name="wsdlPortType" value="Calculer"/>
<parameter name="className" value="fr.jmdoudoux.dej.axis.Calculer"/>
<parameter name="wsdlServicePort" value="Calculer"/>
<parameter name="schemaQualified" value="http://axis.test.jmdoudoux.com"/>
<parameter name="wsdlTargetNamespace" value="http://axis.test.jmdoudoux.com"/>
<parameter name="wsdlServiceElement" value="CalculerService"/>
</service>
La requête SOAP
Exemple : |
<?xml version="1.0" encoding="UTF-8" ?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:q0="http://axis.test.jmdoudoux.com"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soapenv:Body>
<q0:additionner>
<q0:valeur1>20</q0:valeur1>
<q0:valeur2>30</q0:valeur2>
</q0:additionner>
</soapenv:Body>
</soapenv:Envelope>
La réponse SOAP
Exemple : |
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soapenv:Body>
<additionnerResponse xmlns="http://axis.test.jmdoudoux.com">
<additionnerReturn>50</additionnerReturn>
</additionnerResponse>
</soapenv:Body>
</soapenv:Envelope>
Avantages :
Inconvénient :
Les différents formats (style et encodage) des services web ont tous des restrictions d'utilisation qui peuvent engendrer des limitations dans l'écriture des services ou forcer l'utilisation d'un format ou d'un autre.
Dans ce mode, le nom de l'opération n'est pas fourni dans le message : le mapping pour déterminer l'opération à invoquer repose donc sur les paramètres.
Il n'est donc pas possible d'invoquer le service ci-dessous avec le mode document/literal
Exemple : |
public MonService {
public void maMethode(int x, int y);
public void maSecondeMethode(int x, int y);
}
Il pourrait être tentant de toujours utiliser le mode Document/Literal Wrapped mais ce n'est pas forcément le meilleur choix :
Exemple : |
public MonService {
public void maMethode(int x, int y);
public void maMethode(int x);
}
Remarque : WSDL 2.0 interdit l'utilisation des opérations surchargées.
Comme le mode Document/Literal ne contient pas le nom de l'opération à invoquer, il y a des cas où il faut utiliser le mode Document/Literal Wrapped ou un des deux modes RPC/Encoded ou RPC/Literal.
Exemple : |
public MonService {
public void maMethode(int x, int y);
public void maMethode(int x);
public void maSecondeMethode(int x, int y);
}
L'exemple ci-dessus ne peut pas être invoqué ni en Document/Literal ni en Document/Literal Wrapped.
Comme le mode RPC/encoded n'est pas WS-I Basic Profile compliant, il ne reste que le mode RPC/Literal
Le mode RPC/Encoded n'est pas WS-I Basic Profile compliant mais il est parfois nécessaire de l'utiliser. Ce mode est le seul qui puisse prendre en charge un graphe d'objets contenant plusieurs fois la même référence.
Exemple : |
<complexType name="MonElement">
<sequence>
<element name="nom" type="xsd:string"/>
<element name="partie1" type="MonElement" xsd:nillable="true"/>
<element name="partie2" type="MonElement" xsd:nillable="true"/>
</sequence>
</complexType>
RPC/Encoded utilise l'attribut id pour donner un identifiant à un élément et utilise un attribut href pour y faire référence.
Exemple : |
<element1>
<name>nom1</name>
<partie1 href="1234"/>
<partie2 href="1234"/>
</element1>
<element2 id="1234">
<name>nom2</name>
<partie1 xsi:nil="true"/>
<partie2 xsi:nil="true"/>
</element2>
Dans le style Literal, il n'y pas de moyen de faire une référence sur un objet déjà présent dans le graphe : la seule solution c'est de le dupliquer, ce qui va poser des problèmes au consommateur du service.
Avant de développer des services web, il faut valider la solution choisie avec un POC (Proof Of Concept) ou un prototype. Lors de ces tests, il est important de vérifier l'interopérabilité notamment si les services web sont consommés par différentes technologies.
Le choix du moteur SOAP est aussi très important notamment vis-à-vis du support des spécifications, des performances, de la documentation, ...
La mise en oeuvre de services web suit plusieurs étapes.
Etape 1 : définition des contrats des services métiers
Cette étape est une phase d'analyse qui va définir les fonctionnalités proposées par chaque service pour répondre aux besoins
Etape 2 : identification des services web
Cette étape doit permettre de définir les contrats techniques des services web à partir des services métiers définis dans l'étape précédente. Un service métier peut être composé d'un ou plusieurs services web.
La réalisation de cette étape doit tenir compte de plusieurs contraintes :
L'invocation d'un service est coûteuse notamment à cause du mapping objet/xml et xml/objet réalisé à chaque appel. Cette règle est vraie pour toutes les invocations de fonctionnalités distantes mais encore plus avec les services web. Il est donc préférable de limiter les invocations de méthodes d'un service web en proposant des fonctionnalités à forte granularité. Par exemple, il est préférable de définir une opération qui permet d'obtenir les données d'une entité plutôt que de proposer autant d'opérations que l'entité possède de champs. Ceci permet de réduire le nombre d'invocations du service web et réduit le couplage entre la partie front-end et back-end.
La définition des services web doit tenir compte de contraintes techniques liées aux performances ou à la consommation de ressources. Par exemple, si le temps de traitement d'un service web est long, il faudra prévoir son invocation de façon asynchrone ou si les données retournées sont des binaires de tailles importantes, il faudra envisager d'utiliser le mécanisme de pièces jointes (attachment).
Il est préférable de définir des services web qui soient stateless (ne reposant pas par exemple sur une utilisation de la session http). Ceci permet de déployer les services web dans un cluster où la réplication de session sera inutile.
Etape 3 : écriture des services web
Cette étape est celle du codage proprement dit des services web.
Deux approches sont possibles :
Etape 4 : déploiement et tests
Les services web doivent être packagés et déployés généralement dans un serveur d'applications ou un conteneur web.
Pour tester les services web, il est possible d'utiliser des outils fournis par l'IDE ou d'utiliser des outils tiers comme SoapUI qui propose de très nombreuses fonctionnalités pour les tests des services web allant de la simple invocation à l'invocation de scénarios complexes et de tests de charges.
Etape 5 : consommation des services web par les applications clientes
Il faut mettre en oeuvre les outils du moteur Soap utilisé par l'application cliente pour générer les classes nécessaires à l'invocation des services web et utiliser ces classes dans l'application. C'est généralement le moment de faire quelques adaptations pour permettre une bonne communication entre le client et le serveur.
Afin de maximiser la portabilité d'un service web, il faut essayer de suivre quelques recommandations.
Il ne faut pas se lier à un langage de programmation :
Il faut éviter la surchage des méthodes.
Il faut éviter de transformer une classe en service web (notamment en utilisant des annotations) : il est recommandé de définir une interface qui va établir le contrat entre le service et son implémentation. Cette pratique venant de la POO doit aussi s'appliquer pour les services web.
SOAP est assez complexe et sa mise en oeuvre dépend de l'implémentation de la technologie utilisée côtés consommateur et fournisseur de services web. Il en résulte des problèmes d'interopérabilités alors qu'un des but de SOAP est pourtant de s'en affranchir.
Il existe plusieurs types de problèmes :
Les problèmes liés aux versions de SOAP
Les versions SOAP 1.1 et 1.2 étant incompatibles, cela peut entrainer des problèmes de compatibilité si les implémentations des moteurs SOAP utilisés supportent des versions différentes.
Ceci est notamment le cas si l'implémentation du moteur SOAP est assez ancienne.
Les problèmes liés aux modèles de messages
Un message Soap peut être encodé selon plusieurs modèles : le modèle le plus ancien (RPC) est abandonné au profit du modèle Document.
Cela peut introduire des problèmes d'incompatibilité notamment entre des services web existants et des consommateurs plus récents ou vice-versa.
Java propose un ensemble d'API permettant la mise en oeuvre des services web.
API |
Rôle |
JAXP |
API pour le traitement de documents XML : analyse en utilisant SAX ou DOM et transformation en utilisant XSLT. |
JAX-RPC |
API pour le développement de services web utilisant SOAP avec le style RPC |
JAXM |
API pour le développement de services utilisant des messages XML orientés documents |
JAXR |
API pour permettre un accès aux annuaires de référencement de services web |
JAXB |
API et outils pour automatiser le mapping d'un document XML avec des objets Java |
StaX |
API pour le traitement de documents XML |
SAAJ |
API pour permettre la mise en oeuvre des spécifications SOAP with Attachment |
JAX-WS |
API pour le développement grâce à des annotations de services web utilisant SOAP avec le style Document |
L'API de base pour le traitement de document XML avec Java est JAXP. JAXP regroupe un ensemble d'API pour traiter des documents XML avec SAX et DOM et les modifier avec XSLT. Cette API est indépendante de tout parseur. JAXP est détaillée dans le chapitre «Java et XML».
D'autres API sont spécifiques au développement de services web :
JAX-RPC est l'acronyme de Java API for XML-Based Remote Procedure Calls. Cette API permet la mise en oeuvre de services web utilisant SOAP aussi bien côté fournisseur que consommateur : elle permet l'appel de méthodes distantes et la réception de leurs réponses en utilisant SOAP 1.1 et HTTP 1.1.
Cette API a été développée par le JCP sous la JSR 101. Elle propose de masquer un grand nombre de détails de l'utilisation de SOAP notamment en ce qui concerne le codage en XML du message et ainsi de rendre cette API facile à utiliser.
L'utilisation de JAX-RPC est similaire à celle de RMI : le code du client appel les méthodes à partir d'un objet local nommé stub. Cet objet se charge de dialoguer avec le serveur et de coder et décoder les messages SOAP échangés.
Un objet similaire nommé tie permet de réaliser le même type d'opération côté serveur.
La principale différence entre RMI et les services web est que RMI ne peut être utilisé qu'avec Java alors que les services web sont interopérables grâce à XML. Ainsi un client écrit en Java peut utiliser un service web développé avec .Net et vice-versa.
La spécification JAX-RPC définit précisément le mapping entre les types Schema et les types Java.
Type Schema |
Type Java |
xsd:boolean |
boolean |
xsd:short |
short |
xsd:int |
int |
xsd:long |
long |
xsd:integer |
BigInteger |
xsd:float |
float |
xsd:double |
double |
xsd:decimal |
BigDecimal |
xsd:date |
java.util.calendar |
xsd:time |
java.util.calendar |
xsd:datetime |
java.util.calendar |
xsd:base64Binary |
byte[] |
xsd:hexBinary |
byte[] |
Les types primitifs qui sont nillable sont mappés sur leurs wrappers Java correspondants.
Les types complexes sont mappés sur des Java beans.
L'API JAX-RPC est regroupée dans plusieurs sous-packages du package javax.xml.rpc
L'écriture d'un service web avec JAX-RPC requiert plusieurs entités :
L'utilisation de JAX-RPC côté serveur se fait en plusieurs étapes :
1. Définition de l'interface du service (écrite manuellement ou générée automatiquement par un outil à partir de la description du service (WSDL)).
Exemple : |
import java.rmi.Remote;
import java.rmi.RemoteException;
public interface MonWS extends Remote {
public String getMessage(String nom) throws RemoteException;
}
Cette interface doit étendre l'interface java.rmi.Remote.
Toutes les méthodes définies dans l'interface doivent au minimum déclarer la possibilité de lever une exception de type java.rmi.RemoteException. Chaque méthode peut aussi déclarer d'autres exceptions dans sa définition du moment que ces exceptions héritent de la classe java.lang.Exception.
Les méthodes peuvent sans restriction utiliser des types primitifs et l'objet String pour les paramètres et la valeur de retour. Pour les autres types, il existe dans les spécifications une liste minimale prédéfinie de ceux utilisables.
Une interface particulière peut cependant proposer le support d'autres types. Par exemple, l'implémentation de référence propose le support de la plupart des classes de l'API Collection : ArrayList, HashMap, HashTable, LinkedList, TreeMap, TreeSet, Vector, ... Dans ce cas toutefois, attention à la perte de la portabilité lors de l'utilisation d'une autre implémentation.
2. Ecriture de la classe d'implémentation du service
C'est une simple classe Java qui implémente l'interface définie précédemment et posséde un constructeur sans paramètre : dans l'exemple ci-dessous, celui-ci sera généré lors de la compilation car il n'y a pas d'autre constructeur défini.
Exemple : |
public class MonWS_Impl implements MonWS {
public String getMessage(String nom) {
return new String("Bonjour " + nom);
}
}
Il est inutile dans l'implémentation des méthodes de déclarer la levée de l'exception de type RemoteException. C'est lors de l'invocation de la méthode par JAX-RPC que cette exception pourra être levée.
3. Déploiement du service
Le déploiement dépend du moteur Soap utilisé et implique généralement la création d'un fichier de mapping entre l'url et la classe correspondante.
JAX-RPC peut aussi être utilisée pour consommer un service web dans un client.
L'invocation de méthodes côté client se fait de manière synchrone avec JAX-RPC : le client fait appel au service et se met en attente jusqu'à la réception de la réponse
Cette invocation du service peut alors être faite selon trois modes :
Un proxy dynamique met en oeuvre un mécanisme proche de celui utilisé par RMI : le client accède à un service distant en utilisant un stub. Le stub sert de proxy : il implémente l'interface du service et se charge des appels au service en utilisant le protocole SOAP lors de l'invocation de ses méthodes.
Ce proxy est généré par un compilateur dédié qui va utiliser le WSDL pour générer le proxy, notamment le portType pour définir l'interface de l'objet et les binding et port pour connaître les paramètres d'appel du service.
Le proxy généré est responsable de la transformation des invocations de méthodes en requêtes Soap et de la transformation des messages Soap en objets selon les indications fournies dans le WSDL. En cas d'erreur, le message Soap de type fault est transformé en une exception.
|
La suite de cette section sera développée dans une version future de ce document
|
JAXM est l'acronyme de Java API for XML Messaging. Cette API permet le développement de services utilisant des messages XML orientés documents.
JAXM a été développée sous la JSR-067.
Les classes de cette API sont regroupées dans le package javax.xml.messaging.
JAXM met en oeuvre SOAP 1.1 et SAAJ
L'API JAXR (Java API for XML Registries) propose de standardiser les accès aux registres dans lesquels sont recensés les services web. JAXR permet notamment un accès aux registres de type UDDI ou ebXML.
Une implémentation de cette spécification doit être proposée par un fournisseur.
Elle est incluse dans deux packages :
Le support de l'accès aux registres de type ebXML est facultatif.
L'API SAAJ (SOAP with Attachment API for Java) permet l'envoi et la réception de messages respectant les normes SOAP 1.1 et SOAP with attachments : cette API propose un niveau d'abstraction assez élevé permettant de simplifier l'usage de SOAP.
Les classes de cette API sont regroupées dans le package javax.xml.soap.
Initialement, cette API était incluse dans JAXM. Depuis la version 1.1, elles ont été séparées.
SAAJ propose des classes qui encapsulent les différents éléments d'un message SOAP : SOAPMessage, SOAPPart, SOAPEnvelope, SOAPHeader et SOAPBody.
Tous les échanges de messages avec SOAP utilisent une connexion encapsulée dans la classe SOAPConnection. Cette classe permet la connexion directe entre l'émetteur et le receveur du ou des messages.
JAX-WS (Java API for XML based Web Services) est une nouvelle API, mieux architecturée, qui remplace l'API JAX-RPC 1.1 mais n'est pas compatible avec elle. Il est fortement recommandé d'utiliser le modèle de programmation proposé par JAX-WS notamment pour les nouveaux développements.
Elle propose un modèle de programmation pour produire (côté serveur) ou consommer (côté client) des services web qui communiquent par des messages XML de type SOAP.
Elle a pour but de faciliter et simplifier le développement des services web notamment grâce à l'utilisation des annotations. JAX-WS fournit les spécifications pour le coeur du support des services web de la plate-forme Java SE et Java EE.
JAX-WS a été spécifié par la JSR 224 : Java API for XML-Based Web Services (JAX-WS) 2.0.
JAX-WS permet la mise en oeuvre de plusieurs spécifications :
JAX-WS repose sur plusieurs autres JSR :
Le fournisseur de l'implémentation de JAX-WS utilise les spécifications de la JSR 921 pour générer les fichiers de configuration et de déploiement à partir des annotations et d'éventuelles métadonnées.
JAX-WS utilise JAXB 2.0 et SAAJ 1.3. JAXB propose une API et des outils pour automatiser le mapping d'un document XML et des objets Java. A partir d'une description du document XML (Schéma XML ou DTD), des classes sont générées pour effectuer automatiquement l'analyse du document ML et le mapping de ce dernier dans des objets Java.
JAX-WS peut être combiné avec d'autres spécifications comme les EJB 3 par exemple.
JAX-WS est une spécification : pour la mettre en oeuvre, il faut utiliser une implémentation.
L'implémentation de référence de JAX-WS est le projet Metro développé par la communauté du projet GlassFish. Il existe d'autres implémentations notamment Axis 2 qui propose son propre modèle de programmation mais aussi un support de JAX-WS.
Le développement d'un service web en Java avec JAX-WS débute par la création d'une classe annotée avec @WebService du package javax.jws. La classe ainsi annotée définit le endpoint du service web.
Le service endpoint interface (SEI) est une interface qui décrit les méthodes du service : celles-ci correspondent aux opérations invocables par un client.
Il est possible de préciser explicitement le SEI en utilisant l'attribut endpointInterface de l'annotation @WebService
Par rapport à JAX-RPC, l'utilisation de JAX-WS est plus simple : un service web peut être basiquement défini en utilisant une classe de type POJO avec des annotations.
La classe d'implémentation du service est donc très simple : un simple POJO avec des annotations. Il n'y a pas besoin d'implémenter une interface particulière de l'API ni de déclarer une exception dans les méthodes.
Avec JAX-WS, la définition d'un service web et de ses opérations se fait en utilisant des annotations soit dans une interface qui décrit le service soit directement dans la classe d'implémentation.
Ni côté client ni côté serveur, le développeur n'a besoin de manipuler le contenu des messages Soap. Ceci est cependant possible pour des besoins très spécifiques.
Les annotations fournissent des métadonnées exploitées par le moteur Soap pour générer le code des traitements sous-jacents. Le développeur est ainsi déchargé de la plomberie et peut se concentrer sur les traitements métiers qui représentent la plus-value du service.
Le développement d'un service web avec JAX-WS requiert plusieurs étapes :
Pour définir un endpoint avec JAX-WS, il a plusieurs contraintes :
Exemple : |
import javax.jws.WebService;
import javax.jws.WebMethod;
@WebService
public class MonService {
@WebMethod
public String saluer(){
return "Bonjour";
}
}
La classe qui encapsule le endpoint du service peut définir des méthodes annotées avec @PostConstruct et @PreDestroy pour définir des traitements liés au cycle de vie du service. Ces méthodes sont invoquées par le conteneur respectivement avant la première utilisation de la classe et avant le retrait du service.
Il faut compiler la classe et utiliser l'outil wsgen pour générer les classes et fichiers requis pour l'exécution du service web.
L'outil wsgen doit être utilisé pour générer les classes utiles à l'exposition du service web : celles-ci concernent essentiellement des classes qui utilisent JAXB pour mapper le contenu du message avec un objet et vice-versa. Il permet aussi de générer le WSDL et les schémas XML des messages.
La syntaxe est de la forme :
wsgen [options] <sei>
<sei> est le nom pleinement qualifié de classe d'implémentation du SEI.
Option |
Rôle |
-classpath <path> |
Spécifier le classpath |
-d <directory> |
Préciser le répertoire qui va contenir les classes générées |
-help |
Afficher l'aide |
-keep |
Conserver les fichiers générés |
-r <directory> |
Préciser le répertoire qui va contenir les fichiers de ressources générés (WSDL, ...) |
-s <directory> |
Préciser le répertoire qui va contenir les fichiers sources générés |
-verbose |
Activer le mode verbeux |
-version |
Afficher la version |
-wsdl[:protocol] |
Demander la génération du WSDL : ce fichier n'est pas utilisé à l'exécution mais il peut être consulté par le développeur pour vérification. Le protocole est facultatif : il permet de préciser la version SOAP qui sera utilisée (les valeurs possibles sont soap1.1 et soap1.2) |
-servicename <name> |
Définir la valeur de l'attribut name du tag <wsdl:service> lorsque l'option -wsdl est utilisée |
-portname <name> |
Définir la valeur de l'attribut name du tag <wsdl:port> lorsque l'option -wsdl est utilisée |
Une tâche Ant est proposée pour invoquer wsgen par cet outil de build.
Il faut packager le service dans une webapp avec les fichiers compilés.
Il faut déployer le service dans un conteneur web ou un serveur d'applications. Au déploiement, JAX-WS va créer les différentes classes requises pour l'utilisation du service web (celles encapsulant les messages) si celles-ci ne sont pas présentes.
Pour voir le WSDL du service il faut utiliser l'url :
http://localhost:8080/helloservice/hello?wsdl
JAX-WS utilise JAXB-2.0 pour le mapping entre les objets et XML : les objets échangés par les services web peuvent utiliser les annotations de JAXB pour paramétrer finement certains éléments du message SOAP.
Exemple : |
package fr.jmdoudoux.dej.ws;
import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.jws.WebService;
@WebService()
public class PersonneWS {
@WebMethod(operationName = "Saluer")
public String Saluer(@WebParam(name = "personne") final Personne personne) {
return "Bonjour " + personne.getNom() + " "+personne.getPrenom();
}
}
Exemple : |
package fr.jmdoudoux.dej.ws;
import java.util.Date;
public class Personne {
private String nom;
private String prenom;
private Date dateNaiss;
public Personne() {
super();
}
public Personne(String nom, String prenom, Date dateNaiss) {
super();
this.nom = nom;
this.prenom = prenom;
this.dateNaiss = dateNaiss;
}
public synchronized String getNom() {
return nom;
}
public synchronized void setNom(String nom) {
this.nom = nom;
}
public synchronized String getPrenom() {
return prenom;
}
public synchronized void setPrenom(String prenom) {
this.prenom = prenom;
}
public synchronized Date getDateNaiss() {
return dateNaiss;
}
public synchronized void setDateNaiss(Date dateNaiss) {
this.dateNaiss = dateNaiss;
}
}
Le WSDL généré définit l'élément avec un nom dont la première lettre est en minuscule.
Exemple : |
<?xml version='1.0' encoding='UTF-8'?>
<!-- Published by JAX-WS RI at http://jax-ws.dev.java.net. RI's version is
JAX-WS RI 2.1.7-hudson-48-. -->
<xs:schema xmlns:tns="http://ws.test.jmdoudoux.com/"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
version="1.0" targetNamespace="http://ws.test.jmdoudoux.com/">
<xs:element name="Saluer" type="tns:Saluer" />
<xs:element name="SaluerResponse" type="tns:SaluerResponse" />
<xs:complexType name="Saluer">
<xs:sequence>
<xs:element name="personne" type="tns:personne" minOccurs="0" />
</xs:sequence>
</xs:complexType>
<xs:complexType name="personne">
<xs:sequence>
<xs:element name="dateNaiss" type="xs:dateTime" minOccurs="0" />
<xs:element name="nom" type="xs:string" minOccurs="0" />
<xs:element name="prenom" type="xs:string" minOccurs="0" />
</xs:sequence>
</xs:complexType>
<xs:complexType name="SaluerResponse">
<xs:sequence>
<xs:element name="return" type="xs:string" minOccurs="0" />
</xs:sequence>
</xs:complexType>
</xs:schema>
En utilisant l'annotation @XmlType, il est possible de forcer le nom de l'élément généré dans le schéma
Exemple : |
package fr.jmdoudoux.dej.ws;
import java.util.Date;
import javax.xml.bind.annotation.XmlType;
@XmlType(name = "Personne")
public class Personne {
private String nom;
private String prenom;
private Date dateNaiss;
...
}
Le WSDL généré définit l'élément avec un nom dont la première lettre est en majuscule.
Exemple : |
<?xml version='1.0' encoding='UTF-8'?>
<!-- Published by JAX-WS RI at http://jax-ws.dev.java.net. RI's version is
JAX-WS RI 2.1.7-hudson-48-. -->
<xs:schema xmlns:tns="http://ws.test.jmdoudoux.com/"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
version="1.0" targetNamespace="http://ws.test.jmdoudoux.com/">
<xs:element name="Saluer" type="tns:Saluer" />
<xs:element name="SaluerResponse" type="tns:SaluerResponse" />
<xs:complexType name="Saluer">
<xs:sequence>
<xs:element name="personne" type="tns:Personne" minOccurs="0" />
</xs:sequence>
</xs:complexType>
<xs:complexType name="Personne">
<xs:sequence>
<xs:element name="dateNaiss" type="xs:dateTime" minOccurs="0" />
<xs:element name="nom" type="xs:string" minOccurs="0" />
<xs:element name="prenom" type="xs:string" minOccurs="0" />
</xs:sequence>
</xs:complexType>
<xs:complexType name="SaluerResponse">
<xs:sequence>
<xs:element name="return" type="xs:string" minOccurs="0" />
</xs:sequence>
</xs:complexType>
</xs:schema>
Dans la partie cliente, un objet de type proxy est généré pour faciliter l'invocation et la consommation des services web. Les classes de ce proxy sont générées par l'outil wsimport à la demande du développeur à partir du wsdl.
Pour développer un client qui consomme le service web, il y a plusieurs étapes :
|
La suite de cette section sera développée dans une version future de ce document
|
Les handlers proposent un mécanisme de traitements particuliers exécutés par le moteur Soap pour permettre d'agir sur les messages de type requête ou réponse. JAX-WS propose deux types de handlers selon la source des données à obtenir ou modifier dans le message :
Les handlers sont généralement utilisés pour traiter des informations particulières du message Soap
Les handlers pour le protocole SOAP doivent hériter de la classe javax.xml.ws.handler.soap.SOAPHandler. La classe SOAPMessageContext propose des méthodes pour permettre un accès au contenu du message encapsulé dans un objet de type SOAPMessage. Le contenu du message peut alors être manipulé avec l'API SAAJ.
Les logical handlers doivent hériter de la classe javax.xml.ws.handler.LogicalHandler. Ils permettent un accès au contenu du message qui correspond pour un message de type SOAP au body. La classe LogicalMessageContext propose des méthodes pour permettre un accès au contenu du message encapsulé dans un objet de type LogicalMessage.
|
La suite de cette section sera développée dans une version future de ce document
|
La JSR 181 propose une spécification pour permettre le développement de services web en utilisant des POJO et des annotations.
La JSR 181 a pour but de définir un modèle de programmation pour faciliter le développement des services web. Ce modèle repose essentiellement sur les annotations : ceci permet de définir les services web sans avoir à connaître les détails de l'implémentation qui sera mise en oeuvre.
Les annotations proposées permettent un contrôle assez fin sur la façon dont un service web va être exposé et invoqué.
La JSR 181 est une spécification dont le but est de fournir un standard pour la déclaration de services web en proposant :
Chaque implémentation de cette JSR doit fournir des fonctionnalités pour permettre d'exécuter les classes annotées dans un environnement d'exécution pour les services web.
La mise en oeuvre suit plusieurs étapes :
Exemple : |
import javax.jws.WebService;
import javax.jws.WebMethod;
@WebService public class MonService {
@WebMethod
public String saluer() {
return "Bonjour";
}
}
Il existe plusieurs contraintes dont il faut tenir compte lors de l'implémentation du service. La classe de l'implémentation doit :
Par défaut, toutes les méthodes public sont exposées sous la forme d'une opération et ne doivent utiliser que des paramètres respectant ceux définis dans JAX-RPC 1.1. Les méthodes héritées sont aussi exposées sauf celles héritées de la classe Object.
Les annotations sont utilisées dans la classe d'implémentation ou dans l'interface d'un service web.
Toutes les annotations de la JSR 181 sont définies dans le package javax.jws.
Ces annotations sont exploitées au runtime.
Attention : plusieurs implémentations fournissent, en plus des annotations de la JSR 181, des annotations qui leur sont propres. Même si elles sont pratiques, elles limitent la portabilité des services web à s'exécuter dans un autre moteur Soap (exemple : @EnableMTOM, @ServiceProperty, @ServicesProperties dans XFire).
L'annotation javax.ws.WebService permet de définir une classe ou une interface comme étant l'interface du endpoint d'un service web.
L'annotation WebService est la seule annotation obligatoire pour développer un service web.
Si l'annotation est utilisée sur l'interface du service web (SEI), il faut aussi l'utiliser sur la classe d'implémentation en précisant l'interface avec l'attribut endpointInterface.
Cette annotation s'utilise sur une classe ou une interface uniquement.
Attribut |
Rôle |
String name |
le nom du service web utilisé dans l'attribut name de l'élément wsdl:portType du WSDL Par défaut, c'est le nom non qualifié de la classe |
String targetNamespace |
espace de nommage utilisé dans le WSDL Par défaut c'est le nom du package |
String serviceName |
le nom du service utilisé dans l'attribut name de l'élément wsdl:service du WSDL Par défaut, c'est le nom de la classe suffixée par "Service" |
String wsdlLocation |
url relative ou absolue du WSDL prédéfini |
String endpointInterface |
nom pleinement qualifié de l'interface du endpoint (SEI), ce qui permet de séparer l'interface de l'implémentation |
String portName |
Nom du port du service web utilisé dans l'attribut name de l'élément wsdl:port du WSDL |
Exemple : |
@WebService(name = "BonjourWS", targetNamespace = "http://www.jmdoudoux.fr/ws/Bonjour")
public class BonjourServiceImpl {
@WebMethod
public String saluer() {
return "Bonjour";
}
}
L'annotation javax.ws.WebMethod permet de définir une méthode comme étant une opération d'un service web.
Cette annotation s'utilise sur une méthode uniquement. La méthode sur laquelle cette annotation est appliquée doit être public.
Elle possède plusieurs attributs.
Attribut |
Rôle |
String operationName |
nom utilisé dans l'élément wsdl:operation du message Par défaut: le nom de la méthode |
String action |
action associée à l'opération : utilisé comme valeur du paramètre SOAPAction |
boolean exclude |
booléen qui précise si la méthode doit être exposée ou non dans le service web. Cette propriété n'est utilisable que dans une classe et doit être le seul attribut de l'annotation. Par défaut : false |
L'annotation WebMethod ne peut être utilisée que dans une classe ou une interface annotée avec @WebService.
Les paramètres de la méthode, sa valeur de retour et les exceptions qu'elle peut lever doivent obligatoirement respecter les spécifications relatives à ces entités dans les spécifications JAX-RPC 1.1.
L'annotation javax.ws.OneWay permet de définir une méthode comme étant une opération d'un service web qui ne fournit pas de réponse lors de son invocation. Elle permet une optimisation à l'exécution qui évite d'attendre une réponse qui ne sera pas fournie.
Cette annotation s'utilise sur une méthode uniquement : celle-ci ne doit pas avoir de valeur de retour ou lever une exception puisque dans ce cas, il y a une réponse de type SoapFault.
Cette annotation ne possède aucun attribut.
Exemple : |
@WebService
public class MonService {
@WebMethod
@Oneway
public void MonOperation() {
}
}
L'annotation javax.ws.WebParam permet de configurer comment un paramètre d'une opération sera mappé dans le message SOAP.
Cette annotation s'utilise uniquement sur un paramètre d'une méthode de l'implémentation du service.
Attribut |
Rôle |
String name |
nom du paramètre utilisé dans le WSDL Par défaut: le nom du paramètre |
Mode mode |
mode d'utilisation du paramètre. Le type Mode est une énumération qui contient IN, OUT et INOUT Par défaut : IN |
String targetNamespace |
précise l'espace de nommage du paramètre dans les messages utilisant le mode document Par défaut : l'espace de nommage du service web |
boolean header |
booléen qui indique si la valeur du paramètre est contenue dans l'en-tête de la requête http plutôt que dans le corps Par défaut : false |
String partName |
Définit l'attribut name de l'élément <wsdl:part> des messages de type RPC et DOCUMENT/BARE |
Cette annotation est pratique pour permettre d'utiliser le même paramètre dans plusieurs opérations d'un service web encodé en document literal.
L'annotation javax.ws.WebResult permet de choisir comment une valeur de retour d'une opération sera mappée dans l'élément wsdl:part message SOAP.
Cette annotation s'utilise sur une méthode uniquement.
Attribut |
Rôle |
String name |
nom de la valeur de retour utilisé dans le WSDL. Avec le style RPC, c'est l'attribut name de l'élément wsdl:part. Avec le style DOCUMENT, c'est le nom de l'élément dans la réponse Par défaut: return pour RPC et DOCUMENT/WRAPPED et le nom de la méthode suffixé par "Response" pour DOCUMENT/BARE |
String targetNamespace |
espace de nommage de la valeur de retour dans les messages utilisant le mode document Par défaut : l'espace de nommage du service web |
boolean header |
booléen qui indique si la valeur de retour est stockée dans l'en-tête de la requête http plutôt que dans le corps Par défaut : false |
String partName |
attribut name de l'élément wsdl:part des messages de type RPC et DOCUMENT/BARE Par défaut : la valeur de l'attribut name |
Cette annotation est pratique pour permettre d'utiliser la même valeur de retour dans plusieurs opérations d'un service web encodé en Document Literal.
L'annotation javax.jws.soap.SOAPBinding permet de déterminer l'encodage du message et de la réponse SOAP.
Cette annotation s'utilise sur une classe, une interface ou une méthode.
Attribut |
Rôle |
Style style |
Définir le style d'encodage du message. Style est une énumération qui contient DOCUMENT et RPC. Par défaut: DOCUMENT |
Use use |
Définir le format du message. Use est une énumération qui contient ENCODED et LITERAL. Par défaut: LITERAL |
ParameterStyle parameterStyle |
Définir si les paramètres forment le contenu du message ou s'ils sont encapsulés par un tag du nom de l'opération à invoquer. ParameterStyle est une énumération qui contient BARE et WRAPPED. BARE ne peut être utilisé qu'avec le style DOCUMENT Par défaut: WRAPPED |
Exemple : |
@WebService
@SOAPBinding(style=Style.DOCUMENT, use=Use.LITERAL, parameterStyle=ParameterStyle.BARE)
public class MonService {
@WebMethod
public void MonOperation() {
}
};
|
La suite de cette section sera développée dans une version future de ce document
|
|
La suite de cette section sera développée dans une version future de ce document
|
La JSR 224 définit les annotations spécifiques à JAX-WS 2.0.
Toutes ces annotations sont dans le package javax.xml.ws.
L'annotation @BindingType permet de préciser le binding qui sera utilisé pour invoquer le service. Elle s'utilise sur une classe
Attribut |
Rôle |
value |
Identifiant du binding à utiliser. Les valeurs possibles sont : SOAPBinding.SOAP11HTTP_BINDING, SOAPBinding.SOAP12HTTP_BINDING ou HTTPBinding.http_BINDING La valeur par défaut est SOAP11_HTTP_BINDING |
L'annotation @RequestWrapper permet de préciser la classe JAXB de binding qui sera utilisée dans la requête à l'invocation du service. Elle s'utilise sur une méthode
Attribut |
Rôle |
String className |
Préciser le nom pleinement qualifié de la classe qui encapsule la requête (Obligatoire) |
String localName |
Définir le nom de l'élément dans le schéma qui encapsule la requête. Par défaut, c'est la valeur de l'attribut operationName de l'annotation WebMethod |
String targetNamespace |
l'espace de nommage. Par défaut, c'est le targetNamespace du SEI |
L'annotation @ResponseWrapper permet de préciser la classe JAXB de binding qui sera utilisée dans la réponse à l'invocation du service. Elle s'utilise sur une méthode
Attribut |
Rôle |
String localName |
Définir le nom de l'élément dans le schéma qui encapsule la réponse. Par défaut c'est le nom de l'opération définie par l'annotation @WebMethod concaténé à Response |
String targetNamespace |
Définir l'espace de nommage. Par défaut, c'est le targetNamespace du SEI |
String ClassName |
Préciser le nom pleinement qualifié de la classe qui encapsule la réponse (Obligatoire) |
Cette annotation permet de préciser si le provider va avoir accès uniquement au payload du message (PAYLOAD) ou à l'intégralité du message (MESSAGE).
Elle s'utilise sur une classe qui doit obligatoirement implémenter un Provider.
Attribut |
Rôle |
Service.Mode value |
Indiquer si le provider va avoir accès uniquement au payload du message (PAYLOAD) ou à l'intégralité du message (MESSAGE). La valeur par défaut est PAYLOAD |
Exemple : |
@ServiceMode(value=Service.Mode.PAYLOAD)
public class MonOperationProvider implements Provider<Source> {
public Source invoke(Source source)
throws WebServiceException {
Source source = null;
try {
// code du traitement de la requete et generation de la reponse
} catch(Exception e) {
throw new WebServiceException("Erreur durant les traitements du Provider", e);
}
return source;
}
}
Cette annotation s'utilise sur une classe qui encapsule une exception afin de personnaliser certains éléments de la partie Fault du message Soap. Elle s'utilise sur une exception levée par une opération.
Attribut |
Rôle |
String name |
Le nom de l'élément fault Cet attribut est obligatoire |
String targetNameSpace |
Définir l'espace de nommage pour l'élément fault. |
String faultBean |
Nom pleinement qualifié de la classe qui encapsule l'exception |
Cette annotation permet de préciser le portName d'une méthode du SEI.
Elle s'utilise sur une méthode.
Attribut |
Rôle |
String name |
Définir le nom qui va identifier de façon unique l'élément <wsdl:port> du tag <wsdl:service> |
|
La suite de cette section sera développée dans une version future de ce document
|
Cette annotation est à utiliser sur une implémentation d'un Provider
Elle s'utilise sur des classes qui héritent de la classe Provider.
Attribut |
Rôle |
String portName |
nom du port du service (élément <wsdl:portName>) |
String serviceName |
nom du service (élément <wsdl:service>) |
String targetNamespace |
espace de nommage |
String wsdlLocation |
chemin du WSDL du service |
Exemple : |
@WebServiceProvider
public class MonOperationProvider implements Provider<Source> {
public Source invoke(Source source) throws WebServiceException {
Source source = null;
try {
// code du traitement de la requete et generation de la reponse
} catch(Exception e) {
throw new WebServiceException("Erreur durant les traitements du Provider", e);
}
return source;
}
}
L'annotation WebServiceRef permet de définir une référence sur un service web et éventuellement autorise son injection.
Cette annotation est à utiliser dans un contexte Java EE.
Elle s'utilise sur une classe, une méthode (getter ou setter) ou un champ.
Attribut |
Rôle |
String name |
nom JNDI de la ressource. Par défaut sur un champ c'est le nom du champ. Par défaut sur un getter ou un setter, c'est le nom de la propriété |
Class type |
type de la ressource. Par défaut sur un champ, c'est le type du champ. Par défaut sur un getter ou un setter, c'est le type de la propriété |
String mappedName |
nom spécifique au conteneur sur lequel le service est mappé (non portable) |
Class value |
classe du service qui doit étendre javax.xml.ws.Service |
String wsdlLocation |
chemin du WSDL du service |
Il existe de nombreuses implémentations possibles de moteurs SOAP permettant la mise en oeuvre de services web avec Java, notamment plusieurs solutions open source :
A cause d'un effort de spécification tardif de JAX-WS, plusieurs implémentations utilisent une approche spécifique pour la mise en oeuvre et le déploiement de services web, ce qui rend le choix d'une de ces solutions délicat. Heureusement, toutes tendent à proposer un support de JAX-WS.
Même si les concepts sous-jacents sont équivalents, quelle que soit l'implémentation utilisée, sa mise en oeuvre est très différente d'une implémentation à l'autre.
De plus, la plupart des solutions historiques sont relativement complexes à mettre en oeuvre car certains points techniques ne sont pas assez masqués par les outils (code à écrire, fichiers de configuration, descripteurs de déploiement, ...). Avec ces solutions, le développeur doit consacrer une part non négligeable de son temps à du code technique pour développer le service web.
JAX-WS propose une solution pour simplifier grandement le développement des services grâce à l'utilisation d'annotations qui évitent d'avoir à écrire du code ou des fichiers pour la plomberie. JAX-WS, en tant que spécification, est implémentée dans plusieurs solutions.
Axis (Apache eXtensible Interaction System) est un projet open-source du groupe Apache diffusé sous la licence Apache 2.0 qui propose une implémentation d'un moteur de service web implémentant le protocole SOAP : il permet de créer, déployer et consommer des services web.
Son but est de proposer un ensemble d'outils pour faciliter le développement, le déploiement et l'utilisation des services web écrits en Java. Axis propose de simplifier au maximum les tâches pour la création et l'utilisation des services web. Il permet notamment de générer automatiquement le fichier WSDL à partir d'une classe Java et le code nécessaire à l'appel du service web.
Pour son utilisation, Axis 1.0 nécessite un J.D.K. 1.3 minimum et un conteneur de servlets (les exemples de cette section utilise Tomcat).
Le site officiel est à l'url https://ws.apache.org/axis/
C'est un projet open source d'implémentation du protocole SOAP. Il est historiquement issu du projet Apache SOAP.
C'est un outil populaire qui de fait est la référence des moteurs de services web Open Source implémentant JAX-RPC en Java : son utilisation est répandue notamment dans des produits open source ou commerciaux.
La version 1.2 diffusé en mai 2005 apporte le support de l'encodage de type Document/Literal pour être compatible avec les spécifications WS-I Basic Profile 1.0 et JAX-RPC 1.1.
La version 1.3 est diffusée en octobre 2005
La version la plus récente est la 1.4, diffusée en avril 2006.
Attention : Axis 1.x n'est plus supporté au profit de Axis 2 qui possède lui aussi des numéros de versions 1.x.
Axis implémente plusieurs spécifications :
Axis permet donc la mise en oeuvre de :
Attention : Axis 1.0 n'est pas compatible avec
Axis génère le document wsdl du service web : pour accéder à ce document il suffit d'ajouter ?wsdl à l'url d'appel du service web.
L'interopérabilité entre Axis et .Net 1.x est assurée tant que les types utilisés se limitent aux primitives, aux chaînes de caractères, aux tableaux des types précédents et aux Java Beans composés uniquement des types précédents ou d'autres Java Beans.
L'interopérabilité entre Axis 1.4 et .Net 2.0 est bien meilleure. Par exemple, la gestion des objets Nullable dans .Net 2.0 est prise en compte (notamment pour les dates et types primitifs) : il n'est donc plus nécessaire d'utiliser une gestion particulière pour ces objets.
Les extensions sont mises en oeuvre au travers du mécanisme de handlers.
Il faut télécharger Axis 1.x (par exemple le fichier axis-bin-1_4.zip pour la version 1.4) sur le site et décompresser le contenu de l'archive dans un répertoire du système.
Axis s'utilise en tant qu'application web dans un conteneur web. Pour un environnement de développement avec Tomcat, le plus simple est de copier le répertoire axis contenu dans le sous-répertoire webapps issu de la décompression dans le répertoire des applications web du conteneur (le répertoire webapps pour le serveur Tomcat) et de redémarrer le serveur.
Pour vérifier la bonne installation, il suffit d'ouvrir un navigateur sur l'url de l'application web axis :
http://localhost:8080/axis/index.html
Un clic sur le lien « List » permet de voir les services web qui sont installés.
Un clic sur le lien « Validation » permet d'exécuter une JSP qui fait un état des lieux de la configuration du conteneur et des API nécessaires et optionnelles accessibles.
Axis 1.x propose deux méthodes pour déployer un service web :
Axis propose une solution pour facilement et automatiquement déployer une classe Java en tant que service web. Il suffit simplement d'écrire la classe, de remplacer l'extension .java en .jws (java web service) et de copier le fichier dans le répertoire de la webapp axis.
Remarque : il ne faut pas compiler le fichier .jws
Cette solution est un peu moins facile à mettre en oeuvre mais elle permet d'avoir un meilleur contrôle sur le déploiement du service web.
Il faut écrire la classe Java qui va contenir les traitements proposés par le service web.
Exemple : |
public class MonServiceWebAxis2{
public String message(String msg){
return "Bonjour "+msg;
}
}
Il faut compiler cette classe et mettre le fichier .class dans le répertoire WEB-INF/classes de la webapps axis.
Il faut créer le fichier WSDD qui va contenir la description du service web.
Exemple : le fichier deployMonServiceWebAxis2.wsdd |
<deployment xmlns="http://xml.apache.org/axis/wsdd/"
xmlns:java="http://xml.apache.org/axis/wsdd/providers/java">
<service name="monServiceWebAxis2" provider="java:RPC">
<parameter name="className" value="MonServiceWebAxis2"/>
<parameter name="allowedMethods" value="*"/>
</service>
</deployment>
Il faut ensuite déployer le service web en utilisant l'application AdminClient fournie par Axis.
Résultat : |
C:\java\jwsdp-1.1\webapps\axis\WEB-INF\classes>java org.apache.axis.client.Admin
Client deployMonServiceWebAxis2.wsdd
- Processing file deployMonServiceWebAxis2.wsdd
- <Admin>Done processing</Admin>
L'extension wsdd signifie WebService Deployment Descriptor.
C'est un document xml dont le tag racine est deployment.
Les informations relatives au service web sont définies dans le tag service qui possède plusieurs attributs notamment :
Plusieurs informations doivent être fournies avec un tag parameter qui possède les attributs name et value :
Pour faciliter l'utilisation d'un service web, Axis propose l'outil WSDL2Java qui génère automatiquement à partir d'un document WSDL des classes qui encapsulent l'appel à un service web. Grace à ces classes, l'appel d'un service web par un client ne nécessite que quelques lignes de code.
Résultat : |
C:\java\jwsdp-1.1\webapps\axis\WEB-INF\classes>java org.apache.axis.wsdl.WSDL2Ja
va
http://localhost:8080/axis/services/monServiceWebAxis2?wsdl
L'utilisation de l'outil WSDL2Java nécessite une url vers le document WSDL qui décrit le service web. Il génère à partir de ce fichier plusieurs classes dans le package localhost. Ces classes sont utilisées dans le client pour appeler le service web.
Résultat : |
C:\java\jwsdp-1.1\webapps\axis\WEB-INF\classes>java org.apache.axis.wsdl.WSDL2Ja
va http://localhost:8080/axis/services/monServiceWebAxis2?wsdl
Il faut utiliser les classes générées pour appeler le service web.
Exemple : |
import localhost.MonServiceWebAxis2;
import localhost.*;
public class MonServiceWebAxis2Client{
public static void main(String[] args) throws Exception{
MonServiceWebAxis2Service locator = new MonServiceWebAxis2ServiceLocator();
MonServiceWebAxis2 monsw = locator.getmonServiceWebAxis2();
String s = monsw.message("Jean Michel");
System.out.println(s);
}
}
Résultat : |
C:\java\jwsdp-1.1\webapps\axis\WEB-INF\classes>javac MonServiceWebAxis2client.java
C:\java\jwsdp-1.1\webapps\axis\WEB-INF\classes>java MonServiceWebAxis2Client
Bonjour Jean Michel
Axis propose une API regroupée dans le package org.apache.axis.client pour faciliter l'appel de services web par un client.
Exemple : |
package fr.jmdoudoux.dej.axis;
import java.rmi.RemoteException;
import javax.xml.namespace.QName;
import javax.xml.rpc.ServiceException;
import org.apache.axis.client.Call;
import org.apache.axis.client.Service;
public class TestCalculerManuel {
public static void main(String[] args) {
Service service = new Service();
Call call;
try {
call = (Call) service.createCall();
String endpoint = "http://localhost:8080/TestWS/services/Calculer";
call.setTargetEndpointAddress(endpoint);
call.setOperationName(new QName("additionner"));
long resultat = (Long) call.invoke(new Object[] { 10, 20 });
System.out.println("resultat = " + resultat);
} catch (ServiceException e) {
e.printStackTrace();
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
Résultat : |
26 déc. 2006 11:22:32 org.apache.axis.utils.JavaUtils isAttachmentSupported
ATTENTION: Unable to find required classes (javax.activation.DataHandler
and javax.mail.internet.MimeMultipart). Attachment support is disabled.
resultat = 30
La classe Call permet l'invocation d'une méthode d'un service web. Une instance de cette classe est obtenue en utilisant la méthode createCall() d'un objet de type Service.
La méthode setTargetEndpointAddress() permet de préciser l'url du service web à invoquer.
La méthode setOperationName() permet de préciser le nom de l'opération à invoquer.
La méthode invoke() permet de réaliser l'invocation du service web proprement dit.
Pour faciliter cette mise en oeuvre, Axis fournit l'outil wsdl2java qui génère des classes et interfaces à partir du WSDL du service qui sera à invoquer. Ces classes implémentent un proxy qui facilite l'invocation du service web.
Exemple : code client mettant en oeuvre le proxy généré |
package fr.jmdoudoux.dej.axis;
import java.rmi.RemoteException;
import javax.xml.rpc.ServiceException;
public class TestCalculerGenere {
public static void main(String[] args) {
CalculerServiceLocator locator = new CalculerServiceLocator();
long resultat;
Calculer service;
try {
service = locator.getCalculer();
resultat = service.additionner(10, 20);
System.out.println("resultat = " + resultat);
} catch (ServiceException e) {
e.printStackTrace();
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
Résultat : |
26 déc. 2006 11:22:32 org.apache.axis.utils.JavaUtils isAttachmentSupported
ATTENTION: Unable to find required classes (javax.activation.DataHandler
and javax.mail.internet.MimeMultipart). Attachment support is disabled.
resultat = 30
Le proxy généré encapsule toute la mécanique d'appel. Un objet de type ServiceLocator facilite l'obtention du endpoint. L'utilisation du proxy rend le code plus simple, plus compréhensible et plus évolutif puisqu'il est généré.
Cet outil agit comme un proxy qui permet de visualiser les requêtes http échangées entre un client et un serveur.
Résultat : |
C:\java\axis-1_4\lib>java -cp ./axis.jar org.apache.axis.utils.tcpmon 1234 localhost 8080
Les paramètres optionnels pouvant être fournis sont :
Si aucun paramètre n'est fourni, l'outil affiche une première fenêtre qui permet de saisir les informations requises.
L'outil écoute les requêtes faites sur un port local, les affiche puis ces requêtes sont envoyées au serveur. Les réponses suivent le chemin inverse pour permettre leur affichage.
Cet outil est pratique pour afficher le contenu des requêtes et réponses http échangées lors des invocations.
Axis 2 est le successeur du projet Axis : le projet a été complètement réécrit pour proposer une architecture plus modulaire.
Il propose un modèle de déploiement spécifique : les services web peuvent être packagés dans un fichier ayant l'extension.aar (Axis ARchive) ou contenus dans un sous-répertoire du répertoire WEB-INF/services. La configuration se fait dans le fichier META-INF/services.xml
Le runtime d'Axis 2 est une application web qui peut être utilisée dans n'importe quel serveur d'applications Java EE et même un conteneur web comme Apache Tomcat.
Des modules complémentaires permettent d'enrichir le moteur en fonctionnalités notamment le support de certaines spécifications WS-*. Chaque module est packagé dans un fichier avec l'extension .mar
Axis 2 permet de choisir le framework de binding XML/Objets.
XFire était un projet open source initié par la communauté CodeHaus
Ce projet n'est plus maintenu car il a été repris par le projet CXF d'Apache.
Apache CXF est né de la fusion des projets XFire et Celtix.
L'url du projet est https://cxf.apache.org/
CXF propose un support de plusieurs standards des services web notamment, SOAP 1.1 et 1.2, WSDL 1.1 et 1.2, le WS-I Basic Profile, MTOM, WS-Addressing, WS-Policy, WS-ReliableMessaging, et WS-Security.
CXF utilise une api propriétaire mais implémente aussi les spécifications de JAX-WS. CXF propose plus qu'une implémentation d'un moteur SOAP en proposant un framework complet pour le développement de services
Ses principaux objectifs sont la facilité d'utilisation, les performances, l'extensibilité et l'intégration dans d'autres systèmes. CXF utilise le framework Spring.
CXF est utilisé dans d'autres projets notamment ServiceMix et Mule.
Le Java Web Services Developer Pack (JWSDP) est un ensemble d'outils et d'API fournis par Sun qui permet de faciliter le développement, le déploiement et le test des services web et des applications web avec Java.
Le JWSDP contient les outils suivants :
La plupart de ces éléments peuvent être installés manuellement séparément. Le JWSDP propose un pack qui les regroupe en une seule installation et propose en plus des outils spécifiquement dédiés au développement de services web.
Le JWSDP contient les API particulières suivantes :
JWSDP fournit aussi toutes les APIs nécessaires aux développements d'applications Web notamment les API Servlet/JSP, JSTL et JSF.
Remarque : le projet GlassFish remplace le JWSDP.
Pour pouvoir l'utiliser, il faut au minimum un jdk 1.3.1. Il faut télécharger sur le site de Sun le fichier jwsdp-1_1-windows-i586.exe et l'exécuter.
Un assistant guide l'installation :
L'installation a créé une entrée dans le menu "Démarrer/Programmes".
Pour lancer le serveur d'applications Tomcat, il faut utiliser l'option Start Tomcat.
Attention, les ports 8080 et 8081 ne doivent pas être occupés par un autre serveur.
Pour accéder à la console d'administration, il faut lancer un navigateur sur l'url http://localhost:8081/admin. Si la page ne s'affiche pas, il faut aller voir dans le fichier catalina.out contenu dans le répertoire logs où a été installé le JWSDP.
Il faut saisir le nom de l'utilisateur et le mot de passe défini lors de l'installation de JWSDP.
Cette console permet de modifier les paramètres du JWSDP.
Il faut créer un fichier build.properties dans le répertoire home (c:\document and settings\user_name) qui contient :
username= password=
Il faut s'assurer que le chemin C:\java\jwsdp-1_0_01\bin est en premier dans le classpath surtout si une autre version de Ant est déjà installée sur la machine
Il faut lancer Tomcat puis suivre les étapes proposées ci-dessous :
Résultat : |
C:\java\jwsdp-1_0_01\docs\tutorial\examples\jaxrpc\hello>dir
Le volume dans le lecteur C s'appelle SYSTEM
Le numéro de série du volume est 18AE-3A71
Répertoire de C:\java\jwsdp-1_0_01\docs\tutorial\examples\jaxrpc\hello
03/01/2003 13:37 <DIR> .
03/01/2003 13:37 <DIR> ..
01/08/2002 14:16 309 build.properties
01/08/2002 14:17 496 build.xml
01/08/2002 14:17 222 config.xml
01/08/2002 14:16 2 342 HelloClient.java
01/08/2002 14:17 1 999 HelloIF.java
01/08/2002 14:16 1 995 HelloImpl.java
01/08/2002 14:17 545 jaxrpc-ri.xml
01/08/2002 14:17 421 web.xml
8 fichier(s) 8 329 octets
2 Rép(s) 490 983 424 octets libres
C:\java\jwsdp-1_0_01\docs\tutorial\examples\jaxrpc\hello>ant compile-server
Buildfile: build.xml
prepare:
[echo] Creating the required directories....
[mkdir] Created dir: C:\java\jwsdp-1_0_01\docs\tutorial\examples\jaxrpc\hell
o\build\client\hello
[mkdir] Created dir: C:\java\jwsdp-1_0_01\docs\tutorial\examples\jaxrpc\hell
o\build\server\hello
[mkdir] Created dir: C:\java\jwsdp-1_0_01\docs\tutorial\examples\jaxrpc\hell
o\build\shared\hello
[mkdir] Created dir: C:\java\jwsdp-1_0_01\docs\tutorial\examples\jaxrpc\hell
o\build\wsdeploy-generated
[mkdir] Created dir: C:\java\jwsdp-1_0_01\docs\tutorial\examples\jaxrpc\hell
o\dist
[mkdir] Created dir: C:\java\jwsdp-1_0_01\docs\tutorial\examples\jaxrpc\hell
o\build\WEB-INF\classes\hello
compile-server:
[echo] Compiling the server-side source code....
[javac] Compiling 2 source files to C:\java\jwsdp-1_0_01\docs\tutorial\examp
les\jaxrpc\hello\build\shared
BUILD SUCCESSFUL
Total time: 7 seconds
C:\java\jwsdp-1_0_01\docs\tutorial\examples\jaxrpc\hello>ant setup-web-inf
Buildfile: build.xml
setup-web-inf:
[echo] Setting up build/WEB-INF....
[delete] Deleting directory C:\java\jwsdp-1_0_01\docs\tutorial\examples\jaxrp
c\hello\build\WEB-INF
[copy] Copying 2 files to C:\java\jwsdp-1_0_01\docs\tutorial\examples\jaxrp
c\hello\build\WEB-INF\classes\hello
[copy] Copying 1 file to C:\java\jwsdp-1_0_01\docs\tutorial\examples\jaxrpc
\hello\build\WEB-INF
[copy] Copying 1 file to C:\java\jwsdp-1_0_01\docs\tutorial\examples\jaxrpc
\hello\build\WEB-INF
BUILD SUCCESSFUL
Total time: 2 seconds
C:\java\jwsdp-1_0_01\docs\tutorial\examples\jaxrpc\hello>ant package
Buildfile: build.xml
package:
[echo] Packaging the WAR....
[jar] Building jar: C:\java\jwsdp-1_0_01\docs\tutorial\examples\jaxrpc\hel
lo\dist\hello-portable.war
BUILD SUCCESSFUL
Total time: 2 seconds
C:\java\jwsdp-1_0_01\docs\tutorial\examples\jaxrpc\hello>ant process-war
Buildfile: build.xml
set-ws-scripts:
process-war:
[echo] Running wsdeploy....
[exec] info: created temporary directory: C:\java\jwsdp-1_0_01\docs\tutoria
l\examples\jaxrpc\hello\build\wsdeploy-generated\jaxrpc-deploy-b5e49c
[exec] info: processing endpoint: MyHello
[exec] Note: sun.tools.javac.Main has been deprecated.
[exec] 1 warning
[exec] info: created output war file: C:\java\jwsdp-1_0_01\docs\tutorial\ex
amples\jaxrpc\hello\dist\hello-jaxrpc.war
[exec] info: removed temporary directory: C:\java\jwsdp-1_0_01\docs\tutoria
l\examples\jaxrpc\hello\build\wsdeploy-generated\jaxrpc-deploy-b5e49c
BUILD SUCCESSFUL
Total time: 15 seconds
C:\java\jwsdp-1_0_01\docs\tutorial\examples\jaxrpc\hello>ant deploy
Buildfile: build.xml
deploy:
[deploy] OK - Installed application at context path /hello-jaxrpc
[deploy]
BUILD SUCCESSFUL
Total time: 7 seconds
C:\java\jwsdp-1_0_01\docs\tutorial\examples\jaxrpc\hello>ant generate-stubs
Buildfile: build.xml
set-ws-scripts:
prepare:
[echo] Creating the required directories....
generate-stubs:
[echo] Running wscompile....
[exec] Note: sun.tools.javac.Main has been deprecated.
[exec] 1 warning
BUILD SUCCESSFUL
Total time: 14 seconds
C:\java\jwsdp-1_0_01\docs\tutorial\examples\jaxrpc\hello>ant compile-client
Buildfile: build.xml
prepare:
[echo] Creating the required directories....
compile-client:
[echo] Compiling the client source code....
[javac] Compiling 1 source file to C:\java\jwsdp-1_0_01\docs\tutorial\exampl
es\jaxrpc\hello\build\client
BUILD SUCCESSFUL
Total time: 4 seconds
C:\java\jwsdp-1_0_01\docs\tutorial\examples\jaxrpc\hello>ant jar-client
Buildfile: build.xml
jar-client:
[echo] Building the client JAR file....
[jar] Building jar: C:\java\jwsdp-1_0_01\docs\tutorial\examples\jaxrpc\hel
lo\dist\hello-client.jar
BUILD SUCCESSFUL
Total time: 2 seconds
C:\java\jwsdp-1_0_01\docs\tutorial\examples\jaxrpc\hello>ant run
Buildfile: build.xml
run:
[echo] Running the hello.HelloClient program....
[java] Hello Duke!
BUILD SUCCESSFUL
Total time: 5 seconds
C:\java\jwsdp-1_0_01\docs\tutorial\examples\jaxrpc\hello>
Java EE 5 utilise une nouvelle API pour le développement de services web : JAX-WS (Java API for XML Web Services).
Java SE 6 fournit en standard une implémentation de JAX-WS 2.0 permettant ainsi de consommer mais aussi de produire des services web uniquement avec la plate-forme SE.
L'écriture et le déploiement d'un service web suit plusieurs étapes.
Il faut écrire la classe du service web en utilisant les annotations de JAX-WS.
Exemple : |
package fr.jmdoudoux.dej.ws;
import javax.jws.WebService;
@WebService
public class TestWS {
public String Saluer(final String nom) {
return "Bonjour " + nom;
}
}
La classe javax.xml.ws.Endpoint encapsule le endpoint d'un service web permettant ainsi son accès.
La méthode publish() permet de publier un endpoint associé à l'url fournie en paramètre.
Exemple : |
package fr.jmdoudoux.dej.ws;
import javax.xml.ws.Endpoint;
public class Main {
public static void main(String[] args) {
System.out.println("Lancement du serveur web");
Endpoint.publish("http://localhost:8080/ws/TestWS", new TestWS());
}
}
Il faut compiler la classe.
Il faut ensuite utiliser l'outil wsgen pour générer les classes JAXB qui vont mapper les requêtes et réponses des messages Soap.
Résultat : |
C:\eclipse35\workspace\TestJava6\bin>wsgen -cp . -d ../src fr.jmdoudoux.dej.ws.TestWS
Dans l'exemple, deux classes sont générées dans le package fr.jmdoudoux.dej.ws.jaxws :
Il faut alors exécuter la classe Main : un serveur web minimaliste est lancé et le service web y est déployé.
Attention, l'environnement d'exécution doit être un JDK.
Il suffit alors d'ouvrir l'url http://localhost:8080/ws/TestWS?wsdl dans un navigateur
Le navigateur affiche alors le contenu du WSDL qui décrit le service web.
Le service web peut alors être consommé par un client, tant que l'application est en cours d'exécution.
Exemple : Le message Soap de la requête |
<soapenv:Envelope
xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:ws="http://ws.test.jmdoudoux.com/">
<soapenv:Header/>
<soapenv:Body>
<ws:Saluer>
<arg0>JM</arg0>
</ws:Saluer>
</soapenv:Body>
</soapenv:Envelope>
Exemple : Le message Soap en réponse |
<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
<S:Body>
<ns2:SaluerResponse xmlns:ns2="http://ws.test.jmdoudoux.com/">
<return>Bonjour JM</return>
</ns2:SaluerResponse>
</S:Body>
</S:Envelope>
Le projet Metro est une pile pour services web utilisée dans le serveur d'applications GlassFish V2 et V3. Metro est l'implémentation de référence de JAX-WS. Metro est livré avec GlassFish mais il est possible de l'utiliser dans d'autres serveurs d'applications ou conteneurs web, par exemple Tomcat. Dans ce dernier cas, il faut ajouter les bibliothèques de Metro et JAXB.
Metro est composé de deux éléments :
Le projet Tango est une implémentation open source des spécifications Reliability, Security et Transaction des spécifications WS-*, ce qui facilite l'interopérabilité avec le framework WCF (Windows Communication Foundation) de Microsoft .Net versions 3.0 et ultérieures.
WSIT (Web Service Interoperability) est un projet commun entre Sun et Microsoft pour garantir l'intéropérabilité des piles de services web des plates-formes Java et .Net (avec le Windows Communication Framework).
Cette interopérabilité est assurée car Metro et WCF supportent tous les deux plusieurs spécifications WS-* :
La mise en oeuvre de ces spécifications via WSIT repose sur une configuration dans un fichier XML. Le contenu de ce fichier peut être fastidieux à créer ou à modifier : Netbeans propose des assistants graphiques qui facilitent grandement leur mise en oeuvre.
Pour inclure des données binaires importantes dans un message SOAP, il faut utiliser le mécanisme des pièces jointes (attachment).
Malheureusement, ce mécanisme est implémenté par plusieurs standards :
MTOM devient le standard utilisé par Java (JAX-WS) et .Net (WSE 3.0)
Les nombreuses spécifications concernant les services web sont fréquemment incomplètes ou peu claires : il en résulte plusieurs incompatibilités lors de leur mise en oeuvre.
Le consortium WS-I (Web Service Interoperability) http://www.ws-i.org/ a été créé pour définir des profiles qui sont des recommandations dont le but est de faciliter l'interopérabilité des services web entre plateformes, systèmes d'exploitation et langages pour promouvoir ces normes.
Le WS-I a définit plusieurs spécifications :
Le site web est à l'url : www.ws-i.org
WS-I Basic Profile est un ensemble de recommandations dont le but est d'améliorer l'interopérabilité entre les différents moteurs SOAP.
|
La suite de cette section sera développée dans une version future de ce document
|
Les spécifications SOAP et WSDL permettent de réaliser des échanges de messages basiques. L'accroissement de l'utilisation des services web a fait émerger la nécessité de fonctionnalités supplémentaires telles que la gestion de la sécurité, des transactions, de la fiabilité des messages, ...
Les spécifications désignées sous l'acronyme WS-* concernent les spécifications de seconde génération des services web (elles étendent les spécifications de la première génération de spécifications constituée par SOAP, WSDL, UDDI). L'abréviation WS-* est communément utilisée car la majorité de ces spécifications commence par WS-.
De nombreuses autres spécifications sont en cours d'élaboration et de tentatives de standardisation ou de reconnaissance par le marché.
Fréquemment ces spécifications sont complémentaires ou dépendantes voire même dans quelques cas concurrentes car elles sont soutenues par des acteurs du marché ou des organismes de standardisation différents. Il est généralement nécessaire d'utiliser plusieurs de ces spécifications pour permettre de répondre aux besoins notamment en terme de sécurité, fiabilité, ...
Il est aussi très important de tenir compte de la maturité d'une spécification avant de la mettre en oeuvre.
Ces spécifications permettent de mettre en oeuvre des scénarios complexes impliquant l'utilisation de services web.
Toutes ces spécifications requièrent l'utilisation de SOAP.
|
La suite de cette section sera développée dans une version future de ce document
|
Développons en Java v 2.40 Copyright (C) 1999-2023 Jean-Michel DOUDOUX. |