I wanted to get some input about a hack that I just created in ColdSpring.
First, some background:
I was playing with the new AJAX features in CF8. In particular, I was
messing with the AJAX grid. I created a simple service component that
returns a query, and wanted to use ColdSpring to generate a remote proxy
bean that the grid can call. So far so good.
The first issue I ran into was that the AJAX grid requires a query to be
reformatted using QueryConvertForGrid(). So I created a simple AOP advice
that runs the target method and then returns the result wrapped with
QueryConvertForGrid(). I could also imagine doing this to format data for
use in the AJAX tree component.
Anyway, when I bound my grid to the CFC method, ColdFusion complained
loudly. I seems any method used to update a grid requires several arguments,
such as cfgridpage, cfgridsize, cfgridpagesortcolumn, and
cfgridpagesortdirection. So I specified these arguments in my CFC call from
the grid. CF still complained. It seems that the AJAX to CFC binding uses
CFC metadata and REQUIRES that cfargument tags exist in the target CFC that
match these arguments.
Now you can probably see the problem. My base service component has no such
arguments. It has no idea I'm eventually using the data it returns in an
AJAX grid. And since ColdSpring uses the base CFC metadata when generating
the remote proxy bean, it won't generate any arguments at all for that
remote method. So it looked like I was out of luck on using ColdSpring for
this sort of thing. Which would suck for an app that sent a lot of data to
AJAX grids...I'd have to manually create remote facades for every method.
Unless....
The wheels began turning...
What if I could somehow inject artificial metadata into the system, so that
even though my base service component didn't have these grid-specific
cfargument tags, the generated remote proxy would? Then ColdFusion's AJAX to
CFC binding would be happy, and the AOP advice would have these arguments
available for use in the QueryConvertForGrid() function call that it does.
Well, I did it. The result is that a bean definition like this will have
extra metadata defined, that will be used the the RemoteFactoryBean to
generate the remote proxy bean:
<!-- Generate remote facade for Product Service -->
<bean id="artServiceAJAX" class="
coldspring.aop.framework.RemoteFactoryBean" lazy-init="false">
<property name="target">
<ref bean="artService" />
</property>
<property name="serviceName">
<value>ArtServiceAJAX</value>
</property>
<property name="relativePath">
<value>/artstore/components/</value>
</property>
<property name="remoteMethodNames">
<value>get*</value>
</property>
<property name="beanFactoryName">
<value>BeanFactory</value>
</property>
<property name="interceptorNames">
<list>
<value>AJAXGridAdvisor</value>
</list>
</property>
<metadata>
<methods>
<method name="getArtists" returnType="query">
<parameter name="cfgridpage" required="yes"
type="numeric" />
<parameter name="cfgridpageSize" required="yes"
type="numeric" />
<parameter name="cfgridpagesortcolumn" required="no"
type="string" default="" />
<parameter name="cfgridpagesortdirection" required="no"
type="string" default="" />
</method>
</methods>
</metadata>
</bean>
And the resulting ArtServiceAJAX.cfc has this method defined:
<cffunction name="getArtists" access="remote" returntype="any"
output="false" >
<cfargument name="cfgridpage" type="numeric" required="yes" />
<cfargument name="cfgridpageSize" type="numeric" required="yes" />
<cfargument name="cfgridpagesortcolumn" type="string" required="no"
default="" />
<cfargument name="cfgridpagesortdirection" type="string"
required="no" default="" />
<cfset var rtn = callMethod('getArtists', arguments) />
<cfif isDefined('rtn')><cfreturn rtn /></cfif>
</cffunction>
It seems to me this could be very useful in any remote proxy bean that also
uses AOP to do something beyond what the base component it is proxying does.
In theory it would work for web service calls too, which are also obviously
highly dependent on the metadata of the proxy to generate WSDL, etc. Flex
apps might benefit less since I don't believe they depend on the actual
metadata/arguments/types of the methods being called, but someone who knows
more might correct me. This proof of concept requires you to spell out the
full method names, but I could easily see changing it to support wildcards
like get*.
I set it up so that you can alter the return type of the method itself,
alter the type, required, and default attributes of existing arguments, and
add completely artificial arguments as well.
I'm interested to hear what people think. One downside is that it seems to
definitely alter the ColdSpring XML DTD to differ from the Java Spring DTD,
though I know there is already at least one other difference between the two
(in the constructor arg I think).
Is this madness?
Thanks,
Brian