2 * @class Ext.direct.RemotingProvider
3 * @extends Ext.direct.JsonProvider
5 * <p>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).</p>
9 * <p>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.</p>
13 * <p>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.</p>
17 Ext.define('Ext.direct.RemotingProvider', {
19 /* Begin Definitions */
21 alias: 'direct.remotingprovider',
23 extend: 'Ext.direct.JsonProvider',
26 'Ext.util.MixedCollection',
27 'Ext.util.DelayedTask',
28 'Ext.direct.Transaction',
29 'Ext.direct.RemotingMethod'
35 * @cfg {Object} actions
36 * Object literal defining the server side actions and methods. For example, if
37 * the Provider is configured with:
39 "actions":{ // each property within the 'actions' object represents a server side Class
40 "TestAction":[ // array of methods within each server side Class to be
41 { // stubbed out on client
45 "name":"multiply",// name of method
46 "len":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.
51 "formHandler":true, // direct the client to use specialized form handling method
56 * <p>Note that a Store is not required, a server method can be called at any time.
57 * In the following example a <b>client side</b> handler is used to call the
58 * server side method "multiply" in the server-side "TestAction" Class:</p>
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
66 var t = e.getTransaction();
67 var action = t.action; // server side Class called
68 var method = t.method; // server side method called
70 var answer = Ext.encode(result); // 8
73 var msg = e.message; // failure message
78 * In the example above, the server side "multiply" function will be passed two
79 * arguments (2 and 4). The "multiply" method should return the value 8 which will be
80 * available as the <tt>result</tt> in the example above.
84 * @cfg {String/Object} namespace
85 * Namespace for the Remoting Provider (defaults to the browser global scope of <i>window</i>).
86 * Explicitly specify the namespace Object, or specify a String to have a
87 * {@link Ext#namespace namespace created} implicitly.
92 * <b>Required<b>. The url to connect to the {@link Ext.direct.Manager} server-side router.
96 * @cfg {String} enableUrlEncode
97 * Specify which param will hold the arguments for the method.
98 * Defaults to <tt>'data'</tt>.
102 * @cfg {Number/Boolean} enableBuffer
103 * <p><tt>true</tt> or <tt>false</tt> 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 <tt>10</tt>).</p>
106 * <br><p>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
114 * @cfg {Number} maxRetries
115 * Number of times to re-attempt delivery on failure of a call. Defaults to <tt>1</tt>.
120 * @cfg {Number} timeout
121 * The timeout to use for each request. Defaults to <tt>undefined</tt>.
125 constructor : function(config){
127 me.callParent(arguments);
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
134 * @param {Ext.direct.RemotingProvider} provider
135 * @param {Ext.direct.Transaction} transaction
136 * @param {Object} meta The meta data
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
149 me.namespace = (Ext.isString(me.namespace)) ? Ext.ns(me.namespace) : me.namespace || window;
150 me.transactions = Ext.create('Ext.util.MixedCollection');
158 initAPI : function(){
159 var actions = this.actions,
160 namespace = this.namespace,
168 for (action in actions) {
169 cls = namespace[action];
171 cls = namespace[action] = {};
173 methods = actions[action];
175 for (i = 0, len = methods.length; i < len; ++i) {
176 method = Ext.create('Ext.direct.RemotingMethod', methods[i]);
177 cls[method.name] = this.createHandler(action, method);
183 * Create a handler function for a direct call.
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
189 createHandler : function(action, method){
193 if (!method.formHandler) {
194 handler = function(){
195 me.configureRequest(action, method, Array.prototype.slice.call(arguments, 0));
198 handler = function(form, callback, scope){
199 me.configureFormRequest(action, method, form, callback, scope);
202 handler.directCfg = {
210 isConnected: function(){
211 return !!this.connected;
221 me.fireEvent('connect', me);
224 Ext.Error.raise('Error initializing RemotingProvider, no url configured.');
230 disconnect: function(){
234 me.connected = false;
235 me.fireEvent('disconnect', me);
240 * Run any callbacks related to the transaction.
242 * @param {Ext.direct.Transaction} transaction The transaction
243 * @param {Ext.direct.Event} event The event
245 runCallback: function(transaction, event){
246 var funcName = event.status ? 'success' : 'failure',
250 if (transaction && transaction.callback) {
251 callback = transaction.callback;
252 result = Ext.isDefined(event.result) ? event.result : event.data;
254 if (Ext.isFunction(callback)) {
255 callback(result, event);
257 Ext.callback(callback[funcName], callback.scope, [result, event]);
258 Ext.callback(callback.callback, callback.scope, [result, event]);
264 * React to the ajax request being completed
267 onData: function(options, success, response){
277 events = me.createEvents(response);
278 for (len = events.length; i < len; ++i) {
280 transaction = me.getTransaction(event);
281 me.fireEvent('data', me, event);
283 me.runCallback(transaction, event, true);
284 Ext.direct.Manager.removeTransaction(transaction);
288 transactions = [].concat(options.transaction);
289 for (len = transactions.length; i < len; ++i) {
290 transaction = me.getTransaction(transactions[i]);
291 if (transaction && transaction.retryCount < me.maxRetries) {
294 event = Ext.create('Ext.direct.ExceptionEvent', {
296 transaction: transaction,
297 code: Ext.direct.Manager.self.exceptions.TRANSPORT,
298 message: 'Unable to connect to the server.',
301 me.fireEvent('data', me, event);
303 me.runCallback(transaction, event, false);
304 Ext.direct.Manager.removeTransaction(transaction);
312 * Get transaction from XHR options
314 * @param {Object} options The options sent to the Ajax request
315 * @return {Ext.direct.Transaction} The transaction, null if not found
317 getTransaction: function(options){
318 return options && options.tid ? Ext.direct.Manager.getTransaction(options.tid) : null;
322 * Configure a direct request
324 * @param {String} action The action being executed
325 * @param {Object} method The being executed
327 configureRequest: function(action, method, args){
329 callData = method.getCallData(args),
330 data = callData.data,
331 callback = callData.callback,
332 scope = callData.scope,
335 transaction = Ext.create('Ext.direct.Transaction', {
341 callback: scope && Ext.isFunction(callback) ? Ext.Function.bind(callback, scope) : callback
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);
352 * Gets the Ajax call info for a transaction
354 * @param {Ext.direct.Transaction} transaction The transaction
355 * @return {Object} The call params
357 getCallData: function(transaction){
359 action: transaction.action,
360 method: transaction.method,
361 data: transaction.data,
368 * Sends a request to the server
370 * @param {Object/Array} data The data to send
372 sendRequest : function(data){
381 enableUrlEncode = me.enableUrlEncode,
387 if (Ext.isArray(data)) {
389 for (len = data.length; i < len; ++i) {
390 callData.push(me.getCallData(data[i]));
393 callData = me.getCallData(data);
396 if (enableUrlEncode) {
398 params[Ext.isString(enableUrlEncode) ? enableUrlEncode : 'data'] = Ext.encode(callData);
399 request.params = params;
401 request.jsonData = callData;
403 Ext.Ajax.request(request);
407 * Add a new transaction to the queue
409 * @param {Ext.direct.Transaction} transaction The transaction
411 queueTransaction: function(transaction){
413 enableBuffer = me.enableBuffer;
415 if (transaction.form) {
416 me.sendFormRequest(transaction);
420 me.callBuffer.push(transaction);
423 me.callTask = Ext.create('Ext.util.DelayedTask', me.combineAndSend, me);
425 me.callTask.delay(Ext.isNumber(enableBuffer) ? enableBuffer : 10);
432 * Combine any buffered requests and send them off
435 combineAndSend : function(){
436 var buffer = this.callBuffer,
440 this.sendRequest(len == 1 ? buffer[0] : buffer);
441 this.callBuffer = [];
446 * Configure a form submission request
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
454 configureFormRequest : function(action, method, form, callback, scope){
456 transaction = Ext.create('Ext.direct.Transaction', {
460 args: [form, callback, scope],
461 callback: scope && Ext.isFunction(callback) ? Ext.Function.bind(callback, scope) : callback,
467 if (me.fireEvent('beforecall', me, transaction, method) !== false) {
468 Ext.direct.Manager.addTransaction(transaction);
469 isUpload = String(form.getAttribute("enctype")).toLowerCase() == 'multipart/form-data';
472 extTID: transaction.id,
474 extMethod: method.name,
476 extUpload: String(isUpload)
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),
484 params: callback && Ext.isObject(callback.params) ? Ext.apply(params, callback.params) : params
486 me.fireEvent('call', me, transaction, method);
487 me.sendFormRequest(transaction);
492 * Sends a form request
494 * @param {Ext.direct.Transaction} transaction The transaction to send
496 sendFormRequest: function(transaction){
499 params: transaction.params,
500 callback: this.onData,
502 form: transaction.form,
503 isUpload: transaction.isUpload,
504 transaction: transaction