Skip to content

Using ColdSpring With Mach II

thofrey edited this page Apr 1, 2014 · 8 revisions

Table of Contents

  1. What is ColdSpring
  2. What is a Mach-II Property
  3. What is the ColdspringProperty
  4. ColdspringProperty Usage and Configuration
  5. Parameter Descriptions
  6. Parent/Child Bean Factories Configuration for Use with Modules
  7. Using Mach-II Properties within ColdSpring
  8. Using Autowire by Depends Attribute
  9. Common Errors Using the ColdSpringProperty
  10. Additional Information and Considerations
  11. 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.

The concepts behind ColdSpring seem intimidating (DI, IoC, setter injection, etc), but in all reality what it does is much easier to understand. In a nutshell, ColdSpring wires objects together. You defined relationships using an XML configuration file. When you ask ColdSpring for an object, it first checks a cache to see if it has one yet and if not uses this wiring plan to give you back a fully wired and instantiated object back.

The first hurdle to get over is one term that ColdSpring uses the term "bean". ColdSpring is a functionality port of a package in the Spring project (Java). In Spring, an object == bean while in CFML we typically use the term bean to describe a business object. So when using ColdSpring, when you see "bean" think "object" instead.

Let's look at a simple CS configuration file:

    <beans>
        <bean id="showroomService"
            class="spectrum.model.showroom.showroomService">
            <property name="showroomDao"><ref bean="showroomDao"/></property>
            <property name="showroomGateway"><ref bean="showroomGateway"/></property>
            <property name="udfs"><ref bean="udfs"/></property>
        </bean>
        <bean id="showroomDao"
            class="spectrum.model.showroom.showroomDao"
            parent="baseDaoGateway" />
        <bean id="showroomGateway"
            class="spectrum.model.showroom.showroomGateway"
            parent="baseDaoGateway" />
    </beans>

Each "bean" (object) in our model gets a definition. As you can see, the ShowroomService has some properties. This are items are need to be injected into the service for it to work. Without ColdSpring in the picture, you would probably create the ShowroomDao and ShowroomGateway in the init() method of the ShowroomService. Instead, the instantiation of these objects is taken care of by ColdSpring and injected into the service by using setter methods. What I just described is called Inversion of Control (IoC). Instead of letting the service have "control" on how objects are instantiated, an outside "provider" (ColdSpring) takes care of doing that and setting them into the service.

This wiki entry does not explain ColdSpring in great detail or how to use the ColdSpring API. For more information, 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.

<a name=cfcGenerationLocation"">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 the 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>
    </property>

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. And if you need to pass in structures or arrays previously defined as machii properties, update your Coldspring config with:

    <bean id="SomeCFC" class="model.com.SomeCFC">
        <property name="someProperty">
            <map>${someStruct}</map>
        </property>
        <property name="anotherProperty"
            <list>${someArray}</list>
        </property>
    </bean>

Using Autowire by Depends Attribute

The autowire by dynamic method generation feature eliminates the necessity of creating get/set methods or constructor variables in all Mach-II developer extended CFCs:

  • Listeners
  • Plugins
  • Filters
  • Properties
  • Endpoints (1.9+)

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 (trac-wiki) #86 "enhancement: Walking Inheritance Tree in Listeners/Plugins (closed: Moved to GitHub)").
  • 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.

Clone this wiki locally