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:

<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>
This didn't work either. I tried several other variants as well, all with similar results.

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:

<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>
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:
<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#" />
The web service call was successful and the company_AddResult and txtErrorMessage variables held the webservice results after the call.

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.

Comments
Critical5's Gravatar Hi, I like this article, simply because i think i may be having the same problem. I am trying to consume a third party web service(.NET) and keeps getting the same message! I am getting my manager to add this WSDL2Java tool to have a go at it.
One question: does that mean you do not need to add a proxy class??

Thanks
# Posted By Critical5 | 7/19/07 12:19 PM
Nathan Mische's Gravatar ColdFusion still needs to create the webservice stub, which it does automatically when you use cfinvoke, cfobject, or createObject(). The WSDL2Java tool just shows you the Java source code used to create that stub. For a little more info check out the related post "WSDL2Java Batch File."
# Posted By Nathan Mische | 7/19/07 1:06 PM
Critical5's Gravatar Hi Nathan,
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
# Posted By Critical5 | 7/20/07 5:23 AM
Nathan Mische's Gravatar Trying to use cfinvoke against a static WSDL will not work, there is nothing implementing the webservice. You _may_ be able to run the WSDL2Java utility against it, but I haven't tried that. I would suggest getting the actual web service URL from the vendor. If they give you a .aspx URL just append ?wsdl to get the WSDL.
# Posted By Nathan Mische | 7/20/07 8:57 AM
Critical5's Gravatar The thing is the url they gave to me displays absolutely nothing or displays an uknown soap error.Now in my wsdl file i have this:[   <service name="name">
      <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!!
# Posted By Critical5 | 7/20/07 10:45 AM
Critical5's Gravatar Sorry for the double entry...When i do <cfhttp> over it and <cfdump>it i get an incompatible browser message!!
Any ideas?
# Posted By Critical5 | 7/20/07 10:46 AM
Phillip Gomer's Gravatar Great post!

Any luck getting a parameter with javax.xml.rpc.holders.BigDecimalHolder to work?
# Posted By Phillip Gomer | 8/6/07 6:07 PM
Nathan Mische's Gravatar @ Phillip - I haven't tried a BigDecimalHolder parameter. Did you try setting up similar to the company_AddResult variable in the last code example above?
# Posted By Nathan Mische | 8/6/07 6:17 PM
Fuseflash's Gravatar Thanks so much!! ;-) Works like a charm.
# Posted By Fuseflash | 6/7/08 6:31 AM
Robs67's Gravatar I know this is three years old, but this post saved me. I pulled my hair out for three days and, after trying this, it freaking worked!

Thanks so much!
# Posted By Robs67 | 4/21/10 12:29 PM
BlogCFC was created by Raymond Camden. This blog is running version 5.8.001.