Service.js 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331
  1. /*
  2. Copyright (c) 2004-2012, The Dojo Foundation All Rights Reserved.
  3. Available via Academic Free License >= 2.1 OR the modified BSD license.
  4. see: http://dojotoolkit.org/license for details
  5. */
  6. if(!dojo._hasResource["dojox.rpc.Service"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  7. dojo._hasResource["dojox.rpc.Service"] = true;
  8. dojo.provide("dojox.rpc.Service");
  9. dojo.require("dojo.AdapterRegistry");
  10. dojo.declare("dojox.rpc.Service", null, {
  11. constructor: function(smd, options){
  12. // summary:
  13. // Take a string as a url to retrieve an smd or an object that is an smd or partial smd to use
  14. // as a definition for the service
  15. //
  16. // smd: object
  17. // Takes a number of properties as kwArgs for defining the service. It also
  18. // accepts a string. When passed a string, it is treated as a url from
  19. // which it should synchronously retrieve an smd file. Otherwise it is a kwArgs
  20. // object. It accepts serviceUrl, to manually define a url for the rpc service
  21. // allowing the rpc system to be used without an smd definition. strictArgChecks
  22. // forces the system to verify that the # of arguments provided in a call
  23. // matches those defined in the smd. smdString allows a developer to pass
  24. // a jsonString directly, which will be converted into an object or alternatively
  25. // smdObject is accepts an smdObject directly.
  26. //
  27. // description:
  28. // dojox.rpc.Service must be loaded prior to any plugin services like dojox.rpc.Rest
  29. // dojox.rpc.JsonRpc in order for them to register themselves, otherwise you get
  30. // a "No match found" error.
  31. var url;
  32. var self = this;
  33. function processSmd(smd){
  34. smd._baseUrl = new dojo._Url((dojo.isBrowser ? location.href : dojo.config.baseUrl) ,url || '.') + '';
  35. self._smd = smd;
  36. //generate the methods
  37. for(var serviceName in self._smd.services){
  38. var pieces = serviceName.split("."); // handle "namespaced" services by breaking apart by .
  39. var current = self;
  40. for(var i=0; i< pieces.length-1; i++){
  41. // create or reuse each object as we go down the chain
  42. current = current[pieces[i]] || (current[pieces[i]] = {});
  43. }
  44. current[pieces[pieces.length-1]]= self._generateService(serviceName, self._smd.services[serviceName]);
  45. }
  46. }
  47. if(smd){
  48. //ifthe arg is a string, we assume it is a url to retrieve an smd definition from
  49. if( (dojo.isString(smd)) || (smd instanceof dojo._Url)){
  50. if(smd instanceof dojo._Url){
  51. url = smd + "";
  52. }else{
  53. url = smd;
  54. }
  55. var text = dojo._getText(url);
  56. if(!text){
  57. throw new Error("Unable to load SMD from " + smd);
  58. }else{
  59. processSmd(dojo.fromJson(text));
  60. }
  61. }else{
  62. processSmd(smd);
  63. }
  64. }
  65. this._options = (options ? options : {});
  66. this._requestId = 0;
  67. },
  68. _generateService: function(serviceName, method){
  69. if(this[method]){
  70. throw new Error("WARNING: "+ serviceName+ " already exists for service. Unable to generate function");
  71. }
  72. method.name = serviceName;
  73. var func = dojo.hitch(this, "_executeMethod",method);
  74. var transport = dojox.rpc.transportRegistry.match(method.transport || this._smd.transport);
  75. if(transport.getExecutor){
  76. func = transport.getExecutor(func,method,this);
  77. }
  78. var schema = method.returns || (method._schema = {}); // define the schema
  79. var servicePath = '/' + serviceName +'/';
  80. // schemas are minimally used to track the id prefixes for the different services
  81. schema._service = func;
  82. func.servicePath = servicePath;
  83. func._schema = schema;
  84. func.id = dojox.rpc.Service._nextId++;
  85. return func;
  86. },
  87. _getRequest: function(method,args){
  88. var smd = this._smd;
  89. var envDef = dojox.rpc.envelopeRegistry.match(method.envelope || smd.envelope || "NONE");
  90. var parameters = (method.parameters || []).concat(smd.parameters || []);
  91. if(envDef.namedParams){
  92. // the serializer is expecting named params
  93. if((args.length==1) && dojo.isObject(args[0])){
  94. // looks like we have what we want
  95. args = args[0];
  96. }else{
  97. // they provided ordered, must convert
  98. var data={};
  99. for(var i=0;i<method.parameters.length;i++){
  100. if(typeof args[i] != "undefined" || !method.parameters[i].optional){
  101. data[method.parameters[i].name]=args[i];
  102. }
  103. }
  104. args = data;
  105. }
  106. if(method.strictParameters||smd.strictParameters){
  107. //remove any properties that were not defined
  108. for(i in args){
  109. var found=false;
  110. for(var j=0; j<parameters.length;j++){
  111. if(parameters[i].name==i){ found=true; }
  112. }
  113. if(!found){
  114. delete args[i];
  115. }
  116. }
  117. }
  118. // setting default values
  119. for(i=0; i< parameters.length; i++){
  120. var param = parameters[i];
  121. if(!param.optional && param.name && !args[param.name]){
  122. if(param["default"]){
  123. args[param.name] = param["default"];
  124. }else if(!(param.name in args)){
  125. throw new Error("Required parameter " + param.name + " was omitted");
  126. }
  127. }
  128. }
  129. }else if(parameters && parameters[0] && parameters[0].name && (args.length==1) && dojo.isObject(args[0])){
  130. // looks like named params, we will convert
  131. if(envDef.namedParams === false){
  132. // the serializer is expecting ordered params, must be ordered
  133. args = dojox.rpc.toOrdered(parameters, args);
  134. }else{
  135. // named is ok
  136. args = args[0];
  137. }
  138. }
  139. if(dojo.isObject(this._options)){
  140. args = dojo.mixin(args, this._options);
  141. }
  142. var schema = method._schema || method.returns; // serialize with the right schema for the context;
  143. var request = envDef.serialize.apply(this, [smd, method, args]);
  144. request._envDef = envDef;// save this for executeMethod
  145. var contentType = (method.contentType || smd.contentType || request.contentType);
  146. // this allows to mandate synchronous behavior from elsewhere when necessary, this may need to be changed to be one-shot in FF3 new sync handling model
  147. return dojo.mixin(request, {
  148. sync: dojox.rpc._sync,
  149. contentType: contentType,
  150. headers: method.headers || smd.headers || request.headers || {},
  151. target: request.target || dojox.rpc.getTarget(smd, method),
  152. transport: method.transport || smd.transport || request.transport,
  153. envelope: method.envelope || smd.envelope || request.envelope,
  154. timeout: method.timeout || smd.timeout,
  155. callbackParamName: method.callbackParamName || smd.callbackParamName,
  156. rpcObjectParamName: method.rpcObjectParamName || smd.rpcObjectParamName,
  157. schema: schema,
  158. handleAs: request.handleAs || "auto",
  159. preventCache: method.preventCache || smd.preventCache,
  160. frameDoc: this._options.frameDoc || undefined
  161. });
  162. },
  163. _executeMethod: function(method){
  164. var args = [];
  165. var i;
  166. for(i=1; i< arguments.length; i++){
  167. args.push(arguments[i]);
  168. }
  169. var request = this._getRequest(method,args);
  170. var deferred = dojox.rpc.transportRegistry.match(request.transport).fire(request);
  171. deferred.addBoth(function(results){
  172. return request._envDef.deserialize.call(this,results);
  173. });
  174. return deferred;
  175. }
  176. });
  177. dojox.rpc.getTarget = function(smd, method){
  178. var dest=smd._baseUrl;
  179. if(smd.target){
  180. dest = new dojo._Url(dest,smd.target) + '';
  181. }
  182. if(method.target){
  183. dest = new dojo._Url(dest,method.target) + '';
  184. }
  185. return dest;
  186. };
  187. dojox.rpc.toOrdered=function(parameters, args){
  188. if(dojo.isArray(args)){ return args; }
  189. var data=[];
  190. for(var i=0;i<parameters.length;i++){
  191. data.push(args[parameters[i].name]);
  192. }
  193. return data;
  194. };
  195. dojox.rpc.transportRegistry = new dojo.AdapterRegistry(true);
  196. dojox.rpc.envelopeRegistry = new dojo.AdapterRegistry(true);
  197. //Built In Envelopes
  198. dojox.rpc.envelopeRegistry.register(
  199. "URL",
  200. function(str){ return str == "URL"; },
  201. {
  202. serialize:function(smd, method, data ){
  203. var d = dojo.objectToQuery(data);
  204. return {
  205. data: d,
  206. transport:"POST"
  207. };
  208. },
  209. deserialize:function(results){
  210. return results;
  211. },
  212. namedParams: true
  213. }
  214. );
  215. dojox.rpc.envelopeRegistry.register(
  216. "JSON",
  217. function(str){ return str == "JSON"; },
  218. {
  219. serialize: function(smd, method, data){
  220. var d = dojo.toJson(data);
  221. return {
  222. data: d,
  223. handleAs: 'json',
  224. contentType : 'application/json'
  225. };
  226. },
  227. deserialize: function(results){
  228. return results;
  229. }
  230. }
  231. );
  232. dojox.rpc.envelopeRegistry.register(
  233. "PATH",
  234. function(str){ return str == "PATH"; },
  235. {
  236. serialize:function(smd, method, data){
  237. var i;
  238. var target = dojox.rpc.getTarget(smd, method);
  239. if(dojo.isArray(data)){
  240. for(i = 0; i < data.length;i++){
  241. target += '/' + data[i];
  242. }
  243. }else{
  244. for(i in data){
  245. target += '/' + i + '/' + data[i];
  246. }
  247. }
  248. return {
  249. data:'',
  250. target: target
  251. };
  252. },
  253. deserialize:function(results){
  254. return results;
  255. }
  256. }
  257. );
  258. //post is registered first because it is the default;
  259. dojox.rpc.transportRegistry.register(
  260. "POST",
  261. function(str){ return str == "POST"; },
  262. {
  263. fire:function(r){
  264. r.url = r.target;
  265. r.postData = r.data;
  266. return dojo.rawXhrPost(r);
  267. }
  268. }
  269. );
  270. dojox.rpc.transportRegistry.register(
  271. "GET",
  272. function(str){ return str == "GET"; },
  273. {
  274. fire: function(r){
  275. r.url= r.target + (r.data ? '?' + ((r.rpcObjectParamName) ? r.rpcObjectParamName + '=' : '') + r.data : '');
  276. return dojo.xhrGet(r);
  277. }
  278. }
  279. );
  280. //only works ifyou include dojo.io.script
  281. dojox.rpc.transportRegistry.register(
  282. "JSONP",
  283. function(str){ return str == "JSONP"; },
  284. {
  285. fire: function(r){
  286. r.url = r.target + ((r.target.indexOf("?") == -1) ? '?' : '&') + ((r.rpcObjectParamName) ? r.rpcObjectParamName + '=' : '') + r.data;
  287. r.callbackParamName = r.callbackParamName || "callback";
  288. return dojo.io.script.get(r);
  289. }
  290. }
  291. );
  292. dojox.rpc.Service._nextId = 1;
  293. dojo._contentHandlers.auto = function(xhr){
  294. // automatically choose the right handler based on the returned content type
  295. var handlers = dojo._contentHandlers;
  296. var retContentType = xhr.getResponseHeader("Content-Type");
  297. var results = !retContentType ? handlers.text(xhr) :
  298. retContentType.match(/\/.*json/) ? handlers.json(xhr) :
  299. retContentType.match(/\/javascript/) ? handlers.javascript(xhr) :
  300. retContentType.match(/\/xml/) ? handlers.xml(xhr) : handlers.text(xhr);
  301. return results;
  302. };
  303. }