Extending The Application.cfc ColdFusion Framework Component With A Relative-Path Proxy

Posted February 3, 2011 at 11:05 AM by Ben Nadel

Tags: ColdFusion

Yesterday, I demonstrated how to use a ColdFusion custom tag in order to define an Application.cfc body such that you could extend the current Application.cfc instance with a base Application.cfc using a relative path. In the comments to that post, Walter Seethaler dropped a knowledge bomb on us, explaining that you could accomplish the same thing by simply extending a local proxy that, itself, CFInclude'd the root Application.cfc. This might just be brilliant!

To quickly outline this approach, imagine that you have an Application.cfc in a sub-directory that needs to extend a root Application.cfc; but, you don't know what the web-root relevant class path for that root Application.cfc is. To use a relative path (relative to the sub-directory Application.cfc), you can extend a local proxy:

  • <cfcomponent extends="RootProxy">
  • <!--- Sub-directory App.cfc. --->
  • </cfcomponent>

This local proxy (RootProxy.cfc) then does nothing more than include the root Application.cfc using a relative path:

  • <!--- RootProxy.cfc -- Include root App.cfc --->
  • <cfinclude template="../../Application.cfc" />

Here, we are taking advantage of the fact that CFInclude works with relative paths and that one ColdFusion component can be CFIncluded into another ColdFusion component. In the end, we are using a relative path to extend a ColdFusion component.

Now, in the comments, Walter mentioned that there were some issues with object creation during the CFInclude process, so I wanted to make sure that I tested this thoroughly. Here is my testing directory structure:

./
./Application.cfc
./Test.cfc
./index.cfm
./sub/
./sub/Application.cfc
./sub/RootApplication.cfc
./sub/index.cfm

This time, you'll notice that there is a Test.cfc ColdFusion component in the root directory. There is nothing in this component (save a CFComponent tag); it is there simply to determine whether or not calls to the createObject() function will succeed or fail during various aspects of the page load.

Ok, let's dive into the code. First, let's take a look at our root-level Application.cfc file:

Application.cfc (In The Root-Directory)

  • <!--- NOTE: This is the ROOT Application.cfc file. --->
  •  
  • <cfcomponent
  • output="false"
  • hint="I define the application settings and event handlers.">
  •  
  •  
  • <!--- Define the application settings. --->
  • <cfset this.name = "AppExtendViaProxyInclude" />
  • <cfset this.applicationTimeout = createTimeSpan( 0, 0, 10, 0 ) />
  •  
  • <!---
  • Store the path of the current template. We want to see if
  • this shows up as the root template or the sub template.
  • --->
  • <cfset this.ROOT_currentTemplatePath = getCurrentTemplatePath() />
  •  
  • <!---
  • Try to create an instance of the Test CFC located in the
  • root. Will the createObject() call be relative to the
  • current template? Or, to the sub-directory template?
  • --->
  • <cftry>
  • <cfset this.ROOT_testCFC = createObject( "component", "Test" ) />
  • <cfcatch>
  • <cfset this.ROOT_testCFC = cfcatch.message />
  • </cfcatch>
  • </cftry>
  •  
  •  
  • <!--- ------------------------------------------------- --->
  • <!--- ------------------------------------------------- --->
  •  
  •  
  • <cffunction
  • name="onApplicationStart"
  • access="public"
  • returntype="boolean"
  • output="false"
  • hint="I initialize the application.">
  •  
  • <!--- Set some app variables for testing. --->
  • <cfset application.ROOT_onApplicationStart = true />
  •  
  • <!--- Return true so the page can process. --->
  • <cfreturn true />
  • </cffunction>
  •  
  •  
  • <cffunction
  • name="onRequestStart"
  • access="public"
  • returntype="boolean"
  • output="false"
  • hint="I initialize the request.">
  •  
  • <!--- Set some request variables for testing. --->
  • <cfset request.ROOT_onRequestStart = true />
  •  
  • <!---
  • Try to create an instance of the Test component located
  • in the root directory. Will the createObject() call be
  • relative to teh current template? Or the sub-directory
  • template?
  • --->
  • <cftry>
  • <cfset request.ROOT_testCFC = createObject( "component", "Test" ) />
  • <cfcatch>
  • <cfset request.ROOT_testCFC = cfcatch.message />
  • </cfcatch>
  • </cftry>
  •  
  • <!--- Return true so the page can process. --->
  • <cfreturn true />
  • </cffunction>
  •  
  •  
  • <cffunction
  • name="onRequest"
  • access="public"
  • returntype="void"
  • output="true"
  • hint="I process the user's request.">
  •  
  • <!--- Define arguments. --->
  • <cfargument
  • name="script"
  • type="string"
  • required="true"
  • hint="I am the request script."
  • />
  •  
  • --- START SCRIPT REQUEST ---<br />
  • <br />
  •  
  • <!--- Output the current THIS collection. --->
  • <cfdump
  • var="#this#"
  • label="THIS"
  • />
  •  
  • <br />
  • <hr />
  • <br />
  •  
  • <!--- Include (execute) requested script. --->
  • <cfinclude template="#arguments.script#" />
  •  
  • <br />
  • --- END SCRIPT REQUEST ---<br />
  •  
  • <!--- Return out. --->
  • <cfreturn />
  • </cffunction>
  •  
  • </cfcomponent>

As you can see, we're going to be checking a number of things:

  • What does getCurrentTemplatePath() give us in the pseudo constructor.
  • Is createObject() relative to the current template in the pseudo constructor.
  • Is createObject() relative to the current template in the onRequestStart() event handler.
  • What does the THIS object look like within the onRequest() event handler.

Normally, I would execute the index.cfm in the root and show you the output; but, let's face it - when we're in the root of the application, there's nothing special going on. As such, let's proceed directly to the Application.cfc located within our sub-directory:

Application.cfc (In The Sub-Directory)

  • <!--- NOTE: This is the SUB Application.cfc file. --->
  •  
  • <cfcomponent
  • extends="RootApplication"
  • hint="I define the application settings and event handlers.">
  •  
  •  
  • <!--- Define the application settings. --->
  •  
  • <!--- Store the path of the current template. --->
  • <cfset this.SUB_currentTemplatePath = getCurrentTemplatePath() />
  •  
  • <!---
  • Try to create an instance of the Test CFC located in the
  • root. Will the createObject() call be relative to the
  • current template? Or, to the root-directory template?
  • --->
  • <cftry>
  • <cfset this.SUB_testCFC = createObject( "component", "Test" ) />
  • <cfcatch>
  • <cfset this.SUB_testCFC = cfcatch.message />
  • </cfcatch>
  • </cftry>
  •  
  •  
  • <!--- ------------------------------------------------- --->
  • <!--- ------------------------------------------------- --->
  •  
  •  
  • <cffunction
  • name="onSessionStart"
  • access="public"
  • returntype="void"
  • output="false"
  • hint="I initialize the session.">
  •  
  • <!--- Set some session variables for testing. --->
  • <cfset session.dateInitialized = now() />
  •  
  • <!--- Return out. --->
  • <cfreturn />
  • </cffunction>
  •  
  •  
  • <cffunction
  • name="onRequestStart"
  • access="public"
  • returntype="boolean"
  • output="false"
  • hint="I initialize the SUB request.">
  •  
  • <!--- Invoke the super onRequestStart() method. --->
  • <cfset super.onRequestStart( arguments[ 1 ] ) />
  •  
  • <!--- Set some request variables for testing. --->
  • <cfset request.SUB_onRequestStart = true />
  •  
  • <!---
  • Try to create an instance of the Test component located
  • in the root directory. Will the createObject() call be
  • relative to the current template? Or the root-directory
  • template?
  • --->
  • <cftry>
  • <cfset request.SUB_testCFC = createObject( "component", "Test" ) />
  • <cfcatch>
  • <cfset request.SUB_testCFC = cfcatch.message />
  • </cfcatch>
  • </cftry>
  •  
  • <!--- Return true so the page can process. --->
  • <cfreturn true />
  • </cffunction>
  •  
  • </cfcomponent>

In this Application.cfc ColdFusion component, we are testing the same functionality, roughly; how things work in the pseudo constructor vs. the event handlers. You'll notice, however, that this component extends the sub-directory-based RootApplication.cfc component. And, as I described above, this proxy does nothing more than CFInclude the root Application.cfc:

RootApplication.cfc (In The Sub-Directory)

  • <!--- This is the RootApplication.cfc in the SUB directory. --->
  •  
  • <!--- Include the root Application.cfc. --->
  • <cfinclude template="../Application.cfc" />

Ok, now we need to pull all of this together into a test. In our sub-directory, we have an index.cfm file which outputs the Application and request scopes, followed by a new instance of the local Application.cfc component. If you look back up in the root-level Application.cfc, however, you'll see in the onRequest() event handler that we also start off by CFDump'ing out the current THIS configuration (of the current request):

Index.cfm (In The Sub-Directory)

  • <!--- NOTE: This is the SUB index. --->
  •  
  • <cfdump
  • var="#application#"
  • label="Sub Application"
  • />
  •  
  • <br />
  •  
  • <cfdump
  • var="#request#"
  • label="Sub Request"
  • />
  •  
  • <br />
  •  
  • <cfdump
  • var="#createObject( 'component', 'Application' )#"
  • label="Sub Application.cfc Instance"
  • />

When we run the above page, we get the following output. Since there are a lot of interesting things going on here, I have marked up the CFDump output with numbers, which I will then explain below.


 
 
 

 
Extending Application.cfc With A Proxy Component That CFIncludes Another Application.cfc.  
 
 
 

In the above output, there is a horizontal line. The Application.cfc instance above the horizontal line is the THIS scope of the Application.cfc that has been implicitly instantiated by the ColdFusion application server as a way to process the incoming page request. The Application.cfc instance below the horizontal line is a completely new instance that we manually instantiate and output. I make sure to draw this distinction because these two modes of instantiation result in different behaviors.

Ok, let's take a look at the callouts in the above output:

  1. We can see that calls to the getCurrentTemplatePath() function return the appropriate paths. The root Application.cfc reports as the root version and the sub Application.cfc reports as the sub version.
  2. Within the sub-directory Application.cfc pseudo constructor, we could not create an instance of our root-directory Test.cfc component. This indicates that the pseudo constructor of the sub-directory Application.cfc executes relative to the sub-directory (and therefore cannot locate the Test.cfc class definition that resides in the directory above).
  3. In addition to #2, we also could not instantiate Test.cfc from within the pseudo constructor of our extended root-directory Application.cfc. Since the root Application.cfc is in the same directory as the Test.cfc component, this indicated that the pseudo constructor of the base Application.cfc (that we are extending) executes relative to the sub-directory.
  4. If you look at the code, you'll see that our sub-directory Application.cfc onRequestStart() event handler invokes the super-scoped onRequestStart() event handler. The use of "super" in this case allows us to invoke the onRequestStart() event handler defined within our root-directory Application.cfc. From the CFDump, you can see that we were able to instantiate our Test.cfc component. This indicates that the event handlers of our super component execute relative to the root-directory even when invoked from within our sub-directory.
  5. The event handlers within the sub-directory execute relative to the sub-directory (this is the expected behavior) and therefore, cannot instantiate the Test.cfc component located within the parent directory.
  6. This is from the instance of the sub-directory Application.cfc that we instantiated manually. You'll notice that when instantiated manually, we were able to create an instance of our Test.cfc from within the pseudo constructor of our base Application.cfc. This is the opposite of what we found in #3. It appears that the context of the pseudo constructor of the base component changes depending on the explicit vs. implicit nature of instantiation.

Another thing that I noticed (but did not show in the testing) was that within the pseudo constructor of the extended-by-CFInclude Application.cfc, there are no methods in either the THIS scope or the VARIABLES. Typically, you can use local methods within the pseudo constructor; but, not in the extended version - those methods are not available until within the event handlers.

NOTE: You could use unscoped methods (ie. no THIS or VARIABLES prefix); but, they still execute relative to the sub-directory.

So, there's a lot of stuff going on here; but, this totally works if you follow the one main caveat:

Don't do anything within your pseudo constructor.

Of course, you still need to define the application settings (ie. this.name, this.applicationTimeout, etc.) in the pseudo constructor; but, don't do any real file-based logic within your pseudo constructor such as trying to load component instances.

This might seem like a big limitation; but, in my experience, it's not really. The ColdFusion application framework provides hooks to the life-cycle of the page request by giving you event handlers. You really shouldn't need to go outside of these event handlers to initialize your application (too much). I know people who have some requirement to load template-specific DSN values in the pseudo constructor or boot up ColdSpring instances; but, I still don't fully understand these requirements, so I can't speak to them.

We've done a lot of exploration of the Application.cfc ColdFusion application framework component over the last few days. There's definitely a lot to absorb here; and, I'm sure the exploration is far from comprehensive. A huge thanks to Walter for bringing this particular approach to light.




Reader Comments

Feb 4, 2011 at 2:29 PM // reply »
5 Comments

Ben, Can we invoke application.cfc as web-service?


Feb 6, 2011 at 11:55 AM // reply »
11,238 Comments

@Manoj,

You cannot invoke Application.cfc directly; is there a reason you would want to?


Feb 9, 2011 at 8:21 AM // reply »
5 Comments

@Ben,
I was just asking for a security concern. So it means that the application.cfc file is a most secure file in a project.


Feb 9, 2011 at 8:47 AM // reply »
11,238 Comments

@Manoj,

Yes, Application.cfc is completely safe. If you try to access it directly from the URL, it will throw an error.


Feb 20, 2011 at 1:34 PM // reply »
2 Comments

Hi Ben - I just wanted to make sure I'm reading and implementing the appropriate solution based on my needs.

So - if I'm trying to use something like...

<cfset AddBioInfo=createobject("component", "../cfc/AddModule")>

Which I know fails - I can use the above instead? Or am I looking at the wrong solution for this problem? Thanks in advance.


Sep 20, 2011 at 3:56 PM // reply »
5 Comments

This technique as well as the earlier posted method using cfinclude work great in Adobe Coldfusion (9) but NOT in Railo (3.2.3). None of the application-scoped variables appear to pass through to the Application.cfc in the subdirectory. Instead, the entirety of the application scope contents is:

application.applicationname = '';

Any ideas anyone?


Aug 26, 2012 at 12:00 PM // reply »
1 Comments

Thanx for this. I have been wrestling with a related challenge wishing I could extend a dynamic Class path set in an ini since the dev and prod environs are dif. I combined the above solution with a Charlie Arehart solution from awhile ago and it seems to have done the trick in my situation. I will ditto the use of the word brilliant above, and yet simple. Very cool, intuitive and much appreciated.


Sep 25, 2012 at 2:25 PM // reply »
44 Comments

This post came in real handy when I had to set up a sandbox folder. Within the sandbox I needed the onApplicationStart method to load all the singletons, but I needed to override onRequestStart and onRequestEnd since they both include the site layout. Having a RootProxy.cfc allows me to extend the root-level Application.cfc and override those methods at the same time.


RDL
Mar 6, 2013 at 11:54 AM // reply »
1 Comments

Is it then necessary to create a different RootApplication.cfc file in every subdirectory that extends the root Application.cfc? I am having trouble getting a subdirectory Application.cfc to extend a RootApplication. also located in the root.


Post A Comment

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.

Please review the following issues:

Author Name:


Author Email:

Author Website:

Comment:

Supported HTML tags for formatting: <strong>bold</strong>   <em>italic</em>   <code>code</code>







  • Help Wanted - Find Your Next ColdFusion Job
Ben Nadel's Company - Epicenter Consulting Recent Blog Comments
May 17, 2013 at 7:42 PM
HashKeyCopier - An AngularJS Utility Class For Merging Cached And Live Data
Ben - thanks so much for posting these Angular articles and findings, they've been a huge help towards learning one of the more 'complex' JavaScript frameworks out there (IMO). I have been using Angu ... read »
May 16, 2013 at 5:01 PM
UPDATE: Parsing CSV Data Files In ColdFusion With csvToArray()
Your code was the closest thing I've found to obtaining some direction for converting ISO fields to values that CF can translate properly. Thank you for posting! ... read »
May 15, 2013 at 10:37 PM
Very Simple Pusher And ColdFusion Powered Chat
hi id making plz easy ... read »
May 15, 2013 at 6:07 PM
Making SOAP Web Service Requests With ColdFusion And CFHTTP
Ben, you once again saved my bacon at work. Thank you, thank you, thank you! ... read »
May 15, 2013 at 4:15 PM
What If All User Interface (UI) Data Came In Reports?
@Josh, Thanks! @Ben, I definitely recommend the David West book "Object Thinking" I've been quoting from. It goes deeply into the philosophy and history of OO programming. His breadth ... read »
May 15, 2013 at 11:36 AM
Ask Ben: Print Part Of A Web Page With jQuery
I found this helpfull when you need to keep (refresh) the original parent page after closing the iframe child print dialog (Hoping you're not using a form at this time so it won't submit again): On ... read »
May 14, 2013 at 7:13 PM
What If All User Interface (UI) Data Came In Reports?
@Jonah, If there's any books you'd recommend on the subject of domain modelling, I'd love to hear it. I just downloaded the free PDF of "Domain Driven Design Quickly". Figured I'd give it ... read »
May 14, 2013 at 6:57 PM
The UX Of Prototyping: Low-Fidelity Is The New High-Fidelity
@Phillip, I'm not sure I follow what you mean? Are you saying that you looked at the list of widgets provided by the jQuery UI and let that be your style guide? ... read »
InVision App - Prototyping Made Beautiful With Prototyping Tools