I have a cfc that is a service. It only has functions. Up until now did not have any member variables.
login.cfc
function post(required string email, required string password) {
...
variables.password = arguments.password; // wish I didn't have to do this
var User = entityLoad("Users", {email : arguments.email}).filter(
function(item){
return item.validatePassword(variables.password);
});
variables.password = "";
...
I don't like that I have to set arguments.password to variables.password just so that the function inside of .filter can see it. Isn't there a cleaner way to do this?
In CF11 and newer, including Lucee 4/5, CFML closures can access variables in the parent scope (and up the stack). CF10 seems to have problems with this... but here's the code you can run in https://trycf.com to see how it works on each version of ColdFusion:
<cfscript>
function doFilter(term) {
var superheroes=[
{"name":"Iron Man","member":"Avengers"},
{"name":"Wonder Woman","member":"Justice League"},
{"name":"Hulk","member":"Avengers"},
{"name":"Thor","member":"Avengers"},
{"name":"Aquaman","member":"Justice League"}
];
var filtered=superheroes.filter(function(item){
return item.member==term;
});
writeDump(filtered);
}
doFilter("Avengers");
</cfscript>
So, in other words, you should have access to the arguments in the post() method if you're using CF11 or newer, or Lucee.
Related
I'm using Lucee CFML to create a website.
I have a global variable stored in application.cfc like this:
component {
application.globalquery;
}
On the second page of the website, I assign a value to that variable:
<cfscript>
myquery = ["object1", "object2", "object3", "object4", "object5"];
application.globalquery = myquery;
</cfscript>
On a third page, I dump the globalquery variable:
<cfscript>
dump(var="#application.globalquery#");
</cfscript>
But the result of this dump is string: empty
The expected behavior should be, that it contains the 5 objects of myquery.
Why isn't that the case?
You're resetting the value on every request.
By putting the definition at the root of the component:
component {
application.globalquery;
}
You're essentially telling CF to run that code on every request, like this:
component {
function onRequestStart() {
application.globalquery;
}
}
You need to define that variable only once, when the application starts, like this:
component {
function onApplicationStart() {
application.globalquery;
}
}
From then on, the value should only change when you tell it to change.
Here's more info on Application.cfc. There may be a few differences between Adobe CF and Lucee.
I am using a proxy/delegate pattern in a coldfusion component, and am getting unexpected results (from my point of view). Below is my proxy component - its pretty straight forward, I just init the CFC with the actual component I want to delegate to, and then map the named functions from that CFC through to a proxy function (the below is simplified for this example)
I have created a proxy component as follows:
component output="false"{
/** Constructor for proxy - requires an instance of myFusebox **/
public MyFuseboxProxy function init( Required any myFb ){
variables.myFusebox = arguments.myFb;
return this;
}
this.do = variables.proxy;
this.getApplication = variables.proxy;
this.getApplicationData = variables.proxy;
private any function proxy(){
var local.functionName = getFunctionCalledName();
var local.function = variables.myFusebox[local.functionName];
var local.returnVal = local.function( argumentCollection=arguments );
return local.returnVal;
}
}
From my application I call the following code:
variables.myFusebox = new ab.MyFuseboxProxy( variables.myFusebox );
variables.myFusebox.getApplicationData().startTime = now();
Now, in the above scenario, I would expect my proxy component to map the getApplicationData() function straight through to the original myFusebox component (via my proxy() function).
That function in the underlying component is as follows:
<cffunction name="getApplicationData" returntype="struct" access="public" output="false"
hint="I am a convenience method to return a reference to the application data cache.">
<cfreturn getApplication().getApplicationData() />
</cffunction>
That proxy all works fine, however, once I am in the above function in the original myFusebox I get the following error:
Message: Variable GETAPPLICATION is undefined.
StackTrace: coldfusion.runtime.UndefinedVariableException: Variable GETAPPLICATION is undefined.
And if I dump "this" inside that function, it actually dumps my proxy object.
Can anyone explain this or what I have done wrong? I was expecting that once the function call was inside the underlying object, it would just use its own context from there (my proxy just being a pass through really to the delegate)
I think this is the key point:
I was expecting that once the function call was inside the underlying
object
You've got this:
private any function proxy(){
var local.functionName = getFunctionCalledName();
var local.function = variables.myFusebox[local.functionName];
var local.returnVal = local.function( argumentCollection=arguments );
return local.returnVal;
}
When you do this bit:
var local.function = variables.myFusebox[local.functionName];
you are effectively pulling the function referenced by local.functionName out of variables.myFusebox, and putting it into the current function, within the context of your MyFuseboxProxy instance.
So when you do this:
var local.returnVal = local.function( argumentCollection=arguments );
You are not running variables.myFusebox[local.functionName]() (so in the context of variables.myFusebox), but you are running local.function() (so in the context of your proxy object).
I don't have the patience to try to follow your logic here, but I am still surprised you get that error. I would have expected this to happen:
local.function (a reference to getApplicationData from variables.myFusebox) runs getApplication().
getApplication() in the context of the MyFuseboxProxy instance should be a reference to variables.proxy().
variables.proxy() resolves the proxied function as getApplication(), and pulls that out of variables.myFusebox, and runs it in the context of your MyFuseboxProxy instance.
You do not include the code of the getApplication() function from variables.myFusebox, so I dunno what would happen next, but this is not what you want to be happening.
Anyway, the crux is - I think - that instead of running the functions inside variables.myFusebox, you're running them in your MyFuseboxProxy instance instead. If you want to do this sort of proxying (and ignoring for a moment you have invoke() specifically for doing this), you need to still call the function in its original context, not reference it in some new context.
I guess you're doing all this horsing around because ColdFusion doesn't like this syntax:
someObject[someMethodName]()
It baulks at the []() notation. However the solution is not this:
someOutOfContextReference = someObject[someMethodName]
result = someOutOfContextReference()
It's this:
someObject.someInContextReference = someObject[someMethodName]
result = someObject.someInContextReference()
See the subtle difference?
ColdFusion functions are not intrinsically closures, which is what you'd need them to be to work the way you want.
I am trying to work out the correct <cfscript> syntax for calling a dynamic method within ColdFusion 9. I have tried a number of variations and had a good search around.
<cfinvoke> is clearly the tag I want, sadly however I cannot use this within my pure cfscript component as it was implemented in ColdFusion 10.
i.e coldfusion 9 dynamically call method
I have tried the following within my CFC:
/** Validate the method name **/
var resources = getResources();
if (structKeyExists(variables.resources, name)) {
variables.resourceActive[name] = true;
var reflectionMethod = resources[name];
var result = "#reflectionMethod.getMethodName()#"(argumentCollection = params);
}
Where the return value of reflectionMethod.getMethodName() is the method name I want to call. It is 100% returning the correct value (the name of the method) where that method is correctly defined and accessible,
My error is a syntax error on that line.
You don't want to get the method name, you want to get the actual method, eg something like:
function getMethod(string method){
return variables[method];
}
The call that, thus:
theMethod = getMethod(variableHoldingMethodName);
result = theMethod();
Unfortunately one cannot simply do this:
result = getMethod(variableFoldingMethodName)();
Or:
result = myObject[variableFoldingMethodName]();
As the CF parser doesn't like the double-up of the parentheses or brackets.
The caveat with the method I suggested is that it pulls the method out of the CFC, so it will be running in the context of the calling code, not the CFC instance. Depending on the code in the method, this might or might not matter.
Another alternative is to inject a statically-named method INTO the object, eg:
dynamicName = "foo"; // for example
myObject.staticName = myObject[dynamicName];
result = myObject.staticName(); // is actually calling foo();
Assuming the method is in your current (variables) scope, you could try:
var result = variables[reflectionMethod.getMethodName()](argumentCollection = params);
I've inherited a project where there are a number of remote CFC's opened up for some Ajax requests and inside most methods in the CFC have the following:
<cfset var this.response = true />
Now I've never seen the var and this scope used together like this so I'm really not sure what to make of it so I guess my questions is:
Are there any issues with how this was coded? If so, are they major enough that I should put in
the effort to update all the CFC's to something like <cfset var
req.response = true />?
Here is a quick example of what I'm seeing:
<cfcomponent>
<cffunction name="check_foo" access="remote" returnformat="plain">
<cfargument
name = "isfoo"
type = "string"
required = "false"
default = "nope"
hint = "I check the string for foo"
/>
<cfscript>
/*setup new response*/
var this.response = false;
/*check for foo*/
if( !findnocase( "foo", arguments.isfoo ) ) {
/*no foo!*/
this.response = false;
}
return this.response;
</cfscript>
</cffunction>
</cfcomponent>
.
Updates:
Based on the feedback/answers below I've replace all instances of var this. Thanks again to everyone that helped out!
.
update: upon checking your dump, the "this" in var this is still this this scope, not local.this.
It is setting the response to the this scope, and it works in this case because because the CFC is instantiated every time it's being invoked remotely. However, it'd be best to rename this into something else to ensure thread-safety in case the method is invoked by other CFC as public method.
Using var this is the same as using this.
Dumping the local scope will include local variables as well as the Arguments and This scopes. (Can't find this documented; but I get this result in a bare cfc, and you got it in your screenshots.)
Because your function is access="remote" you'll be getting a new instance of the cfc on every call, and therefore a bare This scope. So those are "safe", but still a bad idea.
If there is any use of var this in non-remote functions then you will be getting undesired persistence and may suffer race conditions that result is invalid data.
Relevant CF documentation:
"Methods that are executed remotely through Flash Remoting and web services always create a new instance of the CFC before executing the method."
"Variable values in the This scope last as long as the CFC instance exists and, therefore, can persist between calls to methods of a CFC instance."
Quick background.
CFHTTP doesn't support Windows NTLM/Authenticate authentication, only basic authentication. I need to make http requests that will have to authenticate against NTLM, so I've ended up rolling my own version of CFHTTP.
I found Terry Ryan's article that uses the apache httpclient version 3.1 to perform digest authentication and have built upon that using version 4.1.2 instead which includes NTLM functionality.
I have a function that will perform a get request and then other functions to handle returning a structure that looks like the cfhttp result set. The changes I made are based on the authentication tutorial example.
public any function httpRequest(url,username,password,domain) {
var httpClient = createObject("java","org.apache.http.impl.client.DefaultHttpClient");
var authScope = createObject("java","org.apache.http.auth.AuthScope");
var httpCredentials = createObject("java","org.apache.http.auth.NTCredentials");
var httpGet = createObject("java","org.apache.http.client.methods.HttpGet");
var jURL = createObject("java", "java.net.URL").init(arguments.url);
var host = jURL.getHost();
var path = jURL.getPath();
var httpHostTarget = createObject("java","org.apache.http.HttpHost").init(host,80,"http");
var localContext = createObject("java","org.apache.http.protocol.BasicHttpContext");
var httpContent = {};
var response = '';
if (len(arguments.username) and len(arguments.password) gt 0){
httpCredentials.init(arguments.Username, arguments.password, cgi.remote_host,arguments.domain);
httpClient.getCredentialsProvider().setCredentials(authScope.ANY, httpCredentials);
}
if (!Len(path)) path = "/";
httpGet.init(path);
response = httpClient.execute(httpHostTarget, httpget, localContext);
httpContent = convertHttpClientResponseToCFHTTPFormat(response);
httpClient.getConnectionManager().shutdown();
return httpContent;
}
This was working fine until I altered the function to perform the authentication.
Unfortunately I'm now getting :
The execute method was not found.
Either there are no methods with the specified method name and argument types or the execute method is overloaded with argument types that ColdFusion cannot decipher reliably. ColdFusion found 2 methods that match the provided arguments. If this is a Java object and you verified that the method exists, use the javacast function to reduce ambiguity.
As far as I can tell there is only one matching execute() function in HttpClient for the object classes passed to it, so I'm a little confused. JavaCast doesn't allow you to cast to complex objects or super types, so that didn't work.
Can anyone suggest how I can get this to work? How can I reduce the ambiguity?
Looking at the error, it's getting confused between two execute methods that have the same number of parameters. Although I don't know why it is...
Anyway, I found a way around the error. It involves pulling the method you're after out of the class and invoking it directly. If ColdFusion was happier with casting Java objects life might be easier.
//response = httpClient.execute(httpHostTarget, httpget, localContext);
classes = [httpHostTarget.getClass(), CreateObject('java', 'org.apache.http.HttpRequest').getClass(), CreateObject('java', 'org.apache.http.protocol.HttpContext').getClass()];
method = httpClient.getClass().getMethod('execute', classes);
params = [httpHostTarget, httpget, localContext];
response = method.invoke(httpClient, params);
There may be another way of doing this (casting instead) but it's all I've got ;)
As a guess, could you be loading the wrong version of the .jars ? You don't seem to be using JavaLoader like Ryan did...