Wednesday, October 7, 2009

Generic DAO Factory for ColdFusion 9

Joe Rinehart has a nice post on creating a generic DAO wrapper for Hibernate in ColdFusion 9. For example, if you wanted to create a UserDAO using the GenericDAO, you could set this up in ColdSpring like:


<beans>

<bean id="userDAO" class="GenericDAO" />

<bean id="userService" class="UserService">
<property name="dao">
<ref bean="userDAO" />
</property>
</bean>

</beans>


Then to get a User record from your UserDAO inside your UserService, your code might look like:


component {
public any function get(string id) {
return getDAO().get("User",id);
}
}


While this works, it seems a little weird having to pass in the name of the entity to the DAO. Ideally the code would simply be:


component {
public any function get(string id) {
return getDAO().get(id);
}
}


I decided to play around a little bith with the code and was able to accomplish this with the help of ColdSpring. Rather than having my userDAO point directly to the GenericDAO class, I've created a DAOFactory instead to generate an instance of a DAO. Here's what my config looks like:


<beans>

<bean id="userDAO" factory-bean="daoFactory" factory-method="getUserDAO" />

<bean id="daoFactory" class="DAOFactory">
<constructor-arg name="class">
<value>GenericDAO</value>
</constructor-arg>
</bean>

<bean id="userService" class="UserService">
<property name="dao">
<ref bean="userDAO" />
</property>
</bean>

</beans>


Here's what my DAOFactory.cfc looks like:


component extends="SingletonFactory" {

public any function init(string class) {
return super.init("dao",class);
}

public any function onMissingMethod(string missingMethodName, struct missingMethodArguments) {

var object = getObject();

object.entityName = getEntityName(missingMethodName);

return object;

}

}


Here's what SingletonFactory.cfc looks like, which the DAOFactory extends:


component {

public any function init(string type, string class) {
variables.type = type;
variables.class = class;

return this;
}

private any function getObject() {
return createObject("component",variables.class);
}

private string function getEntityName(string methodName) {
methodName = replaceNoCase(methodName,"get","");

return left(methodName,len(methodName)-len(variables.type));
}

public any function onMissingMethod(string missingMethodName, struct missingMethodArguments) {
return getObject();
}

}


Finally, here's what my GenericDAO.cfc looks like:


component {

this.entityName = "";

public array function list(string criteria, array params) {

var hql = "from " & this.entityName;

if (structKeyExists(arguments,"criteria")) {
hql = hql & " where " & criteria;
}

if (structKeyExists(arguments,"params")) {
return ormExecuteQuery(hql,params);
}

return ormExecuteQuery(hql);
}

public any function get(string id) {
return entityLoad(this.entityName, id);
}

public void function save(any entity) {
entitySave(entity);
}

public void function delete(any entity) {
entityDelete(entity);
}

}


Taking this one step further, you could use the same idea and create really simple Services using a ServiceFactory.

Here's what your coldspring.xml might look like:


<beans>

<bean id="userDAO" factory-bean="daoFactory" factory-method="getUserDAO" />

<bean id="daoFactory" class="DAOFactory">
<constructor-arg name="class">
<value>GenericDAO</value>
</constructor-arg>
</bean>

<bean id="userService" factory-bean="serviceFactory" factory-method="getUserService">
<property name="dao">
<ref bean="userDAO" />
</property>
</bean>

<bean id="serviceFactory" class="ServiceFactory">
<constructor-arg name="class">
<value>GenericService</value>
</constructor-arg>
</bean>

</beans>


Here's what the ServiceFactory.cfc looks like:


component extends="SingletonFactory" {

public any function init(string class) {
return super.init("service",class);
}

}


And here's GenericService.cfc:


component {

public void function setDAO(any dao) {
variables.dao = arguments.dao;
}

private string function getDAO() {
return variables.dao;
}

public array function list(string criteria, array params) {
if(!StructKeyExists(arguments,"criteria")){
arguments.criteria = "";
}
if(!StructKeyExists(arguments,"params")){
arguments.params = [];
}

return getDAO().list(criteria,params);
}

public any function get(string id) {
return getDAO().get(id);
}

public any function save(any entity) {
return getDAO().save(entity);
}

public any function delete(any entity) {
return getDAO().delete(entity);
}

}


I took most of this code from Joe's example, but just shrunk it down a little bit for the sake of the post. Also, I haven't hooked this up to a database to test it, so there might be some syntax errors. Finally, I wrote this during a rather boring session at MAX, so I'll admit there's a lot of rambling going on. Hopefully you can kind of follow the logic.

Sunday, October 4, 2009

Adobe MAX 2009

Heading to LA today to attend Adobe MAX for the 2nd time. Hopefully it's as inspiring for me as it was last year.

UPDATE: There's been some speculation that Adobe will release ColdFusion 9 at MAX. I guess there's no more need to speculate... http://www.adobe.com/products/coldfusion/buy/

Thursday, October 1, 2009

Implicit Getters and Setters

While the addition of implicit getters and setters for properties in ColdFusion is really nice, I'm still a little jealous of the how they're handled in Groovy.

Take the typical User object.


<cfcomponent name="User">

<cfproperty name="firstName" />
<cfproperty name="lastName" />

</cfcomponent>


In ColdFusion, you would interact with this object like such:


<cfset user = new User() />
<cfset user.setFirstName("Tony") />
<cfset user.setLastName("Nelson") />

<cfoutput>
Hello, my name is #user.getFirstName()# #user.getLastName()#
</cfoutput>


Pretty basic. Now how would that look if ColdFusion had implicit getters and setters like Groovy:


<cfset user = new User() />
<cfset user.firstName = "Tony" />
<cfset user.lastName = "Nelson" />

<cfoutput>
Hello, my name is #user.firstName# #user.lastName#
</cfoutput>


Much cleaner. Now before you say it's just accessing the properties directly (as if they were stored in the this scope), there's more to it than that. When you're accessing the property, the call is still being routed through the implicit getters and setters. This means you can still add your own custom logic to the properties by overriding their methods.

To demonstrate, let's add a 3rd property to the User component, fullName, and add our own getter.


<cfcomponent name="User">

<cfproperty name="firstName" />
<cfproperty name="lastName" />
<cfproperty name="fullName" />

<cffunction name="getFullName">
<cfreturn getFirstName() & " " & getLastName() />
</cffunction>

</cfcomponent>


Now let's update our code:


<cfset user = new User() />
<cfset user.firstName = "Tony" />
<cfset user.lastName = "Nelson" />

<cfoutput>
Hello, my name is #user.fullName#
</cfoutput>


Even though it looks like we're accessing the fullName property directly, it will still be routed through getFullName(), which should output "Tony Nelson".

I've gone back and forth on whether or not this is a good thing. On one hand, you're calling a method even though it doesn't look like it. On the other, it's a lot cleaner. Some people may argue that access to properties should remain hidden behind Accessors (getters) and Mutators (setters), but in truth they still are.

Besides, if our goal is to have rich business objects that have both properties and behavior, shouldn't we be able to treat the properties like they were just simply properties and nothing more? To me, having a getter implies there's some additional behavior required in order to retrieve a property, which in most cases there isn't. What are we really gaining by masking the call to a property behind a method?

Monday, September 21, 2009

ColdFusion and Frameworks

If you haven't read it yet, I would suggest reading this post by Matt Woodward. Very good points.

Honestly, if ColdFusion has been around for almost 15 years, why don't we have anything that comes even remotely close to Ruby on Rails or Grails? You could make the case that ColdFusion on Wheels and ColdBox are similar in their implementations, but then there are people that swear by Model-Glue, Mach-II, and Fusebox... and then there's Edmund, FW/1, onTap, etc... and those are just the front controller frameworks. If you want the total package, you'll have to wire in ColdSpring, LightWire, Transfer, Reactor, MXUnit, cfcUnit, cfSpec, etc...

Unfortunately, the ColdFusion framework community is so fragmented that I don't think there will ever be a widely-accepted, well-designed, dominant framework - a framework that just works - a framework that some developers might actually confuse with a language, like what happens with Rails(Ruby) and Grails(Groovy). Right now there are just too many smart ColdFusion developers all working on their own versions of the same thing.

Tuesday, September 1, 2009

Annotation-based Dependency Injection using ColdSpring

ColdSpring is by far the most essential tool I need for building ColdFusion applications. However, I'm not a huge fan of writing a ton of XML and I'm certainly not a fan of writing a lot of getters and setters inside my components.

To help ease that pain, I borrowed a feature from Model-Glue 3 and extended ColdSpring to create the "beans" scope.

Little did I realize ColdSpring can already handle a lot of what I wanted to do without having to modify ColdSpring itself simply by creating a factory post processor. To automatically inject the "beans" scope into my ColdSpring-managed beans, I created the following BeansScopeFactoryPostProcessor.cfc:


<cfcomponent>

<cffunction name="postProcessBeanFactory" access="public" returntype="void">

<cfset var local = {} />

<cfset local.beanDefs = getConcreteBeanClasses() />

<cfloop collection="#local.beanDefs#" item="local.beanName">

<cfset local.beanList = getBeanScopeList(local.beanDefs[local.beanName]) />

<cfif local.beanList neq "">

<cfset local.bean = getBeanFactory().getBean(local.beanName) />

<cfset injectBeansScope(local.bean,local.beanList) />

</cfif>

</cfloop>

</cffunction>

<cffunction name="getConcreteBeanClasses" access="private" returntype="struct">

<cfset var local = {} />

<cfset local.classes = {} />

<cfset local.beanDefs = getBeanFactory().getBeanDefinitionList() />

<cfloop collection="#local.beanDefs#" item="local.beanName">

<cfif not local.beanDefs[local.beanName].isAbstract()>
<cfset local.classes[local.beanName] = local.beanDefs[local.beanName].getBeanClass() />
</cfif>

</cfloop>

<cfreturn local.classes />

</cffunction>

<cffunction name="injectBeansScope" access="private" returntype="void">
<cfargument name="bean" required="true" />
<cfargument name="beanList" required="true" />

<cfset var local = {} />

<cfif isCFC(arguments.bean)>

<cfset local.beans = {} />

<cfloop list="#arguments.beanList#" index="local.beanName">
<cfset local.beans[local.beanName] = getBeanFactory().getBean(local.beanName) />
</cfloop>

<cfset arguments.bean.__setVariable = variables.__setVariable />

<cfset arguments.bean.__setVariable("beans",local.beans) />

<cfset StructDelete(arguments.bean,"__setVariable") />

</cfif>

</cffunction>

<cffunction name="getBeanScopeList" access="private" returntype="string">
<cfargument name="bean" required="true" />

<cfset var local = {} />

<cfset local.metaData = getComponentMetaData(arguments.bean) />

<cfset local.beanList = "" />

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

<cfloop list="#local.metaData.beans#" index="local.beanName">
<cfset local.beanList = ListAppend(local.beanList,local.beanName) />
</cfloop>

</cfif>

<cfset local.extendedMetaData = local.metaData />

<cfloop condition="StructKeyExists(local.extendedMetaData,'extends')">

<cfif StructKeyExists(local.extendedMetaData,"beans")>

<cfloop list="#local.extendedMetaData.beans#" index="local.beanName">

<cfif not listFindNoCase(local.beanList,local.beanName)>
<cfset local.beanList = ListAppend(local.beanList,local.beanName) />
</cfif>

</cfloop>

</cfif>

<cfset local.extendedMetaData = local.extendedMetaData.extends />

</cfloop>

<cfreturn local.beanList />

</cffunction>

<cffunction name="setBeanFactory" access="public" returntype="void">
<cfargument name="beanFactory" required="true" type="coldspring.beans.BeanFactory" />

<cfset variables.beanFactory = arguments.beanFactory />

</cffunction>

<cffunction name="getBeanFactory" access="private" returntype="any">

<cfreturn variables.beanFactory />

</cffunction>

<cffunction name="__setVariable" access="public" returntype="void">
<cfargument name="key" required="true" />
<cfargument name="value" required="true" />

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

</cffunction>

<cffunction name="isCFC" access="private" returntype="boolean">
<cfargument name="object" required="true" />

<cfset var metaData = getMetaData(arguments.object) />

<cfreturn isObject(arguments.object) and structKeyExists(metaData,"type") and metaData.type eq "component" />

</cffunction>

</cfcomponent>


Long story short, it looks at all the beans defined in ColdSpring, checks their metadata for a "beans" attribute inside the cfcomponent tag, and automatically injects the requested beans into the component inside a variables.beans struct.

To get ColdSpring to process my beans, I define the factory post processor as such:


<bean id="beanInjector" class="test.BeansScopeFactoryPostProcessor" factory-post-processor="true" />


For those unaware of how factory post-processors work, ColdSpring will look for any beans where factory-post-processor="true" and automatically call postProcessBeanFactory() on those beans once the bean factory has been initialized.

To keep the code relatively small in this post, I removed any comments. Hopefully it's still somewhat straight-forward and easy to follow.