Before I jumped across the pond to London and Paris last week, I had posted a blog entry about ColdSpring Remote Proxies. In response to some of the comments and IM messages, I wanted to expand on my mention of extending ColdSpring with your own Factory Beans. The ability to create your own extensions to ColdSpring is an extremely powerful technique. Unfortunately, it's also an extremely unexplained technique! I hope to change that a bit, so let's get to it.

If you aren't familiar with ColdSpring's ability to automatically generate Remote Proxy components, I'll refer you to a few blog entries:

In a nutshell, ColdSpring can create remote proxy CFCs for your actual underlying CFCs, typically service layer components. Which is pretty darn sweet. Unfortunately, the configuration XML to do this can get rather verbose, especially if you want to create numerous proxies. Consider:

<bean id="remoteUserService" class="coldspring.aop.framework.RemoteFactoryBean">
	<property name="target">
		<ref bean="userService" />
	</property>
	<property name="beanFactoryScope">
		<value>application</value>
	</property>        
	<property name="beanFactoryName">
		<value>beanFactory</value>
	</property>    
	<!--- Physical name given to the proxy component --->
	<property name="serviceName">
		<value>RemoteUserService</value>
	</property>
	<!--- location to the physical proxy component --->
	<property name="relativePath">
		<value>/remote</value>
	</property>
	<!--- you could have something like get* for all get methods only --->
	<property name="remoteMethodNames">
		<value>*</value>
	</property>
</bean>
		

It seems like a lot, and I suppose it is. The good news is that you only have to do it one time, at the start of your project. Still, my previous blog entry showed that you can trim this down a bit, especially if you are creating more than one remote proxy:

<!--- Create a parent bean that all concrete remote proxies to extend, which reduces the XML for the child beans. --->
<bean id="abstractRemoteProxy" class="" abstract="true">
	<property name="beanFactoryScope">
		<value>application</value>
	</property>
	<property name="beanFactoryName">
		<value>beanFactory</value>
	</property>
	<!--- location to the physical proxy component --->
	<property name="relativePath">
		<value>/remote</value>
	</property>
</bean>

<!--- Concrete bean extends the abstract parent bean. --->
<bean id="remoteUserService" class="coldspring.aop.framework.RemoteFactoryBean" parent="abstractRemoteProxy" lazy-init="false">
	<property name="target">
		<ref bean="userService" />
	</property>
	<!--- Physical name given to the proxy component --->
	<property name="serviceName">
		<value>RemoteUserService</value>
	</property>
	<!--- you could have something like get* for all get methods only --->
	<property name="remoteMethodNames">
		<value>*</value>
	</property>
</bean>
		

Better, but I still think we can improve on it. But this is where we go into potentially scary territory: we're going to extend ColdSpring to make our lives easier. In this case, I'm going to create my own custom Remote Factory Bean that extends ColdSpring's RemoteFactoryBean and sets up some default values that I tend to use over and over in my applications. This reduces the XML configuration even further, but still allows me to override the defaults if I need to. Here is the XML:

<bean id="abstractRemoteProxy" class="" abstract="true">
	<property name="relativePath">
		<value>/remote</value>
	</property>
</bean>

<bean id="remoteUserService" class="factory.CustomRemoteFactoryBean" parent="abstractRemoteProxy" lazy-init="false">
	<property name="targetBeanID">
		<value>UserService</value>
	</property>
	<property name="remoteMethodNames">
		<value>*</value>
	</property>
</bean>
		

Hmm, that's quite a bit shorter! You'll notice that my RemoteUserService is specifying it's class as "CustomRemoteFactoryBean". That's the first change. You may also notice that many of the properties are gone, but a new one has appeared: "targetBeanID". By passing the targetBeanID to my custom factory bean, I can set up a series of default property values due to the fact that my remote proxies tend to follow a fairly standard setup. Here is the code for the CustomRemoteFactoryBean to demonstrate what it is doing:

<cfcomponent name="CustomRemoteFactoryBean" extends="coldspring.aop.framework.RemoteFactoryBean" hint="I add custom behavior to Remote Factory Beans.">
	
	<cffunction name="init" access="public" returntype="any" hint="Constructor.">
		<cfset super.init() />
		<cfreturn this />
	</cffunction>
	
	<cffunction name="getObject" access="public" returntype="any" output="true">
		<cfset var local = StructNew() />
		
		<!--- If the current bean is not constructed, set default values. --->
		<cfif not isConstructed()>
		
			<!--- Default the service name to the name of the target bean ID with 'Remote' appended to the front of it. --->
			<cfif not StructKeyExists(variables, 'serviceName') or not Len(variables.serviceName)>
				<cfset setServiceName('Remote#getTargetBeanID()#') />
			</cfif>
			
			<!--- Set the target to the bean referenced by the target bean ID. --->
			<cfif not StructKeyExists(variables, 'target') or not IsObject(variables.target)>
				<cfset setTarget(getBeanFactory().getBean(getTargetBeanID())) />
			</cfif>
			
			<!--- Set default Bean Factory Name. --->
			<cfif not StructKeyExists(variables, 'beanFactoryName') or not Len(variables.beanFactoryName)>
				<cfset setBeanFactoryName('beanFactory') />
			</cfif>
			
			<!--- Set default Bean Factory Scope. --->
			<cfif not StructKeyExists(variables, 'beanFactoryScope') or not Len(variables.beanFactoryScope)>
				<cfset setBeanFactoryScope('application') />
			</cfif>
			
		</cfif>
		<cfset local.object = super.getObject() />
		<cfreturn local.object />
	</cffunction>
	
	<cffunction name="getTargetBeanID" access="public" returntype="string" output="false" hint="I return the Target Bean ID.">
		<cfreturn variables['targetBeanID'] />
	</cffunction>
		
	<cffunction name="setTargetBeanID" access="public" returntype="void" output="false" hint="I set the Target Bean ID.">
		<cfargument name="targetBeanID" type="string" required="true" hint="TargetBeanID" />
		<cfset variables['targetBeanID'] = arguments.targetBeanID />
	</cffunction>
	
</cfcomponent>
		

The CustomRemoteFactoryBean extends the ColdSpring RemoteFactoryBean and adds additional behavior. If you weren't aware, under the hood ColdSpring calls getObject() on the RemoteFactoryBean when it needs to create an instance of your remote proxy. You can see that my getObject() method is automatically setting up default values for the service name, target, bean factory name, and bean factory scope if these are not defined. Then it simply calls the superclass's getObject() method to actually create the remote proxy. This is how I can get away with specifying so little in the XML.

Your first reaction might be "OK that's kind of cool because I hate typing XML, but is that really it?" Yes, that is it! For this blog entry anyway, since it's gotten rather long. But if people want me to keep going I can expand further. In the meantime, you might start thinking about what sort of behavior you can add to your own custom getObject() method that adds behavior beyond defining simple default values. And please fire away with thoughts or comments!

Comments Comments (1) | del.ico.us del.icio.us | Digg It! Digg It! | Linking Blogs Linking Blogs | 2445 Views

Related Blog Entries

Comments

  • # Posted By Brian Klaas | 6/4/08 8:37 AM

    Thanks for posting this, Brian. I know the team is working on it, but ColdSpring documentation is quite lacking at the moment. And thanks for linking to my blog! =D