I'm using <cfproperty /> to make use of implicit getters and setters in ColdFusion (Railo).
However, for more complex values like structs and arrays, how can I append to these?
<cfproperty name="settings" type="struct" />
How can I append an item into the property called settings? If I do the following:
<cfset setSettings(structAppend(getSettings(), { "hello" = "world" })) />
I get the following error:
java.lang.NullPointerException
Am I missing something here? I'm new to the cfproperty tag and thought it would be a time saver, but I can't figure this out.
Also, as a bonus how would I set a default value to these complex data types?
Thanks,
Mikey
Couple things here...
<cfset setSettings(structAppend(getSettings(), { "hello" = "world" })) />
Settings is a struct but structAppend() returns a boolean. Do your struct appending before this line. Second, structs are always passed by reference, meaning, if you do getSettings() you get a struct, which you can make changes to. Another call to getSettings() will return the same struct with the updated settings.
All you need is this:
<cfset structAppend(getSettings(), { "hello" = "world" }) />
One last thing. You could be getting a null pointer exception because getSettings() starts uninitialized. In your cfc, in the constructor area (after your properties), you should set an initial settings struct, like this:
<cfset setSettings({}) />
Related
I've never seen this before I have a component in a cfc file and I use
<cfset request = CreateObject("component","path/to/component") />
When I set request to the above or:
<cfset request = CreateObject("component","path/to/component").init() /> or
<cfset request = CreateObject("component","path/to/component").from_request() />
etc I always get a struct with a single item that is the equivilant of
{cfdumpinited = false}
I've never see this before. The from_request method reuturns init and init returns this.
When I <cfdump this> right before the <cfreturn this> I get the full object output on the screen. But when I <cfdump request> I get the struct stated above. Anyone know what causes Coldfusion to return this type of struct. I can post the entire cfc but I don't think that will help as I stated, right before the return I can output this and it is the entire object/component.
request is a scope in ColdFusion, therefore you should use another variable name.
I have found a strange bug in Coldfusion when adding two different components into some form of data structure and dumping them out.
For the purpose of this question I have simplified my code. I have Component A and Component B, both have one property, one constructor, and one function.
I create an instance of A, and an instance of B. When I dump the instances out separately I see the correct metadata.
However when I add the two instances to a data structure (Component B in first slot, A in second), it appears as if the methods from the instance in slot 1 are duplicated in the instance in slot 2, even though they are different objects. i.e. I see CompBFunction, GetCompBProp, SetCompBProp listed as methods for Comp A. The properties however look correct.
It is worth mentioning, that I cannot call the methods for Comp B on Comp A - This seems to be a display issue. I tried calling CompBFunction on my object 'a' and it failed as expected.
Any help here would be greatly appreciated. I understand it's not a huge issue - but it can be rather misleading to see these methods here when they don't exist. There are many instances in my code where I would want to build up a Data Structure of mixed components.
Apologies if this question has been asked before - I searched high and low, but could not find any info on it. I am using CF9.
Here is my example code:-
Component A
component name="CompA" output="false" cache="false" accessors="true" {
property name="CompAprop" type="Numeric" getter="true" setter="true";
public CompA function init() output="false" {
return this;
}
public String function CompAFunction() output="false" {
return "All";
}
}
Component B
component name="CompB" output="false" cache="false" accessors="true" {
property name="CompBprop" type="Numeric" getter="true" setter="true";
public CompB function init() output="false" {
return this;
}
public String function CompBFunction() output="false" {
return "All";
}
}
index.cfm
<cfset a = new CompA() />
<cfset b = new CompB() />
<cfset test = ArrayNew(1) />
<cfset test[1] = b />
<cfset test[2] = a />
<cfdump var="#test#" /> <!--- METHODS FOR B APPEAR IN METADATA FOR A --->
<cfdump var="#test[1]#" /> <!--- METADATA CORRECT --->
<cfdump var="#test[2]#" /> <!--- METADATA CORRECT --->
<cfabort />
Images
It works fine for me on CF 9.0.1 (update: and on CF10), and I have never encountered anything like the problem you're describing in the past. People would have raised this as a bug if it was an actual problem with ColdFusion, because it'd crop up fairly frequently, I reckon.
You have noticed that you're putting CompB into the array at index 1, and CompA into element 2, yeah? This conflicts with what you say in your question "However when I add the two instances to a data structure (Component A in first slot, B in second),". So one would expect them to be the "wrong" way round?
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
How Can I get the URL parameter & Value in Coldfusion?
for Ex:-
my URL is
test.cfm?par1=val1&par2=val2&par3=val3
Is it possible to get the second parameter and its value directly?
with <cfset param='#url.par2#'> I can get value of par2,
But my parameters are dynamicically generated from other page and passed to here (par2 may be next time abc2,xyz2 etc..)
So I think better way is to get the parameter and Value in 2nd Possition(Possition dont change always).
Any Idea How can I get it ?
Thanks in advance
You can also access the url scope as a struct, so you could get:
<cfset param2 = url['param2'] />
This is useful if you might have a naming convention for a bunch of fields. Say you're collecting names and emails like so:
email1=foo#bar.com&name1=Fred&email2=xxx#yyy.com&name2=Sally
You could write something like:
<cfloop condition="someCondition">
<cfset email = url['email' & i] />
<cfset name = url['name' & i] />
<!--- Do something --->
<cfset i++ />
</cfloop>
<cfset Param2 = ListGetAt(CGI.QUERY_STRING,2,"&")>
Order of query string variables is not relevant, or your app shouldnt expect it to be relevant. I think your best bet is to have another variable which is a list of the variables in the order. Like so:
test.cfm?par1=val1&par2=val2&par3=val3&list=var1,var2,var3
Notice the presence of the new variable "list".
So you first grab the value of "list" and then takes it 2nd entry "var2" and reference that in the URL scope. You could easily abstract all of this so the names of the variables themselves dont matter. Good error handling will be necessary to guard against missing expectations.
to get the list of params you can use structKeyList(url) or structKeyArray(url) then access those parameters through the url scope like #url['par1']#
<cfset params = structKeyList(url) />
<cfdump label="parameters" var="#params#" />
<cfloop index="ix" list="#params#">
<cfoutput><div>#ix# = #url[ix]#</div></cfoutput>
</cfloop>
as others have mentioned, you really shouldn't rely on the order of parameters
<cfscript>
par2=getToken(cgi.query_string,2,"&"); // contains "par2=val2"
par2name=getToken(par2,1,"="); // contains "par2"
par2value=urlDecode(getToken(par2,2,"=")); // contains "val2"
</cfscript>
You could also use the listGetAt function, which is basically equivalent to getToken, with slightly different syntax.
By default, ColdFusion passes simple types (like numeric, string, and GUID) by value to functions. I'd like to pass a simple type by reference.
I'm currently wrapping a simple value in a struct (they get passed by reference). This solves my problem but it is very ugly:
<!--- TheFunctionName---->
<cffunction name="TheFunctionName">
<cfargument name="OutVariable" type="struct">
<cfset OutVariable.ID = 5>
</cffunction>
<cfset OutVariable=StructNew()>
<cfset TheFunctionName(OutVariable)>
<!--- I want this to output 5--->
<cfoutput>#OutVariable.ID#</cfoutput>
I'd rather something like this:
<!--- TheFunctionName---->
<cffunction name="TheFunctionName">
<cfargument name="OutVariable" passbyref="true">
<cfset OutVariable = 5>
</cffunction>
<cfset TheFunctionName(OutVariable)>
<!--- I want this to output 5--->
<cfoutput>#OutVariable#</cfoutput>
AFAIK, there's no way to pass simple values by reference in ColdFusion. The only workaround I can think of is the one you're already using.
Instead, I would suggest trying to restructure your program to work with the grain of the language. In cases where there's only one simple value to "modify", you could just make your function return the new value, and call it like:
<cfset SomeVar = TheFunctionName(SomeVar)>
In cases where you're modifying multiple values, take a step back and think about whether it's possible to bundle those multiple values up into a CFC with your mutator functions becoming methods of the CFC. This could be clearer and more maintainable solution anyway.
You can arrange for the variables used outside and inside the function to be in a scope that exists in both code areas. For example, if you put a variable in the "session" or the "request" scope you will be able to access it from within the function. The changes made will persist.
Note that when you are doing this you aren't actually "passing" the variables to the function. The function just assumes the variable exists or creates it, depending on how you code it.
<cffunction name="TheFunctionName">
<cfset Request.StrVar = "inside function<br />" />
</cffunction>
<cfscript>
Request.StrVar = "outside function<br />";
WriteOutput(Request.StrVar);
TheFunctionName();
WriteOutput(Request.StrVar);
</cfscript>
About ColdFusion Scopes
If there is any doubt about the calling page declaring the variable in advance when it is required you'll have to do some legwork with the <cfparam> tag or IsDefined() function.
If you:
declare the function inside of a CFC
invoke the function using <cfinvoke>
You would be able to specify the <cfinvoke> parameter "returnvariable", and then output that variable however you like.
<cfinvoke component="this" method="TheFunctionName" returnvariable="blah">
<cfinvokeargument name="data" value="whatever" type="string">
<cfreturn data>
</cfinvoke>
<cfdump var="#blah#">
If you are writing everything in cfscript, then I would go with what SurroundedByFish said.