source: Dev/branches/rest-dojo-ui/client/dojox/rpc/Service.js @ 256

Last change on this file since 256 was 256, checked in by hendrikvanantwerpen, 13 years ago

Reworked project structure based on REST interaction and Dojo library. As
soon as this is stable, the old jQueryUI branch can be removed (it's
kept for reference).

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