Conduit: The ColdFusion Adapter for BlazeDS

This project started with writing the translation layer for doing Transfer to Flex communication, I ran into a pretty nasty bug in ColdFusion's remoting implementation,
and then ended up in a place that solves all these problems, and is
incredibly flexible and powerful to boot!  Quite the round trip, but
well worth it in the end.

Conduit requires that you set up BlazeDS
on your machine, as well as install the conduit.jar file, and some CFCs
as well.  I'm not going to go into too much details here, as you can
read about in the Library Installation section of the documentation.

To configure Conduit, we open up our remoting-config.xml, and we add the Conduit adapter to the <adapters> section:

<adapter-definition id="conduit" class="com.compoundtheory.conduit.adapters.ColdFusionAdapter"/>

And now we continue by adding a new Destination to our remoting-config.xml, which we will then call from Flex via RemoteObject (examples of the configuration are provided in the downloads).


<destination id="Conduit">
    <channels>
        <channel ref="my-cfamf" />
    </channels>
    <adapter ref="conduit" />
    <properties>
        <source>*</source>
        <cfcs>
            <!--
            Whether or not reload the CFCs below on each request.
            Useful for debugging when building new invokers,serialisers
            or deserialisers
            -->
            <reloadcfcs>false</reloadcfcs>
            <!--
            The CFC that invokes the remote method call
             -->
            <invoker>conduit.core.CFCInvoker</invoker>
            <!--
            Translates CF=>AS3
             -->
            <serialiser>conduit.core.CFSerialiser</serialiser>
            <!--
            Translates AS3=>CF
             -->
            <deserialiser>conduit.core.CFDeserialiser</deserialiser>
        </cfcs>
    </properties>
</destination>


The id of the destination can be whatever you like, but we started with
'Conduit', so we would know what we are calling from Flex.

The <adapter> is specified to use the conduit adapter, rather than the default cf-object adapter.

We then set a series of properties, most important of which are setup
within the <cfc> section.  This section controls what CFCs are
called upon to perform various duties within the ColdFusion <=>
Flex communication process.

Just to emphasise this point – this means that the majority of the heavy lifting done by Conduit is done with ColdFusion code
This makes it really easy to extend, change, manipulate or debug.  It
gives you almost complete control over the AS3<=>CF translation
process, without you having to know much about Java at all (I will
admit there are some Java classes involved).

We can see from there are three CFCs that the Conduit adapter has configured for it.

The <invoker>
This is the CFC that does the actual method calling.  In the instance
of the conduit.core.CFCInvoker that comes with Conduit, all it does is
create an instance of the CFC that has been requested in the
<RemoteObject>'s source attribute, and calls the passed with method name and any parameters that were passed down from Flex.

The code looks something like this (just to show you how simple it is):


<cffunction name="execute" hint="Creates a cfc, and invokes a method
on it" access="public" returntype="any" output="false">
    <cfargument name="source" hint="the cfc source" type="string" required="Yes">
    <cfargument name="methodName" hint="the name of the method" type="string" required="Yes">
    <cfargument name="params" hint="the parameters to pass in" type="any" required="Yes">
    <cfscript>
        var local = {};
        var object = createObject("component", "#arguments.source#");
    </cfscript>

    <cfinvoke component="#object#" method="#arguments.methodName#"
argumentcollection="#arguments.params#"
returnvariable="local.return">

    <cfif StructKeyExists(local, "return")>
        <cfreturn local.return />
    </cfif>
</cffunction>


Pretty straight forward, no?

The <serialiser>

The job of this CFC is to take whatever is returned from the
<invoker> and translate it into whatever you want to return back
to Flex.  Since BlazeDS handles the AMF conversion part of it for us,
the conduit.core.CFSerialiser only really needs to convert CFCs in Actionscript objects, set all their properties correct, and we are good to go.

The <deserialiser>

The deserialiser CFC's job it to take the incoming parameters that come
down from a Flex RemoteObject call, and translate them into something
usable for the <serialiser> CFC.

Again, since BlazeDS does a lot of the heavy lifting, the
conduit.core.CFDeseriailser's main job, is to convert Actionscript
Objects into their appropriate CFCs, and set all their properties
correctly.

Changing how things work
 
While there are plans to put some interesting enhancements for Conduit
to provide above and beyond what ColdFusion remoting already does, the
real power of Conduit comes from being able to write your own custom
Invoker, Serialiser or Deserialiser.

Say for example, when we send information from ColdFusion to Flex, we
want to reverse every Simple value (String, date, numeric) that we come
across.  Don't ask me why you would want to do this ;o), maybe you just like messing with your co-workers.

First thing we need to do is write our own custom CFSerialiser.cfc.  For this example, I'm going to reuse the conduit folder I would have setup, which already has a /conduit ColdFusion mapping pointing to it, and create a new folder called reverse inside it.

So I create a new component, under /conduit/reverse, and call in
CFReverseSerialiser.cfc, and make it extend conduit.core.CFSerialiser.

We will now overwrite the translate method, which handles what data gets converted, and how, depending on its data type.

The code would look something like this:


<cfcomponent output="false" extends="conduit.core.CFSeriaiser" hint="Component for serialising CF=>AS3, in reverse">

<cffunction name="translate" hint="translation function for objects,
reverses simple values" access="private" returntype="any"
output="false">
    <cfargument name="object" hint="the object to serialise" type="any" required="no">
    <cfargument name="cache" hint="local reference cache for cyclic graphs" type="any" required="Yes">
    <cfscript>
        if(isSimpleValue(arguments.object)
        {
            //if it's simple, then reverse it!
            return reverse(arguments.object);   
        }
        else
        {
            return super.translate(argumentCollection=arguments);
        }
    </cfscript>
</cffunction>
</cfcomponent>

And we can configure a special 'ConduitReverse' destination for anyone who wants reversed Strings in their code:

<destination id="ConduitReverse">
    <channels>
        <channel ref="my-cfamf" />
    </channels>
    <adapter ref="conduit" />
    <properties>
        <source>*</source>
        <cfcs>

            <reloadcfcs>false</reloadcfcs>

            <invoker>conduit.core.CFCInvoker</invoker>

            <serialiser>conduit.reverse.CFReverseSerialiser</serialiser>

            <deserialiser>conduit.core.CFDeserialiser</deserialiser>
        </cfcs>
    </properties>
</destination>



As you can see, we can do almost anything we want to the communication
process, simply by extending the core components (or even writing brand
new ones) and because its at a low level, its almost completely
seamless to those who are writing the ColdFusion and/or Flex code. That
being said, its open source, so we can add extra logging and/or
debugging as we need.  No more failing silently!

Trying it out

This is still Alpha code, and there is a lot of logging
currently in it, but there is enough there for you to start playing. 
Code is in SVN, and daily builds are available from the Google Group (saves you having to compile the .jar file).  The documentation is slowly getting fleshed out, but there is enough there to get started.

I am more than happy to get code contributions, and/or ideas for how the to expand on the current ColdFusion remoting feature so send through whatever you have.

I hope you enjoy Conduit!

Leave a Comment

Comments

  • Nathan Mische | December 16, 2008

    This looks _very_ cool.

  • Jamie Jackson | December 17, 2008

    Would you say that this is the replacement for the (abandoned?) Bender project? I’m new to both projects.

  • Andrew | December 18, 2008

    FYI, the source ‘*’ may represent a security hole, as it lets anyone execute remoting calls for any remote methods in any cfc on the coldfusion instance if they have some insider knowledge of the API’s on your server. With employees coming and going, sometimes insider disclosure is an issue to watch out for. The vulnerability depends of your security infrastructure. Some folks think they have apache redirects protecting ‘remote’ methods in API’s from access to the outside world, and sometimes these gateway calls can get around those kinds of safeguards.

    It may be arguable therefore that one should tie the source directly to a particular cfc as a convention. Once even one destination is put in place as source ‘*’, the hole may be opened.
    Just a caution, not a big deal. Unfortunately it’s one-to-one, or ‘*’ (wide open). There’s no middle of the road where you can allow the destination to map to a particular package structure only.

  • Mark | December 18, 2008

    @Andrew,
    You raise a good point, but would you agree that since remoting is (now) limited to only methods that are set to access="remote", this risk is mitigated, as you have fine grained control over what is able to be called or not.

    (Sorry for the double post.. I saw your name, and then wrote ‘Andrew’ instead of my name)

  • Andrew | December 18, 2008

    That situation depends upon the extent that people open up methods for WebServices. Unfortunately, Remoting and WebServices are both opened up by that access identifier. Some security strategies secure their webservices thinking only of http access, but still leave them open in the Flex Gateway without realizing it. Just a caution. The finer grained access is just more conservative and more deliberate. I consider it akin to the permissive cross domain policy for Flex having a ‘*’ in it for allowed domains.

  • Kurt Bonnet | January 21, 2009

    Mark, this is awesome!!! Thanks for releasing this, I’m thoroughly enjoying playing around with it!