How to best create and store APPLICATION variables? - coldfusion

I am using ColdFusion 9.0.1
I am taking over a site and the guy before me created about 100 variables and put them into the APPLICATION scope. I believe that his 100 variables were continuously being overwritten with each page load.
Basically, he had this in Application.cfc:
APPLICTION.VariableOne = "SomeStringOne";
APPLICTION.VariableTwo = "SomeStringTwo";
APPLICTION.VariableThree = "SomeStringThree";
My plan is to keep it simple and yet very readable is to test for a specific structure in the application scope. If it's not there, create the structure and variables:
if (not isDefined("APPLICTION.AppInfo") or not isStruct(APPLICTION.AppInfo)) {
APPLICTION.AppInfo = structNew();
APPLICTION.AppInfo.VariableOne = "SomeStringOne";
APPLICTION.AppInfo.VariableTwo = "SomeStringTwo";
APPLICTION.AppInfo.VariableThree = "SomeStringThree";
}
Of course, once the site is live and we are done creating all of the application variables, I'd move this into the into the onApplicationStart() method.
The solution that I want has to be more about "readability" and less about "efficiency". Several non-CFers, but very experience coders will be using this and will need to "get it" quickly.
Does my plan have any gaping holes or is it too inefficient?
Is there a more readable way of creating and managing application variables?

Why not move the definition into onApplicationStart() right now? If you need to reset them during development, you could always pass in a URL variable to flag it for reset, like so:
<!--- in Application.cfc --->
<cffunction name="onRequestStart">
<cfif IsDefined("url.resetApp")>
<cfset ApplicationStop()>
<cfabort><!--- or, if you like, <cflocation url="index.cfm"> --->
</cfif>
</cffunction>

Actually, after re-reading the OP, and reading the suggested solutions, I'm going to have to agree with the OP on his setup, for this very important reason:
This, in onApplicationStart()
APPLICTION.AppInfo = structNew();
APPLICTION.AppInfo.VariableOne = "SomeStringOne";
APPLICTION.AppInfo.VariableTwo = "SomeStringTwo";
Can then later be turned into this, within onRequestStart()
<cflock name="tmp" type="readonly" timeout="15">
<cfset REQUEST.AppInfo = APPLICATION.AppInfo />
</cflock>
Your app can then go on to access the REQUEST vars conveniently, esp, if you decide you want to cache CFCs in the same scope--they would simply go into a separate key:
APPLICATION.Model.MyObject = CreateObject('component','myobject');
Which, of course, also gets poured into REQUEST (if you choose)
Want to go Jake Feasel's route above? No problem:
<cfif isDefined('URL.reload')>
<cfset APPLICATION.Model = StructNew() />
</cfif>
Now you're able to flexibly kill your object cache but maintain your vars (or vice versa as you choose).
This is a great setup for another reason: If you want to build in your own Development/Production "mode", in which the development mode always recompiles the CFCs, but the production mode keeps them cached. The only change you have to make on top of this, is the REQUEST set noted above:
<cfif (isProduction)>
<cflock name="tmp" type="readonly" timeout="15">
<cfset REQUEST.AppInfo = APPLICATION.AppInfo />
</cflock>
<cfelse>
<cfset REQUEST.AppInfo = StructNew() />
<cfset REQUEST.AppInfo.VariableOne = "SomeStringOne" />
...etc...
</cfif>
You can also make the setting of vars and the creation of objects into a private method within Application.cfc, for even further convenience.

I would go ahead and just use OnApplicationStart but back in the pre Application.cfc days we used to do something like Application.Build and if the Build value was different then we did all of our sets on Application variables. So quick and dirty would be something like:
<cfparam name="Application.Build" default="" />
<cfset Build = "28-Nov-2011" />
<cfif Application.Build IS NOT Variables.Build OR StructKeyExists(URL, "Rebuild")>
<cfset Application.Build = Variables.Build />
<!--- A bunch of other CFSETs --->
</cfif>
This method though was something we used back when all we had was the Application.cfm

Related

ColdFusion Application.cfm cfinclude not working

I have a function called "conv" which is needed in several programs within my application. So rather than include it in each program, I put it in the Application.cfm, which looked like this:
<cfapplication name = "Moxware" sessionmanagement = "Yes">
<cfset lang = "LU">
<cfset x127 = Chr(127)>
<cfset mmox = 'Moxware'>
<cfinclude template="conv.cfc">
When I ran one of the programs using the function "conv" (which is in conv.cfc) I got an error that the function conv could not be found.
After I hunted around the internet for ideas I tried this:
<cfapplication name = "Moxware" sessionmanagement = "Yes">
<cfset lang = "LU">
<cfset x127 = Chr(127)>
<cfset mmox = 'Moxware'>
<cffunction name="onRequestStart" output="true" returntype="void">
<cfinclude template="conv.cfc">
</cffunction>
That gave me the same error message as before.
Can someone explain to me how to do this?
Note that the function conv was tested and works just fine.
Instead of using an include, like this...
<cfinclude template="conv.cfc">
Try creating an object, like this...
<cfscript>
MyObject = createObject("component", "conv");
</cfscript>
When you want to access a function within that object, try this...
<cfscript>
SomeValue = MyObject.MyFunction();
</cfscript>
You can include CFM pages into the CFC, but I don't think you can include CFC code into a CFC.
So this is possible...
<cffunction name="OnRequest" access="public" returntype="void" output="true" hint="Fires after pre page processing is complete.">
<cfargument name="TargetPage" type="string" required="true" />
<cfinclude template = "/myMapping/onRequestStart_include.cfm" />
<cfinclude template = "/myMapping/onRequest_include.cfm" />
<cfinclude template = "/myMapping/onRequestEnd_include.cfm" />
</cffunction>
Note the use of a mapping (in our case /myMapping), which can help if your doing this in CFC's. If no mapping is needed, just drop that.
But probably the best option is to instantiate your CFC from within the Application.cfc and use it.
<cfset myConv = createObject("component", "myMapping.conv").init() />
Again using a mapping to get to the CFC. The .init() is not always needed, depends how your CFC is setup.
Then presumably conv has methods you want to use (You talk about it as a single function? A cfc is essentially an object, so you create it as an object first and then use it's methods), so then you'd invoke then by using...
myConv.functionName()
Put another way - Application.cfc is no different from anywhere else in your code. How ever it's invoked and used elsewhere is how you should do it here. The only trick might be pathing to that CFC, which you can do by creating a custom mapping.
Also consider going old school (if it is just a function), and using custom-tags.
Good luck.

Fusebox not parsing xml files

Currently, in the application.cfc, I extend the Fusebox 5.5 Framework.
Then in the OnRequestStart method below, I set the fusebox mode depending on a certain condition.
The problem is that sometimes, the fusebox xml files do not reparse no matter what changes I make. If I force a reparse using the url variables fusebox.parse=true&fusebox.loadclean=true&fusebox.password=xxx then the files parse again.
It is almost like Fusebox remains in production mode even though when I dump the FUSEBOX_PARAMETERS.mode it says "development-full-load"
What could be causing this? Is the way that the fusebox mode is being manipulated correct in the code below or should that kind of setting be done somewhere else (besides the fusebox.xml obviously)??
Any help would be great.
Thanks
<cffunction name="onRequestStart">
<cfset variables.server_type = "Development" />
<cfswitch expression="#variables.server_type#">
<cfcase value="development">
<cfset FUSEBOX_PARAMETERS.mode = "development-circuit-load" />
<cfset FUSEBOX_PARAMETERS.debug = true />
<cfset request.component_reload = true />
</cfcase>
<cfdefaultcase>
<cfset FUSEBOX_PARAMETERS.mode = "production" />
<cfset FUSEBOX_PARAMETERS.debug = false />
<cfset request.component_reload = false />
</cfdefaultcase>
</cfswitch>
<cfif (StructKeyExists(attributes, "fusebox.loadapp") AND attributes.fusebox.password EQ application.fusebox.password) OR FUSEBOX_PARAMETERS.mode NEQ application.fusebox.mode>
<cfset this.onApplicationStart() />
</cfif>
<cfset superReturn = super.onRequestStart(arguments.1) />
</cffunction>
See, FUSEBOX_PARAMETERS are stored in application scope, by default they are included in huge container application.fusebox. Fusebox settings are populated when super.onApplicationStart() invoked, so modifying them in onRequestStart does not make sense.
I would recommend to move your cfswitch code into the component body where you define application settings.
In onRequestStart you can force the application restart to reread the settings, possibly something like this:
<cfif StructKeyExists(attributes, "fusebox.loadapp") AND attributes["fusebox.password"] EQ application.fusebox.password>
<cfset this.onApplicationStart() /
</cfif>
Please note that fusebox.loadapp is not built-in Fusebox attribute, it will work only for your app, simply prefixed like others for convenience. This way you can reread the singletones of your application.

What is the proper way to assign a general udf to application.cfc?

I simply want to define a function in application.cfc and expose it application wide to all requests. Preferably the "assignment" would only happen on application startup.
Is the preferred method to do something along the lines of this:
<CFCOMPONENT OUTPUT="FALSE">
<CFSET this.name = "Website">
<CFSET this.clientManagement = true>
<CFSET this.SessionManagement = true>
<CFFUNCTION NAME="GetProperty" OUTPUT="False">
<CFARGUMENT NAME="Property">
<CFRETURN this.Props[Property]>
</CFFUNCTION>
<CFFUNCTION NAME="OnApplicationStart" OUTPUT="FALSE">
<CFSET Application.GetProperty = GetProperty>
.
.
.
or is there something better?
By default, GetProperty will be visible in Variables scope already, this can be sufficient for many usages (in .cfm templates).
If you want to use these methods directly in the components, referencing them in the Application scope is fine.
Though I do this with Request scope in the onRequestStart(), it's just my personal preference. Something like this:
request.udf = {};
request.udf.halt = halt;
Please note that best practice in general is incapsulating the objects and having them referenced in variables scope of the host object. I typically do this when initializing the object, simply pass previously created objects as init() arguments.
P.S. Nowadays it is recommended to use lower case for tags and their attributes. Kind of good coding practices.
The best way to store site specific config data is probably going to be to create a new component named something such as SiteConfig.cfc with methods such as getProperty(propertyName) and setProperty(propertyName, value). You would then store this CFC in the application scope by doing the following inside Application.cfc's onApplicationStart method like:
<cfset application.siteConfig = createObject("component", "SiteConfig").init() />
Back to your original question though about storing a UDF in the Application scope, below is a way to do that. The basis is that in onApplicationStart you will create a new application persisted struct with your site's config properties like siteName and whatever else. Then a function is stored in a CFM file which is cfincluded only in onApplicationStart, then copied into the application scope. This means that all your regular page CFM files can use application.getProperty(propertyName).
Since the function is only created once and stored in the application scope it satisfies your original question's requirements about "assignment would only happen on application startup".
Hope this helps a bit!
getProperty.function.cfm
<cffunction name="getProperty" output="false">
<cfargument name="propertyName" type="string" required="true" />
<cfreturn application.config[propertyName] />
</cffunction>
Application.cfc
<cffunction name="onApplicationStart" output="false">
<cfset application.config = structNew() />
<cfset application.config.siteName = "My App's Display Name" />
<cfinclude template="getProperty.function.cfm" />
<cfset application.getProperty = variables.getProperty />
</cffunction>
test.cfm
<cfset propertyValue = application.getProperty("siteName") />
<cfdump var="#propertyValue#" />
You might consider creating a seperate "properties" CFC and instanciating it as
a singleton in the SERVER scope then it will be available from any CFML page even
if it isn't part of an application. If you go this route then there is no "server
start" event to bind to. Instead you can put this in the contructor of application.cfc
or in the body of application.cfm
<cfif not structkeyexists(server,"properties")>
<cflock name ="loadProperties"
timeout ="10"
type ="exclusive"
>
<cfif not structkeyexists(server,"properties")>
<cfset server.properties =
createObject("component","path-to-props.cfc")
.init({..inital properties..})
>
</cfif>
</cflock>
</cfif>
The lock code is to prevent the overhead of creating and assigning the UDF on every
request. This also allows the properties instance to persist so that having a
properties.SetProperty() function will work
you might also want to use the technique discussed here

Is onApplicationStart is good Idea in ColdFusion?

I have to use a Variable(Query Resultset) in ColdFusion, which will get the results from Other Application DB, and stores in Coldfusion Application.
The main idea is that I need to call the other Application DB only at Server startup time and cache the results in local. And I need to read the variable in other pages in my Application. I won't overwrite that variable in any page.
On googling I found that 'onApplicationStart' is useful to assign the variables at Application Startup time.
Is using the onApplicationStart fine or is there any other way? We can assign a variable at startup time(one time).
If onApplicationStart is fine: how to use? Maybe any link where it is explained clearly is helpful.
Well, it depends. How often will this query data be updated? If it really is unchanging, then onApplicationStart() is a fine place to put it. However, if it will change every so often, you can just tell Coldfusion to cache the query for a certain period of time, then you don't need to mess with onApplicationStart(), but rather when you call the query it will return the cached result automatically (within your specified time period).
Regardless, I would write a custom function to retrieve the data. Then it will be trivial to call it from onApplicationStart() or elsewhere.
Startup.cfc: (Named whatever you like)
<!--- Replace the datasource name with your db name --->
<cffunction name="getStartupQuery" hint="Returns a query recordset for startup">
<cfargument name="datasource" required="no" type="string" default="OtherAppDB">
<!--- Init the query variable --->
<cfset var result = queryNew("id")>
<!-- Get the query dataset --->
<cfquery name="result" datasource="#arguments.datasource#">
YOUR QUERY HERE
</cfquery>
<cfreturn result>
</cffunction>
Application.cfc: (Just the important parts)
<cffunction name="onApplicationStart">
<!--- init the startup.cfc, then retrieve the data
and save it to the application scope. Remember the component name must match
your component above --->
<cfset var startup = createObject("component", "startup")>
<cfset application.varFromOtherDB = startup.getStartupQuery()>
<cfreturn true>
</cffunction>
Now, you should be able to access this variable from any CFM or CFC in your application using:
<cfset myNewVar = application.varFromOtherDB>
or
#application.varFromOtherDB#
IF you use the onApplicationStart() method, I highly recommend implementing a method to reinit the application. For an example, see this other discussion.

Global Variables in ColdFusion

2 Questions -
In CF8 in the application.cfm I cold set a global variable
like so
<cfset DSN = "dej6_42">
I am now trying to adjust to the Application.cfc in CF10/Lucee and can not figure out how to set this same variable.
Here is my current Application.cfc
<cfcomponent output="false">
<cfset This.name = "My Application">
<cfset This.clientmanagement="True">
<cfset This.loginstorage="Session">
<cfset This.sessionmanagement="True">
<cfset This.sessiontimeout="#createtimespan(0,0,10,0)#">
<cfset This.applicationtimeout="#createtimespan(5,0,0,0)#">
<cfset DSN = "dej6_42">
</cfcomponent>
I have tried
<cfset This.DSN = "dej6_42">
Then tried to call in a separate page
<cfoutput>#Applicaton.DSN#</cfoutput>
I think from my research I will need to use both application.cfc and application.cfm to accomplish the above. *edit - I tried to add an include at the end of the application.cfc file to applciation.cfm and it did not work.
2 Question.
When I place any of the standard functions in the Application.cfc my site turns to a blank page
Here is that Application.cfc - I if I remove everything below the DSN set then it will display the site.
<cfcomponent output="false">
<cfset This.name = "My Application">
<cfset This.clientmanagement="True">
<cfset This.loginstorage="Session">
<cfset This.sessionmanagement="True">
<cfset This.sessiontimeout="#createtimespan(0,0,10,0)#">
<cfset This.applicationtimeout="#createtimespan(5,0,0,0)#">
<cfset DSN = "dej6_42">
<cffunction name="onApplicationStart">
</cffunction>
<cffunction name="onApplicationEnd">
</cffunction>
<cffunction name="onRequestStart">
</cffunction>
<cffunction name="onRequest">
</cffunction>
<cffunction name="onRequestEnd">
</cffunction>
<cffunction name="onSessionStart">
</cffunction>
<cffunction name="onSessionEnd">
</cffunction>
<cffunction name="onError">
</cffunction>
</cfcomponent>
Your example doesn't set a global variable. It sets a variable in the variables scope: it will not be accessible to any CFC-based code nor any custom tags used within the request. It'll only be available in the Application.cfm, the file requested, files it includes, and OnRequestEnd.cfm
Application.cfc is a CFC (to state the obvious), so variables-scoped variables set within it are only available within it. If you want to set an application-wide variable, you need to put it in the application scope. Application scope variables should be set in the onApplicationStart) handler which is run once when the application starts, but not on every request. By way of comparison Application.cfm (which is misnamed) is run on every request. It should be called OnRequestStart.cfm.
So to be clear, setting an application-scoped variable in onApplicationStart would be as thus:
function onApplicationStart() {
application.DSN = "dej6_42";
}
If you use an onRequest() interceptor, and within that include the originally requested file, then the request will be run in the context of the Application.cfc instance, and variables set within onRequest will be available to the rest of the mainline request code, much like the way you set your variable in Application.cfm. Semantically though, if you mean a variable to exist for the life of the application (like a DSN), then putting it in the application scope is the best bet.
It sounds to me from the inferences one can make from your question that your app architecture might be languishing in the 1990s. I think you should read up on using a framework (eg: FW/1 or ColdBox) to better organise your code in a maintainable and scalable way.
Also you should read up on Application.cfc (and method reference). And probably CFCs in general: Using ColdFusion components-Developing guide.
You also might want to think about modernising your approach to writing CFML and spare the tags for view code, and otherwise using script. It makes the code easier to follow for both you and other developers who might end up needing to maintain it if the whole app isn't cluttered up with tags.
You need to set it into the application scope
<cfcomponent output="false">
<cfset This.name = "My Application">
<cfset This.clientmanagement="True">
<cfset This.loginstorage="Session">
<cfset This.sessionmanagement="True">
<cfset This.sessiontimeout="#createtimespan(0,0,10,0)#">
<cfset This.applicationtimeout="#createtimespan(5,0,0,0)#">
<cfset application.DSN = 'dej6_42'>
</cfcomponent>