SharePoint Web Services jax-ws namespace conflict - java

I am trying to use SharePoint Web service to retrieve list changes but there seems to be a namespace conflict between what the jax-ws client generates and what SharePoint will accept. Below is the xml that is generated by jax-ws.
<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
<S:Body>
<GetListItemChanges xmlns="http://schemas.microsoft.com/sharepoint/soap/">
<listName>Documents</listName>
<viewFields>
<FieldRef Name="Modified"/>
<FieldRef Name="_CheckinComment"/>
<FieldRef Name="Title"/>
<FieldRef Name="Created"/>
</viewFields>
<since>1970-01-01T00:00:00</since>
<contains/>
</GetListItemChanges>
</S:Body>
</S:Envelope>
i need to remove the xmlns="http://schemas.microsoft.com/sharepoint/soap/" from GetListItemChanges. I have tried the following (and various permutations thereof) but the changes seem to be ignored. The xmlns is removed when debugging but the output xml does not change.
public class SharePointSoapHandler implements SOAPHandler<SOAPMessageContext> {
...
#Override
public boolean handleMessage(SOAPMessageContext p_soapMessageContext) {
try {
SOAPMessage l_soapMessage = p_soapMessageContext.getMessage();
l_soapMessage.getSOAPBody().getFirstChild().getAttributes().removeNamedItem("xmlns");
l_soapMessage.saveChanges();
ByteArrayOutputStream l_outputStream = new ByteArrayOutputStream();
l_soapMessage.writeTo(l_outputStream);
m_logger.info(new String(l_outputStream.toByteArray()));
} catch (Exception ex) {
m_logger.error("Soap exception modifying xml for request", ex);
}
return true;
}
}
Am I missing something? Is there a better way to accomplish this or do I need to generate the xml by hand?
EDIT: A valid soap request using soap ui. jax-ws drops the second prefix and moves the xmlns attribute.
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:soap="http://schemas.microsoft.com/sharepoint/soap/">
<soapenv:Header/>
<soapenv:Body>
<soap:GetListItemChanges>
<!--Optional:-->
<soap:listName>Shared Documents</soap:listName>
...
<soap:since>2012-02-15T00:00:00</soap:since>
</soap:GetListItemChanges>
</soapenv:Body>
</soapenv:Envelope>

See Changing the default XML namespace prefix generated with JAXWS for using a handler to intercept the soap and modify it as needed; also a useful technique for debugging the soap as its sent over the wire.
you can also set the namespace declarations in the soap header like so:
SOAPMessage request = factory.createMessage();
SOAPEnvelope envelope = request.getSOAPPart().getEnvelope();
envelope.addNamespaceDeclaration("uri", "uri:foo.bar.com");
request.saveChanges();
and then create elements with the namespace prefix like this:
SOAPBody body = request.getSOAPBody();
SOAPElement ping = body.addChildElement("foo", "uri");
without setting the declaration in the header first, adding the element with the prefix will fail.
doing things this way seems to circumvent having the namespace declaration hanging off of body nodes, which was breaking what i was trying to do.
here's my jUnit test that i used to verify this works:
public void testPing() throws Exception {
String endpoint = "http://www.foo.bar/ws/someWebservice";
QName port = new QName(endpoint, "uri");
QName serviceName = new QName(endpoint, "someWebserviceMethod");
Service service = Service.create(serviceName);
service.setHandlerResolver(new MyHandlerResolver());
service.addPort(port, SOAPBinding.SOAP11HTTP_BINDING, endpoint);
Dispatch<SOAPMessage> dispatch = service.createDispatch(port, SOAPMessage.class, Service.Mode.MESSAGE);
MessageFactory factory = MessageFactory.newInstance(SOAPConstants.SOAP_1_1_PROTOCOL);
SOAPMessage request = factory.createMessage();
SOAPEnvelope envelope = request.getSOAPPart().getEnvelope();
envelope.addNamespaceDeclaration("uri", "uri:bar.foo");
request.saveChanges();
SOAPBody body = request.getSOAPBody();
SOAPElement element = body.addChildElement("someRequestElement", "uri");
SOAPElement child = ping.addChildElement("someRequestChild");
SOAPElement text_node = child.addChildElement("someTextNode");
messageType.addTextNode("test text");
request.saveChanges();
System.out.println();
request.writeTo(System.out);
System.out.println();
Object o = dispatch.invoke(request);
System.out.println("ret: " + o.toString());
assertNotNull( o );
}

Related

How to add childElements without prefix to Soap header?

I need to add header elements to a Soap Request, but the child elements inside the header dont have any prefix defined. When I try to add the element without specifing a prefix, this throws a exception.
private SOAPHeader addSecuritySOAPHeader(SOAPMessageContext context) {
SOAPEnvelope envelope = context.getMessage().getSOAPPart().getEnvelope();
envelope.addNamespaceDeclaration("S", "http://schemas.xmlsoap.org/soap/envelope/");
envelope.addNamespaceDeclaration("SOAP-ENV", "http://schemas.xmlsoap.org/soap/envelope/");
SOAPEnvelope header = envelope.getHeader();
// ACTION NODE
SOAPElement action = header.addChildElement("Action");
return header;
}
Last line produces next exception
"com.sun.xml.messaging.saaj.SOAPExceptionImpl: HeaderElements must be namespace qualified"
Heaser i need to create:
<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<S:Header>
<Action xmlns="http://schemas.xmlsoap.org/ws/2004/08/addressing">http://cgbridge.rategain.com/2011A/ReservationService/HotelResNotif</Action>
</S:Header>
..............
</S:Envelope>
If I include any prefix, like S, request fail, server response with "Bad request"
How can i add a "clean" Action node?
Is I add a prefix in action:
SOAPElement action = header.addChildElement("Action","S");
Service responses with a "Bad request" message.
<S:Action xmlns="http://schemas.xmlsoap.org/ws/2004/08/addressing">http://cgbridge.rategain.com/2011A/ReservationService/HotelResNotif</S:Action>
Any help, please?
This should work:
#Test
public void someTest() throws Exception {
MessageFactory messageFactory = MessageFactory.newInstance();
SOAPMessage soapMessage = messageFactory.createMessage();
SOAPEnvelope soapEnvelope = soapMessage.getSOAPPart().getEnvelope();
var header = soapEnvelope.getHeader();
var actionElement = header.addChildElement("Action", "prefix", "http://schemas.xmlsoap.org/ws/2004/08/addressing");
actionElement.addTextNode("http://cgbridge.rategain.com/2011A/ReservationService/HotelResNotif");
ByteArrayOutputStream out = new ByteArrayOutputStream();
soapMessage.writeTo(out);
System.out.println(new String(out.toByteArray()));
}
Prints:
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"><SOAP-ENV:Header><prefix:Action xmlns:prefix="http://schemas.xmlsoap.org/ws/2004/08/addressing">http://cgbridge.rategain.com/2011A/ReservationService/HotelResNotif</prefix:Action></SOAP-ENV:Header><SOAP-ENV:Body/></SOAP-ENV:Envelope>

SOAP request in JAVA. How to pass HEADER

I have a problem with create SOAP request. I have a works request from command line with usage curl
<?xml version="1.0"?>
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<s:Body>
<u:SetVolume xmlns:u="urn:schemas-upnp-org:service:RenderingControl:1">
<InstanceID>0</InstanceID>
<Channel>Master</Channel>
<DesiredVolume>20</DesiredVolume>
</u:SetVolume>
</s:Body>
</s:Envelope> | curl -v -d #- -H 'SOAPAction: "urn:schemas-upnp-org:service:RenderingControl:1#SetVolume"' -H 'content-type: text/xml; charset="utf-8"' http://192.168.0.172:1400/MediaRenderer/RenderingControl/Control
I tried crate equivalent in JAVA
import javax.xml.namespace.QName;
import javax.xml.soap.*;
public class SOAPClientSAAJ {
public static void main(String args[]) {
try {
MessageFactory mf = MessageFactory.newInstance();
SOAPMessage sm = mf.createMessage();
SOAPHeader sh = sm.getSOAPHeader();
SOAPBody sb = sm.getSOAPBody();
sh.detachNode();
QName bodyName = new QName("urn:schemas-upnp-org:service:RenderingControl:1#SetVolume", "SetVolume", "u");
SOAPBodyElement bodyElement = sb.addBodyElement(bodyName);
QName instanceID = new QName("InstanceID");
QName channel = new QName("Channel");
QName volumeLevel = new QName("DesiredVolume");
SOAPElement qInstanceID = bodyElement.addChildElement(instanceID);
SOAPElement qChannel = bodyElement.addChildElement(channel);
SOAPElement qVolumeLevel = bodyElement.addChildElement(volumeLevel);
qInstanceID.addTextNode("0");
qChannel.addTextNode("Master");
qVolumeLevel.addTextNode("30");
sm.writeTo(System.out);
System.out.println();
} catch(Exception e) {
}
}
}
This give me build request
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Body>
<u:SetVolume xmlns:u="urn:schemas-upnp-org:service:RenderingControl:1#SetVolume">
<InstanceID>0</InstanceID>
<Channel>Master</Channel>
<DesiredVolume>30</DesiredVolume>
</u:SetVolume>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
Unfortunately this don't want to works. I supose that problem is in HEADER, but actualy I don't know how to pass them in other way to my request. I also try solution from, but also get error
SOAP request to WebService with java
It seems like you are setting wrong QName. What you are setting is action so apart from that your generated soap envelope is fine I would suggest you to try calling this using SOAP UI.

Spring-WS SOAP header prefix issues

I'm working on a Spring-WS project and I'm trying to consume a certain SOAP service but I'm getting some issues with the request's Header tag.
PS: I ran the same request on SOAP UI and it works perfectly.
This is the code I ran:
JAXBElement<ChangeOtherIDsRequestType> request = createRequestBody();
WebServiceTemplate template = new WebServiceTemplate(marshaller);
template.setDefaultUri(URI);
#SuppressWarnings({ "unchecked" })
JAXBElement<ChangeOtherIDsResponseType> response = (JAXBElement<ChangeOtherIDsResponseType>) template
.marshalSendAndReceive(request, new WebServiceMessageCallback() {
#Override
public void doWithMessage(WebServiceMessage message) {
Instant instant = Instant.now();
SaajSoapMessage saajSoapMessage = (SaajSoapMessage) message;
SOAPMessage soapMessage = saajSoapMessage.getSaajMessage();
SOAPPart soapPart = soapMessage.getSOAPPart();
try {
SOAPEnvelope soapEnvelope = soapPart.getEnvelope();
SOAPHeader soapHeader = soapEnvelope.getHeader();
Name headerElementName = soapEnvelope.createName("Security", "wsse",
"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd");
// Add "Security" soapHeaderElement to soapHeader
SOAPHeaderElement soapHeaderElement = soapHeader.addHeaderElement(headerElementName);
soapHeaderElement.setMustUnderstand(true);
soapHeaderElement.addNamespaceDeclaration("wsu",
"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd");
// Add usernameToken to "Security" soapHeaderElement
SOAPElement usernameTokenSOAPElement = soapHeaderElement.addChildElement("UsernameToken",
"wsse");
// Add username to usernameToken
QName userQname = soapHeaderElement.createQName("Username", "wsse");
SOAPElement userNameSOAPElement = usernameTokenSOAPElement.addChildElement(userQname);
userNameSOAPElement.addTextNode("username");
// Add password to usernameToken
QName passwordQname = usernameTokenSOAPElement.createQName("Password", "wsse");
SOAPElement passwordSOAPElement = usernameTokenSOAPElement.addChildElement("Password");
passwordSOAPElement.setAttribute("Type",
"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText");
passwordSOAPElement.addTextNode("password");
// Add Nonce to usernameToken.
QName nonceQname = soapHeaderElement.createQName("Nonce", "wsse");
SOAPElement nonceSOAPElement = usernameTokenSOAPElement.addChildElement("Nonce");
nonceSOAPElement.setAttribute("EncodingType",
"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary");
// Add Created to usernameToken.
QName createdQname = soapHeaderElement.createQName("Created", "wsu");
SOAPElement createdElement = usernameTokenSOAPElement.addChildElement(createdQname);
createdElement.addTextNode(instant.toString());
} catch (SOAPException | UnsupportedEncodingException e) {
e.printStackTrace();
}
}
});
template.marshalSendAndReceive(URI, request);
This code is supposed to add the Header tag in the request and it serves to authenticate the user through "username" and "password".
The result I'm expecting is the same that is generated by SOAP UI for the same request. The result is this:
<SOAP-ENV:Header>
<wsse:Security SOAP-ENV:mustUnderstand="1" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
<wsse:UsernameToken wsu:Id="UsernameToken-C17C5A667A3F11A8DA153735499778322">
<wsse:Username>username</wsse:Username>
<wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">password</wsse:Password>
<wsse:Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">Tl6jznnHitsSE9F16FWTsw==</wsse:Nonce>
<wsu:Created>2018-09-19T11:03:17.783Z</wsu:Created>
</wsse:UsernameToken>
</wsse:Security>
But the result I'm getting looks like this:
<SOAP-ENV:Header>
<wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" SOAP-ENV:mustUnderstand="1">
<wsse:UsernameToken>
<wsse:Username>username</wsse:Username>
<Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">password</Password>
<Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary"> </Nonce>
<wsu:Created>2018-09-19T11:02:21.918Z</wsu:Created>
</wsse:UsernameToken>
</wsse:Security>
</SOAP-ENV:Header>
Let's see that there are some things that are missing in my result such as:
"wsse" prefix on the tags "Password" and "Nonce".
"UsernameToken" attribute on the "Username" tag.
The value of "Nonce" tag.
I faced some issues when trying to fix these things but I still have some problems:
When I try to add "wsse" prefix to "Username" and "Nonce" tags: the whole "Header" tag dissapears. (I used the same technique as in the "Username" tag)
How does SOAP UI generate the "Nonce" value ?
How does SOAP UI generate the "UsernameToken" value ?
Last resort, is there another way to mock the SOAP UI request ?
Thanks in advance.
You are trying to shoehorn security into your request whereas there is an interceptor and default way of doing that. Configure the Wss4jSecurityInterceptor for your WebServiceTemplate.
Something like the following should do the trick.
#Bean
public Wss4jSecurityInterceptor securityInterceptor() {
Wss4jSecurityInterceptor interceptor = new Wss4jSecurityInterceptor();
interceptor.setSecurementUsername("username");
interceptor.setSecurementPassword("password");
interceptor.setSecurementUsernameTokenNonce(true);
interceptor.setSecurementActions("UsernameToken Timestamp");
return interceptor;
}
Then inject this into your WebServiceTemplate. That should add the needed headers without you having to do anything else. Ideally you would configure the WebServiceTemplate once and reuse it.
#Bean
public WebServiceTemplate webServiceTemplate(Marshaller marshaller) {
WebServiceTemplate wsTemplate = new WebServiceTemplate(marshaller);
wsTemplate.setInterceptors(new ClientInterceptor[]{ securityInterceptor() });
return wsTemplate;
}
Then use the pre configured WebServiceTemplate in your code.

How to add xmlbean document element to soap header spring-ws

I am trying to hit a webservice using spring-ws, but the webservice producer requires a custom element in the soap header. I am very new to webservices, and am having trouble trying to inject the values into the soap header. I am using XMLBeans to transform from xsd to java and also to do the marshaling and unmarshaling. I have constructed the xmlbean document and set all values for the custom header element, I just need to get the document or maybe even just the element attached to that document to be injected into the soap header. Listed below is the wsdl (just header) in soapui (what I used to learn and do initial testing)
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:v1="http://www.ups.com/XMLSchema/XOLTWS/UPSS/v1.0" xmlns:v11="http://www.ups.com/XMLSchema/XOLTWS/Rate/v1.1" xmlns:v12="http://www.ups.com/XMLSchema/XOLTWS/Common/v1.0">
<soapenv:Header>
<v1:UPSSecurity>
<v1:UsernameToken>
<v1:Username>name</v1:Username>
<v1:Password>password</v1:Password>
</v1:UsernameToken>
<v1:ServiceAccessToken>
<v1:AccessLicenseNumber>accesskey</v1:AccessLicenseNumber>
</v1:ServiceAccessToken>
</v1:UPSSecurity>
</soapenv:Header>
I found a solution that works, and isn't much code. I had to ditch using xmlbeans, and just create the elements, but at least the functionality is there and the webservice calls works.
#Override
public void doWithMessage(WebServiceMessage message) throws IOException, TransformerException
{
try
{
SOAPMessage soapMessage = ((SaajSoapMessage)message).getSaajMessage();
SOAPHeader header = soapMessage.getSOAPHeader();
SOAPHeaderElement soapHeaderElement = header.addHeaderElement(new QName("http://www.ups.com/XMLSchema/XOLTWS/UPSS/v1.0", "UPSSecurity", "v1"));
SOAPEnvelope envelope = soapMessage.getSOAPPart().getEnvelope();
envelope.addNamespaceDeclaration("v1", "http://www.ups.com/XMLSchema/XOLTWS/UPSS/v1.0");
SOAPElement usernameToken = soapHeaderElement.addChildElement("UsernameToken", "v1");
SOAPElement username = usernameToken.addChildElement("Username", "v1");
SOAPElement password = usernameToken.addChildElement("Password", "v1");
SOAPElement serviceAccessToken = soapHeaderElement.addChildElement("ServiceAccessToken", "v1");
SOAPElement accessLicenseNumber = serviceAccessToken.addChildElement("AccessLicenseNumber", "v1");
username.setTextContent("username");
password.setTextContent("password");
accessLicenseNumber.setTextContent("key");
}
catch (SOAPException e)
{
e.printStackTrace();
}
}
You can marshal to a SoapHeader's Result, like so:
SoapMessage msg = ...
SoapHeader header = msg.getSoapHeader();
XmlBeansMarshaller marshaller = ...
MyXmlBeansDocument doc = ...
marshaller.marshal(doc, header.getResult());
You can convert an XmlObject (XmlBeans model) to a SOAPElement using the following factory method:
YourModel xmlObject = YourModelDocument.Factory.newInstance().addNewYourModel();
SOAPElement soapElement = SOAPFactory.newInstance()
.createElement((Element) xmlObject.getDomNode());
The XmlObject must be part of a document otherwise getDomNode() will return an XmlFragment rather than an Element.
Once converted to a SOAP element, the XML can be added to most parts of a SOAPMessage using addChildElement(). For example:
SOAPMessage soapMessage = MessageFactory.newInstance().createMessage();
soapMessage.getSOAPBody().addChildElement(soapElement);

Java - Error Non-default namespace can not map to empty URI in XML 1.0 documents

I try to invoke a webservice in Java. Mainly I followed this link and also I solved several problems with google and other questions of stackoverflow. However, I get an error that I can not solve:
Exception in thread "main" javax.xml.ws.WebServiceException: javax.xml.stream.XMLStreamException: Non-default namespace can not map to empty URI (as per Namespace 1.0 # 2) in XML 1.0 documents
I think that the problem is in the xml that I created but I see well.
SOAPUI request:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:acc="http://url/acceso">
<soapenv:Header/>
<soapenv:Body>
<acc:petitionSession>
<acc:codUser/>
<acc:pass>?</acc:pass>
<acc:codOp>?</acc:codOp>
</acc:petitionSession>
</soapenv:Body>
</soapenv:Envelope>
Request I generated:
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:acc="http://url/acceso">
<SOAP-ENV:Header/>
<SOAP-ENV:Body>
<acc:petitionSession>
<acc:codUser/>
<acc:pass>user1</acc:pass>
<acc:codOp>Temp1</acc:codOp>
</acc:petitionSession>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
Code:
Dispatch dispatcher = getDispatcher(wsdlLocation, namespace, serviceName, portName);
dispatcher.getRequestContext().put(BindingProvider.SOAPACTION_USE_PROPERTY, Boolean.TRUE);
dispatcher.getRequestContext().put(BindingProvider.SOAPACTION_URI_PROPERTY, soapActionUri);
...
...
public static SOAPMessage construirMensaje() throws SOAPException, IOException{
MessageFactory factory = MessageFactory.newInstance();
SOAPMessage soapMsg = factory.createMessage();
SOAPPart part = soapMsg.getSOAPPart();
SOAPEnvelope envelope = part.getEnvelope();
envelope.addNamespaceDeclaration("acc", "http://url/acceso");
SOAPHeader header = envelope.getHeader();
SOAPBody body = envelope.getBody();
SOAPBodyElement element = body.addBodyElement(envelope.createName("petitionSession",
"acc", null));
element.addChildElement(body.addBodyElement(envelope.createName("codUser",
"acc", null)));
SOAPBodyElement pass = body.addBodyElement(envelope.createName("pass",
"acc", null));
pass.addTextNode("user1");
element.addChildElement(pass);
SOAPBodyElement codOp = body.addBodyElement(envelope.createName("codOp",
"acc", null));
codOp.addTextNode("Temp1");
element.addChildElement(codOp);
soapMsg.writeTo(System.out);
FileOutputStream fOut = new FileOutputStream("SoapMessage.xml");
soapMsg.writeTo(fOut);
System.out.println();
return soapMsg;
}
Any idea?
Regards.
I find the problem that was in the header that it wasn`t built well. It needed to add:
MimeHeaders headers = soapMessage.getMimeHeaders();
headers.addHeader("SOAPAction", serverURI + "name");

Categories