A user sent in a request for me to look over some code he would like to use for his Application.cfc file. (As an aside, don't forget my online Application.cfc template you can use.) The template looked nice but had one main issue that I noticed. Since the user got the template from someone else, I won't post the whole thing as it isn't my code, but I will demonstrate the problem I saw.
First is the onSessionStart:
<cffunction name="onSessionStart" output="false">
<cfscript>
session.started = now();
</cfscript>
<cflock
scope="application" timeout="5" type="Exclusive">
<cfset
application.sessions = application.sessions + 1>
</cflock>
</cffunction>
Notice that he is keeping count of the sessions, a topic I covered last week. Most importantly though - notice his lock. He is using a scope based lock to ensure his update of the application variable is single threaded. Now lets move on to the onSessionEnd method:
<cffunction name="onSessionEnd" output="false">
<cfargument name = "sessionScope" required=true/>
<cfargument name =
"applicationScope" required=true/>
<cfset var sessionLength =
TimeFormat(Now() - sessionScope.started, "H:mm:ss")>
<cflock
name="AppLock" timeout="5" type="Exclusive">
<cfset
arguments.applicationScope.sessions = arguments.applicationScope.sessions - 1>
</cflock>
</cffunction>
(Forgive the bad formatting - this came in via email.) Notice the problem? Try to figure it out before reading on.
He correctly uses a lock again to update the session count. But he can't use a scope lock since the application scope isn't available directly in onSessionEnd. That's why he has to use the application scope passed in via arguments.
So the problem then is that in one case he has a scope based lock and in another case he has a named based lock. He needs to use one or the other. Since he can't use a scope lock in onSessionEnd he should use the named based lock only.
p.s. A few other small things that I'll point out that are minor but bug me: I don't like using cfscript as he did here, just for one line of code. He also forgot the arguments prefix for sessionScope in onSessionEnd. I tend to be anal about using the arguments prefix in my methods.
Archived Comments
Since I have never setup an appplication.cfc nd have usually used application.cfm I know I am behind the curve.. however just to make sure, and I am sure this is a stoopid question, I can just rename the application.cfm files to application.cfc and make the appropriate code changes for it to work correctly?
Ray,
Your app.cfc reference has
<cfset this.scriptProtect = false>
Shouldn't it be the following?
<cfset this.scriptProtect = "all">
PS: No. I prefer to write my own security. Plus I find scriptProtect tends to interfere at times, like on a blog if you try to post code as the author, it will block it. It _is_ a nice feature, I just don't use it myself.
great website and good idea! However, you spelt my hometown wrong...its spelt worcester park. Not that it matters but thought you should know
@Jay - I wouldn't just rename it. A CFC is quite different from an CFM file. You need to reorganize stuff. I'm pretty darn sure I did a blog entry on this.
Hmm, I can't find it.
I can maybe write a new one on it. Do you have a good, and not too complex App.cfm you could share?
Ray, why is it necessary to use only one kind of lock, named or scoped, across the whole Application.cfc?
Tom:
2 issues.
1) It is important to use the same type of lock, or it is useless.
2) onSessionEnd can't use the scope lock, it can only use the named lock.
I have a very simple newbie one ..
here it is..
<CFSETTING ENABLECFOUTPUTONLY="Yes">
<CFAPPLICATION NAME="QAX"
CLIENTMANAGEMENT="Yes"
SESSIONMANAGEMENT="Yes"
SETCLIENTCOOKIES="Yes"
SESSIONTIMEOUT="#CreateTimeSpan(0, 8, 0, 0)#"
APPLICATIONTIMEOUT="#CreateTimeSpan(0, 8, 0, 0)#"
CLIENTSTORAGE="Registry">
<CFERROR type="request" template="app_error.cfm" mailto="jay_mcconathy@txeb.uscourts.gov">
<!--- Do I need to create my app var? --->
<cfset needInit = false>
<cflock scope="application" type="readOnly" timeout="30">
<cfif not structKeyExists(application,"sessions")>
<cfset needInit = true>
</cfif>
</cflock>
<!--- Yes, I do need to make it. --->
<cfif needInit>
<cflock scope="application" type="exclusive" timeout="30">
<cfif not structKeyExists(application,"sessions")>
<cfset application.sessions = structNew()>
</cfif>
</cflock>
</cfif>
<!--- Store my last hit. --->
<cfset application.sessions[session.urltoken] = now()>
<CFSETTING ENABLECFOUTPUTONLY="No">
I have not seen a blog entry on it which is why I am asking..
Do you have to var scope the lock name like you would a query name?
As far as I know, no.
mmm Ray & Jay there's this entry in the coldfusion cookbook ;-p
http://www.coldfusioncookbo...
Ray,
I think I've realized why I was confused, and I'm wondering if you can confirm: the problem wasn't that the user was merely using two different kinds of locks in Application.cfc; the problem was that the user had two different kinds of locks protecting the same scope, which would render them less effective, right?
It doesn't render it less effective. It renders it UNeffective. :)
Great post Ray and thank you for confirming my suspicion on this. I'm trying to clean up another developer's Application.cfc logic and I came across this very scenario which threw up a red flag.
One thing, though:
As I read the sentence "But he can't use a named lock since the application scope isn't available directly in onSessionEnd" I was a bit confused. Since nobody has commented on it yet I suppose most understood what Ray was trying to say, though, I feel like posting a comment to help out the noobies (not to correct Ray as the paragraph immediately following this sentence already does that for us), the sentence should read:
"But he can't use a [scope based lock] since the application scope isn't available directly in onSessionEnd"
When in doubt, check Ray's blog!
^ Write that down :)
Good catch there Matt. I'll edit the post.
When switching from Application.cfm to Application.cfc and adding error handling my load time has increased dramatically. A page on the server using .cfm loads in about half a second, whereas the .cfc's server page takes over 9 seconds to load. Is this normal?
Nope. Can you show us your code? Use Pastebin please.
http://paste2.org/p/457110
Thanks!
There is a lot wrong with this file - but none that immediately imply a slowdown. Comments from top to bottom.
Lines 15-20. If you use J2EE Sesions (CF Admin setting) you can get rid of those lines.
Line 22 doesn't make sense. This should either be an Application variable.
Lines 24 is an include. What is the purpose of it being there? It will run on every request. If you want that - use it within onRequestStart.
Lines 25-33 should most likely be in onSessionStart. No - lines 25-27 shoul dbe. Lines 29-33 should probably be in onRequestStart.
Think of it like this - if you want something to run on every request, you really want to use the onRequestStart method. If you want to define some session variables then you to use onSessionStart.
You can also consider wrapping your code blocks with <cftimer> to determine the exact portion that is slowing things down.
Thanks Ray! I made those changes. The timing seemed to calm down a bit the more pages I ran. I'm not sure why that'd happen, but it did. cftimer just showed close to 0ms runs, so maybe it was something else completely. Thanks for all your help!