Sep 05

Webservices are one reason ColdFusion doesn’t scale well

I stumbled across a post on Managed Mayhem about ColdFusion scaling well and then ran across a code issue that contrasts the enterprisey-scalyness of ColdFusion. Before going into that, though, let me give you a little background about myself:

I have been developing CF applications for over ten solid years. Almost exclusively ColdFusion. I have worked with c, c#, java, PHP, perl, classic ASP and ASP.net, but I generally don’t work with these technologies as my day job unless there’s something weird or non-standard going on. That being said, I’m not a CF weenie (I didn’t drink the Allaire/Macromedia/Adobe Kool-aid), either – I am a real software developer, thankyouverymuch.

So, this gives you some background – Anyway, I have always like CF, but there are massive gaping holes in it that just grate me the wrong way and really need to be brought to a head in order to possibly be fixed. Without fixing them, I don’t think CF can really be considered a truly enterprise solution, no matter how hard other people want to look away, stick their fingers in their ears and pretend to not listen. Continuing on with my story:

We’ve got an application that calls a ColdFusion function as a web service that returns a custom data type. Those are the main points here. The Data type, since it’s custom, had to be defined through a cfcomponent, and looks something like this:

<cfcomponent name="custom">
  <cfproperty name="variableone" type="string" required="yes" />
  <cffunction name="init" access="public" output="false" returntype="custom">
    <cfreturn this />
  </cffunction>
</cfcomponent>

Pretty standard stuff – things in this code have changed to protect the innocent, but this should be a fair representation of the code.

Now, the calling function (in CF) that returns this is simply:

<cffunction name="returnCustom" returnType="custom">
  <cfscript>
    var returnMe = createObject("component", "custom").init();
    return returnMe;
  </cfscript>
</cffunction>

Calling this as a web service, you get all sorts of XML returned to you defining a complex type, and everything works as it should, and we’re all happy.

Suddenly, one day (today, on Friday,) without any code changes and without outputting any errors in any of your CF logs, the program consuming your web service starts throwing errors like this:

coldfusion.xml.rpc.CFCInvocationException: [java.lang.ClassCastException : custom]

And I should note that the program consuming this web service is not a ColdFusion program – it’s written in another language. So, to summarize: your CF application server starts throwing errors to your end-users, but is not showing them in your logs and your non-CF application are throwing errors you’ve seen CF throw before. The only glimmer of evidence is that your application consuming the service is returning the above error about a CFCInvocationException.

So, what do you do? Well, my debugging skills are probably better than they should be, so the first thing I did was start looking around in the logfiles. Nothing. Not one line showing that exceptions were being thrown by CF. Thanks for the help, ColdFusion! As important as the void logs would be something like the debugging message that I got from the other application. Now, “java.lang.ClassCastException” is telling me that Java is choking on casting some class in some manner, but it’s not exactly telling me much more than that, except that the class is probably “custom”. So, I’m assuming that the underlying JVM is choking on the custom returntype that we’ve defined in our CF webservice? There’s not much more to go on other than that. What would be next?

Calling the web service through a browser was the next step. Oddly enough, calling it directly worked just fine. This is certainly odd – there’s no log, calling it directly works, and yet the consumer is still getting an error. Maybe it’s a cache issue on the consumer side? Alright; I install the program on another machine and go test it – the same thing is happening. There’s no way this has been cached on the consumer side. What about the ColdFusion server side?

The server itself have “Save Class Files” or “Trusted Cache” checked, but I will go ahead and clear the cache anyway. After clearing the cache, everything works just fine. The consumer of the web service is now returning the proper results and working as expected (furthering the point that it’s not a client-side caching issue) – of course, I don’t have log entries showing successfully processed CF pages, but I don’t care, because what should work, now works.

So, this is what I’m telling you: You can have a perfectly fine web service in ColdFusion that stops working for no reason and won’t start working again until the cache is cleared (you could probably also re-save the web service without making any changes – ColdFusion might pick up on this and reload it’s cache, but I wouldn’t bet on it) – This is so amazingly frustrating when you’ve got a web service that has thousands of requests per hour – it certainly makes me think twice about implementing new web services in CF for something that’s supposed to be an enterprise application :-\

Jul 21

Computing Levenshtein Distance with ColdFusion

Need to compare strings using ColdFusion? Yeah, I did, too. so, I read up a bit on Levenshtein Distance and then wrote myself a function that will compute the Levenshtein Distance of two strings.

Now, Technically, Levenshtein Distance is the distance of each character in a string, but I didn’t need that, and what I actually wanted to have was a way to compare two things like, say, an email subject line – I wanted the distance between strings based on the words in the strings to compare, rather than the distance between the characters in the strings to compare.

This function could certainly be rewritten to compare two strings in the strict Levenshtein Distance sense, but I will leave that as an exercise to the readers :-)

<cffunction name="computePseudoLevenshtein" returnType="numeric" hint="computes levenshtein distance based on words in base string compared to words in compare string">
		<cfargument name="baseString" type="string" required="yes" />
		<cfargument name="compareString" type="string" required="yes" />
		<cfscript>
			var baseStringLen = listLen(arguments.baseString, " ");
			var compareStringLen = listLen(arguments.compareString, " ");
			var longestListLen = 0;
			var thisMatrix = ArrayNew(2);

			var thisCost = 0;
			var cellAbove = 0;
			var cellLeft = 0;
			var cellDiag = 0;
			var thisTitleAryThisString = '';
			var compareTitleAryThisString = '';

			if (baseStringLen gt compareStringLen)
				longestListLen = baseStringLen;
			else
				longestListLen = compareStringLen;

			// Initialize the first row & column of the array
			thisMatrix[1][1] = 0;
			for (i = 0; i lte baseStringLen; i = i + 1) {
				thisMatrix[i+1][1] = i;
			}
			for (j = 0; j lte compareStringLen; j = j + 1) {
				thisMatrix[1][j+1] = j;
			}

			// Now loop through the baseString
			for (i = 1; i lte baseStringLen; i = i + 1) {
				thisTitleArythisStr = listGetAt(arguments.baseString, i, " ");
				//writeoutput(variables.thisTitleArythisStr);

				// Loop through the compareString to check against the baseString's value
				for (k = 1; k lte compareStringLen; k = k + 1) {
					thisCost = 0;
					compareTitleAryThisStr = listGetAt(arguments.compareString, k, " ");

					if (thisTitleAryThisStr neq compareTitleAryThisStr) {
						thisCost = 1;
					}

					cellAbove = ((thisMatrix[i][k+1]) + 1);
					cellLeft = (thisMatrix[i+1][k] + 1);
					cellDiag = (thisMatrix[i][k] + thisCost);

					thisMatrix[i+1][k+1] = min(min(cellAbove, cellLeft), cellDiag);
				}
			}

			return thisMatrix[baseStringLen+1][compareStringLen+1];
		</cfscript>
	</cffunction>