Creating a Hybrid ColdFusion – Java Project in Eclipse

Sunday, May 23, 2010

On more than one occasion recently I’ve wanted to incorporate Java libraries into ColdFusion applications. You can do this in your CFML, but it usually involves the following:

  1. CFML code littered with createObject(“java”,”whatever.Whatever”);
  2. Zero content assist for the Java objects
  3. Optionally, Putting jar files in your CF installation’s lib directories and deploying those to all servers (dev, test, staging, prod) and developer machines

1 and 2 are the worst problems because 3 is solved with Mark Mandel’s javaloader (more on that later).

Ideally, I’d be able to write Java code in Java source files, compile it and test it in Eclipse, and fairly seamlessly use it in my CFML… in the same project. In this post, I’ll show you how to set it up in ColdFusion Builder. Note that these same steps apply to CFEclipse as well. In a follow-up post, I’ll show a more real-world example

Create your CFML project

  1. In CFBuilder or CFEclipse, either create a new project or use an existing one.
  2. Create a directory named “java”
    1. under there, create “src”, “bin”, and “lib” directories
    2. Download javaloader from Mark’s site, and drag the “javaloader” directory into the root of your project (or put it in your webroot)
    3. If you put it into your project, add this to your Application.cfc: this.mappings["/javaloader"] = getDirectoryFromPath(getCurrentTemplatePath());

By now, your project should look something like this:

HybridCFJavaStep1

Apply the Java Nature

  1. Close the project: right click – “Close Project”
  2. From the file system, open the project’s “.project” file in a text editor
  3. In the “<natures>” element, add this: <nature>org.eclipse.jdt.core.javanature</nature>
  4. In the “<buildSpec>” element, add the following:

<buildCommand>
	<name>org.eclipse.jdt.core.javabuilder</name>
	<arguments>
	</arguments>
</buildCommand>

Back in Eclipse, open the project (right click -- “Open Project”). Once you do that, it’s going to look very strange… something like this:

HybridCFJavaAllPackages

 

Configure the Java Build Path

Your project now looks funky because it’s treating everything as a Java source folder. Let’s fix that.

  1. On the project, right click, select “Build Path”, then “Configure Build Path”
  2. In the “Source” tab, remove any entries
  3. In the same tab, select “Add Folder” and navigate to your project’s “java/src” folder
  4. Down at the bottom, in “Default output folder” text field, browse to your project’s “java/bin” directory

Now, your project should look something like this:

HybridCFJavaStep3

That’s better.

 

Create a simple Java file

Note in that screenshot above that you have “java/src” with a package decorator… that’s where you’ll be working. Ignore the “java” directory that you see in that screenshot… we don’t care about it for now.

  1. In the “java/src” directory, right click, select “New – Package”. Name it “hello”.
  2. In that (now empty) package, right click, select “New – Java Class”. Name it “Hello”
  3. This will open a new Hello.java in a java editor. Let’s create a simple “echo” method. For now, just use a “main” method to drive it in Eclipse… we don’t need to get fancy with unit tests just yet:

package hello;

public class Hello {
	
	public String echo(String input){
		return "Hello. You said: " + input;
	}
	
	public static void main(String[] args) {
		Hello h = new Hello();
		System.out.println(h.echo("marc"));
	}
	
}

If all goes well, you should be able to run this file and see output in the console. To run it, select “Hello.java” in the Project Explorer and hit the green “Play” button in the toolbar, or right click in Hello.java and select “run as – java application”, or hit “Alt-Shift-X, J"

Use it in your CFML

To use it in your CFML, you’ll want to use javaloader. Conceptually, you simply tell javaloader where your compiled classes are, where any additional .jar files live, and then use it to create objects. Here’s what it looks like in practice. For demo purposes, I just created a file in my project root; thus, all paths you see in here are relative to that file in the root.

<!--- set up javaloader --->
<cfset binDir = expandPath("java/bin")>
<cfset libDir = expandPath("java/lib")>
<cfset jars = directoryList(libDir)>
<cfset arrayAppend(jars,binDir)>
<cfset loader = createObject("component","javaloader.JavaLoader").init(jars)>

<!--- run it --->
<h1>Hello!</h1>

<h2>Echo example</h2>
<cfset hello = loader.create("hello.Hello")>
<cfoutput>#hello.echo("marc")#</cfoutput>

If you’ve done everything right, when you run this in your browser you should see the expected output. If you go back into your java file, make a change, and hit refresh in the browser, the  changes you made should be reflected.

Final Thoughts

For years, folk have praised the ability to integrate Java into CFML. However, the workflow has always been complicated and the tooling nonexistent. CFMLers who’ve worked for years without content assist on their own Components most likely have no idea just how powerful an ally their IDE can be. So we suffer, typing “whatever = createObject(“java”, “some.Java”); whatever.NowWhatWasThatMethodAgain(AndWhatWereThoseArgumentsAgain)”, not realizing that if we were writing our java in a “real” editor, you’d get all the richness the IDE can offer. In other words, most CFMLers don’t know what they’re missing when they have to incorporate java into their projects.

I am not saying that when you create Java objects in your CFML code that you’re going to get content assist; rather, what I’m saying is that if you have more than a few lines of Java code in your CFML, then perhaps it’s best to put that code into its own Java sources – inside the same project, as I’ve described above – and simplify the work you have to do with Java without sacrificing workflow and deployment speed.

It also opens up some other nice possibilities: If you’ve a Groovy fan, or have a problem that would be better suited for Groovy – yes… even if the thing you want to do is simply more enjoyable in that language --- then you could do very similar steps to turn your CFML project into a hybrid Groovy project. Create your Whatever.g sources, and use Barney Boivert’s excellent CFGroovy to run your Groovy sources.

If you need to write some java of your own, and you need to incorporate it into your CFML projects, then this IDE setup can help you.

Coming soon: a real-world example.

A Fun Bug in VMWare Workstation Installer

Friday, May 7, 2010

I hit this fun bug this morning and wanted to share it. It’s quite unlikely that you’ll hit it yourself, so the value here is probably as much in the debugging as it is in helping folks down the road fix this should they find it via Googling.

The Problem

I clicked to install VMWare workstation, and was greeted with this:

vmware

Yes, you’re seeing that correctly. Rather than seeing a normal installer window, I was looking at a Disney Vacation site. At first, I thought I must’ve had a virus on my computer but ruled that out pretty quickly. If it were a virus, I’d have been seeing pr0n or warez or some other such thing, not a Disney site… especially not a “static” Disney site.

Debugging the problem

The programmer in me isn’t thinking “Why isn’t this working?”. Instead, I’m thinking “What would cause an installer to display a Disney html file?”

  1. Some virus is hijacking the file and replacing it with the Disney file
  2. The VMWare installer is hosed
  3. The Real Answer

I wanted to explore the idea of something hijacking the file first. The question is… what file? This was easy to find. I simply looked in the VMWare workstation installer log and looked at what files it was unpacking and writing. Here’s a snippet:

20100507074429:INFO CPackageManager::GetFile: index.htm 20100507074429:INFO CHtmlUI::SetHtmlFile: Navigating from '' to 'C:\Users\Marc\AppData\Local\Temp\vmware_1273232669\index.htm' 20100507074429:INFO CHtmlDialog::NavigateToFile: Loading file C:\Users\Marc\AppData\Local\Temp\vmware_1273232669\index.htm?lang=1033&locale=1033

I opened that index.htm file and sure enough, it was the Disney html. So, off to see what activity was happening around index.htm. To do that, I used Sysinternals Process Monitor. This simply involved firing it up and adding a filter for index.htm to get rid of the copious noise that Process Monitor spits out by default.

Looking at the output, the answer became obvious. The first instance of an index.htm was in c:\<path_to_my_downloads>\index.htm.  Then I saw the index.htm in the temp directory. Ahhh…. so the installer was creating a file in the same directory in which it lived and then copying it into the temp directory.

I’m sure you see where this is going.

I headed to my downloads directory, and there she was… index.htm, Disney-fied.

How does this happen?

You might be wondering how in the heck that Disney index.htm file might have gotten in my downloads folder. The answer is simple: I have kids. If you’ve ever watched a kid use an internet browser, you’ll see them mousing around and doing all manner of weird draggy things that inevitably involves downloading a bunch of web files (usually images, but occasionally they can manage html files) into wherever you have configured downloads to go. It’s usually harmless.

In this case, you can probably imagine the code in the installer: “if !fileExists…, write index.htm” …. “copy index.htm to temp\vmware_blahblah\index.htm”

But why would you, the installer writer (or the program that emits the installers), fail to handle the case when there could already be a file like that in the place where downloads go?  It’s a stretch, and that’s why this is such a fun bug.