I am using Apache CXF 3.0.2 for a client and a server. On the server there is a fairly simple web service that accepts various parameters, one of which is a String array:
<xs:complexType name="getThing">
<xs:sequence>
<xs:element minOccurs="0" name="connection" type="tns:connectionID"/>
<xs:element maxOccurs="unbounded" minOccurs="0" name="types" type="xs:string"/>
</xs:sequence>
</xs:complexType>
When the client calls this, it might be that it wishes to pass a single null value for "types" and that's where I encounter problems. The SOAP message from the client looks like this:
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<ns2:getThing xmlns:ns2="http://serverl.url/">
<connection>Connection details....</connection>
<types xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:nil="true"/>
</ns2:getThing>
</soap:Body>
</soap:Envelope>
I think that's correct, but CXF on the server translates that into an array with a single empty string value; not an array containing a null value. This then causes various issues with the server code.
The interface the server implements looks like this?
ThingResult getThing(#WebParam(name = "connection") ConnectionID connection, #WebParam(name = "types") String[] types)
{ code }
And if 'nil="true"' isn't valid, then why is CXF generating it?
A null array works OK (the client omits the element from the message and the server interprets that as a null) but not an array containing a null value. Why is CXF behaving this way and how do I configure it so the server deserialises the SOAP message correctly back to what the client sent?
I have searched everywhere for the answer and am fairly sure I am missing something embarrassingly obvious!
edit: Added code sample
The schema doesn't have a nillable="true" attribute for the types element. Thus, xsi:nil="true" is not a valid value for that element.
I could find no way of making CXF behave (either in WSDL generation or client respect of the generated WSDL).
Instead I had to modify the server code to treat nulls and empty Strings as if they were equivalent.
An bit of a hack that shouldn't be needed, but seems to work.
Related
I am using CXF JAXWS Client for retrieving some information from an external web services. So, basically, we have .xsd and .wsdl files from the external and try to implement our ws consumer by generating the client stubs using cxf-codegen-plugin. We cannot change the .xsd and .wsdl files. Everything works fine, we are able to send and retrieve soap messges from the external services.
However, when I look into the soap request message from the log, I find a lot of unused namespaces in the soap (body) message sent from our ws client.
Below is an example
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:aheaderns="http://xxx.xxx.xxx.xxx/header.xsd">
<soap:Header>
...
</soap:Header>
<soap:Body>
<ns7:requestQuery xmlns:ns15="http://xxx.xxx.xxx.xxx/AAA" xmlns:ns14="http://xxx.xxx.xxx.xxx/BBB" xmlns:ns13="http://xxx.xxx.xxx.xxx/DDD" xmlns:ns12="http://xxx.xxx.xxx.xxx/CCC" xmlns:ns11="http://xxx.xxx.xxx.xxx/EEE" xmlns:ns10="http://xxx.xxx.xxx.xxx/FFF" xmlns:ns9="http://xxx.xxx.xxx.xxx/GGG" xmlns:ns8="http://xxx.xxx.xxx.xxx/HHH" xmlns:ns7="http://xxx.xxx.xxx.xxx/III" xmlns:ns6="http://xxx.xxx.xxx.xxx/JJJ" xmlns:ns5="http://xxx.xxx.xxx.xxx/KKK">
<ns7:sample>
<ns7:type>A</ns7:type>
<ns7:ref>1</ns7:ref>
</ns7:sample>
</ns7:requestQuery>
</soap:Body>
Is there any way to remove the unused namespaces from the outbound soap message sending from the client?
You could move your client proxy classes to the same package and map it to the proposed namespace specified in the wsdl.
Put this code into the package-info.java class:
#XmlSchema(xmlns = { #XmlNs(prefix = "ns1", namespaceURI = "http://xxx.xxx.xxx") },
namespace = "http://xxx.xxx.xxx")
package clinet.sample.proxy;
import javax.xml.bind.annotation.XmlNs;
Hope this helps.
After digging into this problem for quite sometime, I found the solution. However, I think my solution is not to solve the problem at the root cause but it works for now.
Basically, I use XSLTOutInterceptor from CXF to apply the XSL to the out message. However, the default PRE_STEAM phase is not working for me. Then, I decide to extend the XSLTOutInterceptor so as to change its PHASE to WRITE. So that, the unused namespaces will be removed during the writing of the protocol message before sending it out.
The XSL I use is from how to remove unused namespaces from source xml
By the way, I still have no idea why those unused namespaces have been generated. If anyone could give me a suggestion, I would appreciate it.
Thanks :)
I created a web service using:
Apache Axis 2 CodeGen Wizard v.1.6.2 (Binding: ADB)
Eclipse Juno
Tomcat 7
Java 6
The Service returns a Custom Java Object (DataBean) back to the client, but I stumbled upon an exception in the client code:
org.apache.axis2.AxisFault: org.apache.axis2.databinding.ADBException: Unexpected subelement {schemaTargetNs}message
From what I have researched, over n over again ... I think this is a very common problem but haven't yet got a conclusive answer as to what should be done to rectify it.
Some posts on this and other forums state that the WSDL needs to be modified (some name space), or the client stub needs modification. Some even state that there is a bug in the ADB. It was surely a bug in earlier versions of Axis but there are many posts in the mail-archives stating that the bug was fixed. Those mailing-archives were related to earlier versions of Axis2.
Now my questions are:
Is it still a bug ?
What exactly needs to be changed in the WSDL or the Client stub ?
What is worth mentioning is that I created a similar web service which returns a "String" back to the client. It works fine ! So, it fails when a complex data type is involved.
There was some information on Apache's website, under the heading "Known Limitations"...
It reads: "ADB is meant to be a 'Simple' databinding framework and was not meant to compile all types of schemas. The following limitations are the most highlighted.
Complex Type Extensions and Restrictions."
Is that the problem ?
The following is the snippet from the WSDL file which might be of some interest to you...
<wsdl:types>
<xs:schema xmlns:ax26="http://mywebservice/xsd" attributeFormDefault="qualified" elementFormDefault="qualified" targetNamespace="schemaTargetNs">
<xs:import namespace="http://mywebservice/xsd"/>
<xs:element name="getMsg">
<xs:complexType>
<xs:sequence>
<xs:element minOccurs="0" name="reqData" nillable="true" type="ax25:DataBean"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="getMsgResponse">
<xs:complexType>
<xs:sequence>
<xs:element minOccurs="0" name="return" nillable="true" type="ax25:DataBean"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
<xs:schema attributeFormDefault="qualified" elementFormDefault="qualified" targetNamespace="http://mywebservice/xsd">
<xs:complexType name="DataBean">
<xs:sequence>
<xs:element minOccurs="0" name="message" nillable="true" type="xs:string"/>
<xs:element minOccurs="0" name="name" nillable="true" type="xs:string"/>
</xs:sequence>
</xs:complexType>
</xs:schema>
</wsdl:types>
Now how do I fix the problem ? Should I include some other code snippets here?
"Unexpected subelement" means the message received by the receiver contained an XML element that the receiver wasn't expecting. "{schemaTargetNs}message" is the name of the unexpected element that it encountered. In other words, the sender sent an invalid message to the receiver.
The sender may have included an element which it wasn't supposed to.
The sender may have left out a mandatory element.
The sender may have put the elements in the wrong order.
The sender may have sent a completely incorrect message.
If the server issued the exception that you reported, then the client sent an invalid message to the server. If the client issued the exception, then the error was in the response from the server to the client.
if the xsd(wsdl) is correct with xml request o response is because the problem is the order of xml elements. One posible solution is generate your axis2 client with -Eosv option. that work for me.
The code generated by CodeGen (from WSDL) for the Java Object (bean) that I was using, expected a different namespace for the fields in the bean. Somehow an incorrect namespace was present in the code generated by Axis. I fixed the namespace to reflect what it should have been and everything worked fine. I can see people still answering this question so thought I would re-post my solution here (have already posted this in response to Kenster's solution). As none of the solutions posted before me finding the solution worked, I did not accept any answer.
Look into your .xsd file. Sort your xs elements alphabetically below your <xs:extension base=...>. That will fit your needs.
When I checked the axis code, i found the following
if( new javax.xml.namespace.QName("http://someurl","someElementName").equals(reader.getName()) )
this is where error happens,
. equals() method of QName checks for localPart & namespaceURI
. but reader.getName() has no namespace URI set and hence the error happend
I changed all the if-check from
if( new javax.xml.namespace.QName("http://someurl","someElementName").equals(reader.getName()) )
to
if( new javax.xml.namespace.QName("someElementName").equals(reader.getName()) )
and it worked fine for me
This error can be kind of misleading. After I modified the WSDL and added a new mandatory element, I created my client. Than this error appeared. The solution was, that I forgot to fill this element in one method of the my web service. If this error appears, also check if your mandatory elements are filled within the server.
in my case the Web Service is sending elements in different order than the sequence that is on the xsd. I am modifying the stub right now so order does not matter, because I have no chance of altering the Web Service.
I had the same problem. When the base64binary overcame 16k limit the parser starts giving the error, Substantially it stops reading the content after 16k so obviously the rest of the document seems corrupted.
My problem was that i was using com.sun.xml.stream.XMLReaderImpl.
Removing
<dependency>
<groupId>com.sun.xml.stream</groupId>
<artifactId>sjsxp</artifactId>
</dependency>
and adding
<dependency>
<groupId>org.codehaus.woodstox</groupId>
<artifactId>wstx-asl</artifactId>
</dependency>
solved my problem (so wstx as suggested before was working)
This describes a problem I had and the solution found, which I did not see discussed elsewhere (except the Background material).
Background – What Should Work (but May Not)
Let’s assume a web service defined by “https://mysite.com/?wsdl”, whose functions are to be called by a Windows client “myclient.exe”. In Visual Studio, you provide a “Service Reference” (The problem described here also manifests with the older-style “Web Reference”.)
Suppose you have more than 1 machine (mysite and mysite2) exposing the same web services. It is desirable to just change the client’s choice of endpoint at runtime to point to the correct machine, without having to update the Service Reference. This could be done in 3 ways:
Edit the deployed myclient.exe.config (or its source at compile time in VS, app.config), in particular:
<system.serviceModel>
...
<client>
<endpoint address="https://mysite2.com/?wsdl" …/>
</client>
</system.serviceModel>
Arrange your client code so that it reads in or selects the endpoint address, and sets it in the constructor (where “MyWebService” is the name of the Service Reference, and MyWebServiceBinding is defined in app.config), e.g. in C#:
public static EndpointAddress remoteAddress =
new EndpointAddress("https://mysite2.com/?wsdl");
public static BasicHttpBinding basicBinding =
new BasicHttpBinding("MyWebServiceBinding"); //Your binding type may differ
public MyWebService.myWebServicePortTypeClient mws =
new MyWebService.myWebServicePortTypeClient(basicBinding, remoteAddress);
Do like (2), but instead of in the constructor, set the endpoint later, say, during the first call to the service:
private void SetMyEndpointAddress(MyWebService.myWebServicePortTypeClient mws)
{
mws.Endpoint.Address = new
System.ServiceModel.EndpointAddress("https://mysite2.com/?wsdl");
}
All of these can still quietly fail if the specifics of the service WSDL are problematic (probably not generated in a routine manner by a MS server; PHP NuSoap in my case). Assuming you have control over the server WSDL, the next section explains what to look for.
Removing Unnecessary URLs from the WSDL
The WSDL is a long document with overall structure shown next. If any of the phrases pointed to here by "<===" contains a URL (e.g., https://mysite.com/...), change its generation process so that this is no longer true.
<definitions ...
xmlns:tns="soap/MyWebServices" <===
...
targetNamespace="soap/MyWebServices"> <===
<types>
<xsd:schema elementFormDefault="qualified"
targetNamespace="soap/MyWebServices"> <===
...
</xsd:schema>
</types>
[multiple <message>...</message> sections, ones for each defined function.]
<portType name="myWebServicesPortType">...</portType>
<binding name="MyWebServicesBinding" type="tns:myWebServicesPortType">...</binding>
<service name="MyWebServices">...</service>
</definitions>
In addition, within the <binding>…</binding> section, the “soapAction” for each function should be only the function name. It should not include a URL prefix (followed by “#” and the function). Typical function:
<operation name="getMyData">
<soap:operation
soapAction="getMyData" <===
style="document"/>
<input><soap:body use="literal" namespace=""/></input>
<output><soap:body use="literal" namespace=""/></output>
</operation>
The ONLY place where a URL to the particular machine should appear is in the <service> section:
<service name="MyWebServices">
<port name="MyWebServicesPort" binding="tns:MyWebServicesBinding">
<soap:address location="https://mysite.com/?wsdl"/>
</port>
</service>
I'm writing a SOAP client. I'm using gSOAP version 2.7.17 and I have to stick to that version as the server is using this one and I cannot modify it as it is already running in the field.
When I do my SOAP call, I always receive empty answer! However, when I sniff the network, the XML answer seems correct:
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:Supervisor="urn:Supervisor"
xmlns:ActiveLogin="urn:ActiveLogin" xmlns:TechLogin="urn:TechLogin"
xmlns:UMSLogin="urn:UMSLogin">
<SOAP-ENV:Body SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<unitCallResponse>
<szServerName>UMS_DEV_LANCC2 (2)</szServerName>
<dResponse>cUNITRETURNCODE_RESTARTIMMEDIATE</dResponse>
<dDetail>cBla</dDetail>
</unitCallResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
Here is a simplified code snippet about how I perform the SOAP call with the proxy generated with soapcpp2:
UMSLogin::unitCallResponse response; // Response from SOAP
UMSLogin::UMSLoginProxy* m_soapProxy = // The SOAP proxy
new UMSLogin::UMSLoginProxy(SOAP_IO_KEEPALIVE, SOAP_IO_KEEPALIVE);
callRes = m_soapProxy->unitCall(m_eUnitType, serial, m_dInterfaceVersion,
xl2CallTypeToUmsLoginCallType(m_pSoapEndPoint->getCallType()),
xl2CommTypeToUmsLoginCommType(m_pSoapEndPoint->getComType()),
response);
logDebug(LOG_COM_DEVICE, "SOAP call done to %s, SOAP code %i, central code %i",
response.szServerName.c_str(), callRes, response.dResponse);
m_soapProxy->soap_close_socket();
m_soapProxy->destroy();
delete m_soapProxy;
The actual output:
SOAP call done to , SOAP code 0, central code 0
When I expect
SOAP call done to UMS_DEV_LANCC2 (2), SOAP code 0, central code 5
/* 5 -> cUNITRETURNCODE_RESTARTIMMEDIATE */
There was an old implementation that was working previously, this must be working! Where am I wrong? Did anyone has already had this kind of problem? Any pointer is strongly appreciated!
EDIT:
Problem was "solved" by using an earlier version of SOAP (2.7.14) without changing a single line of code from the original version of my program. However, I have the feeling I miss something important again! I should probably tweak a little my header file giving the SOAP "definitions" to make things working ...
Looking at your code you declare response and then reference the elements of it as response.dResponse. This seems to imply that response is a struct rather than a pointer to a struct so after you call m_soapProxy->unitCall the version of response that you then report on hasn't changed, only the copy in m_soapProxy->unitCall will have changed.
I'd try tracing this call through in the debugger and see what is actually happening in m_soapProxy->unitCall. BTW is it you code or generated by GSOAP?
I'm calling a .Net Web Service which takes a complex type as a parameter (using KSoap2). I've done this before successfully so I decided to just copy my old code and paste it into a new method, replacing some code with code relevant to the current web service of course.
The only real difference I can see is that the URL to the new web service's .asmx is different.
The thing is I keep getting the following error:
(I get the following when SoapEnvelope.VER11)
org.xmlpull.v1.XmlPullParserException: expected: END_TAG {http://schemas.xmlsoap.org/soap/envelope/}Body (position:END_TAG </{http://schemas.xmlsoap.org/soap/envelope/}soap:Fault>#1:338 in java.io.InputStreamReader#199cb474)
(And the following when SoapEnvelope.VER12)
org.xmlpull.v1.XmlPullParserException: expected: START_TAG {http://www.w3.org/2001/12/soap-envelope}Envelope (position:START_TAG <{http://schemas.xmlsoap.org/soap/envelope/}soap:Envelope>#1:207 in java.io.InputStreamReader#5b209e72
Why would the error message change like this depending on the version of SOAP I use?
I also tried printing out my envelope with HttpTransport.requestDump and I get the following:
xmlns:v="http://schemas.xmlsoap.org/soap/envelope/">
<v:Header />
<v:Body>
<postMessage xmlns="http://www.magnatech.com/" id="o0" c:root="1">
<myMessage i:type="n0:Message" xmlns:n0="http://www.magnatech.com/">
<messageContent i:type="d:string">HEY</messageContent>
<userName i:type="d:string">TEST USERNAME</userName>
<agentName i:type="d:string">TEST AGENT NAME</agentName>
<userID i:type="d:string">0</userID>
<agentID i:type="d:string">0</agentID>
<fromAgent i:type="d:string">false</fromAgent>
<messageID i:type="d:string"></messageID>
<accountNumber i:type="d:string">TEST ACCOUNT</accountNumber>
</myMessage>
</postMessage>
</v:Body>
Which looks fine, I actually did the same thing with the envelope from my other "WORKING" web service call and they are the same, except for the element names of course.
And finally, here is the output from HttpTransport.responseDump:
<?xml version="1.0" encoding="utf-8"?><soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"><soap:Body><soap:Fault><faultcode>soap:Client</faultcode><faultstring>Could not deserialize Soap message</faultstring></soap:Fault></soap:Body></soap:Envelope>
So obviously there is something wrong with the envelope as I'm sending it, yet I see nothing wrong with it.
What could be the problem? I've tried everything possible and am running out of ideas.
Any help at all will be greatly appreciated.
Thanks