Running Server-Side JavaScript with ColdFusion 9

August 27th, 2010

This is my third post, so already I’m 300% more prodigious than my last attempt at blogging. In that short time, I’ve realised that blogging is very much a two-way communication process – already other people’s comments have helped me develop and enhance my programming knowledge. Barney Boisvert’s comment from my previous post, Using Less CSS with ColdFusion, is a good example, specifically the following sentence:

Andrew, CF8 and above includes the necessary JRE version to natively support executing JavaScript via Rhino with no extra anything.

That was too much of a challenge to resist, so I set myself the task of creating a means of executing less.js (and JavaScript in general) directly from ColdFusion – thus neatly avoiding the need to rely on JARs, and meaning I can just drop in the latest version of less.js as soon as it becomes available. I’ve managed to knock up a basic component to do the job, which I’ll come to in a bit.

Firstly, however, both Barney and Ben Nadel have already blogged about running JavaScript using the former’s CFGroovy project. It’s a great solution as it can run scripts from any JSR-223 compliant scripting language, providing you have the necessary Java implementation available. Scripts run in this way also have transparent access to your ColdFusion scopes. All impressive stuff, and I wish I’d taken a look at implementing this before doing my own thing!

Anyway, for what it’s worth here’s how I managed to get JavaScript up and running in ColdFusion 9, based loosely on the instructions here. The first job is to create a context in Rhino, Mozilla’s JavaScript engine for Java:

<!--- create and enter the runtime context of the executing script --->
<cfset variables.context = CreateObject("java", "org.mozilla.javascript.Context").init().enter() />
<!--- maximum level of optimisation --->
<cfset variables.context.setOptimizationLevel(9) />
<!--- initialise standard objects (Object, Function) and get a scope object --->
<cfset variables.scope = variables.context.initStandardObjects() />

(A quick note here: we’re using the version of Rhino that ships as a library in CF9 – js.jar – and not the cut down version of Rhino found in the JRE itself.)

Then we can take some JavaScript (in this example we’re storing it as a string) and evaluate it. Here’s a ridiculously trivial example:

<cfsavecontent variable="variables.someScript">
	var openingText = "I like ";
	var middleText = " but only when they're ";

	function getSentence(things, colour) {
		return openingText + things + middleText + colour;
	}
</cfsavecontent>

<cfset variables.context.evaluateString(variables.scope, variables.someScript, "inlineScript", 1, JavaCast('null', '') ) />

We now have a JavaScript function we can call from within CF:

<cfset variables.compiledFn = CreateObject('java', "org.mozilla.javascript.Function").getClass().cast(variables.scope.get("getSentence", variables.scope)) />
<!--- equivalent to JS getSentence("traffic lights", "red") --->
<cfoutput>#variables.context.call(JavaCast('null', ''), variables.compiledFn, scope, scope, ["traffic lights", "red"])#</cfoutput>

The output of this function becomes:

I like traffic lights but only when they're red

As mentioned before, I’ve created a component that encapsulates all this and is available for you to examine as a handy download. When initialised, it will set up a Rhino context for you and provide a handy set of methods for reading and evaluating JavaScript from strings, local files and even URLs. This is more of a proof of concept than anything else and it probably needs some cajoling before it’s ready for a production environment but the principles are there for you to run with. It’s also CF9 only at the moment.

js.cfc (Download)

In the component I’ve taken advantage of CF’s onMissingMethod() handler to drop into JavaScript if you’re not calling any of js.cfc’s methods. Therefore you can run JavaScript functions using JavaScript syntax – but in ColdFusion!

<cfoutput>
	<!--- call the JavaScript function using the CF method --->
	#oJS.callJsFunction('getSentence', ["traffic lights", "red"])#
	<br />
	<!--- or using the same syntax as you would in JavaScript --->
	#oJS.getSentence("traffic lights", "red")#
</cfoutput>

And it works with less.js too. You’ll also need a couple of JavaScript files from Asual’s Java implementation to set up a rudimentary window object and provide a nicer interface, but it works:

<cfset oJS = CreateObject("component", "js").init() />
<cfset oJS.addScriptFromFile(ExpandPath('js/browser.js')) />
<cfset oJS.addScriptFromUrl('http://lesscss.googlecode.com/files/less-1.0.35.min.js') />
<cfset oJS.addScriptFromFile(ExpandPath('js/engine.js')) />

<cfsavecontent variable="inputcss">
@brand_color: #4D926F;
@the-border: 1px;
@base-color: #111;

#header {
  color: @brand_color;
}

h2 {
  color: @brand_color / 2;
}

#footer {
  color: (@base-color + #111) * 1.5;
}
</cfsavecontent>

<h1>Input CSS</h1>
<pre>
<cfoutput>#inputcss#</cfoutput>
</pre>

<h1>Output CSS</h1>
<cfset text = oJS.callJsFunction("compileString", [inputcss]) />
<pre>
<cfoutput>#text#</cfoutput>
</pre>

<!--- cleanup --->
<cfset oJS.exit() />

(although you probably don’t want to grab the less.js file from Google Code each time – far better to store it locally).

Finally, the question that usually pops up at this point is “why the heck do you want to do this?”. After all, you can’t do any DOM manipulation – so anyone thinking of doing a JavaScript version of Super Mario in ColdFusion can walk away disappointed. But I can only speak for myself, and my goal was to run less.js on the server. Yes, there aren’t that many things you can do with server-side JS that you can’t do with ColdFusion. But with the advent of projects such as CommonJS, the need to execute some must-have bit of server-side JavaScript may become more compelling in the future.

Using Less CSS with ColdFusion

August 17th, 2010

I’m going to start with an admission. I hate the word ‘leverage’. That word is a clear signal that you’re reading some meaningless marketing jargon. So I won’t be ‘leveraging’ anything in the post that follows – I shall be ‘making use of’ things instead.

And that’s one of the great things about ColdFusion. It’s a thriving community, but there are times when you need to look further afield – usually in the direction of Java – to find time-saving projects to ‘make use of’ in your applications.

There seems to be an increasing amount of publicity surrounding Less CSS at the moment, most recently from a SitePoint blog post on the matter. For the uninitiated, Less CSS is a neat tool which gives programmer-style additions to CSS, such as variables, operators and nested rules.

So, for example:

@brand_color: #4D926F;
@the-border: 1px;
@base-color: #111;

#header {
    color: @brand_color;
    a { text-decoration: underline; }
    a:hover {text-decoration: none; }
}

h2 {
    color: @brand_color / 2;
}

#footer {
    color: (@base-color + #111) * 1.5;
}

gets translated as:

#header {
    color: #4d926f;
 }

#header a {
    text-decoration: underline;
 }

#header a:hover {
    text-decoration: none;
}

h2 {
    color: #274938;
}

#footer {
    color: #333333;
}

Less CSS was originally implemented in Ruby, and Barney Boisvert has already blogged about using the Ruby implementation in ColdFusion in the past.  However the author of Less CSS is now working on a JavaScript version, less.js. This is really exciting, but while it’s great that this functionality is now available to everyone, regardless of server-side technology, surely it’s undesirable for end users to require JavaScript to be activated purely to display styles on their page.

Fortunately the Java community has come to the rescue. Asual has created a Java implementation which takes less.js and runs it in Rhino (Mozilla’s JavaScript engine for Java). This takes the form of a .jar file which we can make use of with JavaLoader.

(Note: The following code works in ColdFusion 9. ColdFusion 7 users running JRE 1.4 won’t be able to use the jar and will need to recompile the source. I’ve been through that particular ordeal and I’ll try and summarise in another post the steps needed to get it working. I haven’t tested CF8 yet – sorry.)

You can then compile .less files to your heart’s content:

<cfscript>
    loadPaths = ArrayNew(1);
    loadPaths[1] = expandPath("lesscss-engine-1.0.22.jar");
</cfscript>

<cfset loader = createObject("component", "javaloader.JavaLoader").init(loadPaths, 'true') />
<cfset engine = loader.create("com.asual.lesscss.LessEngine").init() />

<cfsavecontent variable="inputcss">
@brand_color: #4D926F;
@the-border: 1px;
@base-color: #111;

#header {
    color: @brand_color;
}

h2 {
    color: @brand_color / 2;
}

#footer {
    color: (@base-color + #111) * 1.5;
}
</cfsavecontent>

<h1>Input CSS</h1>

<pre>
<cfoutput>#inputcss#</cfoutput>
</pre>

<h1>Output CSS</h1>

<cfset text = engine.compile(inputcss) />
<pre>
<cfoutput>#text#</cfoutput>
</pre>

Obviously it’s hideously inefficient to create a Java object, parse the .less file and output the CSS file for every page request on a website so I’ll leave it as an exercise for the reader to come up with a nice caching solution.

And – that reminds me – don’t get me started on the word ’solution’. I hate it almost as much as ‘leverage’.

Hello world. Again.

August 16th, 2010

Welcome to the newly resurrected Web Applications Blog. This is the second attempt at creating a web development-related blog; the first consisted of a “Hello, world” style post dated May 2009 and absolutely nothing else.

The original intention of this blog was to reflect some of the experience gained from my role developing websites and applications for the public sector. We use ColdFusion quite extensively and jQuery is our JavaScript framework of choice, but I’ve also dabbled a bit with PHP CMSes such as Drupal and modx. I hoped to encompass topics as diverse as enhancing usability and overcoming resistance from reluctant users.

But life gets in the way (as is inevitable) and the blog fizzled out as a result.

This time round is going to be different, in that I’ve basically admitted defeat before I’ve even started.  I know this blog won’t be updated with any regularity. It acts as a repository for all the webby hints, tips and tricks I’ve come across in my job which may be of use to others.

So that’s the statement of intent; hope you will find the blog useful.