Testing across the CF/Java boundary

For reasons of speed I’m currently embedding Java in Coldfusion code. Given Coldfusion is built on Java, and can instantiate Java objects through createObject("java", class_name), you’d think that’d be reasonably easy. But the boundary between Coldfusion and Java is like the gap between two halves of a broken bone: jagged, bleeding-edged and painful when coerced into heavy lifting.

Java nulls in Coldfusion

The first problem is the Great Coldfusion Error, which is that Coldfusion has no concept of null. At least, it can’t differentiate natively between null and an empty string. So while you can’t explicitly tell if a Java method has returned null, and indeed there’s very little warning of a null appearing in your program’s collection of function pointers, the second line in the below will explode if there’s no associated helper for this object:

<cfset java_object.getAssociatedHelper()>
<cfset java_object.getAssociatedHelper().callHelperMethod()>

If this threw a sensible error in a unit-testing environment I’d have solved the problem quickly, but more of that later. The way round this is to work with Coldfusion’s prediliction for (a) converting nulls to “” and (b) doing all sorts of type-casting on Java objects to integrate Java and CF at the scripting level. Therefore, a check of Len(java_object.getAssociatedHelper()) before you proceed does what you might expect for nulls, yet is also happy when the Java method returns a non-null object.

Errors from errors reporting on errors

The second major problem is that of Coldfusion’s introspection. Coldfusion attempts to latch its metadata introspection methods onto Java objects—including thrown Java exceptions—wherever it thinks is possible. Sadly, that’s far more times than it actually is possible to provide introspection. This is made worse in a unit-testing environment, where introspection is used for reporting purposes. I’m using CFCUnit, which is fairly thorough, but would frequently fail with a generic error that I couldn’t trace at all:

coldfusion.runtime.java.MethodSelectionException: The selected method getDetail was not found.
Stack trace: all from files within CFCUnit

I eventually worked out that Java’s XSLT engine wasn’t happy with an instance of the XSLT/XPath function concat() having only one argument; an error, moreover, that xsltproc failed to catch. The detail isn’t important, though, because you could see a getDetail error anywhere.

When you get this error, don’t bother with cfdump. That tag itself attempts introspection and throws the same generic error. Instead, use cftry/cfcatch to trap the error, and then dump that and quickly abort:

<cftry>
    cfset java_object.thisThrowsAGetDetailException()>
    <cfcatch>
        cfdump var=”#CFCATCH#”><cfabort>
    </cfcatch>
</cftry>

In a struct of structs of structs, of exceptions within exceptions within exceptions, I found it: CFCATCH.Cause.Exception.Exception.LocationAsString carried the precise location of the XSLT error in the file. Easy, when you know where to look.

Exit gracefully: when Coldfusion calls up the underlying Java, often you don’t have all of the usual Coldfusion debugging toolkit at your disposal. This means that any introspection of obscure Java exceptions to see what’s going on will often lead to errors itself, masking the original problem. Let the system throw the error, because the thrown error will eventually bubble up as a Coldfusion struct, with everything else on the way reduced to “safe” Coldfusion data-types, suitable for introspection by cfdump. Although Coldfusion is built on Java, its knowledge of its own foundations is not innate: someone has had to build the CF/Java interface. Don’t expect it to be any stricter or more OO-robust than the rest of Coldfusion.