CF Groovy

Those of you who remember CF Rhino will recognize the name for my latest little project. I whipped up a small proof-of-concept integration for Groovy into CF apps tonight while playing with some classloading issues within Groovy itself.

Groovy has a number of advantages for this type of integration of JavaScript, the biggest one being that Groovy IS Java, and carries Java's semantics almost perfectly. So unlike with Rhino, no major conversions are needed moving in and out of Groovy, which makes things enormously easier. CF still brings it's special blend of "insane" to the party, but it works quite well overall. Definitely has more real-world potential than CF Rhino ever did.

CF Groovy is not a whole framework like CF Rhino was, it's really just a proof of concept for integrating the two together and sharing resources. You can get the demo (which includes the engine via svn:externals) from Subversion at https://ssl.barneyb.com/svn/barneyb/cfgroovy/trunk/demo/. Just check it out to a directory named 'cfgroovy' in your webroot, drop the groovy JAR from the 'groovyEngine' subdirectory into your /WEB-INF/lib folder, and you should be done.  You can see it in action at the demo page, though without source to browse, it's not very interesting.

Also, unlike CF Rhino, there's no dependencies on the internals of ColdFusion (the Adobe product). To do some of the bridging to JavaScript, I had to invoke internal APIs of ColdFusion, so it wouldn't run on BlueDragon, Railo, etc. I built CF Groovy with Open BlueDragon and Railo 3 on Tomcat simply because I didn't have a CFML runtime on my box, and CF is like 400MB while oBD is 11MB and Railo is 25MB.

Unfortunately, all three runtimes have bugs that prevent them from working as expected.

Railo was the worst: it has some recursion issue with the JEE.include() call. It also doesn't seem to implement java.util.Map with it's struct implementation, and throws on missing keys rather than returning null.

Open BlueDragon initializes ResultSets from manually assembled queries (and possibly "real" queries) with the row pointer pointed at the first row, rather than before the first row as the spec says it should be.

ColdFusion doesn't create java.util.Date objects with the createDate built in, so trying to call ResultSet.getDate() throws a ClassCastException. Using ResultSet.getObject() and letting the ducks work their magic fixes this issue. It's also 15-35 times larger than the others.

Except for the download size issue, I've got conditionals to cover all the cases in the app, so it should be checkout and run on all three platforms. I haven't tested NA BD, CF7-, or Railo 2. I don't know why they wouldn't work, though, since I'm just doing standard Java stuff. I'm not doing any version conditionals, only platform ones, just in case.

FB3 Lite Updates

After 6 months of use in the wild, I decided to consolidate and republish a couple mods I've made to the framework. The core functionality is unchanged, so for background, check my original post from last year. The enhancements, in no particular order, are as follows:

  • Changed the config at the top of index.cfm to use CFPARAM instead, so the framework can be parameterized by calling code.
  • Most significant is the addition of an 'appSearchPath' control variable, which means index.cfm (the core file) and fbx_Switch.cfm no longer have to be in the same directory. This allows you to put index.cfm in some arbitrary directory (probably with svn:externals) and use it from there. It defaults to ".,.." (this directory and the parent directory), but can be set to whatever is needed.
  • Added the context path and script name to the front of the 'self' variable. I realize this breaks the core on non-JEE CFML implementations, but it fixes some weird redirect issues with IE, so I feel it's worth it. If you don't want it, just remove everything before the question mark.
  • Removed defaulting of the empty fuseaction. Now only a missing fuseaction will be set to the configured default.
  • Added CFABORT to the end of the location() UDF. I went back and forth on this one for quite a while (like measured in days) when I was originally writing the framework, and finally decided to leave it out. After using the framework for a while, I've decided that was the wrong decision, so I've put it in there in this version.
  • Added ability to use multiple circuits via the new 'allowMultipleCircuits' control variable. It defaults to true, which means any slashes in your fuseactions will be treated as path segments, potentially breaking existing apps. If you need to use slashes in your fuseactions, set it to false. When true, a fuseaction of "circuit/fusection" will be converted into an include of "circuit/fbx_Switch.cfm" with attributes.currentFuseaction set to "fuseaction". I've intentionally NOT used the dot separator because they are not circuit aliases as in real Fusebox, but are simply path segments. The include() UDF has always had this ability do to it's strictly path-based nature, though it was undocumented.
  • Added some comments to index.cfm.

There is a project page for the framework, as well as Subversion access available. Current utilization (that I'm aware of) is about 15 distinct applications, most of which I either own or am a contributor to.

Design Patterns in Dynamic Langauges

I went to the Portland JUG meeting this evening to hear Neal Ford talk about design patterns in dynamic languages. Unlike Joshua Marinacci a couple months ago, Neal actually seemed to understand what he was talking about, which was a relief. He gave a quick run-through of dynamic vs. static languages (or ceremonial vs. essential, as he says), and then stepped through a bunch of the GoF design patterns, what they're about, and how they're largely irrelevant in Groovy and/or Ruby because the language provides the "pattern" as part of core language constructs.

What I found most interesting was the different ways that Ruby and Groovy do similar things. While I don't know that I've blogged much about it, those who interact with me in meat-space know all about my aversion for Ruby's ghastly syntax. (I swear Matz was drunk when he was doing the initial implementation and decided it should be a design goal to use every friggin' printable character in ASCII.) Seeing some of it's power compared with Groovy gave me some more appreciation for the language, even if it did nothing for it's appeal. Decorator implementations and method interception were particularly interesting.

Bottom line, however, is that design patterns (at least th GoF's) are incredibly relevant to dynamic languages. In fact, I'd say they're more relevant than ever. With "old" langauge (Java, C++, etc. that the GoF book was targeted at), you could either recognize the patterns and leverage their nomenclature, or you could not recognize them, lose a bit of productivity, but not really be too worse for wear. However, with the dynamic languages having many of the pattern implementations built in, you're really stuck if you don't understand the patterns and what they're about, because you can't use the language without that knowledge.

To use my favorite metaphor (carpentry tools), anyone can use a hammer and a chisel bevel the edge of a board (one specific board, mind you, no retries). It probably won't be very pretty unless you're quite skilled, but anyone can do it given time and patience. Give someone a table router and you've got all kinds of potential to hurt yourself, not to mention chances to ruin their one board, but you can do it quick if you know how. If you don't understand how the more powerful tool works, you're going to be in a world of hurt, but if you can leverage it's strengths, you'll be amazingly better off.

So don't skip the GoF book just because you use a dynamic language. You better know it inside out, just ignore the implementation details.

Google Search Update

After converting to Google Search and giving Google a chance to get everything active, ads stared appearing on the results.  That's part of the deal for getting free search, so they were expected, but after seeing them, I realized the layout wasn't too hot.  With both my sidebar and the ads, the top couple search results were crammed into around 250px of horizontal space – definitely not ideal.

Like the obstacles before, the solution was simple.  I added a couple little jQuery snippets to hide the contents of the sidebar (aside from the search widget) and remove the vertical rule.  Then I set the search results IFRAME's width to take up the entire body of the page, rather than just the main column, and it's done.

Like all compromises, it's not without downsides.  Foremost, there is a brief flicker on page load where the sidebar appears and then vanishes.  The markup for the sidebar is also included in the page, so it has to be generated server-side as well as sent over the wire, just to be ignored when it gets to the browser.  However, contrasted against having to go build a custom page template just for the search results page, two lines of JS and a little network overhead seems a cheap price.

Google Site Search

After frustrations with the K2/WordPress search on my site, I've switched to Google custom search.  I'd never really used the built-in stuff, but when I was setting up my project pages, I did a bunch of searching, and it just didn't seem to work very smoothly.  In particular, you have to manually URL-escape your search string (e.g. replace spaces with pluses) for it to work right.

The Google interface is devoid of such issues, of course, and has the added benefit of searching my full domain, not just my blog posts.  I also included my public Subversion repositories in the search scope, though I'm not sure I'm going to leave that.  The high-churn nature of source code repositories makes for often-outdated results.

The integration was a snap: drop the form code in a Text Widget on the sidebar, and drop the results code in the body of a new Search Results page and I was done.  There were three minor gotchas I had to resolve:

  1. The form didn't repopulate with the current search query on the results page.  That was easily addressed with a little JavaScript in the result page body.
  2. The results page IFRAME was too wide for the site layout.  Again, easily addressed with a little CSS in the result page body.
  3. WordPress lists all top-level pages across the top, and I didn't want the Search Results page showing up there, since clicking it doesn't make sense.  Addressed with a little CSS in the search form widget.

Checkbox Range Selection Update

Just a little update to my checkbox range selection jQuery plugin to allow chaining.  I'd forgotten to return 'this' at the end of the function.  Here's the full source, including the mod:

(function($) {
  $.fn.enableCheckboxRangeSelection = function() {
    var lastCheckbox = null;
    var $spec = this;
    $spec.bind("click", function(e) {
      if (lastCheckbox != null && e.shiftKey) {
        $spec.slice(
          Math.min($spec.index(lastCheckbox), $spec.index(e.target)),
          Math.max($spec.index(lastCheckbox), $spec.index(e.target)) + 1
        ).attr({checked: e.target.checked ? "checked" : ""});
      }
      lastCheckbox = e.target;
    });
    return $spec;
  };
})(jQuery);

You can check the project page as well, for full history and updates.

My Projects Page

I've just added a simple projects page for the projects that I blog about here.  There you should be able to find a comprehensive list of projects and their updates from my blog.  It's a one-stop shop for the stuff I write.  I've often considered the best solution for managing all that, and decided simplest is best.  It's a bunch of static pages with static links managed via WordPress's page infrastructure.  Enjoy!

AmazonS3.cfc Update

I've updated my AmazonS3 CFC to include local caching of files. The new source is available here: amazons3.cfc.txt, or visit the project page.  The only public API change from the first version is the addition of an optional third parameter to the init method for specifying the local directory to use as a cache. If you're doing repetitive read operations on S3-stored assets, using the local cache can speed things up significantly, though it is not without drawbacks.

In particular, the CFC assumes that it is the only interface to the S3-stored assets that it is used to interface with. If you use any other mechanism to manipulate those assets (including multiple CF applications), you'll run into issues. The cache itself is the canonical source for cache state, so emptying the cache folder will always revert the CFC back to S3's state if the cache is out of sync.

If you cluster multiple CF instance together, you can still use the local cache, but you must use a single cache for all CF instances. I.e. the cache must reside on a disk shared by all instances, rather than each instance having it's own separate cache. This reduces the performance benefit slightly (since you must use a non-local disk), but it will still be faster than S3.

The CFC exposes a deleteCacheFor() method that accepts a bucket and objectKey pair that can be used for managing the cache outside of actual S3 operations. If you have multiple CF instances that cannot share a single local cache, or for which the network overhead for a shared cache is still undesirable, you can use this method to synchronize the instances' caches via JMS or something. Obviously that's far outside the scope of the CFC itself, but the hook is there to support it. Note that you must delete cache when overwriting an asset on S3, as the local cache will not pick up the change in S3, it will continue to return the old version if it's not cleared.

Prototype's Array.any/all with jQuery

I needed to convert a couple Array.any() and Array.all() calls (from Prototype) to jQuery syntax. Since jQuery doesn't extend the built-in objects with nice functionality like this, you have to fake it. Here's what I came up with. Old version ('images' is an array):

images.all(function(o){return o.status == "ready";})

and the new version:

jQuery.inArray(false,
  jQuery.map(images, function(o){return o.status == "ready";})
) < 0

Array.any()'s equivalent is the reverse:

jQuery.inArray(true,
  jQuery.map(images, function(o){return o.status == "ready";})
) >= 0

Might have to rip the Enumerable and Array extensions out of Prototype for standalone use as well.

Use ColdFusion? Use Java.

If you use ColdFusion (or another Java-based CFML runtime), you should be using Java. There's a reason that CF uses Java under the hood: Java is incredibly powerful. Yes the interface to Java from the CF level is cumbersome and creating hybrid CF/Java applications pretty much costs you CF's RAD capabilities, but there are some real gems in the Java libraries.

On CF-Talk today, someone asked about reversing an array. There's not a built-in function for doing that, but if you remember that CF arrays are just Java Lists (java.util.Vector, specifically), you can suddenly leverage the full Java Collections framework. In this case, the solution was simple:

createObject("java", "java.util.Collections").reverse(myArray);

Want get unique values from an array? Not a difficult problem, but how about this:

myArray = createObject("java", "java.util.ArrayList").init(
  createObject("java", "java.util.HashSet").init(myArray)
);

Want them sorted? Here you go:

createObject("java", "java.util.Collections").sort(myArray);

Yes, there is an arraySort() CF built-in, but it only sorts text and numbers. So if you want to sort an array of Dates, you're stuck. Collections.sort, on the other hand, will happily sort the dates.

This only breaks the surface of what you can do with Java. Obviously you can't leverage this if you have to support non-Java CFML runtimes, but if you developing for a Java runtime (or runtimes), you owe it to yourself to learn a little bit about the Java tooling available to you.  I've blogged about a couple other Java tricks (fast directory filename listings and string builder tricks) in addition to myriad Java libraries that can be leveraged (Batik, Weka, Ant, etc.)

Here's an complete example of the above tricks:

a = [1, 2, 3, 4, 5, 1, 2, 3, 4, 5];
// reverse it
createObject("java", "java.util.Collections").reverse(a);
writeOutput(a.toString()); // [5, 4, 3, 2, 1, 5, 4, 3, 2, 1]

// sort it
createObject("java", "java.util.Collections").sort(a);
writeOutput(a.toString()); // [1, 1, 2, 2, 3, 3, 4, 4, 5, 5]

// pull unique elements
a = createObject("java", "java.util.ArrayList").init(
  createObject("java", "java.util.HashSet").init(a)
);
writeOutput(a.toString()); // [3, 2, 1, 5, 4]

// unique elements in sorted order
createObject("java", "java.util.Collections").sort(a);
writeOutput(a.toString()); // [1, 2, 3, 4, 5]

EuroModels.ch