ColdFusion and server side javascript

Whirlpool has its own forum mark-up language which we call Whirlcode; it's analogous to, but not the same as BBCode. In recent weeks I've been considering how to improve the Whirlcode syntax, particularly now that it's being used to mark up more complex documents such as entries in the Whirlpool wiki.

My first instinct was to see what's already out there — and the short-list of candidates were Textile and Markdown. However neither have complete ColdFusion implementations, and from what I can tell the javascript implementations aren't exactly complete either — certainly not consistent with their server-side brethren.

I realised that more than anything else, I needed a mark-up language with 100% compatible server and client-side implementations, otherwise client-side previews could not be useful. And given the existing ‘investment’ in Whirlcode across the site, building my own ‘Whirlcode 2.0’ seemed the way to go. I took some ideas gleaned from Textile, combined them with traditional Whirlcode syntax, wrote a specification, and then built the first implementation in javascript.

My original plan was to port the javascript version to <cfscript>, but was also thinking about Rhino, an implementation of javascript in Java. I wondered how easy it could be to get my javascript parsed server-side, within my ColdFusion code.

Rhino in ColdFusion isn't new territory. Barney Boisvert cracked the puzzle six months ago, though his code uses Rhino to power a whole development framework. All I needed was to proxy one javascript function as a ColdFusion function, a very different, much simpler task.

So I started with Rhino's examples, and got a class working within NetBeans that worked for a very simple javascript function. I then turned it into a Java CFX tag (CFX tags have many limitations, but compiling a class meant easier debugging).

Here is a snippet of the Java:

import org.mozilla.javascript.*;
...
Context context = ContextFactory.getGlobal().enterContext();
try {
   Scriptable scope = context.initStandardObjects();
   Object result1 = context.evaluateString(scope, javascript_as_string, "somelabel", 1, null);
   Object theFunction = scope.get(function_name_as_string, scope);
   Object functionArgs[] = arguments_as_array;
   Function f = (Function)theFunction;
   Object output = f.call(cx, scope, scope, functionArgs);
} finally {
   Context.exit();
}

Finally, I transcoded it into <cfscript>. The following is the simplest possible code for executing a javascript function within ColdFusion. I have stripped out the error handling and function wrapping to show the underlying concept as clearly as possible — a more featureful implementation is left as an exercise for the reader.

<cfscript>
   script = FileRead(path_to_your_script.js);
   try {
      context = CreateObject("java", "org.mozilla.javascript.ContextFactory")
         .getGlobal().enterContext(); // Get the context object 
      try {
         scope = context.initStandardObjects(); // Prep the environment 
         context.evaluateString(scope, script, "somelabel", 1, javacast("null",0)); // Eval script 
         output = scope.get(function_name_as_string, scope)
            .call(context, scope, scope, arguments_as_array)
            .toString(); // Execute the function and return the result as a string
      }
      catch (any excpt) { }
      context.exit(); // Clean up
   }
   catch(any excpt) { }
</cfscript>

There are two alternative points where caching could be applied. Either cache the FileRead so you're not hitting the file system every time, or don't exit the context and cache it (and 'scope') somewhere.

Before this will work, you'll probably need to update the copy of Rhino embedded in ColdFusion. (Does anyone know why it's there at all?) Download the latest version, and to ensure this version loads first, copy js.jar into \ColdFusion8\runtime\servers\lib\. Packages in this directory load before any other.

4 comments

Nice... Now if we could just get the DOM on the server side... :)

Will the new Whirlcode allow for easy anchors in wiki pages?

I notice that there is no brown in this blog??? :)

You might try cfx_markdown: http://sebduggan.com/pages/projects/cfxmarkdown It's a simple wrapper for MarkdownJ: http://sourceforge.net/projects/markdownj/

Looking at the source I suspect it would be better as a standard CFC, with calls direct to the Java class file rather than a CFX. Certainly would be easy enough to implement :)










(no HTML)