Search code examples
javaweb-servicessoapwsdl

How to accept mustUnderstand and other attributes on SOAP header parts?


I have a WSDL for a soap-based web service that contains a custom header:

<message name="Request">
    <part element="common:Request" name="Request"></part>
    <part element="common:myHeader" name="Header"></part>
</message>

<operation name="processRequest">
    <soap:operation soapAction=""/>
    <input>
        <soap:body parts="Request" use="literal"/>
        <soap:header message="tns:Request" part="Header" use="literal"></soap:header>
    </input>
    <output>
        <soap:body use="literal"/>
    </output>
</operation>

The header type is defined in a separate schema file:

<xs:element name="myHeader" nillable="false" type="tns:myHeaderType"/>

<xs:complexType name="myHeaderType">
    <xs:sequence minOccurs="1" maxOccurs="1">
        <xs:element name="JobID" type="tns:JobIDType" minOccurs="1" maxOccurs="1"/>
        <xs:element name="TransactionID" type="xs:string" minOccurs="1" maxOccurs="1"/>
    </xs:sequence>
</xs:complexType>

Furthermore, I am validating against the definition of myHeaderType to make sure that the required elements are there, formatting constraints are met, etc. This all appears to be working correctly, in that well-formed requests are accepted and incorrectly formatted requests are rejected.

My problem arises with a consumer who uses Apache Axis (hidden behind a proprietary tool) to generate their web-service client. As described in another StackOverflow question here, Axis inserts some optional attributes on myHeader that are allowed by the SOAP standard (as nearly as I can tell):

<soapenv:Header>
    <com:myHeader soapenv:mustUnderstand="0" soapenv:actor="">
                  ^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^
        <JobID>myJobId</JobID>
        <TransactionID>myTransactionId</TransactionID>
    </com:myHeader>
</soapenv:Header>

Because of the tool that my consumer is using, it is not feasible to modify the Axis-generated stubs to omit these attributes, as has been suggested elsewhere. Furthermore, it seems these attributes should be allowed by my service, if I'm going to claim to be a soap service. My question is, how can I modify my header definition to accommodate these optional attributes, and ideally plan for attributes that I might not be anticipating. Is there a standard type that I can extend for my header type definition, or a general best practice for this situation?


Solution

  • I was able to find a couple of solutions to my problem today. They both solve the problem, but I'm not confident in my judgement of which is really the better of the two, so I'll present them both here.

    Both solutions center around modifying the definition of myHeaderType so that it can accommodate the unanticipated SOAP-defined attributes.

    1. Within the SOAP WSDL schema definition (http://schemas.xmlsoap.org/wsdl/), there is a type called tExtensibleAttributesDocumented that includes the following very flexible attribute definition:

      <xs:anyAttribute namespace="##other" processContents="lax"/>
      

      By extending this abstract type, I was able to incorporate this liberal allowance for unanticipated attributes into my type. Here is the resulting code:

        <xs:complexType name="myHeaderType">
            <xs:complexContent>
                <xs:extension base="wsdl:tExtensibleAttributesDocumented">
                    <xs:sequence minOccurs="1" maxOccurs="1">
                        <xs:element name="JobID" type="tns:JobIDType" minOccurs="1" maxOccurs="1"/>
                        <xs:element name="TransactionID" type="xs:string" minOccurs="1" maxOccurs="1"/>
                    </xs:sequence>
                </xs:extension>
            </xs:complexContent>
        </xs:complexType>
      

      Note that this does not check the contents of mustUnderstand or actor, and allows other attributes, including complete garbage, so long as it comes from a namespace that I have defined in my XML request.

    2. The other alternative was to directly include the <xs:anyAttribute> in my type:

        <xs:complexType name="myHeaderType">
            <xs:sequence minOccurs="1" maxOccurs="1">
                <xs:element name="JobID" type="tns:JobIDType" minOccurs="1" maxOccurs="1"/>
                <xs:element name="TransactionID" type="xs:string" minOccurs="1" maxOccurs="1"/>
            </xs:sequence>
            <xs:anyAttribute namespace="##other" processContents="lax"/>
        </xs:complexType>
      

      This has basically the same effect, as far as I was able to determine.

    If there are any subtle differences between these solutions that I'm not aware of, I'd love to hear about it. If there is an accepted standard for this situation, I was not able to find it. Another weakness of this solution is that I was not able to get the attributes to validate against their definitions in the schema. Changing the processContents attribute to strict prevented even the well-defined mustUnderstand and actor attributes from being processed.