Upgrade to ExtJS 3.3.1 - Released 11/30/2010
[extjs.git] / docs / source / RemotingProvider.html
1 <html>
2 <head>
3   <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />    
4   <title>The source code</title>
5     <link href="../resources/prettify/prettify.css" type="text/css" rel="stylesheet" />
6     <script type="text/javascript" src="../resources/prettify/prettify.js"></script>
7 </head>
8 <body  onload="prettyPrint();">
9     <pre class="prettyprint lang-js">/*!
10  * Ext JS Library 3.3.1
11  * Copyright(c) 2006-2010 Sencha Inc.
12  * licensing@sencha.com
13  * http://www.sencha.com/license
14  */
15 <div id="cls-Ext.direct.RemotingProvider"></div>/**
16  * @class Ext.direct.RemotingProvider
17  * @extends Ext.direct.JsonProvider
18  * 
19  * <p>The {@link Ext.direct.RemotingProvider RemotingProvider} exposes access to
20  * server side methods on the client (a remote procedure call (RPC) type of
21  * connection where the client can initiate a procedure on the server).</p>
22  * 
23  * <p>This allows for code to be organized in a fashion that is maintainable,
24  * while providing a clear path between client and server, something that is
25  * not always apparent when using URLs.</p>
26  * 
27  * <p>To accomplish this the server-side needs to describe what classes and methods
28  * are available on the client-side. This configuration will typically be
29  * outputted by the server-side Ext.Direct stack when the API description is built.</p>
30  */
31 Ext.direct.RemotingProvider = Ext.extend(Ext.direct.JsonProvider, {       
32     <div id="cfg-Ext.direct.RemotingProvider-actions"></div>/**
33      * @cfg {Object} actions
34      * Object literal defining the server side actions and methods. For example, if
35      * the Provider is configured with:
36      * <pre><code>
37 "actions":{ // each property within the 'actions' object represents a server side Class 
38     "TestAction":[ // array of methods within each server side Class to be   
39     {              // stubbed out on client
40         "name":"doEcho", 
41         "len":1            
42     },{
43         "name":"multiply",// name of method
44         "len":2           // The number of parameters that will be used to create an
45                           // array of data to send to the server side function.
46                           // Ensure the server sends back a Number, not a String. 
47     },{
48         "name":"doForm",
49         "formHandler":true, // direct the client to use specialized form handling method 
50         "len":1
51     }]
52 }
53      * </code></pre>
54      * <p>Note that a Store is not required, a server method can be called at any time.
55      * In the following example a <b>client side</b> handler is used to call the
56      * server side method "multiply" in the server-side "TestAction" Class:</p>
57      * <pre><code>
58 TestAction.multiply(
59     2, 4, // pass two arguments to server, so specify len=2
60     // callback function after the server is called
61     // result: the result returned by the server
62     //      e: Ext.Direct.RemotingEvent object
63     function(result, e){
64         var t = e.getTransaction();
65         var action = t.action; // server side Class called
66         var method = t.method; // server side method called
67         if(e.status){
68             var answer = Ext.encode(result); // 8
69     
70         }else{
71             var msg = e.message; // failure message
72         }
73     }
74 );
75      * </code></pre>
76      * In the example above, the server side "multiply" function will be passed two
77      * arguments (2 and 4).  The "multiply" method should return the value 8 which will be
78      * available as the <tt>result</tt> in the example above. 
79      */
80     
81     <div id="cfg-Ext.direct.RemotingProvider-namespace"></div>/**
82      * @cfg {String/Object} namespace
83      * Namespace for the Remoting Provider (defaults to the browser global scope of <i>window</i>).
84      * Explicitly specify the namespace Object, or specify a String to have a
85      * {@link Ext#namespace namespace created} implicitly.
86      */
87     
88     <div id="cfg-Ext.direct.RemotingProvider-url"></div>/**
89      * @cfg {String} url
90      * <b>Required<b>. The url to connect to the {@link Ext.Direct} server-side router. 
91      */
92     
93     <div id="cfg-Ext.direct.RemotingProvider-enableUrlEncode"></div>/**
94      * @cfg {String} enableUrlEncode
95      * Specify which param will hold the arguments for the method.
96      * Defaults to <tt>'data'</tt>.
97      */
98     
99     <div id="cfg-Ext.direct.RemotingProvider-enableBuffer"></div>/**
100      * @cfg {Number/Boolean} enableBuffer
101      * <p><tt>true</tt> or <tt>false</tt> to enable or disable combining of method
102      * calls. If a number is specified this is the amount of time in milliseconds
103      * to wait before sending a batched request (defaults to <tt>10</tt>).</p>
104      * <br><p>Calls which are received within the specified timeframe will be
105      * concatenated together and sent in a single request, optimizing the
106      * application by reducing the amount of round trips that have to be made
107      * to the server.</p>
108      */
109     enableBuffer: 10,
110     
111     <div id="cfg-Ext.direct.RemotingProvider-maxRetries"></div>/**
112      * @cfg {Number} maxRetries
113      * Number of times to re-attempt delivery on failure of a call. Defaults to <tt>1</tt>.
114      */
115     maxRetries: 1,
116     
117     <div id="cfg-Ext.direct.RemotingProvider-timeout"></div>/**
118      * @cfg {Number} timeout
119      * The timeout to use for each request. Defaults to <tt>undefined</tt>.
120      */
121     timeout: undefined,
122
123     constructor : function(config){
124         Ext.direct.RemotingProvider.superclass.constructor.call(this, config);
125         this.addEvents(
126             <div id="event-Ext.direct.RemotingProvider-beforecall"></div>/**
127              * @event beforecall
128              * Fires immediately before the client-side sends off the RPC call.
129              * By returning false from an event handler you can prevent the call from
130              * executing.
131              * @param {Ext.direct.RemotingProvider} provider
132              * @param {Ext.Direct.Transaction} transaction
133              * @param {Object} meta The meta data
134              */            
135             'beforecall',            
136             <div id="event-Ext.direct.RemotingProvider-call"></div>/**
137              * @event call
138              * Fires immediately after the request to the server-side is sent. This does
139              * NOT fire after the response has come back from the call.
140              * @param {Ext.direct.RemotingProvider} provider
141              * @param {Ext.Direct.Transaction} transaction
142              * @param {Object} meta The meta data
143              */            
144             'call'
145         );
146         this.namespace = (Ext.isString(this.namespace)) ? Ext.ns(this.namespace) : this.namespace || window;
147         this.transactions = {};
148         this.callBuffer = [];
149     },
150
151     // private
152     initAPI : function(){
153         var o = this.actions;
154         for(var c in o){
155             var cls = this.namespace[c] || (this.namespace[c] = {}),
156                 ms = o[c];
157             for(var i = 0, len = ms.length; i < len; i++){
158                 var m = ms[i];
159                 cls[m.name] = this.createMethod(c, m);
160             }
161         }
162     },
163
164     // inherited
165     isConnected: function(){
166         return !!this.connected;
167     },
168
169     connect: function(){
170         if(this.url){
171             this.initAPI();
172             this.connected = true;
173             this.fireEvent('connect', this);
174         }else if(!this.url){
175             throw 'Error initializing RemotingProvider, no url configured.';
176         }
177     },
178
179     disconnect: function(){
180         if(this.connected){
181             this.connected = false;
182             this.fireEvent('disconnect', this);
183         }
184     },
185
186     onData: function(opt, success, xhr){
187         if(success){
188             var events = this.getEvents(xhr);
189             for(var i = 0, len = events.length; i < len; i++){
190                 var e = events[i],
191                     t = this.getTransaction(e);
192                 this.fireEvent('data', this, e);
193                 if(t){
194                     this.doCallback(t, e, true);
195                     Ext.Direct.removeTransaction(t);
196                 }
197             }
198         }else{
199             var ts = [].concat(opt.ts);
200             for(var i = 0, len = ts.length; i < len; i++){
201                 var t = this.getTransaction(ts[i]);
202                 if(t && t.retryCount < this.maxRetries){
203                     t.retry();
204                 }else{
205                     var e = new Ext.Direct.ExceptionEvent({
206                         data: e,
207                         transaction: t,
208                         code: Ext.Direct.exceptions.TRANSPORT,
209                         message: 'Unable to connect to the server.',
210                         xhr: xhr
211                     });
212                     this.fireEvent('data', this, e);
213                     if(t){
214                         this.doCallback(t, e, false);
215                         Ext.Direct.removeTransaction(t);
216                     }
217                 }
218             }
219         }
220     },
221
222     getCallData: function(t){
223         return {
224             action: t.action,
225             method: t.method,
226             data: t.data,
227             type: 'rpc',
228             tid: t.tid
229         };
230     },
231
232     doSend : function(data){
233         var o = {
234             url: this.url,
235             callback: this.onData,
236             scope: this,
237             ts: data,
238             timeout: this.timeout
239         }, callData;
240
241         if(Ext.isArray(data)){
242             callData = [];
243             for(var i = 0, len = data.length; i < len; i++){
244                 callData.push(this.getCallData(data[i]));
245             }
246         }else{
247             callData = this.getCallData(data);
248         }
249
250         if(this.enableUrlEncode){
251             var params = {};
252             params[Ext.isString(this.enableUrlEncode) ? this.enableUrlEncode : 'data'] = Ext.encode(callData);
253             o.params = params;
254         }else{
255             o.jsonData = callData;
256         }
257         Ext.Ajax.request(o);
258     },
259
260     combineAndSend : function(){
261         var len = this.callBuffer.length;
262         if(len > 0){
263             this.doSend(len == 1 ? this.callBuffer[0] : this.callBuffer);
264             this.callBuffer = [];
265         }
266     },
267
268     queueTransaction: function(t){
269         if(t.form){
270             this.processForm(t);
271             return;
272         }
273         this.callBuffer.push(t);
274         if(this.enableBuffer){
275             if(!this.callTask){
276                 this.callTask = new Ext.util.DelayedTask(this.combineAndSend, this);
277             }
278             this.callTask.delay(Ext.isNumber(this.enableBuffer) ? this.enableBuffer : 10);
279         }else{
280             this.combineAndSend();
281         }
282     },
283
284     doCall : function(c, m, args){
285         var data = null, hs = args[m.len], scope = args[m.len+1];
286
287         if(m.len !== 0){
288             data = args.slice(0, m.len);
289         }
290
291         var t = new Ext.Direct.Transaction({
292             provider: this,
293             args: args,
294             action: c,
295             method: m.name,
296             data: data,
297             cb: scope && Ext.isFunction(hs) ? hs.createDelegate(scope) : hs
298         });
299
300         if(this.fireEvent('beforecall', this, t, m) !== false){
301             Ext.Direct.addTransaction(t);
302             this.queueTransaction(t);
303             this.fireEvent('call', this, t, m);
304         }
305     },
306
307     doForm : function(c, m, form, callback, scope){
308         var t = new Ext.Direct.Transaction({
309             provider: this,
310             action: c,
311             method: m.name,
312             args:[form, callback, scope],
313             cb: scope && Ext.isFunction(callback) ? callback.createDelegate(scope) : callback,
314             isForm: true
315         });
316
317         if(this.fireEvent('beforecall', this, t, m) !== false){
318             Ext.Direct.addTransaction(t);
319             var isUpload = String(form.getAttribute("enctype")).toLowerCase() == 'multipart/form-data',
320                 params = {
321                     extTID: t.tid,
322                     extAction: c,
323                     extMethod: m.name,
324                     extType: 'rpc',
325                     extUpload: String(isUpload)
326                 };
327             
328             // change made from typeof callback check to callback.params
329             // to support addl param passing in DirectSubmit EAC 6/2
330             Ext.apply(t, {
331                 form: Ext.getDom(form),
332                 isUpload: isUpload,
333                 params: callback && Ext.isObject(callback.params) ? Ext.apply(params, callback.params) : params
334             });
335             this.fireEvent('call', this, t, m);
336             this.processForm(t);
337         }
338     },
339     
340     processForm: function(t){
341         Ext.Ajax.request({
342             url: this.url,
343             params: t.params,
344             callback: this.onData,
345             scope: this,
346             form: t.form,
347             isUpload: t.isUpload,
348             ts: t
349         });
350     },
351
352     createMethod : function(c, m){
353         var f;
354         if(!m.formHandler){
355             f = function(){
356                 this.doCall(c, m, Array.prototype.slice.call(arguments, 0));
357             }.createDelegate(this);
358         }else{
359             f = function(form, callback, scope){
360                 this.doForm(c, m, form, callback, scope);
361             }.createDelegate(this);
362         }
363         f.directCfg = {
364             action: c,
365             method: m
366         };
367         return f;
368     },
369
370     getTransaction: function(opt){
371         return opt && opt.tid ? Ext.Direct.getTransaction(opt.tid) : null;
372     },
373
374     doCallback: function(t, e){
375         var fn = e.status ? 'success' : 'failure';
376         if(t && t.cb){
377             var hs = t.cb,
378                 result = Ext.isDefined(e.result) ? e.result : e.data;
379             if(Ext.isFunction(hs)){
380                 hs(result, e);
381             } else{
382                 Ext.callback(hs[fn], hs.scope, [result, e]);
383                 Ext.callback(hs.callback, hs.scope, [result, e]);
384             }
385         }
386     }
387 });
388 Ext.Direct.PROVIDERS['remoting'] = Ext.direct.RemotingProvider;</pre>    
389 </body>
390 </html>