How do you add XML prolog in javax.xml.soap.SOAPBody - java

I am trying to send an XML document via SOAP to a server that requires the payload XML to contain an XML prolog e.g., <?xml version="1.0" encoding="utf-8" ?>. The following is very similar to the code I'm using:
SOAPMessage soapMessage = MessageFactory.newInstance().createMessage();
SOAPPart soapPart = getSoapMessage().getSOAPPart();
SOAPEnvelope envelope = soapPart.getEnvelope();
envelope.addNamespaceDeclaration("SOAP-ENV", "http://schemas.xmlsoap.org/soap/envelope/");
soapHeader = envelope.getHeader();
soapBody = envelope.getBody();
soapHeader.detachNode();
SOAPBodyElement bodyElement = soapBody.addBodyElement(getEnvelope().createName("my_element_name"));
bodyElement.addAttribute(getEnvelope().createName("my_attribute_name"), "my_attribute_value");
bodyElement.addChildElement("my_child_element");
...

A XML prolog can only exist at the very start of a XML document.
From the XML Specification available here : http://www.w3.org/TR/2000/REC-xml-20001006#sec-prolog-dtd
The document type declaration must appear before the first element in the document.
Writing a prolog in the middle of a SOAP document, therefore, is invalid - and I doubt you'll find a SOAP library or implementation that will allow you to do that. There may be a requirement you and/or your "client" have misunderstood in requiring this.
You can try a XML validator softare (e.g. http://www.w3schools.com/xml/xml_validator.asp ), it should report an error for your expected result.

Here is how to add xml prolog declaration right before the first SOAP Envelope tag:
soapMessage.setProperty(SOAPMessage.WRITE_XML_DECLARATION, Boolean.TRUE.toString());

Related

Dynamically adding namespace to SOAP Envelope in Java

I am new to all of this but I am trying to create a SOAP message and get stuck at the off, I am using Java 8 and the standard javax.xml.soap classes but seem unable to add namespaces to the Envelope
MessageFactory factory = MessageFactory.newInstance();
SOAPMessage message = factory.createMessage();
SOAPPart part = message.getSOAPPart();
SOAPEnvelope envelope = part.getEnvelope();
envelope.addNamespaceDeclaration( "xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance");
If I try this at runtime I get the following error
NAMESPACE_ERR: An attempt is made to create or change an object in a way which is incorrect with regard to namespaces.
I have done this now by creating the required envelope definition as an XML String and then setting the SOAPPart content using that string

How do I append an XML request to the only SOAPElement child of the SOAP Body?

The use case
I need to contact an external SOAP service in a programmatic way. To do so, I need to create a SOAP request which looks like the below:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:pric="http://myURI/">
<soapenv:Header/>
<soapenv:Body>
<pric:myAPI>
<XmlDocument>
<OtherXmlContent>
</OtherXmlContent>
</XmlDocument>
</pric:myAPI>
</soapenv:Body>
</soapenv:Envelope>
The task I'm blocked on
I manage to create the following SOAP envelope:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:pric="http://myURI/">
<soapenv:Header/>
<soapenv:Body>
<pric:myAPI>
</pric:myAPI>
</soapenv:Body>
</soapenv:Envelope>
What I need to do, provided an input, is to add the XML request:
<XmlDocument>
<OtherXmlContent>
</OtherXmlContent>
</XmlDocument>
... as a child of <pric:myAPI>, which is the only child of my <soapenv:Body>.
For information, the above Soap envelope (without XmlDocument yet) is created by the following code:
MessageFactory messageFactory = MessageFactory.newInstance();
SOAPMessage soapMessage = messageFactory.createMessage();
SOAPPart soapPart = soapMessage.getSOAPPart();
SOAPEnvelope envelope = soapPart.getEnvelope();
envelope.addNamespaceDeclaration(pricingNamespace, pricingNamespaceURI);
SOAPBody soapBody = envelope.getBody();
SOAPElement pricingWrapper = soapBody.addChildElement(pricingAction, pricingNamespace);
... hence, what I need to do is to append a child to pricingWrapper. I choose how I create this child, I have full control on the function generating it which is this one:
private static String createXmlProductFromDealingDocument(Document dealings)
Attempt 1 - adding the document as text
I have tried to add the XmlDocument as text of pricingWrapper.
This is how I did it:
pricingWrapper.addTextNode(createXmlProductFromDealingDocument(dealingFile));
The problem, though, is that all the characters < and > of the XmlDocument rendered as String are escaped by the method addTextNode. In other words, I can see my body has the correct content, but < is replaced with < and > is replaced with >, so making the SOAP request invalid for the target service.
Attempt 2 - adding the document as child node
Another attempt I did was to return a Node instead of a String from my function:
private static Node createXmlProductFromDealingDocument(Document dealings)
and append this Node as a child of pricingWrapper:
pricingWrapper.appendChild(createXmlProductFromDealingDocument(dealingFile));
The above raises an exception of type:
org.w3c.dom.DOMException: WRONG_DOCUMENT_ERR: A node is used in a different document than the one that created it.
Ok, fair enough. I posted a question (deleted shortly as I wanted to go deeper in my researches) yesterday, and a user kindly suggested me in a comment to check how to clone DOM nodes by referencing this answer.
I tried to do so as follows:
Node pricingRequest = createXmlProductFromDealingDocument(dealingFile);
Node soapPricingRequest = pricingRequest.cloneNode(true);
pricingWrapper.getOwnerDocument().adoptNode(soapPricingRequest);
pricingWrapper.appendChild(soapPricingRequest);
However, this raises a new exception:
org.w3c.dom.DOMException: NOT_SUPPORTED_ERR: The implementation does not support the requested type of object or operation.
... on the following line:
pricingWrapper.getOwnerDocument().adoptNode(soapPricingRequest);
... and I really don't see how else I could append the child differently than what I did above.
What is my question
I just would like to complete my SOAP request the right way. I do not have any preference whether this would be done by injecting the XML as text or as a Node, as far as is the proper way and especially as far as it works :)
Could anyone please tip me how could I fix my above issues?
Reading the javadoc of adoptNode() :
DOMException - NOT_SUPPORTED_ERR: Raised if the source node is of type DOCUMENT, DOCUMENT_TYPE. ".
So, you could you chek if soapPrincingRequest is indeed a DOCUMENT type of node (using getNodeType()) ?
If so, I would advise you to cast soapPrincingRequest as a Document, then access its root NODE with getDocumentElement(), and try to adopt() this NODE, and not the DOCUMENT one.
XML APIs, when they strive for correctness are always a bit touchy to use, and the distinction between a Document and its root element actually matter. So it's a bit of a pain, but we get there in the end.

Adding several URIs to SOAP Envelope using Java

I am trying to write a SOAP pull. I am running into difficulty formatting the message correctly.
I am only using the included javax.xml.soap.* library with Exclipse
I need the Envelope to have multiple URIs in it. This is the example provided for use with SOAPUI.
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:sh="http://www.website.com/ems/soap/sh" xmlns:user="http://www.website.com/ems/soap/sh/userdata" xmlns:ser="http://www.website.com/ems/soap/sh/servicedata">
However after looking through several tutorials I am only able to produce
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:sh="http://www.website.com/ems/soap/sh">
I have haven't been able to find any documentation on how to achieve the required output. I am still new to SOAP and Java so I am not sure how to articulate exactly what I need.
Here is the code I have so far minus the child element portion
MessageFactory messageFactory = MessageFactory.newInstance();
SOAPMessage soapMessage = messageFactory.createMessage();
SOAPPart soapPart = soapMessage.getSOAPPart();
soapMessage.getSOAPPart().getEnvelope().setPrefix("soapenv");
soapMessage.getSOAPPart().getEnvelope().removeNamespaceDeclaration("SOAP-ENV");
soapMessage.getSOAPBody().setPrefix("soapenv");
soapMessage.getSOAPHeader().setPrefix("soapenv");
String serverURI = "http://www.website.com/ems/soap/sh";
// SOAP Envelope
SOAPEnvelope envelope = soapPart.getEnvelope();
envelope.addNamespaceDeclaration("sh", serverURI );
If you want to get this soap-envelope,
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:sh="http://www.website.com/ems/soap/sh" xmlns:user="http://www.website.com/ems/soap/sh/userdata" xmlns:ser="http://www.website.com/ems/soap/sh/servicedata">
Just add addNamespaceDeclaration after your code like this.
SOAPEnvelope envelope = soapPart.getEnvelope();
envelope.addNamespaceDeclaration("sh", serverURI );
//added code here
envelope.addNamespaceDeclaration("user", "http://www.website.com/ems/soap/sh/userdata" );
envelope.addNamespaceDeclaration("ser", "http://www.website.com/ems/soap/sh/servicedata" );

Parsing SOAP request sent from SOAPUI - using Axis2 Servlet

Here's the SOAP request that I am submitting using SOAPUI
<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:My namespace="My Package">
<soapenv:Header>
<Username>q</Username>
<Password>q</Password>
</soapenv:Header>
<soapenv:Body>
<Op:Op>
<Op:int>2134</Op:int>
</Op:Op>
</soapenv:Body>
</soapenv:Envelope>
Now I have created a maven project in eclipse and have generated a wsdl file, aar file (for deploying using Tomcat 7) and a jar file from the Java code (java2wsdl). When the request is submitted, the code must authorize the user with credentials provided under the header element. However, I am not able to parse the SOAP request. When I tried parsing with,
SOAPFactory fac = OMAbstractFactory.getSOAP11Factory();
SOAPEnvelope envelope = fac.getDefaultEnvelope();
SOAPHeader header = envelope.getHeader();
SOAPBody body = envelope.getBody();
Iterator it = header.getChildElements();
Iterator bodyIt = body.getChildElements();
while (it.hasNext()) {
OMElement e = (OMElement) it.next();
System.out.println(e.getText().toString());
}
while (bodyIt.hasNext()) {
OMElement e = (OMElement) bodyIt.next();
System.out.println(e.getText().toString());
}
whereby SOAPFactory and other objects are imported from axiom, none of the print statements where executed. So the question is how do I parse this request so that I have the ability to read the header and body?
I apologize if anything is vague; I am still new to Java web services.
From the Javadoc of the SOAPFactory#getDefaultEnvelope() method:
Create a default SOAP envelope with an empty header and an empty body.
So your Java code behaves as expected.

Java - Consume SOAP Web Service from String XML Envelope

Using SoapUI I have built the following XML envelope and encapsulated it in a String.
ProcessJournal is a method which has no parameters, and returns a string.
String soapText = "<Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:upd=\"http://webservices.vm.vmc/UpdateCMA/\">" +
"<Header/>" +
"<Body>" +
"<upd:ProcessJournal/>" +
"</Body>" +
"</Envelope>";
Now... I simply want to invoke the web service defined above. And have found some sample code to do so
// Create SoapMessage
MessageFactory msgFactory = MessageFactory.newInstance();
SOAPMessage message = msgFactory.createMessage();
SOAPPart soapPart = message.getSOAPPart();
// Load the SOAP text into a stream source
byte[] buffer = soapText.getBytes();
ByteArrayInputStream stream = new ByteArrayInputStream(buffer);
StreamSource source = new StreamSource(stream);
// Set contents of message
soapPart.setContent(source);
// -- DONE
message.writeTo(System.out);
//Try accessing the SOAPBody
SOAPBody soapBody = message.getSOAPBody();
The problem is, at message.getSOAPBody();, I get an error
XML-22103: (Fatal Error) DOMResult can not be this kind of node.
Apr 16, 2013 12:05:06 PM com.sun.xml.internal.messaging.saaj.soap.EnvelopeFactory createEnvelope
SEVERE: SAAJ0511: Unable to create envelope from given source
All the various samples I find, end up having the same type of error when getSOAPBody() is executed.
I don't know whether you ever found a solution to this. I've just recently been seeing the exact same error and it was due to a TransformerFactory not being set.
I used the TransformerFactory from the Saxon library - the jar for which can be obtained here.
I then set a system property which referenced the Saxon TransformerFactory:
System.setProperty("javax.xml.transform.TransformerFactory",
"net.sf.saxon.TransformerFactoryImpl");
The error then disappeared when I re-ran my code.
The above is just one way to set the TransformerFactory. There are a number of other ways to do this which I found here: stackoverflow.com/questions/11314604/
It looks like the problem was with the namespace prefixing in your soapText. I was able to execute your example without error and without manually setting any TransformerFactory simply by modifying your soapText as follows:
String soapText =
"<soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:upd=\"http://webservices.vm.vmc/UpdateCMA/\">" +
"<soapenv:Header/>" +
"<soapenv:Body>" +
"<upd:ProcessJournal/>" +
"</soapenv:Body>" +
"</soapenv:Envelope>";
Note the added soapenv: prefix in all the Soap elements in contrast to the upd prefix in your Soap service namespace.

Categories

Resources