ColdFusion

Using Inner-Classed Enums with ColdFusion and Java

I recently had to call a method from Java that required an Enum type. In my case I did not have control over the java code and the Enums were stored inside a Class called Enums. It was not readily apparent to me on how to access these inner-classed Enums and there wasn't any specific documentation that I could find on the topic. I struggled a bit with trying to make a string work (the true end result), using JavaCast and trying to instantiate the class and Enum directly. Thankfully the final solution was actually easier than expected. Learn how I solved this problem!

Adding a Polygon Search to CFMongoDB

CFMongoDB rocks my socks...when I need to use ColdFusion. I noticed that the CFMongoDB doesn't natively support a polygon search :( It's ok though because I wrote one and as far as our team's testing is concerned, it works.

I added a method to DBCollection.cfc directly under the find method called findWithin_Poly. It follows.

 

function findWithin_Poly(string loc, array poly){
  polyConverted = CreateObject("java","java.util.ArrayList").init(ArrayLen(poly));
  for ( i =1 ; i lte ArrayLen(poly) ; i++){
    polyConverted.add(poly[i]);
  }
  polyObject = variables.mongoConfig.getMongoFactory()
              .getObject("com.mongodb.BasicDBObject").init("$polygon", polyConverted);
  within = variables.mongoConfig.getMongoFactory()
              .getObject("com.mongodb.BasicDBObject").init("$within", polyObject);
  query = variables.mongoConfig.getMongoFactory()
              .getObject("com.mongodb.BasicDBObject").init(loc, within);
  var search_results = [];
  search_results = collection.find(query);
  return createObject("component", "SearchResult").init( search_results, structNew(), mongoUtil );
}

As you can see, the process is very straight forward. I build up a BasicDBObject according to the Mongo Java API and call a slightly different find method on the Mongo collection.

Parameters

The loc parameter is a string that represents the key of the document that holds the coordinates of the point. I used loc because that what I saw all over the documentation but it's clear about not needing to be called loc.

The poly parameter is an array of arrays. Each element of the poly array is itself an array of exactly two elements: an x and a y. It represents a list of points which describe the boundaries of the polygon.

Format

The format of the poly parameter is important - as is the data in the collection. CF will automatically make your array elements strings, but they need to be doubles - java doubles.

To do this, cast them with javacast(). See the example below.

 

poly = ArrayNew(1);
polyA = ArrayNew(1);
polyA[1] = javacast("double",3);
polyA[2] = javacast("double",3);
polyB = ArrayNew(1);
polyB[1] = javacast("double",8);
polyB[2] = javacast("double",3);
polyC = ArrayNew(1);
polyC[1] = javacast("double",6);
polyC[2] = javacast("double",7);
ArrayAppend(poly,polyA);
ArrayAppend(poly,polyB);
ArrayAppend(poly,polyC);

You would then use this to call the  findWithin_Poly function.

hits = people.findWithin_Poly("loc",poly);

Now, I haven't done this yet, but I would recommend doing it: add a utility method that would cast all of the elements to double. Simple and reduces clutter.

 

2 Important (Overlooked) Features of ColdFusion 9

Recently I was chatting with someone on our dev team and I realized that not everyone is as heads-down, buried in ColdFusion every day like I am. New features come and go (do they go?) and we latch on to what we can, when we need it. But depending on what you were doing back when CF9 released, you may have missed these. Yes.  these are old features already (4 years)  but real nice features just seem to slip through without being harnessed. these are (2) big deal items I see overlooked that you really should use:

local variables in functions

We all know about using the var keyword to declare local variables. if you don't already know, look it up. In CF8, we had to do all these var declarations at the top of the function. Pain in the ass. someone kick the compiler guy/gal.

Leave it to our CF community to come up with a brilliant solution that is one of the more elegant "standards" I've seen evolve in the CF world (along with init() pseudo-constructors). All ya gotta do is add this code at the top of every function:

var local = {}; // structNew()? get hip, use the squirrelly brackets!

Then, in your function code when you need a local variable, just use local.

what sucked? well, you have to use local. prefixes on all your local variables which is particularly lame with an lcvs (loop control variables), like i,j,k etc... sometimes you'd see folks declare these as local separately just so they could reference it using #i# or the like.

Overall, nice solution. but that was pre 2009; (eh hem, 4 YEARS AGO) - what's the gig with CF9?

First of all, in CF9 we can declare a var anywhere in the function. 'bout time. the compiler guy/gal got the message. but that's only half the story.

Them Adobe folks also made an implicit local scope in all functions, called... (wait for it, wait for it... drumroll) local. Yea, they scarfed the "standard" and rolled it in. nice job Adobe (i don't say those words too often these days, so I had to throw it in). Well, anyway, what are the perks to this new scope? (2) things:

  1. You don't have to declare the local structure at the top of the function. it's already there (but if you do, it's backwards compatible, so no worries)
  2. You don't have to reference the local variables using local. once they are initially set in the local. scope, you just reference them directly and they are found in the scope chain

thus:

for(local.i=1;i<10;i++){ writeOutput(i); // works like a charm }

Implicit setters and setters

Here's one i didn't figure out until i had to teach a class in ColdFusion 9 - ColdFusion allows you to add implicit getters and setters with one easy attribute to the <cfcomponent> tag: accessors="true"

of course Ben Nadel crawled behind the dashboard to check the wiring on this - pretty cool experiment he did too. At the end of the day it's not a huge deal, but here's whay I like it. If forces me to do things "the right way" and it makes it easy. That's pretty much what CF is all about.

Mike Pacella, one of our CF/Java ninjas got me in the habit of using getters() and setters() a lot - it seems a bit uptight sometimes and quite frankly, you dont have to do it for everything - but if your talking about instance properties on an object, well, yeah, you probably should. but it's a hassle to write getters and it feels like a waste of time when those methods just get and set. I write enough code, i dont need to do more. shut up, I know i can use snippets or generators, or whatever. I do. it's still a pain in the ass.

so here's the layup - you add accessors=true to the <cfcomponent tage and all your properties have getters and setters available by default. AH BUT WAIT how the hell does the compiler know what the run-time instance properties will be. this is ColdFusion, we don't declare those things to our compiler. This is the other thing i love about this feature - we  actually use <cfproperty and now, its a tag that's actually used by the compiler ISO just being a documentation scrap. I'm a bit of a documentaiton nut, so I like the idea that if I create this tag and thereby write documentation for my doc engine parser, I'm also writing code that is doing something - in this case defining properties for the implicit getters and setters. putting this all togther,  here's a little cfc code you might see:

<cfcomponent accessors="true"> <cfproperty name="Globals" type="struct" hint="the global variables, by reference." />

<cffunction name="init" access="public" returntype="obj"> <cfargument name="globals" required="no" default="#{}#" hint="reference to global variables" /> <cfscript> setGlobals(globals); </cfscript> <cfreturn this> </cffunction> </cfcomponent>

so that's it. if you wanna mess around with it, here's some code.

Singletons Business Objects in CFWheels

Like many application development frameworks, there is lots of plumbing within that you don't have to use, but hey, it makes a ton of sense. Singleton object management is a good use case. Now let's be clear, I could add something like ColdSpring to offload my object factory pattern to a separate  framework - that often makes sense, but depending on the client and how encapsulated you want to keep things, sometimes just a good clean way to do it on your own makes the most sense.

So we want to make instances of a component and keep these instances around beyond a request. In CFWheels, what's the best way?

As it turns out, the answer is mind-bogglingly simple - but we have to consider (2) things:

  1. Where do we create the object instances?
  2. How will we be able to reference them?

In straight-up ColdFusion (no framework), we have Application events like onRequestStart, onSessionStart, OnApplicationStart & onServerStart that answer question#1. As for how to reference them, well, that's easy too - we can just use the "scope" of choice for the persistence - "request, session,application or server" and your done. Of course you may need a trigger to "refresh" the cached instances (now you're adding some plumbing). Here's the "old school" way of handling this (or at least the non-framework way)

<cffunction name="onApplicationStart" returntype="boolean" output="false">
	<cfset buildCache() />
	<cfreturn true />
</cffunction>

<cffunction name="onRequestStart" returntype="boolean" output="false">
	<cfif structkeyExists(url,"reload")>
		<cfset onApplicationStart() />
	</cfif>
	<cfreturn true />
</cffunction>

<cffunction name="buildcache" hint="I'll build the app cache of singletons">
	<cflock scope="application" type="exclusive" timeout="10">
		<cfset util = createObject("component","lib.util.widget").init() />
	</cflock>
</cffunction>

But with CFWheels, we really should leverage the framework and not worry about micro-managing these events (although we certainly can if we want to get that granular) - so check it out. Answer #1: config/settings.cfm Answer#2: use the CFWheels set() function:

<cfscript>
	set(util= createobject("component","lib.util.widget").init());
</cfscript>

The Beauty of this solution is that settings.cfm only fires when the application loads (or reloads) - essentially (but not identical to) the onApplicationStart event. Secondly, to provide visibility of our variable anywhere in the application, using the set() function allows us to access the variable by using the get() function

<cfscript>
	util = get("util");
</cfscript>

please note: This solution is specifically for application singletons - behind the scenes, CFWheels is using the application scope for these settings - in fact, the documentation on config/settings.cfm explicitly states this file is for configuration - which is in fact a good use case for application level persistence, but not the only use case. Singleton (Business) Objects are also perfectly viable candidates for this same sort of persistence.

 

Migrating fckeditor 2.6.4 to ColdFusion 10

I moved a site with FCKEditor from ColdFusion 8 to ColdFusion 10. When testing the file browser capability I was seeing the following error:

The server didn't reply with a proper XML data. Please check your configuration.

The FCKeditor I was using relies on a ColdFusion connector that calls a custom function called FileUpload. This function is located in /editor/filemanager/connectors/cfm/cf_command.cfm. Since FileUpload() is a reserved function in ColdFusion 9+, the server was throwing a 500 error and the FCKeditor was displaying the less than useful error message above.

The fix was pretty easy. Just do a search and replace in /editor/filemanger/~. I replaced FileUpload with fckFileUpload. You will end up changing nine files total. That's it. I probably should have simply updated the editor, but there were some custom js configurations that I didn't want to track down. So... there you go.

More Fun with ColdFusion & Spreadsheets

I do like spreadsheets. They're basically glorified csv files and talk about the lowest-common-denominator for data - I'm pretty sure after the nuclear holocaust, the last two things left on earth will be cockroaches and data stored in spreadsheets... There are many ways to work with spreadsheets, but it's not uncommon to be pulling data into ColdFusion queries, massaging the data, and then stashing it. This is the sort of utility script work that I get asked to do on client sites like it's my job. Oh, wait... yeah yeah...

Like most data structures, ColdFusion queries are great for some jobs and stink for others. But then there are things about queries that should work well but they simply don't. If you have ever tried to generically output a query and you used the "queryname.columnlist" property, you know what I'm talking about. That column list is in alphabetic order, not in the order it appeared in a select statement, or in this case, in the order of the spreadsheet columns. Oh, and its translated to upper case, which is seriously ugly. This is not always a problem, however, when working with spreadsheets, it often is. Spreadsheet users usually put their columns in an order for a reason - and if you are working with a script to read a spreadsheet into a query, do a little dance and then write a new spreadsheet back out, generically (i.e. without hard-coding the column names in your script) - then using that columnlist property for the headers in the output spreadsheet is trouble.

Solution? use the getmetadata function on your query to get an array of structs that describe the columns in the query - and yes, that's an array, which means ordered, in this case from left-to-right in the spreadsheet. Nice! Also, the case of the values is completely preserved. Extra Nice!

One trick is that you now have an array of structs and what you usually want (for your output to the spreadsheet) is just a common-delimited list of the column names. The column names are all in the key "name" in each structure item in the array. Oh, look what i found (and by found i mean wrote)  http://www.cflib.org/udf/arrayofStructsToList

So, drop that <cffunction /> into a page and sprinkle in some of this sample code:  note, you'll also need a source xls file

<cfscript>
    fpath = expandPath(".");
    sep = request.vars.sep;
    source = listappend(fpath,"source.xlsx",sep);
    dest = listappend(fpath,"dest.xls",sep);
</cfscript>
<!--- pull the source spreadsheet into a query object --->
<cfspreadsheet action="read" src="#source#" query="q" headerrow="1" rows="2-9999" />

<cfoutput>ugly stuff:#q.columnlist#</cfoutput><br />
<cfoutput>much better:#arrayofStuctsToList(getmetadata(q),'name')#</cfoutput><br />

<!--- image you wanted to modify the data and do some calculations --->
<cfset queryAddColumn(q,"check",[]) />
<cfloop query="q">
    <cfscript>
      isok = "BAD";
        // do whatever you need to do here
        if (true) isok = "OK";
        querySetCell(q,"check",isok,currentrow);
  </cfscript>
</cfloop>

<!--- note the newly created column comes back in both calls. thats good! --->
<cfoutput>ugly stuff:#q.columnlist#</cfoutput><br />
<cfoutput>much better:#arrayofStuctsToList(getmetadata(q),'name')#</cfoutput><br />

<!--- create an output spreadsheet for the new data  --->
<cfset out = spreadsheetNew()>
<!--- Add header --->
<cfset spreadsheetAddRow(out, arrayofStuctsToList(getmetadata(q),"name"))>
<!--- format header if you like --->
<cfset spreadsheetFormatRow(out,{bold=true},1)>
<!--- drop in the data --->
<cfset spreadsheetAddRows(out, q)>
<cfset spreadsheetWrite(out, dest, true)>

Bugs with reading spreadsheets from ram:// disk

So this week I had to create a utility to whip together a few spreadsheets, zip them up and provide a download link.There are (2) good reasons (and probably more) to use the RAM disk for storing the files during the processing ISO traditional disk:

  1. Disk bloat: One of the challenges with these sorts of utility apps is orphaned files on the server and after a few months, years, disk space gets scarey. Of course we can clean up the files with a <cfdirectory action="delete" /> but there are times when you simply can't rely on that - an error may halt processing, the code to cleanup may not fire and there are those nasty little orphans again! Oh, and then there is the "I'm really lazy" part of programming that, during development, when I'm aborting processing all over the place (for debugging purposes) - well, i just don't want to see all those files getting orphaned...
  2. Performance: Disk I/O is slow, memory allocation is fast. 'nuff said.

ColdFusion 9 introduced the ram disk - basically, a "virtual"disk drive that CF treats like a regular disk system: You can write, read, delete,  even <cfinclude />. Anyway, use "ram://" as the "drive" specification and create *nix style paths and you are all set.

What are the cons? Well, the ram disk is volatile - if the server reboots it's all gone. There are some other limitations, like how much ram it will tie-up and the (lack of) restrictions on who can access what (it doesn't use traditional O/S ACLs). But for what i needed, it was perfect.

And so life was good. I was cranking out the spreadsheets (by copying existing templates) and was able to zip them up and deliver them through <cfcontent /> just fine, and dang fast. Then I went back and tried to actually access the spreadsheets after i copied them, to update some data in them before creating the zip. That's when I hit this little snafu.

It appears that when you write a spreadsheet to the ram disk, ColdFusion cannot access it through the spreadsheet functions or the <cfspreadsheet /> tag. I guess I'm not surprised - I'm not sure what (java) utility CF is using under the covers to access the spreadsheets, but i suspect the drivers cannot accept a ram disk file path. Bummer.

Back to using disk and hoping like hell my cleanups do their thing...

here is a sample code you can run to see it yourself:

<cfscript>
sep = createObject("java", "java.lang.System").getProperty("file.separator");
folder = "AF2E3351-1422-52FB-568E3B79A02D455F";
disk = {"dir" = "#expandpath('./')##folder#","list"="","fname"="","spreadsheet"="","err"="no errors"};
ram = {"dir" = "ram://#folder#","list"="","fname"="","spreadsheet"="","err"="no errors"};
if(NOT directoryExists(ram.dir)) directoryCreate(ram.dir);
if(NOT directoryExists(disk.dir)) directoryCreate(disk.dir);
source = listAppend(expandpath("./"),"template.xlsx",sep);
disk.fname = listAppend(disk.dir,"spreadsheet.xlsx",sep);
ram.fname = listAppend(ram.dir,"spreadsheet.xlsx","/");
fileCopy(source,disk.fname);
fileCopy(source,ram.fname);
ram.list = directoryList(ram.dir);
disk.list = directoryList(disk.dir);

try{
	disk.spreadsheet = spreadsheetRead(disk.fname);
}
catch(any e){disk.err = e.message;}

try{
	ram.spreadsheet = spreadsheetRead(ram.fname);
}
catch(any e){ram.err = e.message;}

writedump(disk);
writedump(ram);
</cfscript>

CF 9.0.01 code changes

Many of you may know, but CF 9.0.1 update added a *very important* security update - especially for hosted environments. This "vulnerability" has been around since CF6 - it's really a feature, but in the wrong hands, it's about as bad as it gets (thus, "vulnerability" in quotes)The issue is that with a little code like this:

createObject( "java", "coldfusion.server.ServiceFactory" ).getDataSourceService().getDatasources()

you can get a list of all the data sources on a server and more importantly, all the data source metadata stored in the ColdFusion server. Now many hosting companies disable access to internal ColdFusion Java components, but, if this restriction isn't in place, the datasource information is so blatantly vulnerable to unauthorized access, well, let's just say this is the reason why shared hosting is really never secure enough. A simple dump of the result of this serviceObject call would reveal the encrypted passwords; add a little Google search and a quick decrypt and you're in; with <cfdbinfo />, you are inspecting the data sources dynamically and querying anything you like. Chances are, somewhere on every shared host server there is at least one (1) dingbat who decided to stash unencrypted credit card info or passwords. Chaos ensues.... Well if you are like me, or my co worker Mike Pacella, we've used this type of service call in our development environments for some of our code generation tools; So today, when i went to use [his][[our] dao / gateway generator ColdGen, I threw an ugly error. CF 9.0.1 won't let me call that getDataSources