ColdFusion Application.cfc & cfinvoke & THIS scope - coldfusion

In using the CF application.cfc - there is a need to create some vars to begin with (in the THIS scope) - such as this.name and this.applicationtimeout() etc.
I ran across something I consider an odd behavior - and hope someone can explain why this happens and a possible workaround.
There are quite a few "THIS" vars accepted that can work to set application specific variables (of course you have to check "allow application specific vars" in the CFADMIN - which I did)
One of these is THIS.mappings - which is an array of mappings - MINE looks something like this:
this.path = GetDirectoryFromPath( GetCurrentTemplatePath() );
this.mappings = {
'/files' = '#this.path#_my_files\'
,'/functions' = '#this.path#_my_functions\'
,'/logs' = '#this.path#_my_logs\'
};
it works fine - when it is set inside a cfscript block at the top of the application.cfc
it works fine - if I put that script block in it's own file and cfinclude it in the application.cfc
however - In an attempt to segment my code - I wanted to put ALL of my application setting in a settings.cfc... (the thought here was IF some setting had to be changed - I wouldn't have to worry about 'where' to look, I don't really want to split the THIS stuff on my app.cfc and other application or session settings in the settings.cfc
SO I created a method in the settings.cfc called getTHIS, and I put the script block there... then used
<cfinvoke component="settings"
method="getTHIS"
returnvariable="THIS"
/>
Which WORKS - except (it seems) on the mappings...
The this.name etc, all seem to work and get set - as a matter of fact the this.mappings gets set fine too (so it appears) if i do a
<cfdump var="#THIS#" label="THIS" />
The dump is identical to the dump of THIS when I set it 'literally' on the app.cfc page..
HOWEVER - any attempt to call a template thru the mapping - results in a standard "if you want to use absolute paths you have to create a mapping blah blah..."
MY BIGGER goal was (on application start) to scan a directory for sub-directories, and create mappings based on certain sub-directories.. but if I can't abstract that functionality out into it's own function - I'll be forced to write it directly in the app.cfc (which wouldn't KILL me, but again, I was trying to segment my code logically... There seems to be a limitation of when and where these mappings can be set... true?
So I guess the big question is - can I SET this.mappings thru an outside method??
I guess I could bring back the 'settings I want' using the cfc call, and then just do the 'set this.whatever = return form cfc' - (this may be my answer...)
Thanks

Mappings can only be set in the 'pseudo-constructor' and not inside any of the methods inside Application.cfc - http://adobe.ly/QN2oX1
You could try setting this.mappings to the result of a CFC call (I cannot think of why this would not work), but if it depends on a mapping to do so, it will likely not work.

I haven't tested this, but I'm pretty sure it would work if your application.cfc extends your settings.cfc.
component {
public any function getMappings() {
var mappings = {};
//code to get your directories etc.
return mappings;
}
}
component extends="settings" {
this.name = "xxxx";
this.mappings = getMappings();
...
}

Related

Calling the same ColdFusion component from different templates

In a.cfm I have:
<CFSET VerObj = New cfcomponents.VerFold.Ver_Users()>
<CFSET VerObj.Functiion_1(userid)>
In the next few templates, c.cfm, I need to call a different function within Ver_Users.cfc I can do:
<CFSET VerObj = New cfcomponents.VerFold.Ver_Users()>
<CFSET VerObj.Function_2(userid)>
If I need to call the same cfc again from yet another template down the road and I keep doing this I think it is very awkward and repetitive? the purpose of using .cfc is so that I can reuse or call the functions by just doing:
<CFSET VerObj,Function_1()> or calling Function_2
from anywhere without keep instantiating the cfc am I correct? is there any example on how to accomplish This?
Instantiating a component has a very small performance penalty. It is not false to re-instantiate it in different templates.
If you really want to cut away these few nanoseconds an bytes of memory you can instantiate the component one time at the onApplicationStart() event im your Application.cfc and store it in the application scope.
<cfset application.verUser = new cfcomponents.VerFold.Ver_Users()>
And then call the functions in your cfm like:
<cfset application.verUser.Function_1()>
#Rbt - you could create an Application.cfc that instantiates the Ver_Users component, and call application.Function1() and application.Function2() in your cfm files. You could create a session and use Session scope, or use Application scope which would treat it as a global, depending on how you want to use it.

Any way to block out new code for legacy versions of CF

I have written a library I include for some security patches. In that library there are a number of CF8 and up function,attributes,etc.
I really really dont want to cut the functionality down bc adobe couldnt get it together and get this basic functionality into CF7 so Im looking to write separate lines into each function. The issue is when CF is initially reading the code it bugs out if it finds something it doesnt recognize, whether or not it is going to be called.
For example in cfdirectory CF7 doesnt support the 'type' attribute. I have a find directory function that fails in CF7 when the 'type' attribute is present and taking it out increases the search time by 10x.
<cftry>
<cfdirectory action="list" directory="#arguments.start#" name="LOCAL.dirquery" type="dir" /><!---//GET QUERY OF DIRECTORIES IN START DIRECTORY--->
<cfcatch>
<cfsetting requesttimeout="600">
<cfdirectory action="list" directory="#arguments.start#" name="LOCAL.dirquery" /><!---//GET QUERY OF DIRECTORIES IN START DIRECTORY--->
</cfcatch>
This code does not work, neither does a conditional block. Im guessing the reader cannot parse thsi into whatever language and so is failing.
The code is probably failing at compile time, not at run time (need to see the error you are receiving to know for sure). If it's a compiler error, you'll need to have break out your CF7 functions in separate components from your CF8+ functions. Then check the CF version at run time to determine which component/function to call.
Here's some pseudo code you can use in application startup to load the correct version functions into memory. That way you have a consistent interface for calling the functions anywhere in your code.
Pseudo Code:
onApplicationStart(){
if (server.coldfusion.productversion == 7){
application.cffunctions = CF7FunctionsComponent;
} else {
application.cffunctions = CF8FunctionsComponent;
}
}
Everywhere else, you call the same function regardless of CF version currently running:
application.cffunctions.doSomething();

Undefined error when using CFThread in Custom Tags in ColdFusion 10

NOTE: I have completely rewritten this question to take into account new information. Please reread if you've already been through this one.
I'm getting errors when using a cfthread from within a custom tag in ColdFusion 10. In the Application log, I get the following entries:
Variable _cffunccfthread_cfThreadTag2ecfm16902001291 is undefined.
The name of the function it's returning as an error is _cffunccfthread_cf[Page Name Calling the CFThreadTag] then a number that doesn't change from request to request. I can duplicate this every time with the following code:
Application.cfc:
component
{
this.name = "CFThreadCustomTagTest";
}
ThePage.cfm:
<cfthread action="run" name="ThreadTestInPage">
<cflog log="Application" text="The thread in the page successfully ran" type="information" />
</cfthread>
<cf_ThreadTag />
ThreadTag.cfm:
<cfif thisTag.ExecutionMode EQ "start">
<cfthread action="run" name="ThreadTest">
<cflog log="Application" text="The thread within the tag successfully ran" type="information" />
</cfthread>
</cfif>
Just drop all three files in a directory in ColdFusion 10 and load ThePage.cfm. I get the following entries in the Application log:
"Severity","ThreadID","Date","Time","Application","Message"
"Information","ajp-bio-8012-exec-1","06/19/12","07:18:11",,"C:\ColdFusion10\cfusion\logs\application.log initialized"
"Information","cfthread-11","06/19/12","07:18:15","CFTHREADCUSTOMTAGTEST","The thread in the page successfully ran"
"Error","cfthread-9","06/19/12","07:18:15",,"THREADTEST: Variable _cffunccfthread_cfThreadTag2ecfm16902001291 is undefined. "
I also noticed that in the error for the thread within the custom tag, it does not contain the application name. While the log entry for the thread in the page does. Notice that the error line simply has ,, for the Application column of the log, while the successful thread has "CFTHREADCUSTOMTAGTEST".
If I change ThreadTag.cfm to wait for the thread within the tag to finish processing, then everything works fine, and I get the two entries in the log as I expect:
<cfif thisTag.ExecutionMode EQ "start">
<cfthread action="run" name="ThreadTest">
<cflog log="Application" text="The thread within the tag successfully ran" type="information" />
</cfthread>
<cfthread action="join" name="ThreadTest" timeout="10" />
<cfdump var="#cfthread#">
</cfif>
And to verify that there are no funky settings anywhere, here's my local development environment's settings summary.
So it seems that if I just throw the thread out there, and don't wait for it to finish, then the thread seems to be looking for something from the parent page that is no longer in memory. At least that's my completely unfounded guess :).
I've also filed a bug with Adobe. Bug number 3218452.
I understand your predicament, but there's no actual question here.
Basically you've found a bug in CF. A few people - myself included - can replicate it.
No-one's missing anything, except for the Adobe engineers who missed including this sort of thing in their regression testing when implementing CF10. That's no indictment of them, really, as I think this is reasonably edge case, perhaps?
The "variable" CF ain't finding is actually the name of the compiled class that the CF compiler makes when compiling the code. It looks like there's a compiler bug to me.
For example, my error is this:
THREADTEST: Variable _cffunccfthread_cfThreadTag2ecfm13713410591 is undefined.
However the compiled class is:
cfThreadTag2ecfm1371341059$func_CFFUNCCFTHREAD_CFTHREADTAG2ECFM13713410591.class
I don't know what part of the class name the variable is supposed to be named for, but I suspect it should be looking for func_CFFUNCCFTHREAD_CFTHREADTAG2ECFM13713410591. Or on the other hand the compiler should not be compiling the ThreadTag.cfm file as a FUNCTION? But I'm guessing... it might need to compile the thread code as a function so as to call it in a separate thread? That's a guess. Either way: it's compiling the code as one thing, and then looking for a different thing. because of a bug.
But bottom line here: yep, you've found a bug. And you've flagged it with Adobe. I dunno what else there is for you to do here?
So basically we now know one cannot have a <cfthread> call in a custom tag (I also tested via <cfmodule>: same problem). You're gonna have to write yer code a different way, using an include or a method or something. Less than ideal, I know, but what can you do?
Footnote:
I looked at the compiled code and it looks like the problem is CF is compiling the class with that func_ prefix, but it's referring to it in its code without. I didn't follow the code all the way to checking everything that's happening, but it looks to me like there are references to it trying to load _cffunccfthread_cfThreadTag2ecfm13713410591 rather than the correct name: func_CFFUNCCFTHREAD_CFTHREADTAG2ECFM13713410591.class.
Dan,
Please look on this page:
http://help.adobe.com/en_US/ColdFusion/10.0/Developing/WSc3ff6d0ea77859461172e0811cbec0b2e1-7ff0.html
And this section "Determining the tag execution mode"
And try the equivalent mode check code <cfswitch expression=#thisTag.ExecutionMode#>
To see if this causes a different compiler code gen. Maybe it will gen code a bit different and the variable will get generated.
Also, another test to try is to put you tag first in "ThePage.cfm" and see if that causes a different code gen as well.
Finally, are you on a newer JVM, as newer ones use the latest Fork/Join features of the Java concurrency approach such as Futures, etc. and they may be causing different code gen. Maybe set your JDK to an older one and see if the code gen is different.
No real answer, but some things to look at.
I ran into this as well.
My solution was to refactor and put the cfthread stuff into a .cfc and call that from the .cfm page.

Creating a new instance from within CFC

Now this would seem to be something very straight forward, but seemingly not so in ColdFusion. I need to create an instance of a CFC from within itself as in var a = new this() but this obviously does not work. The CFC name can't be used as it is a base that will be extended so I am attempting a hack around the issue with the following:
component {
public function subQuery (required string table) {
var classPath = getMetaData(this).fullname;
return createObject("component", classPath).init(table, this.dsn);
}
}
This would be acceptable but the class path returned from getMetaData(this).fullname is incorrect. The CFC is within a folder named with a hypen as in my-folder and the returned path looks like my.-folder.myCFC with a period inserted before the hyphen. Obviously I could manipulate this string with a Regex but that is just not a road I want to go down.
Hoping someone has a cleaner approach, thanks.
You should be able to do it without any context on the object name in theory, as it will be being executed from within itself and it should check its current directory.
The following should therefore do the job you need
var classPath = ListLast(getMetaData(this).fullname,'.');
return createObject("component", classPath).init(table, this.dsn);
This way it doesn't matter what the directory names are, and it will work on any objects that extend that one regardless of directory structure, or for a complete example
public function cloneMe() {
return CreateObject('component', ListLast(getMetaData(this).fullname,'.')).init(argumentCollection=arguments);
}
This way any arguments passed in will be passed through into the init. I.e. an extending CFC may redefine the method as the following (if you want errors when the init arguments aren't supplied)
public function cloneMe(required string table) {
return super.cloneMe(table=arguments.table,dsn=this.dsn);
}

In Coldfusion, how do I init a component that is located above the current path folder?

If I have a folder structure that looks like this:
/
/bin/myComponent.cfc
/reports/index.cfm
How do I initiate myComponent.cfc from index.cfm?
myService = createObject("component", "bin.myComponent");
Using the dot syntax, I know how to go to deeper folders, but how to do I go up a folder, and down into the other folder? Using slash syntax it would be something like this:
../bin/myComponent.cfc
But createObject() doesn't work that way. I'd like to keep a relative path so that I can move this folder to another server without breaking the paths.
Ideas? Thanks!
EDIT:
My example didn't display a deep enough folder structure for the creative answers that you all have provided. Here's what I should have done:
/[my project folder]/
/[my project folder]/bin/myComponent.cfc
/[my project folder]/reports/index.cfm
My basic question was if it was possible to go UP a directory when using createObject("component","dot path") from index.cfm to myComponent.cfc IF the name of [my project folder] is not static across all installs of the project.
If the answer is NO, then I'll just need to figure out what the best practice is, whether it's a mapping or an application setting.
We handle this using a mapping in the cf administrator. Usually all of the components go in one directory which is above the www root. In your case you could add a mapping to / which would allow you to do:
myService = createObject("component", "mymapping.bin.myComponent");
if you have the Application.cfc in the root of your folder structure, you could use something like this:
<cfset this.mappings["/local"] = getDirectoryFromPath(getCurrentTemplatePath()) />
and then access it through "local.bin.myComponent"
It is an end of the hard week, so pretty likely that following code can be enhanced somehow, but generally this approach should work:
<cfscript>
// this script is here http://XXXXXXX/test/paths/relative/reports/index.cfm
// component is here http://XXXXXXX/test/paths/relative/bin/myComponent.cfc
local = {};
// initialize with dynamic mapping
local.myComponentDynamic = createObject("component", "/bin/myComponent");
// grab the current directory name
local.parentPathExpanded = ExpandPath("../");
local.scriptPathExpanded = ExpandPath(cgi.SCRIPT_NAME);
local.thisDirectory = GetDirectoryFromPath(Replace(local.scriptPathExpanded, local.parentPathExpanded, ""));
// build base path
local.scriptPathDirectory = GetDirectoryFromPath(cgi.SCRIPT_NAME);
local.basePath = Replace(local.scriptPathDirectory, local.thisDirectory, "");
// this is relative path we already know
local.relativePath = "bin/myComponent";
// initialize with slash-syntax (path starting with /)
local.myComponentSlash = createObject("component", local.basePath & local.relativePath);
// convert path to the dot-syntax
local.dottedPath = Replace(local.basePath & local.relativePath, "/", ".", "ALL");
local.dottedPath = Right(local.dottedPath, Len(local.dottedPath)-1);
// initialize with dot-syntax path
local.myComponentDot = createObject("component", local.dottedPath);
</cfscript>
<cfdump var="#local#">
I've split the process into the separate variables and dumped the common container just to make it easy to read and understand this example.
But any way, if you can use dynamic mapping in Application.cfc -- use it.
EDIT: I've added such example, assuming you have following Application.cfc in the parent folder (e.g. "../Application.cfc" if looking from the index.cfm):
<cfcomponent output="false">
<cfset this.mappings["/bin"] = getDirectoryFromPath(getCurrentTemplatePath()) & "bin/" />
</cfcomponent>
My "paths-converting" example is just a fun trickery and playing with code which not really straightforward approach for good applications.
just use the full path from the root
<cfset obj = createObject("component", "bin.cart.item")>
where item.cfc in located in [website root]/lib/cart/ - this will work from anywhere in your code.
I had this same issue and this was my solution. It's pretty straight forward but it took a few hours for it to hit me. Hopefully this will save someone some time.
I started with
<bean id="ColdBooksConnectionService" class="myservice.model.service.ConnectionService" />
and always got the error that it wasn't available, so I wrote out the full path
<bean id="ColdBooksConnectionService" class="/CFIDE.administrator.myservice.model.service.ConnectionService" />
and the problem is solved.
Hope this helps.
Put the cfobject in an include.cfm file within the same directory as the .cfc, then you can call using a <cfinclude template="..\Whatever\include.cfm" />