Web Service Consumer Connector with Mule ESB CE

Most examples found in the official documentation and various blogs shows how to use the Web Service Consumer in Mule ESB Enterprise Edition. So they are using DataWeave or, for Mule ESB 3.6 or earlier, DataMapper to prepare the XML payload to be send. This is sometimes misinterpreted by users so that they think that the Web Service Consumer by it self is a Enterprise Edition only feature, when in fact it is available, and very useful, even in Mule ESB Community edition.
In this blog I will explain two ways to prepare the payload to be used by the Web Service Consumer when you can not or do not want to use DataMapper or DataWeave. I expect that the reader is familiare with the basics of creating a Mule ESB application and using the HTTP Listner connector aswell as the Web Service Consumer.

The use case

In this blog we will create a simple flow that exposes a HTTP endpoint, receives a GET with a query parameter (name) and uses that to call an SOAP Webservice. Normally the SOAP webservice will of course be provided by another server or even a SaaS provider. However to allow the example to be easy to run and test the SOAP webservice will in this case be implemented in the same project.

In our case the SOAP request payload is a simple XML document that should look something like this:

<echo:echo xmlns:echo="http://echo.example.com/">
  <echo:arg0>Svante</echo:arg0>
</echo:echo>

So to be able to successfully call the backedn service the Web Service Consumer requires the payload of the Mule message to contain that XML payload. When using Enterprise Edition this can be done using DataWeave. However you can also use any transformer, or component, that prepare the Mule message by generating the request XML. Two examples of such transformers that are available out of the box in Mule ESB CE 3.7.0 are the 'Parse Template' transformer and the 'XSLT Transformer'.

Using 'Parse Template' transformer

If the input in your flow is a simple Java Object, such as a Map, and the request XML structure is fairly simple then the simplest way to create a XML payload in Mule ESB is probably the 'Parse Template' transformer.

The 'Parse Template' transformer parses a template file that can contain MEL expressions and places the resulting string into the message payload.

So we start by creating a new text file request.txt in the src/main/resources folder of the project.

<?xml version="1.0" encoding="UTF-8"?> 
<echo:echo xmlns:echo="http://echo.example.com/">
  <echo:arg0>#[message.inboundProperties.'http.query.params'.name]</echo:arg0>
</echo:echo>

The MEL expression #[message.inboundProperties.'http.query.params'.name] will expand to the query parameter name send in by the calling client. If you instead have your data in the message payload as a Map object you can use the expression #[message.payload.name] instead.

Then in your flow, just before the Web Service Consumer you then add the parse-template transformer element.

<flow name="useParseTemplateFlow">
    <http:listener config-ref="HTTP_Listener_Configuration" path="/parsetemplate" doc:name="HTTP"/>
    <parse-template location="request.txt" mimeType="text/xml" encoding="UTF-8"/>
    <ws:consumer config-ref="Web_Service_Consumer" doc:name="Web Service Consumer" operation="echo"/>
</flow>

Calling http://localhost:8081/parsetemplate?name=Svante will now create the request message shown in the beginning of this blog and the back end service will return the response message.

Using XSLT transformer

When the input in your flow is XML or the creation of the request XML structure requires more complex transformations, such as iterations and/or formatting numbers and dates, using XSLT is a better option than using the 'Parse Template' transformer.

In this case we will start by creating a new file request.xslt in the src/main/resources folder of the project with the following content.

<?xml version="1.0" encoding="UTF-8"?> 
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:echo="http://echo.example.com/">
  <xsl:output method="xml" encoding="UTF-8"  indent="yes" omit-xml-declaration="yes"/>
  <xsl:param name="name"/>

  <!-- Transform from outer to inner -->
  <xsl:template match="/">
        <echo:echo>
           <echo:arg0>
             <xsl:value-of select="$name"/>
           </echo:arg0>
        </echo:echo>
  </xsl:template> 

</xsl:stylesheet>

This XSLT will create the request message using a parameter that we will provide to the XSLT engine from the Mule flow.

Then in your flow, just before the Web Service Consumer you then add the xslt-transformer element. The xslt-transformer requires the payload to be a valid XML document. Since we, in this particular case, do not have a valid XML document in the payload we have to add a dummy XML document. This dummy document will not be used in the transformation.

<flow name="useXSLTFlow">
    <http:listener config-ref="HTTP_Listener_Configuration" path="/xslt" doc:name="HTTP"/>
    <set-payload value="&lt;root/&gt;" doc:name="Set Payload to a dummy XML document"/>
    <mulexml:xslt-transformer returnClass="java.lang.String" xsl-file="request.xslt" 
        doc:name="XSLT to create a valid request.">
        <mulexml:context-property key="name" value="#[message.inboundProperties.'http.query.params'.name]"/>
    </mulexml:xslt-transformer>
    <ws:consumer config-ref="Web_Service_Consumer" doc:name="Web Service Consumer" operation="echo"/>
</flow>

As you can see in the flow above the name context-property is populated using the same MEL expression as was used when we used the 'Parse Template' transformer.

So calling http://localhost:8081/xslt?name=Svante should now generate a identical request to the back end and the result should be the same.

The full code for this blog are available as a bitbucket snippet.