Daniel Pomrehn
Daniel Pomrehn

Reputation: 855

spring-ws Generating SOAP Service without Request Parameter Suffix

I need to use some XSDs files from an industry standard to generate a SOAP webservice from it. REST is not possible because the consumer is only able to do SOAP calls.

The parameters in the XSD are defined as the following:

   <xsd:element name="getData">
      <xsd:complexType>
         <xsd:sequence>
            <xsd:element name="Request" type="CT_getDataRequest" minOccurs="1" maxOccurs="1"/>
         </xsd:sequence>
      </xsd:complexType>
   </xsd:element>
   <xsd:element name="getDataResponse">
      <xsd:complexType>
         <xsd:sequence>
            <xsd:element name="Response" type="CT_getDataResponse" minOccurs="1" maxOccurs="1"/>
         </xsd:sequence>
      </xsd:complexType>
   </xsd:element>

That is my endpoint definition:

@Endpoint
@RequiredArgsConstructor
public class DataEndpoint {
    private static final String NAMESPACE_URI = "http://www.machine-config.net/namespace/data";

    @PayloadRoot(namespace = NAMESPACE_URI, localPart = "getData")
    @ResponsePayload
    public GetDataResponse getData(@RequestPayload GetData request) {
    }
}

Here is my WsConfiguration:

@EnableWs
@Configuration
public class WebServiceConfig extends WsConfigurerAdapter {

    @Bean
    public ServletRegistrationBean messageDispatcherServlet(ApplicationContext applicationContext) {
        MessageDispatcherServlet messageDispatcherServlet = new MessageDispatcherServlet();
        messageDispatcherServlet.setApplicationContext(applicationContext);
        messageDispatcherServlet.setTransformWsdlLocations(true);
        return new ServletRegistrationBean(messageDispatcherServlet, "/ws/*");
    }

    @Bean(name = "dataDefinition")
    public DefaultWsdl11Definition dataWsdl11Definition() {
        DefaultWsdl11Definition wsdl11Definition = new DefaultWsdl11Definition();
        wsdl11Definition.setPortTypeName("dataServicePortType");
        wsdl11Definition.setLocationUri("/ws");
        wsdl11Definition.setTargetNamespace("http://www.machine-config.net/namespace");
        wsdl11Definition.setSchemaCollection(dataSchemas());
        
        return wsdl11Definition;
    }

    @Bean(name = "dataSchema")
    public XsdSchemaCollection dataSchemas() {
        CommonsXsdSchemaCollection xsds = new CommonsXsdSchemaCollection(new ClassPathResource("xsd/data-2.18.1433.2.xsd"));
        xsds.setUriResolver(new DefaultURIResolver());
        xsds.setInline(true);
        return xsds;
    }
}

But inside the generated WSDL the following operation is generated:

<wsdl:operation name="getData">
    <wsdl:output message="sch0:getDataResponse" name="getDataResponse"> </wsdl:output>
</wsdl:operation>

The wsdl:input part is missing.

When I change the xsd:element getData to getDataRequest the wsdl:input element is generated. But that workaround will not work because the XSD will be delivered by a vendor and I am not allowed to change anything in this file.

How can I make spring-ws to recognize the input parameter without the Request Suffix?

Upvotes: 0

Views: 421

Answers (1)

FiruzzZ
FiruzzZ

Reputation: 826

to have "request" parameters without the suffix expected by Spring, you need to:

private static class CustomMessagesProvider extends SuffixBasedMessagesProvider {
    private static final String[] REQUEST_ELEMENTS = new String[]{
        "getData", "paramaterWithoutRequeeeeest", "anotherInput"
    };

    @Override
    protected boolean isMessageElement(Element element) {
        if (super.isMessageElement(element)) {
            return true;
        } else {
            boolean isElement = "element".equals(element.getLocalName()) && 
                "http://www.w3.org/2001/XMLSchema".equals(element.getNamespaceURI());
            String elementName = getElementName(element);
            return isElement && Arrays.asList(REQUEST_ELEMENTS).contains(elementName);
        }
    }
}

private static class CustomPortTypesProvider extends SuffixBasedPortTypesProvider {
    @Override
    protected String getOperationName(Message message) {
        String messageName = message.getQName().getLocalPart();
        if (isInputMessage(message)) {
            return messageName;
        } else if (isOutputMessage(message)) {
            return messageName.substring(0, messageName.length() - getResponseSuffix().length());
        } else if (isFaultMessage(message)) {
            return messageName.substring(0, messageName.length() - getFaultSuffix().length());
        }
        return null;
    }

    @Override
    protected boolean isInputMessage(Message message) {
        String messageName = message.getQName().getLocalPart();
        return messageName.endsWith(getResponseSuffix()) && !messageName.endsWith(getFaultSuffix());
    }
}

then, in your

@Bean(".....")
public ProviderBaseWsdl4jDefinition methodName(....) {
 var wsdl4jDef = new Provider....
..
..
wsdl4jDef.setMessagesProvider(new CustomMEssagesProvider()); //<-- relevant code 
}

Probably anyone who has/had to give support to legacy/old SOAP projects these days struggle with the same :)

Upvotes: 0

Related Questions