AxisFault: ColdFusion Web Services And XML Data Types

Posted June 15, 2009 at 10:23 AM

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:

 Launch code in new window » Download code as text file »

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

 Launch code in new window » Download code as text file »

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

 Launch code in new window » Download code as text file »

  • <!--- 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":

 Launch code in new window » Download code as text file »

  • <!--- 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.

Download Code Snippet ZIP File

Post Comment  |  Ask Ben  |  Permalink  |  Other Searches  |  Print Page



Learning ColdFusion 9 - ColdFusion 9 tutorials, samples, examples, demos

Reader Comments

Jun 15, 2009 at 1:26 PM // reply »
19 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.


Jun 15, 2009 at 1:29 PM // reply »
6,516 Comments

@Stefan,

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


Jun 15, 2009 at 1:43 PM // reply »
19 Comments

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)


Jun 15, 2009 at 1:58 PM // reply »
6,516 Comments

@Stefan,

Try IsXmlDoc(). It's meant for already-parsed documents.


Jun 15, 2009 at 3:00 PM // reply »
19 Comments

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.


Jun 15, 2009 at 3:02 PM // reply »
6,516 Comments

@Stefan,

I believe XmlSearch() can take either an XML string or an XML document.


Jun 15, 2009 at 7:48 PM // reply »
19 Comments

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


Jun 16, 2009 at 4:09 AM // reply »
8 Comments

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.


Jun 16, 2009 at 8:32 AM // reply »
6,516 Comments

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


Jul 10, 2009 at 2:15 PM // reply »
3 Comments

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


Jul 10, 2009 at 3:15 PM // reply »
6,516 Comments

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


Post Comment  |  Ask Ben

Recent Blog Comments
Nov 20, 2009 at 11:32 PM
Five Months Without Hungarian Notation And I'm Loving It
I've used headless camel case for years for not only ColdFusion variables, but also SQL tables and fields... pretty much everything involving code. I also subscribe to the "don't abbreviate and clea ... read »
Nov 20, 2009 at 11:00 PM
Five Months Without Hungarian Notation And I'm Loving It
@Marcel, Yeah, I always err on the side of longer but more readable variable names. As for the camel casing of CF methods and the headless camel casing of custom items, I get around this by always ... read »
Nov 20, 2009 at 10:56 PM
Five Months Without Hungarian Notation And I'm Loving It
I use the following and love it: my.namespace.MyComponents.functionMethodsOrUDF() CONSTANT_VALUES_OR_PROPERTIES One thing I always try is to CamelCaseBuiltInColdFusionFunctions() so others can tell ... read »
Nov 20, 2009 at 5:38 PM
Learning ColdFusion 8: CFImage Part I - Reading And Writing Images
Hi Ben, Great article. I've been looking around to see if ColdFusion image engine can programatically create the following "wrap around" effect: http://www.creativepro.com/article/photoshop-s-she ... read »
Nov 20, 2009 at 5:35 PM
Maintaining ColdFusion Sessions Across SMS Text Message Requests Without Cookies
@Dave: I talked to Gert he suggested: <cfhttp method="get" url="http://{some cf website}" result="stuff" addtoken="yes" /> Note the addition of cfhttp attribute addtoken. That should persist y ... read »
Nov 20, 2009 at 5:23 PM
Maintaining ColdFusion Sessions Across SMS Text Message Requests Without Cookies
@Todd, Ahh, gotcha, yeah that makes sense. ... read »
Nov 20, 2009 at 5:17 PM
Maintaining ColdFusion Sessions Across SMS Text Message Requests Without Cookies
Ben, sorry if I didn't make this clear. You can make it work like that if you want, just put <cfset session.foo = 1> (and <cfset application.foo = 1>) in your OnRequestStart() and it reve ... read »