Requesting an Application.cfc Element of 'THIS' - coldfusion

If this has been asked before I apologize but I wasn't able to get a solid enough understanding without some assistance ....
I have a Local ColdFusion9 App that I use for development and testing running on WinXP SP3 with Apache 2 ...
What I'm having a problem understanding how to call request elements passed from the Application's 'THIS' scope to the onRequestStart() method.
For instance, My Application.cfc has the following in 'THIS' ...
<!--- MY 'THIS' Statements in Application.cfc --->
<cfscript>
/* define custom coldfusion mappings. Keys are mapping names, values are full paths */
this.mappings = structNew();
this.mappings['/tags'] = ExpandPath('/cfdev/tags');
</cfscript>
Further into my Application.cfc I have the following in the onRequestStart() function ...
<!--- Run before the request is processed --->
<cffunction name="onRequestStart" returnType="boolean" output="false">
<cfargument name="thePage" type="string" required="true">
<cfinclude template="#arguments.thePage#">
<!--- Lot's of onRequest statements and then ... --->
<cfset request.mappings = #THIS.mappings#>
<cfreturn true>
</cffunction>
Now ... Supposing I have a page where I call <cfdump var="#request.mappings#"> ...
I get an error stating 'Element MAPPINGS is undefined in REQUEST.' ...
However, (here's where my confusion begins) ... If I call <cfdump var="#request#"> Two structures are returned ... The first containing a key for "cfdumpinited" with a value of 'False' and the second with the structKey 'mappings' which contains another struct with a key of '/tags' and a value of 'C:\vir_dir\CFDEV\tags' as one would expect ...
If someone could please explain why it is that request succeeds yet request.mappings fails as I'm a bit in the woods here ... ;-)

Follow the logic through:
a request is made
onRequestStart runs
you explicitly include the template requested
youTHEN set request.mappings = this.mappings
onrequeststart finishes
the template requested executes
So if you have this in your requested template:
<cfdump var="#request.mappings#>
Then when you include that file at step 3... request.mappings doesn't exist yet (as they are created in step 4).
However when you change it to:
<cfdump var="#request#>
Then there's no error condition, so your CFINCLUDE runs fine, but DOESN'T OUTPUT ANYTHING (because you have output="false" on the function definition.
The dump you are seeing is the one coming from CF executing the requested template (step 6), by which time request.mappings exists.
I think you are confusing onRequestStart() with onRequest(), to be honest. It's in onRequest() that one might explicitly include the requested template, because onRequest() runs INSTEAD of CF running the requested template automatically. One would not generally include the requested template in onRequestStart() because it'll end up running twice.
Make sense?

I'm late to the party here, and may be missing a key element, but why not do this:
application.mappings = structNew()
instead of
this.mappings = structNew()
that way, you only set them once (on application start), you have access to them across your whole application, and you don't have the extra overhead of setting the request scope on every page hit.

Related

Instantiating one CFC twice

When I try to instantiate one CFC from 2 different CFCs, ColdFusion returns a 500 error. I tried making one of the CFC extend the other, but it did not solve the issue. Is this possible, or am I simply doing something incorrectly?
<!--- one.cfc --->
<cfcomponent name="FirstCFC">
<cfset this.Tools = createObject('component', 'toolbox').init()>
....
</cfcomponent>
<!--- two.cfc --->
<cfcomponent name="SecondFC">
<cfset this.Tools = createObject('component', 'toolbox').init()>
....
</cfcomponent>
<!--- toolbox.cfc --->
<cfcomponent name="Toolbox">
<cffunction name="init" access="public">
<cfreturn this>
</cffunction>
<cffunction name="someFunc" access="public">
</cffunction>
</cfcomponent>
Here is a screenshot of the 500 error
Here is a more "full" code sample
http://pastebin.com/zJ1zpHYy
The java stack trace you've included is indicitive of an infinite recursion error. Take care when creating objects of type A, which have a new object of type B in their pseudo-constructor. If the object B itself creates an object of type A in its pseudo-constructor, you have yourself an infinite recursion of objects being created, ending in an ugly java stack trace.
Shawn is right. He posted more code and you can see two.cfc makes tools.cfc and tools.cfc makes two.cfc.
You need to move to an injection style setup (ala ColdSpring for example).

How to include UDF_library in application.cfm?

I am using ColdFusion 8.0.1
I am working on an existing application that has thousands of pages. I am trying to include a library of new UDFs in my application.cfm file.
I addedd this line to application.cfm:
<!--- UDF library include --->
<cfinclude template="UDF/udf_library.cfm">
The UDF library includes other files that contain UDFs, like this:
<cfinclude template="udf_powerreview.cfm">
I have functions in the udf_powerreview.cfm file, such as:
// CREATE POWER REVIEWS SNIPPET
function createPRSnippet(Page_ID) {
LOCAL.Page_ID = ARGUMENTS.Page_ID;
if (isNumeric(LOCAL.Page_ID) && LOCAL.Page_ID > 0) {
LOCAL.Snippet = "<div class='pr_snippet_product'><script type='text/javascript'>var pr_snippet_min_reviews = 0; POWERREVIEWS.display.snippet(document, { pr_page_id : '#LOCAL.Page_ID#' });</script></div>";
} else {
LOCAL.Snippet = "";
}
return LOCAL.Snippet;
}
The debugging tool says that UDF/udf_library.cfm and udf_powerreview.cfm are being successfully included.
The problem is when I call the function in another page, I get an error that says that function doesn't exist. When I can copy the function and put it directly into the page that it is used in and it works just fine. And, I do not get the error "routines can not be declared twice".
In every site that I build, I create a udf_library.cfm or udf_library.cfc in the exact same manner. They always work fine.
What might prevent the functions from being available and accessed? Is there an application setting that needs to be set?
It's a page scope issue. Don't think of the Application.cfm as an include on all your pages, just know that it runs first. Somethings it initializes will carry over to your existing page scope and some things won't. Using an Application.cfc instead of an application.cfm takes care of much of the ambiguity.
To make your UDF's available to your whole application, I would suggest using a "Singleton" Design pattern. First take your UDF's and put them in a CFC format. This will make them more portable.
in your application.cfm you could put the following lines:
<cfif NOT isdefined('session.udf_powerreview') or isdefined('url.resetudf')>
<cfset session.udf_powerreview = createobject('Component','udf.udf_powerreview')/>
<!--- this 'udf.udf_powerreview' represents the physical path udf/udf_powerreview.cfc --->
</cfif>
I'm stuffing it in the session scope instead of the application scope, becuase you won't have an good way of resetting the application scope if you modify your UDF's.
Either way, once this is in your application.cfm you should be able to see your functions on any page.
<cfdump var="#session.udf_powerreview#">
Here is one strategy that I use. This basically calls the UDFs "on demand". It won't reimport the UDFs if it already exists. You do however have to have named arguments however, otherwise you'd have to strip out the UDFName out of the argument collection. I'm worried however that argument order might not be preserved, I haven't investigated that.
application.cfm
<cfapplication
name="udftest_001" />
<cffunction name="udf">
<cfargument name="udfname" type="string" required="true">
<cfif NOT isDefined(udfname)>
<cfinclude template='./udfs/#udfname#.cfm'>
</cfif>
<cfset tempfunc = variables[udfname]>
<cfreturn tempfunc(argumentCollection=arguments)>
</cffunction>
index.cfm
<cfoutput>
#udf(udfname='testUDF',firstname='John',lastname='Smith')#<br/>
#udf(udfname='testUDF',firstname='Betty',lastname='Ford')#<br/>
</cfoutput>
/udfs/testudf.cfm
<cfscript>
function testUDF() {
return 'Hello ' & arguments.firstname & ' #arguments.lastname#';
}
</cfscript>
I suspect something is up with relative paths.
Can you make "UDF" a mapping? Then you can do
<cfinclude template="/UDF/udf_library.cfm">

Get names of functions injected into CFC?

I am using the technique detailed in this answer to manage a library of small utility functions. (Essentially, each function is loaded as a "mix-in" using cfinclude.)
I have need, however, to know the names of the functions that the object has (once instantiated). A cfdump on the object only shows the init function which is written directly in the CFC.
Some more detail:
I am creating the object in the application scope in OnApplicationStart().
<cfset application.udfs=createObject("component","extensions.udfs").init()>
However, to save the developers from having to constantly write application.udfs.foo(), I thought I'd grab all of the functions and drop them in to the variables scope in OnRequestStart(), so that these hypothetical developers could just write foo().
<cfset foo=application.udfs.foo>
Obviously, though, this needs to be dynamic and to happen for each of the functions in the object, no matter how many there are. If I repeat this line for every function I've lost whatever I'd gained by having a library that is dynamically generated.
I thought perhaps I could use a collection loop, but that was invalid. I am fairly certain there's a way to get the list of methods in an object, but I have not yet been able to find it.
Any clues?
By the by, my fallback is going to be to copy the application.udfs object to a local object with a nice short name (like "u") so that the developers can simply type u.foo(), so no need to suggest that if what I want to do can't be done.
This should allow you to import all your udfs into the global variables scope:
StructAppend(variables, application.udfs);
I think GetMetaData should help you.
Here another interesting option suggested by Ben Nadel:
Check out the detail in his blog entry: http://www.bennadel.com/blog/1776-Creating-Globally-Accessible-User-Defined-Functions-In-ColdFusion-Safer-Version-.htm
UDF.cfc
<cfcomponent
output="false"
hint="I define user defined functions.">
<cffunction
name="getMessage"
access="public"
returntype="string"
output="false"
hint="I return a test message.">
<cfreturn "I am defined in the UDF component" />
</cffunction>
</cfcomponent>
Application.cfc
<!--- Define the application. --->
<cfset this.name = hash( getCurrentTemplatePath() ) />
<cfset this.applicationTimeout = createTimeSpan( 0, 0, 5, 0 ) />
<!---
Add all of our "global" methods to the URL scope. Since
ColdFusion will automatically seach the URL scope for
non-scoped variables, it will find our non-scoped method
names.
--->
<cfset structAppend(
url,
createObject( "component", "UDF" )
) />

The symbol you provided is not a function

Key points before reading further
All variables are properly var'ed (you'll have to trust me)
Scopes are not being reset while these long-running processes are happening
When dumping the metadata for the supposedly missing/invalid method, I get the right information
There are only two places where the name of this method are referenced in the application. Once where it's defined, and once were the method is called in the code below.
I have a very strange intermittent error that I can't seem to track down. Here's the background (these are severely trimmed down to simplify for posting).
FeedService.cfc:
<cfcomponent output="false" extends="FeedDAO">
<cffunction name="processXmlFile" access="public" output="false" returntype="struct">
<cfset Var local = StructNew() />
/***************************************
THE VARIABLES ARE ALL VAR'D - PROMISE!!!
Lots of other stuff goes on in here to get the ultimate set of XML nodes to loop through
*****************************************/
<cfloop from="1" to="#ArrayLen(local.arrChannels)#" index="local.currentChannelItem">
... Lots of XML parsing and stuff and things going on here ...
<cfset LOCAL.invCheck = checkCustomerListing(
Acct_ID = local.invStruct.AcctID
, CustomerListingID = local.invStruct.CustomerListingID
) />
... Lots more stuff going on here ...
</cfloop>
</cffunction>
</cfcomponent>
FeedDAO:
<cfcomponent output="false">
<cffunction name="checkCustomerListing" access="public" output="false" returntype="numeric" hint="Returns the numeric inventory ID for an existing inventory listing, or 0 if the listing doesn't exist.">
<cfargument name="Acct_ID" type="numeric" required="true" hint="" />
<cfargument name="CustomerListingID" type="string" required="true" hint="" />
<cfset var rs = "">
<cfquery name="rs" datasource="#Variables.DSNs.Primary#">
SELECT ID FROM TheTable
WHERE
Acct_ID = <cfqueryparam cfsqltype="cf_sql_integer" value="#Arguments.Acct_ID#" />
AND Customer_Listing_ID = <cfqueryparam cfsqltype="cf_sql_varchar" value="#Arguments.CustomerListingID#" />
</cfquery>
<cfif rs.RecordCount>
<cfreturn rs.Inv_ID />
<cfelse>
<cfreturn 0 />
</cfif>
</cffunction>
</cfcomponent>
I'm calling the initial function like so:
<cfset processStruct = Server.FeedService.processXmlFile(filePath) />
So, when a feed gets submitted to the processXMLFile function, it looks through all of the items in the file. A feed file may have 10, 100, or even 1000 entries. I get occasional error messages like this while a file is getting processed:
[struct]
Detail: The symbol you provided checkCustomerListing is not the name of a function.
Message: Entity has incorrect type for being called as a function.
StackTrace: coldfusion.runtime.CfJspPage$UninvocableEntityException: Entity has incorrect type for being called as a function.
at coldfusion.runtime.CfJspPage._invokeUDF(CfJspPage.java:2441)
at coldfusion.runtime.SuperScope.invoke(SuperScope.java:18)
at coldfusion.runtime.CfJspPage._invoke(CfJspPage.java:2222)
More stack dump information
Type: Application
symbolName: checkCustomerListing
[object of coldfusion.runtime.CfJspPage$UninvocableEntityException]
Class Name: coldfusion.runtime.CfJspPage$UninvocableEntityException
Fields:
java.lang.String symbolName: checkCustomerListing
Parent Class: [object of coldfusion.runtime.ApplicationException]
Class Name: coldfusion.runtime.ApplicationException
Parent Class: [object of coldfusion.runtime.NeoException]
Class Name: coldfusion.runtime.NeoException
Methods:
findAdvancedCFTarget(coldfusion.runtime.AdvancedCFException, java.lang.String[]) returns int
findCustomTarget(coldfusion.runtime.CustomException, java.lang.String[]) returns int
findThrowableTarget(java.lang.Throwable, java.lang.String[]) returns int
getDetail() returns java.lang.String
getLocalizedMessage() returns java.lang.String
getMessage() returns java.lang.String
getRootCause() returns java.lang.Throwable
getString(java.lang.Throwable, java.lang.String, java.util.Locale) returns java.lang.String
getType() returns java.lang.String
setLocale(java.util.Locale) returns void
unwrap(java.lang.Throwable) returns java.lang.Throwable
Parent Class: [object of java.lang.RuntimeException]
Class Name: java.lang.RuntimeException
Parent Class: [object of java.lang.Exception]
Class Name: java.lang.Exception
Parent Class: [object of java.lang.Throwable]
Class Name: java.lang.Throwable
Methods:
fillInStackTrace() returns java.lang.Throwable
getCause() returns java.lang.Throwable
getLocalizedMessage() returns java.lang.String
getMessage() returns java.lang.String
getStackTrace() returns java.lang.StackTraceElement[]
initCause(java.lang.Throwable) returns java.lang.Throwable
printStackTrace(java.io.PrintWriter) returns void
printStackTrace(java.io.PrintStream) returns void
printStackTrace() returns void
setStackTrace(java.lang.StackTraceElement[]) returns void
toString() returns java.lang.String
I may get one error in 1000 entries, or I may get a small batch of errors at one time, and the rest of the feed processes just fine (due to some try/catch logic to prevent the entire thing from crapping out). At one point, the checkCustomerListing was in a completely different Server scoped object, and I never had a problem. I moved it into the FeedDAO and started calling it via the Super scope, and that's when these intermittent errors began.
UPDATE: I have everything properly var'ed, I just chopped it all out for the sake of brevity.
UPDATE AGAIN: Changed code sample comments to make it clear that there is a lot of stuff going on before the first loop begins, including setting all LOCAL variables that will be used in the loop.
More Code Information:
I should note that there are only two places in our entire application (thousands upon thousands of lines of code) where the string 'checkCustomerListing' exists. One is where the function is called, and two is where the function is declared. There are no other instances of the string checkCustomerListing anywhere.
Update: 6 September, 2011
I added some additional error checking to see if I could find out what the app thought checkCustomerListing was (thanks Adam and Ryan). Here's my new try/catch statement:
<cfcatch type="any">
<cfset local.tmpError.cfcatch = cfcatch>
<cfif isDefined("checkCustomerListing")>
<cfset local.tmpError.customerListing = checkCustomerListing />
<cfset local.tmpError.customerListingMeta = getMetaData(checkCustomerListing) />
<cfelse>
<cfset local.tmpError.customerListing = "Checkcustomerlisting is not defined" />
</cfif>
<cfset Server.Utilities.Errors.emailCaughtError(local.tmpError)>
</cfcatch>
So I got an error this morning, and in the email I received, there is no customerListing node in the dump, but there is a meta node:
CUSTOMERLISTINGMETA:
[struct]
ACCESS: public
HINT: Returns the numeric inventory ID for an existing inventory listing, or 0 if the listing doesn't exist.
NAME: checkCustomerListing
OUTPUT: false
PARAMETERS:
[array]
1) [struct]
HINT: [empty string]
NAME: Acct_ID
REQUIRED: true
TYPE: numeric
2) [struct]
HINT: [empty string]
NAME: CustomerListingID
REQUIRED: true
TYPE: string
RETURNTYPE: numeric
All of that meta information is exactly correct... so if it can find the metadata for the function, why can't it find the function itself?
This usually crops up as a result of lack of VARing as others have alluded to. Quite often people have a private function called "getStuff" and within that they have a query called "getStuff". If the variable isn't VARed, then the getStuff query result goes into the CFC's variables scope, which will overwrite the function getStuff because that also resides in the variables scope.
So check your usage of checkCustomerListing for any same-named variables that aren't VARed (or in the local scope).
(and note that the checkCustomerListing variables don't need to be within the same method for this to happen... it could be anywhere in the CFC or any extending or super CFCs...)
Why are you doing super.checkCustomerListing? You would only do that if you overrode the function in your service and wanted to run the 'parent'. Just call checkCustomerListing().
It may be in your abridged code but are you validating that local.invStruct.AcctID and local.invStruct.CustomerListingID both exist and are of the proper types? Sometimes in Java "function doesn't exist" means "method signature doesn't exist". I am not sure when CF validates datatypes but if you are looping over the same two variables if might be shortcutting and not validating the types in later loops.
Might also toss a val() on the return value just in case.
i've seen this before when you have an object cached in a scope that is taking a while to run and you either overwrite or remove the object in the cache. my guess is this is what is happening, another application is doing something to the object in the cache and it's casuing the other applications to trip over each other.
a good way to test this is to duplicate the object cached in the server scope to a local variable and then use that local variable to run your process:
<cfset _feedservice = duplicate(Server.FeedService)>
<cfset processStruct = _feedservice.processXmlFile(filePath) />
now even if the object in the server scope gets overwritten or removed, you still have a copy of the original object that your process will use. it also won't be a bad idea to use locks around the above code:
<cflock scope="server" timeout="5" type="readonly">
<cfset _feedservice = duplicate(Server.FeedService)>
<cfset processStruct = _feedservice.processXmlFile(filePath) />
</cflock>
now personally an even better practice (in my opinion) would be to add something to your onApplicationOnStart() event that will copy all the objects that you are caching to the server scope into the application scope. the only reason i can see anyone ever using the server scope is because you want all the applications on the server sharing the same objects. doing this will still allow your applications to share the same code, but will prevent other applications from accidentally tripping over each other

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.