Amazon CloudFront CFC

Amazon CloudFront is a CDN that sits atop their S3 file hosting service to provide caching and geographically dispersed delivery.  It's all very simple, except security.  Much like my Amazon S3 CFC's original goal, my new Amazon CloudFront CFC's primary purpose is to ease the creation of signed URLs for CloudFront.  You can grab a copy from amazoncloudfrontcfc.txt.  The API for the CFC is about what you'd expect:

<cfset cloudfront = createObject("component", "amazoncloudfront").init(keyPairId, privateKeyFile) />
<cfset signedUrl = cloudfront.signUrlWithTimeout(resourceUrl, 600) />

This will generate a signed URL for the CloudFront resourceUrl (direct domain or CNAMEd) which expires in 600 seconds (10 minutes).  Very much like the S3 CFC.  Here we're dealing with resourceURLs directly (which correspond to a bucket and object key) rather than separate buckets and object keys.  CloudFront doesn't distinguish between the two parts, so the CFC doesn't either.

The biggest gotcha, however, is with the signing mechanism.  S3 uses a simple pre-shared key, but CloudFront uses an RSA private key which is significantly more complicated to deal with.  Unfortunately, Amazon provides it's keys in PEM format, but core Java can only read DER format, so you must either convert your private key to DER format with OpenSSL, or use a third party library.  Fortunately, both are pretty simple.

Here's the command to convert your key with OpenSSL:

openssl pkcs8 -topk8 -in pk-KEYPAIRID.pem -nocrypt -outform DER -out pk-KEYPAIRID.der

Alternatively, you can use the not-yet-commons-ssl package, which provides support for reading keys in PEM format (among a pile of other things).  If you can add the JAR to your classpath, this is definitely a superior solution to manual conversion, since you can transparently use pretty much any RSA private key.  And there's nothing to enable in the CFC; if not-yet-commons-ssl is available on the classpath, it'll automatically use it for reading in the private key.  As an aside, the name 'not-yet-commons-ssl' reflects the fact that the package has applied fro Apache Commons incubation, but it hasn't been accepted yet.  The code is orgniazed in the 'org.apache.commons.ssl' package assuming it's acceptance, but it is still unofficial.

As always, updates and such are available on the project page.

I Am No Longer Married

I learned today, via a letter from the court, that I am no longer married (and haven't been since the 12th). Seven years, five months, and nine days.

Improving the JSON/P Twitter Widget

For quite some time now I've had a sidebar widget that displays an excerpt of my Twitter feed on my blog.  It uses the standard Twitter JSON/P interface for loading the tweets and then a Twitter-provided script (http://twitter.com/javascripts/blogger.js) for rendering them on the page.  Unfortunately the default installation instructions would have you set up something like this:

<ul id="twitter_update_list"></ul>
<script type="text/javascript" src="http://twitter.com/javascripts/blogger.js"></script>
<script type="text/javascript" src="http://twitter.com/statuses/user_timeline/barneyb.json?callback=twitterCallback2&count=5"></script>

That works, but it puts the JSON/P call (the second SCRIPT tag) into the main page flow, so anything after the call won't render until the script load is complete.  When Twitter is slow/down it can cause problems, but even when it's fast you still get a brief hang with a partial page before the rest snaps into view.  Fortunately, the solution is simple: do the call on DOMLoaded instead of inline.  I'm using jQuery, but the principle is the same regardless of what framework you use (or none at all):

<ul id="twitter_update_list"><li class="load-ind"><img src="http://www.barneyb.com/s/loading_indicator.gif" /></li></ul>
<script type="text/javascript">
jQuery(function() {
  jQuery("body").append(
    '<sc'+'ript type="text/javascript" src="http://twitter.com/javascripts/blogger.js"></sc'+'ript>' +
    '<sc'+'ript type="text/javascript" src="http://twitter.com/statuses/user_timeline/barneyb.json?callback=twitterCallback2&amp;count=5"></sc'+'ript>'
  );
});
</script>

Now the SCRIPT tags will be written on DOMReady, allowing the full page to render before the calls to Twitter are dispatched.  This gives a faster user experience, though you do get a reflow of the sidebar when the statuses load.  This has become so prevalent on modern websites, however, that I don't think it's worth caring about, but if you're concerned, you could specify a height to the loading indicator LI that is approximately what the rendered tweet list will be to mitigate the effect.  It won't avoid the reflow, but it'll make things move less because the space will be reserved up front.

I've also added a simple "loading" image to the UL that the JS will place the tweets in.  The way blogger.js is written, any content within the UL will be removed (i.e. the tweets overwrite the content; they're not appended to the content), so no need to explicitly remove the image or it's container LI.

Implicit Blocks in Fusebox 5.5.1

The XML syntax used by Fusebox since the 4.0 version allows for conditional expressions like these:



  


  





  

In the latter case, the
tags are basically irrelevant, because they provide the same containership and semantic as the tag itself.  Fusebox 5.0 and older allowed you to omit the
tags in this case:


  

This only makes sense if you don't have a block, because if you have both blocks, then the tag doesn't provide the correct containership.

Fusebox 5.5, however, made the
tag required even for the simple case. This is unfortunate, I think, because it adds needless verbosity, and from digging around in the code, I suspect that it was primarily driven by implementation limitations, rather than user requests.

As part of migrating an app from 5.0 to 5.5.1, I really didn't want to have to go back and add all those extra tags, nor deal with the extra verbosity when maintaining the application down the road.  So I wrote a small patch for the core files that will allow an implicit
tag in the 5.0 style when using the 5.5.1 cores:

Index: fuseboxVerb.cfc
===================================================================
--- fuseboxVerb.cfc	(revision 778)
+++ fuseboxVerb.cfc	(working copy)
@@ -33,20 +33,26 @@

 		
 		
+		
 		
 		
-		
-
-		
-		
-
-		
-			
-			
-		
+
+		
+			
+			
+			
+		
+			
+			
+
+			
+				
+				
+			
+		

 		
 		
Index: verbs/if.cfm
===================================================================
--- verbs/if.cfm	(revision 778)
+++ verbs/if.cfm	(working copy)
@@ -35,13 +35,9 @@
 		// at most one
, at most one , nothing else:
 		fb_.hasTrue = false;
 		fb_.hasFalse = false;
+		fb_.verbInfo.hasOther = false;
 		for (fb_.i = 1; fb_.i lte fb_.verbInfo.nChildren; fb_.i = fb_.i + 1) {
-			if (fb_.verbInfo.children[fb_.i].getNamespace() is not "") {
-				fb_throw("fusebox.badGrammar.illegalVerb",
-						"Illegal verb",
-						"An 'if' may contain only 'true' and 'false' verbs in fuseaction #fb_.verbInfo.circuit#.#fb_.verbInfo.fuseaction#.");
-			}
-			switch (fb_.verbInfo.children[fb_.i].getVerb()) {
+			switch (fb_.verbInfo.children[fb_.i].getNamespace() & fb_.verbInfo.children[fb_.i].getVerb()) {
 			case "true":
 				if (fb_.hasTrue) {
 					fb_throw("fusebox.badGrammar.illegalVerb",
@@ -61,10 +57,41 @@
 				}
 				break;
 			default:
+				fb_.verbInfo.hasOther = true;
+				break;
+			}
+		}
+		if (fb_.verbInfo.hasOther) {
+			if (fb_.hasTrue OR fb_.hasFalse OR fb_.verbInfo.action.getCircuit().getApplication().strictMode) {
 				fb_throw("fusebox.badGrammar.illegalVerb",
 						"Illegal verb",
 						"An 'if' may contain only 'true' and 'false' verbs in fuseaction #fb_.verbInfo.circuit#.#fb_.verbInfo.fuseaction#.");
-				break;
+			} else {
+				// only non-true/false and not strict mode, so wrap an implicit true around the children
+				// we have to do some acrobatics around the 'children' references
+				fb_.children = structNew();
+				for (fb_.i = 1; fb_.i lte fb_.verbInfo.nChildren; fb_.i = fb_.i + 1) {
+					fb_.children[fb_.i] = fb_.verbInfo.children[fb_.i];
+				}
+				structClear(fb_.verbInfo.children);
+				fb_.factory = fb_.verbInfo.action.getCircuit().getApplication().getFuseactionFactory();
+				fb_.implicitTrue = fb_.factory.create(
+						"true",
+						fb_.verbInfo.action,
+						structNew(),
+						fb_.children
+					);
+				fb_.verbInfo.children[1] = fb_.implicitTrue;
+				// now null out the remaining children
+				for (fb_.i = 2; fb_.i lte fb_.verbInfo.nChildren; fb_.i = fb_.i + 1) {
+					fb_.verbInfo.children[fb_.i] = fb_.factory.create(
+						"noop",
+						fb_.verbInfo.action,
+						structNew(),
+						arrayNew(1)
+					);
+				}
+				fb_.hasTrue = true;
 			}
 		}

Index: verbs/noop.cfm
===================================================================
--- verbs/noop.cfm	(revision 0)
+++ verbs/noop.cfm	(revision 0)
@@ -0,0 +1 @@
+') />

The implementation is kind of hacky because the XML compiler lacks a IR transformation step, so I had to fake one.  And that was further complicated by the parallelism in the internal datastructures, necessitating the creation of a "noop" verb to use as a placeholder in certain situation. 5 minimum deposit casino  More specifically, there isn't a way to add or remove verbs from the parse tree, only to change verbs, and implementing the functionality within that constraint required some "creative" programming.  ;)

A Personal Update

This morning, Heather and I cooperatively filed for divorce.  This eventuality is old news for some, expected by others, and out of the blue for the rest.  I have not been forthcoming with this information outside need-to-know situations for a variety of reasons, the primary one being that without any agreement between Heather and I for the terms of our separation, I didn't have any answers to give beyond the fact that we were separating.

The process of separating began 18 months ago (early summer '08), and while it's not yet over, it's now just a matter of waiting for the court system to crank it's cogs.  As of this weekend, we will no longer be living together, and the kids will split their time between our places.  Initially, there is a bias towards the kids being with Heather since I work and she does not.  The kids will alternate weekends with each one of us, plus they'll stay with me one night a week and a couple weeks each summer.  That yields a roughly 35/65 split, and will change over time.

During the past year and a half I have steadily become more and more frustrated with the situation (imagine that), and while I've done what I can to prevent that from affecting other things, I know I have been far from successful.  That is fair to no one, least of all the kids, and I'm happy that things have pretty much reached their conclusion.  Not that it's even close to all roses, but stability and predictability keep me sane, and starting next week, they will again be present in my life to a large degree.

Also, as many of you know, I have a skin affliction called Nummular Eczema.  The short version is that my white blood cells congregate in the vessels under my skin (rather than being distributed through my entire bloodstream) and irritate the hell out of it.  It isn't an overproduction of white blood cells, nor an autoimmune response, they just congregate when they're idle and destroy my skin.  Interestingly, when I was ill this fall, the irritation completely ceased until I was well again, when it came back full force.  Those were the most blissful days of being sick I've ever had.

In the past I've typically just lived with it, used lots of moisturizing lotion, and after a month or two it dissipated.  This outbreak, however, has lasted for six months at full force, resisting even the gnarly topical steroids I was prescribed.  I've recently started UVB irradiation (basically tanning, but with a very specific section of the spectrum) which seems to help significantly, though not without side effects.

I don't doubt that there is a correlation between the pending separation and my skin issues; stress is a causal (or at least contributing) agent for a wide variety of problems.  The conclusion of our separation and the corresponding state of limbo will reduce my stress levels significantly, and hopefully that will help my skin be less pissy.

The net of this is that as much as I've tried not to be, I have been an irritable asshole in a wide array of situations and to a similarly wide array of people.  I don't claim my situation as justification for that behaviour, but I'm as human as the next guy, and I haven't had the energy or emotional capacity to deal with adversity.  Please accept my apologies, especially since I've not provided any overt indication as to why I might be acting in that way, thereby leaving "Barney is an asshole" as a perfectly viable conclusion.  I'm not an asshole (or at least try not to be), but I've done a rather poor job for the past couple years.

Finally, there are a pile of people who have helped me keep my chin "up-ish", and a few that have done absolute wonders for keeping me sane (and safe) over the past months.  You know who you are, and you have my sincerest thanks.

I have disabled comments on this post for what are hopefully obvious reasons.  If you would like to chat, you can email me at bboisvert@gmail.com, use the contact form, or call/text/IM/visit/etc., whatever suits you best.  I've no reservations about talking and/or answering questions, just not in a comment thread.

Even Better CF DB Error Messages

A few weeks ago I posted about fixing CF's DB error messages so that they include query params you passed as well as the raw SQL of the query.  I also supplied a patch for detail.cfm so your SQL renders in a PRE tag for better readability, but didn't make it general enough to handle all cases.  I've addressed that with some reflection, as illustrated in the patches below.  They're almost identical, but not quite.

If you did NOT use my patch from a few weeks ago (i.e. you have a stock CF), you'll want this patch (plus the unmodified patch for DatabaseException.cfm):

Index: WEB-INF/exception/detail.cfm
===================================================================
--- WEB-INF/exception/detail.cfm	(revision 1495)
+++ WEB-INF/exception/detail.cfm	(working copy)
@@ -213,6 +213,12 @@
     <cfif isDefined("attributes.properties")>
 	<tr><td>
         <!--- Format and display miscellaneous exception properties. --->
+		<cftry>
+			<cfset isSql = getClass().forName("java.sql.SQLException").isAssignableFrom(getClass().forName(error.type)) />
+			<cfcatch type="java.lang.ClassNotFoundException">
+				<cfset isSql = false />
+			</cfcatch>
+		</cftry>
         <table border="0" cellpadding="0" cellspacing="0">
         <cfset exclude="localizedmessage,locale,detail,message,tagcontext,rootcause">

@@ -227,8 +233,8 @@
             	</cfcatch>
                 </cftry>
     	<tr>
-    		<td><font style="COLOR: black; FONT: 8pt/11pt verdana">#xmlFormat(m)#</font></td>
-    		<td><font style="COLOR: black; FONT: 8pt/11pt verdana">&nbsp;&nbsp;#htmlEditFormat(val)#</font></td>
+    		<td valign="top"><font style="COLOR: black; FONT: 8pt/11pt verdana">#xmlFormat(m)#</font></td>
+    		<td><font style="COLOR: black; FONT: 8pt/11pt verdana"><cfif isSql AND listFindNoCase("sql,parameters", m) GT 0><pre>#htmlEditFormat(REReplace(val, "#chr(10)#\s*#chr(10)#", chr(10), "all"))#</pre><cfelse>&nbsp;&nbsp;#htmlEditFormat(val)#</cfif></font></td>
     	</tr>
             </cfif>
         </cfloop>

If you already patched with the first version, apply this one instead to get just the upgrade:

Index: WEB-INF/exception/detail.cfm
===================================================================
--- WEB-INF/exception/detail.cfm	(revision 6066)
+++ WEB-INF/exception/detail.cfm	(working copy)
@@ -213,6 +213,12 @@
     <cfif isDefined("attributes.properties")>
 	<tr><td>
         <!--- Format and display miscellaneous exception properties. --->
+		<cftry>
+			<cfset isSql = getClass().forName("java.sql.SQLException").isAssignableFrom(getClass().forName(error.type)) />
+			<cfcatch type="java.lang.ClassNotFoundException">
+				<cfset isSql = false />
+			</cfcatch>
+		</cftry>
         <table border="0" cellpadding="0" cellspacing="0">
         <cfset exclude="localizedmessage,locale,detail,message,tagcontext,rootcause">

@@ -228,7 +234,7 @@
                 </cftry>
     	<tr>
     		<td valign="top"><font style="COLOR: black; FONT: 8pt/11pt verdana">#xmlFormat(m)#</font></td>
-    		<td><font style="COLOR: black; FONT: 8pt/11pt verdana"><cfif error.type EQ "java.sql.SQLException" AND listFindNoCase("sql,parameters", m) GT 0><pre>#htmlEditFormat(REReplace(val, "#chr(10)#\s*#chr(10)#", chr(10), "all"))#</pre><cfelse>&nbsp;&nbsp;#htmlEditFormat(val)#</cfif></font></td>
+    		<td><font style="COLOR: black; FONT: 8pt/11pt verdana"><cfif isSql AND listFindNoCase("sql,parameters", m) GT 0><pre>#htmlEditFormat(REReplace(val, "#chr(10)#\s*#chr(10)#", chr(10), "all"))#</pre><cfelse>&nbsp;&nbsp;#htmlEditFormat(val)#</cfif></font></td>
     	</tr>
             </cfif>
         </cfloop>

Hope it makes your life as much easier as it makes mine.

I Had a Doubt (About CFGroovy2 and Ehcache)

A couple months ago I added Ehcache support to CFGroovy2 as an alternative to the simple HashMap/WeakReference caching that had been there since it's inception.  I waffled a little bit at the time, but it seemed like the right thing to do.  I've changed my mind, and removed the Ehcache functionality in the latest build.  If you upgrade and were using Ehcache before, you'll need to either restart your container or manually delete server.cfgroovy.

The <g:manager> tag that I added is still supported in it's entirety, though it's reporting capabilities were really designed for the Ehcache mechanism, so it's not of as much use with the simple caching mechanism.  However, you can still clear the cache using it, if you wish.

Another minor tweak has also snuck in: better wrapping of output streams.  The Rhino implementation apparently requires a java.io.PrintWriter for it's Writer, where I'd been supplying a java.io.StringWriter.  I was programming against the API, but Rhino is more strict.  However, I've changed it to a java.io.PrintWriter so Rhino will work.  Other languages that only required the Writer type continue to work as before.

imageTranslateDrawingAxis is Your Friend

I briefly mentioned imageTranslateDrawingAxis in my previous post about Fusebox flowcharts, but wanted to provide a more self-contained example of how it can be used to great effect.  The idea here is to draw a house with a window, and to do it in a relative manner, rather than an absolute manner.  Consider these two functions:

function drawHouse_absolute(img, x, y, width, height) {
  imageDrawRect(img, x, y + height * ROOF_PROPORTION, width, height * (1 - ROOF_PROPORTION));
  imageDrawLine(img, x + width / 2, y, x, y + height * ROOF_PROPORTION);
  imageDrawLine(img, x + width / 2, y, x + width, y + height * ROOF_PROPORTION);
}
function drawWindow_absolute(img, x, y, width, height) {
  imageDrawRect(img, x, y, width, height);
  imageDrawLine(img, x, y + height / 2, x + width, y + height / 2);
  imageDrawLine(img, x + width / 2, y + 0, x + width / 2, y + height);
}

If you were to use them like this:


You might get an image like this:

As you can see, we have to do a lot of math in the functions, and it's all x/y anchored.  Even worse, we have to specify absolute coordinates (the "20, 60″ in the argument list to drawWindow_absolute), rather than specifying them relative to the house.

Instead, consider these two functions that do the same thing:

ROOF_PROPORTION = 0.35;
function drawHouse(img, width, height) {
  imageDrawRect(img, 0, height * ROOF_PROPORTION, width, height * (1 - ROOF_PROPORTION));
  imageDrawLine(img, width / 2, 0, 0, height * ROOF_PROPORTION);
  imageDrawLine(img, width / 2, 0, width, height * ROOF_PROPORTION);
}
function drawWindow(img, width, height) {
  imageDrawRect(img, 0, 0, width, height);
  imageDrawLine(img, 0, height / 2, width, height / 2);
  imageDrawLine(img, width / 2, 0, width / 2, height);
}

I've removed the 'x' and 'y' arguments and simplified the calculations to all be origin anchored.  This makes things a bit easier to manage, both inside the function and outside.  To draw the same picture, you'd use them like this, combined with my friend the 'imageTranslateDrawingAxis' function:




Remember the 'x' and 'y' arguments we passed in the first example?  Well they're still here, they're just split up.  The (10, 10) we passed to drawHouse_absolute is now directly visible in the first line, and the (20, 60) we passed to drawWindow_absolute is visible in the third line (keeping in mind that translation is cumulative; 10 + 10 == 20 and 10 + 50 == 60).

So we're still drawing the same picture, but now we have simpler drawing routines, and the same ability to control where stuff lays out.  But the big payoff is when we nest translations.

Consider this final function (which delegates to drawWindow from above):

function drawHouseWithWindows(img, width, height) {
  var i = 0;
  imageDrawRect(img, 0, height * ROOF_PROPORTION, width, height * (1 - ROOF_PROPORTION));
  imageDrawLine(img, width / 2, 0, 0, height * ROOF_PROPORTION);
  imageDrawLine(img, width / 2, 0, width, height * ROOF_PROPORTION);
  for (i = 5; i LTE arrayLen(arguments); i += 2) {
    // we have pairs of window coordinates, so lets draw windows
    imageTranslateDrawingAxis(img, arguments[i - 1], arguments[i] + height * ROOF_PROPORTION);
    drawWindow(img, width / 5, width / 5); // they don't get to pick the size :)
    imageTranslateDrawingAxis(img, -1 * arguments[i - 1], -1 * (arguments[i] + height * ROOF_PROPORTION));
  }
}

Lines 3-5 are identical to drawHouse above, but I've added support for an arbitrary number of coordinate pairs specifying where windows ought to be in relation to the "wall" of the house.  With this single function, we can draw the same image a third time like this:


Same translation to place the house at (10, 10), but this time we're letting drawHouseWithWindows take care of translation for drawing the windows, and we can specify the window location relative to the house itself.  This is hugely powerful, if you think about it.  When you're drawing a house, you don't draw the window based on global space, you draw it relative to the house.  As the house moves, the window moves too.

It might not be obvious, but this also lets us package up the offsets needed for the height of the roof in a totally transparent way, all for free.  Before we had to explicitly specify them in our positioning: the 50 in the second translation of the second example is really the 15 in this third example, plus a 35 for the roof height (which I manually figured out based on the height of the house (100) and ROOF_PROPORTION (0.35)).  So that 50 is really violating DRY, because it's specifying the height of the roof (which is defined in drawHouse).

If you're doing any sort of complex drawing, not having to pass around x/y coordinates everywhere will save you a significant amount of work, keep your code a lot simpler and easier to read, and let you avoid the double-encoding of certain types of relative data.

Most of the interesting code is already here, but check out drawing.cfm.txt if you want the full test script that I used for building the examples and generating the image above.  Just save the file with a .CFM extension and hit it in your browser. How to get no deposit forex bonus? How to fulfill the conditions for trade turnover to withdraw bonus? Which forex broker is better to take a no deposit bonus and what docs are needed? The current list of Forex no deposit bonuses is on page , here with a list of bonuses of forex brokers (binary options) you'll see all the conditions for getting free money and withdrawal them from the trading account. Firstly, try bonuses from Roboforex, Instaforex, FBS. These are reliable Forex brokers who quickly transfer bonus money and allow to withdraw trading profit received during working with the bonus.

Fusebox XML Flowchart Generator

About a week ago someone posted on the Fusebox mailing list looking for a way to generate flowcharts from his Fusebox XML files.  Adalon was suggested, but it didn't do quite what he was looking for.  So I sunk some of my spare time into building such a tool, and even managed to refrain from using Groovy and kept it all CFML.  All the layout stuff is custom, and the drawing is all via the CF8 image functions.  The imageTranslateDrawingAxis function, in particular, was of great utility, allowing me to do all my drawing in local space, rather than absolute space.  This means that every box starts at (0, 0), regardless of where it eventually ends up on the chart, so the drawing of each element is totally encapsulated on it's own coordinate space.

It's easiest to show with an example, so consider this contrived circuit.xml:

chome<circuit>

  <fuseaction name="home">
    <xfa name="page" value="page" />
    <if condition="structKeyExists(attributes, 'name')">
      <true>
        <loop collection="attributes" item="i">
          <set name="m" value="#i#" />
        </loop>
        <set name="message" value="Hello, strange unknown person!" />
      </true>
      <false>
        <set name="message" value="Hello, #name#!" />
        <relocate />
        <xfa name="exit" />
      </false>
    </if>
    <include template="dsp_home" contentvariable="bodyContent" />
    <do action="lay_default" />
  </fuseaction>

  <fuseaction name="page">
    <include template="dsp_page" contentvariable="bodyContent" />
    <do action="lay_default" />
  </fuseaction>

  <fuseaction name="lay_default">
    <if condition="structKeyExists(attributes, 'showNav')">
      <true>
        <do action="nav" contentvariable="nav" />
      </true>
      <false>
        <loop>
          <set name="message" value="Hello, strange unknown person!" />
          <do action="nav" contentvariable="nav" />
        </loop>
      </false>
    </if>
    <include template="lay_default" />
  </fuseaction>

  <fuseaction name="nav">
    <xfa name="home" value="home" />
    <xfa name="page" value="page" />
    <include template="dsp_nav" />
  </fuseaction>

</circuit>

cnavYes, I understand that it wouldn't actually execute in real life (there are a number of missing attributes), but that's not the point.  The generated flowcharts for the 'home' and 'nav' fuseactions are floating to the right. You can click the 'home' one to view it full size.

The UI is pretty simple, you enter the path to either a single circuit.xml file (as above), or your fusebox.xml file and hit 'Go'.  Then you can pick from the list of available fuseactions to act as your entry point, choose whether you'd like to include global pre/postprocess actions and pre/postfuseaction on the flowchart, and it'll draw it all out for you as a nice little PNG.

If you turn on the implicit fuseactions the charts can get pretty large pretty fast, but even generating some pretty gnarly charts for a sizeable app we have at Mentor didn't take more than a second or two.

If you'd just like to render a single circuit's stuff, you can point it directly at the circuit file, and you'll get an error box for any <do> verbs that point outside the circuit, so you'll still get a full chart, with the external dependencies called out explicitly.

I've made the code available in a zip archive if you'd like to run it yourself (or just poke around).  Just unzip it somewhere, and hit index.cfm in the browser.  The example circuit.xml above is included, so you can regenerate the demo charts I've shown here, or point it at your own Fusebox app and get a look at what you're doing.

The code itself is hardly an example of eloquent design, since this was just a little "screwing around" project.  The drawing stuff is pretty devoid of magic numbers, but I made no attempt to fully/properly decompose my types or to really enforce DRY throughout.  It is what it is: something to keep me from getting bored that I though might be of interest to others.

Fixing CF DB Error Messages

When you're working on someone else's code and you get an error message (because I know you'd never write code that errors), CF usually does a pretty good job of giving you the info you need to debug the issue.  But for some reason they don't show you query parameters, even though they're included in the exception object.  Fortunately, it's easy to get them.  Crack open /WEB-INF/exception/coldfusion/runtime/DatabaseException.cfm and apply this patch:

Index: WEB-INF/exception/coldfusion/runtime/DatabaseException.cfm
===================================================================
--- WEB-INF/exception/coldfusion/runtime/DatabaseException.cfm    (revision 5924)
+++ WEB-INF/exception/coldfusion/runtime/DatabaseException.cfm    (working copy)
@@ -11,6 +11,7 @@
     errorProperties = structNew();
     if(bRobustEnabled) {
         if( isdefined("error.sql")) errorProperties.sql = error.sql;
+        if( isdefined("error.where")) errorProperties.parameters = REReplace(trim(error.where), ",\s*\(", ",#chr(10)#(", "all");
         if( isdefined("error.datasource")) errorProperties.datasource = error.datasource;
         if( isdefined("error.nativeerrorcode") AND error.nativeerrorcode NEQ 0) errorProperties.vendorErrorCode = error.nativeerrorcode;
         if( isdefined("error.sqlState") AND error.sqlState NEQ "n/a") errorProperties.sqlState = error.sqlState;

It's hardly an elegant format, but it at least gives you the parameter values, which can be invaluable in troubleshooting.  But if you want to get even better, patch /WEB-INF/exception/detail.cfm as well.  This will PRE your SQL, which makes it MUCH easier to read.

Index: WEB-INF/exception/detail.cfm
===================================================================
--- WEB-INF/exception/detail.cfm    (revision 5924)
+++ WEB-INF/exception/detail.cfm    (working copy)
@@ -227,8 +227,8 @@
                 </cfcatch>
                 </cftry>
         <tr>
-            <td><font style="COLOR: black; FONT: 8pt/11pt verdana">#xmlFormat(m)#</font></td>
-            <td><font style="COLOR: black; FONT: 8pt/11pt verdana">&nbsp;&nbsp;#htmlEditFormat(val)#</font></td>
+            <td valign="top"><font style="COLOR: black; FONT: 8pt/11pt verdana">#xmlFormat(m)#</font></td>
+            <td><font style="COLOR: black; FONT: 8pt/11pt verdana"><cfif error.type EQ "java.sql.SQLException" AND listFindNoCase("sql,parameters", m) GT 0><pre>#htmlEditFormat(REReplace(val, "#chr(10)#\s*#chr(10)#", chr(10), "all"))#</pre><cfelse>&nbsp;&nbsp;#htmlEditFormat(val)#</cfif></font></td>
         </tr>
             </cfif>
         </cfloop>

With these patches applied, an error might generate a page like this.

If you wanted to go crazy, you could do what the Railo guys do and substitute the parameter values back into the SQL statement, so you get an "effective" SQL statement in the error, rather than the passed statement (with parameter placeholders).  I'm not sure that's necessarily good, since knowing what values are parameters and what are static can be useful, but for the common use case (wanting to copy and paste the query to debug it in your DB tool), it makes a lot of sense.

Note: These patches were made against CF 8.0.1.  They may or may not apply cleanly against other versions of CF.