Service.js 9.6 KB

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