Search code examples
wso2esbwso2-esb

Split XML and transform it in one or more SOAP Envelope


TL;DR; Sequence receives an XML message and I need to build many SOAP Envelope according to the number of <product> tags I received.

I have a sequence that receives this following XML (Message 1):

<?xml version="1.0" encoding="UTF-8"?>
<pricing>
    <product>
        <idFF>1</idFF>        
        <skuPartner>abc</skuPartner>        
        <original>123</original>        
        <new>123</new>      
    </product>
    <product>
        <idFF>2</idFF>        
        <skuPartner>aba</skuPartner>        
        <original>123</original>        
        <new>123</new>      
    </product>
    <product>
        <idFF>3</idFF>        
        <skuPartner>ae</skuPartner>        
        <original>123</original>        
        <new>123</new>      
    </product>
</pricing>

I would like to iterate in this message and for each <product> node make a call to a SOAP Service using the following contract:

<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope" xmlns:tem="http://tempuri.org/">
   <soap:Header/>
   <soap:Body>
      <tem:ProductPriceUpdate>
         <!--Optional:-->
         <tem:safeKey>?</tem:safeKey>
         <tem:storeId>?</tem:storeId>
         <tem:articleId>?</tem:articleId>
         <!--Optional:-->
         <tem:barcode>?</tem:barcode>
         <!--Optional:-->
         <tem:sku>?</tem:sku>
         <tem:price>?</tem:price>
         <tem:discount>?</tem:discount>
      </tem:ProductPriceUpdate>
   </soap:Body>
</soap:Envelope>

In this message above, I need to replace

<tem:articleId>?</tem:articleId>
<tem:sku>?</tem:sku>
<tem:price>?</tem:price>

with the values contained in the nodes from the XML message received

<idFF>1</idFF>        
<skuPartner>abc</skuPartner>                
<new>123</new>

So, facing the problem described I did (at least tried to):

  • Iterate over the message (Message 1) received
  • Take the values from the nodes <idFF> <skuPartner> <new>
  • Build a SOAP Envelope using PayloadFactory
  • Call SOAP service

I am pretty sure that I am little bit far from the solution. This is what I've tried.

<iterate expression="//produto" id="iterateXML" sequential="true xmlns:ns="http://org.apache.synapse/xsd">
        <target>
            <sequence>
                <payloadFactory media-type="xml">
                    <format>
                        <ProductPriceUpdate>
                            <safeKey>nExd8CzMRDo=</safeKey>
                            <storeId>123</storeId>
                            <articleId>$1</articleId>
                            <barcode>1</barcode>
                            <sku>$2</sku>
                            <price>$3</price>
                            <discount>1</discount>
                        </ProductPriceUpdate>
                    </format>
                    <args>
                        <arg evaluator="xml" expression="//idFF"/>
                        <arg evaluator="xml" expression="//skuPartner"/>
                        <arg evaluator="xml" expression="//new"/>
                    </args>
                </payloadFactory>
                <callout action="ProductPriceUpdate"
                    initAxis2ClientOptions="false" serviceURL="http://url-url.url.info/pub/url.asmx">
                    <source xpath="*"/>
                    <target key="Result"/>
                </callout>
            </sequence>
        </target>
    </iterate>

This is the output from the console:

[2016-02-11 11:04:52,982] ERROR - SequenceMediator Expecting an implementation of SOAP Envelope as the parent. But received some other implementation
org.apache.axiom.soap.SOAPProcessingException: Expecting an implementation of SOAP Envelope as the parent. But received some other implementation
        at org.apache.axiom.soap.impl.llom.SOAPHeaderImpl.checkParent(SOAPHeaderImpl.java:408)
        at org.apache.axiom.soap.impl.llom.SOAPElement.setParent(SOAPElement.java:81)
        at org.apache.axiom.om.impl.llom.OMElementImpl.addChild(OMElementImpl.java:296)
        at org.apache.axiom.om.impl.llom.OMElementImpl.addChild(OMElementImpl.java:212)
        at org.apache.axiom.soap.impl.llom.SOAPBodyImpl.addChild(SOAPBodyImpl.java:231)
        at org.apache.synapse.mediators.builtin.CalloutMediator.mediate(CalloutMediator.java:161)
        at org.apache.synapse.mediators.AbstractListMediator.mediate(AbstractListMediator.java:81)
        at org.apache.synapse.mediators.AbstractListMediator.mediate(AbstractListMediator.java:48)
        at org.apache.synapse.mediators.base.SequenceMediator.mediate(SequenceMediator.java:149)
        at org.apache.synapse.mediators.eip.Target.mediate(Target.java:106)
        at org.apache.synapse.mediators.eip.splitter.IterateMediator.mediate(IterateMediator.java:163)
        at org.apache.synapse.mediators.AbstractListMediator.mediate(AbstractListMediator.java:81)
        at org.apache.synapse.mediators.AbstractListMediator.mediate(AbstractListMediator.java:48)
        at org.apache.synapse.mediators.base.SequenceMediator.mediate(SequenceMediator.java:149)
        at org.apache.synapse.mediators.MediatorWorker.run(MediatorWorker.java:69)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
        at java.lang.Thread.run(Thread.java:745)
[2016-02-11 11:04:53,031]  INFO - LogMediator To: , WSAction: urn:mediate, SOAPAction: urn:mediate, MessageID: urn:uuid:481ca0ce-8502-486a-b501-350bda23f263, Di
rection: request, MESSAGE = Executing default 'fault' sequence, ERROR_CODE = 0, ERROR_MESSAGE = Expecting an implementation of SOAP Envelope as the parent. But
received some other implementation, Envelope: <?xml version='1.0' encoding='utf-8'?><soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Body><pricing>
        <product>
        <idFF>1</idFF>
        <skuPartner>abc</skuPartner>
        <original>123</original>
        <new>123</new>
    </product>
    <product>
        <idFF>2</idFF>
        <skuPartner>aba</skuPartner>
        <original>123</original>
        <new>123</new>
    </product>
    <product>
        <idFF>3</idFF>
        <skuPartner>ae</skuPartner>
        <original>123</original>
        <new>123</new>
    </product>
</pricing></soapenv:Body></soapenv:Envelope>

What I am missing? Should I use another mediator like Enrich? I appreciate every tip, thank you. =)

EDIT:

As suggested by @Jean-Michel, I replaced the xpath expression in callout mediator and the error of SOAP implementation, gone. Although I am still not able to do the request. When I go to the Monitor -> SOAP trace of my WSO2 I see the following request:

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
   <soapenv:Header/>
   <soapenv:Body>
      <text xmlns="http://ws.apache.org/commons/ns/payload">IDFF;SKUPARTNER;ORIGINAL;NEW&#xd;11035073;BOBSKU11035073;185.99;200&#xd;</text>
   </soapenv:Body>
</soapenv:Envelope>

EDIT 2:

After some modification, I just realized that the message I am sending in the callout mediator is the same message inside a SOAP envelope that I receive in the begining of the sequence. As it follows (please do not consider the xpath error, it was intentional):

[2016-02-11 12:44:43,197]  INFO - LogMediator To: , WSAction: urn:mediate, SOAPAction: urn:mediate, MessageID: urn:uuid:c3bda9c7-157e-46db-ae9f-f5b687d11848, Di
rection: request, MESSAGE = Executing default 'fault' sequence, ERROR_CODE = 0, ERROR_MESSAGE = The evaluation of the XPath expression : body did not result in
an OMElement, Envelope: <?xml version='1.0' encoding='utf-8'?><soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"><soapenv:Body><pricing>
        <product>
        <idFF>1</idFF>        
        <skuPartner>abc</skuPartner>        
        <original>123</original>        
        <new>123</new>      
    </product>
    <product>
        <idFF>2</idFF>        
        <skuPartner>aba</skuPartner>        
        <original>123</original>        
        <new>123</new>      
    </product>
    <product>
        <idFF>3</idFF>        
        <skuPartner>ae</skuPartner>        
        <original>123</original>        
        <new>123</new>      
    </product>
</pricing></soapenv:Body></soapenv:Envelope>

EDIT 3:

I changed the address of the callout to the http://localhost:1205 and this is the result using TCPMon. Seems that everything is alright.

TCPMon Result


Solution

  • I was able to solve the issue. Thank you @Jean-Michel for helping me through.

    I did the following:

    • Created a PassThrough Proxy
    • Changed the Callout Mediator to a Send Mediator invoking the PassThroug Proxy
    • Added a Header Mediator and defined the SOAP Action - MOST IMPORTANT!

    Here you can find my WSO2-Config.XML:

    <?xml version="1.0" encoding="UTF-8"?>
    <definitions xmlns="http://ws.apache.org/ns/synapse">
        <registry provider="org.wso2.carbon.mediation.registry.WSO2Registry">
            <parameter name="cachableDuration">15000</parameter>
        </registry>
        <taskManager provider="org.wso2.carbon.mediation.ntask.NTaskTaskManager"/>
        <proxy name="smook_proxy" startOnLoad="true" trace="disable" transports="https http vfs">
            <description/>
            <target>
                <inSequence>
                    <property name="FORCE_SC_ACCEPTED" scope="axis2"
                        type="STRING" value="true"/>
                    <property name="OUT_ONLY" scope="default" type="STRING" value="true"/>
                    <clone>
                        <target sequence="pricing"/>
                    </clone>
                </inSequence>
            </target>
            <parameter name="transport.PollInterval">5</parameter>
            <parameter name="transport.vfs.ActionAfterProcess">MOVE</parameter>
            <parameter name="transport.vfs.MoveAfterProcess">file:///C:\Users\victor.viola\Desktop\out</parameter>
            <parameter name="transport.vfs.FileURI">file:///C:\Users\victor.viola\Desktop\in</parameter>
            <parameter name="transport.vfs.MoveAfterFailure">file:///C:\Users\victor.viola\Desktop\error</parameter>
            <parameter name="transport.vfs.FileNamePattern">.*.csv</parameter>
            <parameter name="transport.vfs.ContentType">text/plain</parameter>
            <parameter name="transport.vfs.ActionAfterFailure">MOVE</parameter>
        </proxy>
    
        <proxy name="PS_PRICING" startOnLoad="true" trace="disable" transports="https http">
            <description/>
            <target>
                <endpoint name="wsFF">
                    <address trace="disable" uri="ADRESSTOTHEBACKEND.asmx"/>
                </endpoint>
                <inSequence/>
                <outSequence>
                    <send/>
                </outSequence>
                <faultSequence/>
            </target>
        </proxy>
    
        <localEntry key="smooks-csv.xml" src="file:C:/wso2esb-4.9.0/repository/resources/smooks/smooks-csv.xml">
            <description/>
        </localEntry>
        <endpoint name="PS_PRICING">
            <address uri="http://localhost:8280/services/PS_PRICING"/>
        </endpoint>
    
        <sequence name="pricing" statistics="enable" trace="enable">
            <smooks config-key="smooks-csv.xml">
                <input type="text"/>
                <output type="xml"/>
            </smooks>
            <iterate expression="//product" id="iterateXML"
                sequential="true" xmlns:ns="http://org.apache.synapse/xsd">
                <target>
                    <sequence>
                        <payloadFactory media-type="xml">
                            <format>
                                <ProductPriceUpdate>
                                    <safeKey>******</safeKey>
                                    <storeId>*****</storeId>
                                    <articleId>$1</articleId>
                                    <barcode/>
                                    <sku>$2</sku>
                                    <price>$3</price>
                                    <discount>100</discount>
                                </ProductPriceUpdate>
                            </format>
                            <args>
                                <arg evaluator="xml" expression="//idFF"/>
                                <arg evaluator="xml" expression="//skuPartner"/>
                                <arg evaluator="xml" expression="//new"/>
                            </args>
                        </payloadFactory>
                        <property name="messageType" scope="axis2" value="text/xml"/>
                        <in>
                            <header name="Action" scope="default" value="http://tempuri.org/ProductPriceUpdate"/>
                            <send>
                                <endpoint key="PS_PRICING"/>
                            </send>
                            <drop/>
                        </in>
                        <out>
                            <send/>
                        </out>
                    </sequence>
                </target>
            </iterate>
        </sequence>
    </definitions>
    

    Here is my smooks.xml

    <?xml version="1.0" encoding="UTF-8"?><smooks-resource-list xmlns="http://www.milyn.org/xsd/smooks-1.1.xsd" xmlns:csv="http://www.milyn.org/xsd/smooks/csv-1.2.xsd" xmlns:ftl="http://www.milyn.org/xsd/smooks/freemarker-1.1.xsd" xmlns:file="http://www.milyn.org/xsd/smooks/file-routing-1.1.xsd">
      <params>
        <param name="stream.filter.type">SAX</param>
        <param name="default.serialization.on">true</param>
      </params>
      <csv:reader fields="idFF,skuPartner,original,new" separator=";" skipLines="1"/>
      <ftl:freemarker applyOnElement="#document">
        <ftl:template><![CDATA[<?xml version="1.0" encoding="UTF-8"?>
    <pricing>
        <#list .vars["csv-set"]["csv-record"] as csv_record>
        <product>
            <idFF>${.vars["csv_record"]["idFF"]}</idFF>        
            <skuPartner>${.vars["csv_record"]["skuPartner"]}</skuPartner>        
            <original>${.vars["csv_record"]["original"]}</original>        
            <new>${.vars["csv_record"]["new"]}</new>      
        </product>
        </#list>    
    </pricing>]]></ftl:template>
      </ftl:freemarker>
      <resource-config selector="#document">
        <resource>org.milyn.delivery.DomModelCreator</resource>
      </resource-config>
    </smooks-resource-list>
    

    Another thing that was VERY important to solve the issue was enabling these two logs in log4j.properties (wso2home/repository/conf)

    log4j.logger.org.apache.synapse.transport.http.headers=DEBUG
    log4j.logger.org.apache.synapse.transport.http.wire=DEBUG