ColdFusion Per-Application Mappings Don't Work In The OnApplicationEnd() Event Handler
The ColdFusion application framework provides hooks into the full application and session life-cycles; but, to be honest, I hardly ever use the onSessionEnd() or onApplicationEnd() event handlers. This inexperience came to light the other day when I went to clean up data in the onApplicationEnd() event handler and it wasn't working. After some trial and error, I realized that the per-application mappings don't work in the onApplicationEnd() method. But, since the method is part of the Application.cfc, I could still use the mappings through explicit references; however, this only solves a small portion of the problem.
In a ColdFusion application, the per-application mappings are typically used for two things:
- Locating external files.
- Locating ColdFusion components and templates.
Since per-application mappings don't work in the onApplicationEnd() event handler, both of these actions will break when used in the onApplicationEnd() event handler. However, if you need to perform logic directly in the Application.cfc code, you can overcome some of these issues by referencing the "mappings" property directly on the THIS scope of the Application.cfc.
To demonstrate this approach, I've created a small application that quickly times-out. Then, in the onApplicationEnd() event handler, I'm comparing the expandPath() approach (which depends on per-application mappings) to an explicit reference:
<cfscript>
component
output = false
hint = "I define the application settings and event handlers."
{
// Define the application settings.
this.name = hash( getCurrentTemplatePath() );
// We're going to keep the application timeout really short since we are just
// demonstrating the interplay between the onApplicationEnd() event and the use
// of per-application mappings.
this.applicationTimeout = createTimeSpan( 0, 0, 0, 5 );
// Define the per-application mappings.
this.mappings[ "/" ] = getDirectoryFromPath( getCurrentTemplatePath() );
this.mappings[ "/root" ] = this.mappings[ "/" ];
this.mappings[ "/logs" ] = ( this.mappings[ "/root" ] & "logs/" );
this.mappings[ "/wwwroot" ] = ( this.mappings[ "/root" ] & "wwwroot/" );
// ---
// PUBLIC METHODS.
// ---
/**
* I teardown the application, consuming any remaining data.
*
* @applicationScope I am the application scope of the application that ended.
* @output false
*/
public void function onApplicationEnd( required any applicationScope ) {
// In the application-end (and session-end) event, we have to catch our own
// errors since will not be natively logged by ColdFusion (long-standing bug).
try {
// Log the expanded path that attempts to leverage the per-application
// mappings for the wwwroot path.
logItem( "wwwroot mapping", expandPath( "/wwwroot" ) );
// Since we are in the Application.cfc instance, even if the application has
// ended, we still get access to the THIS scope. As such, we can access the
// the per-application mappings definition.
logItem( "wwwroot map item", this.mappings[ "/wwwroot" ] );
} catch ( any error ) {
logItem( "onApplicationEnd Error", error );
}
}
// ---
// PRIVATE METHODS.
// ---
/**
* I log the given value to the log file (using cfdump).
*
* @label I am the label of the value (NOTE: Does not get logged in this version).
* @value I am the value being logged.
* @output false
*/
private void function logItem(
required string label,
required any value
) {
writeDump(
var = value,
label = label,
output = ( this.mappings[ "/logs" ] & "log.txt" ),
format = "text"
);
}
}
</cfscript>
You'll notice that I'm wrapping the entire onApplicationEnd() event handler in a try-catch. This is due to a long-standing bug in ColdFusion in which errors don't seem to get logged in the onApplicationEnd() and onSessionEnd() event handlers. But, that aside, when we run this code, we get the following log output (when the application times-out):
/Applications/ColdFusion10/cfusion/wwwroot/wwwroot
************************************************************************************/Users/ben/Sites/bennadel.com/testing/on-application-end/mappings/wwwroot/
************************************************************************************
As you can see, when we try to use the mapping with expandPath(), it ends up going into the ColdFusion root - not what we want; but, if we reference the mappings directly in the Application.cfc ColdFusion component properties, we can still use it to locate the proper file.
Now, this specific example works; but, if you try to execute a CFInclude or instantiate a ColdFusion component using a per-application mapping, that will likely break. Of course, you can consume existing components that live in the passed-in Application scope; but, if any of that encapsulated logic relies on mappings, those will break too. Given all of these limitations, it would be best to keep your onApplicationEnd() logic as simple as possible.
NOTE: This was tested in ColdFusion 10.
Want to use code from this post? Check out the license.
Reader Comments