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