RemotingProvider.html 19 KB

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