Upgrade to ExtJS 4.0.7 - Released 10/19/2011
[extjs.git] / docs / source / RemotingProvider.html
1 <!DOCTYPE html>
2 <html>
3 <head>
4   <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
5   <title>The source code</title>
6   <link href="../resources/prettify/prettify.css" type="text/css" rel="stylesheet" />
7   <script type="text/javascript" src="../resources/prettify/prettify.js"></script>
8   <style type="text/css">
9     .highlight { display: block; background-color: #ddd; }
10   </style>
11   <script type="text/javascript">
12     function highlight() {
13       document.getElementById(location.hash.replace(/#/, "")).className = "highlight";
14     }
15   </script>
16 </head>
17 <body onload="prettyPrint(); highlight();">
18   <pre class="prettyprint lang-js"><span id='Ext-direct-RemotingProvider'>/**
19 </span> * @class Ext.direct.RemotingProvider
20  * @extends Ext.direct.JsonProvider
21  * 
22  * &lt;p&gt;The {@link Ext.direct.RemotingProvider RemotingProvider} exposes access to
23  * server side methods on the client (a remote procedure call (RPC) type of
24  * connection where the client can initiate a procedure on the server).&lt;/p&gt;
25  * 
26  * &lt;p&gt;This allows for code to be organized in a fashion that is maintainable,
27  * while providing a clear path between client and server, something that is
28  * not always apparent when using URLs.&lt;/p&gt;
29  * 
30  * &lt;p&gt;To accomplish this the server-side needs to describe what classes and methods
31  * are available on the client-side. This configuration will typically be
32  * outputted by the server-side Ext.Direct stack when the API description is built.&lt;/p&gt;
33  */
34 Ext.define('Ext.direct.RemotingProvider', {
35     
36     /* Begin Definitions */
37    
38     alias: 'direct.remotingprovider',
39     
40     extend: 'Ext.direct.JsonProvider', 
41     
42     requires: [
43         'Ext.util.MixedCollection', 
44         'Ext.util.DelayedTask', 
45         'Ext.direct.Transaction',
46         'Ext.direct.RemotingMethod'
47     ],
48    
49     /* End Definitions */
50    
51 <span id='Ext-direct-RemotingProvider-cfg-actions'>   /**
52 </span>     * @cfg {Object} actions
53      * Object literal defining the server side actions and methods. For example, if
54      * the Provider is configured with:
55      * &lt;pre&gt;&lt;code&gt;
56 &quot;actions&quot;:{ // each property within the 'actions' object represents a server side Class 
57     &quot;TestAction&quot;:[ // array of methods within each server side Class to be   
58     {              // stubbed out on client
59         &quot;name&quot;:&quot;doEcho&quot;, 
60         &quot;len&quot;:1            
61     },{
62         &quot;name&quot;:&quot;multiply&quot;,// name of method
63         &quot;len&quot;:2           // The number of parameters that will be used to create an
64                           // array of data to send to the server side function.
65                           // Ensure the server sends back a Number, not a String. 
66     },{
67         &quot;name&quot;:&quot;doForm&quot;,
68         &quot;formHandler&quot;:true, // direct the client to use specialized form handling method 
69         &quot;len&quot;:1
70     }]
71 }
72      * &lt;/code&gt;&lt;/pre&gt;
73      * &lt;p&gt;Note that a Store is not required, a server method can be called at any time.
74      * In the following example a &lt;b&gt;client side&lt;/b&gt; handler is used to call the
75      * server side method &quot;multiply&quot; in the server-side &quot;TestAction&quot; Class:&lt;/p&gt;
76      * &lt;pre&gt;&lt;code&gt;
77 TestAction.multiply(
78     2, 4, // pass two arguments to server, so specify len=2
79     // callback function after the server is called
80     // result: the result returned by the server
81     //      e: Ext.direct.RemotingEvent object
82     function(result, e){
83         var t = e.getTransaction();
84         var action = t.action; // server side Class called
85         var method = t.method; // server side method called
86         if(e.status){
87             var answer = Ext.encode(result); // 8
88     
89         }else{
90             var msg = e.message; // failure message
91         }
92     }
93 );
94      * &lt;/code&gt;&lt;/pre&gt;
95      * In the example above, the server side &quot;multiply&quot; function will be passed two
96      * arguments (2 and 4).  The &quot;multiply&quot; method should return the value 8 which will be
97      * available as the &lt;tt&gt;result&lt;/tt&gt; in the example above. 
98      */
99     
100 <span id='Ext-direct-RemotingProvider-cfg-namespace'>    /**
101 </span>     * @cfg {String/Object} namespace
102      * Namespace for the Remoting Provider (defaults to the browser global scope of &lt;i&gt;window&lt;/i&gt;).
103      * Explicitly specify the namespace Object, or specify a String to have a
104      * {@link Ext#namespace namespace created} implicitly.
105      */
106     
107 <span id='Ext-direct-RemotingProvider-cfg-url'>    /**
108 </span>     * @cfg {String} url
109      * &lt;b&gt;Required&lt;/b&gt;. The url to connect to the {@link Ext.direct.Manager} server-side router. 
110      */
111     
112 <span id='Ext-direct-RemotingProvider-cfg-enableUrlEncode'>    /**
113 </span>     * @cfg {String} enableUrlEncode
114      * Specify which param will hold the arguments for the method.
115      * Defaults to &lt;tt&gt;'data'&lt;/tt&gt;.
116      */
117     
118 <span id='Ext-direct-RemotingProvider-cfg-enableBuffer'>    /**
119 </span>     * @cfg {Number/Boolean} enableBuffer
120      * &lt;p&gt;&lt;tt&gt;true&lt;/tt&gt; or &lt;tt&gt;false&lt;/tt&gt; to enable or disable combining of method
121      * calls. If a number is specified this is the amount of time in milliseconds
122      * to wait before sending a batched request.&lt;/p&gt;
123      * &lt;br&gt;&lt;p&gt;Calls which are received within the specified timeframe will be
124      * concatenated together and sent in a single request, optimizing the
125      * application by reducing the amount of round trips that have to be made
126      * to the server.&lt;/p&gt;
127      */
128     enableBuffer: 10,
129     
130 <span id='Ext-direct-RemotingProvider-cfg-maxRetries'>    /**
131 </span>     * @cfg {Number} maxRetries
132      * Number of times to re-attempt delivery on failure of a call.
133      */
134     maxRetries: 1,
135     
136 <span id='Ext-direct-RemotingProvider-cfg-timeout'>    /**
137 </span>     * @cfg {Number} timeout
138      * The timeout to use for each request.
139      */
140     timeout: undefined,
141     
142     constructor : function(config){
143         var me = this;
144         me.callParent(arguments);
145         me.addEvents(
146 <span id='Ext-direct-RemotingProvider-event-beforecall'>            /**
147 </span>             * @event beforecall
148              * Fires immediately before the client-side sends off the RPC call.
149              * By returning false from an event handler you can prevent the call from
150              * executing.
151              * @param {Ext.direct.RemotingProvider} provider
152              * @param {Ext.direct.Transaction} transaction
153              * @param {Object} meta The meta data
154              */            
155             'beforecall',            
156 <span id='Ext-direct-RemotingProvider-event-call'>            /**
157 </span>             * @event call
158              * Fires immediately after the request to the server-side is sent. This does
159              * NOT fire after the response has come back from the call.
160              * @param {Ext.direct.RemotingProvider} provider
161              * @param {Ext.direct.Transaction} transaction
162              * @param {Object} meta The meta data
163              */            
164             'call'
165         );
166         me.namespace = (Ext.isString(me.namespace)) ? Ext.ns(me.namespace) : me.namespace || window;
167         me.transactions = Ext.create('Ext.util.MixedCollection');
168         me.callBuffer = [];
169     },
170     
171 <span id='Ext-direct-RemotingProvider-method-initAPI'>    /**
172 </span>     * Initialize the API
173      * @private
174      */
175     initAPI : function(){
176         var actions = this.actions,
177             namespace = this.namespace,
178             action,
179             cls,
180             methods,
181             i,
182             len,
183             method;
184             
185         for (action in actions) {
186             cls = namespace[action];
187             if (!cls) {
188                 cls = namespace[action] = {};
189             }
190             methods = actions[action];
191             
192             for (i = 0, len = methods.length; i &lt; len; ++i) {
193                 method = Ext.create('Ext.direct.RemotingMethod', methods[i]);
194                 cls[method.name] = this.createHandler(action, method);
195             }
196         }
197     },
198     
199 <span id='Ext-direct-RemotingProvider-method-createHandler'>    /**
200 </span>     * Create a handler function for a direct call.
201      * @private
202      * @param {String} action The action the call is for
203      * @param {Object} method The details of the method
204      * @return {Function} A JS function that will kick off the call
205      */
206     createHandler : function(action, method){
207         var me = this,
208             handler;
209         
210         if (!method.formHandler) {
211             handler = function(){
212                 me.configureRequest(action, method, Array.prototype.slice.call(arguments, 0));
213             };
214         } else {
215             handler = function(form, callback, scope){
216                 me.configureFormRequest(action, method, form, callback, scope);
217             };
218         }
219         handler.directCfg = {
220             action: action,
221             method: method
222         };
223         return handler;
224     },
225     
226     // inherit docs
227     isConnected: function(){
228         return !!this.connected;
229     },
230
231     // inherit docs
232     connect: function(){
233         var me = this;
234         
235         if (me.url) {
236             me.initAPI();
237             me.connected = true;
238             me.fireEvent('connect', me);
239         } else if(!me.url) {
240             //&lt;debug&gt;
241             Ext.Error.raise('Error initializing RemotingProvider, no url configured.');
242             //&lt;/debug&gt;
243         }
244     },
245
246     // inherit docs
247     disconnect: function(){
248         var me = this;
249         
250         if (me.connected) {
251             me.connected = false;
252             me.fireEvent('disconnect', me);
253         }
254     },
255     
256 <span id='Ext-direct-RemotingProvider-method-runCallback'>    /**
257 </span>     * Run any callbacks related to the transaction.
258      * @private
259      * @param {Ext.direct.Transaction} transaction The transaction
260      * @param {Ext.direct.Event} event The event
261      */
262     runCallback: function(transaction, event){
263         var funcName = event.status ? 'success' : 'failure',
264             callback,
265             result;
266         
267         if (transaction &amp;&amp; transaction.callback) {
268             callback = transaction.callback;
269             result = Ext.isDefined(event.result) ? event.result : event.data;
270         
271             if (Ext.isFunction(callback)) {
272                 callback(result, event);
273             } else {
274                 Ext.callback(callback[funcName], callback.scope, [result, event]);
275                 Ext.callback(callback.callback, callback.scope, [result, event]);
276             }
277         }
278     },
279     
280 <span id='Ext-direct-RemotingProvider-method-onData'>    /**
281 </span>     * React to the ajax request being completed
282      * @private
283      */
284     onData: function(options, success, response){
285         var me = this,
286             i = 0,
287             len,
288             events,
289             event,
290             transaction,
291             transactions;
292             
293         if (success) {
294             events = me.createEvents(response);
295             for (len = events.length; i &lt; len; ++i) {
296                 event = events[i];
297                 transaction = me.getTransaction(event);
298                 me.fireEvent('data', me, event);
299                 if (transaction) {
300                     me.runCallback(transaction, event, true);
301                     Ext.direct.Manager.removeTransaction(transaction);
302                 }
303             }
304         } else {
305             transactions = [].concat(options.transaction);
306             for (len = transactions.length; i &lt; len; ++i) {
307                 transaction = me.getTransaction(transactions[i]);
308                 if (transaction &amp;&amp; transaction.retryCount &lt; me.maxRetries) {
309                     transaction.retry();
310                 } else {
311                     event = Ext.create('Ext.direct.ExceptionEvent', {
312                         data: null,
313                         transaction: transaction,
314                         code: Ext.direct.Manager.self.exceptions.TRANSPORT,
315                         message: 'Unable to connect to the server.',
316                         xhr: response
317                     });
318                     me.fireEvent('data', me, event);
319                     if (transaction) {
320                         me.runCallback(transaction, event, false);
321                         Ext.direct.Manager.removeTransaction(transaction);
322                     }
323                 }
324             }
325         }
326     },
327     
328 <span id='Ext-direct-RemotingProvider-method-getTransaction'>    /**
329 </span>     * Get transaction from XHR options
330      * @private
331      * @param {Object} options The options sent to the Ajax request
332      * @return {Ext.direct.Transaction} The transaction, null if not found
333      */
334     getTransaction: function(options){
335         return options &amp;&amp; options.tid ? Ext.direct.Manager.getTransaction(options.tid) : null;
336     },
337     
338 <span id='Ext-direct-RemotingProvider-method-configureRequest'>    /**
339 </span>     * Configure a direct request
340      * @private
341      * @param {String} action The action being executed
342      * @param {Object} method The being executed
343      */
344     configureRequest: function(action, method, args){
345         var me = this,
346             callData = method.getCallData(args),
347             data = callData.data, 
348             callback = callData.callback, 
349             scope = callData.scope,
350             transaction;
351
352         transaction = Ext.create('Ext.direct.Transaction', {
353             provider: me,
354             args: args,
355             action: action,
356             method: method.name,
357             data: data,
358             callback: scope &amp;&amp; Ext.isFunction(callback) ? Ext.Function.bind(callback, scope) : callback
359         });
360
361         if (me.fireEvent('beforecall', me, transaction, method) !== false) {
362             Ext.direct.Manager.addTransaction(transaction);
363             me.queueTransaction(transaction);
364             me.fireEvent('call', me, transaction, method);
365         }
366     },
367     
368 <span id='Ext-direct-RemotingProvider-method-getCallData'>    /**
369 </span>     * Gets the Ajax call info for a transaction
370      * @private
371      * @param {Ext.direct.Transaction} transaction The transaction
372      * @return {Object} The call params
373      */
374     getCallData: function(transaction){
375         return {
376             action: transaction.action,
377             method: transaction.method,
378             data: transaction.data,
379             type: 'rpc',
380             tid: transaction.id
381         };
382     },
383     
384 <span id='Ext-direct-RemotingProvider-method-sendRequest'>    /**
385 </span>     * Sends a request to the server
386      * @private
387      * @param {Object/Array} data The data to send
388      */
389     sendRequest : function(data){
390         var me = this,
391             request = {
392                 url: me.url,
393                 callback: me.onData,
394                 scope: me,
395                 transaction: data,
396                 timeout: me.timeout
397             }, callData,
398             enableUrlEncode = me.enableUrlEncode,
399             i = 0,
400             len,
401             params;
402             
403
404         if (Ext.isArray(data)) {
405             callData = [];
406             for (len = data.length; i &lt; len; ++i) {
407                 callData.push(me.getCallData(data[i]));
408             }
409         } else {
410             callData = me.getCallData(data);
411         }
412
413         if (enableUrlEncode) {
414             params = {};
415             params[Ext.isString(enableUrlEncode) ? enableUrlEncode : 'data'] = Ext.encode(callData);
416             request.params = params;
417         } else {
418             request.jsonData = callData;
419         }
420         Ext.Ajax.request(request);
421     },
422     
423 <span id='Ext-direct-RemotingProvider-method-queueTransaction'>    /**
424 </span>     * Add a new transaction to the queue
425      * @private
426      * @param {Ext.direct.Transaction} transaction The transaction
427      */
428     queueTransaction: function(transaction){
429         var me = this,
430             enableBuffer = me.enableBuffer;
431         
432         if (transaction.form) {
433             me.sendFormRequest(transaction);
434             return;
435         }
436         
437         me.callBuffer.push(transaction);
438         if (enableBuffer) {
439             if (!me.callTask) {
440                 me.callTask = Ext.create('Ext.util.DelayedTask', me.combineAndSend, me);
441             }
442             me.callTask.delay(Ext.isNumber(enableBuffer) ? enableBuffer : 10);
443         } else {
444             me.combineAndSend();
445         }
446     },
447     
448 <span id='Ext-direct-RemotingProvider-method-combineAndSend'>    /**
449 </span>     * Combine any buffered requests and send them off
450      * @private
451      */
452     combineAndSend : function(){
453         var buffer = this.callBuffer,
454             len = buffer.length;
455             
456         if (len &gt; 0) {
457             this.sendRequest(len == 1 ? buffer[0] : buffer);
458             this.callBuffer = [];
459         }
460     },
461     
462 <span id='Ext-direct-RemotingProvider-method-configureFormRequest'>    /**
463 </span>     * Configure a form submission request
464      * @private
465      * @param {String} action The action being executed
466      * @param {Object} method The method being executed
467      * @param {HTMLElement} form The form being submitted
468      * @param {Function} callback (optional) A callback to run after the form submits
469      * @param {Object} scope (optional) A scope to execute the callback in
470      */
471     configureFormRequest : function(action, method, form, callback, scope){
472         var me = this,
473             transaction = Ext.create('Ext.direct.Transaction', {
474                 provider: me,
475                 action: action,
476                 method: method.name,
477                 args: [form, callback, scope],
478                 callback: scope &amp;&amp; Ext.isFunction(callback) ? Ext.Function.bind(callback, scope) : callback,
479                 isForm: true
480             }),
481             isUpload,
482             params;
483
484         if (me.fireEvent('beforecall', me, transaction, method) !== false) {
485             Ext.direct.Manager.addTransaction(transaction);
486             isUpload = String(form.getAttribute(&quot;enctype&quot;)).toLowerCase() == 'multipart/form-data';
487             
488             params = {
489                 extTID: transaction.id,
490                 extAction: action,
491                 extMethod: method.name,
492                 extType: 'rpc',
493                 extUpload: String(isUpload)
494             };
495             
496             // change made from typeof callback check to callback.params
497             // to support addl param passing in DirectSubmit EAC 6/2
498             Ext.apply(transaction, {
499                 form: Ext.getDom(form),
500                 isUpload: isUpload,
501                 params: callback &amp;&amp; Ext.isObject(callback.params) ? Ext.apply(params, callback.params) : params
502             });
503             me.fireEvent('call', me, transaction, method);
504             me.sendFormRequest(transaction);
505         }
506     },
507     
508 <span id='Ext-direct-RemotingProvider-method-sendFormRequest'>    /**
509 </span>     * Sends a form request
510      * @private
511      * @param {Ext.direct.Transaction} transaction The transaction to send
512      */
513     sendFormRequest: function(transaction){
514         Ext.Ajax.request({
515             url: this.url,
516             params: transaction.params,
517             callback: this.onData,
518             scope: this,
519             form: transaction.form,
520             isUpload: transaction.isUpload,
521             transaction: transaction
522         });
523     }
524     
525 });
526 </pre>
527 </body>
528 </html>