| | 1 | = How To Use Remoting = |
| | 2 | |
| | 3 | == History == |
| | 4 | As Model-Glue came to be used more and more often for larger and larger applications, it became commonplace to have a Flex or AJAX widget that required functionality already written for another part of the application. While it is possible to structure your application to use other techniques, it's also very convenient to run a Model-Glue event as a remoting call. Unfortunately, under Model-Glue 2 the only way to accomplish this was to create a new event that used existing broadcasts and a new view to generate text in XML or JSON format. The Flex or AJAX client would then use an HTTP GET or POST operation against this event and parse the body of the response into the appropriate native container type. |
| | 5 | |
| | 6 | This particular technique wasn't very well received because it amounts to warping a view into a transfer object of a sort. While it works, it's certainly not a desired approach. While AJAX was, due to it's pure HTTP- and text-based nature, less affected by the issues with this approach, Flex and Flash applications or widgets were extremely hampered by it. This technique is entirely text-based and cannot make use of the binary protocol AMF and the !FlashRemoting gateway for the dual benefits of performance and reduced bandwidth. It required an HTTP GET or POST, requiring the Flex HTTPRequest object instead of the !RemoteObject for more efficient remote transmissions. As Flex became a bigger player in conjunction with Model-Glue applications, this solution became less and less desirable. Ultimately, a complete analasys of the ramifications of this technique is beyond the scope of this document, but it is widely considered to be a messy solution at best and a complete hack at worst. A better way for remoting clients to leverage Model-Glue's strengths was needed. |
| | 7 | |
| | 8 | Enter Model-Glue Remoting in !ModelGlue 3. This feature allows you to accomplish the exact same result without having to warp the <views /> into data packets. |
| | 9 | |
| | 10 | == The Basics == |
| | 11 | |
| | 12 | The process is simple. Model-Glue 3 has a component called "!RemotingService.cfc" that sits in the root of your project folder and uses cfmodule to run the index.cfm file of your application. The !RemotingService extends !ModelGlue.gesture.remoting.!AbstractRemotingService which provides one function: executeEvent(). When you call !RemotingService.executeEvent(...), the remoting service uses a handle on the ModelGlue instance in the application scope to run the event and then extracts values you've asked for from the Event object into a struct which is then returned to your remoting client. And because of how this is all implemented, it can be used either via a Flex !RemoteObject, directly, or as a web service. This makes it possible for Flex, AJAX, and HTML applications to all make use of common functionality. |
| | 13 | |
| | 14 | == Some basic rules regarding the !RemotingService.cfc == |
| | 15 | |
| | 16 | Regardless of any other factor, the CFC in question must follow two rules: |
| | 17 | |
| | 18 | 1. It must live in the root folder of a Model-Glue application. This is because it relies on the presence of an instane of !ModelGlue.cfc in the application scope to locate and run any events. It can have any name but it has to be in the root of the application. |
| | 19 | 1. It must extend modelglue.gesture.remoting.!AbstractRemotingService. This base class provides essential functionality (such as locating the Model-Glue instance in the application scope regardless of the name of the variable containing it). |
| | 20 | |
| | 21 | |
| | 22 | == Example Application == |
| | 23 | |
| | 24 | Before showing how the Flex and AJAX remoting works within Model-Glue, we first need to set up a very simple MG3 application which will have a single event "get.users" which will return a list of users. Feel free to use the new Model-Glue 3 "Event Generation" feature to generate some of this setup code. We're not going to explain the MG3 event handling in this article. There are specific chapters in the documentation that fully explains this process. We are assuming here that you know this process already from your standard MG3 development. |
| | 25 | |
| | 26 | |
| | 27 | First, we need to create a Users.cfc in our "model" folder. |
| | 28 | |
| | 29 | The code for this CFC |
| | 30 | {{{ |
| | 31 | #!xml |
| | 32 | <cfcomponent output="false"> |
| | 33 | <cfset variables.instance = StructNew()/> |
| | 34 | |
| | 35 | <cffunction name="init" access="public" output="false" returntype="Users"> |
| | 36 | <cfreturn this/> |
| | 37 | </cffunction> |
| | 38 | |
| | 39 | <cffunction name="getUsers" access="remote" output="false" returntype="any"> |
| | 40 | <cfset var local = StructNew()/> |
| | 41 | <cfset local.myQuery = QueryNew("name","varchar")> |
| | 42 | <cfloop from="1" to="12" index="local.j"> |
| | 43 | <cfset QueryAddRow(local.myQuery, 1)> |
| | 44 | <cfset QuerySetCell(local.myQuery, "name", "Person-#local.j#", local.j)> |
| | 45 | </cfloop> |
| | 46 | <cfreturn local.myQuery/> |
| | 47 | </cffunction> |
| | 48 | |
| | 49 | </cfcomponent> |
| | 50 | }}} |
| | 51 | |
| | 52 | So this CFC has a "getUsers" method which returns a query object of users. This naturally could be altered to pull from a database. |
| | 53 | |
| | 54 | Next, we need a controller CFC called "!GetController" which will be placed in our "controller" folder. |
| | 55 | |
| | 56 | The code for !GetController.cfc is.. |
| | 57 | {{{ |
| | 58 | #!xml |
| | 59 | <cfcomponent output="false" beans="Users" extends="ModelGlue.gesture.controller.Controller"> |
| | 60 | |
| | 61 | <cffunction name="init" access="public" output="false" hint="Constructor"> |
| | 62 | <cfreturn this /> |
| | 63 | </cffunction> |
| | 64 | |
| | 65 | <cffunction name="users" output="false"> |
| | 66 | <cfargument name="event" /> |
| | 67 | |
| | 68 | <cfset event.setValue("users",beans.Users.getUsers())/> |
| | 69 | </cffunction> |
| | 70 | |
| | 71 | </cfcomponent> |
| | 72 | }}} |
| | 73 | |
| | 74 | Notice here, we are using the new Model-Glue 3 feature called "bean injection". The controller has a scope called "beans" which contains an instance of our Users.cfc class. For this to work properly, you need to define the Users.cfc bean in the Coldspring.xml file like so.. |
| | 75 | |
| | 76 | {{{ |
| | 77 | #!xml |
| | 78 | <bean id="Users" class="mapping_to_your_app.model.Users"/> |
| | 79 | }}} |
| | 80 | |
| | 81 | Naturally, the mapping to your application may vary. Typically it will be your project name followed by ".model.Users". So, for example, "mydemo.model.Users". |
| | 82 | |
| | 83 | We now need a listener for our new controller. We define this in our !ModelGlue.xml file. Inside the "controllers" tags, include the following.. |
| | 84 | |
| | 85 | {{{ |
| | 86 | #!xml |
| | 87 | <controller id="!GetController" type="mapping_to_your_app.controller.!GetController"> |
| | 88 | <message-listener function="users" message="get.users" /> |
| | 89 | </controller> |
| | 90 | }}} |
| | 91 | |
| | 92 | Next, we need to define our event handler in our !ModelGlue.xml file. Inside our "event-handlers" tags, we include the following.. |
| | 93 | |
| | 94 | {{{ |
| | 95 | #!xml |
| | 96 | <event-handler name="get.users"> |
| | 97 | <broadcasts> |
| | 98 | <message name="get.users" /> |
| | 99 | </broadcasts> |
| | 100 | <results /> |
| | 101 | <views> |
| | 102 | <include name="body" template="get/users.cfm" /> |
| | 103 | </views> |
| | 104 | </event-handler> |
| | 105 | }}} |
| | 106 | |
| | 107 | Ok, so when Model-Glue receives an event called "get.users". To make certain we did everything right, go ahead and create a page called "users.cfm" in "/views/get/" folder. In the users.cfm page we will simply dump the query object to make certain everything is wired up correctly. |
| | 108 | |
| | 109 | {{{ |
| | 110 | #!xml |
| | 111 | <cfdump label="My Users" var="#event.getValue("users")#"> |
| | 112 | }}} |
| | 113 | |
| | 114 | Now, if you run your application and using the following url "index.cfm??event=get.users", you should see the following. |
| | 115 | |
| | 116 | (Image in progress) |
| | 117 | |
| | 118 | Ok, we can now proceed with our Flex and AJAX remoting examples. |
| | 119 | |
| | 120 | == Flex Example == |
| | 121 | |
| | 122 | In this example, we will make a Flex remote object call to get our "users" query which will be placed in an !ArrayCollection which in turn will be the data provider for a !DataGrid. |
| | 123 | |
| | 124 | == A couple of Important Things to Note about using Flex Remoting: == |
| | 125 | |
| | 126 | 1. If you have a /!ModelGlue mapping (via either Application.cfc or the ColdFusion Administrator), Flash Remoting will not work until you have edited {cfusion_home}/wwwroot/WEB-INF/flex/remoting-config.xml (on J2EE or multiserver, it's inside {jrun_home}/servers/cfusion.ear/cfusion.war/WEB-INF/flex/remoting-config.xml). You have to find the setting <use-mappings>false</use-mappings> and change it to true. Then restart your CF server. Until you've done this, calls to MG remoting from Flex applications will fail due a file not found. This only applies to Flex applications making remoting calls through the !FlashRemoting gateway. |
| | 127 | |
| | 128 | 1. For the application to work properly, it will need to understand the remoting destination and endpoints. There are a number of ways to handle this, this is the simplest. In the Flex project properties in Flex Builder, go to Flex Compiler and add the following to the Additional Compiler Arguments" |
| | 129 | |
| | 130 | {{{ |
| | 131 | -services "directory_path_to_your_flex_folder/WEB-INF/flex/services-config.xml" -context-root="" |
| | 132 | }}} |
| | 133 | |
| | 134 | For the Flex example, we will go ahead and show the complete code and then explain what is happening here. |
| | 135 | |
| | 136 | {{{ |
| | 137 | #!xml |
| | 138 | <?xml version="1.0" encoding="utf-8"?> |
| | 139 | <mx:Application |
| | 140 | xmlns:mx="http://www.adobe.com/2006/mxml" |
| | 141 | creationComplete="init()"> |
| | 142 | |
| | 143 | <mx:Script> |
| | 144 | <![CDATA[ |
| | 145 | import mx.collections.ArrayCollection; |
| | 146 | import mx.rpc.events.*; |
| | 147 | |
| | 148 | [Bindable] |
| | 149 | public var _users:ArrayCollection; |
| | 150 | |
| | 151 | public function init():void{ |
| | 152 | |
| | 153 | //call Model-Glue Remoting Service cfc |
| | 154 | mgrs.executeEvent("get.users",{},"users"); |
| | 155 | } |
| | 156 | |
| | 157 | public function resultHandler(event:ResultEvent):void{ |
| | 158 | _users = event.result.users as ArrayCollection; |
| | 159 | } |
| | 160 | |
| | 161 | ]]> |
| | 162 | </mx:Script> |
| | 163 | |
| | 164 | <mx:RemoteObject id="mgrs" |
| | 165 | destination="ColdFusion" |
| | 166 | source="mapping_to_your_MG3_app.RemotingService" |
| | 167 | result="resultHandler(event)" |
| | 168 | showBusyCursor="true" /> |
| | 169 | |
| | 170 | <mx:DataGrid id="dg" dataProvider="{_users}"/> |
| | 171 | |
| | 172 | </mx:Application> |
| | 173 | }}} |
| | 174 | We define our Remote Object service with the "mx:RemoteObject" tag. In this tag, we called the standard "!ColdFusion" service defined in your remoting-config.xml in your flex folder on the !ColdFusion server. The source defines the specific cfc we will are calling which in with our MG3 app will always be the !RemotingService.cfc. When the result comes back, we have a function called "resultHandler()" which will process the data. |
| | 175 | |
| | 176 | When this Flex application runs, we start off with calling the init() function with our creationComplete() located near the top of the file. The init() makes the remote object call to the "executeEvent" method located in the !RemotingService.cfc. That method has 3 arguments. The first argument is the event string. This is exactly the same value you would use in the url in a normal MG3 application. The second argument is are values you need to submit with the event. In this example we didn't have any values to submit, but for future reference you simply can submit values as an object of name/value pairs like so, {Param1:"Hi!", Param2:76}. The third argument is the MG3 event object key we want back. MG normally returns lots of things in the "event" object. For our purposes, we just want the "users" object. |
| | 177 | |
| | 178 | The resultHandler() function simply puts the returned object into the internal _users array collection which powers our datagrid. |
| | 179 | |
| | 180 | So when we run this application, we should see the following. |
| | 181 | |
| | 182 | (Image in progress) |
| | 183 | |
| | 184 | Pretty simple example, but with this you can now understand the flow of using the MG3 remoting features. |
| | 185 | |
| | 186 | == AJAX Example with jQuery == |
| | 187 | jQuery is a popular Javascript library for building Rich Internet Applications. We're not going explain all the features of jQuery here, but this simple example will explain how it can be used to call the MG3 remoting service. As before, we'll go ahead and see the entire code and then go through it's operation. |
| | 188 | |
| | 189 | The following code is a single html file. |
| | 190 | {{{ |
| | 191 | #!xml |
| | 192 | <script type="text/javascript" src="js/jquery.js"></script> |
| | 193 | |
| | 194 | <script type="text/javascript"> |
| | 195 | $(document).ready(function() { |
| | 196 | |
| | 197 | var mgrsURL = "/url_path_to_my_app/RemotingService.cfc?method=executeEvent&returnformat=json&queryFormat=column"; |
| | 198 | var args = new Object(); |
| | 199 | args.eventName = "get.users"; |
| | 200 | args.returnValues = "users"; |
| | 201 | |
| | 202 | //Various additional variables to pass with this request |
| | 203 | args.orderby = "name"; |
| | 204 | args.active = true; |
| | 205 | |
| | 206 | $.ajax({ |
| | 207 | type: "POST", |
| | 208 | url: mgrsURL, |
| | 209 | data: args, |
| | 210 | dataType: "json", |
| | 211 | success: function(qry){ |
| | 212 | |
| | 213 | $('.users').empty(); |
| | 214 | |
| | 215 | for(var i=0;i < qry.users.ROWCOUNT; i++){ |
| | 216 | var html = '<li>'+qry.users.DATA.name[i]+'</li>'; |
| | 217 | $('.users').append(html); |
| | 218 | }; |
| | 219 | } |
| | 220 | }); |
| | 221 | }); |
| | 222 | </script> |
| | 223 | |
| | 224 | <div class="users">Processing..</div> |
| | 225 | }}} |
| | 226 | |
| | 227 | The $(document).ready() fires up after the page is loaded. We start off by setting some variables. We will call the !RemotingService.cfc directly by url. When we do this, we can also specify the return format. The default is WDDX but the JSON format is prefer for AJAX applications. The "queryFormat=column" provides some additional information which we use in handling the loop later on. |
| | 228 | |
| | 229 | Like the previous Flex example, we have two critical variables to define. The first is the MG3 "event name" we wish to call and the second is the event object key (basically the returned values) we need back for the remoting call. We can also set additional arguments if required. |
| | 230 | |
| | 231 | If the call is successful, then we loop over the result and build out a html string of list items "<li>". This html string is then appended into the 'users' div area on the page. When you run this page you should initial see a single line saying "Processing.." for a second then the following result. |
| | 232 | |
| | 233 | (Image in progress) |
| | 234 | |
| | 235 | Again, this is a fairly simple example, but you can now see how the same MG3 Remoting service can power AJAX or Flex applications with ease. |