Explicit getter & setter not accessible

34 views
Skip to first unread message

Charles Robertson

unread,
Sep 3, 2016, 4:59:40 AM9/3/16
to Railo
When I inject a CFC, using a explicit setter, in Lucee/Railo, the injected CFC seems to lose access to the injected CFC. I am using explicit setters & getters to resolve circular dependency.

The scenario, in which I lose access, is as follows:

a.cfc injected with b.cfc

I have another CFC, called c.cfc, which has an implicit getter & setter for setting & getting a.cfc. By the way, for implicit [accessors=true, cfproperty etc ] getters & setters, I set in the constructor.

I then call a method in c.cfc from a CF page template. Something, like c.HelloWorld()...

c.cfc then makes a method call like getA().Foo() from within HelloWorld()...

Inside the method a.Foo(), there is another call to getB().Bar()

Only in Railo/Lucee, do I get an error [ACF is fine]:

Component [conversionService] has no accessible Member with name [DATABASE2SERVICE]

Then a bit more detail:

string Component [conversionService] has no accessible Member with name [DATABASE2SERVICE] at lucee.runtime.ComponentScopeShadow.get(ComponentScopeShadow.java:127):127 at com.services.conversionservice_cfc$cf.udfCall1(C:\domains\establishmindfulness.com\wwwroot\com\services\conversionService.cfc:42):42 at ...

So, a recap:

CF page -> c.HelloWorld() [implicit A setter & getter] -> getA.Foo() [explicit B setter & getter] -> getB.Bar()

Now, the really bizarre thing, is that if I call a.Foo() from a CF page template, everything works fine. It is only when I go via an intermediary. [c.HelloWorld()]

One other piece of information:

The method call getA().Foo() from within c.HelloWorld() is made from inside a cfsavecontent tag.

I wonder whether 'cfsavecontent' is causing a problem, because I have other scenarios, just like this one, that work in Lucee/Railo [calling an injected CFC via an intermediary, but not from within cfsavecontent]


Message has been deleted

Charles Robertson

unread,
Sep 3, 2016, 8:08:36 AM9/3/16
to Railo
My CFCs are created via a service factory that loops through a list and instantiates each service.
It then calls setters for the circular dependents.

Below, is part of the application.cfc. the service factory component and three service components from this framework.

I am using accessors for non circular dependencies, and explicit getters & setters for circular dependencies.
Accessors create synthesized getters & setters, which reduces the amount of code. These are set in the constructor.
The explicit getters & setters are created the old fashioned way, but allow the CFC to initialize, before injection.

CFC mapping from previous post:

a.cfc = test1Service.cfc
b.cfc = test2Service.cfc
c.cfc = test3Service.cfc

Part of 'OnRequestStart' method of application.cfc


<cfcomponent displayname="application">

 
<cffunction name="OnRequestStart">
   
<cfargument name="TargetPage" type="string">
   
<cfset var local = {}>
   
<cfset request.servicelist = "dateService,translationService,test1Service,test2Service,test3Service">
   
<cfset request.daolist = "categorysectorDAO,venueDAO">
   
<cfset request.circulardependencies = StructNew()>
   
<cfset request.circulardependencies["test1Service"] = ["setTest2Service"]>
   
<cfset request.circulardependencies["test2Service"] = ["setTest1Service"]>
   
<cfset request.requestkey = structNew()>
   
<cfset request.domain_dsn = "dsn">
   
<cfset request.requestkey.domain_dsn = request.domain_dsn>
   
<cfif not StructKeyExists(APPLICATION,"servicefactory") or request.appreload>
     
<cflock name="servicefactory" type="exclusive" timeout="1000">
       
<cfset application.servicefactory = createobject("component","com.serviceFactory").init(request.servicelist,request.daolist,request.circulardependencies,request.requestkey)>
     
</cflock>
   
</cfif>
   
<cflock name="servicefactorylck" type="readonly" timeout="1000">
     
<cfset request.lckservicefactory = application.servicefactory>
   
</cflock>
   
<cfreturn true />
 
</cffunction>

</cfcomponent>



serviceFactory.cfc

<cfcomponent displayname="serviceFactory">

 
<cffunction name="init">
   
<cfargument name="servicelist" type="string">
   
<cfargument name="daolist" type="string">
   
<cfargument name="circulardependencies" type="struct">
   
<cfargument name="requestkey" type="struct">
   
<cfset var local  = StructNew()>
   
<cfloop list="#arguments.servicelist#" index="local.service">
     
<cfset this[local.service] = CreateObject("component","services.#local.service#").init(this,arguments.requestkey) />
   
</cfloop>
   
<cfloop list="#arguments.daolist#" index="local.dao">
     
<cfset this[local.dao] = CreateObject("component","services.dataAccess.#local.dao#").init(this,arguments.requestkey) />
   
</cfloop>
   
<cfif NOT StructIsEmpty(arguments.circulardependencies)>
     
<cfloop collection="#arguments.circulardependencies#" item="local.key">
       
<cfif IsArray(arguments.circulardependencies[local.key]) AND ArrayLen(arguments.circulardependencies[local.key])>
         
<cfloop from="1" to="#ArrayLen(arguments.circulardependencies[local.key])#" index="local.index">
           
<cfset local.object = local.key>
           
<cfset local.setter = arguments.circulardependencies[local.key][local.index]>
           
<cfif REFind("^set[A-Z]{1}[A-Za-z0-9_]+(Service|DAO)$",local.setter)>
             
<cfset local.argumentvalue = Mid(local.setter,4,Len(local.setter))>
             
<cfset local.temp = LCase(Left(local.argumentvalue,1)) & Right(local.argumentvalue,Len(local.argumentvalue)-1)>
             
<cfset local.argumentvalue = local.temp>
             
<cfinvoke component="#this[local.object]#" method="#local.setter#" service="#this[local.argumentvalue]#" />
           
</cfif>
         
</cfloop>
       
</cfif>
     
</cfloop>
   
</cfif>
 
<cfreturn this />
 
</cffunction>
 
</cfcomponent>



test1Service.cfc

<cfcomponent displayname="test1Service" accessors="true">
 
 
<cfproperty name="dateService" type="struct">
 
<cfproperty name="translationService" type="struct">

 
<cffunction name="init">
   
<cfargument name="service" type="struct">
   
<cfargument name="requestkey" type="struct">
   
<cfset variables.service = arguments.service>
   
<cfset variables.requestkey = arguments.requestkey>
   
<cfset setDateService(CreateObject("component","dateService").init(this,variables.requestkey))>
   
<cfset setTranslationService(CreateObject("component","translationService").init(this,variables.requestkey))>
 
<cfreturn this />
 
</cffunction>
 
 
<cffunction name="getTest2Service">
   
<cfreturn variables.test2Service />
 
</cffunction>
 
 
<cffunction name="setTest2Service">
   
<cfargument name="service" type="component">
   
<cfset variables.test2Service = arguments.service>
 
</cffunction>
 
 
<cffunction name="Foo">
   
<cfset var result = "foo">
 
<cfreturn result />
 
</cffunction>
 
 
<cffunction name="Bar">
   
<cfset var result = getTest2Service().Bar()>
 
<cfreturn result />
 
</cffunction>

</cfcomponent>



test2Service.cfc


<cfcomponent displayname="test2Service" accessors="true">

 
<cfproperty name="dateService" type="struct">
 
<cfproperty name="translationService" type="struct">

 
<cffunction name="init">
   
<cfargument name="service" type="struct">
   
<cfargument name="requestkey" type="struct">
   
<cfset variables.service = arguments.service>
   
<cfset variables.requestkey = arguments.requestkey>
   
<cfset setDateService(CreateObject("component","dateService").init(this,variables.requestkey))>
   
<cfset setTranslationService(CreateObject("component","translationService").init(this,variables.requestkey))>
 
<cfreturn this />
 
</cffunction>
 
 
<cffunction name="getTest1Service">
   
<cfreturn variables.test1Service />
 
</cffunction>
 
 
<cffunction name="setTest1Service">
   
<cfargument name="service" type="component">
   
<cfset variables.test1Service = arguments.service>
 
</cffunction>
 
 
<cffunction name="Foo">
   
<cfset var result = getTest1Service().Foo()>
 
<cfreturn result />
 
</cffunction>
 
 
<cffunction name="Bar">
   
<cfset var result = "bar">
 
<cfreturn result />
 
</cffunction>

</cfcomponent>



test3Service.cfc

<cfcomponent displayname="test3Service" accessors="true">

 
<cfproperty name="test1Service" type="struct">

 
<cffunction name="init">
   
<cfargument name="service" type="struct">
   
<cfargument name="requestkey" type="struct">
   
<cfset variables.service = arguments.service>
   
<cfset variables.requestkey = arguments.requestkey>
   
<cfset setTest1Service(CreateObject("component","test1Service").init(this,variables.requestkey))>
 
<cfreturn this />
 
</cffunction>
 
 
<cffunction name="Foo">
   
<cfset var result = "foo">
 
<cfreturn result />
 
</cffunction>
 
 
<cffunction name="Bar">
   
<cfset var result = getTest1Service().Bar()>
 
<cfreturn result />
 
</cffunction>

</cfcomponent>



test.cfm

<cfset foo = request.lckservicefactory.test1Service.Foo()>
<cfset bar = request.lckservicefactory.test2Service.Bar()>

#foo# #bar#

<cfset foo = request.lckservicefactory.test2Service.Foo()>
<cfset bar = request.lckservicefactory.test1Service.Bar()>

#foo# #bar#

<cfset foo = request.lckservicefactory.test3Service.Foo()>
<cfset bar = request.lckservicefactory.test3Service.Bar()>

#foo# #bar#



test.cfm output

foo bar

foo bar

The web site you are accessing has experienced an unexpected error.
Please contact the website administrator.

The following information is meant for the website developer for debugging purposes.
Error Occurred While Processing Request
Element TEST2SERVICE is undefined in VARIABLES.
 
The error occurred in C:/ColdFusion11/cfusion/wwwroot/establishmindfulness/com/services/test1Service.cfc: line 40
Called from C:/ColdFusion11/cfusion/wwwroot/establishmindfulness/com/services/test1Service.cfc: line 86
Called from C:/ColdFusion11/cfusion/wwwroot/establishmindfulness/com/services/test3Service.cfc: line 51
Called from C:/ColdFusion11/cfusion/wwwroot/establishmindfulness/test.cfm: line 2925

38 :  
39 :   <cffunction name="getTest2Service" access="public" returntype="any" output="false" hint="getter description: test2Service">
40 :     <cfreturn variables.test2Service />
41 :   </cffunction>
42 :  


Charles Robertson

unread,
Sep 3, 2016, 8:21:14 AM9/3/16
to Railo
UPDATE:

When I change the test1Service.getTest2Service() to:

<cffunction name="getTest2Service">
 
<cfif StructKeyExists(variables,"test2Service") AND IsObject(variables.test2Service)>
   
<cfreturn variables.test2Service />
 
<cfelse>
   
<cfset setTest2Service(CreateObject("component","test2Service").init(this,variables.requestkey)) />
   
<cfreturn variables.test2Service />
 
</cfif>
</cffunction>



The error dissappears and I get the final correct output:

foo bar

foo bar

foo bar

Summary:


But, this seems a little messay to me, to have to keep resetting the component in the getter.
Surely, the injection should persist for the applications lifetime, as my framework is saved into the application scope.
What, I don't understand, is why the injection works, when calling the component method directly, but does not persist, when I call via an intermediary.

Charles Robertson

unread,
Sep 6, 2016, 10:41:29 AM9/6/16
to Railo
If anyone is interested, I have resolved the problem.

After many days of thought about this issue, I suddenly had a 'eureka' moment.
By getting rid of all the implicit setters, and adding all my connecting services via explicit getters & setters, everything worked!

I reckon this is why implicit setters are not used, as much, to set components, because they need to be set inside the constructor.
This means that implicitly set components lose scope when accessing a component that uses an explict getter/setter.

Now, I understand why MURA CMS has so many getter & setters just below its cfc constructor code.
Reply all
Reply to author
Forward
0 new messages