Ben Nadel
On User Experience (UX) Design, JavaScript, ColdFusion, Node.js, Life, and Love.
I am the chief technical officer at InVision App, Inc - a prototyping and collaboration platform for designers, built by designers. I also rock out in JavaScript and ColdFusion 24x7.
Meanwhile on Twitter
Loading latest tweet...
Ben Nadel at the jQuery Conference 2009 (Cambridge, MA) with:

Exploring The ColdFusion Component (CFC) Extension Lifecycle

By Ben Nadel on
Tags: ColdFusion

Warning: Before you read on, I should probably tell you that this blog entry has absolutely no practical value. In fact, the stuff I am doing here is a miscarriage of object inheritance. I am only doing this to gain a better understanding of the state of ColdFusion components as they are instantiated and extended.

The other day, it suddenly occurred to me that I didn't know what would happen if I used the pseudo constructor of a super-class component (a class being extended) to rename a method within the class. Would it break? Would it rename the super method? Would it rename the sub-class component method? I couldn't think of a reason why you would want to do this; but, not knowing how it would behave, I thought it was worth a little exploration.


 
 
 

 
 
 
 
 

This thought occurred to me because a few people at cf.Objective() mentioned that an Application.cfc component in a framework-based ColdFusion application can't use the traditional event-handlers. The reason for this would be that if the Application.cfc extended a "framework component", it would override the framework-based event handlers and would, essentially, break the framework. What if the framework component - the super-class - could change its own signature during instantiation? Could it manipulate both its classes and the sub-class methods to rewire the component at runtime?

No - not exactly. But, we can still have some fun with it!

As it turns out, during the pseudo-constructor phase of the super-class instantiation, the super-class has absolutely no knowledge of the sub-class that is extending it. This means that the super-class can't access the sub-class methods. However, this also means that during instantiation, the super-class has access to the original super-class methods. That is, even if the sub-class is overriding super-class methods, the super-class pseudo constructor has access to the original super-class versions of those methods.

So, while we cannot communicate down the inheritance chain, we can at least collect all the references to our original super-class methods. Then, if we get the sub-class to call up the inheritance chain from within its pseudo constructor, we can start to do some interesting method rewiring.

To demonstrate this concept, I have created an Application.cfc ColdFusion application framework component that extends a Framework.cfc. Both of these components contain standard event handlers; however, you'll see that the Application.cfc doesn't have to use any special event handler names in order to avoid collisions.

Application.cfc - ColdFusion Application Framework Component

  • <cfcomponent
  • extends="Framework"
  • output="false"
  • hint="I define the application settings and event handlers.">
  •  
  • <!--- Initialize the framework. --->
  • <!--- ------------------------------------------------- --->
  • <!--- ------------------------------------------------- --->
  • <cfset this.init() />
  • <!--- ------------------------------------------------- --->
  • <!--- ------------------------------------------------- --->
  •  
  •  
  • <!--- Define the application settings. --->
  • <cfset this.name = hash( getCurrentTemplatePath() ) />
  • <cfset this.applicationTimeout = createTimeSpan( 0, 0, 0, 10 ) />
  • <cfset this.sessionManagement = true />
  • <cfset this.sessionTimeout = createTimeSpan( 0, 0, 0, 5 ) />
  •  
  •  
  • <cffunction
  • name="onRequest"
  • access="public"
  • returntype="void"
  • output="true"
  • hint="I process the requested script.">
  •  
  • <p>
  • I am the output provided by Application.cfc.
  • </p>
  •  
  • <!--- Return out. --->
  • <cfreturn />
  • </cffunction>
  •  
  • </cfcomponent>

As you can see, the code here is fairly typical. The Application.cfc extends "Framework" and then goes on to define the application settings and event handlers (I've only provided one event handler for brevity). The only thing that is curious at all is the call to this.init() from within the pseudo constructor.

In this scenario, the Application.cfc is the sub-class in our inheritance chain. That means that during its pseudo constructor, the super-class has already been fully instantiated and merged with the current context (for lack of a better technical description). As such, the Application.cfc instance can invoke the init() method provided by the Framework.cfc that it is extending.

We'll get back to that concept in a second; but for now, when we make a request to the above ColdFusion application, we get the following output:

I am output provided by Framework.cfc

I am the output provided by Application.cfc.

I am output provided by Framework.cfc

If you look back at our Application.cfc-based onRequest() event handler, you'll see that it only provides output for the following:

I am the output provided by Application.cfc.

The other two lines (before and after the above line) are provided by the super-class, Framework.cfc. But, if the Application.cfc is providing its own onRequest() event handler, how is it possible that the correlating super-class method has an opportunity to provide pre and post event aspects?

The trick lies within the init() method that the sub-class pseudo constructor invokes. From within the init() method, the super-class swaps out all overridden versions of the event handlers with those originally provided by the super-class. It is then up to the super-class event handlers to provide any pre/post event aspects as well as invoke the extended, overridden versions of the event handlers.

This will become a bit more clear in the code; so, let's take a look at our super-class, Framework.cfc:

Framework.cfc

  • <cfcomponent
  • output="false"
  • hint="I define the Framework event handlers.">
  •  
  • <!---
  • The first thing we need to do in this component is store
  • a private reference to the methods defined in THIS class.
  • Since this class is designed to be extended, these method
  • references will be the SUPER CLASS method references.
  •  
  • NOTE: During inheritance-based initialization, the super
  • class has access to its own methods, but NOT that of the
  • sub-class.
  • --->
  • <cfset variables.superClassMethods = {
  • onApplicationStart = this.onApplicationStart,
  • onSessionStart = this.onSessionStart,
  • onRequestStart = this.onRequestStart,
  • onRequest = this.onRequest
  • } />
  •  
  • <!---
  • This will be populated eventually (in the init() method).
  • I am just setting it here so that we know it will exist.
  • --->
  • <cfset variables.subClassMethods = {} />
  •  
  •  
  • <cffunction
  • name="init"
  • access="public"
  • returntype="void"
  • output="false"
  • hint="I initialize the framework (I am called from the base component).">
  •  
  • <!--- Define the local scope. --->
  • <cfset var local = {} />
  •  
  • <!---
  • At the time this method is called, both the SUPER
  • class and the SUB-CLASS have been instantiated,
  • pseudo-constructed, and merged. As such, the THIS
  • reference at this point is that of the SUB-CLASS.
  •  
  • Store the reference to the sub-class methods, if
  • they exist.
  • --->
  • <cfloop
  • item="local.key"
  • collection="#this#">
  •  
  • <!---
  • Check to see that the key exists in both super
  • class and the sub-class method collections and that
  • they have different origins.
  • --->
  • <cfif (
  • structKeyExists(
  • variables.superClassMethods,
  • local.key
  • )
  • &&
  • !structKeyExists(
  • getMetaData( this[ local.key ] ),
  • "framework:origin"
  • )
  • )>
  •  
  • <!---
  • Copy the sub-class method reference into the
  • private method collection.
  • --->
  • <cfset variables.subClassMethods[ local.key ] = this[ local.key ] />
  •  
  • <!---
  • Override the sub-class version with the super
  • class version.
  • --->
  • <cfset this[ local.key ] = variables.superClassMethods[ local.key ] />
  •  
  • </cfif>
  •  
  • </cfloop>
  •  
  • <!--- Return out. --->
  • <cfreturn />
  • </cffunction>
  •  
  •  
  • <cffunction
  • name="onApplicationStart"
  • access="public"
  • returntype="boolean"
  • output="false"
  • hint="I initialize the application."
  • framework:origin="super">
  •  
  • <!--- Return true so the application can load. --->
  • <cfreturn true />
  • </cffunction>
  •  
  •  
  • <cffunction
  • name="onSessionStart"
  • access="public"
  • returntype="void"
  • output="false"
  • hint="I initialize the session."
  • framework:origin="super">
  •  
  • <!--- Return out. --->
  • <cfreturn />
  • </cffunction>
  •  
  •  
  • <cffunction
  • name="onRequestStart"
  • access="public"
  • returntype="boolean"
  • output="true"
  • hint="I initialize the request."
  • framework:origin="super">
  •  
  • <!--- Return true so the request can load. --->
  • <cfreturn true />
  • </cffunction>
  •  
  •  
  • <cffunction
  • name="onRequest"
  • access="public"
  • returntype="void"
  • output="true"
  • hint="I process the requested script."
  • framework:origin="super">
  •  
  • <!--- Define arguments. --->
  • <cfargument
  • name="script"
  • type="string"
  • required="true"
  • hint="I am the script requetsed by the user."
  • />
  •  
  • <!--- Define the local scope. --->
  • <cfset var local = {} />
  •  
  • <!---
  • Declare a reference to the subClassMethod. We need to do
  • this because ColdFusion has a problem invoking the method
  • as a property of the local scope.
  • --->
  • <cfset var subClassMethod = "" />
  •  
  • <!--- Output a header. --->
  • <h1>
  • I am output provided by Framework.cfc
  • </h1>
  •  
  • <!---
  • Check to see if the onRequest method has been defined
  • by the sub-class.
  • --->
  • <cfif structKeyExists( variables.subClassMethods, "onRequest" )>
  •  
  • <!---
  • Get a reference to the method. We need to do this
  • rather than directly invoking it to get around some
  • strange ColdFusion invocation rules (which I believe
  • were fixed in CF9).
  • --->
  • <cfset subClassMethod = variables.subClassMethods.onRequest />
  •  
  • <!--- Invoke the sub-class method. --->
  • <cfset subClassMethod( argumentCollection = arguments ) />
  •  
  • </cfif>
  •  
  • <!--- Output a footer. --->
  • <p>
  • I am output provided by Framework.cfc
  • </p>
  •  
  • <!--- Return out. --->
  • <cfreturn />
  • </cffunction>
  •  
  • </cfcomponent>

As you can see, the first thing we do in the pseudo constructor is create a collection of references to the original super-class methods. This way, once our inheritance chain is complete, our init() method will still be able to easily access both versions of each event handler. Then, post-instantiation, our init() method swaps out the sub-class versions with the original super-class versions. And, if you look at the super-class onRequest() event handler (the only one fleshed out for brevity), you'll see that it can provide pre and post event aspects while still invoking the onRequest() event handler from our sub-class (Application.cfc).

In essence, the super class is become more like a proxy to the sub-class and less like an inherited object.

When it comes to application architecture, I don't know all that much about frameworks; I'm still very much a CFM-based processing kind of guy. I use the Framework concept in this exploration because it was the only situation that I could possibly think of in which event execution might want to move down the inheritance chain rather than up it.

And again, I am not advocating this approach at all. I was just using this as a context for learning more about the ColdFusion component extension lifecycle. If nothing else it was just an interesting way to see what aspects of ColdFusion components were available during the various portions of CFC instantiation.



Reader Comments

"As it turns out, during the pseudo-constructor phase of the super-class instantiation, the super-class has absolutely no knowledge of the sub-class that is extending it."

In my experience it's better to think of the entire "pseudo-constructor" block of a CFC as its type definition; the only thing it's "constructing" is the type def for that cfc before it sends it back to the calling code. The behavior you've demonstrated here seems to show that to be a more correct way of thinking about it.

While ColdFusion allows any code to go in the pseudo-constructor/type definition block for sanity's sake any code which is not involved in defining the component (declaring properties, fields, & methods) should be moved to a method.

Also: what's with the

  • <cfset var local = {} />

declaration? You still primarily on CF8 Ben?

Reply to this Comment

Also, I meant to mention that your statement which I quoted makes sense, because at the pseudo-constructor phase of the cfc definition is in the process of being loaded, so there's no way for it know about anything but itself.

Reply to this Comment

@Graeme,

Yeah, I still mostly code on CF8. We have one CF9 box, but for that project, I mostly work on the SQL stuff, not much of the actual ColdFusion code. And, my blog still runs on CF8.

I need to get on CF9 though - I've just been lazy about it.

I agree with you as far as what the pseudo constructor does; or, at the very least, that anything non-static should probably just be in a function rather than the pseudo constructor.

The only place where I would probably consider using serious logic in the pseudo constructor would be in the Application.cfc where you might need to change settings on a per-request basis (which does go to define the Type, in a way).

Mostly though, I just never thought about how extended objects exist / work before they are actually tied together. From a technical standpoint, I am a bit clueless as to the architecture. So, at least, I can poke around at the functionality.

Reply to this Comment

@Ben,

I think with the Application.cfc it can be hard to completely avoid logic in the pseudo-constructor, sometimes you want to tweak an application setting one way or another depending on certain conditions (such as what environment you're running in: dev/test/production/etc.)

Poking around/experimenting with a given platform is one of my preferred ways of learning about it. Have you tried using a Java bytecode viewer (such as jclasslib), opening the cfusion.jar file, and looking through the classes that the CF Platform operates on top of?

Reply to this Comment

@Graeme,

Yeah, the only way that Application.cfc could get away with that is if ColdFusion started using an implicit init() method on all components. They seem to be moving in that direction with the "new" keyword.

Picking stuff apart is awesome! I haven't really looked at much byte code since I don't really know how to examine that stuff. I think I have decompile stuff, right? One of these days, I'll have to poke around a bit more.

Reply to this Comment

@Ben,

No decompile necessary; check out some screenshots here: http://www.ej-technologies.com/products/jclasslib/screenshots.html

You don't see actual bytecode until you open the Code "folder" within a method. Otherwise the class definition is laid out in a fairly easy to navigate tree structure.

As for reading and understanding the bytecode itself, the way I did it was to literally have the JVM Spec open on the instructions reference page (http://java.sun.com/docs/books/jvms/second_edition/html/Instructions2.doc.html) and just go over it line by line. After awhile you don't need the reference unless you encounter some rare instruction.

Reply to this Comment

Post A Comment

You — Get Out Of My Dreams, Get Into My Comments
Live in the Now
Oops!
Comment Etiquette: Please do not post spam. Please keep the comments on-topic. Please do not post unrelated questions or large chunks of code. And, above all, please be nice to each other - we're trying to have a good conversation here.