A ColdFusion UDF For Dynamically Loading Java Classes

Posted by Dan on Jun 27, 2006 @ 10:22 PM
UPDATE:
2007-09-21
I just noticed today that the createClass() function below was an old version of the actual code I'm using, so I've updated the code with the latest version.

I've been working on modifying some ColdFusion based web services for XStandard originally written by Ben Nadel. One of the things the original code didn't support was the Spell Checker support feature built-in to XStandard.

After doing some searching, it appeared the Jazzy Spell Checker Java API would fill my needs nicely. It's based on the algorithms in aspell and it can use both dictionary and phonetic files to help with spelling suggestions and it returns all the information required by XStandard (position of word and suggested spellings.) The only "disadvantage" of Jazzy is that it does come "ready-to-use" with CFMX—you need to write a wrapper class for easy use w/ColdFusion. (More on that to come in a later post.)

Anyway, to make the XStandard CF web services as easy to use as possible, I wanted to load the invoke the class dynamically from a location outside of the JVM classpath—which is what the documentation recommends. I remember seeing a blog post from Spike a while back which talked about using a URLClassLoader to load a class file dynamically.

After reading the article and the comments, I noticed that Dan Plesse had some code that was a little more simplified and seemed just as affective. In just a few minutes I had whipped up the following UDF which will allow you to create an instance of a Java class from a dynamic location.

<cffunction name="createClass" access="private" returntype="any" output="false"
    hint="This will create a Java class from a file location. The class does not have to be in the class path.">


    <cfargument name="path" hint="An string of directories, or paths to .jar files to load into the classloader" type="string" required="true">
    <cfargument name="className" hint="The name of the class to create" type="string" required="Yes">
    <cfargument name="parentClassLoader" hint="(Expert use only) The parent java.lang.ClassLoader to set when creating the URLClassLoader" type="any" default="#getClass().getClassLoader()#" required="false">

    <cfscript>
        var oArray = createObject("java", "java.lang.reflect.Array");
        var oClass = createObject("java", "java.lang.Class");
        var aURLs = oArray.newInstance(oClass.forName("java.net.URL"), javaCast("int", 1));
        var oFile = createObject("java", "java.io.File").init(arguments.path);
        var oURLClassLoader = 0;

        if( NOT oFile.exists() ) throw("PathNotFoundException", "The path you have specified could not be found", oFile.getAbsolutePath() & " does not exist.");

        oArray.set(aURLs, javaCast("int", 0), oFile.toURL());

        //pass in the system loader
        oURLClassLoader = createObject("java", "java.net.URLClassLoader").init(aURLs, arguments.parentClassLoader);

        return createObject("java", "coldfusion.runtime.java.JavaProxy").init( oURLClassLoader.loadClass(arguments.className) );
    
</cfscript>
</cffunction>

Usage of the UDF is pretty straightforward. It requires two arguments: path and class. The path argument takes the absolute path of the directory or jar file the class is located in. The class argument is a pointer to the class to initiate. Unlike using the createObject("java", "java.net.URL") to initiate a Java class, there's no need to call the init() method once the object is created—that's what the newInstance() does for you. So for example, the following line would invoke a the "SpellCheck.class" file located in the "./classes/CFSpellChecker.jar" file.

<cfset oSpellChecker = createClass(expandPath("./classes/CFSpellCheck.jar"), "com.jazzy.SpellCheck") />

To invoke a single class file in a directory, you would use the following:

<cfset oSpellChecker = createClass(expandPath("./classes/"), "SpellCheck.class") />

I hope someone else finds this useful, although I can't take credit for the code. That goes to Dan Plesse and Spike.

Categories: HTML/ColdFusion, Java, Source Code

4 Comments


Comments for this entry have been disabled.