Saturday, July 25, 2009

ColdFusion 8: Experiment with compiling Java code from ColdFusion - Part 2

In Part 1 I described how you can use JavaCompiler's run() method to compile source code from a However the api also describes how you might extend the SimpleJavaFileObject class so you can compile source code from a string, rather than a physical file.

Since you can now compile code on-the-fly, you can easily use the CF code from part 1 to create their sample class: JavaSourceFromString.

<cfsavecontent variable="javaCode">

* A file object used to represent source coming from a string.
public class JavaSourceFromString extends SimpleJavaFileObject {
* The source code of this "file".
final String code;

* Constructs a new JavaSourceFromString.
* @param name the name of the compilation unit represented by this file object
* @param code the source code for the compilation unit represented by this file object
public JavaSourceFromString(String name, String code) {
super(URI.create("string:///" + name.replace('.','/') + Kind.SOURCE.extension),
this.code = code;

public CharSequence getCharContent(boolean ignoreEncodingErrors) {
return code;

<cfset pathToInputFile = "C:\myFiles\" />
<cfset fileWrite(pathToInputFile, javaCode) />

However, instead of using the JavaLoader this time, we are going to output the class file directly to the WEB-INF\classes\ directory. That way the class will be automatically detected by ColdFusion. Now to redirect the output of the compiler, simply prepend the -d <directory> flag to the list of sources passed into the compiler.

<!--- create a stream to capture errors --->
<cfset errStream = createObject("java", "").init() />

<!--- get a compiler reference --->
<cfset provider = createObject("java", "") />
<cfset compiler = provider.getSystemJavaCompiler() />

<cfset args = [ "-d", "C:\ColdFusion8\wwwroot\WEB-INF\classes\", pathToInputFile ]>
<cfset status = javacast("null", ""),
javacast("null", ""),
) />

<!--- display the status and any error messages --->
<!--- status: 0 == success --->
<cfset message = toString(errStream.toByteArray()) />
<cfset errStream.close() />
<b>COMPILER RESULTS:</b><hr />
STATUS: #status# (0 == success)<br />
ERRORS: #message# <br /><br />

Once the code is compiled, ColdFusion should automatically detect the new .class file in WEB-INF\classes\. So if everything went as planned, the new class should be ready to use right away, without rebooting the server. (Note: If you make changes and compile the class a second time, ColdFusion will not automatically reload the class).

Using the new class is very simple. Just create a new instance of JavaSourceFromString, and pass in the name of your class and the source code as strings.

<cfsavecontent variable="javaCode">
import java.util.List;
import java.util.Vector;
public class MyClass
public static List<String> test() {
List<String> list = new Vector<String>();
list.add("It worked");
return list;

<cfset SourceFromString = createObject("java", "JavaSourceFromString") />
<cfset fileObject = SourceFromString.init( "MyClass", javaCode) />
<cfset sourceList = [ fileObject ] />

Now to compile these sources, we are going to try something a little different: using a CompilationTask. This option just provides some additional control over the compile process. We are also going to use a DiagnosticCollector object, which provides an easy way to access error message details.

You can create a CompilationTask using the compiler's getTask() method. Most of the arguments are the same as for the run() method. But there are two additional arguments: options and classNames. The options argument is just an array of settings you want to pass to the compiler. Such as the -d flag used in the previous example. We are going to use that flag again to send the output to the WEB-INF\classes directory. Now to perform the actual compilation, use the call() method.

<cfset provider = createObject("java", "") />
<cfset compiler = provider.getSystemJavaCompiler() />

<cfset collector = createObject("java", "").init() />
<cfset args = ["-d", "C:\ColdFusion8\wwwroot\WEB-INF\classes\" ]>
<cfset task = compiler.getTask( javacast("null", ""),
javacast("null", ""),
javacast("null", ""),
) />
<cfset status = />

To retrieve any error messages from the DiagnosticCollector, simply call the getDiagnostics() method. It returns an array of Diagnostic objects.

<cfset results = collector.getDiagnostics() />
STATUS: #status# (0 == success) <br />
<cfloop array="#results#" index="diagnostic">
Error on #diagnostic.getLineNumber()#
Starting at #diagnostic.getStartPosition()# <hr />
#diagnostic.getMessage(javacast("null", ""))# <br />
<br />

All that is left is to test the compiled class. If everything went well, your results should look something like this. Ah, cfdump has never looked better ;)

<cfset myObj = createObject("java", "MyClass") />
<cfdump var="#myObj.test()#" label="MyClass.test();" />

As always comments/questions/corrections are welcome.


Mike Henke July 26, 2009 at 7:26 AM  

This is awesome. Thanks, I was wondering how to compile without using the cfcompile bat. Keep up the great posts.

TJ July 26, 2009 at 8:59 AM  

Interesting post! I can't see many use cases where I would want to use it, but it does give one more insight into the inner workings of CF and how closely it is tied to the JVM. I could possibly see this being used to compile a class dynamically onServerStart, although for what purpose I have no idea!

cfSearching August 30, 2009 at 8:12 PM  

Re-post of a user comment deleted by accident

By User: DonOmite
I agree this is interesting, but what can I do with it for a web app? Wouldn't it just be easier to make a cfx or such and use that? If I could run java on the fly and use it, now that would be kewl.

  © Blogger templates The Professional Template by 2008

Header image adapted from atomicjeep