3 This file is part of Ext JS 4
5 Copyright (c) 2011 Sencha Inc
7 Contact: http://www.sencha.com/contact
9 GNU General Public License Usage
10 This file may be used under the terms of the GNU General Public License version 3.0 as published by the Free Software Foundation and appearing in the file LICENSE included in the packaging of this file. Please review the following information to ensure the GNU General Public License version 3.0 requirements will be met: http://www.gnu.org/copyleft/gpl.html.
12 If you are unsure which license is appropriate for your use, please contact the sales department at http://www.sencha.com/contact.
16 * @class Ext.direct.RemotingProvider
17 * @extends Ext.direct.JsonProvider
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>
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>
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>
31 Ext.define('Ext.direct.RemotingProvider', {
33 /* Begin Definitions */
35 alias: 'direct.remotingprovider',
37 extend: 'Ext.direct.JsonProvider',
40 'Ext.util.MixedCollection',
41 'Ext.util.DelayedTask',
42 'Ext.direct.Transaction',
43 'Ext.direct.RemotingMethod'
49 * @cfg {Object} actions
50 * Object literal defining the server side actions and methods. For example, if
51 * the Provider is configured with:
53 "actions":{ // each property within the 'actions' object represents a server side Class
54 "TestAction":[ // array of methods within each server side Class to be
55 { // stubbed out on client
59 "name":"multiply",// name of method
60 "len":2 // The number of parameters that will be used to create an
61 // array of data to send to the server side function.
62 // Ensure the server sends back a Number, not a String.
65 "formHandler":true, // direct the client to use specialized form handling method
70 * <p>Note that a Store is not required, a server method can be called at any time.
71 * In the following example a <b>client side</b> handler is used to call the
72 * server side method "multiply" in the server-side "TestAction" Class:</p>
75 2, 4, // pass two arguments to server, so specify len=2
76 // callback function after the server is called
77 // result: the result returned by the server
78 // e: Ext.direct.RemotingEvent object
80 var t = e.getTransaction();
81 var action = t.action; // server side Class called
82 var method = t.method; // server side method called
84 var answer = Ext.encode(result); // 8
87 var msg = e.message; // failure message
92 * In the example above, the server side "multiply" function will be passed two
93 * arguments (2 and 4). The "multiply" method should return the value 8 which will be
94 * available as the <tt>result</tt> in the example above.
98 * @cfg {String/Object} namespace
99 * Namespace for the Remoting Provider (defaults to the browser global scope of <i>window</i>).
100 * Explicitly specify the namespace Object, or specify a String to have a
101 * {@link Ext#namespace namespace created} implicitly.
106 * <b>Required</b>. The url to connect to the {@link Ext.direct.Manager} server-side router.
110 * @cfg {String} enableUrlEncode
111 * Specify which param will hold the arguments for the method.
112 * Defaults to <tt>'data'</tt>.
116 * @cfg {Number/Boolean} enableBuffer
117 * <p><tt>true</tt> or <tt>false</tt> to enable or disable combining of method
118 * calls. If a number is specified this is the amount of time in milliseconds
119 * to wait before sending a batched request.</p>
120 * <br><p>Calls which are received within the specified timeframe will be
121 * concatenated together and sent in a single request, optimizing the
122 * application by reducing the amount of round trips that have to be made
128 * @cfg {Number} maxRetries
129 * Number of times to re-attempt delivery on failure of a call.
134 * @cfg {Number} timeout
135 * The timeout to use for each request.
139 constructor : function(config){
141 me.callParent(arguments);
145 * Fires immediately before the client-side sends off the RPC call.
146 * By returning false from an event handler you can prevent the call from
148 * @param {Ext.direct.RemotingProvider} provider
149 * @param {Ext.direct.Transaction} transaction
150 * @param {Object} meta The meta data
155 * Fires immediately after the request to the server-side is sent. This does
156 * NOT fire after the response has come back from the call.
157 * @param {Ext.direct.RemotingProvider} provider
158 * @param {Ext.direct.Transaction} transaction
159 * @param {Object} meta The meta data
163 me.namespace = (Ext.isString(me.namespace)) ? Ext.ns(me.namespace) : me.namespace || window;
164 me.transactions = Ext.create('Ext.util.MixedCollection');
172 initAPI : function(){
173 var actions = this.actions,
174 namespace = this.namespace,
182 for (action in actions) {
183 cls = namespace[action];
185 cls = namespace[action] = {};
187 methods = actions[action];
189 for (i = 0, len = methods.length; i < len; ++i) {
190 method = Ext.create('Ext.direct.RemotingMethod', methods[i]);
191 cls[method.name] = this.createHandler(action, method);
197 * Create a handler function for a direct call.
199 * @param {String} action The action the call is for
200 * @param {Object} method The details of the method
201 * @return {Function} A JS function that will kick off the call
203 createHandler : function(action, method){
207 if (!method.formHandler) {
208 handler = function(){
209 me.configureRequest(action, method, Array.prototype.slice.call(arguments, 0));
212 handler = function(form, callback, scope){
213 me.configureFormRequest(action, method, form, callback, scope);
216 handler.directCfg = {
224 isConnected: function(){
225 return !!this.connected;
235 me.fireEvent('connect', me);
238 Ext.Error.raise('Error initializing RemotingProvider, no url configured.');
244 disconnect: function(){
248 me.connected = false;
249 me.fireEvent('disconnect', me);
254 * Run any callbacks related to the transaction.
256 * @param {Ext.direct.Transaction} transaction The transaction
257 * @param {Ext.direct.Event} event The event
259 runCallback: function(transaction, event){
260 var funcName = event.status ? 'success' : 'failure',
264 if (transaction && transaction.callback) {
265 callback = transaction.callback;
266 result = Ext.isDefined(event.result) ? event.result : event.data;
268 if (Ext.isFunction(callback)) {
269 callback(result, event);
271 Ext.callback(callback[funcName], callback.scope, [result, event]);
272 Ext.callback(callback.callback, callback.scope, [result, event]);
278 * React to the ajax request being completed
281 onData: function(options, success, response){
291 events = me.createEvents(response);
292 for (len = events.length; i < len; ++i) {
294 transaction = me.getTransaction(event);
295 me.fireEvent('data', me, event);
297 me.runCallback(transaction, event, true);
298 Ext.direct.Manager.removeTransaction(transaction);
302 transactions = [].concat(options.transaction);
303 for (len = transactions.length; i < len; ++i) {
304 transaction = me.getTransaction(transactions[i]);
305 if (transaction && transaction.retryCount < me.maxRetries) {
308 event = Ext.create('Ext.direct.ExceptionEvent', {
310 transaction: transaction,
311 code: Ext.direct.Manager.self.exceptions.TRANSPORT,
312 message: 'Unable to connect to the server.',
315 me.fireEvent('data', me, event);
317 me.runCallback(transaction, event, false);
318 Ext.direct.Manager.removeTransaction(transaction);
326 * Get transaction from XHR options
328 * @param {Object} options The options sent to the Ajax request
329 * @return {Ext.direct.Transaction} The transaction, null if not found
331 getTransaction: function(options){
332 return options && options.tid ? Ext.direct.Manager.getTransaction(options.tid) : null;
336 * Configure a direct request
338 * @param {String} action The action being executed
339 * @param {Object} method The being executed
341 configureRequest: function(action, method, args){
343 callData = method.getCallData(args),
344 data = callData.data,
345 callback = callData.callback,
346 scope = callData.scope,
349 transaction = Ext.create('Ext.direct.Transaction', {
355 callback: scope && Ext.isFunction(callback) ? Ext.Function.bind(callback, scope) : callback
358 if (me.fireEvent('beforecall', me, transaction, method) !== false) {
359 Ext.direct.Manager.addTransaction(transaction);
360 me.queueTransaction(transaction);
361 me.fireEvent('call', me, transaction, method);
366 * Gets the Ajax call info for a transaction
368 * @param {Ext.direct.Transaction} transaction The transaction
369 * @return {Object} The call params
371 getCallData: function(transaction){
373 action: transaction.action,
374 method: transaction.method,
375 data: transaction.data,
382 * Sends a request to the server
384 * @param {Object/Array} data The data to send
386 sendRequest : function(data){
395 enableUrlEncode = me.enableUrlEncode,
401 if (Ext.isArray(data)) {
403 for (len = data.length; i < len; ++i) {
404 callData.push(me.getCallData(data[i]));
407 callData = me.getCallData(data);
410 if (enableUrlEncode) {
412 params[Ext.isString(enableUrlEncode) ? enableUrlEncode : 'data'] = Ext.encode(callData);
413 request.params = params;
415 request.jsonData = callData;
417 Ext.Ajax.request(request);
421 * Add a new transaction to the queue
423 * @param {Ext.direct.Transaction} transaction The transaction
425 queueTransaction: function(transaction){
427 enableBuffer = me.enableBuffer;
429 if (transaction.form) {
430 me.sendFormRequest(transaction);
434 me.callBuffer.push(transaction);
437 me.callTask = Ext.create('Ext.util.DelayedTask', me.combineAndSend, me);
439 me.callTask.delay(Ext.isNumber(enableBuffer) ? enableBuffer : 10);
446 * Combine any buffered requests and send them off
449 combineAndSend : function(){
450 var buffer = this.callBuffer,
454 this.sendRequest(len == 1 ? buffer[0] : buffer);
455 this.callBuffer = [];
460 * Configure a form submission request
462 * @param {String} action The action being executed
463 * @param {Object} method The method being executed
464 * @param {HTMLElement} form The form being submitted
465 * @param {Function} callback (optional) A callback to run after the form submits
466 * @param {Object} scope (optional) A scope to execute the callback in
468 configureFormRequest : function(action, method, form, callback, scope){
470 transaction = Ext.create('Ext.direct.Transaction', {
474 args: [form, callback, scope],
475 callback: scope && Ext.isFunction(callback) ? Ext.Function.bind(callback, scope) : callback,
481 if (me.fireEvent('beforecall', me, transaction, method) !== false) {
482 Ext.direct.Manager.addTransaction(transaction);
483 isUpload = String(form.getAttribute("enctype")).toLowerCase() == 'multipart/form-data';
486 extTID: transaction.id,
488 extMethod: method.name,
490 extUpload: String(isUpload)
493 // change made from typeof callback check to callback.params
494 // to support addl param passing in DirectSubmit EAC 6/2
495 Ext.apply(transaction, {
496 form: Ext.getDom(form),
498 params: callback && Ext.isObject(callback.params) ? Ext.apply(params, callback.params) : params
500 me.fireEvent('call', me, transaction, method);
501 me.sendFormRequest(transaction);
506 * Sends a form request
508 * @param {Ext.direct.Transaction} transaction The transaction to send
510 sendFormRequest: function(transaction){
513 params: transaction.params,
514 callback: this.onData,
516 form: transaction.form,
517 isUpload: transaction.isUpload,
518 transaction: transaction