javajunky
01-31-2007, 09:11 AM
HI, I'm running into some problems with the WebServiceClientFactory! Let me start by saying that it works fantastically well when consuming Spring.Net exposed services, but I'm trying to use it to consume Java Spring exposed services. Having read around the forums, I'm lead to believe that if I control the server and client (which I do in this case) then I should be able to solve the problem I'm seeing.
Anyway I digress, I'm exposing a Java WebService, using the JSR181 annotations [pretty much the same as the C# Web Service Attributes].
The WSDL exposed by this service is as follows:
<?xml version="1.0" encoding="UTF-8"?>
<wsdl:definitions targetNamespace="http://mydomain/wsdl" xmlns:ns1="http://mydomain/wsdl/" xmlns:soapenc12="http://www.w3.org/2003/05/soap-encoding" xmlns:tns="http://mydomain/wsdl" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap11="http://schemas.xmlsoap.org/soap/envelope/" xmlns:wsdlsoap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:soapenc11="http://schemas.xmlsoap.org/soap/encoding/" xmlns:soap12="http://www.w3.org/2003/05/soap-envelope">
<wsdl:types>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" attributeFormDefault="qualified"
elementFormDefault="qualified"
targetNamespace="http://mydomain/wsdl">
<xsd:element name="getHelloWorld">
<xsd:complexType/>
</xsd:element>
<xsd:element name="getHelloWorldResponse">
<xsd:complexType>
<xsd:sequence>
<xsd:element maxOccurs="1" minOccurs="1" name="out" nillable="true" type="xsd:string"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
</xsd:schema><xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" attributeFormDefault="qualified"
elementFormDefault="qualified"
targetNamespace="http://mydomain/wsdl/">
<xsd:element name="WebServiceException" type="xsd:string"/>
</xsd:schema> </wsdl:types>
<wsdl:message name="WebServiceException">
<wsdl:part name="WebServiceException" element="ns1:WebServiceException"/>
</wsdl:message>
<wsdl:message name="getHelloWorldRequest">
<wsdl:part name="parameters" element="tns:getHelloWorld"/>
</wsdl:message>
<wsdl:message name="getHelloWorldResponse">
<wsdl:part name="parameters" element="tns:getHelloWorldResponse"/>
</wsdl:message>
<wsdl:portType name="RulesServiceSoap">
<wsdl:operation name="getHelloWorld">
<wsdl:input name="getHelloWorldRequest" message="tns:getHelloWorldRequest"/>
<wsdl:output name="getHelloWorldResponse" message="tns:getHelloWorldResponse"/>
<wsdl:fault name="WebServiceException" message="tns:WebServiceException"/>
</wsdl:operation>
</wsdl:portType>
<wsdl:binding name="RulesServiceHttpBinding" type="tns:RulesServiceSoap">
<wsdlsoap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
<wsdl:operation name="getHelloWorld">
<wsdlsoap:operation soapAction=""/>
<wsdl:input name="getHelloWorldRequest">
<wsdlsoap:body use="literal"/>
</wsdl:input>
<wsdl:output name="getHelloWorldResponse">
<wsdlsoap:body use="literal"/>
</wsdl:output>
<wsdl:fault name="WebServiceException">
<wsdlsoap:fault name="WebServiceException" use="literal"/>
</wsdl:fault>
</wsdl:operation>
</wsdl:binding>
<wsdl:service name="RulesService">
<wsdl:port name="RulesServiceHttpPort" binding="tns:RulesServiceHttpBinding">
<wsdlsoap:address location="http://localhost:8080/smartagent/Rules"/>
</wsdl:port>
</wsdl:service>
</wsdl:definitions>
Which to me looks like a valid Document Literal Wrapped convention'd WSDL document. However the WebServiceClientFactory has problems with it (big'ns).
On the C# side I've declared an interface as follows:
public interface ISimpleRules
{
string getHelloWorld();
}
As you can see this is pretty much the bare minimum (the real WSDL I'm having problems with is actually far more complex ;) )
Having investigated the differences between the WSDL Java is exposing, versus the WSDL Spring.Net is exposing, I can see a couple of differences.
The first problem I've come across is:
threw exception on object creation. ---> System.ApplicationException: No OperationBinding has been found for the method
'System.String getHelloWorld()' with the web service binding 'RulesServiceHttpBinding'.
I eventually tracked this down to the 'GetOperationBinding' method in the WebServiceClientFactory class, specifically I had to replace:
foreach (OperationBinding operationBinding in (((System.Collections.CollectionBase)(binding.Oper ations))))
{
if (operationBinding.Name == methodInfo.Name)
{
if (operationBinding.Input.Name == GetMessageBindingName(methodInfo))
{
return operationBinding;
}
}
}
with
foreach (OperationBinding operationBinding in (((System.Collections.CollectionBase)(binding.Oper ations))))
{
if (operationBinding.Name == methodInfo.Name)
{
return operationBinding;
}
}
[Admittedly I could've set-up the messagebindings as an alternative, but since I've not explicitly renamed my web-method that seemed to be a strange requirement]
The only thing I can spot in the wSDL that could cause this is the WSDL exposed by XFire/Java/Spring defines the wsdl input elements in a form similar to:
<wsdl:input name="getHelloWorldRequest" message="tns:getHelloWorldRequest"/>
Whereas Spring.Net /would/ define the wsdl input element as follows:
<wsdl:input message="tns:getHelloWorldRequest"/>
The crucial difference being Java using the 'name' of the WSDL message, and C# using the 'message' attribute to refer to the correct WSDL message. My understanding of the WS Basic Profile [which could easily be wrong!] says that both are valid, but the name can be discarded, so I would expect the WebServiceClientFactory to be able to cope with this ?
Having 'fixed' this problem, I then run into two problems, the first is the address of the web-service, for some reason I have to replace the code in the ImplementConstructors method
il.Emit(OpCodes.Ldstr, this.wsDescription.RetrievalUrl);
with
il.Emit(OpCodes.Ldstr, this.wsDescription.RetrievalUrl.Substring(0, this.wsDescription.RetrievalUrl.IndexOf("?wsdl") ) );
Quite obviously this isn't a robust solution, but it works to get me to the next problem. Without this, when the SOAP Proxy invokes, it tries to POST to the WSDL address, which fails, or more accurately returns a WSDL document which causes C#'s Serialisation to fall over :(
The next problem is with the request naming; for the Java services I needed to make the following change to the CreateWebMethodAttribute method:
from:
wma.MessageName = operationBinding.Input.Name;
to:
wma.MessageName = operationBinding.Input.Name.Substring(0, operationBinding.Input.Name.IndexOf("Request") );
I just don't understand why I need to do this, as the service exposed by C# uses the same 'convention but appends 'SoapIn', but this doesn't seem to be removed?
After making these changes I can get the WebServiceClientFactory to successfully bind the methods and I can even call them (woot!). However then I have problems with the returned data, and I *cannot* seem to sort this out.
Doc/Lit/Wrapped doesn't specify anything about the name of the 'out' parameter iirc?, my Java services return 'out' as the name of the element. If I change them to be '<methodName>Result' then it works, but I cannot seem to find a way to tell the bindings to use a different element name (one that comes from the WSDL) [which is a bit of a pain to say the least, I've tried a plethora of things, but nothing works fully, mainly centered around adding an XMLElementAttribute to the IL'd method, or by changing the 'ResponseElement' value of the SoapDocumentMethodAttribute.
Any and all ideas would be appreciated, I'm sorry for the length of this message, but since the WebServiceClientFactory seems so close to being able to work with JavaSpringExposed WebServices, it would be a shame not to be able to integrate the two frameworks in this way!! :)
Anyway I digress, I'm exposing a Java WebService, using the JSR181 annotations [pretty much the same as the C# Web Service Attributes].
The WSDL exposed by this service is as follows:
<?xml version="1.0" encoding="UTF-8"?>
<wsdl:definitions targetNamespace="http://mydomain/wsdl" xmlns:ns1="http://mydomain/wsdl/" xmlns:soapenc12="http://www.w3.org/2003/05/soap-encoding" xmlns:tns="http://mydomain/wsdl" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap11="http://schemas.xmlsoap.org/soap/envelope/" xmlns:wsdlsoap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:soapenc11="http://schemas.xmlsoap.org/soap/encoding/" xmlns:soap12="http://www.w3.org/2003/05/soap-envelope">
<wsdl:types>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" attributeFormDefault="qualified"
elementFormDefault="qualified"
targetNamespace="http://mydomain/wsdl">
<xsd:element name="getHelloWorld">
<xsd:complexType/>
</xsd:element>
<xsd:element name="getHelloWorldResponse">
<xsd:complexType>
<xsd:sequence>
<xsd:element maxOccurs="1" minOccurs="1" name="out" nillable="true" type="xsd:string"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
</xsd:schema><xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" attributeFormDefault="qualified"
elementFormDefault="qualified"
targetNamespace="http://mydomain/wsdl/">
<xsd:element name="WebServiceException" type="xsd:string"/>
</xsd:schema> </wsdl:types>
<wsdl:message name="WebServiceException">
<wsdl:part name="WebServiceException" element="ns1:WebServiceException"/>
</wsdl:message>
<wsdl:message name="getHelloWorldRequest">
<wsdl:part name="parameters" element="tns:getHelloWorld"/>
</wsdl:message>
<wsdl:message name="getHelloWorldResponse">
<wsdl:part name="parameters" element="tns:getHelloWorldResponse"/>
</wsdl:message>
<wsdl:portType name="RulesServiceSoap">
<wsdl:operation name="getHelloWorld">
<wsdl:input name="getHelloWorldRequest" message="tns:getHelloWorldRequest"/>
<wsdl:output name="getHelloWorldResponse" message="tns:getHelloWorldResponse"/>
<wsdl:fault name="WebServiceException" message="tns:WebServiceException"/>
</wsdl:operation>
</wsdl:portType>
<wsdl:binding name="RulesServiceHttpBinding" type="tns:RulesServiceSoap">
<wsdlsoap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
<wsdl:operation name="getHelloWorld">
<wsdlsoap:operation soapAction=""/>
<wsdl:input name="getHelloWorldRequest">
<wsdlsoap:body use="literal"/>
</wsdl:input>
<wsdl:output name="getHelloWorldResponse">
<wsdlsoap:body use="literal"/>
</wsdl:output>
<wsdl:fault name="WebServiceException">
<wsdlsoap:fault name="WebServiceException" use="literal"/>
</wsdl:fault>
</wsdl:operation>
</wsdl:binding>
<wsdl:service name="RulesService">
<wsdl:port name="RulesServiceHttpPort" binding="tns:RulesServiceHttpBinding">
<wsdlsoap:address location="http://localhost:8080/smartagent/Rules"/>
</wsdl:port>
</wsdl:service>
</wsdl:definitions>
Which to me looks like a valid Document Literal Wrapped convention'd WSDL document. However the WebServiceClientFactory has problems with it (big'ns).
On the C# side I've declared an interface as follows:
public interface ISimpleRules
{
string getHelloWorld();
}
As you can see this is pretty much the bare minimum (the real WSDL I'm having problems with is actually far more complex ;) )
Having investigated the differences between the WSDL Java is exposing, versus the WSDL Spring.Net is exposing, I can see a couple of differences.
The first problem I've come across is:
threw exception on object creation. ---> System.ApplicationException: No OperationBinding has been found for the method
'System.String getHelloWorld()' with the web service binding 'RulesServiceHttpBinding'.
I eventually tracked this down to the 'GetOperationBinding' method in the WebServiceClientFactory class, specifically I had to replace:
foreach (OperationBinding operationBinding in (((System.Collections.CollectionBase)(binding.Oper ations))))
{
if (operationBinding.Name == methodInfo.Name)
{
if (operationBinding.Input.Name == GetMessageBindingName(methodInfo))
{
return operationBinding;
}
}
}
with
foreach (OperationBinding operationBinding in (((System.Collections.CollectionBase)(binding.Oper ations))))
{
if (operationBinding.Name == methodInfo.Name)
{
return operationBinding;
}
}
[Admittedly I could've set-up the messagebindings as an alternative, but since I've not explicitly renamed my web-method that seemed to be a strange requirement]
The only thing I can spot in the wSDL that could cause this is the WSDL exposed by XFire/Java/Spring defines the wsdl input elements in a form similar to:
<wsdl:input name="getHelloWorldRequest" message="tns:getHelloWorldRequest"/>
Whereas Spring.Net /would/ define the wsdl input element as follows:
<wsdl:input message="tns:getHelloWorldRequest"/>
The crucial difference being Java using the 'name' of the WSDL message, and C# using the 'message' attribute to refer to the correct WSDL message. My understanding of the WS Basic Profile [which could easily be wrong!] says that both are valid, but the name can be discarded, so I would expect the WebServiceClientFactory to be able to cope with this ?
Having 'fixed' this problem, I then run into two problems, the first is the address of the web-service, for some reason I have to replace the code in the ImplementConstructors method
il.Emit(OpCodes.Ldstr, this.wsDescription.RetrievalUrl);
with
il.Emit(OpCodes.Ldstr, this.wsDescription.RetrievalUrl.Substring(0, this.wsDescription.RetrievalUrl.IndexOf("?wsdl") ) );
Quite obviously this isn't a robust solution, but it works to get me to the next problem. Without this, when the SOAP Proxy invokes, it tries to POST to the WSDL address, which fails, or more accurately returns a WSDL document which causes C#'s Serialisation to fall over :(
The next problem is with the request naming; for the Java services I needed to make the following change to the CreateWebMethodAttribute method:
from:
wma.MessageName = operationBinding.Input.Name;
to:
wma.MessageName = operationBinding.Input.Name.Substring(0, operationBinding.Input.Name.IndexOf("Request") );
I just don't understand why I need to do this, as the service exposed by C# uses the same 'convention but appends 'SoapIn', but this doesn't seem to be removed?
After making these changes I can get the WebServiceClientFactory to successfully bind the methods and I can even call them (woot!). However then I have problems with the returned data, and I *cannot* seem to sort this out.
Doc/Lit/Wrapped doesn't specify anything about the name of the 'out' parameter iirc?, my Java services return 'out' as the name of the element. If I change them to be '<methodName>Result' then it works, but I cannot seem to find a way to tell the bindings to use a different element name (one that comes from the WSDL) [which is a bit of a pain to say the least, I've tried a plethora of things, but nothing works fully, mainly centered around adding an XMLElementAttribute to the IL'd method, or by changing the 'ResponseElement' value of the SoapDocumentMethodAttribute.
Any and all ideas would be appreciated, I'm sorry for the length of this message, but since the WebServiceClientFactory seems so close to being able to work with JavaSpringExposed WebServices, it would be a shame not to be able to integrate the two frameworks in this way!! :)