Thursday 11 October 2012

CFML: Clarification (in my mind) as to how this.[various settings] work in Application.cfc

G'day:
This might be another one of those "Adam states the obvious" articles. Sorry, but I freely admit two things:
  1. not to know everything already;
  2. to be a bit slow on the uptake sometimes.
So if you fall into those categories too, then maybe this will be worthwhile to read. Apologies to the rest of you.


One of the good things about helping out out on various ColdFusion forums is that the problems people have are not necessarily based on things I find myself doing on a regular basis, so encourages me to consider different approaches to things, and to think outside my own personal box. And this means I broaden my exposure to various facets of ColdFusion that I might not normally be exposed to. I would say I've learned more about CF by helping people solve their challenges than I have from my day-to-day work or any amount of reading blogs or articles detailing other people's investigations. That's not to say you should immediately stop reading... hey... aah... come back!

Ahem. Anyway.

Yesterday a person with the rather interesting name of Aegis Kleais was having a challenge with application-specific custom tag paths. The full thread is here, but it's long and drawn out, so I'll summarise it all below to save you... um... clicking on a link and reading the original.

Oh, before I start, I'd like to personally acknowledge Jason Dean's help and persistence on the forums. Jason's one of the more-clued-up ColdFusion community members out there, so it's an excellent asset to Adobe's forums that he mans-the-pumps there (as it were). I also like Jason's style because - and he might disagree with me here - he can be almost as grumpy as I tend to be, and doesn't shy away from having and expressing a strong opinion when he has one.  Cheers Jason.

Right, what was Aegis asking about?

Aegis (I'm gonna steer clear of personal pronouns unless I get the wrong gender...) was investigating the usage of per-application custom tag paths.  Just some background here.  In the olden days (like up to and including CFMX7 I think) the only place one could set a custom tag path was in CFAdmin, which is a server-wide setting.  These days they're settable in Application.cfc, with the this.customTagPaths setting.  Aegis was leveraging this application-specific approach and had mapped a directory, then called a custom tag on a subsequent line of Application.cfc.  But it was erroring: it just wasn't finding the custom tag file.  If the tag was placed in the same directory as Application.cfc, it ran fine. If the tag call was placed in one of the event handlers within (eg: onApplicationStart() or onRequestStart()), it worked fine.  So it wasn't a syntax or pathing issue.  Weird.  I verified this behaviour on CF8->CF10, and Railo too.

This initially flumoxed me, and I was about to suggest it was a bug when something occurred to me, and I had a watershed moment when I suddenly worked out how Application.cfc works in regards to all those this-scoped settings we use.

I thought about any other code I might run:

component {
    this.foo = "bar";
    
    // other stuff here
    
}

That code sets a variable, this.foo.  That's all it does: sets a variable.  No other action takes place.  I might subsequently do something with that variable that makes excellent stuff happen, but that's not what's happening there.

So what's this code doing:

component {

    this.name            = "myApplicationName";
    this.customTagPaths    = "/path/to/the/custom/tags/";

    public void function onApplicationStart(){
        // stuff
    }
    
    // etc

}

We all recognise this as an Application.cfc file, but it's still just some CFML code, so what is this line of code doing:

    this.customTagPaths    = "/path/to/the/custom/tags/";

It's setting a variable.  That's all it's doing.  It does not say this:

    setCustomTagPaths("/path/to/the/custom/tags/");

If it was saying that, we could reasonably conclude that subsequent to that line of code any custom tag calls would find tags in  /path/to/the/custom/tags/.

But all it's doing is setting a variable.

So I found myself thinking, "OK, what's going on then?  How does this all work?"

It occurred to me that it seems like - under the hood - the ColdFusion server is doing this (pseudocode):

appSettingAndHandlers = new Application() // ie: instantiating Application.cfc

theApp.setName(appSettingAndHandlers.name) // because name was set as this.name, it's exposed as a public variable
theApp.setCustomTagPaths(appSettingAndHandlers.customTagPaths) // use the exposed value for customTagPaths to actually make the custom tag paths
//etc

if not theApp.started() then
    theApp.start()
    appSettingAndHandlers.onApplicationStart()
/if
// etc

So what happens is - the same as any other CFC instantiation - the "calling code" (which is within the inner workings of the ColdFusion server) instantiates Application.cfc, which causes all the pseudo-constructor code to run, and then it accesses the settings created in that code to perform various other actions / config / settings etc.  But the crux is that just like when we instantiate a CFC then use it, all the pseudo-constructor code completes before anything is done with the values. This makes sense. Doesn't it?

This actually explains a situation  had in the past wherein I was creating an application-specific mapping with this.mappings, then trying to use a path leveraging that mapping in my ormSettings.cfcLocation value.  And for the same reasons, this did not work.

Anyway, with a combination of Aegis's own investigations, and guidance from Jason any myself,the problem was sorted out.  And I now know something new.

Cool.

--
Adam