A Guide to Automatic Remote Proxy Generation in ColdSpring
Although the ColdSpring site offers some documentation on doing this, and Chris Scott (who designed and implemented ColdSpring's AOP functionality) wrote a blog post, there's not a lot more information about using ColdSpring's automatic remote proxy generation in your CF apps. It just works, which is nice, but there seem to have been a few changes and bug fixes since the frameworks's release that aren't well documented. I ran in to a few problems trying to get this to work earlier this week, so I thought I'd post about what I did.
I'm going to assume that you already know the basics of ColdSpring and setting up your XML config file. I'm also going to be working in the context of Mach-II, as that's what we use for all of our applications. The setup for Model-Glue isn't all that different, so the same principles apply.
First and foremost, you need to use the latest build from the CVS repository. Instructions on how to do this are available on the CS site. It's very important to do this, as there were some bugs in the 1.0 release or changes made since then that get everything to work nicely. This is particularly important if you're using Mach-II 1.5 as the ColdSpring plug-in for Mach-II has been updated for 1.5 compatability.
(As a side note, I didn't get the latest build when I first started on this project and wated 3 hours trying to figure out why a variable called "scope" wasn't being passed to a function inside the framework. Updating made the problem magically go away.)
Let's begin with the ColdSpring config XML file. You need to have already defined a bean elsewhere in your CS config file for which CS will generate a remote proxy. When CS generates the proxy, it generates a real file that resides in the file system. That's the only way the remote calls can actually work. As such, you need to have a folder on your file system for CS to write to. In order for CS to generate a remote proxy to your bean, include the following:
<property name="target">
<ref bean="theBeanIWantToMakeTheProxyFor" />
</property>
<property name="serviceName">
<value>nameIWantToGiveToGeneratedCFC</value>
</property>
<property name="relativePath">
<value>path/to/directory/CS/will/write/to/value>
</property>
<property name="remoteMethodNames">
<value>methodNamesIWantToMakeRemote,asCommaDelimitedList</value>
</property>
<property name="beanFactoryName">
<value>nameOfTheSeriviceFactoryInApplicationScope</value>
</property>
</bean>
I tried to make things clear with the values above, but let me offer a bit more explanation on a couple items.
- The bean id can be anything really, though I'd recommend it matches the service name.
- The service name is the name you want to give to the generated file. If you're wrapping a bean named "userService,&quout; I'd recommend setting the service name as "remoteUserService" for clarity.
- The remoteMethodNames are the names of the functions in the CFC for which the proxy is being generated that you want to expose to remote calls. This can be a comma-delimited list, or this can be a wildcard character (*). If you enter something like get* then all functions that begin with "get" will have remote proxies geenrated for them. If you just enter * here, then every function in your CFC will have a remote proxy generated for it.
- The beanFactoryName is the name of the ColdSpring bean factory that's going to sit in application (or server) scope. This one isn't in the documentation, so that's tripped some folks up. So when you create your CS bean factory at application startup, use the exact same name you use there in the proxy bean definition here.
You can have as many remote proxies as you need in your config XML, but I'm going to stop at just this one example.
Next up, you need to make an additional setting in your Mach-II XML config file. A sample ColdSpring plug-in definition would look like this:
<parameters>
<parameter name="beanFactoryPropertyName" value="serviceFactory"/>
<parameter name="configFilePropertyName" value="ColdspringComponentRelativePath"/>
<parameter name="configFilePathIsRelative" value="true"/>
<parameter name="resolveMachiiDependencies" value="true"/>
<parameter name="placeFactoryInApplicationScope" value="true"/>
</parameters>
</plugin>
The two key things here are as follows:
- The beanFactoryPropertyName value must match the value of the <property name="beanFactoryName"> element in your CS XML config file that was described above.
- The placeFactoryInApplicationScope value must be set to true so that CS will put a copy of your beanFactory in to the application scope for the application you are building. If you do not do this, you won't have a way of referencing your CS bean factory, nor will your remote proxy when it's invoked from an AJAX/Flash/Flex client.
So you're done with the XML config files. Now on to some object creation.
Just because you've defined the setup for the remote proxy and for the ColdSpring plug-in, ColdSpring doesn't actually create the remote proxy CFC until it is requested to do so. So somewhere in your application's startup/initialization, you need to tell the bean factory to "give" you a copy of the remote proxy bean. When you do that, the CFC is actually created in the file system according to the path you set in the CS XML config file (the relativePath element of the proxy bean definition).
I do this in an applicationConstants.cfc plug-in in a Mach-II application. Example code:
<cfset var sf = getProperty('serviceFactory') />
<cfset sf.getBean("remoteUserService") />
</cffunction>
In the configure method of my applicationConstants plug-in (which is the equivalent of "init()" in CFCs that extend another part of the Mach-II framework, like listeners, filters, or plug-ins), I get a reference to the CS bean factory (called "serviceFactory" here, just like it was in my ColdSpring plug-in definition, as shown above). I then ask the bean factory for a copy of the remote proxy bean — in this case the remoteUserService. Behind the scenes, ColdSpring generates the actual CFC on the file system, loads it in to memory (in to the bean factory) and then passes it along to the requesting object. I'm not going to do anything else with the object in this case. This is merely part of my application startup and I want to make sure that the remote proxy actually exists on the file system before I start making calls to it elsewhere.
This is one of the big "gotchas" when it comes to having ColdSpring create remote proxies for you. You must ask the bean factory for a copy of the remote proxy before actually trying to call the remote proxy via remote code (AJAX, Flash, Flex) or it will not exist. Asking for the remote proxy bean makes ColdSpring generate it. As such, you should do this when the application starts up, after the ColdSpring bean factory has been created and populated in memory.
So what now?
Now that you have successfully created the remote proxy bean, you can start to call it. For example, let's say you have a user search field on a form. You use one of ColdFusion 8's new AJAX components to do an autosuggest while the user is typing in that field. You would reference your ColdSpring-generated remote proxy for your userService as follows:
<p>
<cfinput type="text" name="searchString" autosuggest="cfc:path.to.directory.where.remoteService.was.created.remoteUserService.suggestUsername({cfautosuggestvalue})" autosuggestminlength="3">
</p>
</cfform>
This makes an AJAX call to the suggestUsername method in the ColdSpring-generated remote proxy called "remoteUserService" in the directory "/path/to/directory/where/remoteService/was/created/".
Alernatively, let's say you have a .cfm page which is being called remotely, you could get a copy of the remote proxy bean (or any bean in the ColdSpring bean factory) with this code:
So that's how you set up ColdSpring and your application for remote proxy generation and how you use those proxies in your application.
I hope this has been both clear and useful (and correct!). If you notice any errors or omissions or have any questions, please let me know!

There are no comments for this entry.
[Add Comment]