ColdFusion and ASP.NET Web Service Interoperability, Part III
Yesterday I described some of the problems I was having getting ColdFusion to talk to an ASP.NET web service as well as a brute force solution to the issue. Well my former co-worker Phil Duba left a comment on that post pointing to the follwing article: Notes on Interfacing ColdFusion MX to External Web Services Requiring Complex-within-Complex XML Documents as Input. This article turned out to be the key to my getting this web service call to work using <cfinvoke>.
First let me give a little more detail. Here is a sample of the WSDL I was working with:
<?xml version="1.0" encoding="UTF-8"?>
<wsdl:definitions xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:tm="http://microsoft.com/wsdl/mime/textMatching/" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:mime="http://schemas.xmlsoap.org/wsdl/mime/" xmlns:tns="http://tempuri.org/" xmlns:s="http://www.w3.org/2001/XMLSchema" xmlns:soap12="http://schemas.xmlsoap.org/wsdl/soap12/" xmlns:http="http://schemas.xmlsoap.org/wsdl/http/" targetNamespace="http://tempuri.org/" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/">
<wsdl:types>
<s:schema elementFormDefault="qualified" targetNamespace="http://tempuri.org/">
<s:element name="Company_Add">
<s:complexType>
<s:sequence>
<s:element minOccurs="0" maxOccurs="1" name="txtCompanyName" type="s:string"/>
<s:element minOccurs="0" maxOccurs="1" name="txtCompanyAddress1" type="s:string"/>
<s:element minOccurs="0" maxOccurs="1" name="txtCompanyAddress2" type="s:string"/>
<s:element minOccurs="0" maxOccurs="1" name="txtCompanyZip" type="s:string"/>
<s:element minOccurs="0" maxOccurs="1" name="txtCompanyFax" type="s:string"/>
</s:sequence>
</s:complexType>
</s:element>
<s:element name="Company_AddResponse">
<s:complexType>
<s:sequence>
<s:element minOccurs="1" maxOccurs="1" name="Company_AddResult" type="s:int"/>
<s:element minOccurs="0" maxOccurs="1" name="txtErrorMessage" type="s:string"/>
</s:sequence>
</s:complexType>
</s:element>
</s:schema>
</wsdl:types>
<wsdl:message name="Company_AddSoapIn">
<wsdl:part name="parameters" element="tns:Company_Add"/>
</wsdl:message>
<wsdl:message name="Company_AddSoapOut">
<wsdl:part name="parameters" element="tns:Company_AddResponse"/>
</wsdl:message>
<wsdl:portType name="AdminSoap">
<wsdl:operation name="Company_Add">
<wsdl:input message="tns:Company_AddSoapIn"/>
<wsdl:output message="tns:Company_AddSoapOut"/>
</wsdl:operation>
</wsdl:portType>
<wsdl:binding name="AdminSoap" type="tns:AdminSoap">
<soap:binding transport="http://schemas.xmlsoap.org/soap/http"/>
<wsdl:operation name="Company_Add">
<soap:operation soapAction="http://tempuri.org/Company_Add" style="document"/>
<wsdl:input>
<soap:body use="literal"/>
</wsdl:input>
<wsdl:output>
<soap:body use="literal"/>
</wsdl:output>
</wsdl:operation>
</wsdl:binding>
<wsdl:service name="Admin">
<wsdl:port name="AdminSoap" binding="tns:AdminSoap">
<soap:address location="http://their.service.com/admin.asmx"/>
</wsdl:port>
</wsdl:service>
</wsdl:definitions>
When I first tried to call this web service method my code looked something like the following:
<cfinvoke webservice="http://their.service.com/admin.asmx?wsdl" method="Company_Add" returnvariable="result">
<cfinvokeargument name="txtCompanyName" value="ACME Inc" />
<cfinvokeargument name="txtCompanyAddress1" value="123 Main Street" />
<cfinvokeargument name="txtCompanyAddress2" value="Suite 100" />
<cfinvokeargument name="txtCompanyZip" value="12345" />
<cfinvokeargument name="txtCompanyFax" value="1234567890" />
</cfinvoke>
This resulted in the "Web service operation ... with parameters ... could not be found" error I described yesterday. Looking at the WSDL it looked like Company_Add possibly needed to be a structure, so I also tried:
This didn't work either. I tried several other variants as well, all with similar results.<cfset myCompany = StructNew() />
<cfset myCompany.txtCompanyName = "ACME Inc" />
<cfset myCompany.txtCompanyAddress1 = "123 Main Street" />
<cfset myCompany.txtCompanyAddress2 = "Suite 100" />
<cfset myCompany.txtCompanyZip= "12345" />
<cfset myCompany.txtCompanyFax= "1234567890" />
<cfinvoke webservice="http://their.service.com/admin.asmx?wsdl" method="Company_Add" returnvariable="result">
<cfinvokeargument name="Company_Add" value="myCompany" />
</cfinvoke>
Today however, after reading the above article, I decided to run the WSDL through the WSDL2Java tool to see what I could figure out. After running the utility I took a look at the generated web service interface and this is what I found. (I broke this into multiple lines for easier reading.)
public void company_Add (
java.lang.String txtCompanyName,
java.lang.String txtCompanyAddress1,
java.lang.String txtCompanyAddress2,
java.lang.String txtCompanyZip,
java.lang.String txtCompanyFax,
javax.xml.rpc.holders.IntHolder company_AddResult,
javax.xml.rpc.holders.StringHolder txtErrorMessage
) throws java.rmi.RemoteException;
As you can see, the company_Add method expected to be passed the company_AddResult and txtErrorMessage. Also the method returns void, so I removed the returnvariable attribute from my <cfinvoke> tag and tried the following call:
I was getting closer, no more "Web service operation ... could not be found" error, but now I had a "String index out of range: 0" error being caused by the blank value for txtErrorMessage. The problem I facing was how to get data back from a web service method which returned void. After a little more trial and error I finally came up with the following, which worked:<cfinvoke webservice="http://their.service.com/admin.asmx?wsdl" method="Company_Add">
<cfinvokeargument name="txtCompanyName" value="ACME Inc" />
<cfinvokeargument name="txtCompanyAddress1" value="123 Main Street" />
<cfinvokeargument name="txtCompanyAddress2" value="Suite 100" />
<cfinvokeargument name="txtCompanyZip" value="12345" />
<cfinvokeargument name="txtCompanyFax" value="1234567890" />
<cfinvokeargument name="company_AddResult" value=""/>
<cfinvokeargument name="txtErrorMessage" value="" />
</cfinvoke>
The web service call was successful and the company_AddResult and txtErrorMessage variables held the webservice results after the call.<cfset company_AddResult = CreateObject("java","javax.xml.rpc.holders.IntHolder").init() />
<cfset txtErrorMessage = CreateObject("java","javax.xml.rpc.holders.StringHolder").init() />
<cfinvoke webservice="http://their.service.com/admin.asmx?wsdl" method="Company_Add">
<cfinvokeargument name="txtCompanyName" value="ACME Inc" />
<cfinvokeargument name="txtCompanyAddress1" value="123 Main Street" />
<cfinvokeargument name="txtCompanyAddress2" value="Suite 100" />
<cfinvokeargument name="txtCompanyZip" value="12345" />
<cfinvokeargument name="txtCompanyFax" value="1234567890" />
<cfinvokeargument name="company_AddResult" value="company_AddResult"/>
<cfinvokeargument name="txtErrorMessage" value="txtErrorMessage" />
</cfinvoke>
<cfdump var="#company_AddResult#" />
<cfdump var="#txtErrorMessage#" />
So if you find yourself struggling with a web service call that involves complex inputs or outputs definitely check out the above article and give the WSDL2Java utility a try. They certainly helped me figure this issue out.

One question: does that mean you do not need to add a proxy class??
Thanks
Since the <cfinvoke needs a wsdl file url i have hosted this on my server but when i try to see it in a browser I get a blank page.(I have my wsdl file on CD from the vendor)!!
Any ideas?
Thanks
<port name="soap_port" binding="nsp:binding">
<soap:address location="https://servicename.co.uk/GenericAPIPageController...;
</port>
</service>]
which made me believe that will have to be run through the wsdl file. Probably influenced by a >NET version i did of this webservice althought .NET makes it easier to create stubs!!
Not sure what to try next!!
Any ideas?
Any luck getting a parameter with javax.xml.rpc.holders.BigDecimalHolder to work?