Using The ColdSpring Dependency Injection Framework for ColdFusion

Java Development News:

Using The ColdSpring Dependency Injection Framework for ColdFusion

By Brian Kotek

01 Jan 2009 | TheServerSide.com

Introduction

This article introduces the ColdSpring dependency injection framework for ColdFusion. It explains what ColdSpring is, and how you can use it to solve common software development problems. It also highlights some additional capabilities that ColdSpring brings to the table, such as aspect-oriented programming and remote proxy generation.

Requirements

In order to make the most of this article, you need the following software and files:

ColdFusion 8 ( Enterprise or Developer version)

Prerequisite knowledge

A working knowledge of ColdFusion.

Singleton Management and Dependency Resolution

With the release of ColdFusion MX 6 in 2002, the server was migrated from C++ to Java. Along with this fundamental change, ColdFusion Components (CFCs) were introduced into the language. CFCs opened the door to object-oriented (OO) programming for ColdFusion developers. ColdFusion developers who formerly had little OO experience began to grapple with the fundamental OO design principles. In the process, a number of challenges were encountered that proved quite difficult to solve.

First, CFCs could be instantiated into shared scopes such as the application scope. This essentially provided an easy route to implement the Singleton pattern in ColdFusion. But it meant that developers needed to think much more carefully about concurrency issues and the approach used to instantiate these application-scoped components.

Second, once you adopt an OO design approach, you quickly run into the problem of handling dependencies between components. A large object model may have dozens of components that interact and depend on each other. The task of how to manage these dependencies becomes critical. The components must all be created in the right order, and dependent components must be supplied correctly. With a small number of components this isn't a huge issue. But as the number of components grows, this quickly becomes a major stumbling block.

Dave Ross and Chris Scott looked at this burgeoning issue and decided to do something about it. Having both worked with Java, they were aware that this problem had already been solved through the Spring framework. With this in mind, they decided to port a subset of the Spring's features into a ColdFusion implementation. The ColdSpring framework was born.

To be clear, ColdSpring as a framework is very different from the HTML Controller frameworks that exist for ColdFusion, such as Model-Glue, Mach-II, Fusebox, or ColdBox. ColdSpring will work with any of these frameworks or with no framework at all. Because it does its work within the Model layer of the Model-View-Controller design pattern, it can be used regardless of how the Controller and View are handled.

Let's focus on how ColdSpring addresses the two major problems I outlined above: Singleton management and dependency resolution. ColdSpring handles the creation and management of Singletons automatically. Setting this up is very easy. The framework uses an XML configuration file that follows the Spring Document Type Definition (DTD). By default, ColdSpring assumes that the components you are setting it up to manage are intended to be Singletons. Here is an example of a simple ColdSpring configuration file:

<! DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd" >
< beans >
< bean id= "userService" class= "myapp.components.UserService" />
< bean id = "userGateway" class = "myapp.components.UserGateway" />
< bean id = "configBean" class = "myapp.components.ConfigBean" />
</ beans >

The label "bean" comes from the Java world and simply describes an object that has getters and setters for its properties.

As you can see, the code defines three beans for ColdSpring to manage. It will automatically create these CFCs and store them as Singletons within its bean cache. Since most uses of ColdSpring involve creating it in the application scope, the beans in its bean cache are also kept in the application scope. Here's an example of creating the ColdSpring bean factory:

<cfset coldspringConfig = ExpandPath( 'config/coldspring.xml') />
<cfset application.beanFactory = CreateObject( 'component', 'coldspring.beans.DefaultXmlBeanFactory').init() />
<cfset application.beanFactory.loadBeans(coldspringConfig) />

You create the bean factory just like you would any other CFC, and then you call loadBeans(), passing in the location of the configuration file. ColdSpring takes care of the rest.

Now, whenever you need to obtain a reference to one of the beans that ColdSpring manages, you use the getBean() method, like this:

<cfset userService = application.beanFactory.getBean('userService') />

So ColdSpring will create and manage Singleton CFC instances without you having to do anything. OK, that's pretty cool. But what about the more serious problem of resolving dependencies in large object models? That is the real pain point that most developers run into.

ColdSpring's real power is its ability to manage dependencies between components. Let's look at the ColdSpring configuration file again, but with a small addition:

<! DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd" >
< beans default-autowire = "byName" >
< bean id = "userService" class = "myapp.components.UserService" />
< bean id = "userGateway" class = "myapp.components.UserGateway" />
< bean id = "configBean" class = "myapp.components.ConfigBean" />
</ beans >

You may notice the difference is the default-autowire XML attribute on the beans element. By setting this value to byName, something pretty amazing happens: ColdSpring will automatically try to resolve any dependencies between these CFCs.

How does it do this? It looks for setter methods in the components that match the names of other beans. For example, if the UserService CFC has a setUserGateway() method, ColdSpring will automatically call that setter and pass in the UserGateway CFC. Along the same lines, if the UserGateway CFC has a setConfigBean() method, the ConfigBean CFC will automatically be passed into the UserGateway by calling the setter.

For example, consider a UserService.cfc that looks like this:

<cfcomponent name= "User Service" hint= "I am an example User Service, which is how external things interact with my Model.">
<cffunction name= "init" access= "public" returntype= "any" hint= "Constructor.">
<cfreturn this />
</cffunction>
<cffunction name= "getUserGateway" access= "public" returntype= "any" output= "false" hint= "I return the UserGateway.">
<cfreturn variables.instance[ 'userGateway' ] />
</cffunction>
<cffunction name= "setUserGateway" access= "public" returntype= "void" output= "false" hint= "I set the UserGateway.">
<cfargument name= "userGateway" type= "any" required= "true" hint= "UserGateway" />
<cfset variables.instance[ 'userGateway' ] = arguments.userGateway />
</cffunction>
</cfcomponent>

When the ColdSpring bean factory is created, it will see the setUserGateway() method and automatically call it, passing in the UserGateway bean.

Now it may be more obvious why ColdSpring is called a "dependency injection" framework. You can see it injects dependent components into the necessary components automatically. Frameworks like Spring and ColdSpring are also referred to as inversion of control or IoC frameworks. Personally I don't like this name because it is rather unintuitive. To me, dependency injection (DI) describes the intent of the framework much more clearly, so I use the term DI.

There are immediate advantages to letting ColdSpring manage the dependencies in your model. You no longer have to worry about what order CFCs are created in, or what CFCs have to be passed into other CFCs. It will also handle circular dependencies (where CFC A depends on CFC B, but CFC B depends on CFC A), which can be rather tricky to resolve.

More from ColdSpring: Aspect-Oriented Programming

In addition to managing dependencies, ColdSpring offers a very useful aspect-oriented programming (AOP) capability. AOP is an approach for handling cross-cutting concerns that apply across many different parts of an application. One example is logging – developers typically embed logging calls all over their application in order to log appropriate data.

AOP takes a different approach. Instead of littering logging calls all over your codebase, you can define it in just one place. This place is typically called an Advice, and it is a bit of code that ColdSpring can automatically apply to method calls across many different components. In other words, you can apply logging to whatever methods you wish from one central point, without having to modify your original code at all.

Here is a LoggingAdvice that simply takes the method name and the arguments and logs them into a request-scoped variable:

<cfcomponent extends= "coldspring.aop.MethodInterceptor" output= "false" displayname= "LoggingAdvice" hint= "I advise service layer methods and apply logging.">
<cffunction name= "init" returntype= "any" output= "false" access= "public" hint= "Constructor">
<cfreturn this />
</cffunction>
<cffunction name= "invokeMethod" returntype= "any" access= "public" output= "false" hint= "">
<cfargument name= "methodInvocation" type= "coldspring.aop.MethodInvocation" required= "true" hint= "" />
<cfset var local = StructNew () />
<!--- Capture the arguments and method name being invoked. --->
<cfset local.logData = StructNew () />
<cfset local.logData.arguments = StructCopy (arguments.methodInvocation.getArguments()) />
<cfset local.logData.method = arguments.methodInvocation.getMethod().getMethodName() />
<cfset request.logData = local.logData />
<!--- Proceed with the method call to the underlying CFC. --->
<cfset local.result = arguments.methodInvocation.proceed() />
<!--- Return the result of the method call. --->
<cfreturn local.result />
</cffunction>
</cfcomponent>

To have this advice applied to your CFCs, you define it in your ColdSpring configuration file by creating an AOP Proxy:

<! DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd" >
< beans >
< bean id = "languageService" class = "coldspring.aop.framework.ProxyFactoryBean" >
< property name = "target" >
< bean class = "myapp.components.LanguageService" />
</ property >
< property name = "interceptorNames" >
< list >
< value >loggingAdvisor</value >
</ list >
</ property >
</ bean >
< bean id = "loggingAdvice" class = "myapp.components.LoggingAdvice" />
< bean id = "loggingAdvisor" class = "coldspring.aop.support.NamedMethodPointcutAdvisor" >
< property name = "advice" >
< ref bean = "loggingAdvice" />
</ property >
< property name = "mappedNames" >
< value >*</value >
</ property >
</ bean >
</ beans >

With this proxy in place, any time the LanguageService component is called, the LoggingAdvice will be executed. You can limit what methods the advice will be applied to using the mappedNames property. In the code above, I've set it to *, which means it is applied to all methods.

Here's a quick example of it in action:

<cfset languageService = beanFactory.getBean( 'languageService') />
<p>
Result for duplicate: #languageService.duplicateString('foo', 3)# <br />
<cfdump var= "#request.logData#" label= "Log data for duplicate" > <br />
</p>

In this case, the LanguageService has a method called duplicateString, which takes two arguments: a string, and the number of times to duplicate it. When you call this method, you end up with a request-scoped logData variable that contains the method name along with the arguments that were used in the call.

This is clearly a very simple example, and AOP can get much more complex and intricate than this. You can use AOP to apply database transactions, security, more granular logging, and much more. The applications for AOP are nearly limitless.

Remote Proxy Generation with ColdSpring

Another useful feature of ColdSpring is the ability to automatically generate remote proxy objects that can be the target of web service or Flash Remoting/AMF calls. The key here is that you do not want to expose your entire object model to remote invocation, but rather a specific subset of components and methods. Typically, you would need to write these proxy objects by hand, which is quite tedious and error-prone.

With ColdSpring you can use some XML configuration, similar to the AOP-related XML, to have ColdSpring generate these remote proxy objects for you.

Here is an example ColdSpring XML file that incorporates remote proxy generation:

<! DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd" >
< beans >
< bean id = "languageService" class = "myapp.components.LanguageService" />
< bean id = "remoteLanguageService" class = "coldspring.aop.framework.RemoteFactoryBean" lazy-init = "false" >
< property name = "target" >
< ref bean = "languageService" />
</ property >
< property name = "serviceName" >
< value > RemoteLanguageService </ value >
</ property >
< property name = "relativePath" >
< value > /myapp/components </ value >
</ property >
< property name = "remoteMethodNames" >
< value > reverseString,duplicateString </ value >
</ property >
< property name = "beanFactoryName" >
< value > beanFactory </ value >
</ property >
</ bean >
</ beans >

This generates a proxy component named RemoteLanguageService.cfc in the /myapp/components/ folder off of the web root. It exposes the reverseString() and duplicateString() methods for remote invocation, and only those two methods. You can now easily leverage these methods from a Flex or AIR application, or as a web service from another platform. And once again, you don't have to modify any of the original code to do this.

Where to Go From Here

This article has only scratched the surface of some of the great things you can do with ColdSpring. At this point, I couldn't imagine building a ColdFusion application without incorporating the ColdSpring framework. It works with any HTML Controller framework. It works equally well for HTML applications as it does as a back end for Flex, AIR, or web service-based applications. As ColdFusion developers delve further into object-oriented applications, the problem of dependency resolution becomes ever bigger. Luckily, you have ColdSpring to help you deal with this and get on to the fun part: building killer applications in record time.

For more details on the ColdSpring framework, visit http://www.coldspringframework.org/. The ColdSpring source code contains many examples, along with a full quick start guide that demonstrates most of the core features using live code. You can see the quick start guide (also written by Brian Kotek) at http://www.coldspringframework.org/coldspring/examples/quickstart/.

About the Author

Brian Kotek has been a ColdFusion developer for over ten years. As an active blogger and member of the ColdFusion community, his goals are to build great software and help others do the same. He has developed software for a variety of consulting companies, Fortune 100 companies, and Government agencies. His primary focus is on designing and building flexible and maintainable applications using frameworks and OOP patterns and best practices.