javaweb-servicesxml-namespacesspring-wsaxiom

Generation of empty namespace prefix using WebServiceTemplate from Spring-ws


We are using Spring-ws version 2.4.2.RELEASE for calling an external WebService from a WSDL.

The method we call is:

ValidateObjectResponse response = (ValidateObjectResponse) webServiceTemplate.marshalSendAndReceive(request);

The SOAP request that is generated contains an empty namespace prefix:

<SOAP-ENV:Envelope xmlns:SOAPENV="http://schemas.xmlsoap.org/soap/envelope/"> 
    <SOAP-ENV:Header/>
    <SOAP-ENV:Body>
        <ns2:validateObject xmlns:ns2="http://ws.validator.sch.gazelle.ihe.net/" xmlns=""><base64ObjectToValidate>Cgo8P3ht...
    </SOAP-ENV:Body>
</SOAP-ENV>

Behind the scenes, apache axiom is used (version 1.2.13) and in the release notes I read the following (https://ws.apache.org/axiom/release-notes/1.2.13.html):

"In Axiom 1.2.12, the declareNamespace methods in OMElement didn’t enforce this constraint and namespace declarations violating this requirement were silently dropped during serialization. This behavior is problematic because it may result in subtle issues such as unbound namespace prefixes. In Axiom 1.2.13 these methods have been changed so that they throw an exception if an attempt is made to bind the empty namespace name to a prefix."

When we call the external webservice, we get the following exception:

java.lang.IllegalArgumentException: Cannot bind a prefix to the empty namespace name
at org.apache.axiom.om.impl.dom.ElementImpl.declareNamespace(ElementImpl.java:754)
at org.apache.axiom.om.impl.dom.ElementImpl.declareNamespace(ElementImpl.java:778)
at org.apache.axiom.om.impl.dom.ElementImpl.setAttributeNS(ElementImpl.java:559)
at com.sun.xml.bind.marshaller.SAX2DOMEx.startElement(SAX2DOMEx.java:163)
at com.sun.xml.bind.v2.runtime.output.SAXOutput.endStartTag(SAXOutput.java:124)
at com.sun.xml.bind.v2.runtime.XMLSerializer.endAttributes(XMLSerializer.java:302)
at com.sun.xml.bind.v2.runtime.XMLSerializer.childAsSoleContent(XMLSerializer.java:588)
at com.sun.xml.bind.v2.runtime.ClassBeanInfoImpl.serializeRoot(ClassBeanInfoImpl.java:312)
at com.sun.xml.bind.v2.runtime.XMLSerializer.childAsRoot(XMLSerializer.java:490)
at com.sun.xml.bind.v2.runtime.MarshallerImpl.write(MarshallerImpl.java:328)
at com.sun.xml.bind.v2.runtime.MarshallerImpl.marshal(MarshallerImpl.java:257)
at org.springframework.oxm.jaxb.Jaxb2Marshaller.marshal(Jaxb2Marshaller.java:680)
at org.springframework.ws.support.MarshallingUtils.marshal(MarshallingUtils.java:81)
at org.springframework.ws.client.core.WebServiceTemplate$2.doWithMessage(WebServiceTemplate.java:399)
at org.springframework.ws.client.core.WebServiceTemplate.doSendAndReceive(WebServiceTemplate.java:590)
at org.springframework.ws.client.core.WebServiceTemplate.sendAndReceive(WebServiceTemplate.java:555)
at org.springframework.ws.client.core.WebServiceTemplate.marshalSendAndReceive(WebServiceTemplate.java:390)
at org.springframework.ws.client.core.WebServiceTemplate.marshalSendAndReceive(WebServiceTemplate.java:383)
at org.springframework.ws.client.core.WebServiceTemplate.marshalSendAndReceive(WebServiceTemplate.java:373)

My first idea was to lower the version of axiom to 2.1.12, but this gave conflicts with other libraries.

Is there a way to avoid the creation of an empty namespace prefix using spring-ws?

We have a WSDL, and there, everything looks ok to me, but we cannot modify it:

<?xml version='1.0' encoding='UTF-8'?>
<wsdl:definitions xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:tns="http://ws.validator.sch.gazelle.ihe.net/"
              xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
              name="GazelleObjectValidatorService" targetNamespace="http://ws.validator.sch.gazelle.ihe.net/">
    <wsdl:types>
        <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:tns="http://ws.validator.sch.gazelle.ihe.net/"
               attributeFormDefault="unqualified" elementFormDefault="unqualified"
               targetNamespace="http://ws.validator.sch.gazelle.ihe.net/">
        <xs:element name="about" type="tns:about"/>
        <xs:element name="aboutResponse" type="tns:aboutResponse"/>
        <xs:element name="getAllAvailableObjectTypes" type="tns:getAllAvailableObjectTypes"/>
        <xs:element name="getAllAvailableObjectTypesResponse" type="tns:getAllAvailableObjectTypesResponse"/>
        <xs:element name="getAllSchematrons" type="tns:getAllSchematrons"/>
        <xs:element name="getAllSchematronsResponse" type="tns:getAllSchematronsResponse"/>
        <xs:element name="getSchematronByName" type="tns:getSchematronByName"/>
        <xs:element name="getSchematronByNameResponse" type="tns:getSchematronByNameResponse"/>
        <xs:element name="getSchematronsForAGivenType" type="tns:getSchematronsForAGivenType"/>
        <xs:element name="getSchematronsForAGivenTypeResponse" type="tns:getSchematronsForAGivenTypeResponse"/>
        <xs:element name="validateObject" type="tns:validateObject"/>
        <xs:element name="validateObjectResponse" type="tns:validateObjectResponse"/>

This is the code where we create the WebServiceTemplate:

Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
marshaller.setPackagesToScan("com.test");
WebServiceTemplate webServiceTemplate = new WebServiceTemplate(marshaller);
webServiceTemplate.setDefaultUri(defaultUri);
webServiceTemplate.setMessageSender(new HttpComponentsMessageSender(httpClientBuilder.build()));

ValidateObject request = new ValidateObject();
request.setBase64ObjectToValidate(base64Object);
request.setXmlReferencedStandard(xmlReferencedStandard);
request.setXmlMetadata(xmlMetadata);
ValidateObjectResponse response = (ValidateObjectResponse) 
webServiceTemplate.marshalSendAndReceive(request);
return response.getValidationResult();

The question is how I can avoid the generation of the empty namespace name:

<ns2:validateObject xmlns:ns2="http://ws.validator.sch.gazelle.ihe.net/" xmlns=""> 

so that axiom can no longer complain?


Solution

  • The problem ended up to be a jar conflict between the axiom jar used by spring-ws and the axiom jar used by Axis.

    By forcing the use of a specific SaajSoapMessageFactory for the WebServiceTemplate:

    SaajSoapMessageFactory saajSoapMessageFactory = new SaajSoapMessageFactory(new SOAPMessageFactory1_1Impl());
    saajSoapMessageFactory.setSoapVersion(SoapVersion.SOAP_11);
    saajSoapMessageFactory.afterPropertiesSet();
    
    webServiceTemplate.setMessageFactory(saajSoapMessageFactory);
    

    we managed to solve our issue.