SOAP, Simple Object Access Protocol, avec Java


1- Présentation générale de SOAP

1-1- Introduction

SOAP représente l'acronyme de Simple Object Access Protocol (en français, Protocole Simple d'Accès aux Objets). En fait, il s'agit essentiellement d'un protocole d'échanges d'informations au sein d'une architecture distribuée, et qui présente les caractéristiques générales suivantes :

Le fait que SOAP soit basé XML (et donc qu'il soit un protocole basé texte) est fondamental, car il rend SOAP beaucoup plus attractif (notamment en termes de débuggage) que d'autres protocoles tels que IIOP (Inter-ORB Protocol , assurant la communication entre objets JAVA et CORBA), ORPC (Object Remote Procedure Call) et JMRP (Java Remote Method Protocol utilisé par RMI), tous trois des protocoles reposant sur des flux binaires, et donc plus difficiles à gérer.

1-2- Service RPC

Dans le scénario RPC, SOAP agit simplement à la manière d'un système XML-RPC plus flexible, facilitant notamment la gestion des erreurs ainsi que la transmission de types complexes sur le réseau : un client invoque une procédure distante sur un serveur se trouvant quelque part, puis reçoit une forme de réponse.

Dans ce tutoriel, un scenario est proposé à des fins de test :

La présentation générale de SOAP sera effectuée sur la base du scénario 2 récupérant une variable globale du serveur MySQL distant.

Soit l'interface JAVA :

public interface GetMySQLInfos
{
   public String GetMySQLVariable(String my_Variable)     
}

Un client appelant la méthode GetMySQLVariable avec une variable déterminée attend de recevoir un message venant du serveur donnant la valeur pour la variable MySQL indiquée. SOAP consiste basiquement à sérialiser l'appel de méthode et à l'envoyer vers la machine distante, tout ceci via XML. En supposant que nous voulions simuler l'appel de la méthode GetMySQLVariable("version"), on pourrait intuitivement proposer le message suivant :

<GetMySQLInfos>
  <GetMySQLVariable>
     <my_Variable>version</my_Variable>
  </GetMySQLVariable>
</GetMySQLInfos>

Le nom de l'interface est le noeud racine du message, la méthode et le paramètre sont également transformés en noeuds.

La réponse renvoyée par le serveur pourrait être quant à elle, toujours dans la même logique, de la forme suivante :

<GetMySQLInfos>
  <GetMySQLVariableResponse>
     <my_VariableValue>4.1.0-alpha-max-nt</my_VariableValue>
  </GetMySQLVariableResponse>
</GetMySQLInfos>

Le noeud racine est toujours l'interface GetMySQLInfos, mais cette fois le noeud fils consiste en la concaténation du nom et de la méthode et de la chaîne Response.

1-3- L'enveloppe SOAP-RPC

Ce qui précède peut-être considéré comme la mouture d'un service RPC SOAP. En fait, voilà à quoi ressemble une véritable requête SOAP, et les quelques modifications supplémentaires apportées pour le tutoriel :

<SOAP-ENV:Envelope
 xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
 xmlns:xsi="http://www.w3.org/1999/XMLSchema-instance"
 xmlns:xsd="http://www.w3.org/1999/XMLSchema">

  <SOAP-ENV:Header>
  </SOAP-ENV:Header>

  <SOAP-ENV:Body>
    <ns1:GetMySQLVariable xmlns:ns1="GetMySQLInfos"
      SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
      <my_Variable xsi:type="xsd:string">version</my_Variable>
    </ns1:GetMySQLVariableRequest>
  </SOAP-ENV:Body>
  
</SOAP-ENV:Envelope>

Et voilà ce que serait une réponse renvoyée par le serveur :

<SOAP-ENV:Envelope
 xmlns:SOAP-ENV="http://schemas.xmlsoap.
 xmlns:xsi="http://www.w3.org/1999/XMLSchema-instance"
 xmlns:xsd="http://www.w3.org/1999/XMLSchema">
  
  <SOAP-ENV:Body>
      <ns1:GetMySQLVariableResponse xmlns:ns1="GetMySQLInfos"
       SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
      <return xsi:type="xsd:string">4.0.1-alpha-max-nt</return>
      </ns1:GetMySQLVariableResponse>
  </SOAP-ENV:Body>
</SOAP-ENV:Envelope>

L'ensemble du message XML est contenu dans un élément de plus haut niveau (top-level element) <Envelope>, associé à l'espace de nommage http://schemas.xmlsoap.org/soap/envelope.

Cet élément est obligatoire, et doit correspondre à l'espace de nommage sus-cité. Si un serveur

SOAP reçoit une requête qui référence un autre espace de nom, il rejettera alors cette requête avec un code VersionMismatch dans l'élément <faultcode> .

Le premier élément fils de <Envelope> est l'élément <Header>, optionnel (d'ailleurs non pris en charge par l'implémentation Apache de SOAP). Le but principal d'un tel élément est de fournir des extensions au protocole, n'ayant pas directement à voir avec telle ou telle méthode spécifiée, mais apportant plutôt des informations contextuelles comme l'identifiant de transactions et/ou des informations relatives à la sécurité

<SOAP-ENV:Header>
  <transaction
    xmlns="http://sitexml.com/articles">
         <id>123455-4543544</id>
  </transaction>
</SOAP-ENV:Header>

Vient ensuite l'élément <BODY> qui doit être un fils direct de l'élément <Envelope>. Si un en-tête est présent, alors le corps doit immédiatement le suivre. On remarque ici, précisément à propos de

l'appel RPC, que le nom de l'interface (i.e. GetMySQLInfos) n'est pas le nom d'un noeud comme nous l'avions auparavant approximé, mais que celui-ci réfère plutôt à un espace de nom, ns1.

On note également la valeur de l'attribut encodingStyle, égale à http://schemas.xmlsoap.org/soap/encoding/, informant le serveur de l'encodage à utiliser afin de sérialiser/désérialiser la méthode.

1-4- L'enveloppe d'un message SOAP

L'anatomie d'un Message SOAP est plus simple que celle d'un service SOAP basé RPC.
Elle consiste eseentiellement en:

1. l'encapsulation (wrapping) du message XML d'origine dans le body SOAP
2. l'inclusion du body SOAP dans l'enveloppe SOAP.
3. l'ajout facultatif d'un Header au sein de cette même enveloppe

Ainsi, si le document XML d'origine à envoyer est le suivant :

<xml version="1.0" encoding="UTF-8"?>
  <OrdreAchat xmlns="urn:planetexml-articles">
     <livraison pays="FR">
         <nom>Paul Durand</nom>
         <rue>14 rue des Roses</rue>
         <ville>Paris</ville>
         <code_postal>75011</code_postal>
      </livraison>
      <articles>
         <article>
            <type>livre</type>
            <titre>La genealogie de la morale</titre>
            <quantite>3</quantite>
            <prix>3.00</prix>
            <commentaire>Livraison rapide SVP!</commentaire>
          </article>
      </articles>
  </OrdreAchat>

sa mise en conformité avec la spécification SOAP ressemblera à ceci:

< xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope
  xmlns:SOAP="http://schemas.xmlsoap.org/soap/envelope/"
  xmlns:xsi="http://www.w3.org/1999/XMLSchema-instance"
  xmlns:xsd="http://www.w3.org/1999/XMLSchema">
    
  <SOAP-ENV:Header>
   ...
  </SOAP-ENV:Header>
   
  <SOAP-ENV:Body>
    <OrdreAchat
       xmlns="urn:planetexml-articles">
       <livraison pays="FR">
          <nom>Paul Durand</nom>
          <rue>14 rue des Roses</rue>
          <ville>Paris</ville>
          <code_postal>75011</code_postal>
      </livraison>
      <articles>
        <article>
          <type>livre</type>
          <titre>La genealogie de la morale</titre>
          <quantite>3</quantite>
          <prix>3.00</prix>
          <commentaire>Livraison rapide SVP!</commentaire>
       </article>
      </articles>
    </OrdreAchat>
  </SOAP-ENV:Body>
</SOAP-ENV:Envelope>

1-5- Fault

Le fait d'utiliser SOAP ne garantit malheureusement pas le fait que les requêtes soient toujours couronnées de succès. Les choses peuvent mal se passer en différents moments du processus de traitement. Le serveur renvoie alors une réponse spécifiant un message d'erreur au sein d'un élément <Fault>, fils direct de <Body>:

<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
  xmlns:xsi="http://www.w3.org/1999/XMLSchema-instance"
  xmlns:xsd="http://www.w3.org/1999/XMLSchema">
  <SOAP-ENV:Body>
    <Fault>
        <faultcode>Server</faultcode>
        <faultstring>service:'urn:helloworld' unknown<falutstring>
        <faultactor>/soap/servlet/rpcrouter</faultactor>
    </Fault>
  </SOAP-ENV:Body>
</SOAP-ENV:Envelope>

Sans exception, l'élément <Fault> doit obligatoirement contenir un élément <faultcode> ainsi qu'un élément <faultstring>. Le premier consiste en un code permettant d'identifier la nature du problème, et dont la liste est définie par la spécification SOAP (dont le code VersionMismatch dont il a été question précédemment, indiquant la présence d'un espace de nom erronné, ou le code Client, indiquant que le message de requête n'était pas correctement formé ou ne contenait pas les informations appropriées en vue d'être validé, etc.). Le deuxième élément <faultstring> est quant à lui destiné à une lecture humaine, et explicite plus clairement la cause de l'erreur: dans l'exemple ci-dessus, le serveur est incapable de trouver le service identifié par l'URN helloworld, celui-ci n'ayant vraisemblablement pas été déployé.

1-6- La liaison au protocole (SOAP Protocol Binding)

Dans le cadre de cette présentation générale, il reste à parler d'un dernier élément, qui est la façon dont un message SOAP est lié au protocole sur lequel il se déploie. Bien qu'il soit possible d'utiliser SOAP avec d'autres protocoles que HTTP (SMTP, FTP, etc.), la documentation se concentre sur HTTP, qui reste le protocole en final le plus largement utilisé.

En premier lieu, il vaut la peine de souligner que, ainsi que le note la spécification SOAP Version 1.2, le fait que SOAP se déploie au-dessus de HTTP ne signifie pas qu'il remplace ou substitue quoi que ce soit de la sémantique du protocole, mais plutôt qu'il en hérite, à son grand avantage ("Carrying SOAP in HTTP does not mean that SOAP overrides existing semantics of HTTP, but rather than .)

La définition d'une liaison HTTP concerne trois parties (areas) HTTP: la requête HTTP, la réponse HTTP, et enfin le cadre d'extension HTTP (. Dans tous les cas, le media type "text/xml" doit être utilisé lors de l'encapsulation de messages SOAP dans des échanges HTTP.

En ce qui concerne la requête HTTP, la grande majorité des liaisons se fait avec la méthode de requête HTTP POST. Le champ d'en-tête de requête http SOAPAction (SOAPAction HTTP request header) peut être utilisé afin d'indiquer la cible de requête SOAP HTTP. La valeur qui doit renseigner un tel champ représente l'URI de la cible. SOAPAction sert notamment au filtrage des requêtes par les firewalls.

Une valeur de chaîne vide signifie que la cible du message SOAP est fournie par l'URI de la requête HTTP (on verra que c'est comme cela que fonctionne l'implémentation SOAP d'Apache), tandis que l'absence pure et simple de valeur indique qu'il n'y a pas de cible explicite du message. Voici un exemple d'en-tête de requête SOAP HTTP:

POST /soap/servlet/rpcrouter HTTP/1.1
Host: localhost
Content-Type: text/xml; charset="utf-8"
Content-Length: 345
SOAPAction: "http://electrocommerce.org/">
<env:Envelope xmlns:env="http://www.w3.org/2001/06/soap-envelope">
 ...
</env:Envelope>

En ce qui concerne la réponse HTTP, SOAP suit la sémantique des codes de statut HTTP (HTTP Status codes) pour communiquer des informations de statut sur HTTP.
Par exemple, un code de statut 2xx indique que la requête du client incluant le composant SOAP a été reçu avec succès, correctement interprété, accepté, etc. Si une erreur se produit pendant le traitement de la requête, le serveur SOAP HTTP doit renvoyer une réponse HTTP 500 "Internal Server Error" et inclure un élément SOAP fault dans le message SOAP de retour

La réponse au message SOAP précédent a la forme suivante:

HTTP/1.1 200 OK
Content-Type: text/xml charset="utf-8"
Content-Length: 323
<env:Envelope xmlns:env="http://www.w3.org/2001/06/soap-envelope">
 ...
</env:Envelope>

Enfin, un message SOAP peut être utilisé avec le cadre d'extension http (téléchargeable à l'adresse http://www.w3.org/Protocols/HTTP/ietf-http-ext ) , dans le but d'identifier la présence et la destination d'une requête HTTP. La principale caractéristique de cette extension qui dépasse largement les seuls besoins SOAP est de définir un mécanisme afin d'étendre dynamiquement la fonctionnalité des clients et serveurs HTTP par l'utilisation des espaces de nom. Cela fonctionne de la façon suivante:

  1. Les concepteurs se mettent d'accord sur une extension et assignent à cette extension une URI globale unique. Pour SOAP, il s'agit de l'adresse http://schemas.xmlsoap.org/soap/envelope
  2. Un client ou un serveur implémentant une telle extension déclare son utilisation via cet URI dans le header HTTP. La déclaration de l'URI et l'espace de nom qui lui est associé est effectuée conformément à l'HTTP Extension Framework
  3. L'application HTTP peut alors implémenter le comportement souhaité sans risque de conflit

La même requête que celle vue précédemment a la forme suivante, une fois étendue:

M-POST /soap/servlet/rpcrouter HTTP/1.1
Man: "http://schemas.xmlsoap.org/soap/envelope"; ns=144
Host: localhost
Content-Type: text/xml; charset="utf-8"
Content-Length: 345
144-SOAPAction: "http://electrocommerce.org/">
<env:Envelope xmlns:env="http://www.w3.org/2001/06/soap-envelope">
 ...
</env:Envelope>

La première différence entre la précédente requête HTTP et celle vue précédemment est la mention: M-POST plutôt que POST. M-POST définit une requête HTTP mandataire (mandatory HTTP request): on qualifie ainsi une requête si celle-ci inclut au moins une déclaration de la forme suivante: Man:"http://schemas.xmlsoap.org/soap/envelope"; ns=144

Dés lors, le reste de l'en-tête HTTP doit contenir au moins une declaration extension pour l'URI spécifiée.

Dans notre cas, il s'agit de la ligne suivante: 144-SOAPAction: "http://electrocommerce.org/">

On note que SOAPAction est préfixé par 144: le préfixe utilisé pour l'URI spécifié dans l'élément d'en-tête Man:.

2- Configuration et utilisation de SOAP Apache

2-1- Installation et configuration

L'étape préliminaire est d'inclure à la fois dans son classpath côté client et dans le classpath du moteur de servlet les librairies mail.jar (disponible à l'adresse http://java.sun.com/products/javamail/, requise pour la pris en charge du protocole de transfert SMTP compris dans le projet Apache SOAP et activation.jar (disponible à l'adresse http://java.sun.com/products/beans/glasgow/jaf.html ainsi qu'enfin un parseur compatible JAXP, sensible aux espaces de nom: Apache Xerces v1.1.2 ou plus fait très bien l'affaire.

Les archives mail.jar et activation.jar sont déjà fournies avec le serveur Tomcat 4.0.1. Dans la suite de la documentation $TOMCAT_DIR ou %TOMCAT_DIR% selon le système d'exploitation désignera le répertoire d'installation de Tomcat.

Nous utilisons dans ce tutoriel SOAP Apache (une des trois majeures implémentations de la spécification SOAP, la seconde étant toujours proposée par la fondation Apache sous le nom d'Apache Axis et la dernière étant le fait de Microsoft), disponible en téléchargement sur le site de la fondation Apache à l'adresse http://xml.apache.org/dist/soap/version-2.2 . SOAP d'Apache implémente un composant côté client, ainsi qu'un composant côté serveur. Le fichier à télécharger pour les plate-formes Windows est soap-bin-2.2.zip et soap-bin-2.2.tar.gz pour les plate-formes Linux. Une fois ceci fait, il ne reste plus qu'à extraire la ressource où vous le voulez, par exemple avec l'utilitaire Winzip.

L'étape la plus délicate consiste à présent à déployer SOAP dans le moteur de servlet utilisé, ici Tomcat 4.0.1. Pour cela, deux moyens sont possibles:

  1. Le plus facile: La distribution Apache de SOAP inclut une archive Web à l'adresse /soap-2_2/webapps/soap.war. Il suffit simplement de déplacer cette archive dans le répertoire webapps de Tomcat et de démarrer ce dernier. Il s'agit de cette installation qui a été choisie dans le contexte de cette documentation. Lors de cette opération, un sous répertoire soap est automatiquement déployé dans le répertoire TOMCAT_DIR/webapps. Dans la suite de la documentation $SOAP_INSTALL ou %SOAP_INSTALL% selon le système d'exploitation désignera le répertoire d'installation de soap 2.2 dans le moteur de servlets.
  2. Sinon, il convient de créer un nouveau <Context> dans le fichier server.xml situé dans %tomcat_dir%/conf ou $tomcat_dir/conf, comme suit:

    <Context path="/soap" docBase="chemin-de-apache-soap/webapps/soap" debug="1" reloadable="true">
    </Context>

Dans ce cas-là, il faut veiller à bien mettre toutes les archives contenues dans le dossier /lib de la distribution dans votre classpath.

Une solution simple pour préparer l'environnement côté client et côté serveur consiste à appliquer la variable classpath selon les scripts ci-dessous :

DOS :

set CLASSPATH=%CLASSPATH%; %TOMCAT_DIR%\common\lib\mail.jar;%TOMCAT_DIR%\common\lib\activation.jar; 
%SOAP_INSTALL%\WEB-INF\classes

Unix / Linux :

CLASSPATH=$CLASSPATH;$TOMCAT_DIR\common\lib\mail.jar;$TOMCAT_DIR\common\lib\activation.jar;
$SOAP_INSTALL\WEB-INF\classes
export CLASSPATH

2-2- Test de SOAP

Pour tester si le serveur SOAP est correctement configuré, pointer le navigateur à l'adresse http://<SERVER>:<PORT>/soap/servlet/rpcrouter, après avoir pris bien soin d'avoir démarré Tomcat, le port à spécifier correspond à celui configuré pour Tomcat. La réponse ci-dessous devrait alors être affichée dans le navigateur :

Adresse : http://localhost:9002/soap/servlet/rpcrouter

SOAP RPC ROUTER
Sorry, I don't speak via HTTP GET - you have to use HTTP POST to talk
to me.

Ce message contrairement à son apparence indique que le serveur soap fonctionne correctement.

Un autre test simple consiste à solliciter la page d'administration de SOAP Apache (admin) : http://<SERVER>:<PORT>/soap/admin/index.html

2-3- Déploiement des services SOAP

2-3-1- Déploiement logique d'un service SOAP

Deux méthodes peuvent être envisagées pour le déploiement logique d'un service SOAP :

L'utilisation de classe Java org.apache.soap.server.ServiceManagerClient est résumée ci-dessous :

Usage: java org.apache.soap.server.ServiceManagerClient [-auth username:password] url operation arguments
where               
        username and password is the HTTP Basic authentication info
        url is the Apache SOAP router's URL whose services are managed
        operation and arguments are:
                    deploy deployment-descriptor-file.xml
                    list
                    query service-name
                    undeploy service-name

Des exemples concrets seront donnés dans les paragraphes qui suivent.

2-3-2- Déploiement physique d'un service SOAP

La façon la plus élégante d'implémenter physiquement un service SOAP sur le serveur consiste à utiliser l'outil ANT, toujours de la fondation Apache. Mais pour un service modeste, la création manuelle de l'arborescence sur le serveur est envisageable.

Il est possible de créer l'arborescence où l'on veut sur le disque dur, à condition de bien éditer le fichier server.xml du dossier conf de Tomcat. Le plus simple cependant consiste à utiliser l'arborescence webapps->soap->WEB-INF->classes déjà mise en place au sein de Tomcat après le déploiement de SOAP, et de lui adjoindre le nouveau dossier contenant les classes propres le service SOAP à implémenter physiquement.

Il est nécessaire de redémarrer Tomcat pour la prise en charge du nouveau service SOAP.

3- Cas pratique SOAP RPC

3-1- Présentation du cas SOAP RPC

Voici schématiquement le cas pratique SOAP RPC :

3-2- Implémentation du service SOAP RPC côté serveur

3-2-1- Développement du service SOAP RPC

L'exemple simple ci-dessous de service SOAP consiste à interroger une base de données MySQL avec la commande « show variables like param%' » où param est le paramètre donné au service.

/*
* GetMySQLInfos.java
*
* Classe de test SOAP RPC
*
*/
package com.capdata.soap;
import java.sql.*;

public class GetMySQLInfos {
    public String GetMySQLVariable(String my_Variable)
             throws Exception {
             
             try {
                 Class.forName("com.mysql.jdbc.Driver").newInstance();
                 Connection Conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/cgcam_db?user=root&password=");
                 
                 Statement Stmt = Conn.createStatement();
                 
                 ResultSet RS = Stmt.executeQuery("show variables like '"+my_Variable+"%'");
                 
                 String ReturnValue = new String();
                 
                 while(RS.next()) {
                         ReturnValue= RS.getString(2);
                 }
                 
                 RS.close();
                 Stmt.close();
                 Conn.close();
                 
                 return ReturnValue;
              }
              catch (SQLException E) {
                   String E_error = "Grave erreur..." + E.getMessage();
                   return E_error;
              }
      }
  }

3-2-2- Création du fichier de description de déploiement GetMySQLInfos.xml

Le fichier de description de déploiement de la classe Java GetMySQLInfos permet de déployer ce service SOAP avec ServiceManagerClient

<isd:service xmlns:isd="http://xml.apache.org/xml-soap/deployment"
id="urn:GetMySQLInfos">
    <isd:provider type="java" scope="Request" methods="GetMySQLVariable">
           <isd:java class="com.capdata.soap.GetMySQLInfos" static="false"/>
    </isd:provider>
</isd:service>

Il est possible d'indiquer plusieurs méthodes dans la rubrique methods du fichier de déploiement, ces dernières doivent seulement être séparées par des espaces.

3-2-3- Déploiement logique du service SOAP RPC

Le service est implémenté logiquement avec la ligne de commande ci-dessous (ou via l'interface graphique d'administration de SOAP Apache selon les préférences) :

Dos :

C:\Projets> java org.apache.soap.server.ServiceManagerClient %URL% deploy C:\Projets\Java\com\capdata\soap\GetMySQLInfos.xml

Dans cette ligne de commande, %URL% est une variable d'environnement positionnée à la valeur suivante : http://localhost:9002/soap/servlet/rpcrouter
(NB : attention aux classpaths)

Pour vérifier la bonne implémentation du service SOAP, deux commandes :

C:\Projets> java org.apache.soap.server.ServiceManagerClient %URL% list

L'option list donne la liste des services SOAP implémentés : urn :GetMySQLInfos

C:\Projets> java org.apache.soap.server.ServiceManagerClient %URL% query urn:MySQLInfos

L'option query service-name indique les paramètres de description du service (notamment les paramètres spécifiés dans le document XML de description pour le déploiement logique).

3-2-4- Déploiement physique du service SOAP RPC

Pour le déploiement physique du service SOAP, il suffit d'implémenter le fichier compilé du service GetMySQLInfos (GetMySQLInfos.class) dans le répertoire TOMCAT_DIR/soap/WEB-INF/classes/com/capdata/
A l'issue de l'implémentation physique, redémarrer le serveur Tomcat.

3-3- Sollicitation du service SOAP RPC par un client Java

Une fois le service déployé sur le serveur, il faut créer le côté client Java ClientGetMySQLInfos de SOAP Apache. Avant de regarder le code pas à pas, sommairement les étapes à suivre lors de chaque appel SOAP-RPC sont les suivantes :

  1. Créer l'appel SOAP-RPC
  2. Définir l'URI du service SOAP à utiliser
  3. Spécifier la méthode à invoquer
  4. Ajouter les paramètres de l'appel
  5. Définir les mises en correspondances de types pour les paramètres personnalisés
  6. Se connecter au service SOAP
  7. Recevoir et traiter la réponse

3-3-1- Packages à importer

package com.capdata.soap;
import java.net.URL;
import java.util.Vector;
import java.util.Arrays;
import org.apache.soap.Constants;
import org.apache.soap.SOAPException;
import org.apache.soap.Envelope;
import org.apache.soap.Fault;
import org.apache.soap.rpc.Call;
import org.apache.soap.rpc.Response;
import org.apache.soap.rpc.Parameter;

On commence par la traditionnelle importation des classes requises. La classe Constants permet de définir des valeurs utiles comme le type d'encodage utilisé dans l'appel. L'exception SOAPException est envoyée par SOAP lorque quelque chose se déroule mal, et la classe Fault permet d'encapsuler une section SOAP:FAULT dans la réponse.

Enfin, les classes rpc.* permettent de construire l'appel au service.

3-3-2- Appel du service SOAP RPC urn :GetMySQLInfos

public class ClientGetMySQLInfos {
  public static void main(String args[]) {
    String nomMethode = "com.capdata.soap.ClientGetMySQLInfos.main";
    String url = "http://localhost:9002/soap/servlet/rpcrouter";
    String uri = "urn:GetMySQLInfos";   //mise en relation avec GetMySQLInfos.xml
    String methodeDistante = "GetMySQLVariable";
    String my_Variable = "version";

On définit ensuite un certain nombre de variables pour rendre le code plus lisible par la suite. L'uri que l'on définit ici doit correspondre à l'identifiant unique de notre service, renseigné soit manuellement à travers le descripteur de déploiement GetMySQLInfos.xml, soit interactivement via l'interface d'administration SOAP.

if (args.length != 0) {
   System.err.println("ClientGetMySQLInfos: invoque un service SOAP.");
   System.err.println("Usage: ClientGetMySQLInfos <parameter>");
   System.exit(1);
}

System.out.println(nomMethode + ": debut du test...");

try {
   Call call = new Call();
   Parameter param = new Parameter("my_Variable", my_Variable.getClass(), my_Variable, Constants.NS_URI_SOAP_ENC);

Un Parameter représente un argument pour l'appel RPC. Les objets Parameter sont utilisés à la fois par le client et le serveur. Le premier argument de la méthode correspond au nom du paramètre, le second à son type, le troisième à la variable qui le référence, et enfin le quatrième à l'encodage utilisé, ici l'encodage SOAP standard.

  call.setTargetObjectURI(uri);
call.setMethodName(methodeDistante);
call.setParams(new Vector(Arrays.asList(new Parameter[] {
param
})));
Response reponse = call.invoke(new URL(url), "");

Une fois l'URI du service défini via la méthode setTargetObjectURI, on spécifie également encore une fois le nom de la méthode invoquée via setMethodName, et les paramètres précédemment définis sont transmis via setParams. Le service SOAP distant est sollicité, via invoke(). Le second argument de cette méthode correspond à la valeur du champ SOAPAction du Header. Comme celui-ci est ignoré par le serveur Apache, une valeur vide est transmise.

3-3-3- Traitement de la réponse

// Vérifie la réponse
if (reponse.generatedFault()) {
Fault faute = reponse.getFault();

System.out.println(nomMethode + ": appel a " + methodeDistante + " a retourne une faute!");
System.out.println(" code de la faute: " + faute.getFaultCode());
System.out.println(" cause de la faute: " + faute.getFaultString());

System.exit(1);

} else {

if (reponse.getReturnValue() != null) {
Object result = reponse.getReturnValue().getValue();

System.out.println("Appel a " + methodeDistante + " a retourne un objet "
+ result.getClass() + ": " + result);

}
}
}

catch (SOAPException exception) {
System.err.println(nomMethode + ": Erreur: capture d'exception " + exception);
}

System.out.println(nomMethode + ": Test correctement effectue!");
}
}


Si la réponse pose problème, une faute est générée, à la fois sous forme de code et d'explication via respectivement les méthodes getFaultCode() et getFaultString() (ces deux informations correspondent aux éléments <faultcode> et <code>.

Dans le cas contraire, on récupère le paramètre de la réponse via la méthode public Parameter getReturnValue(), dont on récupère ensuite la valeur via la méthode getValue().

On récupère également dans le message de la ligne de commande la classe du paramètre renvoyé via la méthode classique getClass(). On aurait pu tout aussi bien utiliser la méthode getType() associée à l'objet Parameter.

4- Cas pratique SOAP Messaging

4-1- Présentation d'un cas SOAP Messaging

Voici schématiquement un cas pratique de SOAP Messaging

Dans ce cas pratique, les données d'une commande SQL sont renvoyées au client sous format XML, il est possible d'envisager un autre cas pratique où les données renvoyées au format XML correspondent au parsing d'un fichier de log côté serveur.

Le fichier XML renvoyé au client a la structure suivante :

<parameters>
       <version>4.1.0-alpha-max-nt</version>
       <parameter>value</parameter>
</parameters>

Annexe

Historique

Version Date Commentaires
1.0 04/2003 Version initiale

Liens

Normes W3C du protocol SOAP