ColdFusion Application.cfc Properties Get Normalized In Interesting Ways
As I was working on my Scotch on the Rocks (SOTR) presentation this weekend, I noticed something that I don't think I have ever noticed before: the properties that you define in the Application.cfc pseudo-constructor change once they are made available in the application event handlers. When I look back on my blog, I can see that this change was apparent in my post on accessing ColdFusion application settings; but, even at that time, I don't think I was fully aware of what was actually happening.
The change in Application.cfc component properties takes place in two ways: 1) the Application.cfc property set is fleshed out to contain all of the possible application settings, using the default values for any appended properties. And 2) the timeouts defined in terms of days (createTimeSpan()) get translated into the number of seconds. To see this in action, let's take a look at the following Application.cfc:
Application.cfc
<cfcomponent
output="false"
hint="I define application settings and event handlers.">
<!---
Define application settings - not that the timeouts are
defined using createTimeSpan() which will give us decimal-
representations of days.
--->
<cfset this.name = hash( getCurrentTemplatePath() ) />
<cfset this.applicationTimeout = createTimeSpan( 0, 0, 2, 0 ) />
<cfset this.sessionManagement = true />
<cfset this.sessionTimeout = createTimeSpan( 0, 0, 1, 0 ) />
<!--- Store a copy of the THIS scope for later comparison. --->
<cfset this.copy = {
name = this.name,
applicationTimeout = this.applicationTimeout,
sessionManagement = this.sessionManagement,
sessionTimeout = this.sessionTimeout
} />
<cffunction
name="onRequest"
access="public"
returntype="void"
output="true"
hint="I process the requested script.">
<!--- Include the script name. --->
<cfinclude template="#arguments[ 1 ]#" />
<!--- Return out. --->
<cfreturn />
</cffunction>
</cfcomponent>
As you can see, I am setting the application settings in the Application.cfc pseudo-constructor. But, after I do that, I create a copy of the settings in a separate property, copy, for later output and comparison. Then, in the onRequest() event handler, I include the requested script so as to make the Application.cfc THIS scope available to the mixin. In my demo, the mixin and requested script was this page:
<!---
Because this template was included via the onRequest()
application event handler, it will have access to the
THIS properties as defined in the Application.cfc pseudo
constructor.
--->
<cfdump
var="#structCopy( this )#"
label="THIS :: Event Handler"
showudfs="false"
hide="copy"
/>
<br />
<!---
In the psuedo constructor, we copied the values out of the
this in a deep-copy way. As such, these should be separate,
but same.
--->
<cfdump
var="#this.copy#"
label="THIS :: Pseudo-Constructor"
/>
As you can see, I am simply outputting the THIS-based properties and the "copy" properties to see how they compare. I am using structCopy() here so that I can use CFDump's "hide" attribute (which ColdFusion components appear to ignore). When we run the above test page, we get the following CFDump output:
As you can see, the factional-day timeouts defined in the pseudo-constructor get translated to a number-of-seconds by the time our Application.cfc event handlers are executed.
Now, call me crazy, but I am pretty sure that I remember using seconds to define timeouts way back in the day before I discovered the createTimeSpan() function. And, I am pretty sure that I don't ever remember running into this kind of problem (ie. that 3600 seconds wasn't suddenly misunderstood as 3600 days). To re-visit this concept, I tried setting the timeouts using integers rather than the createTimeSpan() function:
Application.cfc (Timeouts as Integers)
<cfcomponent
output="false"
hint="I define application settings and event handlers.">
<!---
Define application settings - note that the timeouts are
defined using a number of seconds.
--->
<cfset this.name = hash( getCurrentTemplatePath() ) />
<cfset this.applicationTimeout = 120 />
<cfset this.sessionManagement = true />
<cfset this.sessionTimeout = 60 />
<!--- Store a copy of the THIS scope for later comparison. --->
<cfset this.copy = {
name = this.name,
applicationTimeout = this.applicationTimeout,
sessionManagement = this.sessionManagement,
sessionTimeout = this.sessionTimeout
} />
<cffunction
name="onRequest"
access="public"
returntype="void"
output="true"
hint="I process the requested script.">
<!--- Include the script name. --->
<cfinclude template="#arguments[ 1 ]#" />
<!--- Return out. --->
<cfreturn />
</cffunction>
</cfcomponent>
As you can see, this time both the application and session timeouts are defined in seconds (supposedly). Running the above request with the new Application.cfc properties, I get the following page output:
Ok, this is what I remember from back in the day - using seconds as timeouts work just fine.
But, after I ran this test, I remembered an old post from three years ago where I discovered that createTimeSpan() returns a Double data type. At the time, however, I don't think I really understood what I had stumbled across. I thought I was getting an error; but, what I think now was actually happening was that ColdFusion was using the data type to dictate how it made use the given value. To test this, I am going to create an Application.cfc that uses both the createTimeSpan() method as well as double-casted values:
Application.cfc (Timeouts Using CreateTimeSpan() and JavaCast())
<cfcomponent
output="false"
hint="I define application settings and event handlers.">
<!---
Define application settings - note that the timeouts are
defined using a both a createTimeSpan() method as well as a
javaCast()ed double value.
--->
<cfset this.name = hash( getCurrentTemplatePath() ) />
<cfset this.applicationTimeout = createTimeSpan( 0, 0, 2, 0 ) />
<cfset this.sessionManagement = true />
<cfset this.sessionTimeout = javaCast( "double", 60 ) />
<!--- Store a copy of the THIS scope for later comparison. --->
<cfset this.copy = {
name = this.name,
applicationTimeout = this.applicationTimeout,
sessionManagement = this.sessionManagement,
sessionTimeout = this.sessionTimeout
} />
<cffunction
name="onRequest"
access="public"
returntype="void"
output="true"
hint="I process the requested script.">
<!--- Include the script name. --->
<cfinclude template="#arguments[ 1 ]#" />
<!--- Return out. --->
<cfreturn />
</cffunction>
</cfcomponent>
With this new Application.cfc in place, I re-ran the test page and got the following page output:
Now, we really see what's going on here. ColdFusion is assuming that any double value is a day-span where as any non-double value is a second-span.
On one hand, I understand why ColdFusion is doing this; but, on the other hand, I think this kind of inference can be very confusing in a typeless language. Of course, as we are in a typeless language, we are only running into this issue when we try to force the language to be explicitly typed (ie. calling JavaCast()). As such, perhaps this is a non-concern. In any case, I think it's important to understand how the ColdFusion framework operates, especially for me as I am giving a presentation on it!
Want to use code from this post? Check out the license.
Reader Comments
Interesting... I guess most of us follow methods we saw once in some blog post or book long time ago, never wondering "Why do I put that exactly?!"...
Another interestingly potential argument as to why forcing a "typeless" language to strictly type is potentially a bad idea. I'm all for strong typing, but like you said so well yourself, "...it's important to understand how the ColdFusion framework operates".
Cool post Ben.
Somehow I think this post will come in handy at the most frustrating of times (as in to explain why something behaves oddly). Thanks, Ben!
@Zarko,
I guess in most cases stuff just works :) Only the rare cases when things blow up that it becomes critical to understand.
@Adam,
I feel like this must be a backwards-compatibility issue. Even in the livedocs, it tells you to use createTimeSpan() (even as far back as v7 - previous livedocs links no longer work). Nowhere does it say that you *can* use seconds to set these values.
I think pre-Application.cfc, maybe it was seconds and they are using the datatype to make it more backwards compatible. That is the only thing I can think of.
@Randall,
Ha ha, well then I'll see in the future :)
CLIENTSTORAGE: Registry? Really? :)
@Azadi,
Ha ha ha :) At least clientManagement is turned off.
We're still running on CFMX6.1. The documentation for the 'cfapplication' tag describes the 'applicationTimeout' attribute as follows:
Lifespan of application variables. CreateTimeSpan
function and values in days, hours, minutes, and
seconds, separated by commas.
We would need to have a look at the code of the CF runtime to see whether the interpretation of the value specified is based on the type of the value...
@Wouter,
I was trying to look up the MX6 documentation, but I could only find the MX7 version on Adobe Livedocs. I found a v6 link, but it was dead (silly Adobe). Looks like it has always been best practice to use createTimeSpan(). I wonder where I got all this second-based mumbo-jumbo from. I could swear I used that at some point (although maybe I am thinking of CFSetting/requesttimeout.
@Ben, I'm sure you were/are not the only one doing it that way!
Andrew Scott's comment kept getting flagged as spam:
I am not sure what you are trying to promote here, or even point out.
Are you saying that using the timespan is wrong in what it is setting or that when you use it as an integer is wrong?
Curious because the docs clearly spell out that the lifespan is real number of days, so if it is a percentage as what createTimeSpan offers would be correct.
Sorry just looking for a bit more clarification on the actual problem.
Good question - 99% of the time, you won't have to care about this difference. The only time you need to know about this is when you, for some reason, need to compare the current session / application timeout. For example, if you were to use this technique:
www.bennadel.com/blog/1686-Accessing-ColdFusion-Application-Settings.htm
... where you re-create the Application.cfc later in the request, you have to be conscious that the timeouts in the local Application.cfc instance are going to be in terms for fractional-days, NOT seconds.
So, let's say you are in the onRequest() event handler, because you are in the Application.cfc, you will have access to the THIS scope. In such a case, the following values will not be equal:
this.applicationTimeout
createObject( "component", "Application" ).applicationTimeout
The former would be in terms of seconds and the latter would be in terms of fractional-days.
That's the only case when you really ever need to care.