To learn all about Skin Spider, click here.
To view the most updated application, click here.
To view the current code base, click here.
As the first part of Iteration 2, I moved the Application.cfm code over to the newer ColdFusion MX 7 Application.cfc component. The Application.cfc does pretty much the same thing that Application.cfm does, only it gives us more control over the actual system events that can take place:
While I alphabetize most methods in a ColdFusion component, I put these in "execution" order. It helps me think about the page processing is more structured way. And, while ColdFusion offers no true object constructors, anything outside of the method declarations is the component pseudo-constructor and get's called during object instantiation. Notice that in the Application.cfc pseudo constructor, I am defining the application and the general page settings. This replace the CFApplication tag.
Notice that I have left in both the Application.cfm and the Application.cfc templates. This is fine because ColdFusion MX 7 will execute only one. It checks for Application.cfc and executes it if it finds is. If it cannot find it, it then checks for Application.cfm and executes that one. Once we are done, will remove the Application.cfm template, but I am leaving it in for now so that you can see the comparison.
OnApplicationStart() - Application Initialization
I have moved the application initialization code into the OnApplicationStart() method. This method automatically gets called when the ColdFusion application runs for the first time. If all you do is let it run this way, then you never have to worry about race conditions or locking code as this will ONLY be called once. In our application, however, we manually call the OnApplicationStart() method whenever we flag the re-initialization of the application (via URL.reset). When you call the OnApplicationStart() method manually, it runs fine with the exception that it is not thread safe. To handle this, we leave in the double-check locking that we had in place in the Application.cfm file.
Since we are not using any session management in this application, I have commented out this method (but left it in just in case we use it later).
OnRequestStart() - Pre Page Processing
The OnRequestStart() method is our first hook into the pre-page processing of a given page request. This is where I do the file security check and REQUEST scope initialization. As its only argument, this method gets the path of the requested page template. We can use this to determine if there are any security issues and in our case, we are checking for files that start with "_" as being restricted. Notice also that this method returns a boolean. If you choose to return FALSE from this method, it will kill the rest of the page processing for the given page request.
OnRequest() - Executing The Current Page
The OnRequest() method determines which template to actually execute. As its only argument, this method gets the path of the requested page. If you want that template to execute, simply include it. In our case, since we want to the age verification for all index.cfm page requests, we can simply check which template was being requested, and, if it's the index.cfm file then we include the age_verification.cfm instead of index.cfm.
OnRequestEnd() - Post Page Processing
The OnRequestEnd() method gives us a hook into the post-page processing that OnRequestEnd.cfm would have provided. We are not doing anything with it at the moment, but I have left it in for discussion's sake.
OnSessionEnd() - Session Clean Up
The OnSessionEnd() method gets called when a user's session timeouts. This provides us with the ability to clean up a session or perform logging of some sort before we completely lose track of this user. Again, we are not doing any session management so I have commented this code out.
OnApplicationEnd() - Application Shut Down
The OnApplicationEnd() method gets called when the application times out or the server shuts down. It provides us with the ability to clean up data or log any issues. We are not doing anything with it at the moment.
OnError() - Application Error Handling
The OnError() method gets called with any un-caught error that bubbles up through the ColdFusion application. This does not get fired for any errors that are handled via CFTry / CFCatch. It get two arguments passed to it: the error object and the event name. The event name is only available if the error was generated by code in the Application.cfc (to the best of my understanding).
This method is meant to replace the old CFError tag. You will notice that instead of using the CFError tag, I am executing the site_error.cfm template as a custom tag module (via CFModule). I will go more into that in a second, but also take notice that I am not running errors for Abort exceptions. CFLocation fires an Abort error that is not really an error from our stand point (thanks Ray).
Site Error Handling - site_error.cfm
When switching from Application.cfm to Application.cfc the error object that gets generated changes. When using the CFError tag, an object named CFERROR gets created automatically. However, when using the Application.cfc's OnError() method, the error object gets passed in as an argument. To make matters worse, the two error objects do not have the same attributes or the same name. The new one has a bit more information to it.
So, how to cope with the changing specs? I have turned the site_error.cfm template into a custom tag. This way, I can call it from the OnError() method via the CFModule tag and explicitly pass in the values that I want it to use. Then, if you take a look at the site_error.cfm template, you will see that I have defaulted values in the tag's ATTRIBUTES scope. Then, in the page itself, instead of using error values directly, I use only the values passed in as tag attributes. This abstracts out the way the page works and decouples the page from the method of error handling I am using in the system.
I say, decouple, but of course, if I were to move back to using the Application.cfm, I would have to update the code slightly to have an intermediary page get called via CFError which would in turn, call the site_error.cfm custom tag via CFModule.
So now that we have a nice Application.cfc, it's time to start to factor out configuration code. That will be coming soon.
Looking For A New Job?
Ooops, there are no jobs. Post one now for only $29 and own this real estate!
Dude - you rock! Thanks for blogging. I'm learning from you.
Hey, I love this stuff... just glad you like what I am doing. Thanks!
Good stuff! Clean site design, too. Peace.
I have tried and tried with the onApplicationStart() method but it is not clearing the existing application variables. Does anyone know where I might be going wrong?
here is the method i've tried
<cffunction name="onRequestStart" returnType="boolean" output="true">
<cfargument name="thePage" type="string" required="true">
Matt, onApplicationStart isn't actually intended to clear the application variables. I thought that at one point too.
TO clear them, at the top of your onApplicationStart function, add:
<cfset structClear(application) />
That will do it for ya. Be aware though, the built in Adobe ColdFusion variable, application.applicationName, will also be cleared. You can fix this by adding the following after the structClear(...):
<cfset application.applicationName = this.name />
That will grab the application name from the Application.cfc component. There is also another way, but it is undocumented so no guarantees it will continue to work:
<cfset application.applicationName = application.getName() />
Thank you! it worked! I think I had some other issues also affecting the application but this def. did the job.
Thanks for jumping in and helping out @Matt - much appreciated.
Typically, onApplicationStart() can clear out all of your application variables, not by magic, but simply because it resets the value keys. If you are finding that you have code that is not being reset in the onApplicationStart() event handler, you might want to consider configuring that code within the event handler directly.
This actually serves two purposes:
1. We can reset the application more easily (as you are seeing here).
2. It gives you an at-a-glance understanding of how application-level variables are being created and cached. Now, you don't have to scour the rest of your app to see where variables are stored.
Hope some of that helps.