Integrating Mach-II with ColdSpring
Table of Contents
- What is ColdSpring
- What is a Mach-II Property
- What is the ColdspringProperty
- ColdspringProperty Usage and Configuration
- Parameter Descriptions
- Parent/Child Bean Factories Configuration for Use with Modules
- Using Mach-II Properties within ColdSpring
- Using Autowire by Depends Attribute
- Common Errors Using the ColdSpringProperty
- Additional Information and Considerations
- Closing Thoughts
What is ColdSpring
ColdSpring is dependency injection CFML framework based on the Spring framework for Java. In short, it makes your development life a lot easier by abstracting your object dependencies into an easy to read XML configuration file, rather than having those dependencies sprinkled throughout your application.
This wiki entry does not explain ColdSpring or detail how to use ColdSpring so please go read more about ColdSpring at http://www.coldspringframework.org. This wiki entry details how to setup ColdSpring integration with Mach-II.
What is a Mach-II Property
A property in Mach-II is a resource defined in the mach-ii.xml configuration file which is made available on an application wide level. These properties can be simple name/value pairs, and since Mach-II 1.5, xml described structs and arrays, and (most importantly) full CFCs ready for use throughout your application. Mach-II properties are instantiated during the initialization process of the application.
Example Properties:
<properties>
<property name="myImagePath" value="path/to/my/images" />
<property name="debugMode" value="true" />
<property name="coldspringProperty" type="MachII.properties.ColdspringProperty">
<parameters>
<parameter name="configFile" value="config/coldspring.xml" />
</parameters>
</property>
</properties>
Notice first that when defining CFC properties a "type" attribute is required which points to the location of the CFC file on disk. Notice also that input parameters may be specified in the mach-ii.xml file and passed into CFC properties at the time of their instantiation. Since CFC type properties often have a number of parameters that may be passed in, it can be much cleaner to simply include the property via an include statement, rather than within the main properties node of the mach-ii.xml configuration file. This would look like the following:
<includes> <include file="./coldspringProperty.xml" /> <include file="./loggingProperty.xml" /> <include file="./environmentProperty.xml" /> </includes>
Then, you would simply edit the configuration of the ColdspringProperty in its own coldspringProperty.xml file. We will look at all the details of configuring the ColdspringProperty later in this document.
What is the ColdspringProperty
Aside from being an awesome way to tie Mach-II and ColdSpring together, there is really nothing magical about the ColdspringProperty. It is a Mach-II property like any other - it just happens to be pre-built for you and distributed with the Mach-II framework to allow you to easily wire up your Mach-II application to work with ColdSpring. Using the ColdspringProperty is a matter of including the coldspringProperty.xml file from within your main mach-ii.xml config file, then configuring the parameters in the coldspringProperty.xml file at your leisure.
Let's now take a close look at the different options available to you when configuring the ColdspringProperty.
ColdspringProperty Usage and Configuration
<property name="coldSpringProperty" type="MachII.properties.ColdspringProperty"> <parameters> <!-- Mach-II property that reference ColdSpring beanFactory - Default: 'coldspring.beanfactory.root' --> <parameter name="beanFactoryPropertyName" value="serviceFactory"/> <!-- Path to the ColdSpring config file (required) --> <parameter name="configFile" value="/path/to/services.xml"/> <!-- Whether path is relative (mapped) or absolute - Default: FALSE --> <parameter name="configFilePathIsRelative" value="true"/> <!-- Whether to resolve dependencies for listeners/filters/plugins - Default: FALSE --> <parameter name="resolveMachIIDependencies" value="false"/> <!-- scope to pull in a parent bean factory into a child bean factory - Default: application --> <parameter name="parentBeanFactoryScope" value="application"/> <!-- key to pull in a parent bean factory from the application scope - Default: FALSE --> <parameter name="parentBeanFactoryKey" value="serviceFactory"/> <!-- Whether to place the bean factory in the application scope - Default: FALSE --> <parameter name="placeFactoryInApplicationScope" value="false" /> <!-- Whether to place the bean factory in the server scope - Default: FALSE --> <parameter name="placeFactoryInServerScope" value="false" /> <!-- Whether to automatically generate remote proxies for you - Default: FALSE --> <parameter name="generateRemoteProxies" value="true" /> <!-- Autowire attribute name to introspect in cfcomponent tags - Default: 'depends' --> <parameter name="autowireAttributeName" value="depends" /> <!-- Indicates where to write the temporary CFCs of the dynamic autowire method generation feature. Specify a path that can be expanded via expandPath(). Default: to current location of ColdspringProperty.cfc DO NOT DEFINE THESE PARAMETERS UNLESS YOU WANT TO OVERRIDE THE DEFAULT <parameter name="cfcGenerationLocation" value="PathThatCanBeExpanded" /> --> <!-- Indicates the dot path to where the temporary CFCs of the dynamic autowire method generation feature. DO NOT DEFINE THESE PARAMETERS UNLESS YOU WANT TO OVERRIDE THE DEFAULT <parameter name="dotPathToCfcGenerationLocation" value="DotPathToCFCGenerationLocation" /> --> <!-- Struct of bean names and corresponding Mach-II property names for injecting back into Mach-II Default: does nothing if struct is not defined --> <parameter name="beansToMachIIProperties"> <struct> <key name="ColdSpringBeanName1" value="MachIIPropertyName1" /> <key name="ColdSpringBeanName2" value="MachIIPropertyName2" /> </struct> </parameter> </parameters> </property>
Parameter Descriptions
beanFactoryPropertyName
The beanFactoryPropertyName parameter value is the name of the Mach-II property name that will hold a reference to the ColdSpring !beanFactory. This parameter defaults to coldspring.beanfactory.root if not defined.
configFile
The configFile parameter value holds the path of the ColdSpring configuration file. The path can be an relative, CFML mapped or absolute path. If you are using a relative or mapped path, be sure to set the configFilePathIsRelative parameter to TRUE or the ColdSpring will not find your configuration file.
configGilePathIsRelative
The configGilePathIsRelative parameter value defines if the configure file is a relative (including CFML mapped) or absolute path. If you are using a relative or mapped path, be sure to set the configFilePathIsRelative parameter to TRUE or the property will not find your configuration file.
- TRUE (for relative or mapped configuration file paths)
- FALSE (for absolute configuration file paths)
resolveMachIIDependencies
The resolveMachIIDependencies parameter value indicates if the property to "automagically" wire Mach-II listeners/filters/plugins/properties. This parameter defaults to FALSE if not defined.
- TRUE (resolves all Mach-II dependencies)
- FALSE (does not resolve Mach-II dependencies)
parentBeanFactoryScope
The parentBeanFactoryScope parameter values defines which scope to pull in a parent bean factory. This parameter defaults to 'false' if not defined and indicates that a parent bean factory does not need to be referenced.
parentBeanFactoryKey
The parentBeanFactoryKey parameter values defines a key to pull in a parent bean factory from the scope specified in the parentBeanFactoryKey parameter. This parameter defaults to 'false' if not defined and indicates that a parent bean factory does not need to be referenced.
placeFactoryInApplicationScope
The placeFactoryInApplicationScope parameter indicates whether or not to place the bean factory in the application scope. This parameter is used to for setting your bean factory for use as a parent. The key that used is driven from the value from of the beanFactoryPropertyName parameter. If the parent uses the same value for the beanFactoryPropertyName, the module name (e.g. "_account") is append to the end of the key to eliminate namespace conflicts in the application scope. This parameter defaults to 'false' if not defined and indicates that this bean factory should not be placed in the application scope.
placeFactoryInServerScope
The placeFactoryInServerScope parameter indicates whether or not to place the bean factory in the server scope. This parameter is used to for setting your bean factory for use as a parent. The key that used is driven from the value from of the beanFactoryPropertyName parameter. If the parent uses the same value for the beanFactoryPropertyName, the module name (e.g. "_account") is append to the end of the key to eliminate namespace conflicts in the server scope. This parameter defaults to 'false' if not defined and indicates that this bean factory should not be placed in the server scope.
autowireAttributeName
The autowireAttributeName parameters indicates the name of the attribute to introspect for in cfcomponent tags when using the dynamic autowire getter/setter method generation feature of the ColdspringProperty. Autowire method generation injection allows you to put a list of ColdSpring bean names in the autowire attribute (which default to 'depends') in cfcomponent tag of your listeners, filters, plugins and properties CFC in Mach-II. ColdSpring property will automatically generate and dynamically inject getters/setters for the listed bean names into your target cfc at runtime. This does not modify the contents of the cfc file, but injects dynamically while the cfc is in memory. This feature allows you to stop having to type out getters/setters for the service that you want ColdSpring to inject into your cfc.
Example:
<cfcomponent extends="MachII.framework.Listener" depends="someService"> ... additional code ... </cfcomponent>
This will dynamically inject a getSomeService() and setSomeService() method into this listener. ColdSpring will then use the bean name and use setter injection to inject the bean into the listener.
generateRemoteProxies
Automatically generates or regenerates remote proxies of the type coldspring.aop.framework.RemoteFactoryBean that are created by ColdSpring when the CS bean factory is instantiated. By default, ColdSpring creates the remote proxies once and caches them to disk. The only way to get ColdSpring? to regenerate the remote proxies is to delete the remote proxies on disk or use the API. Set this parameter to true to have this done for you automatically. This parameter was added in Mach-II 1.6.
cfcGenerationLocation
This parameter was removed in Mach-II Simplicity 1.8 as this implementation was replaced with a faster option.
The cfcGenerationLocation parameter indicates where to write the temporary CFCs of the dynamic autowire method generation feature. Specify a path that can be expanded via expandPath(). Defaults to current location of ColdspringProperty?.cfc.
DO NOT DEFINE THESE PARAMETERS UNLESS YOU WANT TO OVERRIDE THE DEFAULT
dotPathToCfcGenerationLocation
This parameter was removed in Mach-II Simplicity 1.8 as this implementation was replaced with a faster option.
The dotPathToCfcGenerationLocation parameter indicates the dot path to where temporary CFCs are written for the dynamic autowire method generation feature.
DO NOT DEFINE THESE PARAMETERS UNLESS YOU WANT TO OVERRIDE THE DEFAULT
beansToMachIIProperties
The beansToMachIIProperties parameter holds a struct of bean names and corresponding Mach-II property names. This parameter will inject the specified beans in the Mach-II property manager as the bean factory has been loaded. In the past, a separate property has to be written to accomplish this task. This should be used for framework required "utility" objects that you want to be managed by ColdSpring such as UDF, i18n or session facade objects. Do not use this feature to inject your model objects into the Mach-II property manager.
<property name="coldspringProperty" type="MachII.properties.ColdspringProperty">
<parameters>
<parameter name="configFile" value="/path/to/config/services.xml" />
<parameter name="beanFactoryPropertyName" value="serviceFactory"/>
<parameter name="autowireAttributeName" value="depends" />
<parameter name="placeFactoryInApplicationScope" value="true"/>
<parameter name="resolveMachIIDependencies" value="true"/>
<parameter name="beansToMachIIProperties">
<struct>
<key name="NameOfColdSpringBean" value="PropertyName" />
</struct>
</parameter>
</parameters>
Parent/Child Bean Factories Configuration for Use with Modules
Base Mach-II Config File (i.e. Parent Factory):
<property name="ColdSpring" type="MachII.properties.ColdspringProperty"> <parameters> <parameter name="beanFactoryPropertyName" value="serviceFactory"/> <parameter name="configFile" value="/path/to/config/services.xml"/> <parameter name="configFilePathIsRelative" value="true"/> <parameter name="placeFactoryInApplicationScope" value="true"/> <parameter name="resolveMachIIDependencies" value="true"/> </parameters> </property>
You must put the parent bean factory in the application (or server scope) in orderfor a module to inherit from a parent factory. This example put the parent factory into the application.serviceFactory variable.
Account Module Config File (i.e. Child Factory):
<property name="ColdSpring" type="MachII.properties.ColdspringProperty"> <parameters> <parameter name="beanFactoryPropertyName" value="serviceFactory"/> <parameter name="configFile" value="/path/to/modules/account/config/services_account.xml"/> <parameter name="configFilePathIsRelative" value="true"/> <parameter name="resolveMachIIDependencies" value="true"/> <parameter name="placeFactoryInApplicationScope" value="true"/> <parameter name="parentBeanFactoryScope" value="application"/> <parameter name="parentBeanFactoryKey" value="serviceFactory"/> </parameters> </property>
You are NOT required to put child factories into the application (or server scope) for modules to inherit froma a parent factory. However, in this example the account module puts this child factory into the application scope. Since the parent and module use the same beanFactoryPropertyName, an application scope namespace conflict would occur - so the Property appends the module name to the end. This factory would be located in application.serviceFactory_account variable.
Using Mach-II Properties within ColdSpring
By default, the ColdSpringProperty passes ColdSpring a structure called defaultProperties. This is the term used by ColdSpring, but the defaultProperties is a reference to the Mach-II properties which you then can use as bean contructor-args or properties when defining your beans.
Using the default properties in ColdSpring is easy by using the ${nameOfDefaultProperty} syntax in your XML file.
<bean id="addressGateway"
class="lightpost.model.address.address.addressGateway_mysql">
<constructor-arg name="dbDsn"><value>${dbDsn}</value></constructor-arg>
<constructor-arg name="dbTimeout"><value>${dbTimeout}</value></constructor-arg>
</bean>
The ${dbDsn} and ${dbTimeout} values in the example match properties that were defined in the Mach-II properties section with the same name. The ${} is telling ColdSpring to do a substitution at runtime with the value passed into the bean container from the Mach-II properties.
Using Autowire by Depends Attribute
The autowire by dynamic method generation feature eliminates the necessity of creating get/set methods or constructor variables in your listeners, plugins, filters and properties. Before Mach-II Simplicity (1.8), the ColdSpringProperty would autowire in your beans by looking for setter methods in your listeners, filters, properties and plugins:
<cffunction name="setSomeService" access="public" returntype="void" output="false"
<cfargument name="someService" type="component" required="true"/>
<cfset variables.someService = arguments.someService />
</cffunction>
<cffunction name="getSomeService" access="public" returntype="component" output="false"
<cfreturn variables.someService />
</cffunction>
Writing these tedious methods are no longer necessary because the new MachII.properties.ColdSpringProperty provides easy ColdSpring integration with Mach-II applications by autowiring these objects using the autowire attribute name (by default named depends) in cfcomponent tags. This allows you to autowire ColdSpring managed beans into your listeners, filters, plugins and property CFCs by defining the bean name as metadata in your cfcomponent tag without having to write concrete getter/setter methods.
N.B. The depends attribute only works with components that extend a Mach-II component and will not work on non-Mach-II components.
Setting up autowire by dynamic method generation
The following example shows the parameter that must be included in the ColdSpringProperty declaration to set the attribute name so ColdSpring can inject the dependencies.
Define attribute name:
<parameter name="autowireAttributeName" value="depends" />
If the parameter is not included, the ColdspringProperty will use the default attribute name 'depends'.
Sample ColdSpringProperty definition:
<property name="ColdSpring" type="machii.properties.ColdspringProperty"> <parameters> <parameter name="beanFactoryPropertyName" value="serviceFactory"/> <parameter name="configFile" value="services.xml"/> <parameter name="configFilePathIsRelative" value="true"/> <parameter name="placeFactoryInApplicationScope" value="true"/> <parameter name="resolveMachIIDependencies" value="true"/> <parameter name="autowireAttributeName" value="depends" /> </parameters> </property>
Example before and after code
Using autowire by dynamic method generation, it allows you to take your listener from this:
<cfcomponent extends="MachII.framework.Listener">
<cffunction name="setSomeService" access="public" returntype="void" output="false"
<cfargument name="someService" type="component" required="true"/>
<cfset variables.someService = arguments.someService />
</cffunction>
<cffunction name="getSomeService" access="public" returntype="component" output="false"
<cfreturn variables.someService />
</cffunction>
<cffunction name="setOtherService" access="public" returntype="void" output="false"
<cfargument name="otherService" type="component" required="true"/>
<cfset variables.otherService = arguments.otherService />
</cffunction>
<cffunction name="getOtherService" access="public" returntype="component" output="false"
<cfreturn variables.otherService />
</cffunction>
<cffunction name="doSomethingCool" access="public" returntype="void" output="false"
hint="Does something very cool">
<cfargument name="event" type="MachII.framework.Event" required="true"/>
<cfset getSomeService().doSomethingCool(arguments.event.getArg("tooCoolForSchool")) />
</cffunction>
</cfcomponent>
To this using a custom autowire attribute name using depends as the metadata attribute (since this is customizable you could set the autowireAttributeName parameter above to cs and use that as the metadata attribute):
<cfcomponent extends="MachII.framework.Listener" depends="someService,otherService">
<cffunction name="doSomethingCool" access="public" returntype="void" output="false"
hint="Does something very cool">
<cfargument name="event" type="MachII.framework.Event" required="true"/>
<cfset getSomeService().doSomethingCool(arguments.event.getArg("tooCoolForSchool")) />
</cffunction>
</cfcomponent>
As you can see, the depends attribute is indicating to the ColdspringProperty to generate getter/setter methods for beans named someService and otherService and then autowire those beans into the listener. Even though the getSomeService() method is not physically present in your Listener source code, the method will still be available for use as ColdspringPropery will create and dynamically inject the method in memory into your listener. The someService injected component is also available in the variables.someService variable.
Available Methods When Using Dynamic Method Injection Autowiring
For each autowired component, the ColdspringProperty will generate, inject and autowire in the following methods for each component in the depends metadata attribute.
| Method Name or Variable | Description |
| getXYZ() | Getter for XYZ component |
| setXYZ() | Setter for XYZ component. Used by the ColdspringProperty to autowire the component into the target CFC. |
| variables.xyz | The variables namespace used by the getter and setter that stores the autowired component in the target CFC. |
Common Errors Using the ColdSpringProperty
Wrong Property Used
You must use the MachII.properties.ColdspringProperty component in order to enable the new dynamic method generation autowiring. This feature is not available in the older ColdspringPlugin or ColdspringProperty that is bundled in the ColdSpring core. Not using the property bundled in the Mach-II core is the most common error when trying to implement this feature in a Mach-II application.
Write permissions
The ColdspringProperty needs write permissions to the MachII.properties directory if you are using the autowire by depends attribute. The location of this directory would be wherever you have Mach-II installed. The property needs write permissions because it dynamically builds a CFC with the required methods so it can inject them via mixins to the target CFC. Currently, there is no way to dynamically build a CFC in memory and instantiate it. This requires us to write temporarily write it to disk first, instantiate it, and then delete the temporary file. If your CFML server does not have write permissions, it will throw an error similar to this:
An error occurred when performing a file operation write on file /path/to/MachII/properties/79CA8568FB6F49E4384F1F41E3A289F7.cfc.
Giving your CFML server write permissions to that directory will fix this issue.
Mismatched temporary CFC generation location
Some deployments of CFML engines seem to mess up the temporary CFC generation location. You might get an exception like this:
Please check that the dot path location '9B11663F57F7D926B0B63CEB348C0847' and cfcGenerationLocation 'E:\JRun4\servers\Q5CF9\cfusion.ear\cfusion.war' point to the same directory.
Luckily the temporary CFC generation location is configurable.
cfcGenerationLocation
This parameter was removed in Mach-II Simplicity 1.8 as this implementation was replaced with a faster option.
The cfcGenerationLocation parameter indicates where to write the temporary CFCs of the dynamic autowire method generation feature. Specify a path that can be expanded via expandPath(). Defaults to current location of ColdspringProperty?.cfc.
DO NOT DEFINE THESE PARAMETERS UNLESS YOU WANT TO OVERRIDE THE DEFAULT
dotPathToCfcGenerationLocation
This parameter was removed in Mach-II Simplicity 1.8 as this implementation was replaced with a faster option.
The dotPathToCfcGenerationLocation parameter indicates the dot path to where temporary CFCs are written for the dynamic autowire method generation feature.
DO NOT DEFINE THESE PARAMETERS UNLESS YOU WANT TO OVERRIDE THE DEFAULT
If you have a mapping to your application, you could create a /temp folder to generation a CFC in.
Migration from ColdspringProperty or ColdspringPlugin in ColdSpring core
There have been some reports from people who have migrated from an older version of the ColdSpring connector (files like coldspring.machii.ColdspringProperty or coldspring.machii.ColdspringPlugin) having problems even when reloading their application. Some possible solutions are:
- Restart to your CFML server service
- Clear your CFML template cache (located in your CFML server administrator)
Old and incompatible version of ColdSpring
Another common error is ColdspringProperty throws an error saying the it cannot find a method named findImports().
Additional Information and Considerations
- The ColdspringProperty does not walk the inheritance tree. If your listener, plugin, filter or property extends something other than MachII.framework.XXX where XXX is the normal base component for that framework extension, the property will not walk the inheritance tree and check the parent object for depends or setters to autowire in. A feature enhancement was filed for this on ticket #86.
- This new feature requires ColdSpring 1.2 Stable or higher (such as bleeding edge release from a source code repository).
Closing Thoughts
As a developer of Mach-II applications, you are certainly not required to use the ColdspringProperty. It is simply a convenient way to "wire up" Mach-II and ColdSpring so that you can focus on your custom development rather than configuration and setup. The ColdspringProperty does not in any way change the backwards compatibility of the framework.
