Ask Ben: Extending Application.cfc And OnRequestStart() With SUPER

Posted March 11, 2008 at 8:28 AM

Tags: Ask Ben, ColdFusion

Got one for ya... I'm extending application.cfc with ye ole' proxy.cfc trick. In root/Application.cfc I cfinclude a config.cfm file that has some sitewide variables in the request scope like upload path, dsn, root URL, etc... I do this in onRequestStart(). In my root/sub/Application.cfc that extends the root cfc, how can I get access to the request scope that the root cfc set up??? I must be doing something wrong...

again:
root/app.cfc sets request.ds in onrequeststart()
root/sub/app.cfc extends root/app.cfc
root/sub/index.cfm does not have request scope vars from /root/app.cfc available

I'm confused. HELP! :)

I have one word for you: SUPER. The SUPER scope is created when one ColdFusion component extends another one. It is available to the extending component and gives access from the extending component up into the extended component, sometimes referred to as the "base component" or "super component." The SUPER scope not only allows us to get access to values in the super component (which isn't necessary since those values are also available in the extending component), but more importantly, it allows us to execute functions in the super component even when those functions are being overridden by the extending ColdFusion component (as they will be in our demo).

To leverage this and pull in the configuration data that you have in your root Application.cfc ColdFusion component, we are going to use the SUPER scope to invoke the OnRequestStart() event method of the root Application.cfc from the sub Application.cfc. But first, let's take a quick look at the root Application.cfc to make sure we are all on the same page:

 Launch code in new window » Download code as text file »

  • <cfcomponent
  • output="false"
  • hint="Primary application event handler.">
  •  
  • <!--- Set application settings. --->
  • <cfset THIS.Name = "ExtendedAppDemo" />
  • <cfset THIS.ApplicationTimeout = CreateTimeSpan( 0, 0, 5, 0 ) />
  •  
  • <!--- Set page request settings. --->
  • <cfsetting
  • requesttimeout="10"
  • />
  •  
  •  
  • <cffunction
  • name="OnRequestStart"
  • access="public"
  • returntype="boolean"
  • output="false"
  • hint="Hanldes pre-page processing for each request.">
  •  
  • <!--- Define arguments. --->
  • <cfargument
  • name="Page"
  • type="string"
  • required="true"
  • hint="The template requested by the user."
  • />
  •  
  • <!--- Read in / process configuration file. --->
  • <cfinclude template="config.cfm" />
  •  
  • <!--- Return out. --->
  • <cfreturn true />
  • </cffunction>
  •  
  • </cfcomponent>

Not much going on here. The Application is defined and then the OnRequestStart() method just includes the configuration file and returns out (it returns true which means that rest of the page request can be processed - more on this in a bit).

The configuration file that we are reading in just sets some values that we can use for testing:

 Launch code in new window » Download code as text file »

  • <!--- Set up data source. --->
  • <cfset REQUEST.DSN = {
  • Source = "ExtendsAppDemo",
  • Username = "JazzyJazmin",
  • Password = "coolnhot"
  • } />
  •  
  • <!--- Get the root directory of the application. --->
  • <cfset REQUEST.RootDirectory = GetDirectoryFromPath(
  • GetCurrentTemplatePath()
  • ) />

Now, in a sub folder of the application, we are going to create another Application.cfc that extends the root one. In the following code, my extends path is really long since I don't have any mappings setup for this demo. I am going to shorten it for the output:

 Launch code in new window » Download code as text file »

  • <cfcomponent
  • output="false"
  • extends="personal.ben......app_extend2.Application"
  • hint="Secondary application event handler.">
  •  
  • <cffunction
  • name="OnRequestStart"
  • access="public"
  • returntype="boolean"
  • output="false"
  • hint="Hanldes pre-page processing for each request.">
  •  
  • <!--- Define arguments. --->
  • <cfargument
  • name="Page"
  • type="string"
  • required="true"
  • hint="The template requested by the user."
  • />
  •  
  •  
  • <!---
  • Since this application is extending the root
  • Application.cfc, let's leverage the pre-processing
  • power of the root OnRequestStart(). Check to see
  • what value (true/false) that the root application
  • would have returned for this request.
  •  
  • By calling SUPER.OnRequestStart( Page ), we are
  • giving the root application a chance to run logic
  • on the page request. **Remember that the
  • OnRequestStart() method can return false to kill the
  • current page request. Therefore, we have to check to
  • see what value would be returned and honor that.
  • --->
  • <cfif SUPER.OnRequestStart( ARGUMENTS.Page )>
  •  
  • <!--- Store the sub root directory folder. --->
  • <cfset REQUEST.SubDirectory = GetDirectoryFromPath(
  • GetCurrentTemplatePath()
  • ) />
  •  
  • <!--- Return out. --->
  • <cfreturn true />
  •  
  • <cfelse>
  •  
  • <!---
  • The root application returned false for this
  • page request. Therefore, we want to return
  • false to honor that logic.
  • --->
  • <cfreturn false />
  •  
  • </cfif>
  • </cffunction>
  •  
  • </cfcomponent>

In the sub Application.cfc, we don't have to define the application settings or the page request settings as those will all be inherited from the root Application.cfc. The only thing we have defined in this component is the OnRequestStart() method. Now, as you have experienced, once you have defined the OnRequestStart() method, the one in the base component will not be called; that is because you have overridden in. This is where our SUPER scope comes into play. Take a look at the first line of our OnRequestStart() method:

 Launch code in new window » Download code as text file »

  • <cfif SUPER.OnRequestStart( ARGUMENTS.Page )>

We're doing two very important things here. For starters, by calling SUPER.OnRequestStart(), we are invoking the OnRequestStart() event method of the root Application.cfc. This will read in your configuration file and prepare the REQUEST scope. Then, the rest of the sub Application.cfc OnRequestStart() event method will execute and set up sub-app specific data. And, to be honest, we could have simply left it at that and your problem would have been solved.

However, we don't want to just invoke the parent application's OnRequestStart() event method and then forget about it; that would be to ignore the work flow architecture of ColdFusion application event handling. If you look at the OnRequestStart() event method, you will see that it returns a Boolean value. This return value has the power to halt the execution of the page request; if it returns false, ColdFusion will not process the rest of the page. Of course, since we are invoking the root method programmatically (and not via the internal ColdFusion process), its return value is not inherently appreciated. Therefore, we have to take it upon ourselves to check the return value of the root OnRequestStart() event method and, if it returns false, we have to then turn around and return false in the sub application's OnRequestStart() event method. Not only does this follow the standard ColdFusion event handling model, it also allows us to move a lot of potential security and processing logic up into the root application.

To see if this works, let's create an index.cfm page in the sub directory that will output values from both Application.cfc event methods:

 Launch code in new window » Download code as text file »

  • <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
  • <html>
  • <head>
  • <title>Sub Index File</title>
  • </head>
  • <body>
  • <cfoutput>
  •  
  • <h1>
  • Sub Index File
  • </h1>
  •  
  • <h4>
  • Application Name
  • </h4>
  •  
  • <p>
  • #APPLICATION.ApplicationName#
  • </p>
  •  
  • <h4>
  • DSN
  • </h4>
  •  
  • <p>
  • Source: #REQUEST.DSN.Source#<br />
  • Username: #REQUEST.DSN.Username#<br />
  • Password: #REQUEST.DSN.Password#<br />
  • </p>
  •  
  • <h4>
  • Root Directory
  • </h4>
  •  
  • <p>
  • #REQUEST.RootDirectory#
  • </p>
  •  
  • <h4>
  • Sub Directory
  • </h4>
  •  
  • <p>
  • #REQUEST.SubDirectory#
  • </p>
  •  
  • </cfoutput>
  • </body>
  • </html>

Notice that we are expecting the configuration information from the root Application.cfc and its config.cfm file as well as the SubDirectory value as defined by the sub Application.cfc. Running the above code, we get the following output (I have truncated some of the really long paths):

Sub Index File

Application Name
ExtendedAppDemo

DSN
Source: ExtendsAppDemo
Username: JazzyJazmin
Password: coolnhot

Root Directory
C:\Inetpub\wwwroot\.............\app_extend2\

Sub Directory
C:\Inetpub\wwwroot\.............\app_extend2\sub\

There you go. As you can see, our sub application page request gets the configuration file data from the root as well as path to our sub directory.

Hope that helps.

Download Code Snippet ZIP File

Comments (10)  |  Post Comment  |  Ask Ben  |  Permalink  |  Other Searches  |  Print Page



Adobe ColdFusion 8.0.1 Update - Helping Programmers To Be Signifanctly Less Girlie - Download ColdFusion 8 Update 8.0.1 Now.

Reader Comments

I like. I too was wondering something similar, and failed to recognize that inheritance could apply here. Cool!

-Brian

Posted by Brian Hendel on Mar 11, 2008 at 9:17 AM


@Brian,

Glad to help.

Posted by Ben Nadel on Mar 11, 2008 at 9:36 AM


You're Too Fast, must need more 'real' work to do :)

This helped a lot. It's one of those things I have read about for the past few years I think since cfc's became available. It not until you actually need something that you realize just because you read it doesn't mean you learned anything.... I use CFC's very often, but rarely with inheritance. All of the proxy.cfc examples left this part out. thank you!

Oh, and yes.... SUPER is explained well in the adobe docs, but it's easy to forget that Application.cfc is still just a cfc with methods, so duh, the SUPER scope will work here...

Thank you very much!

Posted by Kevin Sargent on Mar 11, 2008 at 11:46 AM


@Kevin,

Always glad to help; tell your friends :)

Posted by Ben Nadel on Mar 11, 2008 at 11:51 AM


Sean has written an interesting way of extending root Application.cfc in past.

http://corfield.org/blog/index.cfm/do/blog.entry/entry/Extending_Your_Root_Applicationcfc

I have found this to be pretty useful.

Posted by Qasim Rasheed on Mar 11, 2008 at 2:40 PM


@Qasim Rasheed

Exactly, that's known as the application proxy method, which is what I use.

The problem arises when trying to make the variables created by the root cfc available to the sub/cfc. The use of the SUPER scope is designed specifically for that during inheritance situations.

Posted by Kevin Sargent on Mar 11, 2008 at 4:25 PM


I actually asked ben about this same issue and got nearly the same response about 3 weeks ago.

We sent a few emails back and forth and I decided to keep just one application.cfc and use the cgi.script_name to determine what was being accessed. Then I made a bunch of functions that could be called from onRequestStart.

Super worked just like Ben explained, but a word of caution was issued about getting the application messy with multiple application.cfcs

Posted by Justin on Mar 12, 2008 at 7:17 PM


@Justin,

Good point. To this day, I have never actually used more than one Application.cfc. I like the idea of all the requests funneling through one App file, especially if they are all gonna extend that one anyway.

Posted by Ben Nadel on Mar 13, 2008 at 7:35 AM


Continuing the discussion on extending application.cfc, I've created an application.cfc in my 'com' folder and use that as the skeleton for the site. I then put an application.cfc in the root folder that extends com.application.

Since I use a basic code structure for every app I develop, this allows me to copy my base code to a new folder, make a few changes to the root application.cfc (passing vars to the com.application init function, for example, that sets up the normal cfapplication type settings) and I'm ready to go.

In the base application cfc (com.application), I have functions that are basic to any app I'd create and should never have to touch. In the root application cfc, I add any I'll need for the new app.

Just thought I'd put in my two cents :).

Posted by Ed Martin on Apr 9, 2008 at 9:23 AM


@Ed,

Sounds pretty neat. I have always stayed away from that idea out of fear - fear that I would do something that would suddenly need me to change a whole bunch of other apps. I am not sure what that something would be, but I am afraid to find out.

But, it sounds like you have a good system going there. Good to hear it.

Posted by Ben Nadel on Apr 10, 2008 at 8:09 AM


Post Comment  |  Ask Ben


Home   |   Web Log   |   ColdFusion   |   Projects   |   Resume   |   Job Form   |   Search   |   Contact
Epicenter Consulting - Custom Software Solutions for Business Evolution HostMySite.com - The Leader In ColdFusion Hosting