Transfer Extras
Introduction
ColdBox includes some extra classes when working with Transfer ORM. These classes are :
- TransferConfigFactory.cfc : A factory cfc that produces transfer configuration objects based on ColdBox Datasource configurations.
- TDOBeanInjectorObserver.cfc : A transfer observer based on Brian Kotek's amazing TDOBeanInjectorObserver in his Coldspring Utilities project, used to create rich decorators by injecting them with dependencies from Coldspring and Lightwire. However, we had to take it a step further, so it can even inject dependencies from anything that ColdBox can produce.
- TransferLoader.cfc : An interceptor that can load transfer and some of its classes into the ColdBox Cache. A super duper easy way to work with Transfer in your applications.
These utilities can be found in the /coldbox/system/extras folder.
Credits
Transfer Loader Interceptor
The transfer extras have been expanded and a new interceptor has been born called: TransferLoader. This nice interceptor will configure your application to use Transfer and cache it in the coldbox cache. Easily configure your app for Transfer ORM.
Interceptor Properties
| property | type | required | default | description |
| datasourceAlias | string | true | N/A | The datasource alias name to use |
| configPath | string | true | N/A | The path of the config file |
| definitionPath | string | true | N/A | The definitions path |
| TransferFactoryCacheKey | string | false | TransferFactory | The key used to store the factory as a singleton in the coldbox cache. (case sensitive) |
| TransferCacheKey | string | false | Transfer | The key used to store transfer as a singleton in the coldbox cache. (case sensitive) |
| TransactionCacheKey | string | false | TransferTransaction | The key used to store transfer transaction as a singleton in the coldbox cache. (case sensitive) |
| TransferFactoryClassPath | string | false | transfer.TransferFactory | The class path override of the factory |
| TransferConfigurationClassPath | string | false | transfer.com.config.Configuration | The class path override of the configuration object |
| LoadBeanInjector | boolean | false | false | Loads the ColdBox TDOBean Injector |
| BeanInjectorProperties | JSON | false | false | Ability to add bean injector properties |
Bean Injector Properties
- useSetterInjection: boolean (true)
- debugMode : boolean (false)
- onDICompleteUDF : string (onDIComplete)
- stopRecursion: string (transfer.com.TransferDecorator)
<Property name="BeanInjectorProperties">{'useSetterInjection':'false','debugMode':'true'}</Property>
Sample:
<Datasources> <Datasource alias="blogDSN" name="simpleblog" dbtype="mssql" username="" password="" /> </Datasources> <Interceptors> <!-- Transfer Loader --> <Interceptor class="coldbox.system.extras.transfer.TransferLoader"> <Property name="ConfigPath">/${AppMapping}/config/transfer.xml.cfm</Property> <Property name="definitionPath">/${AppMapping}/config/definitions</Property> <Property name="datasourceAlias">blogDSN</Property> <Property name="LoadBeanInjector">true</Property> <Property name="BeanInjectorProperties">{'useSetterInjection':'false','debugMode':'true'}</Property> </Interceptor> </Interceptors>
This declaration will create the following objects in the ColdBox cache and register the TDO Bean Injector Observer.
| Cache Key | Object |
| TransferFactory | The transfer factory object |
| Transfer | The transfer object |
| TransferTransaction | The transfer transaction object |
So do you still think there is more? Well, there is no more, that's it, a few lines of code and you are cooking with Transfer.
Transfer Config Factory
This object is used to create a transfer configuration object based on the datasource information found in the ColdBox configuration file. This idea is thanks to Tom de Manincor and his musings in ColdBox-ColdSpring-Transfer. This approach let's you maintain all of your application's configuration in one single location and not create a datasource.xml for just a datasource information. It is meant to be used alongside either ColdSpring or LightWire but it can be used as a separate object too (You will have to do the wiring). Anyways, here is a sample coldspring and coldbox xml declaration for this usage:
Coldbox Settings:
<YourSettings> <Setting name="TransferConfigPath" value="/config/transfer.xml.cfm" /> <Setting name="TransferDefinitionsPath" value="/config/definitions" /> <Setting name="TransferDSNAlias" value="MyDSN" /> </YourSettings> <Datasources> <Datasource alias="MyDSN" name="myproject" dbtype="mssql" username="" password="" /> </Datasources>
A note here, is that the Transfer DSN Alias setting is the alias of your Coldbox datasource. In this case, MyDSN points to the myproject datasource name.
Coldspring Settings:
<!-- coldbox Factory --> <bean id="ColdboxFactory" class="coldbox.system.extras.ColdboxFactory" singleton="true"/> <!-- MyDatasource --> <bean id="MyDSN" factory-bean="ColdBoxFactory" factory-method="getDatasource"> <constructor-arg name="alias"> <value>${TransferDSNAlias}</value> </constructor-arg> </bean> <!-- Coldbox-transfer Config Factory --> <bean id="TransferConfigFactory" class="coldbox.system.extras.transfer.TransferConfigFactory" singleton="true" /> <!-- transfer --> <bean id="TransferFactory" class="transfer.TransferFactory" singleton="true"> <constructor-arg name="configuration"> <bean factory-bean="TransferConfigFactory" factory-method="getTransferConfig"> <!-- Config Path --> <constructor-arg name="configPath"><value>${TransferConfigPath}</value></constructor-arg> <!-- Definitions Path --> <constructor-arg name="definitionPath"><value>${TransferDefinitionsPath}</value></constructor-arg> <!-- ColdBox Datasource Bean --> <constructor-arg name="dsnBean"><ref bean="MyDSN" /></constructor-arg> </bean> </constructor-arg> </bean> <bean id="Transfer" factory-bean="TransferFactory" factory-method="getTransfer" />
As you can see, we first define the coldbox factory bean (See ColdSpring Guide and construct a datasource bean element with it.
<!-- coldbox Factory --> <bean id="ColdboxFactory" class="coldbox.system.extras.ColdboxFactory" singleton="true"/> <!-- MyDatasource --> <bean id="MyDSN" factory-bean="ColdBoxFactory" factory-method="getDatasource"> <constructor-arg name="alias"> <value>${TransferDSNAlias}</value> </constructor-arg> </bean>
We then setup the transferConfigFactory.cfc as a coldspring factory bean. This means, that we call a method in this object in order to produce another object. In our case, the Transfer configuration object.
<!-- Coldbox-transfer Config Factory --> <bean id="TransferConfigFactory" class="coldbox.system.extras.transfer.TransferConfigFactory" singleton="true" />
The last step is configuring the transfer factory via the xml. We send in a configuration bean (Ours) and set it up as a call to our transfer config factory with the same parameters we are used to, except that for datasource we use the dsnBean element and have it referenced to the datasource that we want to use, in our case MyDSN, that we defined at the beginning.
<!-- transfer --> <bean id="TransferFactory" class="transfer.TransferFactory" singleton="true"> <constructor-arg name="configuration"> <bean factory-bean="TransferConfigFactory" factory-method="getTransferConfig"> <!-- Config Path --> <constructor-arg name="configPath"><value>${TransferConfigPath}</value></constructor-arg> <!-- Definitions Path --> <constructor-arg name="definitionPath"><value>${TransferDefinitionsPath}</value></constructor-arg> <!-- ColdBox Datasource Bean --> <constructor-arg name="dsnBean"><ref bean="MyDSN" /></constructor-arg> </bean> </constructor-arg> </bean> <bean id="Transfer" factory-bean="TransferFactory" factory-method="getTransfer" />
If you need a refresher on how the ColdBox Factory for IoC works, please read the IoC Integration Guide. That's it. Simple as that, now you can define all your configurations via the coldbox configuration file and let coldspring or lightwire do the heavy lifting.
TDOBeanInjectorObserver
This handy tools is based on Brian Kotek's original observer. However, we have modified it to use ColdBox beanFactory plugin to do the autowiring for us. For those familiar with the ColdBox autowiring conventions, you can do autowiring via annotations using the cfproperty tag or via setter injection. Not only that, but you can use ColdSpring or LightWire seamlessly, and to top it off, you can autowire objects from anything in our Autowiring DSL: Autowire guide.
Conventions Overview
To get familiar with some of the autowire annotations in ColdBox, please visit the Autowire guide. Here is a sample of some cool autowire annotations:
<cfcomponent name="User" output="false" hint="A transfer decorator User object" extends="transfer.com.TransferDecorator"> <!--- Injected Dependencies ---> <cfproperty name="ValidatorFactory" type="ioc" scope="instance" /> <cfproperty name="qAvailableCharSets" type="ocm" scope="variables" /> <cfproperty name="logger" type="coldbox:plugin:logger" scope="instance" /> </cfcomponent>
As you can see from the code above we have two dependencies marked by their types: ioc, ocm and coldbox:plugin. The type of ioc means that this dependency must be injected from the ioc plugin (coldspring/lighwire)' and the type of ocm means that the dependency must be injected from the ColdBox Cache. The last dependency comes from the ColdBox Autowire DSL. Please visit the Autowire guide to see all the valid DSL properties.
You also have a scope attribute that is extremely useful, as you can define in which scope or pathed scope you would like this dependency injected to. The default value is is in the variables scope.
On the other hand, if you would prefer to do setter injection, you can still achieve the same effect, but you must add a custom metadata argument to mark the type of injection. If you omit the marker, then the default value is ioc. The default marker is _wireme, which can also be changed.
<cffunction name="getValidatorFactory" access="private" returntype="any" output="false"> <cfreturn instance.ValidatorFactory> </cffunction> <cffunction name="setValidatorFactory" access="private" returntype="void" output="false"> <cfargument name="ValidatorFactory" type="any" required="true"> <cfset instance.ValidatorFactory = arguments.ValidatorFactory> </cffunction> <cffunction name="getqAvailableCharSets" access="private" returntype="any" output="false"> <cfreturn instance.ValidatorFactory> </cffunction> <cffunction name="setqAvailableCharSets" access="private" returntype="void" output="false" _wireme="ocm"> <cfargument name="qAvailableCharSets" type="any" required="true"> <cfset instance.qAvailableCharSets = arguments.qAvailableCharSets > </cffunction> <cffunction name="getlogger" access="private" returntype="any" output="false"> <cfreturn instance.ValidatorFactory> </cffunction> <cffunction name="setlogger" access="private" returntype="void" output="false" _wireme="coldbox:plugin:logger"> <cfargument name="logger" type="any" required="true"> <cfset instance.logger= arguments.logger> </cffunction>
You will see something very tricky here... The access types for the methods are private. The ColdBox bean factory can actually do setter injection even if the methods are private or public.
Bean Injector Definition
The theory behind this tool is to be able to very easily create rich decorators that can be injected with dependencies from the IoC container or the ColdBox Cache. It is a great way to have your objects be composed of other objects and utilities. So let's see the coldspring.xml, but first, please note that the instructions below are for ColdSpring 1.2 that enables the usage of the lazy-init property. For ColdpSring 1.0 or LightWire, you will have to do some more manual work. This means that you will have to manually add the observer to transfer from the application start handler.
<!-- coldbox --> <bean id="ColdboxFactory" class="coldbox.system.extras.ColdboxFactory" singleton="true"/> <!-- coldbox bean factory --> <bean id="ColdBoxBeanFactory" factory-bean="ColdBoxFactory" factory-method="getPlugin" singleton="true"> <constructor-arg name="plugin"> <value>beanFactory</value> </constructor-arg> </bean> <!-- coldbox-transfer observer --> <bean id="TDOBeanInjectorObserver" class="coldbox.system.extras.transfer.TDOBeanInjectorObserver" lazy-init="false"> <constructor-arg name="Transfer"><ref bean="Transfer"></ref></constructor-arg> <constructor-arg name="ColdBoxBeanFactory"><ref bean="ColdBoxBeanFactory"></ref></constructor-arg> <constructor-arg name="useSetterInjection"><value>true or false</value></constructor-arg> <constructor-arg name="onDICompleteUDF"><value>onDIComplete</value></constructor-arg> <constructor-arg name="debugMode"><value>true or false</value></constructor-arg> <constructor-arg name="stopRecursion"><value>transfer.com.TransferDecorator</value></constructor-arg> </bean>
We first define the ColdBox Factory, then the beanFactory plugin we will be using for injections.
<!-- coldbox --> <bean id="ColdboxFactory" class="coldbox.system.extras.ColdboxFactory" singleton="true"/> <!-- coldbox bean factory --> <bean id="ColdBoxBeanFactory" factory-bean="ColdBoxFactory" factory-method="getPlugin" singleton="true"> <constructor-arg name="plugin"> <value>beanFactory</value> </constructor-arg> </bean>
We then define our Observer and make sure we set the lazy-init to false, so it can be created at startup. Again, if you have an older version of Coldspring, you will need to add the observer to transfer manually via an application start handler.
<!-- coldbox-transfer observer --> <bean id="TDOBeanInjectorObserver" class="coldbox.system.extras.transfer.TDOBeanInjectorObserver" lazy-init="false"> <constructor-arg name="Transfer"><ref bean="Transfer"></ref></constructor-arg> <constructor-arg name="ColdBoxBeanFactory"><ref bean="ColdBoxBeanFactory"></ref></constructor-arg> <constructor-arg name="useSetterInjection"><value>true or false</value></constructor-arg> <constructor-arg name="onDICompleteUDF"><value>onDIComplete</value></constructor-arg> <constructor-arg name="debugMode"><value>true or false</value></constructor-arg> <constructor-arg name="stopRecursion"><value>transfer.com.TransferDecorator</value></constructor-arg> </bean>
Not all of the parameters are required. Below you can see a description of each of the parameters:
| argument | type | required | default | description |
| transfer | bean | true | --- | The transfer object bean |
| coldboxBeanFactory | bean | true | --- | The ColdBox beanFactory plugin |
| useSetterInjection | boolean | false | true | A boolean variable that enables or disabled setter injection in preference of annotations via cfproperty. If enabled, it will do cfproperty annotations first and then look for setters. |
| onDICompleteUDF | string | false | onDIComplete | The name of the UDF to call in your decorators once they have been injected with their dependencies. The default value used is onDIComplete. This means that you create a method in your decorators called onDIComplete or whatever you like and when the bean factory injects the dependencies, it will call this method afterwards. You can use this to do configuration or object setup. |
| debugMode | boolean | false | false | If set to true, it will log to the logging facilities all the interactions when trying to autowire the objects. |
| stopRecursion | string | false | transfer.com.TransferDecorator | The class name of where to stop metadata gathering recursion. |
That's it folks. Once the application starts up, coldspring will create and register this observer for you. So when transfer objects are created and have autowire dependencies, they will be wired up by the ColdBox beanFactory plugin.
