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