/public/javascripts/dojo/release/dojo/dojox/rpc/Service.js

http://enginey.googlecode.com/ · JavaScript · 330 lines · 261 code · 28 blank · 41 comment · 72 complexity · 965bbdb0bf8659ec2d73071b598bc9de MD5 · raw file

  1. /*
  2. Copyright (c) 2004-2008, 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(location.href,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. if(envDef.namedParams){
  91. // the serializer is expecting named params
  92. if((args.length==1) && dojo.isObject(args[0])){
  93. // looks like we have what we want
  94. args = args[0];
  95. }else{
  96. // they provided ordered, must convert
  97. var data={};
  98. for(var i=0;i<method.parameters.length;i++){
  99. if(typeof args[i] != "undefined" || !method.parameters[i].optional){
  100. data[method.parameters[i].name]=args[i];
  101. }
  102. }
  103. args = data;
  104. }
  105. var parameters = (method.parameters || []).concat(smd.parameters || []);
  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(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(method.parameters && method.parameters[0] && method.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(method, 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: {},
  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. schema: schema,
  157. handleAs: request.handleAs || "auto",
  158. preventCache: method.preventCache || smd.preventCache,
  159. frameDoc: this._options.frameDoc || undefined
  160. });
  161. },
  162. _executeMethod: function(method){
  163. var args = [];
  164. var i;
  165. for(i=1; i< arguments.length; i++){
  166. args.push(arguments[i]);
  167. }
  168. var request = this._getRequest(method,args);
  169. var deferred = dojox.rpc.transportRegistry.match(request.transport).fire(request);
  170. deferred.addBoth(function(results){
  171. return request._envDef.deserialize.call(this,results);
  172. });
  173. return deferred;
  174. }
  175. });
  176. dojox.rpc.getTarget = function(smd, method){
  177. var dest=smd._baseUrl;
  178. if(smd.target){
  179. dest = new dojo._Url(dest,smd.target) + '';
  180. }
  181. if(method.target){
  182. dest = new dojo._Url(dest,method.target) + '';
  183. }
  184. return dest;
  185. };
  186. dojox.rpc.toOrdered=function(method, args){
  187. if(dojo.isArray(args)){ return args; }
  188. var data=[];
  189. for(var i=0;i<method.parameters.length;i++){
  190. data.push(args[method.parameters[i].name]);
  191. }
  192. return data;
  193. };
  194. dojox.rpc.transportRegistry = new dojo.AdapterRegistry(true);
  195. dojox.rpc.envelopeRegistry = new dojo.AdapterRegistry(true);
  196. //Built In Envelopes
  197. dojox.rpc.envelopeRegistry.register(
  198. "URL",
  199. function(str){ return str == "URL"; },
  200. {
  201. serialize:function(smd, method, data ){
  202. var d = dojo.objectToQuery(data);
  203. return {
  204. data: d,
  205. transport:"POST"
  206. };
  207. },
  208. deserialize:function(results){
  209. return results;
  210. },
  211. namedParams: true
  212. }
  213. );
  214. dojox.rpc.envelopeRegistry.register(
  215. "JSON",
  216. function(str){ return str == "JSON"; },
  217. {
  218. serialize: function(smd, method, data){
  219. var d = dojo.toJson(data);
  220. return {
  221. data: d,
  222. handleAs: 'json',
  223. contentType : 'application/json'
  224. };
  225. },
  226. deserialize: function(results){
  227. return results;
  228. }
  229. }
  230. );
  231. dojox.rpc.envelopeRegistry.register(
  232. "PATH",
  233. function(str){ return str == "PATH"; },
  234. {
  235. serialize:function(smd, method, data){
  236. var i;
  237. var target = dojox.rpc.getTarget(smd, method);
  238. if(dojo.isArray(data)){
  239. for(i = 0; i < data.length;i++){
  240. target += '/' + data[i];
  241. }
  242. }else{
  243. for(i in data){
  244. target += '/' + i + '/' + data[i];
  245. }
  246. }
  247. return {
  248. data:'',
  249. target: target
  250. };
  251. },
  252. deserialize:function(results){
  253. return results;
  254. }
  255. }
  256. );
  257. //post is registered first because it is the default;
  258. dojox.rpc.transportRegistry.register(
  259. "POST",
  260. function(str){ return str == "POST"; },
  261. {
  262. fire:function(r){
  263. r.url = r.target;
  264. r.postData = r.data;
  265. return dojo.rawXhrPost(r);
  266. }
  267. }
  268. );
  269. dojox.rpc.transportRegistry.register(
  270. "GET",
  271. function(str){ return str == "GET"; },
  272. {
  273. fire: function(r){
  274. r.url= r.target + (r.data ? '?'+ r.data : '');
  275. return dojo.xhrGet(r);
  276. }
  277. }
  278. );
  279. //only works ifyou include dojo.io.script
  280. dojox.rpc.transportRegistry.register(
  281. "JSONP",
  282. function(str){ return str == "JSONP"; },
  283. {
  284. fire: function(r){
  285. r.url = r.target + ((r.target.indexOf("?") == -1) ? '?' : '&') + r.data;
  286. r.callbackParamName = r.callbackParamName || "callback";
  287. return dojo.io.script.get(r);
  288. }
  289. }
  290. );
  291. dojox.rpc.Service._nextId = 1;
  292. dojo._contentHandlers.auto = function(xhr){
  293. // automatically choose the right handler based on the returned content type
  294. var handlers = dojo._contentHandlers;
  295. var retContentType = xhr.getResponseHeader("Content-Type");
  296. results = !retContentType ? handlers.text(xhr) :
  297. retContentType.match(/\/.*json/) ? handlers.json(xhr) :
  298. retContentType.match(/\/javascript/) ? handlers.javascript(xhr) :
  299. retContentType.match(/\/xml/) ? handlers.xml(xhr) : handlers.text(xhr);
  300. return results;
  301. };
  302. }