FW/1 - The Invisible Framework

Framework One and friends...

Cfmljure Release 0.1.0

| Comments

I started cfmljure back in 2010, a few months after I started to learn Clojure, as a way to run Clojure code inside a CFML application. That early version worked - we’ve been using it in at World Singles since mid-2011 to integrate Clojure into our large ColdBox application, and updated versions of cfmljure have been in very heavy production usage since May 2012. Those early versions were pretty clunky to setup though, since you needed to mess with the classpath of your CFML engine (or at least the container on which you ran it - Tomcat, in our case) and you needed to copy all your third-party library dependencies into the WEB-INF/lib folder (and restart your container if you changed your dependencies).

When Clojure 1.6 came out (March 2014), it introduced a new API for embedding Clojure inside other JVM applications. I’d been relying on an unsupported API so I wanted to move to the new API as soon as possible. I ran into problems with classpath handling so I put it on the back burner for a while. One of the things that bothered me about the earlier cfmljure was that you had to do so much configuration and manual copying of libraries. Leiningen handles all this for you in Clojure and I wanted things to be that slick for cfmljure. I experimented with some CFML code that executed Leiningen to retrieve the classpath and I had some success, but not enough to create a reliable version that could be used in our app at work.

This past week, I decided to have another run at it, using a different approach to manipulating the classpath in my running CFML application:

// extend the classloader - not at all sketchy, honest!
var threadProxy = createObject( "java", "java.lang.Thread" );
var appCL = threadProxy.currentThread().getContextClassLoader();
var urlCLProxy = createObject( "java", "java.net.URLClassLoader" );
var addURL = urlCLProxy.getClass().getDeclaredMethod( "addURL", __classes( "URL", 1, "java.net" ) );
addUrl.setAccessible( true ); // hack to make it callable
for ( var newURL in urls.toArray() ) {
    addURL.invoke( appCL, [ newURL ] );
}

I gleaned the principle of this from time spent on Google and StackOverflow and several snippets of Java code that did this same thing. It uses Java Reflection to get a handle on URLClassLoader.addURL() and change its access to public so that it can be called by code that doesn’t extend the URLClassLoader class. This got me past the previous blocking point and I was able to complete the rewrite of that earlier version of cfmljure to use the new Clojure 1.6 API and also leverage Leiningen to avoid that configuration and manual copying.

I present: cfmljure Release 0.1.0! An easy-to-use way to embed a Clojure project into your CFML application (running on Lucee or Railo).

The documentation is in the cfmljure README on Github but the basic flow is as follows:

  • Create a Clojure project with Leiningen: lein new myproject
  • Create an instance of cfmljure pointing at that project folder: var clj = new cfmljure("/path/to/myproject");
  • Install Clojure namespaces into a struct (or a scope): clj.install("clojure.core,myproject.core",this);
  • Call Clojure code: this.clojure.core.println("Hello World!");

That’s about as simple as it gets!

Comments