8

I need a reality check - and hopefully an explanation (if my reality is wrong).

The way the CF application framework evaluates things is this (my understanding) - request is passed to cfserver

  • cf finds an application.cfm or cfc (based on traversing rules)

  • application.cfc executes (if found)

  • the THIS scope is set (a series of application specific vars can be set here but

some are required - such as "applicationTimeout" - then a series of events takes place -and methods fired if needed.

-- onApplicationStart()

----onSessionStart()

------onRequestStart()

etc.

so my questions

1) The THIS settings happens on EVERY page request - before anything else?

2) If I set an application variable, in onApplicationStart() - it is available in any process that happens after that - AND should persist in memory for the length of applicationTimeout() - correct?

3) so if I do something like this...

if ( isdefined("application.myvar" ) { this.something = application.myvar; }

it SHOULD work on any page request after the initial request that started the application scope.

however it doesn't appear to do so.

my reason for asking is this - there are some interesting application lever settings that need to be set in the THIS scope... a few of them could be 'intensive' (at least form the perspective of executing on EVERY request - so I want to do them only ONCE, set a structure in persistent mem, and then have those available as THIS.

am I making some wrong assumptions?

thx

j-p
  • 3,410
  • 7
  • 45
  • 79
  • 3
    Why are you injecting them into the THIS scope? Use the application scope. To my knowledge THIS is only used to set certain application.cfc variables and anything you want to access outside of the cfc should in Application. – Busches Aug 13 '12 at 15:06
  • I thought the same. I thought the "this" scope was to provide settings for the attributes that are normally set in the cfapplication tag. I've never tried creating an application variable by using "this.dsn" I don't even know that it would work. – Travis Aug 13 '12 at 15:19
  • travis - i never mentioned this.dsn - i specifically said 'application level' settings - referring to how CF references those settings that are set in the admin, but can be overridden at the application.cfc level... My specific issue is mappings... which CANNOT be set as 'application.x' - they HAVE to be set in 'this.x' Hope that clarifies. it's more of a theoretical question as to WHY the application scope wasn't showing up as existant in the this scope... – j-p Aug 13 '12 at 16:26
  • 1
    this.something indicated to me that you were creating your own variables so I used 'this.dsn' as an example. This is the first time you've mentioned your actual problem being mappings. Where are you attempting to change the mappings, during the psudo constructor or the onRequest() onRequestStart() etc? Do you have Enable Per App Settings checked in the coldfusion administrator? – Travis Aug 13 '12 at 16:53
  • if by "pseudo constructor" you mean outside of any explicitly defined method - then yes, that is where I'm doing it... But I don't "Want" to do it there, because the means by which I'm creating the structure is somewhat resource intensive...and SHOULD NOT be done on every request. So I wanted to ONLY do it onApplicationStart()... but this would mean passing back to 'this' something that is set at application start - which hasn't occurred yet (and ironically, doesn't show up on subsequent requests, even through the scope exists... – j-p Aug 13 '12 at 17:25
  • this was more of a 'theoretical' question... of "this" and application scope... I think i understand it now, thru much trial and error - I'd still like to be able to set "application level" attributes (those that are set in cfadmin) on application start, as opposed to EVERY request in the "this" scope - but that's not the way the server is designed, so I'm looking for ideas on work-arounds - – j-p Aug 13 '12 at 17:29

5 Answers5

3

The ColdFusion Application.cfc documentation has this tidbit of knowledge:

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)
  3. onRequestStart
  4. onRequest/onCFCRequest
  5. onRequestEnd

The onApplicationEnd, onSessionEnd, and onError CFCs are triggered by specific events.

The overall request order has (at least) two more steps.

0: execute all code in cfcomponent that isn't in a cffunction
0.5: run the equivalent of the cfapplication tag for creating the Application

As such the answers to your questions are:

  1. If you're setting those variables in step 0, then yes.
  2. Correct.
  3. That depends on where you're setting the variable. If the values that you are trying to change are listed on the Application variables documentation page for Application.cfc then they must be in step 0. Setting them elsewhere will update the this scope, but will not take effect in step 0.5.
nosilleg
  • 2,103
  • 1
  • 21
  • 36
  • things like 'mappings' and 'mail settings' - which are overrides of CFADMIN settings - cannot, or rather don't "do" anything - if you cast them in the application. scope... the SERVER is looking at the THIS scope... – j-p Aug 14 '12 at 12:35
  • So they are values on this page? http://help.adobe.com/en_US/ColdFusion/10.0/CFMLRef/WSc3ff6d0ea77859461172e0811cbec22c24-750b.html – nosilleg Aug 14 '12 at 13:34
  • That stuff has to be set in step 0 in order to take effect in step 0.5. Once you're past 0.5 changing the `this` variables will have no effect. – nosilleg Aug 14 '12 at 16:27
3

There's two things at play here: when code runs, and when variable scopes are availed and how long they last.

  • The code outwith any method (ie: the "pseudoconstructor") runs every request. Obviously minimise the amount of code in this part of the CFC.
  • Code in various event handlers run as indicated by the event handler name, eg: onApplicationStart() code runs only once, when the application starts. Ditto onSessionStart() only runs once per new session.

Scopes:

  • this scope is available throughout the CFC. Behaves exactly like the this-scope in any other CFC, except some this-scoped variables have special meaning (like this.name, this.datasource, etc). Those special-meaning variables can be changed per session or per request in the relevant handlers, but seem to apply to the entire system (ie: not for the specific session or request making the setting change). In normal CFCs, the this scope is used to expose public variables, but as there is no public instance of Application.cfc, there is no point using the this scope beyond making those special settings. If one wants to have variables available to all methods within the CFC, use the variables scope as one normally would. Contrary to someone's advice here, this-scoped variables are NOT the same as application-scoped variables.
  • The request scope is also available throughout the CFC (pseudo constructor and methods). These will also be available to the calling code of templates called later in the request, like any other request-scoped variables.
  • Application scope: not available in the pseudo-constructor, even after the this.name setting is made. Only becomes available in onApplicationStart() and thenceforth from there.
    • Session scope: similarly, not available in the pseudo-constructor or onApplicationStart()l and not until onSessionStart().

I've demonstrated this in a blog post (test code provided), over here. it's too lengthy to include here, but the stuff above summarises it.

Adam Cameron
  • 28,963
  • 4
  • 33
  • 74
1

Please see the comments as it would appear the below post works but it does not. If you dump the this scope, your new value is displayed, but it does not actually change any application settings.

You can change any of the application settings anywhere you like; however, because the pseudo constructor runs each time a page is requested you will need to constantly change the setting after the pseudo constructor runs. The application scope is not available in the pseudo constructor so you can do this in the onRequestStart or onRequest function. I have done a simple test reassigning the customtagpaths per a condition in the onRequestStart function. You will notice the first time you access the page the custom tags folder will be 'customtags' additional requests will indicate 'someOtherCustomtagsFolder' On a side note if your application settings change on a per-user basis your global application settings will be flip-flopping and could cause problems with other users getting incorrect settings.

<cfcomponent>
<!--- pseudo constructor --->
<cfset this.customtagpaths = expandPath('./customtags')>

<!--- onRequestStart --->
<cffunction name = "onRequestStart" returnType="void">
     <cfif structKeyExists(application,'testSetting')>
          <cfset this.customtagpaths = expandPath('./someOtherCustomtagsFolder')>
     </cfif>
</cffunction>

<!--- onRequest --->
<cffunction name = "onRequest" returntype="void">
     <cfargument name="targetPage" type="String" required = "true" />
     <cfdump var = "#this#" label = "this">
     <cfset application.testSetting = "foo">
     <cfinclude template="#Arguments.targetPage#">
</cffunction>
</cfcomponent>
Travis
  • 4,165
  • 2
  • 20
  • 31
  • 1
    I believe that despite the `this` scope showing the new value, it wont actually effect the customtags that are run. An initial test seems to confirm that. – nosilleg Aug 14 '12 at 16:39
  • Hmm, additional testing shows that if you remove the setting all together it still persists (probably until the application resets). tags in an invalid path will continue to work after removing That would suggest that the psudo constructor actually does something with these values where later changing them only effects the surface value. You can still use conditionals in the psudo constructor to define the settings but CANNOT use any persistent scopes (application,session,etc). – Travis Aug 14 '12 at 18:13
  • I wonder if this could be done after the fact using the administrator API. I suppose if it IS possible it wouldn't be per-application and could cause all sorts of problems anyway. I can't test it due to security. When I post good I get down voted, when I post something that isn't quite right I don't. Trolls must not be awake yet. – Travis Aug 14 '12 at 19:09
-1

Anything in the this scope inside an Application.cfc file becomes an application varialbe and is only created ONCE per application life cycle. After the application starts, there is no other user for this within Application.cfc.

The first time a CF application is run, the contents of onApplicationStart() are run before onRequest/Start/End (with the exception of the "new in CF10" onServerStart()).

Any application variables set anywhere in the application exist until the application is stopped.

Your code from #3 should just be

if ( !structKeyExists( application, "myvar" ) { application.myvar = foo; }

then reference application.myvar wherever you need it.

From your description, nothing needs to be added to the this scope, it just needs to be put in the application scope.

Adrian J. Moreno
  • 12,826
  • 1
  • 34
  • 40
  • 4
    appreciate the attempt KunFoo - and really I'm not bashing - but THIS gets executed EVERY time a request is made - to illustrate. set "this.name" in your application to createUUID or getTickCount(). then dump that to the screen,hit refresh and you'll see that EVERY request is unique in the THIS scope. Those setting have to exist for there to be a construct for the "application" to initialize in onApplicationStart()... [and i knew someone would bring up structkeyexists... its not part of the problem, and i used that syntax for brevity.. but thx.] – j-p Aug 13 '12 at 16:20
-2

the application scope is not available in the Application.cfc pseudo constructor, because until this.name has been set there is no way to tie the request to the application.

If you are worried about the overhead of creating your apps mappings, one approach would be to cache them in the server scope which is available.

if(!structkeyexists(server, 'myappmappings')){ server.myappmappings = createMappings(); } this.mappings = server.myappmappings;

you might also be able to use cachePut/cache Get to store the mappings in ehcache, but I've not tried that in the pseudo constructor.

Chris Blackwell
  • 2,048
  • 15
  • 21
  • 2
    Unless you will only be running a single CF app on a server, using server scope variables is a bad idea..a very bad idea. – Scott Stroz Aug 13 '12 at 18:05
  • chris- I actually tried a couple things with cache - and they didn't seem to work - it IS a private server and a single app - so I took the server solution (for now) I'm looking for other solutions...and the "dynamic" mappings, may become a moot issue so....(it's still an interesting theoretical issue... – j-p Aug 14 '12 at 12:39
  • @ScottStroz, sure I should have added that caveat, but in the case of a single app server instance there is _nothing_ wrong with using the server scope. – Chris Blackwell Aug 14 '12 at 20:43
  • also, Railo has the edge here as you can define mappings per web-context which are loaded once at server start, avoiding the need to create them dynamically in App.cfc – Chris Blackwell Aug 14 '12 at 20:53
  • _"I should have..."_ - [then do it](http://stackoverflow.com/posts/11939502/edit). – Peter Boughton Aug 15 '12 at 23:38