Issue with cfcatch and JavaLoader

This is an issue I came across yesterday while doing some work on Transfer that affects JavaLoader , and generally anything that uses an URLClassLoader to load any external class/jar files.

The issue lies with catching Java exception with <cfcatch>.  The documentation states, that to catch a thrown Java exception you can specify the type in the 'type' attribute of the <cfcatch> tag.  So if we wanted to catch a java.io.IOException, we would do so this way:


<cftry>
   <!--- do some stuff that may throw an exception --->
    <cfcatch type="java.io.IOException">
        <!--- do something with it --->
    </cfcatch>
</cftry>


Now the issue comes when you are using JavaLoader (or anything that utilises any sort of URLClassLoader) and the loaded Java code throws an exception that hasn't been loaded with ColdFusion at start up -


<cfcatch> can't catch the Java Exception by it's specific type!

 
To show you what I mean, I created a simple test bed.

I create a new Java Exception object, 'com.compoundtheory.test.myException', and a class called 'MyThrower' who's sole job was to throw this exception.

Just to note 'jl' is the JavaLoader instance that has loaded my classes.

My test bed looked like this:


<cftry>
    <cfscript>
        myThrower = jl.create("com.compoundtheory.test.MyThrower").init();

        myThrower.myThrow();
    </cfscript>
    <cfcatch type="com.compoundtheory.test.myException">
        full
        <cfdump var="#cfcatch#">
    </cfcatch>
    <cfcatch type="java.lang.Exception">
        java.lang.Exception
        <cfdump var="#cfcatch#">
    </cfcatch>
    <cfcatch type="any">
        any
        <cfdump var="#cfcatch#">
    </cfcatch>
</cftry>


The result that came from this was that 'java.lang.Exception' was output onto the screen.  This means that the ColdFusion <cfcatch> statement bypassed the 'com.compoundtheory.test.myException' type (which is its real type) and caught it at the 'java.lang.Exception' level, which is what 'myException' extends.

This led me to believe that the ColdFusion <cfcatch> can only catch exceptions that are loaded in its class path on start up.  To this effect I had two more test beds.

The first test was to get my JavaLoader loaded class to throw a 'java.io.IOException' – an exception that is native to Java 1.4, which comes with ColdFusion, and is loaded when ColdFusion starts.

The test bed looked like this:


<cftry>
    <cfscript>
        myThrower = jl.create("com.compoundtheory.test.MyThrower").init();

        myThrower.myThrowIOException();
    </cfscript>
    <cfcatch type="java.io.IOException">
        full
        <cfdump var="#cfcatch#">
    </cfcatch>
    <cfcatch type="java.lang.Exception">
        java.lang.Exception
        <cfdump var="#cfcatch#">
    </cfcatch>
    <cfcatch type="any">
        any
        <cfdump var="#cfcatch#">
    </cfcatch>
</cftry>


In this case, 'full' was output, that meaning that the <cfcatch> was able to the exception with the type 'java.lang.IOException', just as it was meant to do.

The final test was when I moved my created classes into the ColdFusion class path, restarted the server, and ran my tests again.

The test bed now looked like this:


<cftry>
    <cfscript>
        myThrower = createObject("java", "com.compoundtheory.test.MyThrower").init();

        myThrower.myThrow();
    </cfscript>
    <cfcatch type="com.compoundtheory.test.myException">
        full
        <cfdump var="#cfcatch#">
    </cfcatch>
    <cfcatch type="java.lang.Exception">
        java.lang.Exception
        <cfdump var="#cfcatch#">
    </cfcatch>
    <cfcatch type="any">
        any
        <cfdump var="#cfcatch#">
    </cfcatch>
</cftry>


In this case, the output was 'full', that meaning that the <cfcatch> could catch the custom exception, as it had been loaded with the ColdFusion library at start up.

So what does this all mean? Does it mean you can't use libraries with custom exceptions in them? No it does not.  But it does mean you need to handle them in a slightly different way when using JavaLoader.

The solution to this problem is to write code that looks like this:


<cfcatch type="java.lang.Exception">
    <cfswitch expression="#cfcatch.Type#">
    <cfcase value="com.compoundtheory.test.myException">
        <!--- do something with it --->
    </cfcase>
    <cfdefaultcase>
        <cfrethrow>
    </cfdefaultcase>
    </cfswitch>
</cfcatch>


The nice thing is that the 'type' attribute on the cfcatch variable will be set properly to the type of the Java Exception, so we can use it as a sort of 'filter' to weed out the Exceptions we wish to handle, and rethrow the ones that we don't.  It's not the prettiest thing in the world, but it works.

I'm not sure if I classify this as a bug in ColdFusion, but it definitely is an issue if you're not sure how to resolve it.

Leave a Comment

Comments

  • Dipak Parikh | February 12, 2007

    Hi,
    I am planning to use Hibernate within ColdFusion.
    As hibernate has different versions of commons-logging and log4j, I was thinking if I can use JavaLoader to load Hibernate & related .jar from other location.
    Can you tell me, how cfcatch issue would affect in using Hibernate?
    Have anyone tried using Hibernate from within ColdFusion with success?

  • Mark | February 12, 2007

    Dipak,

    Some people have definatley got Hibernate to work with ColdFusion. I’ve done some testing with JavaLoader and Hibernate, and it worked a treat. You can see the details at the CFHibernate Yahoo group
    http://tech.groups.yahoo.com/group/cfhibernate/

    As to whether or not this will be an issue for you, depends on how your application is designed. But you use the technique above to make sure that your error handling works correctly.

  • Adam Fortuna | February 20, 2008

    Extremely useful article man. Helped me figure out what in world ColdFusion was doing with those errors. Thanks!

  • Ben Nadel | May 2, 2014

    Thank goodness! I thought I was going mad! I was staring at my CFCatch statements wondering what the heck is going wrong. What made it even more infuriating is that if I used CFThrow to throw an error with the *same* custom-type, it worked. Rock on!

    I just linked back to this post from a few of my posts about error handling.