How to change "SOAP-ENV" default prefix of Spring-WS
Asked Answered
G

3

23

I've created a web service using Spring-WS. To maintain compatibility with the old system, I need to change namespace prefix from SOAP-ENV to soap.

I know that SOAP-ENV and soap are just namespace prefixes. As long as they refer to the correct namespace ("http://schemas.xmlsoap.org/soap/envelope/"), it should be fine.

But the old system hard coded the parser code to expect only soap namespace prefix.

Current response:

<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
   <SOAP-ENV:Header/>
   <SOAP-ENV:Body>
   ...
   </SOAP-ENV:Body>
</SOAP-ENV:Envelope>

Expected response:

<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
   <soap:Header/>
   <soap:Body>
   ...
   </soap:Body>
</soap:Envelope>

Here's what I've tried so far

  1. Create EndpointInterceptorAdapter subclass. This will intercept SOAP response/fault and alter the SOAP envelope. This works, but it's not ideal in terms of performance.

    public class CustomEndpointInterceptor extends EndpointInterceptorAdapter {
    
      private static final String DEFAULT_NS = "xmlns:SOAP-ENV";
      private static final String SOAP_ENV_NAMESPACE = "http://schemas.xmlsoap.org/soap/envelope/";
      private static final String PREFERRED_PREFIX = "soap";
      private static final String HEADER_LOCAL_NAME = "Header";
      private static final String BODY_LOCAL_NAME = "Body";
      private static final String FAULT_LOCAL_NAME = "Fault";
    
      @Override
      public boolean handleResponse(MessageContext messageContext, Object endpoint) throws Exception {
        SaajSoapMessage soapResponse = (SaajSoapMessage) messageContext.getResponse();
        alterSoapEnvelope(soapResponse);
        return super.handleResponse(messageContext, endpoint);
      }
    
      @Override
      public boolean handleFault(MessageContext messageContext, Object endpoint) throws Exception {
        SaajSoapMessage soapResponse = (SaajSoapMessage) messageContext.getResponse();
        alterSoapEnvelope(soapResponse);
        return super.handleFault(messageContext, endpoint);
      }
    
      private void alterSoapEnvelope(SaajSoapMessage soapResponse) {
        Document doc = soapResponse.getDocument();
        Element rootElement = doc.getDocumentElement();
        rootElement.setPrefix(PREFERRED_PREFIX);
        // Remove default SOAP namespace
        rootElement.removeAttribute(DEFAULT_NS);
        NodeList headerNodes = doc.getElementsByTagNameNS(SOAP_ENV_NAMESPACE, HEADER_LOCAL_NAME);
        NodeList bodyNodes = doc.getElementsByTagNameNS(SOAP_ENV_NAMESPACE, BODY_LOCAL_NAME);
        NodeList faultNodes = doc.getElementsByTagNameNS(SOAP_ENV_NAMESPACE, FAULT_LOCAL_NAME);
        // Remove Header node.
        if (headerNodes.getLength() != 0) {
          rootElement.removeChild(headerNodes.item(0));
        }
        // Change Body's SOAP namespace prefix.
        if (bodyNodes.getLength() != 0) {
          Element bodyElement = (Element) bodyNodes.item(0);
          bodyElement.setPrefix(PREFERRED_PREFIX);
        }
        if (faultNodes.getLength() != 0) {
          Element faultElement = (Element) faultNodes.item(0);
          faultElement.setPrefix(PREFERRED_PREFIX);
        }
      }
    }
    
  2. Change package-info.java in the package that contain WSDL generated classes. I've successfully done this with my company's namespace prefix, but it doesn't work for SOAP-ENV prefix.

    @javax.xml.bind.annotation.XmlSchema(namespace = "http://www.example.com/ns/2008/02/02/webservices/blah",
    xmlns = {
      @javax.xml.bind.annotation.XmlNs(namespaceURI = "http://www.example.com/ns/2008/02/02/webservices/blah", prefix = ""),
      @javax.xml.bind.annotation.XmlNs(namespaceURI = "http://schemas.example.com/ns/2007/10/blah", prefix = "ns2"),
      @javax.xml.bind.annotation.XmlNs(namespaceURI = "http://example.com/ns/2007/23/05/blah/fundamental", prefix = "ns3"),
      @javax.xml.bind.annotation.XmlNs(namespaceURI = "http://schemas.xmlsoap.org/soap/envelope/", prefix = "soap") // doesn't work
    },
    elementFormDefault = javax.xml.bind.annotation.XmlNsForm.QUALIFIED)
    package com.thomsonreuters.ts.ets.interdayws.soap.webservice;
    

Is there an ideal way to change SOAP-ENV to soap in Spring-WS?

By the way, here's the code that set this prefix. StroapElement.java

Ganges answered 31/8, 2016 at 6:26 Comment(2)
These guys had the same problem and they solved it. Check the answer from johnreiter : forum.spring.io/forum/spring-projects/web-services/…Evvoia
@AydinK. Thanks for point me to that forum. Actually, I've read this thread before, but it's discussing from a client side point of view. Reading that one more time help me figure out how to adapt his answer to my problem.Ganges
G
30

A better solution

Use SOAPMessage API instead of DOM.

  private void alterSoapEnvelope(SaajSoapMessage soapResponse) {
    try {
      SOAPMessage soapMessage = soapResponse.getSaajMessage();
      SOAPPart soapPart = soapMessage.getSOAPPart();
      SOAPEnvelope envelope = soapPart.getEnvelope();
      SOAPHeader header = soapMessage.getSOAPHeader();
      SOAPBody body = soapMessage.getSOAPBody();
      SOAPFault fault = body.getFault();
      envelope.removeNamespaceDeclaration(envelope.getPrefix());
      envelope.addNamespaceDeclaration(PREFERRED_PREFIX, SOAP_ENV_NAMESPACE);
      envelope.setPrefix(PREFERRED_PREFIX);
      header.setPrefix(PREFERRED_PREFIX);
      body.setPrefix(PREFERRED_PREFIX);
      if (fault != null) {
        fault.setPrefix(PREFERRED_PREFIX);
      }
    } catch (SOAPException e) {
      e.printStackTrace();
    }
  }

It's much faster now.

Ganges answered 1/9, 2016 at 11:20 Comment(7)
Is there an example how to use it? I have an @Endpoint that implements a generated interface/"PortType" by cxf. Where should I add SOAPMessage API?Clause
@Clause Do you have EndpointInterceptorAdapter subclass anywhere in your project? I use SOAPMessage API in its alterSoapEnvelope method.Ganges
yes, I solved it with a class implementing SoapEndpointInterceptor and its public boolean handleResponse(MessageContext messageContext, Object endpoint) in which I alter the response like this: SaajSoapMessage response = (SaajSoapMessage) messageContext.getResponse(); alterSoapEnvelope(response); return true;Clause
SaajSoapMessage is spring. in my case I used the standard jdk SOAPMessage class. after all the change listed in the code above, have to call the SOAPMessage.saveChanges()Recitative
Anyone knows how to main user defined ns as per WSDL or XSD schema?Runthrough
@Ganges this is a great solution, but in order to not confuse people you had to have add a piece of code with Configuration class, where need to add this new CustomInterceptor.Titoism
This solution is wonderful, thanks for sharing itAllheal
O
12

I use SAAJ. Try this.

  1. soapEnvelope.removeNamespaceDeclaration("SOAP-ENV");
  2. soapEnvelope.addNamespaceDeclaration("soapenv", "http://schemas.xmlsoap.org/soap/envelope/");
  3. soapEnvelope.setPrefix("soapenv");
  4. soapHeader.setPrefix("soapenv");
  5. soapBody.setPrefix("soapenv");

Don't forget: soapMessage.saveChanges();

Reference:Changing the default XML namespace prefix generated with JAXWS

Odontalgia answered 6/3, 2018 at 1:11 Comment(0)
I
4

Additional Point :

you have to extend your Webservice config class with WsConfigurationAdapter and add your CustomEndpointInterceptor in the webservice config class as stated below.

Then only the interceptor works.

Refer below link for more details

https://memorynotfound.com/spring-ws-intercept-request-response-soap-messages/

Immixture answered 13/8, 2020 at 11:38 Comment(1)
Guys. This is the answerEmancipate

© 2022 - 2024 — McMap. All rights reserved.