Sunday, January 11, 2009

AOP, Caching, and memcached

Last week I started playing around with Aspect-Oriented Programming (AOP) in ColdSpring and was pretty amazed at how useful it could be. To be able to alter the functionality of your application without touching your core application code is an extremely powerful concept, but I wasn't sure where to begin.

The example on the ColdSpring site shows how AOP can be applied to handle logging across an application, but that doesn't seem all that useful (or sexy) to me. I wanted a better scenario.

After reviewing some code in a project I've been working on, I noticed a prime example of where I could use AOP: caching.

There was code inside multiple function calls that would perform caching based on the component, method, and arguments being passed into the method. The code was using non-deterministic caching, meaning if the data didn't exist in the cache, the system would fetch the data then put it into the cache. The caching code looks similar to this:


<cfset key = application.cache.getKey("reportDAO","getReportByID",arguments) />

<cfif application.cache.hasData(key)>

<cfset report = application.cache.getData(key) />

<cfelse>

... build the report ...

<cfset application.cache.setData(key,report) />

</cfif>


The code worked fine, but I had to add those same 4 lines of code to each method I wanted to cache. Plus, it led to a low level of cohesion; the method needed to know how to build a report as well as how to store and retrieve itself in memory.

With AOP, I could now configure ColdSpring to intercept each method call I wanted to cache without having to flood my code with caching logic.

As a nice side bonus, the non-deterministic key/value caching strategy I was using turned out to be similar to how memcached, a high-performance, distributed memory object caching system, caches its data. Knowing this, I was able to modify the caching advice component I created and plugin memcached in a matter of minutes.

I'm currently working on an XML based caching component that will handle bean/method caching in the request scope, session scope, and memcached. I'll post more details when I have something to show.

Sunday, January 4, 2009

Label Tags (or Lack Thereof)

One of my biggest pet peeves is when sites don't use label tags, especially around radio buttons. How much does this bother me? I actually emailed my bank's support email address about adding label tags to their "Make a Payment" page. So far, nothing has changed.



Why force a user to click the small circle when they could click the text instead? It's such a quick and simple thing to do and yet can make a page so much easier to use. There's really no reason to not use label tags other than developer laziness.

Tuesday, December 30, 2008

Model-Glue-esque Bean Factory, Redux

After using my latest attempt at a Model-Glue inspired bean factory a little longer, I ran into a major problem: circular dependencies.

In my application (and most others), services will need to be able to talk to each other. For example, my application has a productService and a categoryService, and each service will need an instance of the other injected into it.

In ColdSpring, you can get around circular dependencies by using properties (setter injection) rather than constructor arguments. However, I want to use the "beans" scope to inject all my dependencies rather than having to create the necessary setProductService() and setCategoryService() methods.

I solved the problem by re-working my DynamicBeanFactory. I updated the init() method to construct all the beans when the bean factory is created, then inject the "beans", "helpers" and "includes" as necessary. Doing this also allowed me to leave the getBean() method alone, which is kinda nice.

Here's the updated DynamicBeanFactory:


<cfcomponent output="false" extends="coldspring.beanutils.DynamicXMLBeanFactory">

<!------>

<cffunction name="init" access="public" output="false" returntype="any">
<cfargument name="pathToColdspringXML" required="true" type="string" />
<cfargument name="params" required="false" default="" />

<cfset var local = StructNew() />
<cfset var i = "" />

<!--- dynamic properties that get passed into the bean factory --->
<cfif not isStruct(arguments.params)>
<cfset arguments.params = StructNew() />
</cfif>

<!--- init the bean factory --->
<cfset super.init() />

<!--- load the beans --->
<cfset loadBeansFromDynamicXmlFile(arguments.pathToColdspringXML,arguments.params) />

<!--- construct each bean, which puts it in the singleton cache --->
<cfloop collection="#variables.beanDefs#" item="i">
<cfset constructBean(i) />
</cfloop>

<!--- if a "helpers" bean is defined, inject it into all the helpers and beans --->
<cfif StructKeyExists(variables.singletonCache,"helpers")>

<cfset local.helpers = variables.singletonCache.helpers.getHelpers(this) />

<cfset local.injectedHelpers = StructNew() />

<cfloop collection="#local.helpers#" item="i">

<cfset local.bean = local.helpers[i] />

<!--- inject a function to set variables into the "variables" scope --->
<cfset local.bean.__injectVariable = variables.injectVariable />

<!--- inject the "helpers" scope --->
<cfset local.bean.__injectVariable("helpers",local.injectedHelpers) />

<!--- remove the temporary inject function --->
<cfset StructDelete(local.bean,"__injectVariable") />

<cfset local.injectedHelpers[i] = local.bean />

</cfloop>

</cfif>

<cfloop collection="#variables.singletonCache#" item="i">

<!--- set the bean from the cache --->
<cfset local.bean = variables.singletonCache[i] />

<!--- inject a function to set variables into the "variables" scope --->
<cfset local.bean.__injectVariable = variables.injectVariable />

<!--- inject the "helpers" scope if it's defined --->
<cfif StructKeyExists(variables.singletonCache,"helpers")>
<cfset local.bean.__injectVariable("helpers",local.injectedHelpers) />
</cfif>

<!--- get the bean's meta data to see if there are any "beans" or "includes" defined --->
<cfset local.metaData = getMetaData(local.bean) />

<!--- create a container for all the beans that will be injected --->
<cfset local.beans = StructNew() />

<!--- check to see if the bean needs to inject any beans --->
<cfif StructKeyExists(local.metaData,"beans")>

<!--- include each bean in the order specified --->
<cfloop list="#local.metaData.beans#" index="local.injectedBeanName">

<cfset local.injectedBeanName = trim(local.injectedBeanName) />

<!--- insert each bean into the local beans struct, which will be injected after the loop --->
<cfset local.beans[local.injectedBeanName] = variables.singletonCache[local.injectedBeanName] />

</cfloop>

</cfif>

<!--- inject the "beans" scope --->
<cfset local.bean.__injectVariable("beans",local.beans) />

<!--- check for any included beans --->
<cfif StructKeyExists(local.metaData,"includes")>

<!--- include each bean in the order specified --->
<cfloop list="#local.metaData.includes#" index="local.includedBeanName">

<cfset local.includedBeanName = trim(local.includedBeanName) />

<!--- get the bean from cache --->
<cfset local.includedBean = variables.singletonCache[local.includedBeanName] />

<!--- get all the public functions in the included bean and copy them to the main bean --->
<!--- even though private functions aren't copied, references to them from within copied functions will still work --->
<cfloop collection="#local.includedBean#" item="local.includedBeanVariableName">

<!--- only copy the function if it isn't defined yet --->
<cfif not StructKeyExists(local.bean,local.includedBeanVariableName)>

<!--- put the function into the object (the "this" scope) --->
<cfset local.bean[local.includedBeanVariableName] = local.includedBean[local.includedBeanVariableName] />

<!--- set the function into the variables scope, otherwise would need to reference the function as this.fn() --->
<cfset local.bean.__injectVariable(local.includedBeanVariableName,local.includedBean[local.includedBeanVariableName]) />

</cfif>

</cfloop>

</cfloop>

</cfif>

<!--- remove the temporary inject function --->
<cfset StructDelete(local.bean,"__injectVariable") />

</cfloop>

<cfreturn this />

</cffunction>

<!------>

<cffunction name="injectVariable" access="public" output="false" returntype="void" hint="I get injected into a bean in order to set variables">
<cfargument name="key" required="true" />
<cfargument name="value" required="true" />

<cfset variables[arguments.key] = arguments.value />

<cfreturn />

</cffunction>

<!------>

</cfcomponent>

Monday, December 22, 2008

Creating a Model-Glue-esque Bean Factory

I've been playing around with Model-Glue 3 lately and found that there are some really nice features that eliminate a lot of repetitive code, such as automatically injecting helpers and beans into a Controller without having to create getters and setters.

One problem I had with these new features is that they're only available to Model-Glue controllers (obviously) and I want to use them in my model and service layer. And so it began...

My first implementation required each object to extend a base object, which worked, but didn't feel quite right; there was still too much repetitive code. Plus I don't really like extending objects directly since it binds each object to a single implementation of the extended object. I'll get into that a little later.

After playing around with things a little more, I found a way that still works, is a lot cleaner, feels a lot better, and has an added bonus.

The first thing I did was create a custom bean factory that extends Brian Kotek’s DynamicXmlBeanFactory extension for ColdSpring (http://coldspringutils.riaforge.org/) and overrides the getBean method.

My bean factory is created onApplicationStart() like this:


<cfset application.beanFactory = createObject("component", "coldspring.beanutils.DynamicBeanFactory").init("config/coldspring/beans.xml", application.config) />


My bean factory looks like this:


<cfcomponent output="false" extends="coldspring.beanutils.DynamicXMLBeanFactory">

<!------>

<cffunction name="init" access="public" output="false" returntype="any">
<cfargument name="pathToColdspringXML" required="true" type="string" />
<cfargument name="params" required="false" default="" />

<cfif not isStruct(arguments.params)>
<cfset arguments.params = StructNew() />
</cfif>

<cfset super.init() />
<cfset loadBeansFromDynamicXmlFile(arguments.pathToColdspringXML,arguments.params) />

<cfreturn this />

</cffunction>

<!------>

<cffunction name="getBean" access="public" output="false" returntype="any">
<cfargument name="beanName" required="true" />

<cfset var local = StructNew() />

<!--- get the bean from coldspring --->
<cfset local.bean = super.getBean(arguments.beanName) />

<!--- inject the bean with helpers, beans, includes, etc... --->
<cfinclude template="DynamicBeanFactoryMixin.cfm" />

<cfreturn local.bean />

</cffunction>

<!------>

<cffunction name="injectVariable" access="public" output="false" returntype="void" hint="I get copied into beans in order to set variables">
<cfargument name="key" required="true" />
<cfargument name="value" required="true" />

<cfset variables[arguments.key] = arguments.value />

<cfreturn />

</cffunction>

<!------>

</cfcomponent>


You'll notice my getBean method includes DynamicBeanFactoryMixin.cfm, which is responsible for injecting additional logic into my bean. I'm not sold on using a .cfm include, but I wanted to re-use my code in another component that I've been working on and I didn't want to duplicate my efforts. I figure I can always refactor later, but wanted to get the base functionality down.

The DynamicBeanFactoryMixin looks like this:


<!--- inject a function to set variables into the "variables" scope --->
<cfif StructKeyExists(variables,"injectVariable")>
<cfset local.bean.injectVariable = variables.injectVariable />
<cfelseif StructKeyExists(this,"injectVariable")>
<cfset local.bean.injectVariable = this.injectVariable />
</cfif>

<cfif StructKeyExists(variables,"helpers")>

<!--- if the helpers are already defined in the current bean, set them into the target --->
<cfset local.bean.injectVariable("helpers",variables.helpers) />

<cfelse>

<!--- inject the helpers and create the "helpers" scope --->
<cfset local.bean.injectVariable("helpers",super.getBean("helpers").getHelpers()) />

</cfif>

<cfset local.metaData = getMetaData(local.bean) />

<cfif StructKeyExists(local.metaData,"includes")>

<!--- include each bean in the order specified --->
<cfloop list="#local.metaData.includes#" index="local.includedBeanName">

<cfset local.includedBeanName = trim(local.includedBeanName) />

<cfif StructKeyExists(variables,"getBean")>
<cfset local.includedBean = getBean(local.includedBeanName) />
<cfelse>
<cfset local.includedBean = application.beanFactory.getBean(local.includedBeanName) />
</cfif>

<!--- get all the public functions in the included bean and copy them to the main bean --->
<!--- even though private functions aren't copied, references to them from within copied functions will still work --->
<cfloop collection="#local.includedBean#" item="local.includedBeanVariableName">

<!--- only copy the function if it isn't defined yet --->
<cfif not StructKeyExists(local.bean,local.includedBeanVariableName)>

<!--- put the function into the object (the "this" scope) --->
<cfset local.bean[local.includedBeanVariableName] = local.includedBean[local.includedBeanVariableName] />

<!--- set the function into the variables scope, otherwise would need to reference the function as this.fn() --->
<cfset local.bean.injectVariable(local.includedBeanVariableName,local.includedBean[local.includedBeanVariableName]) />

</cfif>

</cfloop>

</cfloop>

</cfif>

<!--- create a container for all the beans that will be injected --->
<cfset local.beans = StructNew() />

<!--- check to see if the bean needs to inject any beans --->
<cfif StructKeyExists(local.metaData,"beans")>

<!--- include each bean in the order specified --->
<cfloop list="#local.metaData.beans#" index="local.injectedBeanName">

<cfset local.injectedBeanName = trim(local.injectedBeanName) />

<!--- if getBean is defined, use it --->
<cfif StructKeyExists(variables,"getBean")>
<cfset local.injectedBean = getBean(local.injectedBeanName) />
<cfelse>
<cfset local.injectedBean = application.beanFactory.getBean(local.injectedBeanName) />
</cfif>

<!--- insert each bean into the local beans struct, which will be injected after the loop --->
<cfset local.beans[local.injectedBeanName] = local.injectedBean />

</cfloop>

</cfif>

<!--- inject the beans --->
<cfset local.bean.injectVariable("beans",local.beans) />


I'll try to describe what's going on here.

First, I copy a reference to an "injectVariable" function into the bean that allows me to inject variables (helpers, beans, etc...) into the variables scope of the target bean.

Next, I create the "helpers" scope inside the bean by using a HelperInjector extension for ColdSpring that I wrote. Here's how I define my helpers in ColdSpring:


<bean id="helpers" class="coldspring.beanutils.HelperInjector">
<property name="directories">
<list>
<value>/clipboard/app/helpers/</value>
</list>
</property>
<property name="files">
<map>
<entry key="date"><value>/clipboard/app/helpers/date.cfc</value></entry>
</map>
</property>
</bean>


Helpers can be passed in as a list (array) of directories or a map (struct) of individual CFCs. This is a little different than the viewMappings node in Model-Glue, but I think it's a little more flexible since you can include, exclude, and override specific components.

In case you're curious, the HelperInjector extension looks like this:


<cfcomponent name="HelperInjector" output="false">

<!------>

<cffunction name="init" access="public" output="false" returntype="any">

<cfset variables.instance = StructNew() />

<cfset setDirectories(ArrayNew(1)) />
<cfset setFiles(StructNew()) />

<cfreturn this />

</cffunction>

<!------>

<cffunction name="setDirectories" access="public" output="false" returntype="void">
<cfargument name="directories" required="true" />

<cfif isArray(arguments.directories)>
<cfset variables.instance.directories = arguments.directories />
</cfif>

</cffunction>

<!------>

<cffunction name="getDirectories" access="public" output="false" returntype="any">
<cfreturn variables.instance.directories />
</cffunction>

<!------>

<cffunction name="setFiles" access="public" output="false" returntype="void">
<cfargument name="files" required="true" />

<cfif isStruct(arguments.files)>
<cfset variables.instance.files = arguments.files />
</cfif>

</cffunction>

<!------>

<cffunction name="getFiles" access="public" output="false" returntype="any">
<cfreturn variables.instance.files />
</cffunction>

<!------>

<cffunction name="getHelpers" access="public" output="false" returntype="any">

<cfif not StructKeyExists(variables.instance,"helpers")>
<cfset setHelpers() />
</cfif>

<cfreturn variables.instance.helpers />

</cffunction>

<!------>

<cffunction name="setHelpers" access="public" output="false" returntype="any">

<cfset var directories = getDirectories() />
<cfset var directory = "" />
<cfset var files = "" />
<cfset var file = "" />
<cfset var i = "" />

<cfset variables.instance.helpers = StructNew() />

<cfif ArrayLen(directories) neq 0>

<cfloop from="1" to="#ArrayLen(directories)#" index="i">

<cfset directory = directories[i] />

<cfdirectory action="list" directory="#expandPath(directory)#" name="files">

<cfloop query="files">
<cfif listLast(files.name,".") eq "cfc">
<cfset setHelper(listFirst(files.name,"."),injectComponent(directory & "/" & files.name)) />
</cfif>
</cfloop>

</cfloop>

</cfif>

<cfset files = getFiles() />

<cfloop collection="#files#" item="i">

<cfif listLast(files[i],".") eq "cfc">
<cfset setHelper(i,injectComponent(files[i])) />
</cfif>

</cfloop>

</cffunction>

<!------>

<cffunction name="setHelper" access="private" output="false" returntype="any">
<cfargument name="key" required="true" />
<cfargument name="value" required="true" />

<cfset variables.instance.helpers[arguments.key] = arguments.value />

<cfreturn />

</cffunction>

<!------>

<cffunction name="injectComponent" access="private" output="false" returntype="any">
<cfargument name="path" required="true" />

<cfset var helperPath = expandPath(arguments.path) />
<cfset var helperFileName = listFirst(getFileFromPath(helperPath), ".") />
<cfset var componentName = replaceNoCase(arguments.path, "/", ".", "all") />
<cfset var instance = "" />

<cfif left(componentName, 1) eq ".">
<cfset componentName = right(componentName, len(componentName) - 1) />
</cfif>

<cfset componentName = listDeleteAt(componentName, listLen(componentName, "."), ".") />

<cfset instance = createObject("component", componentName) />

<cfreturn instance />

</cffunction>

<!------>

</cfcomponent>


Next, I check the bean's metadata for an "includes" attribute, which is similar to the "extends" attribute but with a twist.

When you extend a component, you must specify the full path to the CFC, such as extends="coldspring.beanutils.DynamicXMLBeanFactory", which directly binds your component to the implementation of another component, which doesn't lend itself towards loose coupling. Plus, you can only extend a single component.

With the "includes" attribute, you can specify a comma-separated list of ColdSpring beans that you want to extend. That way, the dependencies are still managed in ColdSpring. If you decide to switch implementations of a component, you only have to update the class path in your bean definition to point to a different CFC; the rest of your code remains unchanged. It's almost like extending an interface. Also, it allows you to extend multiple beans, which could come in handy and help keep your class files smaller.

I've been using the "includes" attribute in my DAOs and beans in order to include generic functionality, such as creating a new instance of a bean or creating implicit getters and setters by using onMissingMethod(). Doing this allows my beans to be a simple list of properties like this:


<cfcomponent name="Product" table="products" includes="bean" output="false">

<cfproperty name="id" type="string" default="" />
<cfproperty name="name" type="string" default="" />
<cfproperty name="abbrev" type="string" default="" />

</cfcomponent>


Hopefully that makes sense. I'm still working on a DynamicTransientFactory extension and DynamicBean extension, so I'll go into more details in a future post. I just thought it might explain how the "includes" attribute works better if I provided an example.

Finally, I create the "beans" scope inside the target bean, which allows me to specify which beans I need in the component's metadata without having to create getters and setters for each injected bean. This also keeps my ColdSpring XML file smaller since I don't need to specify all the dependencies between beans. I tried keeping the functionality as similar as possible to the Model-Glue 3 implementation, since I really liked how that worked.

All in all, I'm quite happy with the way things have turned out so far. I had a little trouble with the .cfm mixin and getting it to work with the "includes" attribute, so I had to hack a couple things like referencing application.beanFactory directly and requiring a "helpers" bean to be defined. But when it came down to it, I decided to go with convention over configuration.

I tried commenting the code where I could, so hopefully it makes sense. I haven't fully tested everything yet, but it seems to work so far. Also, I should note the code requires ColdFusion 8.

UPDATE: I re-worked the bean factory.

Tuesday, December 16, 2008

Dynamic config variables in Model-Glue

I've been working on a project at work called Clipboard that tracks RFP text by product and category. I recently updated the application to use Model-Glue 3 and ColdSpring, but was having trouble managing the Model-Glue configuration settings between my development environment and our beta environment. I'd like to deploy the project from one environment to the next without having to change any XML, but didn't quite know how to use dynamic variables inside the Model-Glue XML file. I finally solved the problem using a combination of techniques.

For starters, I'm running ColdFusion 8 and my project setup looks like this:



I’m using the Environment Config project (http://environmentconfig.riaforge.org/) to manage my variables between environments. This XML file returns a struct of variables depending on the environment you’re currently in, which can then be passed into ColdSpring as a set of default properties.

I’m also using Brian Kotek’s DynamicXmlBeanFactory extension for ColdSpring (http://coldspringutils.riaforge.org/) which extends the standard DefaultXMLBeanFactory but allows the ability to replace dynamic properties anywhere in the XML file, as well as in imported XML files.

By combining the Environment Config with the DynamicXmlBeanFactory, I’m able to pass environment-specific variables into ColdSpring and have ColdSpring evaluate them at runtime.

My onApplicationStart() method looks like this:

<cffunction name="onApplicationStart" output="false">

<cfset this.mappings["clipboard"] = expandPath(".") />
<cfset this.mappings["modelglue"] = expandPath("../modelglue/") />
<cfset this.mappings["coldspring"] = expandPath("../coldspring/") />

<cfset application.config = createObject("component","clipboard.config.environment.environment").init("config/environment/environment.xml.cfm").getEnvironmentByUrl(cgi.server_name) />

<cfset application.config.siteroot = getDirectoryFromPath(getMetaData().path) />

<cfset application.beanFactory = createObject("component","coldspring.beanutils.DynamicXmlBeanFactory").init() />
<cfset application.beanFactory.loadBeansFromDynamicXmlFile("config/coldspring/beans.xml",application.config) />

</cffunction>
Pretty basic stuff. I'm create mappings for my project, ColdSpring, and Model-Glue, create a struct containing my environment's configuration settings, create a dynamic file path to my project (for uploads), then create my bean factory.

Now on to Model-Glue. Model-Glue uses ColdSpring to manage its internal configuration. With each Model-Glue project, there’s a modelglue.modelGlueConfiguration bean that holds the configuration settings for Model-Glue, such as whether to display debug information, the default event, and paths to your views. It also has a property called “reload” that tells your application whether it should load Model-Glue with each request. This is something you’d want to do in a development environment, but not production as it may take a second to fully load Model-Glue.

Unfortunately, you aren’t able to use dynamic variables inside the Model-Glue XML config file by default, so you’re stuck with having to either manually change the “reload” variable or create an ANT script to modify the XML file with each deployment. Yuck.

Fortunately, there’s a nice little trick I found that will allow you to use dynamic variables. There are a couple lines in the Model-Glue index.cfm file that are commented out by default. The ones we care about are ModelGlue_LOCAL_COLDSPRING_PATH, which specifies where the Model-Glue config file is located, and ModelGlue_PARENT_BEAN_FACTORY, which can be used to specify a parent bean factory to Model-Glue.

Simply put, Model-Glue will look for beans inside its bean factory first (created using ModelGlue_LOCAL_COLDSPRING_PATH), and if it can’t find a bean there, it will check the parent bean factory. Knowing this, we can move the modelglue.modelGlueConfiguration bean out of the Model-Glue bean factory and into the parent bean factory, where we can use the combination of Environment Config and DynamicXmlBeanFactory to use dynamic variables.

Here’s what my index.cfm file looks like:
<cfsilent>
<cfset ModelGlue_LOCAL_COLDSPRING_PATH = "config\modelglue\empty.xml" />
<cfset ModelGlue_PARENT_BEAN_FACTORY = application.beanFactory />
</cfsilent><cfinclude template="../modelglue/gesture/modelglue.cfm" />
You’ll notice my ModelGlue_LOCAL_COLDSPRING_PATH is set to empty.xml, which contains just <beans></beans> so Model-Glue won’t error out.

Here’s what config/environment/environment.xml.cfm looks like:
<?xml version="1.0" encoding="UTF-8"?>
<environments>
<default>
<config>
<property name="development">false</property>
<property name="webpath">/clipboard</property>
</config>
</default>
<environment id="development">
<patterns>
<pattern>localhost</pattern>
</patterns>
<config>
<property name="datasource">Clipboard_Dev</property>
<property name="development">true</property>
</config>
</environment>
<environment id="beta">
<patterns>
<pattern>betasite.com</pattern>
</patterns>
<config>
<property name="datasource">Clipboard_Beta</property>
</config>
</environment>
</environments>
Notice I have a property named “development” that is set to “false” by default, but “true” inside my “development” environment. When I load up my application, I use this XML to create an application.config variable, which is then passed into my dynamic ColdSpring bean factory.

Here’s what my config/coldspring/modelglue.xml file looks like, which now needs to be imported into my beans.xml file as part of Model-Glue's parent bean factory:
<beans>

<!-- This is your Model-Glue configuration -->
<bean id="modelglue.modelGlueConfiguration" class="ModelGlue.gesture.configuration.ModelGlueConfiguration">

<!-- Be sure to change these to false when you go to production! -->
<property name="reload"><value>${development}</value></property>
<property name="debug"><value>false</value></property>

<!-- Default event-handler -->
<property name="defaultEvent"><value>index</value></property>
<property name="missingEvent"><value>missing</value></property>
<property name="defaultExceptionHandler"><value>error</value></property>

<!-- Controls reloading -->
<property name="reloadPassword"><value>true</value></property>
<property name="reloadKey"><value>init</value></property>

<!-- Where to find necessary files -->
<property name="configurationPath"><value>config/modelglue/modelglue.xml</value></property>
<property name="applicationPath"><value>${webpath}</value></property>
<property name="viewMappings"><value>${webpath}/app/views</value></property>
<property name="helperMappings"><value>${webpath}/app/helpers</value></property>

<!-- Generate unknown events when in development mode? (reload=false) -->
<property name="generationEnabled"><value>false</value></property>
</bean>

</beans>
Notice my “reload” property is now set to ${development}, which gets evaluated to true/false when the bean factory is created in my Application.cfc. I’ve also made a couple other variables dynamic using my ${webpath} variable, which could change from one environment to another.