Transfer Extras

Introduction

ColdBox includes some extra classes when working with Transfer ORM. These classes are :

  1. TransferConfigFactory.cfc : A factory cfc that produces transfer configuration objects based on ColdBox Datasource configurations.
  2. 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.
  3. 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
Important : Please note that all cache keys are case SENSITIVE.

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>


Note: I would STRONGLY recommend that you setup the values of the observer as settings in your coldbox.xml.

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.