PDA

View Full Version : WebServiceClientFactory on JAX-WS exposed services


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!! :)

Bruno Baia
02-05-2007, 02:12 PM
Hi,

Thanks for this great feedback :)

The main problem comes from here :

<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>



[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]

You right, but have you tried to specify messagebindings, this will avoid other changes you had to do :

<object id="myService" type="Spring.Web.Services.WebServiceClientFactory, Spring.Services">
<property name="ServiceUrl" value="..."/>
<property name="ServiceInterface" value="..."/>
<property name="MessageBindings">
<dictionary>
<entry key="getHelloWorld" value="getHelloWorldRequest" />
</dictionary>
</property>
</object>



Can you try something for me ?
To generate the proxy with Visual Studio (or wsdl.exe utility) to see how it handles this (if he can) and copy paste here the proxy code.


Cheers,
Bruno

javajunky
02-05-2007, 03:53 PM
Hi,

Thanks for this great feedback :)
Happy to help, all progression is good ;)


You right, but have you tried to specify messagebindings, this will avoid other changes you had to do :

<object id="myService" type="Spring.Web.Services.WebServiceClientFactory, Spring.Services">
<property name="ServiceUrl" value="..."/>
<property name="ServiceInterface" value="..."/>
<property name="MessageBindings">
<dictionary>
<entry key="getHelloWorld" value="getHelloWorldRequest" />
</dictionary>
</property>
</object>


I'm sure that would work fine for the message-bindings (but not the retrieval-url or the 'out' parameter element name) but I don't want to have to do this for all of my interfaces, when the WSDL does conform to Doc/lit/wrapped [as I understand it], I(and one of my colleagues) did try fixing it, but we discovered that the ServiceDescription object didn't contain enough information to do this, it wouldn't let us look at the Complex Types that made up the Wsdl message parameter parts?


Can you try something for me ?
To generate the proxy with Visual Studio (or wsdl.exe utility) to see how it handles this (if he can) and copy paste here the proxy code.


Sure, it handles it without problems!, this is what I've used as my template for figuring out which 'CustomAttributes' to apply to the proxy object to fix my 'out' parameter issues:

//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:2.0.50727.42
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------

//
// This source code was auto-generated by Microsoft.VSDesigner, Version 2.0.50727.42.
//
#pragma warning disable 1591

namespace ConsoleApplication1.localhost {
using System.Diagnostics;
using System.Web.Services;
using System.ComponentModel;
using System.Web.Services.Protocols;
using System;
using System.Xml.Serialization;


/// <remarks/>
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.Web.Services", "2.0.50727.42")]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Web.Services.WebServiceBindingAttribute(Nam e="RulesServiceHttpBinding", Namespace="http://www.mydomain.com/wsdl")]
public partial class RulesService : System.Web.Services.Protocols.SoapHttpClientProtoc ol {

private System.Threading.SendOrPostCallback getHelloWorldOperationCompleted;

private bool useDefaultCredentialsSetExplicitly;

/// <remarks/>
public RulesService() {
this.Url = global::ConsoleApplication1.Properties.Settings.De fault.ConsoleApplication1_localhost_RulesService;
if ((this.IsLocalFileSystemWebService(this.Url) == true)) {
this.UseDefaultCredentials = true;
this.useDefaultCredentialsSetExplicitly = false;
}
else {
this.useDefaultCredentialsSetExplicitly = true;
}
}

public new string Url {
get {
return base.Url;
}
set {
if ((((this.IsLocalFileSystemWebService(base.Url) == true)
&& (this.useDefaultCredentialsSetExplicitly == false))
&& (this.IsLocalFileSystemWebService(value) == false))) {
base.UseDefaultCredentials = false;
}
base.Url = value;
}
}

public new bool UseDefaultCredentials {
get {
return base.UseDefaultCredentials;
}
set {
base.UseDefaultCredentials = value;
this.useDefaultCredentialsSetExplicitly = true;
}
}

/// <remarks/>
public event getHelloWorldCompletedEventHandler getHelloWorldCompleted;

/// <remarks/>
[System.Web.Services.Protocols.SoapDocumentMethodAt tribute("", RequestNamespace = "http://www.mydomain.com/wsdl", ResponseNamespace = "http://www.mydomain.com/wsdl", Use = System.Web.Services.Description.SoapBindingUse.Lit eral, ParameterStyle = System.Web.Services.Protocols.SoapParameterStyle.W rapped)]
[return: System.Xml.Serialization.XmlElementAttribute("out", IsNullable=true)]
public string getHelloWorld() {
object[] results = this.Invoke("getHelloWorld", new object[0]);
return ((string)(results[0]));
}

/// <remarks/>
public void getHelloWorldAsync() {
this.getHelloWorldAsync(null);
}

/// <remarks/>
public void getHelloWorldAsync(object userState) {
if ((this.getHelloWorldOperationCompleted == null)) {
this.getHelloWorldOperationCompleted = new System.Threading.SendOrPostCallback(this.OngetHell oWorldOperationCompleted);
}
this.InvokeAsync("getHelloWorld", new object[0], this.getHelloWorldOperationCompleted, userState);
}

private void OngetHelloWorldOperationCompleted(object arg) {
if ((this.getHelloWorldCompleted != null)) {
System.Web.Services.Protocols.InvokeCompletedEvent Args invokeArgs = ((System.Web.Services.Protocols.InvokeCompletedEve ntArgs)(arg));
this.getHelloWorldCompleted(this, new getHelloWorldCompletedEventArgs(invokeArgs.Results , invokeArgs.Error, invokeArgs.Cancelled, invokeArgs.UserState));
}
}

/// <remarks/>
public new void CancelAsync(object userState) {
base.CancelAsync(userState);
}

private bool IsLocalFileSystemWebService(string url) {
if (((url == null)
|| (url == string.Empty))) {
return false;
}
System.Uri wsUri = new System.Uri(url);
if (((wsUri.Port >= 1024)
&& (string.Compare(wsUri.Host, "localHost", System.StringComparison.OrdinalIgnoreCase) == 0))) {
return true;
}
return false;
}
}

/// <remarks/>
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.Web.Services", "2.0.50727.42")]
public delegate void getHelloWorldCompletedEventHandler(object sender, getHelloWorldCompletedEventArgs e);

/// <remarks/>
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.Web.Services", "2.0.50727.42")]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
public partial class getHelloWorldCompletedEventArgs : System.ComponentModel.AsyncCompletedEventArgs {

private object[] results;

internal getHelloWorldCompletedEventArgs(object[] results, System.Exception exception, bool cancelled, object userState) :
base(exception, cancelled, userState) {
this.results = results;
}

/// <remarks/>
public string Result {
get {
this.RaiseExceptionIfNecessary();
return ((string)(this.results[0]));
}
}
}
}

#pragma warning restore 1591


Thanks for taking the time to respond to my message
- Ciaran

Bruno Baia
02-06-2007, 12:14 AM
Thanks Ciaran !

I need to do some work on WebServiceClientFactory, so I'll take care of this at the same time.

- Operation Binding Input/Output name.
- Web Service url.
- Custom output parameter names.

+

- Load wsdl from a file
- Asynchronous methods
- Authentication aspects (windows, client certificates, etc...)


- Bruno

Bruno Baia
02-15-2007, 09:37 AM
Hi,

I resolved some issues.
Remains Asynchronous methods support and Authentication aspects .

I modified WebServiceClientFactory to work with WSDLs that conform to WS-I (Web Services Interoperability) spécifications.

I also renamed WebServiceUrl property to WebServiceUri that takes a Spring resource (http://www.springframework.net/doc-latest/reference/html/resources.html) as value instead of a simple url to allow references to local WSDL files.


<object id="calculatorService" type="Spring.Web.Services.WebServiceClientFactory, Spring.Services">
<property name="ServiceUri" value="assembly://Spring.Calculator.ClientApp/Spring.Calculator.ClientApp.Config.WebServices2005/calculatorService.wsdl"/>
<property name="ServiceInterface" value="Spring.Calculator.Interfaces.IAdvancedCalculator, Spring.Calculator.Contract"/>
</object>


<object id="webCalculatorService" type="Spring.Web.Services.WebServiceClientFactory, Spring.Services">
<property name="ServiceUri" value="http://localhost:1643/Spring.Calculator.Web.2005/calculatorServiceWeaved.asmx"/>
<property name="ServiceInterface" value="Spring.Calculator.Interfaces.IAdvancedCalculator, Spring.Calculator.Contract"/>
</object>


<object id="webCalculatorService" type="Spring.Web.Services.WebServiceClientFactory, Spring.Services">
<property name="ServiceUri" value="file:\\~\calculatorService.wsdl"/>
<property name="ServiceInterface" value="Spring.Calculator.Interfaces.IAdvancedCalculator, Spring.Calculator.Contract"/>
</object>



Please give a try to the latest nightly build (http://www.springframework.net/downloads/nightly/) and let me know.


Bruno

javajunky
03-06-2007, 09:46 AM
Hi, sorry for the delay in getting back to you, I'm using NHibernate1.2 so I couldn't get a truly recent 'nightly build', however I did use the nightly build at the location ( http://www.springframework.net/downloads/Spring.Data.NHibernate12/Spring.Data.NHibernate12-20070226-0202.zip )

Which I believe is dated *after* your post?

Unfortunately this doesn't work for me :( I receive the following error, when I try to inject the proxied singleton into a spring object :

[InvalidOperationException: The datatype 'http://myns:Foo' is missing.]
System.Xml.Serialization.XmlSchemaImporter.FindDat aType(XmlQualifiedName name, TypeFlags flags) +724511
System.Xml.Serialization.XmlSchemaImporter.FindTyp e(XmlQualifiedName name, TypeFlags flags) +319
System.Xml.Serialization.XmlSchemaImporter.ImportT ype(XmlQualifiedName name, Type desiredMappingType, Type baseType, TypeFlags flags, Boolean addref) +139
System.Xml.Serialization.XmlSchemaImporter.ImportE lementType(XmlSchemaElement element, String identifier, Type desiredMappingType, Type baseType, String ns) +65
System.Xml.Serialization.XmlSchemaImporter.ImportE lement(XmlSchemaElement element, String identifier, Type desiredMappingType, Type baseType, String ns, Boolean topLevelElement) +219
System.Xml.Serialization.XmlSchemaImporter.ImportE lementMember(XmlSchemaElement element, String identifier, CodeIdentifiers members, CodeIdentifiers membersScope, INameScope elementsScope, String ns, Boolean repeats, Boolean& needExplicitOrder, Boolean allowDuplicates, Boolean allowUnboundedElements) +195
System.Xml.Serialization.XmlSchemaImporter.ImportG roupMembers(XmlSchemaParticle particle, String identifier, CodeIdentifiers members, CodeIdentifiers membersScope, INameScope elementsScope, String ns, Boolean groupRepeats, Boolean& mixed, Boolean& needExplicitOrder, Boolean allowDuplicates, Boolean allowUnboundedElements) +794
System.Xml.Serialization.XmlSchemaImporter.ImportG roup(XmlSchemaGroupBase group, String identifier, CodeIdentifiers members, CodeIdentifiers membersScope, INameScope elementsScope, String ns, Boolean mixed, Boolean& needExplicitOrder, Boolean allowDuplicates, Boolean groupRepeats, Boolean allowUnboundedElements) +137
System.Xml.Serialization.XmlSchemaImporter.ImportT ypeMembers(XmlSchemaType type, String typeNs, String identifier, CodeIdentifiers members, CodeIdentifiers membersScope, INameScope elementsScope, Boolean& needExplicitOrder, Boolean order, Boolean allowUnboundedElements) +187
System.Xml.Serialization.XmlSchemaImporter.ImportM embersType(XmlSchemaType type, String typeNs, String identifier) +206
System.Xml.Serialization.XmlSchemaImporter.ImportT ype(XmlSchemaComplexType type, String typeNs, String identifier, Type desiredMappingType, Type baseType, TypeFlags flags) +210
System.Xml.Serialization.XmlSchemaImporter.ImportE lementType(XmlSchemaElement element, String identifier, Type desiredMappingType, Type baseType, String ns) +218
System.Xml.Serialization.XmlSchemaImporter.ImportE lement(XmlSchemaElement element, String identifier, Type desiredMappingType, Type baseType, String ns, Boolean topLevelElement) +219
System.Xml.Serialization.XmlSchemaImporter.ImportE lement(XmlQualifiedName name, Type desiredMappingType, Type baseType) +194
System.Xml.Serialization.XmlSchemaImporter.ImportM embersMapping(XmlQualifiedName name) +77
Spring.Web.Services.SoapHttpClientProxyTypeBuilder .GetMembersMapping(String messageName, MessagePartCollection messageParts, SoapBodyBinding soapBodyBinding, SoapBindingStyle soapBindingStyle) +368
Spring.Web.Services.SoapHttpClientProxyTypeBuilder .ApplyCustomAttributes(MethodBuilder builder, MethodInfo targetMethod) +568
Spring.Proxy.AbstractProxyTypeBuilder.ImplementInt erface(TypeBuilder typeBuilder, IProxyMethodBuilder proxyMethodBuilder, Type intf, Type targetType, Boolean proxyVirtualMethods) +372
Spring.Proxy.AbstractProxyTypeBuilder.ImplementInt erface(TypeBuilder typeBuilder, IProxyMethodBuilder proxyMethodBuilder, Type intf, Type targetType) +46
Spring.Web.Services.SoapHttpClientProxyTypeBuilder .BuildProxyType() +350
Spring.Web.Services.WebServiceClientFactory.Genera teProxy() +193
Spring.Web.Services.WebServiceClientFactory.GetObj ect() +49
Spring.Objects.Factory.Support.AbstractObjectFacto ry.GetObjectForSharedInstance(String name, Object instance) +349


Interestingly http://myns:Foo *is* defined as an xsd:ComplexType accessible from the WSDL. However interestingly, the XSD containing this ComplexType definition is 'included' into the WSDL document being queried as follows:

<xsd:include schemaLocation="common.xsd"/>


The WSDL hasn't changed, and is still accepted by my local 'JavaWebServiceClientFactory' object that I created based on my previous findings.

My suspicion would be that the XMLSchemaImporter isn't recursively following includes & imports ? Is this possible?

Also, unrelated. It would be extremely useful for me to be able to 'defer' construction of the http client proxy until the first method access on the proxied object, rather than constructing the proxy when the singleton instance is first injected into the collaborating spring object. The reasons for this are two fold :
i) Constructing the proxy from a remote WSDL is (reasonably) slow, my Web tier has a fair number of spring objects which in turn rely on a large number of Web Services, injected into them. Many of these objects require multiple services, but only need perhaps one web-service to be constructed before the user can use the system effectively. By being able to defer construction of the proxy until the first time an interface method is called I should be able to dramatically reduce my startup time.
ii) There are certain scenarios in my application where I want to be able to programatically handle the fact that a service "isn't up yet, and re-try. As the proxy is constructed when the spring definition is initialised, if the service isn't there then my application context fails to initialise properly.

There is a precedent for this approach already, in Java Spring, we have the class ( JaxRpcPortProxyFactoryBean (http://www.springframework.org/docs/api/org/springframework/remoting/jaxrpc/JaxRpcPortProxyFactoryBean.html))
Which defines a property 'setLookupServiceOnStartup' Which can be used to toggle this specific behaviour :)

I appreciate you may have already fixed these issues, if so please feel free to let me know <g>

Bruno Baia
03-06-2007, 11:21 AM
My suspicion would be that the XMLSchemaImporter isn't recursively following includes & imports ? Is this possible?
[QUOTE]
Problably.
Is this possible to send me (pm or forum) your WSDL file as an example ?

[QUOTE=javajunky;5705]
Also, unrelated. It would be extremely useful for me to be able to 'defer' construction of the http client proxy until the first method access on the proxied object, rather than constructing the proxy when the singleton instance is first injected into the collaborating spring object. The reasons for this are two fold :
i) Constructing the proxy from a remote WSDL is (reasonably) slow, my Web tier has a fair number of spring objects which in turn rely on a large number of Web Services, injected into them. Many of these objects require multiple services, but only need perhaps one web-service to be constructed before the user can use the system effectively. By being able to defer construction of the proxy until the first time an interface method is called I should be able to dramatically reduce my startup time.
ii) There are certain scenarios in my application where I want to be able to programatically handle the fact that a service "isn't up yet, and re-try. As the proxy is constructed when the spring definition is initialised, if the service isn't there then my application context fails to initialise properly.

A solution is to set lazy-int attribute to true. But if your object is injected into another singleton object, it will be evaluated.
Another solution is to download the WSDL file and save it in local (as a file or an assembly resource) and configure WebServiceClientFactory to use it so you don't have to connect to get the WSDL.


<object id="calculatorService" type="Spring.Web.Services.WebServiceClientFactory, Spring.Services">
<property name="ServiceUri" value="assembly://Spring.Calculator.ClientApp/Spring.Calculator.ClientApp.Config.WebServices2005/calculatorService.wsdl"/>
<property name="ServiceInterface" value="Spring.Calculator.Interfaces.IAdvancedCalculator, Spring.Calculator.Contract"/>
</object>


<object id="webCalculatorService" type="Spring.Web.Services.WebServiceClientFactory, Spring.Services">
<property name="ServiceUri" value="file:\\~\calculatorService.wsdl"/>
<property name="ServiceInterface" value="Spring.Calculator.Interfaces.IAdvancedCalculator, Spring.Calculator.Contract"/>
</object>



Bruno

javajunky
03-06-2007, 12:23 PM
Hi Bruno,
Many thanks for your prompt reply, unfortunately our wsdl documents and schemas etc. are commercially sensitive, so I've modified them as a basic test case, that I don't think changes semantically from what we're doing, but it does leave a rather strange looking empty schema element. I've Pm'd you with a ZIP file containing the relevant documents.

A solution is to set lazy-int attribute to true. But if your object is injected into another singleton object, it will be evaluated.
Another solution is to download the WSDL file and save it in local (as a file or an assembly resource) and configure WebServiceClientFactory to use it so you don't have to connect to get the WSDL.


This isn't an ideal solution for me as the WSDL that I'm querying is dynamically generated by the server process (a Java web Service [Xfire/Java-WS] ). As it happens this interface is extremely unlikely to change in this particular case so I can properly work around the issue in this way, but it doesn't help me with the performance problems, I think this is a really useful feature, although I appreciate it could be difficult to work in ? [Although not that difficult to someone as smart as you surely ? ;) ]

edit: In fact on reflection it probably won't work for me :( as the end-point address defined in the WSDL will differ between development and various live deployments, and there's no logical way to re-write the wsdl 'just before' spring brings it in :( eek!

alex.dolin
03-26-2007, 12:04 PM
I modified WebServiceClientFactory to work with WSDLs that conform to WS-I (Web Services Interoperability) spécifications.


Hi. I'm using WebServiceClientFactory. After upgrading to the latest nightly build I got this exception:

NotSupportedException: WebServiceClientFactory only supports document-literal and rpc-literal SOAP messages to conform to WS-I Basic Profiles.

Is there any way to connect to web-service which uses encoded messsages?

Bruno Baia
03-28-2007, 03:03 PM
@javajunky :

I updated WebServiceClientFactory to support nested XSD files.Please give it a try and let me know.

After downloading the latest nightly build (http://www.springframework.net/downloads/nightly/) (since Spring.NET-20070327-1956.zip), have a look to this thread :
Latest changes : WebServiceClientFactory and WebServiceProxyFactory merged (http://forum.springframework.net/showthread.php?t=2260)


About performance issues, an existing solution is to use the lazy-init attribute.
But if your object is injected into another one that is not "lazy-init" it will be loaded...
I don't see another solution atm, the FactoryObject needs to return something that implements the service interface you provided.
I'll investigate.


-Bruno

Bruno Baia
03-30-2007, 03:37 PM
Hi,

Hi. I'm using WebServiceClientFactory. After upgrading to the latest nightly build I got this exception:

NotSupportedException: WebServiceClientFactory only supports document-literal and rpc-literal SOAP messages to conform to WS-I Basic Profiles.

Is there any way to connect to web-service which uses encoded messsages?
I got this working before, but the implementation was only supporting simple web services from .NET world.
I've updated it to make it more robust but now it only supports SOAP messages that conform to WS-I basic profiles.

What you can do is generate the .NET proxy class for the web service and then use the ProxyType to wrap the proxy and code against the interface.
See this thread for some examples :
Latest changes : WebServiceClientFactory and WebServiceProxyFactory merged (http://forum.springframework.net/showthread.php?t=2260)


Bruno