SOAP, Simple Object Access Protocol, avec Java

Logo

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 :

  • ce protocole est écrit en XML
  • il supporte les services concernant les appels de procédure distante (Remote Procedure Call) aussi bien que les services d’échange de messages (services)
  • il est principalement déployé au-dessus du protocole de transport HTTP
  • il est indépendant des plate-formes (platform-independant )
  • il est indépendant des langages (language-independent )

Le fait que SOAP soit en XML (et donc qu’il soit un protocole basé sur du 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), ces trois protocoles reposant sur des flux binaires, et donc plus difficiles à gérer.

Présentation générale de SOAP

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 :

  • récupération des variables globales d’une base MySQL sur le serveur distant (show variables like 'variable%' et show processlist) par SOAP RPC (Remote Procedure Call)

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

Soit l’interface JAVA :

public interface GetMySQLInfosinXML
{
   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 :

<GetMySQLInfosinXML>
  <GetMySQLVariable>
     <my_Variable>version</my_Variable>
  </GetMySQLVariable>
</GetMySQLInfosinXML>

Le nom de l’interface est le nœud racine du message, la méthode et le paramètre sont également transformés en nœuds.

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

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

Le nœud racine est toujours l’interface GetMySQLInfosinXML, mais cette fois le nœud fils consiste en la concaténation du nom et de la méthode et de la chaîne Response.

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="GetMySQLInfosinXML"
      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>
xml Request

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

<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>
      <ns1:GetMySQLVariableResponse xmlns:ns1="GetMySQLInfosinXML"
       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. GetMySQLInfosinXML) n’est pas le nom d’un nœud comme nous l’avions auparavant approximé, mais que celui-ci est référencé dans un espace nommé, ns1.


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

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.

L’enveloppe d’un message SOAP

L’anatomie d’un message SOAP est plus simple que celle d’un service SOAP RPC. Elle consiste eseentiellement en:

  • l’encapsulation (wrapping) du message XML
  • l’inclusion du body SOAP dans l’enveloppe SOAP
  • 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>
xml soap message squelette

sa mise en conformité avec la spécification SOAP Messaging 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>

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 (notamment 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é.

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 se substitue à quoi que ce soit dans 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 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 une requête SOAP HTTP classique et une requête SOAP HTTP étendue est la mention: M-POST plutôt que POST. M-POST définit une requête HTTP obligatoire (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 d’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, préfixe utilisé pour l’URI spécifié dans l’élément d’en-tête Man.

Configuration et utilisation de SOAP Apache

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 et activation.jar. La librairie mail.jar est requise pour la prise en charge du protocole SMTP. Pour télécharger ces 2 librairies :

Un parseur compatible JAXP, sensible aux espaces de nom, est nécessaire : Apache Xerces v1.1.2 ou versions supérieures 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.

SOAP Apache est disponible en téléchargement sur le site de la fondation Apache à l’adresse : http://xml.apache.org/dist/soap/version-2.2. SOAP 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 méthodes sont possibles:

  • La plus facile: la distribution Apache de SOAP inclut une archive Web à l’adresse /soap-2_2/webapps/soap.war. Déplacer cette archive dans le répertoire webapps de Tomcat et 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 Tomcat.
  • Seconde méthode : créer un nouveau <Context> dans le fichier server.xml installé dans le répertoire %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 la variable CLASSPATH.

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

DOS Windows :

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

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 (compte : admin) : http://<SERVER>:<PORT>/soap/admin/index.html

Déploiement des services SOAP

Déploiement logique d’un service SOAP

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

  • via l’interface graphique SOAP Apache.
  • via la classe Java org.apache.soap.server.ServiceManagerClient en ligne de commande.

L’utilisation de la 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.

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 d’é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 au service SOAP à implémenter physiquement.

Le serveur Tomcat doit être redémarré pour la prise en charge du nouveau service SOAP.

Cas pratique SOAP RPC

Présentation du cas SOAP RPC

Voici schématiquement le cas pratique SOAP RPC :

Implémentation du service SOAP RPC côté serveur

Développement du service SOAP RPC

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

/*
* GetMySQLInfosinXML.java
*
* Classe de test SOAP RPC
*
*/
package com.sqlpac.soap;
import java.sql.*;

public class GetMySQLInfosinXML {
  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;
     }
    }
  }

Création du fichier de description de déploiement GetMySQLInfosinXML.xml

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

<isd:service xmlns:isd="http://xml.apache.org/xml-soap/deployment"
id="urn:GetMySQLInfosinXML">
    <isd:provider type="java" scope="Request" methods="GetMySQLVariable">
           <isd:java class="com.sqlpac.soap.GetMySQLInfosinXML" 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.

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:\> java org.apache.soap.server.ServiceManagerClient %URL% deploy C:\Projets\Java\com\sqlpac\soap\GetMySQLInfosinXML.xml

Dans cette ligne de commande, %URL% est une variable d’environnement positionnée à la valeur suivante : http://localhost:9002/soap/servlet/rpcrouter

Si cela ne fonctionne pas, vérifier la variable CLASSPATH utilisée pour retrouver les librairies java nécessaires.

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

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

L’option list affiche les services SOAP implémentés :

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

L’option query service-name donne 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).

Déploiement physique du service SOAP RPC

Pour le déploiement physique du service SOAP, installer le fichier compilé du service GetMySQLInfosinXML (GetMySQLInfosinXML.class) dans le répertoire TOMCAT_DIR/soap/WEB-INF/classes/com/sqlpac/. À l’issue de l’implémentation physique, redémarrer le serveur Tomcat.

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 ClientGetMySQLInfosinXML de SOAP Apache. Avant de regarder le code pas à pas, sommairement les étapes à suivre lors de chaque appel SOAP-RPC sont les suivantes :

  • Créer l’appel SOAP-RPC
  • Définir l’URI du service SOAP à utiliser
  • Spécifier la méthode à invoquer
  • Ajouter les paramètres de l’appel
  • Définir les mises en correspondances de types pour les paramètres personnalisés
  • Se connecter au service SOAP
  • Recevoir et traiter la réponse

Code Java du client ClientGetMySQLInfosinXML


package com.sqlpac.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;

public class ClientGetMySQLInfosinXML {

  public static void main(String args[]) {
  
    String nomMethode = "com.sqlpac.soap.ClientGetMySQLInfosinXML.main";
    String url = "http://localhost:9002/soap/servlet/rpcrouter";
    String uri = "urn:GetMySQLInfosinXML";   //mise en relation avec GetMySQLInfosinXML.xml
    String methodeDistante = "GetMySQLVariable";
    String my_Variable = "version";
    
    if (args.length != 0) {
       System.err.println("ClientGetMySQLInfosinXML: invoque un service SOAP.");
       System.err.println("Usage: ClientGetMySQLInfosinXML <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);
  
      call.setTargetObjectURI(uri);
      call.setMethodName(methodeDistante);
      call.setParams(new Vector(Arrays.asList(new Parameter[] {
           param
       })));
       
      Response reponse = call.invoke(new URL(url), "");   
          
      // 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("Méthode appelée :" + methodeDistante);
            System.out.println("Type objet retourné :" + result.getClass() );
            System.out.println("Valeur retournée :" + result );
        }
      }  
    }
    
    
    catch (SOAPException exception) {
        System.err.println(nomMethode + ": Erreur: capture d'exception "  + exception);
    }
     
    System.out.println(nomMethode + ": Test correctement effectue!");
  }
}

Packages à importer

package com.sqlpac.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 définit 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 encapsule une section SOAP:FAULT dans la réponse.

Enfin, les classes rpc.* construisent l’appel au service.

Appel du service SOAP RPC urn :GetMySQLInfosinXML

public class ClientGetMySQLInfosinXML {

  public static void main(String args[]) {
  
    String nomMethode = "com.sqlpac.soap.ClientGetMySQLInfosinXML.main";
    String url = "http://localhost:9002/soap/servlet/rpcrouter";
    String uri = "urn:GetMySQLInfosinXML";   //mise en relation avec GetMySQLInfosinXML.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 GetMySQLInfosinXML.xml, soit interactivement via l’interface d’administration SOAP.

    if (args.length != 0) {
       System.err.println("ClientGetMySQLInfosinXML: invoque un service SOAP.");
       System.err.println("Usage: ClientGetMySQLInfosinXML <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);

Le nombre d’arguments donné au programme client est d’abord testé

Un objet call pour l’appel RPC est créé puis un objet Parameter pour l’argument my_variable qui sera donné dans l’appel RPC. Les objets Parameter sont utilisés à la fois par le client et le serveur. Le premier argument donné à l’objet Parameter correspond au nom de la variable, 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 le nom de la méthode invoquée avec setMethodName et les paramètres sont transmis avec setParams. Le service SOAP distant est enfin sollicité avec 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.

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("Méthode appelée :" + methodeDistante);
            System.out.println("Type objet retourné :" + result.getClass() );
            System.out.println("Valeur retournée :" + result );
        }
      }  

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.

Cas pratique SOAP Messaging

Présentation d’un cas SOAP Messaging

Voici schématiquement un cas pratique de SOAP Messaging

schema SOAP exemple GetMySQLInfosinXML

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>