We were experiencing this frequently (we do a rolling restart of all of our
web servers every night).

There is a little edge case bug in the autowire method of BeanInjector,
essentially the double check lock fails if there are near simultaneous 1st
requests for an object. The check is a pure variable name existence check,
and the key that it checks is actually created before the object has been
autowired.  So if there is a second request for an object before the first
request has finished autowiring, then that second request will succeed but
it won't have all of it's dependencies injected.

And if that request is for a transfer object it will be cached without it's
dependencies, so later objects will work but those first couple requested
objects that are cached will continue to fail until they fall out of the
cache.

We patched it locally and have been using the patched version in production
for a couple months now with no problems, and I believe that Brian is going
to roll the patch in soon.

If you can't wait I've attach the patch that we've been using.


On Sat, Mar 21, 2009 at 12:09 PM, Brian Kotek <[email protected]> wrote:

> Also, someone (I think it was Paul Marcotte) emailed me a patch for the
> observer that affected some edge cases involving simultaneous access to the
> injection logic, which I really need to incorporate and update the files
> (I'll actually commit to doing this today since I have some time and it's
> not a very major change).
>
>
>
> On Sat, Mar 21, 2009 at 3:07 PM, Brian Kotek <[email protected]> wrote:
>
>> Also, do you have something that is setting or clearing the variables
>> scope in your Decorator where your injected beans are stored, something like
>> <cfset variables.instance = StructNew() />?
>>
>>
>>
>> On Sat, Mar 21, 2009 at 12:15 PM, Bob Silverberg <
>> [email protected]> wrote:
>>
>>> What does your Coldspring config look like for Transfer and the
>>> BeanInjector, and what does your code that loads the CS bean factory look
>>> like?
>>>
>>>
>>> On Sat, Mar 21, 2009 at 11:55 AM, Brian G <[email protected]>wrote:
>>>
>>>>
>>>> A couple of weeks ago I released a big update to my app.  When I roll
>>>> out new versions and restart ColdFusion on my two web servers, I'm
>>>> seeing service layer objects which are injected via Brian K's
>>>> TDOBeanInjectorObserver "disappear" in some of the first Transfer
>>>> objects that are loaded.  My app has the following moving parts:
>>>>
>>>> * Transfer 1.1, Coldspring 1.2
>>>> * TDOBeanInjectorObserver in Coldspring
>>>> * All decorators extend AbstractBeanDecorator
>>>> * TransferSync for synchornizing my two node cluster
>>>>
>>>> My rollout process looks like:
>>>>
>>>> * Ant build script to check out and push code
>>>> * Restart ColdFusion
>>>>
>>>> When the server first comes up, it seems as though some Transfer
>>>> objects are not fully initialized and they're missing some of their
>>>> injected dependencies.  What's interesting is that the object makes it
>>>> into the Transfer cache so it continues to throw errors when a method
>>>> on the decorator is called that accesses the injected service object.
>>>> Here's one such error but it occurs on any variety of objects (user,
>>>> event, others):
>>>>
>>>> Expression: Element CLUBMEMBERSERVICE is undefined in VARIABLES.
>>>>
>>>>    * /var/www/pukka/api-prod/model/event/event.cfc (51, ??)
>>>>    * /var/www/pukka/api-prod/model/event/event.cfc (207,
>>>> CF_UDFMETHOD)
>>>>    * /var/www/pukka/msr-prod/views/registration/dsp.host.cfm (4,
>>>> CF_TEMPLATEPROXY)
>>>>    * /var/www/pukka/api-prod/ModelGlue/unity/view/ViewRenderer.cfm
>>>> (29, CFINCLUDE)
>>>>    * /var/www/pukka/api-prod/ModelGlue/unity/view/ViewRenderer.cfc
>>>> (50, CFMODULE)
>>>>    * /var/www/pukka/api-prod/ModelGlue/unity/framework/ModelGlue.cfc
>>>> (450, CF_TEMPLATEPROXY)
>>>>    * /var/www/pukka/api-prod/ModelGlue/unity/framework/ModelGlue.cfc
>>>> (340, CF_UDFMETHOD)
>>>>    * /var/www/pukka/api-prod/ModelGlue/unity/framework/ModelGlue.cfc
>>>> (289, CF_UDFMETHOD)
>>>>    * /var/www/pukka/api-prod/ModelGlue/unity/ModelGlue.cfm (126,
>>>> CF_TEMPLATEPROXY)
>>>>    * /var/www/pukka/msr-prod/index.cfm (82, CFINCLUDE)
>>>>
>>>> Line 207 basically calls getClubMemberService() and line 51 is
>>>> returning variables.ClubMemberService (which was supposed to be
>>>> injected by setClubMemberService()).
>>>>
>>>> My update a couple of weeks ago changed a lot of bits; I switched from
>>>> my own bean injector to Brian Kotek's version, I updated the version
>>>> of TransferSync, I changed my decorators to all extend an
>>>> AbstractBeanDecorator... and of course I didn't see these issues in my
>>>> development environment. :)
>>>>
>>>> I've been able to correct it by doing a soft restart on my app (which
>>>> clears the Transfer cache, restarts Coldspring and my Model-glue apps/
>>>> etc).  Sometimes it takes a couple of tries though to correct it.
>>>> Anyone seen this before?  Any thoughts about where to poke?
>>>>
>>>>
>>>> Brian, Mr. Edge Case
>>>>
>>>>
>>>>
>>>
>>>
>>> --
>>> Bob Silverberg
>>> www.silverwareconsulting.com
>>>
>>>
>>>
>
>
> >
>

--~--~---------~--~----~------------~-------~--~----~
Before posting questions to the group please read:
http://groups.google.com/group/transfer-dev/web/how-to-ask-support-questions-on-transfer

You received this message because you are subscribed to the Google Groups 
"transfer-dev" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to 
[email protected]
For more options, visit this group at 
http://groups.google.com/group/transfer-dev?hl=en
-~----------~----~----~----~------~----~------~--~---

### Eclipse Workspace Patch 1.0
#P colspringutils
Index: BeanInjector.cfc
===================================================================
--- BeanInjector.cfc    (revision 10)
+++ BeanInjector.cfc    (working copy)
@@ -1,5 +1,5 @@
-<!--- 
-LICENSE 
+<!---
+LICENSE
 Copyright 2008 Brian Kotek
 
    Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,61 +14,61 @@
    See the License for the specific language governing permissions and
    limitations under the License.
 
-File Name: 
+File Name:
 
        BeanInjector.cfc
-       
-Version: 1.0   
+
+Version: 1.0
 
-Description: 
+Description:
 
        This component will autowire any target component with matching beans 
from ColdSpring. It is useful for injecting beans into
-       "transient" or "per-request" components. It also has advantages over 
using ColdSpring to manage the non-Singleton objects: 
-       
+       "transient" or "per-request" components. It also has advantages over 
using ColdSpring to manage the non-Singleton objects:
+
        1. ColdSpring goes through several phases of lookup and resolution when 
beans are created. This adds performance overhead
        when managing non-Singleton beans. This BeanInjector avoids that 
overhead.
-       
+
        2. Since ColdSpring creates a fully-initialized component before it 
returns it to you, you have only limited control over
-       how that object is constructed (via the ColdSpring XML configuration). 
Using this BeanInjector allows you to create and call 
-       the init() method on your components, allowing you full control over 
how they are constructed. 
-       
+       how that object is constructed (via the ColdSpring XML configuration). 
Using this BeanInjector allows you to create and call
+       the init() method on your components, allowing you full control over 
how they are constructed.
+
        This makes it much easier to create "rich" objects that can handle much 
more business logic than objects that can't leverage
-       ColdSpring. The dependencies are cached by the BeanInjector for 
performance. After the first instance of object is created, all 
+       ColdSpring. The dependencies are cached by the BeanInjector for 
performance. After the first instance of object is created, all
        subsequent objects of that type will have their dependencies injected 
using cached information. The component is thread-safe.
 
 Usage:
 
        Usage of the BeanInjector is fairly straightforward. The ColdSpring XML 
file might look like this:
-               
+
                <bean id="userService" class="components.userService">
                        <property name="beanInjector">
                                <ref bean="beanInjector" />
                        </property>
                </bean>
-               
+
                <bean id="beanInjector" class="components.BeanInjector" />
-               
+
                <bean id="validatorFactory" class="components.ValidatorFactory" 
/>
-       
+
        Next, I would create public setter method(s) for the bean(s) you want 
to inject into my component, for example I might
        have a User.cfc with the following method:
-       
+
                <cffunction name="setValidatorFactory" access="public" 
returntype="void" output="false" hint="I set the ValidatorFactory.">
                        <cfargument name="validatorFactory" type="any" 
required="true" hint="ValidatorFactory" />
                        <cfset variables.instance.validatorFactory = 
arguments.validatorFactory />
                </cffunction>
-               
+
        To autowire a new User inside my UserService, I would simply create the 
User (either directly or with a factory) and then
        call autowire() on the BeanInjector:
-       
+
                <cfset var user = CreateObject('component', 
'components.User').init() />
-               <cfset variables.beanInjector.autowire(user) /> 
-       
-       That's it. The User object would now be autowired and have the 
ValidatorFactory injected into it. The end result is that 
-       any setters in your target object that have matching bean IDs in 
ColdSpring will have those beans injected automatically. 
-       As an additional example, a bean with an ID of "productService" would 
be autowired into a component that had a public setter 
+               <cfset variables.beanInjector.autowire(user) />
+
+       That's it. The User object would now be autowired and have the 
ValidatorFactory injected into it. The end result is that
+       any setters in your target object that have matching bean IDs in 
ColdSpring will have those beans injected automatically.
+       As an additional example, a bean with an ID of "productService" would 
be autowired into a component that had a public setter
        method named setProductService(), and so on.
-       
+
        There is an optional constructor argument called "suffixList" that can 
be supplied. This is a comma-delimited list
        of propery name suffixes that will be allowed. If you specify a 
suffixList, the Observer will only inject beans which
        end in one of the suffixes in the list. For example, if you specify a 
suffixList of "service", setter methods for
@@ -76,95 +76,99 @@
        would NOT be called. This can be useful in rare situations where your 
Transfer Object may have database-driven properties
        that conflict with the names of ColdSpring beans. Most people probably 
won't need to worry about this, but the option
        is here in case the issue arises.
-       
+
        In case you have problems determining whether beans are being properly 
injected into your Decorators, there is
        an optional init() method argument called "debugMode". By default, this 
is false. If you set it to true via the ColdSpring
        XML config file, the component will trace successful dependency 
injections to the debugging output. It will also
        rethrow any errors that occur while trying to inject beans into your 
Decorators. Obviously, ensure that this is
        remains off in production.
-       
-       The autowire() method also has two optional arguments that can be used 
for small performance increases: 
-       
-       The first optional argument is "targetComponentTypeName". If the 
calling code already knows the full type name of the target 
-       component, you can pass this string into the autowire() method to avoid 
the need to look up the type name in the component 
-       metadata. For example, Transfer ORM Decorators already know their type, 
so if you are autowiring a Decorator, you can pass in 
+
+       The autowire() method also has two optional arguments that can be used 
for small performance increases:
+
+       The first optional argument is "targetComponentTypeName". If the 
calling code already knows the full type name of the target
+       component, you can pass this string into the autowire() method to avoid 
the need to look up the type name in the component
+       metadata. For example, Transfer ORM Decorators already know their type, 
so if you are autowiring a Decorator, you can pass in
        the type name. The performance differnce is small, but every little bit 
helps so I made this an option. In most cases, the
        type won't be known by the calling code, so this argument won't be used.
-       
+
        The second optional argument is "stopRecursionAt". This is the full 
type name of a superclass of the target component at which
        you want dependency resolution lookup to stop. For example, Transfer 
ORM Decorators inherit from the "transfer.com.TransferDecorator"
        class. However, when autowiring Transfer Decorators, you don't want to 
waste time trying to resolve dependencies at that level,
        because from that parent class upwards, everything is managed by 
Transfer. There are no custom properties for you to define at
        that level. Looking for properties to autowire in those parent classes 
would usually not cause any errors, but it is wasted time.
-       By specifying a type name to stop the lookup recursion, you can save 
some processing time and avoid looking at unnecessary 
+       By specifying a type name to stop the lookup recursion, you can save 
some processing time and avoid looking at unnecessary
        parent classes. Unless the target component is part of some greater 
framework, such as Transfer, using this optional argument
        will usually be unnecessary.
-       
+
 --->
 
 <cfcomponent name="BeanInjector" hint="">
-       
+
        <cffunction name="init" access="public" returntype="any" 
hint="Constructor.">
                <cfargument name="suffixList" type="string" required="false" 
default="" />
                <cfargument name="debugMode" type="boolean" required="false" 
default="false" />
                <cfset variables.DICache = StructNew() />
                <cfset variables.debugMode = arguments.debugMode />
                <cfset variables.suffixList = arguments.suffixList />
+    <cfset variables.loadedKey = createUUID()>
                <cfreturn this />
        </cffunction>
-       
+
        <cffunction name="autowire" access="public" returntype="any" 
output="false" hint="">
                <cfargument name="targetComponent" type="any" required="true" />
                <cfargument name="targetComponentTypeName" type="any" 
required="false" default="" hint="If the calling code already knows the type 
name of the target component, passing this will provide a small speed 
improvment because the type doesn't have to be looked up in the component 
metadata." />
                <cfargument name="stopRecursionAt" type="string" 
required="false" default="" hint="When recursing the parent classes of the 
target component, recusion will stop when it reaches this class name." />
                <cfset var local = StructNew() />
-               
+
                <cfif not Len(arguments.targetComponentTypeName)>
                        <cfset local.typeName = 
GetMetaData(arguments.targetComponent).name />
                <cfelse>
                        <cfset local.typeName = 
arguments.targetComponentTypeName />
                </cfif>
-               
+
                <!--- If the DI resolution has already been cached, inject from 
the cache. --->
-               <cfif StructKeyExists(variables.DICache, local.typeName)>
+    <cfif StructKeyExists(variables.DICache, local.typeName) and 
StructKeyExists(variables.DICache[local.typeName],variables.loadedKey)>
                        <cfset injectCachedBeans(arguments.targetComponent, 
local.typeName) />
                <cfelse>
-               
+
                        <!--- Double-checked lock based on Object Type Name to 
handle race conditions. --->
                        <cflock 
name="Lock_BeanInjector_Exclusive_#local.typeName#" type="exclusive" 
timeout="5" throwontimeout="true">
-                               
-                               <cfif StructKeyExists(variables.DICache, 
local.typeName)>
+
+        <cfif StructKeyExists(variables.DICache, local.typeName) and 
StructKeyExists(variables.DICache[local.typeName],variables.loadedKey)>
                                        <cfset 
injectCachedBeans(arguments.targetComponent, local.typeName) />
-                               <cfelse>        
+                               <cfelse>
                                        <!--- Create a new cache element for 
this component. --->
                                        <cfset 
variables.DICache[local.typeName] = StructNew() />
-                                       
+
                                        <!--- Get the metadata for the 
component. --->
                                        <cfset local.objMetaData = 
GetMetaData(arguments.targetComponent) />
-                               
+
                                <!--- Recurse the inheritance tree of the 
component and attempt to resolve dependencies. --->
                                <cfset 
performDIRecursion(arguments.targetComponent, local.objMetaData, 
local.typeName, arguments.stopRecursionAt) />
+            <cfset variables.DICache[local.typeName][variables.loadedKey]=true 
/>
                                </cfif>
-                               
+
                        </cflock>
-                       
+
                </cfif>
-               
+
                <cfreturn arguments.targetComponent />
        </cffunction>
-       
+
        <cffunction name="injectCachedBeans" access="private" returntype="void" 
output="false" hint="">
                <cfargument name="targetComponent" type="any" required="true" />
                <cfargument name="typeName" type="string" required="true" />
                <cfset var thisProperty = "" />
                <cfif StructCount(variables.DICache[arguments.typeName]) gt 0>
                        <cfloop 
collection="#variables.DICache[arguments.typeName]#" item="thisProperty">
-                               <cfset injectBean(arguments.targetComponent, 
thisProperty, variables.DICache[arguments.typeName][thisProperty]) />
-                               <cfif variables.debugMode><cftrace text="The 
cached dependency #thisProperty# was successfully injected into 
#arguments.typeName#." inline="false"></cfif>
+                               <cfif thisProperty neq variables.loadedKey>
+                                       <cfset 
injectBean(arguments.targetComponent, thisProperty, 
variables.DICache[arguments.typeName][thisProperty]) />
+                                       <cfif variables.debugMode><cftrace 
text="The cached dependency #thisProperty# was successfully injected into 
#arguments.typeName#." inline="false"></cfif>
+                               </cfif>
                        </cfloop>
                </cfif>
        </cffunction>
-       
+
        <cffunction name="injectBean" access="private" returntype="void" 
output="false" hint="">
                <cfargument name="targetObject" type="any" required="true" />
                <cfargument name="propertyName" type="string" required="true" />
@@ -173,7 +177,7 @@
                        <cfinvokeargument name="#arguments.propertyName#" 
value="#arguments.propertyValue#" />
                </cfinvoke>
        </cffunction>
-       
+
        <cffunction name="performDIRecursion" access="private" 
returntype="void" output="false" hint="">
                <cfargument name="targetObject" type="any" required="true" />
                <cfargument name="metaData" type="struct" required="true" />
@@ -183,20 +187,20 @@
                <cfset var propertyName = "" />
                <cfset var thisSuffix = "" />
                <cfset var suffixMatch = true />
-               
+
                <!--- If the metadata element has functions, attempt to resolve 
dependencies. --->
                <cfif StructKeyExists(arguments.metadata, 'functions')>
                        <cfloop from="1" 
to="#ArrayLen(arguments.metaData.functions)#" index="thisFunction">
-                               <cfif 
Left(arguments.metaData.functions[thisFunction].name, 3) eq "set" 
+                               <cfif 
Left(arguments.metaData.functions[thisFunction].name, 3) eq "set"
                                                and 
Len(arguments.metaData.functions[thisFunction].name) gt 3
-                                               and (not 
StructKeyExists(arguments.metaData.functions[thisFunction], 'access') 
-                                                        or 
+                                               and (not 
StructKeyExists(arguments.metaData.functions[thisFunction], 'access')
+                                                        or
                                                         
arguments.metaData.functions[thisFunction].access eq 'public')>
                                        <cfset propertyName = 
Right(arguments.metaData.functions[thisFunction].name, 
Len(arguments.metaData.functions[thisFunction].name)-3) />
                                        <cftry>
-                                               
+
                                                <cfif 
getBeanFactory().containsBean(propertyName)>
-                                                       
+
                                                        <!--- If a suffix List 
is defined, confirm that the property has the proper suffix. --->
                                                        <cfif 
Len(variables.suffixList)>
                                                                <cfset 
suffixMatch = false />
@@ -207,44 +211,44 @@
                                                                        </cfif>
                                                                </cfloop>
                                                        </cfif>
-                                                       
+
                                                        <cfif suffixMatch>
-                                                       
+
                                                                <!--- Try to 
call the setter. --->
                                                                <cfset 
injectBean(arguments.targetObject, propertyName, 
getBeanFactory().getBean(propertyName)) />
-                                                               
-                                                               <!--- If the 
set was successful, add a cache reference to the bean for the current TDO 
property. --->                                           
+
+                                                               <!--- If the 
set was successful, add a cache reference to the bean for the current TDO 
property. --->
                                                                <cfset 
variables.DICache[arguments.originalTypeName][propertyName] = 
getBeanFactory().getBean(propertyName) />
-                                                               
+
                                                                <cfif 
variables.debugMode><cftrace text="The dependency #propertyName# was 
successfully injected into #arguments.originalTypeName# and cached." 
inline="false"></cfif>
-                                                       
+
                                                        </cfif>
-                                                               
+
                                                </cfif>
-                                               
+
                                                <cfcatch type="any">
                                                        <!--- Bean injection 
failed. --->
                                                        <cfif 
variables.debugMode><cfrethrow /></cfif>
                                                </cfcatch>
-                                               
+
                                        </cftry>
                                </cfif>
                        </cfloop>
                </cfif>
-               
-               <!--- If the metadata element extends another component, 
recurse that component. --->           
+
+               <!--- If the metadata element extends another component, 
recurse that component. --->
                <cfif StructKeyExists(arguments.metadata, 'extends')>
                        <cfif not Len(arguments.stopRecursionAt) or 
(Len(arguments.stopRecursionAt) and arguments.metadata.extends.name neq 
arguments.stopRecursionAt)>
                                <cfset 
performDIRecursion(arguments.targetObject, arguments.metaData.extends, 
arguments.originalTypeName, arguments.stopRecursionAt) />
                        </cfif>
                </cfif>
        </cffunction>
-       
+
        <!--- Dependency injection methods for Bean Factory. --->
        <cffunction name="getBeanFactory" access="public" returntype="any" 
output="false" hint="I return the BeanFactory.">
                <cfreturn variables.instance.beanFactory />
        </cffunction>
-               
+
        <cffunction name="setBeanFactory" access="public" returntype="void" 
output="false" hint="I set the BeanFactory.">
                <cfargument name="beanFactory" 
type="coldspring.beans.BeanFactory" required="true" />
                <cfset variables.instance.beanFactory = arguments.beanFactory />

Reply via email to