Objects and Composition in CFCs - Part 2: ColdSpring

Posted on Jun 05, 2007

My last article focused on the basics of developing composite objects in ColdFusion using CFCs. To be honest, that is the hard part. Now we get to the easy and fun parts, the first being ColdSpring integration. This is a pretty straightforward and simple process, especially when all you need to do is copy/paste code from my code generator, so let's get started (as before the code for this is attached).

Note: as a reminder, I will be covering these same topics in my upcoming CFFrameworks.com workshop. So sign up.Installing ColdSpring
Obviously, first you need to go download the ColdSpring Framework created by Dave Ross and Chris Scott. Unpackage the zip file and place the "coldspring" directory in the root of your site. Depending on your local development set up you may require a mapping to ensure that the framework can be found at "/coldspring", however in a typical multi-site setup just putting it under the root will work (and, yes, it works in shared hosting environments).

Configuring ColdSpring
ColdSpring is configured using XML (see the docs for the full XML specs). This is where my code generator can help. The default template generates a ColdSpring XML snippet with each set of components created.

To start, I generated the base XML against the "console" table and pasted that into a /config/services.xml file. You will see below that this already handles passing the consoleDAO and consleGateway to my service component as well as passing the DSN to both the gateway and DAO - more on that later.

<?xml version="1.0" encoding="UTF-8"?> <P>
<beans>    <bean id="consoleDAO" class="com.xbox.consoleDAO">       <constructor-arg name="dsn"><value>${dsn}</value></constructor-arg>    </bean>    <bean id="consoleGateway" class="com.xbox.consoleGateway">       <constructor-arg name="dsn"><value>${dsn}</value></constructor-arg>    </bean>    <bean id="consoleService" class="com.xbox.consoleService">       <constructor-arg name="consoleDAO">          <ref bean="consoleDAO"/>       </constructor-arg>       <constructor-arg name="consoleGateway">          <ref bean="consoleGateway"/>       </constructor-arg>    </bean> </beans>

Now, I would also like to pass my DAOs and Gateways for each of other objects (i.e. the games, controls and accessories) into the consoleService. So, I generate the XML for each of these items, copy paste the DAO and Gateway bean definitions into the XML and the contructor-args for the service and paste tham into my consoleService definition. When you are done, the XML should look like follows:

<?xml version="1.0" encoding="UTF-8"?> <P>
<beans>    <!-- console -->    <bean id="consoleDAO" class="com.xbox.consoleDAO">       <constructor-arg name="dsn"><value>${dsn}</value></constructor-arg>    </bean>    <bean id="consoleGateway" class="com.xbox.consoleGateway">       <constructor-arg name="dsn"><value>${dsn}</value></constructor-arg>    </bean>    <bean id="consoleService" class="com.xbox.consoleService">       <constructor-arg name="consoleDAO">          <ref bean="consoleDAO"/>       </constructor-arg>       <constructor-arg name="consoleGateway">          <ref bean="consoleGateway"/>       </constructor-arg>       <constructor-arg name="controlDAO">          <ref bean="controlDAO"/>       </constructor-arg>       <constructor-arg name="controlGateway">          <ref bean="controlGateway"/>       </constructor-arg>       <constructor-arg name="accessoryDAO">          <ref bean="accessoryDAO"/>       </constructor-arg>       <constructor-arg name="accessoryGateway">          <ref bean="accessoryGateway"/>       </constructor-arg>       <constructor-arg name="gameDAO">          <ref bean="gameDAO"/>       </constructor-arg>       <constructor-arg name="gameGateway">          <ref bean="gameGateway"/>       </constructor-arg>    </bean>    <!-- accessory -->    <bean id="accessoryDAO" class="com.xbox.accessories.accessoryDAO">       <constructor-arg name="dsn"><value>${dsn}</value></constructor-arg>    </bean>    <bean id="accessoryGateway" class="com.xbox.accessories.accessoryGateway">       <constructor-arg name="dsn"><value>${dsn}</value></constructor-arg>    </bean>    <!-- control -->    <bean id="controlDAO" class="com.xbox.controls.controlDAO">       <constructor-arg name="dsn"><value>${dsn}</value></constructor-arg>    </bean>    <bean id="controlGateway" class="com.xbox.controls.controlGateway">       <constructor-arg name="dsn"><value>${dsn}</value></constructor-arg>    </bean>    <!-- game -->    <bean id="gameDAO" class="com.xbox.games.gameDAO">       <constructor-arg name="dsn"><value>${dsn}</value></constructor-arg>    </bean>    <bean id="gameGateway" class="com.xbox.games.gameGateway">       <constructor-arg name="dsn"><value>${dsn}</value></constructor-arg>    </bean> </beans>

Initializing ColdSpring
I think before we get into the ColdSpring code required, it is important to note a few things. First, the power of ColdSpring doesn't easily come through on a small application with only a few dependencies involved, as in this case. It really shines as your application grows and your dependencies become more complex. Second, this could have been done using autowiring which would have required much less XML with only some slight modification to how we pass the dependencies into the service (i.e. using setter arguments instead of constructor arguments - check the docs for more information on this). Lastly, let's look at the code required to create my service previously:

<cfset dsn = "xbox" /> <cfset consoleDAO = createObject("component","com.xbox.consoleDAO").init(dsn) /> <cfset consoleGateway = createObject("component","com.xbox.consoleGateway").init(dsn) /> <cfset controlDAO = createObject("component","com.xbox.controls.controlDAO").init(dsn) /> <cfset controlGateway = createObject("component","com.xbox.controls.controlGateway").init(dsn) /> <cfset accessoryDAO = createObject("component","com.xbox.accessories.accessoryDAO").init(dsn) /> <cfset accessoryGateway = createObject("component","com.xbox.accessories.accessoryGateway").init(dsn) /> <cfset gameDAO = createObject("component","com.xbox.games.gameDAO").init(dsn) /> <cfset gameGateway = createObject("component","com.xbox.games.gameGateway").init(dsn) /> <cfset consoleService = createObject("component","com.xbox.consoleService").init(consoleDAO,consoleGateway,controlDAO,controlGateway,accessoryDAO,accessoryGateway,gameDAO,gameGateway) />

Now let's look at the new code:

<cfset props = structNew() /> <cfset props.dsn = "xbox" /> <cfset factory = createObject("component","coldspring.beans.DefaultXmlBeanFactory").init(defaultProperties=props)/> <cfset factory.loadBeansFromXmlFile(expandPath("/config/services.xml"),true)/> <cfset consoleService = factory.getBean("consoleService") />

To me, this looks so much easier to maintain. Let's examine what is going on here. First, remember the "$dsn" values in my XML above, well, ColdSpring allows us to define a default properties structure where it will pull values referenced in that manner. Thus, I am passing a simple structure with my DSN only in this case. Second, after creating the the ColdSping factory object, I pass it my XML config file path. Finally, I ask for the bean from the factory which is passed back to me with all the dependencies already in place. From this point on, my sample application looks and works exactly as before.

Conclusion
Hopefully this has served as a good example of getting started using ColdSpring, but I need to reinforce that I have only touched a portion of what ColdSpring can do once you delve into it - for instance, I have not shown autowiring, aspect-oriented programming (AOP), remote proxies and automatic CFC to ActionScript object conversion. Nonetheless, simply using the portion that I have shown, I think you can see how ColdSpring can make your applications more flexible and easier to maintain by simplifying and centralizing managing dependencies.

Download the attachment.

Comments

Tom Chiverton To see how ColdSpring can really start saving you time, I wrote a quick example of using 'auto wire' to inject dependant components with no configuration: http://rachaelandtom.info/node/1453

Posted By Tom Chiverton / Posted on 06/06/2007 at 7:19 AM


Paul Marcotte Hi Brian,

Both of the posts in this series got me thinking about object composition, so I modelled both your console and consoleService as domain and application models (respectively) in UML. You can check it out at: http://www.fancybread.com/blog/index.cfm/2007/6/5/Application-and-Domain-Models-Examples-of-Aggregate-and-Composite-Design

Posted By Paul Marcotte / Posted on 06/06/2007 at 1:37 PM


Tony Garcia Hi Brian,
I finally had time to sit and read this post as a follow up on your last post on composition. Very helpful and useful as always. Up next -- Transfer!
By the way, for a good intro to Coldspring's autowiring capabilities, I found this very good blog post by Nando: http://aria-media.com/blog/index.cfm/2006/9/22/The-Mystery-of-AutoWiring
(even though it refers to the Model Glue framework, I didn't find the explanation to only be applicable to MG).

Posted By Tony Garcia / Posted on 06/27/2007 at 3:24 AM


Alan Livie Just started using the cfc generator and it's helping a lot. Good tutorials too!

Question on Coldspring and your generated code.

I notice in the generated xxxxService.cfc that there is the line:

<cfset var XXXX = createObject("component","XXXX").init(argumentCollection=arguments) />

in the createXXXX() method

I take it that if using Coldspring (or any DI object factory) instead of creating an object directly within the CFC you would just use Coldspring to give it one via a setter method or as an argument in the init() method?

Alan Livie

Posted By Alan Livie / Posted on 07/10/2007 at 12:04 AM


Brian Rinaldi Alan, if I understand your question, then the answer is no, you would not use ColdSpring to manage this particular instance of object x. Although ColdSpring can handle this, it really isn't designed, in my understanding, for handling dependencies of "transient objects" which are usually objects that contain instance data and only exist for a short time (like the length of a request as may be in this case).

ColdSpring is generally used for handling service layer dependencies for service singletons (which in CF is an object that exists only once in memory - usually in Application scope though in CF there is no "true" concept of a singleton).

HTH or did I make things more confusing?

Posted By Brian Rinaldi / Posted on 07/10/2007 at 12:06 PM


Jose I had the same question (I am new to colspring as well) but in theory you can also build transient objects with coldspring and since to me the biggest advantage cs has is managing all of those object creation lines littered all over, would it be better practice to do it that way?

Posted By Jose / Posted on 10/20/2008 at 9:32 AM


Tony Garcia @Jose
Take a look at this page in the ColdSpring docs, which explains the singleton vs. transient object issue and why ColdSpring is really designed for singletons:

http://www.coldspringframework.org/coldspring/examples/quickstart/index.cfm?page=singletons

Posted By Tony Garcia / Posted on 10/20/2008 at 11:23 AM


Tony Garcia one more thing -- if you're interested in using ioc for transient objects, take a look at Peter Bell's LightWire framework which, as I understand it, was desingned for this use case.

http://lightwire.riaforge.org/

Posted By Tony Garcia / Posted on 10/20/2008 at 11:28 AM


Write your comment



(it will not be displayed)





About

My name is Brian Rinaldi and I am the Web Community Manager for Flash Platform at Adobe. I am a regular blogger, speaker and author. I also founded RIA Unleashed conference in Boston. The views expressed on this site are my own & not those of my employer.