Blog Projects
Escape Keys
A ColdFusion and Web Development Blog by Tom de Manincor
 

Here is a great post by Brian Ghidinelli on how to integrate TransferSync in a ModelGlue and Coldspring system.

Check it out: Synchronizing Transfer ORM in a Model-Glue/Coldspring cluster with TransferSync

A minor update with a major fix.

0.4 -CHANGE LOG- 11/13/2008 - enhancement - logging improvements and toggle setting/property added - enhancement - improved caching - enhancement - added getKeyMap method - enhancement - added argument to definition file - enhancement - changed definition file extension to .transfer - fix - added double lock to prevent concurrency issues with duplicate method creation

And for those using Coldbox there is an optimized version of the gateway in the package.

Grab a copy at: TransferSync @ riaForge

Coldbox and Coldspring have a lot to offer when it comes to development. However, loading time is the price we pay.

There are a number of settings in Coldbox to optimize performance based on environment, but developing locally with nothing cached can drag between requests. Especially, if you are using a large number of beans with Coldspring.

If you want to trigger a reload it's as simple as:

<cfset getPlugin('ioc').configure() />

Where and how you use this is up to you.

May be we can get it integrated into the Coldbox Sidebar.

We know how to use the Coldbox environment interceptor to load different values for datasources. We know how to circumvent datasource.xml files with Transfer by using a datasource bean. Now, how can we use ColdSpring to put it all together for us.

<!-- coldbox -->
   <bean id="ColdboxFactory" class="coldbox.system.extras.ColdboxFactory" />
   <bean id="Coldbox" factory-bean="ColdBoxFactory" factory-method="getColdbox" singleton="true" />
   
   <!-- transfer -->
   <bean id="TransferFactory" class="transfer.TransferFactory" singeleton="true">
      <constructor-arg name="configuration">
         <bean class="model.TransferConfig">
            <constructor-arg name="DSNBean">
               <bean factory-bean="ColdBoxFactory" factory-method="getDatasource">
                  <constructor-arg name="alias">
                     <value>${Transfer_DSNAlias}</value>
                  </constructor-arg>
               </bean>
            </constructor-arg>
            <constructor-arg name="configPath">
             <value>${Transfer_ConfigPath}</value>
            </constructor-arg>
            <constructor-arg name="definitionPath">
             <value>${Transfer_DefinitionPath}</value>
            </constructor-arg>
         </bean>
       </constructor-arg>
   </bean>
      
   <bean id="Transfer" factory-bean="TransferFactory" factory-method="getTransfer" singleton="true" />
   <bean id="Datasource" factory-bean="TransferFactory" factory-method="getDatasource" singleton="true" />

That's what my coldspring.xml file would look like. There are a few things to note. I use Coldbox to get the Datasource information I need because it has already been defined in the Coldbox config or environment file. This is the reason I wanted to avoid a datasource.xml file for Transfer. Otherwise, I'd have the same DSN info repeated all over the place.

I also point to model.TransferConfig. A simple extension of the Transfer Configuration bean. You can learn more about that from my previous post.

If you notice, there is a Transfer_DSNAlias argument being passed into the Coldbox getDatasource method. This tells CB which DSN you want to load up and pass on to Transfer. Coldbox allows for multiple datasources to be stored for use in your App. But Transfer only connects to one at a time. So I made it a setting in the config/environment.

Here's a peak at my environements.xml file.

<environment name="development" urls="" patterns=".local">
   <Setting name="DebugMode"               value="true" />
   <Setting name="DebugPassword"               value="" />
   <Setting name="ReinitPassword"               value="" />
   <Setting name="EnableDumpVar"               value="true" />
   <Setting name="HandlersIndexAutoReload"          value="true" />
   <Setting name="ConfigAutoReload"            value="true" />
   <Setting name="HandlerCaching"               value="false" />
   <Setting name="EventCaching"               value="false" />
   
   <Setting name="Datasources" value="{'MyDSNAlias': {'Alias': 'MyDSNAlias' , 'Name': 'MyDSN', 'DBType': 'mssql', 'Username': 'dbusername', 'Password': 'dbpass'}}" />
   
   <Setting name="Transfer_ConfigPath" value="/config/transfer.xml.cfm" />
   <Setting name="Transfer_DefinitionPath" value="/model/definition" />
   <Setting name="Transfer_DSNAlias" value="MyDSNAlias" />
</environment>

That's it.

I whipped this up to contribute to Rolando's environmentConfig project.

ColdSpring doesn't like being passed complex values, and it's not that big of a deal unless you are parsing settings from a 3rd party XML, that builds them into a nested struct.

So we came up with this to build all the nested structs into a single struct. By default it adds the parent struct's key as a prefix. This is to assure no values are overwritten.

I didn't see a UDF on CFLib for it, so I figured I'd submit and post here in case it can help anyone else out.

<cffunction name="flattenStruct" access="public" output="false" returntype="struct">
   <cfargument name="stObject" required="true" type="struct" />
   <cfargument name="delimiter" required="false" type="string" default="." />
   <cfargument name="prefix"     required="false" type="string" default="" />
   <cfargument name="stResult" required="false" type="struct" default="#structNew()#" />
   <cfargument name="addPrefix" required="false" type="boolean" default="true" />
   
   <cfset var sKey = '' />
   
   <cfloop collection="#arguments.stObject#" item="sKey">      
      <cfif isSimpleValue(arguments.stObject[sKey])>
         <cfif arguments.addPrefix and len(arguments.prefix)>
            <cfset arguments.stResult[arguments.prefix & arguments.delimiter & sKey] = arguments.stObject[sKey] />
         <cfelse>
            <cfset arguments.stResult[sKey] = arguments.stObject[sKey] />
         </cfif>
      <cfelseif isStruct(arguments.stObject[sKey])>
         <cfset flattenStruct(arguments.stObject[sKey],arguments.delimiter,sKey,arguments.stResult) />   
      </cfif>
   </cfloop>
   <cfreturn arguments />
</cffunction>

I am running ColdFusion on my Windows 2003 Server VM. I have my files on my local machine, available to my VM via a shared folder. Meaning, IIS and CF see it as \.hostShared FoldersWebroot When Coldspring is initialized it runs theshrinkFullRelativePath method in the DefaultXmlBeanFactory.cfc

If you are passing in an expanded path then Coldspring will thrown an error because of the rebuilding of the path and the '\' before '.host' gets stripped down to '/' and is not a valid path.

Or, you are sending in a relative path and its just not found at all.

In the meantime modify this check before the return of the shrinkFullRelativePath method to:

<!--- Modified for Shared Folders in VMs --->
<cfif left(retVal,2) is "/.">
<cfset retVal = "/" & retVal>
</cfif>
<!--- Modified to Allow Relative Path --->
<cfif not directoryExists(getDirectoryFrompath(retVal)) and not directoryExists(getDirectoryFrompath(expandPath(retVal)))>
<cfthrow message="You have specified an invalid directory" detail="The directory path specified, #getDirectoryFromPath(retVal)# does not exist." />
</cfif>

If your're not dealing with a VM but still want to use Relative Paths, only use the second if statement.

** This may only be good for people using ColdFusion 7 and Up. (Application root mapping support issues)