ColdBox Proxy Guide
Introduction
The ColdBox proxy enables remote applications or technologies like Flex, AIR and AJAX to communicate with ColdBox and provide an event driven model framework for those applications or for it to act as an enhanced service layer. Not only that, but you can reinitialize the entire application, get settings, announce custom or core interceptions, execute events, and so much more. You can create custom interceptor chains for your model that can be executed asynchronously when a user hits a save record button for example. You can create a Service Layer with built-in environmental settings, logging, error handling, event interception and chaining, you name it, and the possibilities are endless. The one key feature here, is that ColdBox morphs into a remote event-driven framework and no longer an MVC framework that produces HTML. We not only gave you the ability to do remote calls but also to monitor them. ColdBox 2.6.0 introduces an execution monitor that can help you debug and analyze remote calls.

The remote functionality of the framework enables you to actually create any amount of front ends using the same reusable ColdBox and model code. The code is the same, you create event handlers, you interact with a request collection, with core and custom plugins, but you don't set views or layouts because the framework is now a remote framework for your model. So what do you do, well, return data, arrays, xml, value objects. Anything, right from within the event handlers or setup a configuration setting that tells the framework to always return the request collection. You can also just create remote proxies to your service components (if using a service layers approach), so you can go directly to any object factory to request for services, interact with them and return results. The ColdBox proxy gives you flexibility in all aspects.
Getting Started
ColdBox provides you with a sample coldbox proxy base class that can be found in the Application Template directory. This file can be seen below:
<cfcomponent name="coldboxproxy" output="false" extends="coldbox.system.extras.ColdboxProxy"> <!--- You can override this method if you want to intercept before and after. ---> <cffunction name="process" output="false" access="remote" returntype="any" hint="Process a remote call and return data/objects back."> <cfset var results = ""> <!--- Anything before ---> <!--- Call the actual proxy ---> <cfset results = super.process(argumentCollection=arguments)> <!--- Anything after ---> <cfreturn results> </cffunction> </cfcomponent>
This simple proxy object extends the ColdBox Proxy class that gives you all the remote abilities. We will review this object in the next section.
The Base Proxy Object
As you can see from the code above, this proxy inherits from the coldbox.system.extras.ColdBoxProxy class. This is the key to it all, this base class has all the necessary hooks for your proxy to work. Why is it inherited? Why not just use that one? Well, the answer is that every project is different and I believe in empowering the developer. Therefore, you have your own class in which you can expose any other remote methods as you need. You only need to know the methods that are available to you from the base class. The following are the most commonly used methods in the base proxy class, for an in-depth review of the methods, please visit the CFC API:
| Method | Access | Returntype | Description |
| announceInterception() | remote | boolean | Processes a remote interception. Return true if successful. |
| process() | remote | any | Processes a remote call that will execute a coldbox event and returns data/objects back. |
| getBean() | private | any | Get a bean from the ioc plugin |
| getColdboxOCM() | private | coldbox.system.cache.CacheManager | The coldbox cache manager |
| getController() | private | ColdBox Controller | Returns the ColdBox controller instance |
| getInterceptor() | private | any | Get a named interceptor |
| getIoCFactory() | private | coldspring or lightwire | Get the IoC factory object |
| getPlugin() | private | any | Get a core or custom plugin |
| loadColdBox() | private | void | Gives you the ability to load any external coldbox application in the application scope. Great for remotely loading any coldbox application, it can be located anywhere. |
| tracer() | private | void | Ability to send tracer messages to the debugger |
| verifyColdBox() | private | boolean | Verifies that the coldbox controller has been loaded or not |
You can use any of those methods in your own proxy or even override them. With this in mind, you can create a set of remote objects that inherit from the ColdBox proxy that will be your proxy layer to any remote communication if necessary or just have a main proxy component used to call in to your events.
Expanding The Proxy
Below is a custom method that I created in my own application proxy for retrieving application settings. I DO NOT recommend this, since it will expose your settings. This is just a sample
<!--- Get a setting ---> <cffunction name="getSetting" hint="I get a setting from the FW Config structures. Use the FWSetting boolean argument to retrieve from the fwSettingsStruct." access="remote" returntype="struct" output="false"> <!--- ************************************************************* ---> <cfargument name="name" type="string" hint="Name of the setting key to retrieve" > <cfargument name="FWSetting" type="boolean" required="false" hint="Boolean Flag. If true, it will retrieve from the fwSettingsStruct else from the configStruct. Default is false." default="false"> <!--- ************************************************************* ---> <cfscript> var cbController = ""; var setting = ""; cbController = getController(); //Get Setting else return "" if( cbController.settingExists(argumentCollection=arguments) ){ setting = cbController.getSetting(argumentCollection=arguments); } //Get settings return setting; </cfscript> </cffunction>
This method basically retrieves a setting via the controller and returns it. You can basically create any amount of methods that correspond to your proxy object or just leave the main methods: process() and announceInterception().
The Configuration File
In order to be able to use the proxy for remote calls, I highly suggest filling out the following settings. The App Mapping setting is very important since ColdBox will NOT be able to auto-calculate where your application exists via flash remoting or the sort.
* AppMapping : If using the proxy from Flex or any method via Remoting or Life Cycle Data Services or whatever Adobe renames it to, you have to set this setting so ColdBox can find your application root path. So always remember to set it. This setting is not needed if the remote calls are via Ajax or any http protocol where the application path can be auto-calculated. However, we recommend setting it anyways.
- ProxyReturnCollection : This boolean setting determines if the proxy should return what the event handlers return or just return the request collection structure every time a process() method is called. This can be a very useful setting, if you don't even want to return any data from the handlers (via the process() method). The framework will just always return the request collection structure back to the proxy caller. By default, the framework has this setting turned to false so you can have more control and flexibility.
See Config Guide
Execution Profiler Monitor

This monitor will keep a stack of the latest X requests and their execution profiles that is all configurable via the application configuration file: coldbox.xml.cfm. This monitor can be kept side by side with your flex/AJAX or any front-end and give you execution profiles about your event model and show you event tracers. By default the request profiler is turned off, so you will have to turn it on via the new Debugger Settings in your configuration file. You can also choose the max number of requests to persist execution profiles for, so you can keep track of any amount of requests.
The Event Handlers
The event handlers that you will produce for remote interaction are exactly the same as your other handlers, with the exception of a return type (unless using the above mentioned setting). Below I layout a simple example:
<cffunction name="getCacheItemTypes" access="public" output="false" returntype="struct"> <cfargument name="Event" type="coldbox.system.beans.requestContext"> <cfset var itemTypes = structnew()> <cfset itemTypes = getColdBoxOCM().getItemTypes()> <cfreturn itemTypes> </cffunction>
The above handler method will query the ColdBox cache for the entire item types it contains and return them to the proxy. As you can see, the code is fairly simple and you can use any part of the framework lifecycle at your disposal.
Distinguishing Request Types
Now, what if you want to distinguish between a normal request and a proxy request? Well, the request context object, most commonly known as the event object has a method called:
- isProxyRequest : This boolean method determines what type of request is being executed.
This is extremely useful if you are doing path operations. Why? Well, when you call a coldfusion component via flash remoting or live data cycle services, the expandPath, getCurrentTemplatePath, getBaseTemplatePath, and other template path methods will always give you the path according to the file you are currently executing. This is due to how ColdFusion deals with remote calls. So you can use the method below to distinguish and load accordingly:
<!--- Use the isProxyRequest() ---> <cfif event.isProxyRequest()> <cfset getPlugin("JavaLoader").setup( listToArray( ExpandPath("../includes/helloworld.jar")) )> <cfelse> <cfset getPlugin("JavaLoader").setup( listToArray( ExpandPath("includes/helloworld.jar")) )> </cfif>
The above code will be executed within the proxy as if it’s in the handler’s directory. So to expand the jar file path in the includes directory, you have to go back a level when using the proxy and just directly if not. This is very important to grasp, since its not !ColdBox doing this, but ColdFusion. Executions via http protocols might not exhibit the same behavior.
Ajax Data Binding & More
Please visit the ColdBox & Ajax Integration Guide to learn how to use advanced techniques with the proxy to do data binding, mixing in with the cfajaxproxy tag and much more. Below are just some simple examples:
<!--- Declare the CF Ajax Proxy HERE ---> <cfajaxproxy cfc="coldboxproxy.cfc" jsclassname="cbProxy"> <script type="text/javascript"> var getArtists = function(){ var cbox = new cbProxy(); // Setting a callback handler for the proxy automatically makes // the proxy's calls asynchronous. cbox.setCallbackHandler(populateArtists); cbox.setErrorHandler(myErrorHandler); // The proxy getArtists function represents the CFC // getArtists function. cbox.process(event:'artists.list'); } </script> //ColdBox Proxy Code <cffunction name="getArtists" output="false" access="remote" returntype="Any" hint="Process a remote call and return data/objects back."> <cfargument name="ARTISTID" type="numeric" required="false" default="0"> <cfset var ReturnValue = "" /> <!--- It’s very interesting.. how I am interacting with service-layer, just bypassing controller layer ---> <cfset ReturnValue = getBean("ArtService").getArtist(argumentCollection=arguments) /> <cfreturn ReturnValue> </cffunction> <cffunction name="getNames" output="false" access="remote" returntype="Array" hint="Process a remote call and return data/objects back."> <cfset var qry = "" /> <!--- CFSELECT (bind ) ---> <cfset var TwoDimensionalArray = ArrayNew(2) /> <!--- Get Qry Directly from ArtService.cfc ---> <cfset qry = getBean("ArtService").getArtist() /> <cfset TwoDimensionalArray[1][1] = '0' /> <cfset TwoDimensionalArray[1][2] = 'Please select' /> <cfloop query="qry"> <cfset TwoDimensionalArray[qry.CurrentRow + 1][1] = trim(qry.ARTISTID)> <cfset TwoDimensionalArray[qry.CurrentRow + 1][2] = trim(qry.FIRSTNAME & chr(32) & qry.LASTNAME)> </cfloop> <!--- Anything after ---> <cfreturn TwoDimensionalArray> </cffunction>
Some Flex Code
This is the last step of the guide, so let's review for a moment.
- We have configured the application to return data/objects from the event handlers
- We have customized our proxy and made sure it inherits from the base proxy.
- We have created some event handler methods
- We learned how to determine when a remote call was made via the event object
Now our last step is to actually show some remote calls. For this example, I will be showing some Flex code. I am no big Flex expert, but below are some of the flex sample codes on how to call the ColdBox proxy. I recommend encapsulating the calls to the ColdBox proxy into a class of its own as a delegate class or however you see it fit in your Flex application architecture. The sample below is very flat.
<?xml version="1.0" encoding="utf-8"?> <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute"> <mx:Script> <![CDATA[ import mx.rpc.events.FaultEvent; import mx.events.ItemClickEvent; import mx.rpc.events.ResultEvent; import mx.controls.Alert; import mx.collections.ArrayCollection; //My Proxy Path public var cbProxyPath:String = "coldbox.samples.applications.ColdboxFlexTester.webroot.coldboxproxy"; /* PUBLIC FAULT Handler */ public function faultHandler(event:FaultEvent):void{ Alert.show(event.fault.toString()); } /* UTILITY METHOD TO GET A CBPROXY OBJECT, You can separate all this to a delegate class */ public function getColdBoxProxy():RemoteObject{ var cProxy:RemoteObject = new RemoteObject( "ColdFusion" ); cProxy.source= cbProxyPath; cProxy.showBusyCursor = true; cProxy.addEventListener( FaultEvent.FAULT, faultHandler ); return cProxy; } /* Read the Cache Objects */ public function handleCacheResults(event:ResultEvent):void{ var cacheItems:Object = new Object(); var key:String; var array:Array = new Array(); cacheItems = event.result; for( key in cacheItems ){ array.push( { item:key, total: cacheItems[key] }); } var collection:ArrayCollection = new ArrayCollection(array); cachechart.dataProvider = collection; } /* Call the proxy for cache objects */ public function readCache():void{ var cProxy:RemoteObject = getColdBoxProxy(); cProxy.process.addEventListener("result",handleCacheResults ); cProxy.process({event:"ehFlex.getCacheItemTypes"}); } ]]> </mx:Script> <mx:PieChart id="cachechart" height="190" width="205" showDataTips="true" x="10" y="450"> <mx:series> <mx:PieSeries field="total" nameField="item" labelPosition="callout" /> </mx:series> </mx:PieChart> <mx:Button x="45" y="420" label="Get Cache Chart" click="readCache()"/> </mx:Application>
So what does this application do. Well let's start from the top. The first part of the application just imports some classes for us to use. Then we declare the path to our coldbox proxy:
//My Proxy Path public var cbProxyPath:String = "coldbox.samples.applications.ColdboxFlexTester.webroot.coldboxproxy";
We then setup a public default error handler:
/* PUBLIC FAULT Handler */
public function faultHandler(event:FaultEvent):void{
Alert.show(event.fault.toString());
}
We then declare our mini proxy delegate. Again, this is for sample purposes, you must encapsulate this into a class of its own and even expand on it to make it a true delegate.
/* UTILITY METHOD TO GET A CBPROXY OBJECT, You can separate all this to a delegate class */
public function getColdBoxProxy():RemoteObject{
var cProxy:RemoteObject = new RemoteObject( "ColdFusion" );
cProxy.source= cbProxyPath;
cProxy.showBusyCursor = true;
cProxy.addEventListener( FaultEvent.FAULT, faultHandler );
return cProxy;
}
Now, you might ask, why create a delegate and not a remote object and just call it. Well, the problem lies in that you will always be calling the same method: process() on the proxy, but need to bind the results to different result handlers. Therefore, you need to create a delegate to process your request and assign a results handler for you. There are tons of ways to achieve what I am doing, I am doing the poor man's delegate.
Once I have done this, then I can create some object for me to display the cache in:
<mx:PieChart id="cachechart" height="190" width="205" showDataTips="true" x="10" y="450"> <mx:series> <mx:PieSeries field="total" nameField="item" labelPosition="callout" /> </mx:series> </mx:PieChart> <mx:Button x="45" y="420" label="Get Cache Chart" click="readCache()"/>
This declares a pie chart object and a push button. Once the button get's clicked on it will execute the readCache method we will cover below.
/* Read the Cache Objects */
public function handleCacheResults(event:ResultEvent):void{
var cacheItems:Object = new Object();
var key:String;
var array:Array = new Array();
cacheItems = event.result;
for( key in cacheItems ){
array.push( { item:key, total: cacheItems[key] });
}
var collection:ArrayCollection = new ArrayCollection(array);
cachechart.dataProvider = collection;
}
/* Call the proxy for cache objects */
public function readCache():void{
var cProxy:RemoteObject = getColdBoxProxy();
cProxy.process.addEventListener("result",handleCacheResults );
cProxy.process({event:"ehFlex.getCacheItemTypes"});
}
The readCache method basically calls the getColdBoxProxy delegate method to get a remote object for the proxy. It then creates an event listener for it and calls the proxy. The listener is called handleCacheResults which then manipulates the results and renders the cache.
Caveats & Gotchas
The most important gotchas in using the coldbox proxy for remoting or even event gateways is pathing. Paths are totally different if you are using expandPath() or per-application mappings. Per-Application mappings can sometimes be a hassle for onSessionEnd(). So always be careful when setting up your paths and configurations for remoting. Try to always have the correct paths assigned and tested.
Conclusion
As you can see, calling the ColdBox proxy from flex or any remote interface is very easy, but extremely powerful. Some applications for the proxy are :
- Remote Event driven Model Framework
- One configuration file for all application GUI's
- One common application language for all backend operations
- Multiple GUI's running on one single ColdBox application
- Enhanced service layers (caching, logging, AOP, interceptions, etc)
- Create beautiful RIA applications with a solid backend
- Create widgets and application monitors.
- What your imagination dictates.
Indeed, the ColdBox proxy is a great feature to complement your already great ColdBox applications. You can now use a standardized language for application development in all your front-ends.
