Développer un client SOAP avec le projet groovy-wslite et Membrane SOA Model

groovy-wslite est un projet intéressant faisant partie de l'écosystème Groovy, et permettant d'écrire facilement des clients qui vont pouvoir consommer des services web de type SOAP et REST.

Pour un client SOAP, groovy-wslite permet de contrôler explicitement la forme du message SOAP à envoyer ; cela diffère de la partie cliente du module GroovyWS (projet plus ancien et dormant) qui doit au préalable analyser le document WSDL du service, afin de générer les classes Java de liaison adéquates à la volée, et de permettre l'invocation des opérations du service web. Avec un client groovy-wslite, un message SOAP peut être passé sous la forme d'une chaîne de caractères, ou bien construit comme on le ferait pour un document XML avec le builder MarkupBuilder.

Prenons comme exemple le service web Stock Quote décrit sur le site web WebserviceX.NET ; le script Groovy ci-dessous, qui utilise GRAPE, permet d'invoquer son opération GetStock pour obtenir une valeur boursière :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
 @Grab('com.github.groovy-wslite:groovy-wslite:0.8.0')
import wslite.soap.*

def client = new SOAPClient('http://www.webservicex.net/stockquote.asmx')

// Decomment if you are behind a proxy
//def proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress('proxy.company.net', 8080))

def symbolValue = 'ORA'
def response = client.send(/*proxy: proxy,*/ """\
<soap:Envelope xmlns:soap='http://www.w3.org/2003/05/soap-envelope'>
  <soap:Body>
    <GetQuote xmlns='http://www.webserviceX.NET/'>
      <symbol>${symbolValue}</symbol>
    </GetQuote>
  </soap:Body>
</soap:Envelope>""")

println response.GetQuoteResponse.GetQuoteResult.text()

Dans ce script le message SOAP est donnée explicitement par une chaîne de caractères, et plus précisément, par une GString multiligne qui va permettre l'interpolation de la variable symbolValue.
De manière équivalente, dans le nouveau script ci-dessous, on utilise cette fois le builder de message de groovy-wslite :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Grab('com.github.groovy-wslite:groovy-wslite:0.8.0')
import wslite.soap.*

def client = new SOAPClient('http://www.webservicex.net/stockquote.asmx')

//def proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress('proxy.company.net', 8080))

def symbolValue = 'ORA'
response = client.send/*(proxy: proxy)*/ {
    body {
        GetQuote(xmlns: 'http://www.webserviceX.NET/') {
            symbol(symbolValue)
        }
    }
}

println response.GetQuoteResponse.GetQuoteResult.text()

Comme pour tout autre builder Groovy, on arrive à un code plus concis et clair !

Cependant, pour former un message SOAP correctement, il faut soit disposer d'une documentation (c'est le cas pour le service Stock Quote), soit le déduire de l'analyse du document WSDL du service. Dans ce dernier cas, on peut bien sûr recourir à un outil tel que SoapUI qui, à partir de l'importation du document WSDL, est en mesure de générer des modèles de requêtes pour chaque opération de service web. Cela permet de créer des tests et des suites de test dans l'interface utilisateur de SoapUI.
Une autre alternative est d'utiliser la librairie Membrane SOA Model, qui a l'avantage de pouvoir être appelée depuis du code Groovy, comme dans cet exemple de script :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@GrabResolver(name='predic8-releases',
    root='http://repository.membrane-soa.org/content/repositories/releases')
@Grab(group='com.predic8', module='soa-model-core', version='1.4.1', transitive=false)
@Grab(group='org.apache.httpcomponents', module='httpclient', version='4.2.2')
import com.predic8.wsdl.*
import com.predic8.wstool.creator.*
 
import groovy.xml.MarkupBuilder


// Use an ExternalResolver instance if you are behind a proxy
//def er = new com.predic8.xml.util.ExternalResolver(proxyHost: 'proxy.company.net', proxyPort: 8080)

def parser = new WSDLParser(/*resourceResolver: er*/)
     
def wsdl = parser.parse("http://www.webservicex.net/stockquote.asmx?WSDL")
def writer = new StringWriter()
    
def creator = new SOARequestCreator(wsdl, new RequestTemplateCreator(), new MarkupBuilder(writer))

// Arguments are port type name, operation name, binding name
creator.createRequest("StockQuoteHttpPost", "GetQuote", "StockQuoteSoap12")
 
println writer

Dans ce script relativement simple, le document WSDL du service Stock Quote est analysé et le résultat de cette analyse placé dans la variable wsdl ; puis à l'aide d'uns instance de SOARequestCreator, on génère un modèle de requête pour l'opération GetQuote dans un objet StringWriter, dont le contenu est affiché.
Il a fallu également préciser, le nom du port souhaité, ainsi que le nom du binding, informations présentes dans le document WSDL.

L'affichage produit par le script, que vous allez pouvoir exploiter avec groovy-wslite, est reproduit ci-dessous :

1
2
3
4
5
6
7
8
<s12:Envelope xmlns:s12='http://www.w3.org/2003/05/soap-envelope'>
  <s12:Body>
    <ns1:GetQuote xmlns:ns1='http://www.webserviceX.NET/'>
<!-- optional -->
      <ns1:symbol>?XXX?</ns1:symbol>
    </ns1:GetQuote>
  </s12:Body>
</s12:Envelope>

Grâce à l'API Java de Membrane SOA Model, on peut également générer tous les messages de requête SOAP correspondant aux opérations de service web décrit dans un document WSDL. A titre d'exemple, voici comment procéder avec un script Groovy :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
@GrabResolver(name='predic8-releases',
    root='http://repository.membrane-soa.org/content/repositories/releases')
@Grab(group='com.predic8', module='soa-model-core', version='1.4.1', transitive=false)
@Grab(group='org.apache.httpcomponents', module='httpclient', version='4.2.2') 
import com.predic8.wsdl.*
import com.predic8.wstool.creator.*
import groovy.xml.MarkupBuilder


// Use an ExternalResolver instance if you are behind a proxy
//def er = new com.predic8.xml.util.ExternalResolver(proxyHost: 'proxy.company.net', proxyPort: 8080)

def parser = new WSDLParser(/*resourceResolver: er*/)

def wsdl = parser.parse("http://www.webservicex.net/stockquote.asmx?WSDL")


def writer = new StringWriter()
     
//SOARequestCreator constructor: SOARequestCreator(Definitions, Creator, MarkupBuilder)
def creator = new SOARequestCreator(wsdl, new RequestTemplateCreator(), new MarkupBuilder(writer))

 
wsdl.services.each { service ->
    service.ports.findAll{ it.binding.protocol in ['SOAP11', 'SOAP12'] }.each { port ->     
        def binding = port.binding
        println "\n<!-- Port name: ${port.name}, protocol binding: ${binding.protocol} -->"
        def portType = binding.portType
        portType.operations.each { op ->
            creator.createRequest(port.name, op.name, binding.name)

            System.out.println(writer)

            writer.buffer.length = 0
        }
    }
}

Vous pouvez récupérer ce script en tant que fichier attaché à cet article.

SoapUI permet de créer des suites de test pour interagir avec un service web, et de créer ainsi des scénarios dans lesquels le résultat d'un appel d'opération de service web peut être repris pour alimenter les paramètres d'un autre appel ; il même possible de réaliser cela avec des scripts Groovy. Cela dit, le script que l'on écrit, et qui est sauvegardé au sein du projet SoapUI, n'est pas forcément clair.

Produire un script de test Groovy comprenant tout un scénario à l'aide de groovy-wslite et de Membrane SOA Model constitue donc une alternative très intéressante, si l'on préfère une approche scriptée !


Fichier(s) :