Skip to main content
Ben Nadel at the New York ColdFusion User Group (Jul. 2008) with: Clark Valberg and Simon Free and Dan Wilson
Ben Nadel at the New York ColdFusion User Group (Jul. 2008) with: Clark Valberg ( @clarkvalberg ) Simon Free ( @simonfree ) Dan Wilson ( @DanWilson )

Exploring The ColdFusion Component (CFC) Extension Lifecycle

By on
Tags:

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.

Want to use code from this post? Check out the license.

Reader Comments

14 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?

14 Comments

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.

15,663 Comments

@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.

14 Comments

@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?

15,663 Comments

@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.

14 Comments

@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.

I believe in love. I believe in compassion. I believe in human rights. I believe that we can afford to give more of these gifts to the world around us because it costs us nothing to be decent and kind and understanding. And, I want you to know that when you land on this site, you are accepted for who you are, no matter how you identify, what truths you live, or whatever kind of goofy shit makes you feel alive! Rock on with your bad self!
Ben Nadel