Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Sending XmlData in soap request using spring ws

I have requirement to send XmlData in CDATA to soap request which returns an attachment.

Using org.springframework.oxm.jaxb.Jaxb2Marshaller as marshaller and unmarshaller.

<![CDATA[<tag>Test</tag>]]> this is something I want to send in the soap request and remote service is expecting it to see the same way.

I have created the data but when I use webServiceTemplate.marshalSendAndReceive(payloadRequest, SoapActionCallback)

somehow, payload request xmltags are encoded and shows as

&lt;![CDATA[&lt;tag&gt;Test&lt;\tag&gt;

Because of this encoding, remote service cannot process the request and send org.springframework.ws.soap.client.SoapFaultClientException: Object reference not set to an instance of an object.

how can I fix this issue? Any suggestions!

Update: Does, spring mvc's defaultHtmlEscape as context-param in web.xml responsible for this behaviour?

like image 565
user1609085 Avatar asked Apr 15 '14 20:04

user1609085


People also ask

How do you call a SOAP Web service from REST API in spring boot?

Steps to Consume a SOAP service :Create spring boot project and Get the WSDL from the provider . Convert the WSDL to Stub. Understand the request ,response and the types ,operations using any tool like SOAP UI. Form the request object by mapping data and call the soap uri with marshal the java objects as XML.


2 Answers

I have got the same problem in this day at work, when we must inject a CDATA section over a field text of a data structure defined into a WSDL exposed from a Bank web service.

The requirement was to introduce a CDATA section to escape the Unicode character "+" into a number telephone field. The WSDL is not possible to change for various motivation.

We have the same environment:

 org.springframework.oxm.jaxb.Jaxb2Marshaller

webServiceTemplate.marshalSendAndReceive(payloadRequest, SoapActionCallback)

and the same fail result

&lt;![CDATA[&lt;tag&gt;+&lt;\tag&gt;

instead of

<[[CDATA[+]]>

After many and many test and after have consulting many fonts on Stackoverflow and other site, we have understand that the solution is not natively supported from JAXB engine and it's necessary to manipulate the SOAP message into a puntual step of processing the request.

Other solution indicated to use third part plugin and library, ad example MOXy, to accomplished a strong support of CDATA section also JAXB. But this solution is not possible to integrate in fast time over an huge enterprise application in form ASAP.

This solution that I publish in this reply permit to inject a CDATASection using the canonical method of a classic DOMSource object casting the message SOAP from messagecontext.

In your answer you expose a part of solution, and this is the callback of marshalSendAndReceive.

If you define an ad hoc callback supported by marshalSendAndReceive method and in this callback manipulate the DOMSource injecting the CDATASection you can resolve the problem.

This is the code:

WebServiceMessageCallback callbackCDATANumTelefono = new WebServiceMessageCallback() {
           public void doWithMessage(WebServiceMessage message) {

                //Recover the DOMSource dal message soap
                DOMSource domSource = (DOMSource)message.getPayloadSource();

                //recover set of child nodes of domsource
                NodeList nodeList = domSource.getNode().getChildNodes();

                //definisco il nome del tag da cercare
                String nameNumTel = "ns2:sNumTel"; //in this example, but you can define another QName string to recover the target node (or nodes)
                Node nodeNumTel = null;
                for (int i = 0; i < nodeList.getLength(); i++) {
                    //search of text node of this tag name
                    if (nodeList.item(i).getNodeName().compareTo(nameNumTel) == 0) {
                        nodeNumTel = nodeList.item(i);
                        break;
                    }
                }
                //recover the string value (in this case of telephone number)
                String valueNumTelefono = nodeNumTel.getTextContent();
                //clean of value of target text node
                nodeNumTel.setTextContent("");
                //define and inject of CDATA section, in this case encapsulate "+" character
                CDATASection cdata = nodeNumTel.getOwnerDocument().createCDATASection("+");
                //create of new text node
                Text tnode = nodeNumTel.getOwnerDocument().createTextNode(valueNumTelefono);
                //append of CDATASection (in this point is possible to inject many CDATA section on various parts of string (very usefull)
                nodeNumTel.appendChild(cdata);
                nodeNumTel.appendChild(tnode);

                //done!
            }
        };

this callback will be call from marshalSendAndReceive

PayloadSoapResponse output = (PayloadSoapResponse ) getWebServiceTemplate()
                .marshalSendAndReceive(input, callbackCDATANumTelefono);

The result is a correct send of request with CDATA section valid and not converted in ascii character.

This solution have the objective to manipolate CDATA section over Spring infrastructure with JAXB tecnology , not using third part library. In general the set of instructions of callback method is very trust and good to be injecting in all environment of a web service soap. The crucial aspect is to convert the SoapMessage in an more friendly DOMSource with canonical method to explore node. It's possible using also XPath engine to navigate this DOMSource.

Good luck!

am

like image 66
Alessandro Modica Avatar answered Oct 07 '22 14:10

Alessandro Modica


I had this exact problem when using spring-ws to produce a SOAP web service. The CDATA section would always be escaped as quoted above.

In my case the solution (very similar to Alessandro) was to create an EndpointInterceptor where I translated the required node to a org.w3c.dom.CDATASection in handleResponse. You then need to add this interceptor to the interceptor list in your implementation.

import org.springframework.ws.WebServiceMessage;
import org.springframework.ws.context.MessageContext;
import org.springframework.ws.server.EndpointInterceptor;
import org.springframework.ws.soap.saaj.SaajSoapMessage;
import org.w3c.dom.CDATASection;

import javax.xml.soap.Node;
import javax.xml.soap.SOAPBody;
import javax.xml.soap.SOAPEnvelope;
import javax.xml.soap.SOAPMessage;
import javax.xml.soap.SOAPPart;
import java.util.Iterator;

public class MyCdataInterceptor implements EndpointInterceptor {

@Override
public boolean handleRequest(MessageContext messageContext, Object o) throws Exception {
    return true;
}

@Override
public boolean handleResponse(MessageContext messageContext, Object o) throws Exception {

    WebServiceMessage response = messageContext.getResponse();

    SaajSoapMessage saajSoapMessage = (SaajSoapMessage) response;
    SOAPMessage soapMessage = saajSoapMessage.getSaajMessage();
    SOAPPart soapPart = soapMessage.getSOAPPart();
    SOAPEnvelope envelope = soapPart.getEnvelope();
    SOAPBody body = envelope.getBody();
    Iterator it = body.getChildElements();
    ... 
     /* find node of interest */

    Node interestingNode = (Node) blah.getNextSibling();

    CDATASection cdat = soapPart.createCDATASection(interestingNode.getFirstChild().getNodeValue());
    interestingNode.removeChild(interestingNode.getFirstChild());
    interestingNode.appendChild(cdat);
    return true;
}

@Override
public boolean handleFault(MessageContext messageContext, Object o) throws Exception {
    return true;
}

@Override
public void afterCompletion(MessageContext messageContext, Object o, Exception e) throws Exception {

}
}
like image 35
Tim T Avatar answered Oct 07 '22 13:10

Tim T