Lightwire import node support

September 26, 2009

Lightwire is a Dependancy Injection framework written by Peter Bell for injecting dependancies into singletons and transient business objects. You can configure into using XML or programitically. I like defining my beans in XML, however I like being able to split my beans up into seperate configuration XML files.

For example, one config file might be for Transient Beans, another for Service Beans etc, alternatively you might want to define beans by package. Anyone who has used ColdSpring will be familiar with the import node, which allows you to do this. As Lightwire can read ColdSpring config files I decided to add support for the import node.

I added a new method to Lightwire's BaseConfigObject.cfc called getImportedBeans, and here it is.


<cffunction name="getImportedBeans" returntype="array" hint="I return an array of imported beans." output="false">
<cfargument name="XmlBeanConfig" required="true" type="xml" hint="The XML config (as XML).">
<!--- use XMLSearch to find any imports --->
<cfset var importedBeanDefinitions = XMLSearch(arguments.XmlBeanConfig,"/beans/import") />
<cfset var importedBeans = ArrayNew(1) />
<cfset var i = 0 />
<cfset var importfilepath = "" />
<cfset var filecontents = "" />
<cfset var beans = "" />

<cfloop from="1" to="#ArrayLen(importedBeanDefinitions)#" index="i">

<cfset importfilepath = ExpandPath(importedBeanDefinitions[i].XmlAttributes.resource) />
<cfif FileExists(importfilepath)>
<cffile action="read"
file="#importfilepath#"
variable="filecontents"/>


<cfif IsXml(filecontents)>
<!--- append imported beans to our existing bean array --->
<cfset beans = XmlSearch(XMLParse(filecontents), '/beans/bean') />
<!--- merge the two arrays --->
<cfset importedBeans.addAll( beans ) />
<cfelse>
<cfthrow type="BaseConfigObject.getImportedBeans"
message="Imported bean definition is not valid XML"
detail="File '#importfilepath#' is not valid XML" />

</cfif>
<cfelse>
<cfthrow type="BaseConfigObject.getImportedBeans"
message="Import bean definition not found"
detail="File '#importfilepath#' could not be found" />

</cfif>

</cfloop>
<cfreturn importedBeans />
</cffunction>

Next I simply added a line to the parseXMLConfigFile method:


beans.addAll(getImportedBeans(xml));

So parseXMLConfigFile() now looks like:


<cffunction name="parseXMLConfigFile" returntype="void" hint="I take the path to a ColdSpring XML config file and use it to set all of the necessary LightWire config properties." output="false">
<cfargument name="XMLFilePath" required="true" type="string" hint="The path to the XML config file.">
<cfargument name="properties" required="false" type="struct" hint="A struct of default properties to be used in place of ${key} in XML config file.">

<cfscript>
var i = 0;
// parse coldspring xml bean config
var xml = XMLParse(arguments.XMLFilePath,false);

// use XMLSearch to create array of all bean defs
var beans = XMLSearch(xml,'/beans/bean');

// add any bean definitions in imported config files
beans.addAll(getImportedBeans(xml));

// loop over beans to create singleton or transient
For (i = 1; i lte ArrayLen(beans); i = i + 1)
{
if (structKeyExists(arguments,"properties"))
{
translateBean(beans[i],arguments.properties);
}
else
{
translateBean(beans[i]);
}
}
</cfscript>
</cffunction>

So now I can use a config file like this:


<?xml version="1.0" encoding="UTF-8"?>
<beans>
<import resource="/lightwire/LightWireXMLTest/DAOBeans.xml.cfm" />
<import resource="/lightwire/LightWireXMLTest/ServiceBeans.xml.cfm" />
<import resource="/lightwire/LightWireXMLTest/TransientBeans.xml.cfm" />
</beans>

Thanks to Peter for letting me hack and post his code.


No comments

Leave a comment

If you found this post useful, interesting or just plain wrong, let me know - I like feedback :)

Please note: If you haven't commented before, then your comments will be moderated before they are displayed.