injecting services into Records

1 view
Skip to first unread message

Chris Blackwell

unread,
Dec 18, 2008, 3:19:03 PM12/18/08
to reacto...@googlegroups.com
I was reading through one of Sean Corfield's blog posts about using the TDOBeanInjectorObserver to inject a service into a transfer object and wondered how i might do this with reactor.  Here is his post http://blog.broadchoice.com/index.cfm/2008/8/15/Better-Living-Through-Transfer-and-ColdSpring

Anyway, after a bit of fiddling i think I've managed it using a bit of AOP, which tbh had always been a slight mystery to me, and probably still is!
I though i'd share this to see if anyone else is using anything like this, or incase someone sticks there hand up and says uhhmm no.. dont do that!

Here's what I've cobbled together.

Coldspring.xml

<beans default-autowire="byName">
   
    <bean id="reactorConfiguration" class="reactor.config.config">
    all the usual stuff here ...
    </bean>

    <bean id="reactorFactoryTarget" class="reactor.reactorFactory">
        <constructor-arg name="configuration">
            <ref bean="reactorConfiguration" />
        </constructor-arg>
    </bean>

    <bean id="reactorFactory" class="coldspring.aop.framework.ProxyFactoryBean">
        <property name="target">
            <ref bean="reactorFactoryTarget" />
        </property>
        <property name="interceptorNames">
            <list>
                <value>beanInjectorAdvisor</value>
            </list>
        </property>   
    </bean>

    <bean id="beanInjector" class="coldspring.utils.BeanInjector">
        <constructor-arg name="debugMode"><value>false</value></constructor-arg>
    </bean>   
       
    <bean id="beanInjectorAdvice" class="model.aspects.BeanInjectorAdvice" />   

    <bean id="beanInjectorAdvisor" class="coldspring.aop.support.NamedMethodPointcutAdvisor">
        <property name="advice">
            <ref bean="beanInjectorAdvice" />
        </property>
        <property name="mappedNames">
            <value>createRecord</value>
        </property>
    </bean>

    <bean id="EncryptionService" class="model.service.EncryptionService" />
   
</beans>

model/aspects/BeanInjectorAdvice.cfc

<cfcomponent name="BeanInjectorAdvice" extends="coldspring.aop.MethodInterceptor">

    <cffunction name="setBeanInjector" returntype="void" access="public" output="false">
        <cfargument name="beanInjector" type="any" required="true"/>
        <cfset variables.beanInjector = arguments.beanInjector />
    </cffunction>

    <cffunction name="invokeMethod" access="public" returntype="any">
        <cfargument name="methodInvocation" type="coldspring.aop.MethodInvocation" required="false" />
        <cfset var rtn = arguments.methodInvocation.proceed() />
        <cfset variables.beanInjector.autowire(rtn) />
        <cfreturn rtn />
    </cffunction>
   
</cfcomponent>

model/data/Record/UserRecord.cfc contains

    <cffunction name="setEncryptionService" access="public" output="false" returntype="void">
        <cfargument name="EncryptionService" />
        <cfset variables.beans.encryptionservice = arguments.encryptionservice />
    </cffunction>


so in my limited test (ie, press F5 - no error message) it seems to work... which is nice.

Chris

Chris Blackwell

unread,
Dec 30, 2008, 6:33:54 AM12/30/08
to reacto...@googlegroups.com
Well, just incase anyone else needs to do this i'll save you the time and embarrassment... none of the above is necessary.

If you manage your reactorFactory with coldspring your Records (and other reactor objects) have a _getBean() method which will allow you to access any other bean defined in coldspring.  No idea when that little gem snuck in there!

It seems that if any coldspring managed bean has a method "setBeanFactory" which has a single argument of type "coldspring.beans.BeanFactory" then coldspring will inject itself into the bean.  This isn't mentioned anywhere in the Coldspring docs (it is a Spring feature though). 

There was some chat on the old cs mailing list  about whether it was 'ok' for a manged bean to rely on the beanfactory to access other beans that it needed to do its job, or whether they were better provided as a property or constructor arg.  At the time it seemed the collective wisdom was against stuffing the beanfactory into a managed object, but coldspring has become "BeanFactoryAware" so i wonder if this has changed?

ahh well i learned something today, so i don't feel too bad and my previous post gave me a big insight into AOP :)

Chris

Dutch Rapley

unread,
Dec 30, 2008, 12:17:13 PM12/30/08
to reacto...@googlegroups.com
Ok, that's pretty awesome what you've done with AOP and the Bean Injector.

Just out of curiosity, would it be worth it to just add a directive in the CS config file for each table's createGateway and createRecord factory methods and using that to handle your autowiring. I know that it does make your CS file a little more verbose

    <bean id="UserRecord" factory-bean="ReactorFactory" factory-method="createRecord">
        <constructor-arg name="objectAlias">
            <value>User</value>
        </constructor-arg>
    </bean>


as long has you still have

   <cffunction name="setEncryptionService" access="public" output="false" returntype="void">
        <cfargument name="EncryptionService" />
        <cfset variables.beans.
encryptionservice = arguments.encryptionservice />
    </cffunction>
in your reactor "record's" cfc, it technically should autowire it.

The biggest difference is that instead of

reactor = beanFactory.getBean('ReactorFactory')
user = reactor.createRecord("User")

you can now do

user = beanFactory.getBean("UserRecord")

and if you're using ColdBox and have auto-wiring enabled in your handlers, you can add this to the top of your handler

<cfproperty name="UserRecord" type="ioc" scope="instance" />

and in an event, you can load a user as such

<cfset user = instance.UserRecord.load(userid = yourVariableHere)

-Dutch

Chris Blackwell

unread,
Dec 30, 2008, 12:46:50 PM12/30/08
to reacto...@googlegroups.com
Hi Dutch,

You can use the factory-method to get records, gateways etc but coldspring won't autowire the bean returned by the factory, at least not in my limited tests.  Is it your experience that it should, or are you just postulating?

Cheers, Chris


2008/12/30 Dutch Rapley <dutch....@gmail.com>

Dutch Rapley

unread,
Dec 30, 2008, 1:29:05 PM12/30/08
to reacto...@googlegroups.com
I'm postulating. And you're right, the auto-wiring doesn't work.

So I know you original question on the CS list is "Is it safe to use _getBean()?"

So you can still manually inject beans

        <property name="EncryptionService">
            <ref bean="EncryptionService"/>
        </property>

but you still have to write the getter and setters

By why go through this when you can simply do _getBean()? (But again, is it safe?)

I do admit that your way of adding injector's to createRecord() (for every table/object) with AOP is really cool, though it may be hard to follow for  n00b (like myself).

Personally, I don't think it's worth much time trying to optimize prematurely. I'd say to use _getBean() since it's there, and if/when you begin to have problems, then look to fall back on using AOP.

-Dutch

Dutch Rapley

unread,
Dec 30, 2008, 1:37:17 PM12/30/08
to reacto...@googlegroups.com
I will make one more comment. I think the Encryption component is actually a bad example for this.

In terms of reactor, if I wanted to add encryption, I would consider adding a RecordHelper.cfm file (populated with functions) to data/Record and include that in the appropriate xxxRecord.cfc files. For that type of functionality, or really antything that is specific to manipulating data for Reactor and the database, i wouldn't want to be handled by ColdSpring, that way the Reactor factory can be also be used away from ColdSpring when the case arises. (You're reducing your dependencies for Reactor).

-Dutch

On Tue, Dec 30, 2008 at 12:46 PM, Chris Blackwell <ch...@team193.com> wrote:

Chris Blackwell

unread,
Dec 30, 2008, 1:53:41 PM12/30/08
to reacto...@googlegroups.com
I'm not so much worried about it being safe, i'm sure i wouldn't run into any issues, it just seemed to contradict the wisdom of many of the cs team.

The encryption service is as good an example as any, the example bean i gave didn't have any of the contructor arguments that are actually passed in


    <bean id="EncryptionService" class="model.service.EncryptionService">
        <constructor-arg name="hashAlgorithm"><value>SHA-256</value></constructor-arg>
        <constructor-arg name="encryptionAlgorithm"><value>DESEDE</value></constructor-arg>
        <constructor-arg name="encryptionKey"><value>mysuperdoublesecretkey</value></constructor-arg>
    </bean>

I wouldn't want to hard code these values into a helper file.
I'm going to stick with BeanInjector & AOP for now, this will be a pretty big app when finished so i'll see how it goes.

Dutch Rapley

unread,
Dec 30, 2008, 2:03:47 PM12/30/08
to reacto...@googlegroups.com
cool! Yeah, you make a good point about the encryption service.

BTW, the _getBean() was a good find and thanks for explaining it. I saw it in the source and wondering what it was for.

Eric Knipp

unread,
Dec 30, 2008, 2:05:22 PM12/30/08
to reacto...@googlegroups.com
I am also very thankful for this find. Much appreciated sir.
Reply all
Reply to author
Forward
0 new messages