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
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);
}
}
}
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
SOAP messages must declare the namespace of the Envelope element to be the standard SOAP 1.1 envelope namespace, "http://schemas.xmlsoap.org/soap/envelope/". If a SOAP application receives a message based on some other namespace, it must generate a fault.
An XML namespace is a means of qualifying element and attribute names to disambiguate them from other names in the same document. This section provides a brief description of XML namespaces and how they are used in SOAP. For complete information, see http://www.w3.org/TR/REC-xml-names/
The SOAP envelope indicates the start and the end of the message so that the receiver knows when an entire message has been received. The SOAP envelope solves the problem of knowing when you are done receiving a message and are ready to process it. The SOAP envelope is therefore basically a packaging mechanism.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With