ColdFusion Application.cfc Tutorial And Application.cfc Reference
I sometimes do in-house presentations here at Nylon Technology. Last night, I was asked to prepare "something" for this morning. In a mad rush, I threw together this presentation on ColdFusion's Application.cfc component. I am sure most of you have seen this stuff before, and a lot of it is right in the ColdFusion documentation, but for some people at my office, this will be new stuff. I am covering things like Application.cfc events, variables, and some tips and tricks that might not be obvious.
I finished working on this last night at like 9:30 so you better believe I did NOT, nor do I care to proof read this :) It's advertised strickly "as is."
ColdFusion Application.cfc Tutorial For Nylon Technology
ColdFusion MX 7 introduced the Application.cfc ColdFusion component. This component replaces the traditional Application.cfm and OnRequestEnd.cfm ColdFusion application templates. Furthermore, if Application.cfc is present, both of these templates are ignored by the application.
In addition to replacing the request start/end functionality of the old application templates, Application.cfc provides hooks into many additional application level events including:
This fires when the application first runs. It is a single-threaded method call.
This fires when an individual session first runs. It is a single-threaded method call.
This fires when a page request first runs. It is a single-threaded method call.
This fires when the requested template needs to get processed.
This fires at the end of every page request. It is a single-threaded method call.
This fires at the end of every session. This will fire either for a session timeout or if the server is shutting down. It is a single-threaded method call.
This fires at the end of the application. This will fire for an application timeout of if the server is shutting down. It is a single-threaded method call.
This fires if an exception gets thrown and is not caught by the controlling code.
All of the above methods are optional. You have an Application.cfc that is completely empty. Also, I say that the above methods are single-threaded which means that you do not have to worry about locking the code within them. However, any and all of these methods can be called explicitly by other parts of the application (ex. calling OnApplicationStart() from OnRequestStart() for manually fired application re-initialization). In this case, the manually invoked events are NOT single-threaded and may be open to race conditions.
In addition to application event hooks, Application.cfc provides public properties that define the application in a manner similar to the CFApplication tag. The following are available THIS-scoped properties:
This is the name of the application and is used to tie a request to existing application memory scopes.
The time span an application will exist before it times out (if the application is not accessed in any way). This defaults to the value set in the ColdFusion administrator.
A boolean flag to determine if the SESSION scope should be used. This defaults to false.
The time span a session will exist before it times out (if the application is not access in any way by that session's user). This defaults to the value set in the ColdFusion administrator.
A boolean flag to determine if the CFID and CFTOKEN values are sent as cookies to the client's browser. This defaults to true.
A boolean flag to determine if the CFID and CFTOKEN values are set for domain and not just a host value. This defaults to false. I have never used this and so I am not exactly sure what this means.
A boolean flag to determine if the CLIENT scope should be used. This defaults to the value set in the ColdFusion administrator.
If client management is being used, this determines where the client variables are being stored (cooke, registry, or database). This defaults to the value set in the ColdFusion administrator.
The place login information is stored (cookie or session). I have never used this, so I am not exactly sure what that means. Defaults to cookie.
A boolean flag to determine if the variables will be protected from cross-site-scripting attacks. Again, I have never used this and I am not sure what it does. This defaults to the value set in the ColdFusion administrator.
Putting it all together, an Application.cfc ColdFusion component skeleton would look something like this:
<cfcomponent displayname="Application" output="true" hint="Handle the application."> <!--- Set up the application. ---> <cfset THIS.Name = "AppCFC" /> <cfset THIS.ApplicationTimeout = CreateTimeSpan( 0, 0, 1, 0 ) /> <cfset THIS.SessionManagement = true /> <cfset THIS.SetClientCookies = false /> <!--- Define the page request properties. ---> <cfsetting requesttimeout="20" showdebugoutput="false" enablecfoutputonly="false" /> <cffunction name="OnApplicationStart" access="public" returntype="boolean" output="false" hint="Fires when the application is first created."> <!--- Return out. ---> <cfreturn true /> </cffunction> <cffunction name="OnSessionStart" access="public" returntype="void" output="false" hint="Fires when the session is first created."> <!--- Return out. ---> <cfreturn /> </cffunction> <cffunction name="OnRequestStart" access="public" returntype="boolean" output="false" hint="Fires at first part of page processing."> <!--- Define arguments. ---> <cfargument name="TargetPage" type="string" required="true" /> <!--- Return out. ---> <cfreturn true /> </cffunction> <cffunction name="OnRequest" access="public" returntype="void" output="true" hint="Fires after pre page processing is complete."> <!--- Define arguments. ---> <cfargument name="TargetPage" type="string" required="true" /> <!--- Include the requested page. ---> <cfinclude template="#ARGUMENTS.TargetPage#" /> <!--- Return out. ---> <cfreturn /> </cffunction> <cffunction name="OnRequestEnd" access="public" returntype="void" output="true" hint="Fires after the page processing is complete."> <!--- Return out. ---> <cfreturn /> </cffunction> <cffunction name="OnSessionEnd" access="public" returntype="void" output="false" hint="Fires when the session is terminated."> <!--- Define arguments. ---> <cfargument name="SessionScope" type="struct" required="true" /> <cfargument name="ApplicationScope" type="struct" required="false" default="#StructNew()#" /> <!--- Return out. ---> <cfreturn /> </cffunction> <cffunction name="OnApplicationEnd" access="public" returntype="void" output="false" hint="Fires when the application is terminated."> <!--- Define arguments. ---> <cfargument name="ApplicationScope" type="struct" required="false" default="#StructNew()#" /> <!--- Return out. ---> <cfreturn /> </cffunction> <cffunction name="OnError" access="public" returntype="void" output="true" hint="Fires when an exception occures that is not caught by a try/catch."> <!--- Define arguments. ---> <cfargument name="Exception" type="any" required="true" /> <cfargument name="EventName" type="string" required="false" default="" /> <!--- Return out. ---> <cfreturn /> </cffunction> </cfcomponent>
Now that we understand how all the events and variables are wired together, let's take a closer look at the individual application event methods.
This is the perfect place to define application-scoped variables (ex. APPLICATION.DSN for data source structures). If this method is invoked manually, be sure to call StructClear() on the APPLICATION scope before you re-initialize the data values. This will help to ensure a clean refresh.
The return boolean signals as to whether or not the application loaded successfully. Returning false will halt the processing of the rest of the events of the current page request.
This is the perfect place to define session-scoped variables (ex. SESSION.Cart for eCommerce cart data). If this method is invoked manually, be sure to call StructClear() on the SESSION scope before you re-initialized the data values. HOWEVER! before clearing the scope, get a copy of the CFID/CFTOKEN values so that you can store them back into the session during re-initialization. ColdFusion will not automatically re-create these values as calling this event method is not actually restarting the session.
This the perfect place to define request-specific and other request-scoped variables (ex. REQUEST.Attributes for the union of the FORM/URL scopes). This is also a good place to do universal FORM value scrubbing (such as removing smart quotes and trimming form field values).
If you application or sessions can handle manual re-initialization, this is a good place to check for those URL flags (ex. StructKeyExist( URL, "resetApp" )) and then manually invoke the OnApplicationStart() or OnSessionStart() application events.
If you are using the OnRequest() method and you expect this application to be used for flash remoting or CFC-based web service calls, this is the ideal time at which to check for the request type (standard page request vs. web service) and if need be, delete the OnRequest() method from the Application.cfc (ex. StructDelete( THIS, "OnRequest" )).
If the return value is false, the page processing will be halted.
If you include this event then you must include the requested page via a CFInclude tag. The relative path of the template is passed as the only argument to this function. If you include the in this manner, the processed template becomes what is essentially a "Mix-In" of the Application.cfc meaning that it is actually part of the Application.cfc code. If you do this, the processed page will have access to the THIS and VARIABLES scope of the Application.cfc.
If the request page is included in this manner, it also means that the requested page will have access to all methods defined in the Application.cfc.
This is the perfect place to handle login management. Since this method determines which template gets executed, this is where you can check login status and conditionally include the login template rather than the requested template.
This is the perfect place to handle page logging. By doing it here (especially if the first command is a CFFlush tag), the user experience will not be affected by any processing overhead of this event method.
This is the perfect place to clean up sessions that have times out (ex. deleting uncommitted shopping cart information). This method does not have inherent access to the APPLICATION scope or the SESSION scope (as the OnRequestEnd() method has access to the REQUEST scope). In order to access those scopes, you must use the passed in arguments.
This is the perfect place to log information about the application. This method does not have inherent access to the APPLICATION scope. In order to access that scope, you must used the passed in argument.
This event gets called when an event fires and allows you to handle that error and include an error handling template if you so desire. However, this event cannot display anything to the user if the error occurs during the OnApplicationEnd() or OnSessionEnd() events as those are not request-related (unless called explicitly during a request).
One thing to be aware of (as of MX7) is that the CFLocation tag throws a runtime Abort exception (which makes sense if you understand that the CFLocation tag halts the page and flushes header information to the browser). As a work around to this, you can check the passed in Exception type and if its a Abort exception do not process the error:
<cfif NOT CompareNoCase( ARGUMENTS.Exception.RootCause.Type, "coldfusion.runtime.AbortException" )> <!--- Do not process this error. Return out. ---> <cfreturn /> </cfif>
As a note of caution, if you use the OnError() event method to include a ColdFusion template that is also URL-accessible, you have to put in logic that knows not to use exception-related values if the template is access directly.
The above event methods are those that the ColdFusion server will attempt to hook into during the application and page life cycle. Application.cfc is a ColdFusion component just like any other CFC file and because of that, there is nothing to stop you from defining as many methods in it that you want. One thing that I like to do sometimes is create a method CreateCFC() within the Application.cfc to provide short hand notation (and other benefits) not only to the other Application.cfc methods but also to any template that shares the Application.cfc memory scopes.
In addition to methods, you can create as many variables as you like within the Application.cfc memory scopes.
What Is A ColdFusion Application?
ColdFusion applications do not really exist. At least not in the way you might think of a traditional desktop application that has a running process. ColdFusion applications do not have a constant process. Instead, they have memory scopes. Each application has its own name which ties it to a chunk of memory somewhere. Every time you define an application through the Application.cfc (or CFApplication), what you are really doing it associating the current page request to the chunk of memory that is associated with that application name.
When you start to look at ColdFusion applications this way, it becomes a little more clear why you cannot explicitly kill an application or a session; there simply is nothing to kill. An application doesn't run unless you have a running page that requests to be associated with it.
When Does The Application.cfc Get Created?
It is important to understand that Application.cfc is newly instantiated for every single page request. This means that all the application settings and page request settings can be different for every page request. This information can be used to set up alternate session management configurations. For example, you might set THIS.SessionManagement to false for browsers that you know cannot accept cookies (and therefore cannot handle session management).
What If I Am In A Rush - Isn't CFApplication Easier To Code Than Application.cfc
This is absolutely not true. Remember, all of the information outlined in this presentation is completely optional. You don't have to have anything in your Application.cfc. For example, the following is a perfectly valid Application.cfc:
<cfcomponent> <cfset THIS.Name = "TinyApp" /> </cfcomponent>
Can you get any more simple than that? This is just as easy as the CFApplication tag, but if you go the Application.cfc route, you will have the advantage of being able to easily add in the other application event methods as you need them.
Want to use code from this post? Check out the license.
Thanks for the overview. I've got a couple of questions from trying to use Application.cfc.
First, do you set things in the application scope such as your datasource name and then do an application lock every time you need to call it? Or do you dump it into some other non-locking scope when you need it?
Second, when you run the onSessionStart() call, how do you get access to the variables that you create? For example, I have some queries that need to run only when a session starts but I want to cache them in some scope other than the session scope because they are called frequently and I prefer not to use all those locks. Or am I worrying about excessive locking for no reason?
For example, I would like to run this query:
EXEC pr_getSystemSettings '#cgi.http_host#'
onSessionStart() but I don't seem to have access to the query results outside of the cfc.
"First, do you set things in the application scope such as your datasource name and then do an application lock every time you need to call it? Or do you dump it into some other non-locking scope when you need it?"
You do not need to lock variables set in onApplicationStart. CF single threads it for you.
Later on, if you want to read the values, then you do not need to lock them if they are simple constant variable (like DSN and most app variables.)
"Second, when you run the onSessionStart() call, how do you get access to the variables that you create? For example, I have some queries that need to run only when a session starts but I want to cache them in some scope other than the session scope because they are called frequently and I prefer not to use all those locks. Or am I worrying about excessive locking for no reason?"
You can access the Application scope in onSessionStart, but you would want to ensure you use cflock if you are updating the app scope.
As Ray said, you do not need to lock the variable since OnApplicationStart() is inherently single-threaded. However, if you call OnApplicationStart() manually, then it is being called as if it were a regular, every day function:
<cfif SturctKeyExist( URL, "reset" )>
<cfset THIS.OnApplicationStart() />
In this case, since we are manually trying to reset the application and not letting the ColdFusion application server call it when the app *starts*, then OnApplicaitonStart() is not single threaded.
Because I usually have something like this going on in my applications, I tend to put an APPLICATION-scoped CFLock in my OnApplicaitonStart() method. At the very least, it cannot hurt.
As far as the scopes to use, sorry about that - my presentation did not go into good detail about that - you would use the same scoping that you would in a traditional CFApplication / Application.cfm scenario:
<cfset APPLICATION.DSN = StructNew() />
<cfset APPLICATION.DSN.Source = "cool_db" />
<cfset SESSION.User = StructNew() />
<cfset SESSION.User.ID = 0 />
You can use APPLICAITON, SESSION, REQUEST, and all the other scope within the Application.cfc just as you would have with the Application.cfm (although not all are available in all the event methods).
As far as using the query results set in OnSessionStart() outside of the CFC, the problem here is that OnSessionStart() is not related to any given page request - it's a session-specific event.
If you want to create objects in an application-level event and then use them on the rest of the page processing, do that stuff in OnRequestStart() or even OnRequest().
And, like Ray said, as far as locking goes, if you are reading simple value, don't worry about locking. Locking has overhead and should only be used if you KNOW a race condition might occur (a race condition is that one thread might be reading a chunk of code while another thread might be trying to update variables within that chunk of code). If you know that NO other thread will ever update something like DSN values, then don't worry about locking.
Even if you are manually restarting the application (calling OnApplicationStart()), which could potentially cause a race condition, I would still say don't worry about it as that is a very small possibility and something that will happen only rarely.... and of course, evaluate the worst case scenario - let's say you are updating the DSN and some dude in Delaware gets a CF error cause he was trying to do a query just as you cleared the APPLICATION scope... and then he refreshes the page and it works... is getting rid of that chance WORTH the overhead of locking every single read of the DSN?
So for the various query result sets and string values that don't change I can put them in the application scope and not worry about locking because nothing bad can happen?
What would you recommend for the query example I gave that should only be called when the session starts but because it is dynamic gets called on every page request? I can't really dump it to the session scope because the values are used on just about every page of the site.
On a styling note, do you create the structures in your example so that it is easier to delete them later instead of having to reference each individual value?
As far as the query goes, if it needs to be executed for every single page request, then put it in the OnRequestStart() event method. Since that method is called once per page request (unless called manually which I have never seen done), it is essentially single threaded and you will NOT have to worry about locking.
Scoping it directly into the REQUEST scope, as you have done, is valid, and is what I would do.
However, look at what it is doing... it is getting results based on the http_host value. This data, while it may change depending on the host, will NOT change across a user's page requests. In that case, I would put the query in the OnSessionStart() event method and scope it directly into the SESSION scope:
Of course, you might take this even one step further; if this is going to be the same for a TON of people, no need to keep going back to the database right? Instead, in your OnSessionStart(), you can try to cache it in the APPLICATION scope:
<cfif NOT StructKeyExists( APPLICATION.SystemSettings, CGI.http_host )>
<cfset APPLICATION.SystemSettings[ CGI.http_host ] = qSystemSettings />
<cfset SESSION.SystemSettings = APPLICATION.SystemSettings[ CGI.http_host ] />
What I am doing there is creating a system settings query object for each http_host that comes in and caching it in the APPLICATION scope. Then, for each session, I check to see if that setting object exists yet. If NOT, I created it and cached it. Then after that conditional check, I know that it exists and can then just get a reference to it in my session.
As far as using Structs vs. individual values, I like using structs cause I can logically group related data. This makes my code more readable (IMO) and also makes it easier to pass data; it's less code to pass a structure containing 10 values than to pass all 10 values.
Hope some of that helps.
Yet another great overview. Thanks for the effort.
Could you make your web pages more print-friendly?
Glad it helped. I am going to work on making a print-friendly version of my blog detail pages. Thanks for bearing with me.
Ben - great post! I'm building an quick application using Ray Camden's Soundings survey application and decided to try to convert his application to use application.cfc - and so far so good but I have run into one issue I'm not sure how best to handle - nested application.cfms...
He has an /admin section where he has another application.cfm which include the one in the root. I've done this myself in the past but not sure the best way to handle it in moving over to application.cfc...
Thanks again for all the great blog posts! I don't know how you find the time!
Glad you like the work and effort that I am putting into this stuff. I know that you can extend one Application.cfc with another, however, if they are in nested directories, that will require a mapped path. I have never done this so I cannot give you any real advice other then Google "extending Application.cfc".
Adobe has a full tech note on this. Search at GOogle. Sean Corfield also blogged on it.
I bugged Ray about it and he pointed me to Sean's blog where indeed he has the answer!
Just wanted to mention I copied your onError code as is and got an error that ARGUMENTS.Exception.RootCause.Type doesn't exist. I'm not sure you typed something wrong or my CF installation is different somehow, but RootCause doesn't exist as a key to Exception for me. There's Exception.Type, which is just a name, not something like coldfusion.runtime... Anyway, thought I'd mention this just in case.
Yes, you need to check for the existence first. I make the mistake all the time myself.
Yes Thomas you're right.
I noticed it only exists if your Application.cfc contains the onRequest method.
Interesting. Very interesting. I have to say that I pretty much always use OnRequest() so I have never noticed this. Very good to know. Thanks.
Thanks for the tip. I did a quick little example to see what was different:
I am going through converting an application.cfm to an application.cfc and am running into variables that are set in the variables scope in the application.cfm (used to be accessible in all pages of the application).
Now those variables are not accessible when I set them in the onRequestStart() or outside all the methods of Application.cfc.
The only thing I could think of to make the variables accessible from all application pages is to put them in the application or the request scope.
The problem is that I will have to add a <cfparam ..> at the top of each page that uses the variable to initialize the local variable with application/request scope variable. Can you think of anything simpler than that to accomplish the same thing?
If yo use onRequest, then any variables you set will be in the Variables scope. This also has the side of effect of breaking Flash Remoting and Web Services, and copying other info from the CFC. Personally - I'd just use Request variables. It will help them stand out from variables made on the page itself.
Are you using the OnRequest() event method? That is the only way that the VARIABLES scope of the Application.cfc would be available in the processing template.
I was not using the onRequest(). I was only using onRequestStart().
I think I may end up using the onRequest() though since there are many files to update if I use the Application/Request scope vars. The two scenarios that would be affected by using the onRequest() - Flash Remoting and WS do not apply to my app. Thanks for the info.
No problem. And also, FYI, you can always conditionally delete the OnRequest() method if it is a CFC-based method call for flash remoting. But if that doesn't affect you, don't worry about it.
Interesting Ben, how an that be done? (deleting onRequest method when making a Flash remoting or web services call)
In the OnRequestStart() method, you can check the requested template. If it is a given file type (ex. .CFC) or in a certain directory or something, you can simply delete the function from the CFC scope:
StructDelete( THIS, "OnRequest" )
Then, when the ColdFusion application server finishes executing the OnRequestStart() method, it will look for OnRequest(), but it will no longer exist for that page request.
Cool! Rule of the thumb, CFCs are objects whose properties, variables and methods can be dynamically created or removed (as in this case) at runtime.
Yeah, pretty much. CFCs are super flexible that way.
I have now tryed to use the Application.cfc. But in my code I uses cfc's in a sub catalog to make query requests.
But the problem is now that the cfc's cant see the application.DNS!?
Why is that?
I think it depends on what you mean. You say you use CFCs in a sub catalog - do you mean subdirectory? As you know, if CF can't find an App.cfc/cfm in the current folder, it will look up higher in the directory until it finds one - so it should have no problem finding your App.cfc and if yo have app.dsn defined there, all should be well.
But - you really shouldn't be using "outside" variables in your CFCs. Why not let your App.cfc create an instances of the CFC in onApplicationStart? When you make the instance, pass in the DSN value so the CFC can store it inside itself.
Yes I ment subdirectory.
Today I tryed to use Application.cfc again and now it worked!?
I don't know what caused the problem last time.
But your sugestion about using Application.cfc to create an instance of the cfc's that I uses sounds like a very good idea.
But how would you do that? And how should they be called from the code?
Today I call the cfc's by <cfinvoke ../>
Typically you add a method named init to your cfcs. This method takes any configuaration parameter, like DSN. Then in App.cfc you can do
(note app = application)
cfset app.mycfc = createObject("component","path.to.your.cfc.name").init(app.dsn)>
Hey Ben, Good stuff but are you sure you receive the application scope as a variable?
Also I have another question, I define a lot of high level stuff in those methods, yet they're completely accessible to the included template. If I don't want designers muddling with that logic, how can I make them private or inaccessible in the template's scope?
access="private" doesn't work in function definition, because the included template has private access.
If you don't want the include to have access to the Application.cfc data, don't use the OnRequest() event method. If you exclude that method, then the requested page will simply run; but, since it is not creating a CFC mixin, then you won't have to worry about access.
If you have to use the OnRequest() event method (which I think is a wonderful method), then there is not much you can do.
What are you worried about? Developers messing with cached variables or something? I can assure you that whatever you are doing, even without the CFInclude of the requested template, developers could mess with cached data. The OnRequest() event method does not really change that at all.
I guess the variables stored in the Applicaiton.cfc via the THIS or VARIABLES scope, that is something that is OnRequest() enabled...
great tutorials and files (as ever). Thank you for everything you blog and share.
I'm a CF developer of over two years, but (ashamed to say) never used application.cfc before.
I am also on a CF8 server, and want to create a mapping to a directory to use for custom tags. I have heard that this can be done per application, but I cannot seem to get it working. I currently have the site I am working on in a /V2/ folder, with the application.cfc within.
How can I create a mapping from the .cfc to a directory to create a custom tag path?
Ben, maybe it would be a good idea to put a comment in the onRequest method that it can not be used together with cfc calls?
And/or maybe something like this in onRequestStart (commented out) for those who really want to have the onRequest in there:
<cfif listLast(arguments.targetPage,'.') is 'cfc'>
<cfset StructDelete( THIS, "OnRequest" )>
It is not so obvious that it does not work with cfc, because the cfc IS included and executed, and the url parames, including the method, IS available for the cfc. Still, for reasons I do not know, the cfc will not know what method to run.
Many thanks to you and this blog for helping me solve this thing. I know (now) it is described in the docs, but you really need to read it like a lawyer to see it.
Thanks for all of the good examples, I am in the process of converting over an existing application.cfm to an application.cfc and used your references and examples from Skin Spider and this posting. I ran into troubles when I was trying to have an authenticated area of the website that checks for the existence of a key in the Session OnRequest and forward to a login page at a different level. It cannot find the pathing. This same code worked fine in application.cfm and its giving me some troubles here. Let me explain:
[webroot]\application.cfc = Defines application name, session management, etc.
[webroot]\applicationproxy.cfc = Stub <cfcomponent> that extends application.cfc.
[webroot]\login\index.cfm = has login and authentication code (no application.cfc)
[webroot]\members\application.cfc = authenticated area where Session.User object must exist or the user is forwarded to the ..\login\ page.
This code is returning "Could not find the included template ../login/". Any Help?
hint="Handle the application."
<cffunction name="OnRequest" access="public" returntype="void" output="true"
hint="Fires after pre page processing is complete.">
<!--- Define arguments. --->
<cfargument name="TargetPage" type="string" required="yes" />
<!--- If the member is not logged in --->
<CFIF StructKeyExists(Session, 'User')>
<CFIF StructKeyExists(Session.User, 'userID')>
<!--- All Good, carry on --->
<!--- The user has not logged in. --->
<cfinclude template="../login/" />
<!--- Do not allow the rest of the page to execute. --->
<!--- The user has not logged in. --->
<cfinclude template="../login/" />
<!--- Do not allow the rest of the page to execute. --->
You can't cfinclude a folder. You can only include a file.
I am being asked to clear my mysql transations from a shared hosting account. Would their be a place in the application.cfc that would be good for <cfset cfusion_dbconnections_flush()> and would this do what i need ? been looking for an ansewer i cant find it.
I am not sure what you are referring to. I don't really do much of anything with databases outside of the CFQuery tag; as such, I am not sure I have any advice on some sort of transaction queue?
I'm trying to convert my companies application.cfm to an Application.cfc but when I make the switch nothing happens. I can't get to any page, everything is just blank. It's like non of the events are being fired. I can cause an error in the Application.cfc and get some error output, but I can't even cfdump a variable. Do you have any idea what is causing this?
It should like you are either returning "false" from the OnRequestStart() or OnApplicationStart() event or you have output="false" on your OnRequest() event.
I checked, they both return true and onRequest has outpout="true".
I pretty much just copied you're above example. If I remember correctly i first tried running the site with the empty application example from above to see what would happen, I got a blank page.
So I started filling in the Application.cfc to see what would happen. Everything is still blank.
Is there some setting in the admin I have to change? I know it's getting processed since I can cause it to error out...idk.
Oh and I have another question. If I have some udfs that I want to be available to every page, where is the best place to include them? The onRequest? Or is it possible/good idea to make then members of the Application.cfc?
Oh and we're running CFMX7
Funky. If you tried running it with an empty Application.cfc and still got no output then there might be an error that's not displaying.
Have you checked the Application log file in the ColdFusion administrator to make sure there are no exceptions happening?
You're right looks like there are some SQL errors that are not being reported.
Ok cool. Let us know if you're still getting problems after that's fixed.
I like the information you posted, but I am not able to use Application.cfc with Adobe flex application.
Can you tell me how can we use Application.cfc file from Adobe Flex code ? Any help will be appreciated.
It depends on what you mean Bran. Application.cfc is for CF. It isn't for Flex. However, if your Flex app makes a HTTP request to a CF resource within a folder that has an App.cfc, then it comes into play. Perhaps you can expand a bit on exactly what you mean?
Thanks for your response.
I want to access session variables defined in CF and check it validity from Flex application. My session variables are getting destroyed in CFC while calling from Flex. I need to test User validity(defined in session) for each request.
Do you have any idea how to achieve that using App.CFC ? Any help will be appreciated.
Flex has multiple ways to request data from the server. The preferred way is Flash Remoting. You can use that to ask the server for session data.
Now as for ensuring it works on every request - what I say next should be taken with a grain of salt. First off - if you make use of a CFLOGON based security system, I believe you can set your FR (Flash Remoting calls) to make use of this and ensure all calls are secured. I'm not a big fan of CFLOGON myself. In the past what I've done is to pass username/password info to my service calls. This is probably not optimal though.
This is a very interesting discussion. Maintaining session management across API calls is definitely something that I have thought about looking into, but have not made time to yet. When I have better ideas, I'll report back.
don't take me as stupid, but once again I'm trying to change the Application.cfm into an application.cfc component following your tutorial, but this is what I get:
<pre><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<META content="text/html; charset=utf-8" http-equiv=Content-Type></HEAD>
I have followed all your indications for each event displacing every part of the initialization code in the right function, but nothing happens.
Why is that ?
Thanks in advance for any help you may provide
Sorry for the "<pre>" tag. It is mine, not in the original code.
Forget about what I wrote before.
After reading this thread I went into a debug session and I discovered the problem: an error in the code also unsupported by the fact that the OnError event was empty.
Thanks a lot for this tutorial.
It is great, as all the contents in this website.
Stay so well.
No worries - glad you got it working.
I have a problem regarding the application.cfc and setting session.user struct. It seams to work just fine when user first logs in or after the manual logout, but if session expires and user tries to log back in I am getting session.user.xyz is not found. I am using j2ee for session management. Thanks for your help in advance and here is my application.cfc
<cfparam name="loginQuery.news" default="0">
<cfparam name="loginQuery.calendar" default="0">
<cfparam name="loginQuery.account" default="0">
<cfparam name="loginQuery.site" default="0">
<cfparam name="loginQuery.marketing" default="0">
<cfparam name="loginQuery.users" default="0">
<cfset This.name = "Login">
<cfset This.clientManagement = "true">
<cfset This.loginstorage = "session">
<cfset THIS.SessionTimeout = CreateTimeSpan( 0, 0, 0, 1) />
<cffunction name="OnRequestStart" >
<cfargument name = "request" required="true"/>
<cflocation url="/index.cfm" addtoken="no">
<cfif NOT IsDefined("cflogin")>
<cfif cflogin.name IS "" OR cflogin.password IS "">
<h2>You must enter text in both the User Name and Password fields.</h2>
<cfquery name="loginQuery" dataSource="#Application.DNS#">
SELECT UserID, UserLevel, news, calendar, site, marketing, account, users
UserID = '#cflogin.name#'
AND Password = '#cflogin.password#'
<cfif loginQuery.UserLevel NEQ "">
<cfloginuser name="#cflogin.name#" Password = "#cflogin.password#" roles="#loginQuery.UserLevel#">
<cfset SESSION.User = StructNew() />
<cfset SESSION.User.news = loginQuery.news/>
<cfset SESSION.User.calendar = loginQuery.calendar/>
<cfset SESSION.User.account = loginQuery.account/>
<cfset SESSION.User.marketing = loginQuery.marketing/>
<cfset SESSION.User.site = loginQuery.site/>
<cfset SESSION.User.users = loginQuery.users/>
<H2>Your login information is not valid.<br>
Please Try again</H2>
<cflocation url="/index.cfm" addtoken="no">
Unfortunately, I don't know much about ColdFusion's built-in functionality. Is the problem occurring constantly once it pops up? Or does it happen once and then go away?
I am not sure I understand the CFInclude of "Application.cfc"? Aren't you in Application.cfc? Is this file including itself :)
Also, You don't have to clear the SESSION in OnSessionEnd(). Actually, in OnSessionEnd(), I don't even think that the session object is accessible... there is not way to run CFLocation (or no reason to) in OnSessionEnd() since it is not a traditional page request (made by the user).
Hi Ben, thanks for responding.
The problem seams to stick one it pops up. So if I log in and get an error then it will stick until I log out and lag back in. I am not understanding what is the difference between <cflogout> and session expiring on it's own and loging back in after these 2 events.
I am including application.cfc from the root that has all the application settings and then this application.cfc is in my secure folder. I do not need user management for the whole site.
Originally I had nothing in OnSessionEnd but I've been trying to figure out this problem.
Another thing I tried is placed creation of session.user. struct in the OnSessionStart and it seams to be working every time but the only problem it's not seeing the "loginQuery" and it's using default settings from the <cfparam>.
I am not sure how can I pass user info to the OnSessionStart function.
Quick Update, I removed onapplicationstart from the root application.cfc and placed it into this one just to eliminate any possibilities. I am still getting same problem.
As far as the OnSessionEnd(), ColdFusion passes it two arguments - the currently ending session scope and the existing application scope. That is how you can get the user info (from the session scope passed-in).
As far as session vs. CFLogin system, that's a bit tricky, and part of why I am not crazy about the CFLogin system. From what I remember the few times that I have looked into it, the CFLogin "login" lasts *longer* than the session timeout.
Actually, now that I think about it, that makes perfect sense. Since you are using CFLogin checks to set your SESSION-based data, it's possible that your session would time out, but not be re-populated because ColdFusion still things you are logged in via CFLogin.
I'll try and due a bit of experimentation with this idea.
Ok that makes sense now. I did notice that at times if expired session sits for a while I was able to login with user struct set. Would it be possible to do <cflogout> on OnSessionEnd() or maybe set timeout for the <cflogin>?
Setting idle timeout on cflogin will fix this: <cflogin idleTimeout="n"> CF default value for cflogin idle timeout is 1800 which is 30 min. Thank you for your help and setting me on the right track.
No problem. I'm glad I happen to remember something about the timeout issues :) I think using the CFLogout in the OnSessionEnd() might be good. Changing the timeout might also be good. I don't have enough experience with that login system to say which is better.
How come every time I google a ColdFusion item, your website comes up first? And how come the article is entirely relevant to what I'm doing? Are you a google god or something? :)
Once again, thanks for the tutorial/article. Any significant changes from this to CF9?
No idea (re: Google). My Google Page isn't even that high??
As far as CF9, there are a number of ORM-related changes; but as far as core functionality, I'd say the biggest difference is that there is now a this.datasource for app-specific datasources (so you don't have to include it in every CFQuery tag) and the onCFCRequest() method for CFC-based requests.
Your Google rank may not be very high, but when I search on ColdFusion topics, you often have the first or second result. You really do know what you're talking about :)
What could you use the onCFCRequest method for? I'm thinking it could be used for:
1. Logging access requests of CFC files - could you log which method is called within a CFC this way?
2. Bypassing security when the CFC directory is under a directory secured by Application.cfc
onCFCRequest() would be used to manage CFC-based requests in the same way that onRequest() would be used to manage CFM-based requests. Essentially, it gives you control over how the CFC is instantiated, called, and what data is returned (including a larger variety of data return formats). In this, you could be taking advantage of things like CFC-caching and logging and error handling.
Of course, if you don't need to add anything special to the request, you can just exclude the method and the ColdFusion server will take care of managing the CFC-based request for you.
I am having some issue with OnSessionEnd().
I can get the session timeout to trigger the OnSessionEnd(), but so far the only functionality I can get is to write to a file, I cant run a query. If I directly invoke the onsessionend I can get it to run a query and do a cflocation. Why would the query not run automatically when the session times out? and why would the cflocation not work either.
I am just trying to log info and save session info.
<!--- Define arguments. --->
hint="I am the session scope that is ending."
hint="I am the application scope parent to the given session."
<cfquery name="savesession" attributecollection="#arguments.applicationScope.dbInfo#">
INSERT INTO userstuff (stuff1, stuff2, stuff3, stuff4)
<cffile addnewline="yes" action="append"
file="#arguments.applicationScope.currDir#sessionend.txt" output="session ends:#Now()#">
<cflocation url="sessionend.cfm" addtoken="no">
After posting my comment I immediately saw that I was not referencing the argument scope to get the session variables.......my bad
No problem. Also, I am not sure CFLocation will actually work within the onSessionEnd() event handler. This event handler happens asynchronously to any particular page request. I am not sure how the event is actually invoked by the ColdFusion application server; it might follow CFLocation, but not in any visible way.
Okay, here's a weird one ... I have a cart structure being defined by OnSessionStart() ... except that it's getting redefined with every page all. It's essentially re-starting the session every time I go from one page to the next! Any idea what might be causing this behavior?
Disabled cookies? A forced call to onSessionStart() in onRequestStart()? An application name that is dynamic and changing for every request?
We do have client-side cookies disabled (gov't contractor, we have very draconian privacy reqs we have to follow), but we've never had an issue with session management previously. This is the first time we've used application.cfc instead of application.cfm, tho, so I'm still learning what's what about them.
We don't currently have any onRequestStart(), and the application name is static, so neither of those should be an issue.
W/o cookies, how would the session maintain? Are you using urlSessionFormat() around all your links?
Looking into it, it appears that our previous model simply had definitions for CFID and CFTOKEN within application.cfm, so they automatically carried from page to page (as application.cfm loaded first). Since application.cfc doesn't do this, obviously, it wasn't working.
Turning on client cookies seems to have fixed the problem temporarily, and I'll look into other options. I do have a block that loads at the top of each page via cfinclude, but it's purely decoration and I don't want to muck it up with code if I can avoid it.
urlsessionformat() is a new one on me, but I'll look it up.
Thanks for your help!
urlSessionFormat() fixed it! Thanks much! :)
How was your Application.cfm setting the CFID / CFTOKEN values?
It was essentially redefining them each page via cfset, a la:
cfapplication name="theApp" sessionmanagement="Yes" setclientcookies="No" sessiontimeout=#Createtimespan(0,0,15,0)#
cfcookie name="CFID" Value="#Session.cfid#"
cfcookie name="CFTOKEN" Value="#Session.cftoken#"
This was more-or-less inherited code several years old, and I have no idea where it came from. But it worked, so we used it. :)
We've just recently finally managed to catch up with a more modern version of CF, and are now migrating to working primarily with CFCs.
I see. Yeah, once you get into Application.cfc, you have to be more cognizant of what structures are available in which event handlers. You could probably do the same thing in the onRequestStart() event handler if you were so inclined.
Hi, I have found an example of how to refresh a page with ajax after a form is submitted. It works great, but in my application.cfc i have written this:
and in the "refreshed" page I get the header twice, once in the normal position, on the top of the page, and once somewhere in the middle, where my form used to be before ajax refreshed my page. I am using cflayout type=vbox
can i force the second header not to appear?
Hi Ben, it is amazing that i found your comment in my maintanence project.
There is your name and your blog address.
I just can not believe it.
Do you still remember "modpay"
The onRequestStart() event gets fired for every CFM/CFC request that comes into your ColdFusion application. As such, it sounds like you are replacing the content of your FORM with the content returned from an AJAX request. Since the AJAX request goes through your Application.cfc, it will pick up the header output in the onRequestStart() event handler... hence the duplication.
If you want, you can put in some sort of flag in the onRequestStart() event handler to prevent the header include from time to time.
<cfif not structKeyExists( form, "isAjax" )>
<cfinclude template="header.cfm" />
Here, you can ignore the header include any time you *also* post the "isAjax" form value.
Ha ha, that's too funny. That sounds familiar, but it's been a while :)
From May 2007:
"..I am going to work on making a print-friendly version of my blog detail pages. Thanks for bearing with me."
Love the Blog - some entries (like this one) would be great to print as a reference, but printed versions are still horrible. FireFox does such a completely useless job that I'm reduced to using IE. IE prints a large dark rectangle (some sort of "echo" of the mast head?) at the top of each page, obscuring code and text.
There is such a wealth of good information here, it seems a real shame not to fix this (in your copious spare time - I'm ashamed of myself for complaining, I really am.)
<!--- START: OnSessionStart --->
<cffunction name="OnRequestStart" access="public" returntype="boolean" output="true" hint="Fires at first part of page processing.">
<cfargument name="TargetPage" type="string" required="true" />
<!---Check for asynchronous request--->
<cfif IsDefined("url.ajax_allow") >
<cfset StructDelete( THIS, "OnRequest") >
Ben have a cf8 application.cfc with a required onrequest() section which when commented out allows ajax calls but when present blocks them .
I put the snippet you suggested in onrequeststart() but it doesnt seem to work.
It is seeing my ajax flag variable but it seems onrequest() is still having its noxious effect.
I had a print version for a while; I think it got removed again in one of the "site renovations". I could probably add it back in easily enough (and thank you for the kind words my friend).
You have to delete the method from both the THIS and VARIABLES scope. I was running into that problem a while back. It's a bit strange since deleting it from the public (this) scope should be enough; however, ColdFusion is probably doing something a bit different when invoking these methods.
Try the double-delete and see if that helps.
I'm transitioning to application.cfc as well and now my application can't find a custom tag (cf_) in the same dir as the calling file.
Is there something I must set in app.cfc to use cf_ in this way?
There shouldn't be anything you are doing to prevent the CF_ notation from working within the same folder. Double-check the spelling of the custom tags.
I am now rewriting my website with better coding practices, to simplify & boost it.
In my search for documentation, I have discover what the Application's property "SetDomainCookies" does actually.
If the server runs into a clustered environment (as many of web hosts), and if its value is set to "true" ; it links the cookies to the domain and not only the server.
So the cookies are always accessible even if, for one or another reason, the request is processed from a different server.
Thanks you a lot for this tutorial, it is my reference.
Unfortunately, the world of clustering is not something I've had the opportunity to play in just yet. That said, I am not sure I understand what you mean about the cookies. Taking these two sites, for example:
Are you saying that they share the same domain, but not the same host?
No, I just mean for a given domain, sometimes there is several servers (raid systems) to proceed the requests.
And it seems that the "regular" cookies belong to one server and not to the others of the cluster.
So to make cookies belong to any of the cluster's servers, we need to set the SetDomainCookies to true.
I am not sure that I fully understand. The browser passes cookies back with every single page request. As such, I would think it's the "browser" that actually owns the cookies. The only thing the server can do, I think, is tell the browser what domain the cookie belongs too (or I guess that's actually a feature of the browser's own security).
I'll look into this a bit. It's probably something I should understand :)
I have a question about the onRequestStart().
Do you think it is an ok practive to validate a login request or a confirm registration request here?
For example: from login.cfm user submits the login form onRequestStart() uses validateLogin() and then pushes the user on. Same as a confim registration request.
Or should that processing not be done here? Rather use it for checking to see if a user has logged in or not?
A lot of times, what I'll do is check for logged-in status in the onRequest() event handler, not the onRequestStart() event handler. Typically, I only use the onRequestStart() to initialize request-based variables, not to perform any workflow logic.
In the onRequest() event handler, I might check to see if the user is logged in, and if not, include the login form rather than the front controller (or the requested file). The onRequest() event handler is great for managing the executed script.
Of course, my approach changes depending on the constraints of the application - ie. does the user always need to be logged in? Are only certain parts login-protected?
Ben, I just wanted to say thanks for this awesome tutorial. I have integrated this code into a few sites recently including the ones below.
I really appreciate your help!!!
Thanks so much!!!
Glad you found it useful.
Don't know what I missed but in my CF8 ajax calls
commenting out onrequest function allows a response but even with the second variables scope deleted in onrequeststart (as below)
<cfset StructDelete( THIS, "OnRequest") >
<cfset StructDelete( VARIABLES, "OnRequest") >
my ajax fails again.
Where are you performing the structDelete()? And what logic are you using to decide whether or not to delete?
This is a good point as trying to signal your snippet ( struct delete this & variables in onrequeststart function) from cfajax form posts is hellish as you cant append a url flag like &ajaxr in mycfc.setForm (blah,blah)anywhere.
If you go with the explicit function and use method=myfnc you still cant append vars that get seen.
So I used the hack of recognizing all calls to cfc's as ajax requests (which means I cant use cfc services for synch requests.
But the way I confirmed the nature of my issue
was commenting out onrequest block - ajax works.
then I do the delete both scope structs (no flag just runs) - ajax doesnt work.
when I shift the delete snippet detecting the .cfc file extension into the top of onrequeststart (as below) works
<!--- START: OnRequestStart --->
<cffunction name="OnRequestStart" access="public" returntype="boolean" output="true" hint="Fires at first part of page processing.">
<cfargument name="TargetPage" type="string" required="true" />
<cfif listLast( cgi.cf_template_path, '.' ) is 'cfc'>
<cfset structDelete( this, 'onRequest' ) />
but if you strip away the cfif check and just use the cfset line it is ignored !!
- go figure but at least it works and I can
Thx for the suggestions.
Hmm, not sure why it wouldn't work in the onRequestStart(). Of course, be sure to delete from *both* the THIS and VARIABLES scope (as you mentioned in your first comment).
I would recommend using the CFArgument that gets passed into the onRequestStart() event handler, rather than referring to the CGI object to get the incoming template path; probably just safe since ColdFusion is explicitly providing you with a template path in the argument.
Can you please provide a citation for your assertions that these event handlers are single-threaded?
I'm not disagreeing with you, but I cannot find anywhere that it's explicitly (and officially) stated.
Or was this just based on experimentation?
Did u do any research on the order implicit events get fired?
If I access a webapp for the 1st time, does onSessionStart() get called_before_ onRequestStart() or vv? And is onSessionEnd() called _after_ onRequestEnd()?
I know that onSessionEnd() does not have interact or make request. So I would imagine onSessionEnd() would always happen after. If there was a request it would restart timeout.
"You cannot use this method to display data on a user page, because it is not associated with a request." -http://livedocs.adobe.com/coldfusion/8/htmldocs/help.html?content=AppEvents_11.html
When a request executes, ColdFusion runs the CFC methods in the following order:
1. onApplicationStart (if not run before for this application)
2. onSessionStart (if not run before for this session)
So how is a count of sessions calculated? Is it simply not gone until 8 hours (the default setting in the servers xml) has passed? Add it, wait 8 hours and subtract it?
I think Ben goes over how to end a seesion in a thread. You can end a session programatically, otherwise you have to wait for the session to timeout. You can use an application scope variable to maintain the count.
I asked the question because I thought the graph of sessions you see in the CF Enterprise Performance Monitor did not make sense.
I can't find anything helpful on this, but you might know.
If you have an application.cfm in a root directory and a custom cfc such as test.cfc in a directory above it for just basic site functions, does it use the application.cfm at all?
I try and call application scoped variables out of it and get nothing but non-existent variables.
Never mind, I found the problem. It wasn't being pulled in because someone didn't use the cfapplication tag correctly...
THIS can be quite tricky to catch when debugging ...
Let's say we have an Application.cfc with some default variables setup ... and I want to make some changes to a CFC that's initialized on onApplicationStart() ...
<!--- various init's --->
<cfset application.user = createObject("component", "com.user").init(application.dsn)>
One would presume that the application variables would be reinitialized when the application restarts ...
Yet ... if there is an application scope lock, such as the case when updating user counts onSessionEnd ... the application variables that are defined in onApplicationStart() will NOT update without an exclusive lock being placed on the application scope ...
Tough one to debug ....
In this case, simply reiniting the app through say a call to onApplicationStart() in the onRequestStart() method will not reload the application vars ... The following fails ...
At this point we shouldn't have a user session. However ... This app has a CFLock on the onSessionEnd() method ... So the reinit doesn't reload the application vars ... the same application scope data persists ...
However, if an exclusive lock is placed on the application scope for the reinit ... the desired results come to fruition ...
I'm not exactly confident of my presumption ... but my logic tells me that if the application is reinitialized through a call to onApplicationStart() ... the application variables should NOT persist ... they should be completely redefined when onApplicationStart() is called ...
In this case ... without a CFLock ... I had to manually restart ColdFusion to reload the user component in the application scope ... which I don't believe should be so ...
FWIW IMHO ...
Does onRequestEnd get called on a Request Timeout? I want to capture and log request timeouts in my applications.
Sitting on my first application.cfc file... and I don't understand why I cannot declarate inside of an event.
I can define a variable inside the event, but I need to declare it outside in order to be able to reference the variable.
This doesn't work:
If I declare before onSessionStart:
It works. Can someone shed some light on what I'm doing wrong? Thanks for hints and GREAT SITE to delve into CF :-)
If you added that line _after_ the session started, then it won't work. You have a session, CF isn't going to rerun onSessionStart. If you have done that, you can add a call to onSessionStart in onRequestStart, or just rename the application temporarily.
Hi there, in my app.cfc I want to load some xmlrelative to the location of app.cfc but this only works for cfm files in the same location as app.cfc.
<cfset fileP = expandPath('..\Environment.xml')/>
How can I do this without using full paths?
Is there any particular easy way of excluding Application.cfc from a particular file?
@Peter: No. But - if you are doing some logic in onRequestStart for every request, you could add a simple CFIF to check if the page is the one you want to exclude and skip the logic. Also, you can put the file in a subdirectory and put in an empty App.cfc file. It will run, but do nothing.
About THIS.ScriptProtect (from livedocs):
As we can see, the default protection replaces object, embed, script, applet, and meta tags by 'InvalidTag' text. But you can customize the patterns by modifying the regular expression in neo-security.xml to get more XSS protection. Great thing, imho.