Thursday, October 29, 2009

ColdFusion + Spring?

After the recent additions of Hibernate, Ehcache and Solr (Lucene) to ColdFusion 9, it's starting to become really clear what ColdFusion does best: provide a clean and easy interface to powerful, open-source Java libraries.

With that being said, I would vote for Spring to be included next. While ColdSpring is great, why not go straight to the source? Not only would it allow for a tighter, seemless integration between ColdFusion and Java objects, but it would also leverage the power and performance of Java for our IoC and AOP needs. On top of that, Spring provides other services, such as integration with BlazeDS for Flex powered applications.

Just think, you could configure Spring inside your Application.cfc using this.iocEnabled and this.iocSettings.iocConfig. And if you didn't want to use XML, you could define your beans using annotations in your components (component beanid="userService" scope="singleton") and wire in your dependencies through properties (property name="userService" autowire="true";). It could look pretty similar to the way Hibernate was implemented.

Granted trying to integrate Spring into ColdFusion is probably easier said than done, but if they got it to work with Hibernate, I'm sure it could be done with Spring too.

Thursday, October 22, 2009

ColdFusion 9 ORM Custom Naming Strategy

I'm pretty particular when it comes to naming conventions. If I'm working with code and objects, I prefer properties to be in written in camel case. However, I like database fields to be written in proper case, with all spaces replaced with underscores. For example, I would have user.id and user.firstName in my code, but User.ID and User.First_Name in my database.

Hibernate is able to automatically generate your database schema for you based on your *.hbmxml files, which can be automatically generated for you by ColdFusion after creating your Entity CFCs. While this can be a really handy feature for development, I was a little annoyed when Hibernate generated all of my columns in camel case to match my properties.

Then I found a really nice post by an Adobe engineer that talked about using a custom naming strategy, which is a component that Hibernate can use to generate your database table and column names.

In your Application.cfc, you can set this.ormSettings.namingStrategy to a CFC that implements the "cfide.orm.INamingStrategy" interface.

cfide.orm.INamingStrategy

component {

public string function getTableName(string tableName) {
}

public string function getColumnName(string columnName) {
}

}


With that in mind, here's my UncamelizeStrategy.cfc

component implements="cfide.orm.INamingStrategy" {

public string function getTableName(string tableName) {
return uncamelize(tableName);
}

public string function getColumnName(string columnName) {
return uncamelize(columnName);
}

private string function uncamelize(string name) {

var array = [];

for (var i=1;i <= len(name);i++) {
var char = mid(name,i,1);

if(i == 1) {
arrayAppend(array,ucase(char));
}
else {
if(reFind("[A-Z]",char)) {
arrayAppend(array,"_" & char);
}
else {
arrayAppend(array,lcase(char));
}
}
}

var newName = arrayToList(array,"");

if(newName == "Id") {
newName = "ID";
}
else if(right(newName,3) == "_Id") {
newName = left(newName,len(newName)-3) & "_ID";
}

return newName;

}

}


I'm sure there's probably an easier way to write this, perhaps using a regular expression, but I chose the brute-force approach of looping over each character. Short and simple.

Tuesday, October 20, 2009

Hibernate Data Filters

In a lot of the applications I write, I prefer to use soft deletes rather than hard deletes to ensure that data can easily and quickly be restored if (and when) a user accidentally deletes something. However, trying to implement soft deletes is typically easier said than done. For example, if you wanted a query of all the non-deleted products, your query might look like:


<cfquery name="getProducts">
select *
from product
where isDeleted = 0
</cfquery>


To convert this to ColdFusion 9 ORM, you could write an HQL statement that looks like:


<cfset products = ORMExecuteQuery("from Product where isDeleted = 0") />


Obviously it isn't ideal replacing a simple entityLoad("Product") with HQL, but it becomes even worse when you consider having to recreate that logic across relationships. Yuck.

Introducing Hibernate Filters

"Hibernate3 has the ability to pre-define filter criteria and attach those filters at both a class level and a collection level. A filter criteria allows you to define a restriction clause similar to the existing "where" attribute available on the class and various collection elements. These filter conditions, however, can be parameterized. The application can then decide at runtime whether certain filters should be enabled and what their parameter values should be." Read more.

Long story short, you can define a filter that applies the isDeleted logic to your entities without actually touching your entities.

To accomplish this, I had my Product entity extend a base Entity in order to perform the save() and delete() methods, which are responsible for handling the soft deletes. After that, I updated my Product.hbmxml file to include the filter definition and add it to my Product entity. Finally, I updated Application.onRequestStart() to enable the softDeletes filter, which is disabled by default. Here's the sample code:

Application.cfc

component {

this.name = "sample";
this.datasource = "sample";
this.ormEnabled = true;
this.ormSettings.dbcreate = "update";
this.ormSettings.saveMapping = true;

public void function onRequestStart() {
if(structKeyExists(url,"init")) {
ormReload();
}
ORMGetSession().enableFilter("softDeletes");
}

}


Product.cfc

component persistent="true" extends="Entity" {

property id;
property name;

}


Entity.cfc

component accessors="true" {

property isDeleted;

public void function save() {
setIsDeleted(0);
entitySave(this);
}

public void function delete() {
setIsDeleted(1);
entitySave(this);
}

}


index.cfm

<cfset products = EntityLoad("Product") />

<cfdump var="#products#" />


Product.hbmxml

<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>

<class entity-name="Product" lazy="true" name="cfc:sample.Product" table="Product">
<id name="id" type="int">
<column length="10" name="id"/>
<generator class="native"/>
</id>
<property name="name" type="string">
<column name="name"/>
</property>
<property name="isDeleted" type="boolean">
<column name="isDeleted"/>
</property>
<filter name="softDeletes" condition="isDeleted = 0" />
</class>

<filter-def name="softDeletes" />

</hibernate-mapping>


The really nice part about this approach is that I've got soft deletes working without my Product entity being aware, since that logic is hidden in the base class, a config file, and a global template.

Monday, October 19, 2009

ColdFusion 9 Oddities

I ran into a couple interesting things when playing around with ColdFusion 9 today.

Oddity #1:
I was trying to create a really simple User entity using ORM.

User.cfc

<cfcomponent persistent="true">

<cfproperty name="id" fieldtype="id" generator="native" />
<cfproperty name="firstName" />
<cfproperty name="lastName" />
<cfproperty name="isDeleted" type="boolean" />
<cfproperty name="createdOn" type="date" />

</cfcomponent>


Here's what a dump of the User looks like:



I realized that I typically put isDeleted and createdOn on all of my entities, so I decided I would put them on a base class, Entity.cfc, that my User entity could extend. Here's the updated components:

User.cfc

<cfcomponent persistent="true" extends="Entity">

<cfproperty name="id" fieldtype="id" generator="native" />
<cfproperty name="firstName" />
<cfproperty name="lastName" />

</cfcomponent>


Entity.cfc

<cfcomponent>

<cfproperty name="isDeleted" type="boolean" />
<cfproperty name="createdOn" type="date" />

</cfcomponent>


However, when I went to look at my User, I noticed the extended properties were missing.



After a little while, I tried adding accessors="true" to my Entity base class. It seems slightly odd to need to add that since the parent object is persistent, but it gave me my getters and setters back.

Entity.cfc

<cfcomponent accessors="true">

<cfproperty name="isDeleted" type="boolean" />
<cfproperty name="createdOn" type="date" />

</cfcomponent>


And here's the result:



All good, right? Unfortunately not. I tried executing the following code:

index.cfm

<cfset user = EntityNew("User") />

<cfset user.setFirstName("Tony") />
<cfset user.setLastName("Nelson") />
<cfset user.setIsDeleted(0) />
<cfset user.setCreatedOn(now()) />

<cfset EntitySave(user) />


While this appeared to work in the interface. when I took a peak at the database, it didn't save my isDeleted or createdOn values. In fact, it didn't even create the columns in the database.

I decided to check out what Hibernate was doing by setting this.ormSettings.saveMapping = true in my Application.cfc. Here's the auto-generated User.hbmxml:

User.hbmxml

<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class entity-name="User" lazy="true" name="cfc:cat.User" table="`User`">
<id name="id" type="int">
<column name="id"/>
<generator class="native"/>
</id>
<property name="firstName" type="string">
<column name="firstName"/>
</property>
<property name="lastName" type="string">
<column name="lastName"/>
</property>
</class>
</hibernate-mapping>


Apparently Hibernate wasn't able to pick up any properties defined in my base Entity.cfc. To solve this, I simply manually added the properties to the hibernate mapping file like such:

User.hbmxml

<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class entity-name="User" lazy="true" name="cfc:cat.User" table="`User`">
<id name="id" type="int">
<column length="10" name="id"/>
<generator class="native"/>
</id>
<property name="firstName" type="string">
<column length="255" name="firstName"/>
</property>
<property name="lastName" type="string">
<column length="255" name="lastName"/>
</property>
<property name="isDeleted" type="boolean">
<column name="isDeleted"/>
</property>
<property name="createdOn" type="date">
<column name="createdOn"/>
</property>
</class>
</hibernate-mapping>


This seemed to do the trick and everything was happy once again. I guess I'll be using the mapping files more than I had originally planned.

Oddity #2:
It appears that using ColdFusion mixins inside CFCs no longer works in CF9. Take the following really basic example:

User.cfc

<cfcomponent>

<cfinclude template="mixin.cfm" />

</cfcomponent>


mixin.cfm

<cffunction name="sayHello">

<cfreturn "Hello, world" />

</cffunction>


I could then create a new User like so:

index.cfm

<cfset user = createObject("component","User") />

<cfdump var="#user#" />


If I were to run this code in CF8, I would get a User that looked like:



However in CF9, I get a completely empty User object:



Odd. Yeah it's not a very common use-case, but it's still something that can come in handy every now and then.

UPDATE: Apparently mixins still work, they just don't appear when you dump the component.

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?