Upgrade to ExtJS 3.2.0 - Released 03/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.2.0
11  * Copyright(c) 2006-2010 Ext JS, Inc.
12  * licensing@extjs.com
13  * http://www.extjs.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              */            
134             'beforecall',            
135             <div id="event-Ext.direct.RemotingProvider-call"></div>/**
136              * @event call
137              * Fires immediately after the request to the server-side is sent. This does
138              * NOT fire after the response has come back from the call.
139              * @param {Ext.direct.RemotingProvider} provider
140              * @param {Ext.Direct.Transaction} transaction
141              */            
142             'call'
143         );
144         this.namespace = (Ext.isString(this.namespace)) ? Ext.ns(this.namespace) : this.namespace || window;
145         this.transactions = {};
146         this.callBuffer = [];
147     },
148
149     // private
150     initAPI : function(){
151         var o = this.actions;
152         for(var c in o){
153             var cls = this.namespace[c] || (this.namespace[c] = {}),
154                 ms = o[c];
155             for(var i = 0, len = ms.length; i < len; i++){
156                 var m = ms[i];
157                 cls[m.name] = this.createMethod(c, m);
158             }
159         }
160     },
161
162     // inherited
163     isConnected: function(){
164         return !!this.connected;
165     },
166
167     connect: function(){
168         if(this.url){
169             this.initAPI();
170             this.connected = true;
171             this.fireEvent('connect', this);
172         }else if(!this.url){
173             throw 'Error initializing RemotingProvider, no url configured.';
174         }
175     },
176
177     disconnect: function(){
178         if(this.connected){
179             this.connected = false;
180             this.fireEvent('disconnect', this);
181         }
182     },
183
184     onData: function(opt, success, xhr){
185         if(success){
186             var events = this.getEvents(xhr);
187             for(var i = 0, len = events.length; i < len; i++){
188                 var e = events[i],
189                     t = this.getTransaction(e);
190                 this.fireEvent('data', this, e);
191                 if(t){
192                     this.doCallback(t, e, true);
193                     Ext.Direct.removeTransaction(t);
194                 }
195             }
196         }else{
197             var ts = [].concat(opt.ts);
198             for(var i = 0, len = ts.length; i < len; i++){
199                 var t = this.getTransaction(ts[i]);
200                 if(t && t.retryCount < this.maxRetries){
201                     t.retry();
202                 }else{
203                     var e = new Ext.Direct.ExceptionEvent({
204                         data: e,
205                         transaction: t,
206                         code: Ext.Direct.exceptions.TRANSPORT,
207                         message: 'Unable to connect to the server.',
208                         xhr: xhr
209                     });
210                     this.fireEvent('data', this, e);
211                     if(t){
212                         this.doCallback(t, e, false);
213                         Ext.Direct.removeTransaction(t);
214                     }
215                 }
216             }
217         }
218     },
219
220     getCallData: function(t){
221         return {
222             action: t.action,
223             method: t.method,
224             data: t.data,
225             type: 'rpc',
226             tid: t.tid
227         };
228     },
229
230     doSend : function(data){
231         var o = {
232             url: this.url,
233             callback: this.onData,
234             scope: this,
235             ts: data,
236             timeout: this.timeout
237         }, callData;
238
239         if(Ext.isArray(data)){
240             callData = [];
241             for(var i = 0, len = data.length; i < len; i++){
242                 callData.push(this.getCallData(data[i]));
243             }
244         }else{
245             callData = this.getCallData(data);
246         }
247
248         if(this.enableUrlEncode){
249             var params = {};
250             params[Ext.isString(this.enableUrlEncode) ? this.enableUrlEncode : 'data'] = Ext.encode(callData);
251             o.params = params;
252         }else{
253             o.jsonData = callData;
254         }
255         Ext.Ajax.request(o);
256     },
257
258     combineAndSend : function(){
259         var len = this.callBuffer.length;
260         if(len > 0){
261             this.doSend(len == 1 ? this.callBuffer[0] : this.callBuffer);
262             this.callBuffer = [];
263         }
264     },
265
266     queueTransaction: function(t){
267         if(t.form){
268             this.processForm(t);
269             return;
270         }
271         this.callBuffer.push(t);
272         if(this.enableBuffer){
273             if(!this.callTask){
274                 this.callTask = new Ext.util.DelayedTask(this.combineAndSend, this);
275             }
276             this.callTask.delay(Ext.isNumber(this.enableBuffer) ? this.enableBuffer : 10);
277         }else{
278             this.combineAndSend();
279         }
280     },
281
282     doCall : function(c, m, args){
283         var data = null, hs = args[m.len], scope = args[m.len+1];
284
285         if(m.len !== 0){
286             data = args.slice(0, m.len);
287         }
288
289         var t = new Ext.Direct.Transaction({
290             provider: this,
291             args: args,
292             action: c,
293             method: m.name,
294             data: data,
295             cb: scope && Ext.isFunction(hs) ? hs.createDelegate(scope) : hs
296         });
297
298         if(this.fireEvent('beforecall', this, t) !== false){
299             Ext.Direct.addTransaction(t);
300             this.queueTransaction(t);
301             this.fireEvent('call', this, t);
302         }
303     },
304
305     doForm : function(c, m, form, callback, scope){
306         var t = new Ext.Direct.Transaction({
307             provider: this,
308             action: c,
309             method: m.name,
310             args:[form, callback, scope],
311             cb: scope && Ext.isFunction(callback) ? callback.createDelegate(scope) : callback,
312             isForm: true
313         });
314
315         if(this.fireEvent('beforecall', this, t) !== false){
316             Ext.Direct.addTransaction(t);
317             var isUpload = String(form.getAttribute("enctype")).toLowerCase() == 'multipart/form-data',
318                 params = {
319                     extTID: t.tid,
320                     extAction: c,
321                     extMethod: m.name,
322                     extType: 'rpc',
323                     extUpload: String(isUpload)
324                 };
325             
326             // change made from typeof callback check to callback.params
327             // to support addl param passing in DirectSubmit EAC 6/2
328             Ext.apply(t, {
329                 form: Ext.getDom(form),
330                 isUpload: isUpload,
331                 params: callback && Ext.isObject(callback.params) ? Ext.apply(params, callback.params) : params
332             });
333             this.fireEvent('call', this, t);
334             this.processForm(t);
335         }
336     },
337     
338     processForm: function(t){
339         Ext.Ajax.request({
340             url: this.url,
341             params: t.params,
342             callback: this.onData,
343             scope: this,
344             form: t.form,
345             isUpload: t.isUpload,
346             ts: t
347         });
348     },
349
350     createMethod : function(c, m){
351         var f;
352         if(!m.formHandler){
353             f = function(){
354                 this.doCall(c, m, Array.prototype.slice.call(arguments, 0));
355             }.createDelegate(this);
356         }else{
357             f = function(form, callback, scope){
358                 this.doForm(c, m, form, callback, scope);
359             }.createDelegate(this);
360         }
361         f.directCfg = {
362             action: c,
363             method: m
364         };
365         return f;
366     },
367
368     getTransaction: function(opt){
369         return opt && opt.tid ? Ext.Direct.getTransaction(opt.tid) : null;
370     },
371
372     doCallback: function(t, e){
373         var fn = e.status ? 'success' : 'failure';
374         if(t && t.cb){
375             var hs = t.cb,
376                 result = Ext.isDefined(e.result) ? e.result : e.data;
377             if(Ext.isFunction(hs)){
378                 hs(result, e);
379             } else{
380                 Ext.callback(hs[fn], hs.scope, [result, e]);
381                 Ext.callback(hs.callback, hs.scope, [result, e]);
382             }
383         }
384     }
385 });
386 Ext.Direct.PROVIDERS['remoting'] = Ext.direct.RemotingProvider;</pre>    
387 </body>
388 </html>