Ben Nadel
On User Experience (UX) Design, JavaScript, ColdFusion, Node.js, Life, and Love.
I am the chief technical officer at InVision App, Inc - a prototyping and collaboration platform for designers, built by designers. I also rock out in JavaScript and ColdFusion 24x7.
Meanwhile on Twitter
Loading latest tweet...
Ben Nadel at cf.Objective() 2009 (Minneapolis, MN) with:

AxisFault: ColdFusion Web Services And XML Data Types

By Ben Nadel on
Tags: ColdFusion

A little while ago, I was helping someone debug a ColdFusion, SOAP-Based, web service error. Web services in general are not easy to debug and the problem was not too obvious, so I thought I'd post it here. To narrow down the problem, I set up a sample web service with just enough logic to duplicate the error. Here is the remote ColdFusion component:

  • <cfcomponent
  • output="false"
  • hint="I am a test web service component.">
  •  
  • <cffunction
  • name="Test"
  • access="remote"
  • returntype="string"
  • output="false"
  • hint="I take an XML argument.">
  •  
  • <!--- Define arguments. --->
  • <cfargument
  • name="Data"
  • type="xml"
  • required="true"
  • hint="I am the xml data."
  • />
  •  
  • <!--- Define the local scope. --->
  • <cfset var LOCAL = {} />
  •  
  • <!--- Get the value. --->
  • <cfset LOCAL.ValueNodes = XmlSearch(
  • XmlParse( ARGUMENTS.Data ),
  • "//value"
  • ) />
  •  
  • <!--- Get the value. --->
  • <cfset LOCAL.Value = LOCAL.ValueNodes[ 1 ].XmlText />
  •  
  • <!--- Return value. --->
  • <cfreturn LOCAL.Value />
  • </cffunction>
  •  
  • </cfcomponent>

As you can see from the code, all this remote web service does it take an XML string, parse it, search for the value node, and return the text of that node. To invoke the web service, I simply created the XML data and posted it to the web service using CFInvoke:

  • <!--- Build our WSDL Url. --->
  • <cfset strURL = (
  • "http://localhost" &
  • GetDirectoryFromPath( CGI.script_name ) &
  • "WS.cfc?wsdl"
  • ) />
  •  
  •  
  • <!--- Build our XML data. --->
  • <cfsavecontent variable="strData">
  • <data>
  • <value>Test</value>
  • </data>
  • </cfsavecontent>
  •  
  •  
  • <!--- Invoke the web service. --->
  • <cfinvoke
  • returnvariable="strResponse"
  • webservice="#strURL#"
  • method="Test"
  • refreshwsdl="true">
  •  
  • <cfinvokeargument name="Data" value="#Trim( strData )#" />
  • </cfinvoke>
  •  
  •  
  • <!--- Output the response. --->
  • <cfoutput>
  • #strResponse#
  • </cfoutput>

Nothing out of the ordinary going on here; but, when I invoke the web service, I get this ColdFusion error:

Cannot perform web service invocation Test. The fault returned when invoking the web service operation is:<br> <pre>AxisFault faultCode: {http://schemas.xmlsoap.org/soap/envelope/}Server.userException faultSubcode: faultString: coldfusion.xml.rpc.CFCInvocationException: [coldfusion.xml.XmlProcessException : An error occured while Parsing an XML document.] faultActor: faultNode: faultDetail: {http://xml.apache.org/axis/}stackTrace:coldfusion.xml.rpc.CFCInvocationException: [coldfusion.xml.XmlProcessException : An error occured while Parsing an XML document.]

If you look at the error, you'll see that it was thrown "while Parsing an XML document." This parse is performed in the XmlSearch() method call. So, why is the XmlParse() method failing? After all, if it made it to the XmlSearch() method, then it means that the CFArgument of type, "XML," was fine.

The problem is that "XML" means something different in ColdFusion depending on where it is used. If it's used completely within the ColdFusion application, XML is the standard XML that we are used to dealing with - either an XML string or the ColdFusion XML document. Once, we leave ColdFusion and venture into the web service world, however, XML appears to be a different beast. If we add a CFDump to our remote access web service:

  • <!--- Output ARGUMENTS. --->
  • <cfdump
  • var="#ARGUMENTS#"
  • format="html"
  • output="#ExpandPath( './dump.htm' )#"
  • />

... we get the following output:

 
 
 
 
 
 
XML Web Service Data Type Implicit Convesion. 
 
 
 

As you can see, the XML document that came through wasn't the XML data string that we passed in; it was the Xerces XML document (which, if I had to guess is the Java object that ColdFusion XML documents decorate). It seems that the due to the XML data type, ColdFusion tried to implicitly convert the XML string into an XML document. Because this is no longer an XML string, but a complex XML Java object, XmlParse() doesn't know what to do and throws the above error.

To get around this, all we have to do is change our CFArgument data type from "XML" to "String":

  • <!--- Define arguments. --->
  • <cfargument
  • name="Data"
  • type="string"
  • required="true"
  • hint="I am the xml data."
  • />

This gets rid of the error completely and allows the web service to return.

So, the moral of the story I guess is that while we can easily swap between data types in ColdFusion, once we have to interact with the outside world, we have to be really careful how we define our data. To the outside world, an XML object is NOT the same thing as the string representation of an XML object. It's actually not the same thing in ColdFusion either, but, due to run-time automatic data conversions of a typeless language, we rarely have to worry about it.



Reader Comments

If I replace your cfsavecontent with this:

<cfxml variable="strData">
<data>
<value>Test</value>
</data>
</cfxml>

I get the same error, and it is definitely a XML object being passed.

Reply to this Comment

@Stefan,

If you switch to CFXML, you have to remove the XmlParse() call. Then, it should work fine.

Reply to this Comment

Ah, that is true. But the data is still not considered being XML -
<cfreturn isXML(ARGUMENTS.Data)> returns 'NO' in the cfc, but when tested before being sent in, it says 'YES'. Some strangeness there.

(I put that in before the XmlSearch so I never actually got there)

Reply to this Comment

IsXmlDoc() also returns false. But using your original code, with cfsavecontent and type="xml", it works if you skip the XMLParse().

If I send in a CFXML created object and use type="any", isXML returns true, if i use type="xml", isXML returns false. If I skip XMLParse, the XMLSearch works in all cases, so it does not seem to need XMLParse()?. Seems very strange for me. XMLSearch() has no problem working with a xmlString instead of a xmlDoc.

I guess the bottom line is that you can use type="xml" as long as you do not do XMLParse on the argument before using it, because it turns it into an XML object automatically, regardless if you pass a XML string or a XMLDoc.

Reply to this Comment

Yes. But what I meant is that the error you got was because you tried to do XMLParse on something that was turned into a xml object when you set the type to XML. An argument of type xml can be an xml string or a xml object. So rather than changing the argument type to string, I think it is better to skip xmlParse.

The strange thing, as I see it, is when you invoke the webservice as a component, then it does not to parse it. On the contrary, it seem to un-parse it.

If you <cfreturn arguments.data /> from the cfc
and dump the return value, you will see a xmlDoc from the webservice and a XML string from the component, regardless if you send in a string or a xmlDoc.

To me, it seem like a bug that the XML object you send into a component is turned into a XML string. I would not expect that.

But I am easily confused :)

Reply to this Comment

WebService data types can be a pain in the ass. If you cast everything to a string you can always inside your own application cast them to the appropriate data type. Especially if you connect to other platforms the casting can be problematic, I've had troubles with .NET webservices vs. Coldfusion webservices (which are java thing) but obviously if everything are strings they can casted to the approriate data type.

Reply to this Comment

@Marko,

Yeah, strings are easy to parse back and forth. When it comes to SOAP, generally, I prefer to actually write the XML myself and pass that via CFHTTP so there are no surprises. I've just had trouble with complex type casting so many times in the past.

Reply to this Comment

Ben,
I'm trying to get the data from .asmx?wsdl
but all I see are the methods, just like your screen shot. I'm the consumer of web service so I can do anything with changing the datatype if they're using xml instead of string.
So how can I get my data? I've been searching anywhere with no success

Reply to this Comment

@Mega,

At the end of the day, SOAP is just an XML based standard. If you need to, you can create your own SOAP XML and post that using something like CFHTTP rather than trying to create a web service object. This will give you ultimate control over the way the data is passed. The WSDL file should define the overall XML data structure.

Reply to this Comment

can you show me a sample of calling your webservices using cfhttp. when i call it using cfhttp i get an error.

thanks

Reply to this Comment

@Charles,

What kind of data are you trying to post? I have a number of CFHTTP examples, some posting XML, some posting files; I also have some project stuff that will post just about anything (CFHTTPSession.cfc).

Reply to this Comment

@Charles,

No problem. If you have any issues with the given approach, just drop a comment in the appropriate blog entry.

Reply to this Comment

one more question...

when calling a cfc using http, how will i pass the method to call.

this is how i am trying to us this.

wsProcessRequestorXMLTest.cfc?wsdl

it this right or am i missing something?

Thanks
Charles

Reply to this Comment

@Charles,

When using CFHTTP, you probably won't need to use the WSDL stuff - that is only for SOAP. CFCs can be accessed via SOAP or REST. With SOAP, the method is declared as part of the XML packet.

To call via REST, remove the WSDL and just put the method as part of the Query string, or a URL CFHTTPParam.

<cfhttp url="myCFC.cfc?method=hello&foo=bar" />

... or:

<cfhttp url="myCFC.cfc">
<cfhttpparam type="url" name="method" value="hello" />
<cfhttpparam type="url" name="foo" value="bar" />
</cfhttp>

Does that help?

Reply to this Comment

I'm having difficulty similar to Charles. I am trying to pass in xml via cfhttp, but it's not being seeing by the cfc. The xmlCall is data I've created using CFXML (and passes the IsXML test).

<CFHTTP RESULT="callAns" URL="APIatron.cfc?method=test_call">
<CFHTTPPARAM TYPE="header" NAME="Content-Type" VALUE="text/xml">
<CFHTTPPARAM TYPE="body" NAME="data" ENCODED="no" VALUE="#xmlCall#">
</CFHTTP>

I put a <CFRETURN ARGUMENTS> in the service, and all I get back is an empty result. It's not seeing anything I pass in it seems.

I use this approach to communicate w/ most of the APIs we communicate w/, and the service we are building is going to be mostly consumed by non-CF systems.

Reply to this Comment

Never mind...I found your "Posting XML With ColdFusion..." post,and saw the use of GetHttpRequestData() to get that body info. Once I threw taht at the top of the component in an THIS scope, it worked fine.

Thanks for the pre-answer.

Reply to this Comment

@Paul,

Yeah, you'd have to pass them through as type="url" in order for them to show up in the CFFunction arguments. Glad you got it sorted out.

Reply to this Comment

Hi,

I'm calling a coldfusion webservice from javascript using xmlhttp request something like this

var request ="<soap:Envelope xmlns:soap='http://schemas.xmlsoap.org/soap/envelope/'><soap:Body><getprojects_DCDB xmlns='http://tempuri.org/'><project_status>Completed</project_status></getprojects_DCDB></soap:Body></soap:Envelope>";
alert(request);
xmlhttp.open("POST", "mycoldfusionwebservice.cfc?WSDL", true);
xmlhttp.setRequestHeader("Content-Type","text/xml; charset=utf-8");
xmlhttp.setRequestHeader("SOAPAction","mycoldfusionwebservice.cfc?WSDL");
xmlhttp.setRequestHeader("Content-Length", request.length);
xmlhttp.onreadystatechange=function() {
if (xmlhttp.readyState==4) {
alert('4');
showListItems(xmlhttp.responseText);
}

I'm getting tyhe following error as u have explained above....

faultString: coldfusion.xml.rpc.CFCInvocationException: [coldfusion.xml.XmlProcessException : An error occured while Parsing an XML document

How to change the xml data type to string?

Kindly help on htis

Reply to this Comment

@Sathish,

Are you actually posting the XML data? I don't see where you're posting the data in your snippet. But, if you look at my example, I am not changing the data type on the *calling* side; rather, I am changing the data type on the web service side, which is where the error was occurring.

You might take a look at this post:

http://www.bennadel.com/blog/1853-Posting-XML-SOAP-Requests-With-jQuery.htm

I demonstrate an approach to posting SOAP from jQuery.

Reply to this Comment

Thnak you for your reply.

I am actually posting the xml(SOAP Envelope),

var request ="<soap:Envelope xmlns:soap='http://schemas.xmlsoap.org/soap/envelope/'><soap:Body><getprojects_DCDB xmlns='http://tempuri.org/'><project_status>Completed</project_status></getprojects_DCDB></soap:Body></soap:Envelope>";
alert(request);
xmlhttp.open("POST", "mycoldfusionwebservice.cfc?WSDL", true);
xmlhttp.setRequestHeader("Content-Type","text/xml; charset=utf-8");
xmlhttp.setRequestHeader("SOAPAction","mycoldfusionwebservice.cfc?WSDL");
xmlhttp.setRequestHeader("Content-Length", request.length);
xmlhttp.onreadystatechange=function() {
if (xmlhttp.readyState==4) {
alert('4');
showListItems(xmlhttp.responseText);
}
xmlhttp.send(request);

I have gone through your link
http://www.bennadel.com/blog/1853-Posting-XML-SOAP-Requests-With-jQuery.htm

My problem is, I am developing a windows 7 gadget to show data from cold fusion. Basically my requirement is to call a coldfusion webservice and get data and show it in my gadget.
So If I have to create a proxy where should I create in my case. Should I have to do it inside the gadget?

Waiting for your reply

Reply to this Comment

@Sathish,

You only have to create a proxy for cross-domain posting (as I had in my example). If you are just posting to the same domain, that shouldn't be necessary, as far as I know.

Your SOAPAction value looks a little suspect.

Out of curiosity, why are you using SOAP to access a ColdFusion web service? Using standard URL parameters is typically much easier.

Reply to this Comment

I have a kind of similar problem I think.
I am passing some XML as one of the arguments to my web service, it works fine on CF9 locally and CF8, but on live CF9 server it will not accept the XML and the web service fails with error below.

I have tried passing the XML as an XMLDoc and as a string, same error. Still scratching my head on this.

Message Web service operation checkDBServers with parameters {XML={<?xml version="1.0" encoding="utf-8"?> a big chunk of my xml here ... cannot be found. at coldfusion.xml.rpc.ServiceProxy.invoke(ServiceProxy.java:148) at coldfusion.runtime.CfJspPage._invoke(CfJspPage.java:2301) at coldfusion.tagext.lang.InvokeTag.doEndTag(InvokeTag.java:443) at cfindex2ecfm1362036512.runPage(C:\inetpub\wwwroot\cfmonitor\index.cfm:19) at coldfusion.runtime.CfJspPage.invoke(CfJspPage.java:231) at coldfusion.tagext.lang.IncludeTag.doStartTag(IncludeTag.java:416) at coldfusion.filter.CfincludeFilter.invoke(CfincludeFilter.java:65) at coldfusion.filter.ApplicationFilter.invoke(ApplicationFilter.java:363) at coldfusion.filter.RequestMonitorFilter.invoke(RequestMonitorFilter.java:48) at coldfusion.filter.MonitoringFilter.invoke(MonitoringFilter.java:40) at coldfusion.filter.PathFilter.invoke(PathFilter.java:87) at coldfusion.filter.ExceptionFilter.invoke(ExceptionFilter.java:70) at coldfusion.filter.BrowserDebugFilter.invoke(BrowserDebugFilter.java:74) at coldfusion.filter.ClientScopePersistenceFilter.invoke(ClientScopePersistenceFilter.java:28) at coldfusion.filter.BrowserFilter.invoke(BrowserFilter.java:38) at coldfusion.filter.NoCacheFilter.invoke(NoCacheFilter.java:46) at coldfusion.filter.GlobalsFilter.invoke(GlobalsFilter.java:38) at coldfusion.filter.DatasourceFilter.invoke(DatasourceFilter.java:22) at coldfusion.filter.CachingFilter.invoke(CachingFilter.java:53) at coldfusion.CfmServlet.service(CfmServlet.java:200) at coldfusion.bootstrap.BootstrapServlet.service(BootstrapServlet.java:89) at jrun.servlet.FilterChain.doFilter(FilterChain.java:86) at coldfusion.monitor.event.MonitoringServletFilter.doFilter(MonitoringServletFilter.java:42) at coldfusion.bootstrap.BootstrapFilter.doFilter(BootstrapFilter.java:46) at jrun.servlet.FilterChain.doFilter(FilterChain.java:94) at jrun.servlet.FilterChain.service(FilterChain.java:101) at jrun.servlet.ServletInvoker.invoke(ServletInvoker.java:106) at jrun.servlet.JRunInvokerChain.invokeNext(JRunInvokerChain.java:42) at jrun.servlet.JRunRequestDispatcher.invoke(JRunRequestDispatcher.java:286) at jrun.servlet.ServletEngineService.dispatch(ServletEngineService.java:543) at jrun.servlet.jrpp.JRunProxyService.invokeRunnable(JRunProxyService.java:203) at jrunx.scheduler.ThreadPool$ThreadThrottle.invokeRunnable(ThreadPool.java:428) at

Reply to this Comment

@Snake,

Where is it breaking? Is the actual invoking of the remote method what is breaking it? Or after the method invocation (ie. is it the internal processing that is breaking it). If you remove all processing from within your remote method (or, add a Return statement right after your arguments), does it still error?

Reply to this Comment

it actually turned out to be a Red Herring Ben, I deleted the web service from the cfadmin and it then worked.
So that is 3 hours of my life wasted debugging a bug that did not exist :-)

As a side note to anyone reading, I believe this was sort of caching issue. This is a remote monitoring app and as such the web service runs on multiple servers, and I think I had the same error for another reason earlier when running it on localhost, so after I uploaded it to remote machine it created the stubs with the same error from the previous invocation even though I had fixed that error.
Perhaps CF detects same WSDL schemas and duplicates existing stubs or something ?

Reply to this Comment

Hi Ben,
I'm having issues with the Web Services call where instead of getting data back, I see the definition of an object (somewhat similar to the original output you've listed above).

This is my call:

<cfscript>
ws=createObject("webservice","http://10.1.1.243/main.asmx?wsdl");
wsResult=ws.RunBackgroundSearchStr(xmlBody,usr);
</cfscript>

<CFOUTPUT>
<CFDUMP var="#wsResult#">
</CFOUTPUT>

And the Result:

object of com.mycomp.RunBackgroundSearchStrResponseRunBackgroundSearchStrResult

Methods hashCode (returns int)
equals (returns boolean)
getSerializer (returns interface org.apache.axis.encoding.Serializer)
getDeserializer (returns interface org.apache.axis.encoding.Deserializer)
getTypeDesc (returns org.apache.axis.description.TypeDesc)
get_any (returns [Lorg.apache.axis.message.MessageElement;)
set_any (returns void)
getClass (returns java.lang.Class)
wait (returns void)
wait (returns void)
wait (returns void)
notify (returns void)
notifyAll (returns void)
toString (returns java.lang.String)

Any suggestions?
Thanks,
Igor

Reply to this Comment

Post A Comment

You — Get Out Of My Dreams, Get Into My Comments
Live in the Now
Oops!
Comment Etiquette: Please do not post spam. Please keep the comments on-topic. Please do not post unrelated questions or large chunks of code. And, above all, please be nice to each other - we're trying to have a good conversation here.