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).
Related
I am building a website where I have followed MVC to manage my code without using any frameworks. I have put all of my queries inside cfcs and am initializing them inside my Application.cfm, storing them in application variables like below:
<cfset aplication.customerProfileObject=
createObject("component","cfc.customerprofile").init()>
To perform any query operations, I have made a function and then call it anywhere like this:
<cfset selectedCustomerOb =
application.customerProfileObject.getContactCustomerProfileDetail(session.userid)>
I don't know what is causing the issue, but sometimes a user accesses another user's data. How is that possible? Is it assessing another user's session data or have I initialized the cfc wrong?
Application settings are below:
<cfapplication name="MyDataSourceName"
sessionmanagement="Yes"
setclientcookies="yes"
setdomaincookies="yes"
loginstorage="session"
sessiontimeout="#CreateTimeSpan(0, 2,0,0)#">
CustomerProfile.cfc
<cfcomponent>
<cffunction name="init">
<cfreturn this>
</cffunction>
<cffunction name="getContactCustomerProfileDetail" returntype="query"
description="Returns customer contact details by contactid"
access="public">
<cfargument name="ccId" type="numeric" required="yes">
<cfquery name="getContactCustomerProfileDetail"
datasource="#Application.ds#"
dbtype="ODBC"
username="#Application.UserName#"
password="#Application.Password#">
<!-------My query here--->
</cfquery>
<cfreturn getContactCustomerProfileDetail>
</cffunction>
</cfcomponent>
As Adam says you need to do this:-
<cffunction name="getContactCustomerProfileDetail" returntype="query"
description="Returns customer contact details by contactid"
access="public">
<cfargument name="ccId" type="numeric" required="yes">
<cfset var getContactCustomerProfileDetail = false>
<cfquery name="getContactCustomerProfileDetail"
datasource="#Application.ds#"
dbtype="ODBC"
username="#Application.UserName#"
password="#Application.Password#">
<!-------My query here--->
</cfquery>
<cfreturn getContactCustomerProfileDetail>
</cffunction>
The reason you are getting the problem is because your CFC instance is in a shared scope (application) and you have not var'd the query variable. This means that it is getting set into the variables scope of the CFC instance. Which means that multiple threads can overwrite this value. By just varring the variable as I have shown you make the variable local to the function and so each call to that function creates a localised and thus thread-safe variable.
Basically you should var all local variables in functions as a matter of habit. This code would never pass code review anywhere I have worked.
You're not actually including the relevant bit of the code to answer this... which would be the code within getCustomerProfileDetail().
However I would assume you don't have all your variables VARed in it, which means they go in the CFC's variables scope, which is shared with every user in the application.
But, as I say, you're not giving us the correct info to really answer this accurately. I suggest updating your question to include the relevant code.
I'm trying to refactor all of my CFCs to avoid using SESSION and APPLICATION variables (not an easy task).
However, in this application, SESSION variables are used in every database call, since different logged in users may be accessing different databases and schemas:
<cfquery name="qEmployees" datasource="#SESSION.DataSourceName#">
SELECT *
FROM #SESSION.DatabaseSchema#.Employees
</cfquery>
I don't want to go through the trouble of passing these two SESSION variables to every method call that accesses the database. This is especially the case since I don't want to pass DSNs and Schema Names in remote AJAX calls.
What is best practice for doing this - for all Scopes that shouldn't be used in CFCs?
I think that since the datasource truly is variable I'd pass it into every function as an optional parameter and set the default value to a variables scoped dsn attribute. I'd set the variables scoped DSN in the CFC's constructor. That way you only have to pass in the DSN for the AJAX calls.
<cffunction name="doFoo" access="remote"...>
<cfargument name="dsn" type="String" required="false" default="#variables.datasource#" />
</cffunction>
I'd use the session scope of your app to store the users dsn name and use that var to pass to the AJAX call.
You should create an "init" method that will serve as a constructor for your CFC. You can then instantiate the CFCs and store them in a shared scope, most likely the application scope. From here, to use this CFC via AJAX, I typically will create a remote facade. Basically this is another CFC that will directly access the CFC instance in the application scope. It will implement the methods you need to access via Ajax, expose them using access="remote" giving your application access to the access="public" methods from the actual CFC. In this case it is generally accepted that the remote facade can access the application scope directly as part of the design pattern.
A simple example:
example.cfc:
<cfcomponent output="false">
<cffunction name="init" access="public" output="false" returntype="any">
<cfargument name="dsn" type="string" required="true" />
<cfset variables.dsn = arguments.dsn />
<cfreturn this />
</cffunction>
<cffunction name="doStuff" access="public" output="false" returntype="query">
<cfset var q = "" />
<cfquery name="q" datasource="#variables.dsn#">
select stuff from tblStuff
</cfquery>
<cfreturn q />
</cffunction>
</cfcomponent>
In your Application.cfc onApplicationStart() method:
<cfset application.example = createObject("component","example").init(dsn = "somedsn") />
remote.cfc:
<cfcomponent output="false">
<cffunction name="doStuff" access="remote" returntype="query">
<cfreturn application.example.doStuff() />
</cffunction>
</cfcomponent>
Can you set your datasource variables in the onRequest or onRequestStart functions in your Application.cfc
<cffunction name="onSessionStart">
<cfset session.dsn = _users_personal_dsn_ />
</cffunction>
<cffunction name="onRequestStart" >
<cfset dsn = "#session.dsn#" />
</cffunction>
<cfquery name="qEmployees" datasource="#dsn#">
SELECT *
FROM #SESSION.DatabaseSchema#.Employees
</cfquery>
etc.
not sure if that will work [not tested - actually feels a bit sloppy]
-sean
The scope you choose (for any variation of this question, not just for DSNs) should be based on whether the lifetime of the value is the same as the lifetime of the scope.
In our application, the DSN is just set once in the lifetime of the application, so we have an application.config struct that gets created (parsed from a file) in onApplicationStart, and within it is application.config.dsn
If your value really does change between sessions, but not over the life of a session, go ahead and use the session scope.
If your value could change for any given request, but not in the middle of a request, put it in the request scope.
That said, still heed ryan's advice and add optional arguments that only default to this value: being flexible is always the best.
My suggestion for this is to create a base class and then have your components that need database access extend that component. It doesn't have to be in the immediate parent hierarchy but somewhere down the line.
They goal is to do two things, keep the cfc abstracted from the main program and keep it easily configurable. This accomplishes both.
So your CFC that queries the database would look something like this :
<cfcomponent extends="DataAccessBase">
<cffunction name="myFunction" access="public" returntype="string">
<cfquery datasource="#getDSN()#" name="qStuff">select * from table</cfquery>
</cffunction>
The key above is the extends="DataAccessBase" portion. This adds the layer of abstraction where you can control the data access at one configurable point, but it's not tied to the application itself, leaving the component abstracted from where it's implemented.
Your DataAccessBase.cfc could look something like this:
<cfcomponent>
<cffunction name="loadSettings">
<cfparam name="request.settings" default="#structNew()#">
<cfparam name="request.settigns.loaded" default="false">
<cfif request.settings.loaded eq false>
<!--- load settings from resource bundle etc --->
<cfset request.settings.dsn = 'myDSN'>
<cfset request.settings.loaded = true>
</cfif>
</cffunction>
<cffunction name="getDsn" access="public" returntype="string">
<cfset loadSettings()>
<cfreturn request.settings.dsn>
</cffunction>
You can of course get more intricate with how you configure and store the settings etc, but that's out of scope of the question I think. :)
I don't see any reason to pass the DSN with every method call. Yes, it works, but it's not necessary. The components are developed with a built-in assumption of the datastructure so you know that it is not going to change from a addItem() call to a updateItem() call, thus its duplication of work which means additional points of failure. :P
Make sense?
This is driving me crazy. We have tried to build a CF memcached wrapper. We have a memcached.cfc component like this:
<cfset this.m = arraynew(1)>
<cffunction name="init" access="public" output="false">
<cfif not isdefined("application.memcached")
....
<cfscript>
setup();
</cfscript>
...
<cfset application.memcached = this>
</cfif>
<cfreturn application.memcached>
</cffunction>
<cffunction name="setup" access="private" output="false">
<cftry>
<cfset this.m = arraynew(1)>
<cfloop from="1" to="#this.poolSize#" index="i">
<cfset this.m[i] = createClient()>
</cfloop>
<cflog application="no" file="memcached" text="Successfully set up #this.poolSize# new memcache clients">
<cfcatch>
<cflog application="no" file="memcached" text="Exception in setup() while setting up the pool: type: #cfcatch.type#, message: #cfcatch.message#, detail: #cfcatch.detail#">
</cfcatch>
</cftry>
</cffunction>
<cffunction name="createClient" access="private" output="false">
<cfset var AU = createObject("java", "net.spy.memcached.AddrUtil").init()>
<cfset var c = createObject("java", "net.spy.memcached.MemcachedClient").init(AU.getAddresses("127.0.0.1:11211"))>
<cfreturn c>
</cffunction>
<cffunction name="getCache" access="public" returntype="any" output="false">
<cfset idx = ceiling(rand() * 20)>
<cfreturn application.memcached.m[idx]>
</cffunction>
The weird thing is that after running for 30 minutes or so, the getCache starts failing says that there is no item in the application.memcached.m array at position idx.
How could this be happening? Are CF arrays using weak references or something? Surely once the array is populated with 20 clients the array should stay full always?
Each new client spawns a new thread so once we lose the reference to the client there is now way to shut it down and that thread lives there forever taking memory. Please, what am i missing?
Maybe your application scope gets deleted. You should specify a proper timeout for that. You should also log this whether OnApplication gets called or not for debugging purposes.
When does this memcached CFC get used? Every request? The lack of var scoping on the 'i' and 'idx' variables could well have something to do with it. Here's a couple of articles I wrote that do a simple demonstration of why this is essential.
http://duncan99.wordpress.com/2009/03/12/the-importance-of-var-scoping/
http://duncan99.wordpress.com/2009/03/16/the-importance-of-var-scoping-part-2/
The answer is probably something fairly simple - the fact that any instantiation of this class will immediately invalidate the array in the application scope makes this a fairly troublesome class to manage. In addition, while the spy memcached driver itself behaves as a singleton, and the array (java.util.vector) is thread-safe, nothing else about this implementation is - not the app scope, definitely not the cfc instance.
Assuming you can eliminate an out of bounds value for idx in the getCache() function (which I assume you already did, if not, throw a try/catch block in the function and report what idx is when the exception is thrown), you can remedy the symptoms without actually addressing the cause by using exclusive cflocks with the same name around the constructor and the getCache() method. This way even if you have random instantiations of your class, you won't ever be able to blow the array out while something is trying to access it, and you won't be able to attempt to access it while it's being re-created. This is not an optimal solution at all, and if you experience high concurrency you will notice a slight elevation in response times (fairly negligible, but depending on how many requests/sec you serve, it can definitely have an impact on max throughput.
Here is my coldfusion web service. When I use soapUI tool to call 'test', i am receiving this
"<ns1:stackTrace xmlns:ns1="http://xml.apache.org/axis/">org.xml.sax.SAXParseException: Premature end of file."
error.
can anyone help me on this issue? I already seached online, but no luck. any code issue?
do I need to use <cfproperty> tag?
<cfcomponent output="false">
<!--- initialisation --->
<cffunction
name="init"
output="false"
hint="return an initialized object.">
<!--- Return THIS reference. --->
<cfreturn THIS />
</cffunction>
<!--- ping --->
<cffunction
name="test"
access="remote"
returntype="numeric"
output="false"
hint="return an true = 0.">
<!--- declare local variables --->
<cfset var local = 0 />
<!--- Return 0. --->
<cfreturn local />
</cffunction>
</cfcomponent>
Couple questions: are you on CF9? What happens when you invoke it straight up (not through SoapUI, but as a http call in a browser)?
Also, I would strongly recommend against using "Local" as a variable name in a function - CF9 introduced some changes in how variables are scoped within a function and uses that as the name for that constrained scope.
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