Wednesday, July 3, 2013

Using XSLT to transform a WSDL (so SAP will understand).

If you ever created a SOAP service in TIBCO BusinessWorks and used the WSDL that can be exported from the service agent to generate a service consumer in SAP, you will be familiar with this issue: SAP is very sensitive to the order of the elements in the WSDL - and as it happens, TIBCO BW puts the elements in the "wrong" order.

Specifically, SAP expects the order to be: types, message, portType, binding and service. Anything else will result in an error. The topic of this post is not who's wrong and who's right, but I'm pretty sure SAP is wrong in assuming that the order of the elements after types is relevant.

Obviously you can just edit the WSDL and change the order of the elements manually, but if you have to do this often... So, here is an XSLT that I routinely use to re-arrange the WSDL and keep SAP happy (with the element order that is; it may complain about a million other things):

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/">

    <xsl:output method="xml" encoding="UTF-8" indent="yes"/>
    <xsl:template match="/wsdl:definitions">
        <xsl:copy>
            <xsl:copy-of select="@*"/>
            <xsl:copy-of select="wsdl:types"/>
            <xsl:copy-of select="wsdl:message"/>
            <xsl:copy-of select="wsdl:portType"/>
            <xsl:copy-of select="wsdl:binding"/>
            <xsl:copy-of select="wsdl:service"/>
        </xsl:copy>
    </xsl:template>

</xsl:stylesheet>


I use free tools like XSLT Test and Firstobject XML to do my work, but you should be fine using your favorite tools (caveat emptor).  

Make sure the encoding of the output WSDL is actually UTF-8 without BOM, or you will upset SAP again!

So far, so good. Now let's try something more rewarding. On many occasions I had a requirement to secure the service with a WSS signing policy. That is, the service will accept only signed requests. But how do we get SAP to sign the request? As it turns out, you must provide the policy bindings in the WSDL, so SAP will add them during import (you will probably have to tweak some settings in SOA manager as well, but I'm no SAP expert).

TIBCO BusinessWorks does not add these policies when you export the WSDL, and adding them manually in the format that SAP expects is rather cumbersome and prone to error. So for this I use an XSLT as well, which I'll include below for your convenience.

Have fun!

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy"
xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"
xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">

    <xsl:output method="xml" encoding="UTF-8" indent="yes"/>
    <xsl:template match="/wsdl:definitions">
        <xsl:copy>
      
            <xsl:copy-of select="@*"/>                      
            <wsp:UsingPolicy wsdl:required="true"/>
          
            <!-- binding policies -->
            <xsl:for-each select="wsdl:binding">
                <wsp:Policy>
                    <xsl:attribute name="wsu:Id">
                        <xsl:value-of select="concat('BN_BN_',@name)"/>
                    </xsl:attribute>
                    <saptrnbnd:OptimizedXMLTransfer uri="http://xml.sap.com/2006/11/esi/esp/binxml" xmlns:saptrnbnd="http://www.sap.com/webas/710/soap/features/transportbinding/" wsp:Optional="true"/>
                    <saptrnbnd:OptimizedXMLTransfer uri="http://www.w3.org/2004/08/soap/features/http-optimization" xmlns:saptrnbnd="http://www.sap.com/webas/710/soap/features/transportbinding/" wsp:Optional="true"/>
                    <wsp:ExactlyOne xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy" xmlns:sapsp="http://www.sap.com/webas/630/soap/features/security/policy" xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702" xmlns:wsa="http://www.w3.org/2005/08/addressing" xmlns:wst="http://docs.oasis-open.org/ws-sx/ws-trust/200512" xmlns:wsu="http://schemas.xmlsoap.org/ws/2002/07/utility" xmlns:wsx="http://schemas.xmlsoap.org/ws/2004/09/mex">
                        <wsp:All>
                            <sp:AsymmetricBinding>
                                <wsp:Policy>
                                    <sp:InitiatorSignatureToken>
                                        <wsp:Policy>
                                            <sp:X509Token sp:IncludeToken="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702/IncludeToken/Never">
                                                <wsp:Policy>
                                                    <sp:WssX509V3Token10/>
                                                </wsp:Policy>
                                            </sp:X509Token>
                                        </wsp:Policy>
                                    </sp:InitiatorSignatureToken>
                                    <sp:AlgorithmSuite>
                                        <wsp:Policy>
                                            <sp:Basic128Rsa15/>
                                        </wsp:Policy>
                                    </sp:AlgorithmSuite>
                                    <sp:Layout>
                                        <wsp:Policy>
                                            <sp:Strict/>
                                        </wsp:Policy>
                                    </sp:Layout>
                                    <sp:IncludeTimestamp/>
                                    <sp:OnlySignEntireHeadersAndBody/>
                                </wsp:Policy>
                            </sp:AsymmetricBinding>
                            <sp:Wss10>
                                <wsp:Policy>
                                    <sp:MustSupportRefKeyIdentifier/>
                                </wsp:Policy>
                            </sp:Wss10>
                            <sp:SignedParts>
                                <sp:Body/>
                                <sp:Header Name="Trace" Namespace="http://www.sap.com/webas/630/soap/features/runtime/tracing/"/>
                                <sp:Header Name="messageId" Namespace="http://www.sap.com/webas/640/soap/features/messageId/"/>
                                <sp:Header Name="CallerInformation" Namespace="http://www.sap.com/webas/712/soap/features/runtime/metering/"/>
                                <sp:Header Name="Session" Namespace="http://www.sap.com/webas/630/soap/features/session/"/>
                                <sp:Header Name="To" Namespace="http://schemas.xmlsoap.org/ws/2004/08/addressing"/>
                                <sp:Header Name="ReplyTo" Namespace="http://schemas.xmlsoap.org/ws/2004/08/addressing"/>
                                <sp:Header Name="From" Namespace="http://schemas.xmlsoap.org/ws/2004/08/addressing"/>
                                <sp:Header Name="Action" Namespace="http://schemas.xmlsoap.org/ws/2004/08/addressing"/>
                                <sp:Header Name="FaultTo" Namespace="http://schemas.xmlsoap.org/ws/2004/08/addressing"/>
                                <sp:Header Name="MessageID" Namespace="http://schemas.xmlsoap.org/ws/2004/08/addressing"/>
                                <sp:Header Name="RelatesTo" Namespace="http://schemas.xmlsoap.org/ws/2004/08/addressing"/>
                                <sp:Header Name="To" Namespace="http://www.w3.org/2005/08/addressing"/>
                                <sp:Header Name="ReplyTo" Namespace="http://www.w3.org/2005/08/addressing"/>
                                <sp:Header Name="From" Namespace="http://www.w3.org/2005/08/addressing"/>
                                <sp:Header Name="Action" Namespace="http://www.w3.org/2005/08/addressing"/>
                                <sp:Header Name="FaultTo" Namespace="http://www.w3.org/2005/08/addressing"/>
                                <sp:Header Name="MessageID" Namespace="http://www.w3.org/2005/08/addressing"/>
                                <sp:Header Name="RelatesTo" Namespace="http://www.w3.org/2005/08/addressing"/>
                                <sp:Header Name="ReferenceParameters" Namespace="http://www.w3.org/2005/08/addressing"/>
                                <sp:Header Name="Sequence" Namespace="http://schemas.xmlsoap.org/ws/2005/02/rm"/>
                                <sp:Header Name="SequenceAcknowledgement" Namespace="http://schemas.xmlsoap.org/ws/2005/02/rm"/>
                                <sp:Header Name="AckRequested" Namespace="http://schemas.xmlsoap.org/ws/2005/02/rm"/>
                                <sp:Header Name="SequenceFault" Namespace="http://schemas.xmlsoap.org/ws/2005/02/rm"/>
                                <sp:Header Name="Sequence" Namespace="http://docs.oasis-open.org/ws-rx/wsrm/200702"/>
                                <sp:Header Name="AckRequested" Namespace="http://docs.oasis-open.org/ws-rx/wsrm/200702"/>
                                <sp:Header Name="SequenceAcknowledgement" Namespace="http://docs.oasis-open.org/ws-rx/wsrm/200702"/>
                                <sp:Header Name="SequenceFault" Namespace="http://docs.oasis-open.org/ws-rx/wsrm/200702"/>
                                <sp:Header Name="UsesSequenceSTR" Namespace="http://docs.oasis-open.org/ws-rx/wsrm/200702"/>
                                <sp:Header Name="UsesSequenceSSL" Namespace="http://docs.oasis-open.org/ws-rx/wsrm/200702"/>
                            </sp:SignedParts>
                        </wsp:All>
                    </wsp:ExactlyOne>
                </wsp:Policy>
            </xsl:for-each>          
          
            <!-- portType policies -->
            <xsl:for-each select="wsdl:portType">
                <wsp:Policy>
                    <xsl:attribute name="wsu:Id">
                        <xsl:value-of select="concat('IF_IF_',@name)"/>
                    </xsl:attribute>
                    <sapsession:Session xmlns:sapsession="http://www.sap.com/webas/630/soap/features/session/">
                        <sapsession:enableSession>false</sapsession:enableSession>
                    </sapsession:Session>
                </wsp:Policy>
            </xsl:for-each>
          
            <!-- operation policies -->
            <xsl:for-each select="wsdl:portType/wsdl:operation">
                <wsp:Policy>
                    <xsl:attribute name="wsu:Id">
                        <xsl:value-of select="concat('OP_IF_OP_',@name)"/>
                    </xsl:attribute>
                    <sapcomhnd:enableCommit xmlns:sapcomhnd="http://www.sap.com/NW05/soap/features/commit/">false</sapcomhnd:enableCommit>
                    <sapblock:enableBlocking xmlns:sapblock="http://www.sap.com/NW05/soap/features/blocking/">true</sapblock:enableBlocking>
                    <saptrhnw05:required xmlns:saptrhnw05="http://www.sap.com/NW05/soap/features/transaction/">no</saptrhnw05:required>
                    <saprmnw05:enableWSRM xmlns:saprmnw05="http://www.sap.com/NW05/soap/features/wsrm/">false</saprmnw05:enableWSRM>
                </wsp:Policy>
            </xsl:for-each>
                      
            <!-- and the rest -->          
            <xsl:copy-of select="wsdl:types"/>
            <xsl:copy-of select="wsdl:message"/>
            <xsl:apply-templates select="wsdl:portType"/>
            <xsl:apply-templates select="wsdl:binding"/>
            <xsl:copy-of select="wsdl:service"/>
          
        </xsl:copy>
    </xsl:template>

    <xsl:template match="wsdl:portType">
        <xsl:copy>
            <xsl:copy-of select="@*"/>
            <wsp:Policy>
                <wsp:PolicyReference>
                    <xsl:attribute name="URI">
                        <xsl:value-of select="concat('#IF_IF_',@name)"/>
                    </xsl:attribute>
                </wsp:PolicyReference>
            </wsp:Policy>
            <xsl:apply-templates select="wsdl:operation"/>
        </xsl:copy>
    </xsl:template>
   
    <xsl:template match="wsdl:operation">
        <xsl:copy>
            <xsl:copy-of select="@*"/>
            <wsp:Policy>
                <wsp:PolicyReference>
                    <xsl:attribute name="URI">
                        <xsl:value-of select="concat('#OP_IF_OP_',@name)"/>
                    </xsl:attribute>
                </wsp:PolicyReference>
            </wsp:Policy>
            <xsl:copy-of select="*"/>
        </xsl:copy>
    </xsl:template>
   
    <xsl:template match="wsdl:binding">
        <xsl:copy>
            <xsl:copy-of select="@*"/>
            <wsp:Policy>
                <wsp:PolicyReference>
                    <xsl:attribute name="URI">
                        <xsl:value-of select="concat('#BN_BN_',@name)"/>
                    </xsl:attribute>
                </wsp:PolicyReference>
            </wsp:Policy>
            <xsl:copy-of select="*"/>
        </xsl:copy>
    </xsl:template>

</xsl:stylesheet>

 



No comments:

Post a Comment