08528eb985f98f5e2293ad27cc577f17aabecb2c
[contrib/cloudeebus.git] / cloudeebus / cloudeebus.js
1 /******************************************************************************
2  * Copyright 2012 Intel Corporation.
3  * 
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  * 
8  * http://www.apache.org/licenses/LICENSE-2.0
9  * 
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *****************************************************************************/
16
17
18
19 /*****************************************************************************/
20
21 var dbus = { // hook object for dbus types not translated by python-json
22                 Double: function(value, level) {
23                         return value;
24                 }
25 };
26
27
28
29 /*****************************************************************************/
30
31 var cloudeebus = window.cloudeebus = {
32                 version: "0.5.99",
33                 minVersion: "0.3.2"
34 };
35
36 cloudeebus.reset = function() {
37         cloudeebus.sessionBus = null;
38         cloudeebus.systemBus = null;
39         cloudeebus.wampSession = null;
40         cloudeebus.uri = null;
41 };
42
43
44 cloudeebus.log = function(msg) { 
45 };
46
47 cloudeebus.getError = function(error) {
48         if (error.desc && error.uri)
49                 return error.desc + " : " + error.uri;
50         if (error.desc)
51                 return error.desc;
52         if (error.uri)
53                 return error.uri;
54         if (error.name && error.message)
55                 return error.name + " : " + error.message;
56         if (error.message)
57                 return error.message;
58         if (error.name)
59                 return error.name;
60         return error;
61 };
62
63 cloudeebus.versionCheck = function(version) {
64         var ver = version.split(".");
65         var min = cloudeebus.minVersion.split(".");
66         for (var i=0; i<ver.length; i++) {
67                 if (Number(ver[i]) > Number(min[i]))
68                         return true;
69                 if (Number(ver[i]) < Number(min[i]))
70                         return false;
71         }
72         return true;
73 };
74
75
76 cloudeebus.connect = function(uri, manifest, successCB, errorCB) {
77         cloudeebus.reset();
78         cloudeebus.uri = uri;
79         
80         function onCloudeebusVersionCheckCB(version) {
81                 if (cloudeebus.versionCheck(version)) {
82                         cloudeebus.log("Connected to " + cloudeebus.uri);
83                         if (successCB)
84                                 successCB();
85                 } else {
86                         var errorMsg = "Cloudeebus server version " + version + " unsupported, need version " + cloudeebus.minVersion + " or superior";
87                         cloudeebus.log(errorMsg);
88                         if (errorCB)
89                                 errorCB(errorMsg);
90                 }
91         }
92         
93         function onWAMPSessionAuthErrorCB(error) {
94                 var errorStr = cloudeebus.getError(error);
95                 cloudeebus.log("Authentication error: " + errorStr);
96                 if (errorCB)
97                         errorCB(errorStr);
98         }
99         
100         function onWAMPSessionAuthenticatedCB(permissions) {
101                 cloudeebus.sessionBus = new cloudeebus.BusConnection("session", cloudeebus.wampSession);
102                 cloudeebus.systemBus = new cloudeebus.BusConnection("system", cloudeebus.wampSession);
103                 cloudeebus.wampSession.call("getVersion").then(onCloudeebusVersionCheckCB, errorCB);
104         }
105         
106         function onWAMPSessionChallengedCB(challenge) {
107                 var signature = cloudeebus.wampSession.authsign(challenge, manifest.key);
108                 cloudeebus.wampSession.auth(signature).then(onWAMPSessionAuthenticatedCB, onWAMPSessionAuthErrorCB);
109         }
110         
111         function onWAMPSessionConnectedCB(session) {
112                 cloudeebus.wampSession = session;
113                 if (manifest) {
114                         cloudeebus.wampSession.authreq(
115                                         manifest.name, 
116                                         {permissions: manifest.permissions, 
117                                                  services: manifest.services}
118                                 ).then(onWAMPSessionChallengedCB, onWAMPSessionAuthErrorCB);
119                 }
120                 else
121                         cloudeebus.wampSession.authreq().then(function() {
122                                 cloudeebus.wampSession.auth().then(onWAMPSessionAuthenticatedCB, onWAMPSessionAuthErrorCB);
123                                 }, onWAMPSessionAuthErrorCB);
124         }
125
126         function onWAMPSessionErrorCB(code, reason) {
127                 if (code == ab.CONNECTION_UNSUPPORTED) {
128                         cloudeebus.log("Browser is not supported");
129                 }
130                 else {
131                         cloudeebus.log("Failed to open session, code = " + code + ", reason = " + reason);
132                 }
133                 if (errorCB)
134                         errorCB(reason);
135         }
136
137         return ab.connect(cloudeebus.uri, onWAMPSessionConnectedCB, onWAMPSessionErrorCB);
138 };
139
140
141 cloudeebus.SessionBus = function() {
142         return cloudeebus.sessionBus;
143 };
144
145
146 cloudeebus.SystemBus = function() {
147         return cloudeebus.systemBus;
148 };
149
150
151
152 /*****************************************************************************/
153
154 cloudeebus.BusConnection = function(name, session) {
155         this.name = name;
156         this.wampSession = session;
157         return this;
158 };
159
160
161 cloudeebus.BusConnection.prototype.getObject = function(busName, objectPath, introspectCB, errorCB) {
162         var proxy = new cloudeebus.ProxyObject(this.wampSession, this, busName, objectPath);
163         if (introspectCB)
164                 proxy._introspect(introspectCB, errorCB);
165         return proxy;
166 };
167
168
169 cloudeebus.BusConnection.prototype.addService = function(serviceName) {
170         var self = this;
171
172         if (!serviceName)
173                 serviceName = "";
174         
175         var promise = new cloudeebus.Promise(function (resolver) {
176                 var cloudeebusService = new cloudeebus.Service(self.wampSession, self, serviceName);
177         
178                 function ServiceAddedSuccessCB(serviceName) {
179                         try { // calling dbus hook object function for un-translated types
180                                 cloudeebusService.isCreated = true;
181                                 resolver.fulfill(cloudeebusService, true);
182                         }
183                         catch (e) {
184                                 var errorStr = cloudeebus.getError(e);
185                                 cloudeebus.log("Method callback exception: " + errorStr);
186                                 resolver.reject(errorStr, true);
187                         }               
188                 }
189                 
190                 function ServiceAddedErrorCB(error) {
191                         var errorStr = cloudeebus.getError(error);
192                         cloudeebus.log("Error adding service method: " + self.name + ", error: " + errorStr);
193                         resolver.reject(errorStr, true);
194                 }
195
196                 var arglist = [
197                     self.name,
198                     serviceName
199                     ];
200
201                 // call dbusSend with bus type, destination, object, message and arguments
202                 self.wampSession.call("serviceAdd", arglist).then(ServiceAddedSuccessCB, ServiceAddedErrorCB);
203         });
204         
205         return promise;
206 };
207
208
209
210 /*****************************************************************************/
211 //Generic definition for an agent. An agent needs :
212 //srvDbusName : the DBus parent service
213 //objPath : a DBus path to access it
214 //jsHdl : a Javascript handler to process methods, 
215 //xml : the xml which describe interface/methods/signals...
216 cloudeebus.Agent = function(srvDbusName, objPath, jsHdl, xml) {
217         this.srvName = srvDbusName;
218         this.registered = false;
219         this.xml = xml;
220         this.objectPath = objPath;
221         this.jsHdl = jsHdl;
222         return this;
223 };
224
225
226 cloudeebus.Service = function(session, busConnection, name) {
227         this.wampSession = session;
228         this.busConnection = busConnection; 
229         this.name = name;
230         this.agents = [];
231         this.isCreated = false;
232         return this;
233 };
234
235
236 cloudeebus.Service.prototype.remove = function() {
237         var self = this;
238         
239         var promise = new cloudeebus.Promise(function (resolver) {
240                 function ServiceRemovedSuccessCB(serviceName) {
241                         try {
242                                 resolver.fulfill(serviceName, true);
243                         }
244                         catch (e) {
245                                 var errorStr = cloudeebus.getError(e);
246                                 cloudeebus.log("Method callback exception: " + errorStr);
247                                 resolver.reject(errorStr, true);
248                         }               
249                 }
250                 
251                 function ServiceRemovedErrorCB(error) {
252                         var errorStr = cloudeebus.getError(error);
253                         cloudeebus.log("Error removing service : " + self.name + ", error: " + errorStr);
254                         self.promise.resolver.reject(errorStr, true);
255                 }
256                 
257                 for (var idx in self.agents) {
258                         if (self.agents[idx]) {
259                                 self.removeAgent(self.agents[idx]);
260                         }
261                 }
262                 
263                 var arglist = [
264                     self.name
265                     ];
266         
267                 // call dbusSend with bus type, destination, object, message and arguments
268                 self.wampSession.call("serviceRelease", arglist).then(ServiceRemovedSuccessCB, ServiceRemovedErrorCB);
269         });
270         
271         return promise;
272 };
273
274
275 cloudeebus.Service.prototype._searchMethod = function(ifName, method, objectJS) {
276
277         var funcToCall = null;
278         
279         // Check if 'objectJS' has a member 'interfaceProxies' with an interface named 'ifName' 
280         // and a method named 'method'
281         if (objectJS.interfaceProxies && objectJS.interfaceProxies[ifName] &&
282                 objectJS.interfaceProxies[ifName][method]) {
283                 funcToCall = objectJS.interfaceProxies[ifName][method];
284         } else {
285                 // retrieve the method directly from 'root' of objectJs
286                 funcToCall = objectJS[method];
287         }
288
289         return funcToCall;
290 };
291
292
293 cloudeebus.Service.prototype._addMethod = function(ifName, method, agent) {
294
295         var service = this;
296         var methodId = this.name + "#" + agent.objectPath + "#" + ifName + "#" + method;
297         var funcToCall = this._searchMethod(ifName, method, agent.jsHdl);
298
299         if (funcToCall == null)
300                 cloudeebus.log("Method " + method + " doesn't exist in Javascript object");
301         else {
302                 agent.jsHdl.wrapperFunc[method] = function() {
303                         var result;
304                         var methodId = arguments[0];
305                         var callDict = {};
306                         // affectation of callDict in eval, otherwise dictionary(='{}') interpreted as block of code by eval
307                         eval("callDict = " + arguments[1]);
308                         try {
309                                 result = funcToCall.apply(agent.jsHdl, callDict.args);
310                                 service._returnMethod(methodId, callDict.callIndex, true, result);
311                         }
312                         catch (e) {
313                                 var errorStr = cloudeebus.getError(e);
314                                 cloudeebus.log("Method " + ifName + "." + method + " call on " + agent.objectPath + " exception: " + errorStr);
315                                 service._returnMethod(methodId, callDict.callIndex, false, errorStr);
316                         }
317                 };
318                 agent.jsHdl.methodId[agent.objectPath].push(methodId);
319                 this.wampSession.subscribe(methodId, agent.jsHdl.wrapperFunc[method]);
320         }
321 };
322
323
324 cloudeebus.Service.prototype._addSignal = function(ifName, signal, agent) {
325         var service = this;
326         var methodExist = false;
327
328         if (agent.jsHdl.interfaceProxies && agent.jsHdl.interfaceProxies[ifName])
329                 if (agent.jsHdl.interfaceProxies[ifName][signal]) {
330                         methodExist = true;
331                 } else {
332                         agent.jsHdl.interfaceProxies[ifName][signal] = function() {
333                                 service._emitSignal(agent.objectPath, signal, arguments[0]);
334                         };
335                 return;
336         }
337                 
338         if ((agent.jsHdl[signal] == undefined || agent.jsHdl[signal] == null) && !methodExist) 
339                 agent.jsHdl[signal] = function() {
340                         service._emitSignal(agent.objectPath, signal, arguments[0]);
341                 };
342         else
343                 cloudeebus.log("Can not create new method to emit signal '" + signal + "' in object JS this method already exist!");
344 };
345
346
347 cloudeebus.Service.prototype._createWrapper = function(agent) {
348         var self = this;
349         var parser = new DOMParser();
350         var xmlDoc = parser.parseFromString(agent.xml, "text/xml");
351         var ifXml = xmlDoc.getElementsByTagName("interface");
352         agent.jsHdl.wrapperFunc = [];
353         agent.jsHdl.methodId = [];
354         agent.jsHdl.methodId[agent.objectPath] = [];
355         for (var i=0; i < ifXml.length; i++) {
356                 var ifName = ifXml[i].attributes.getNamedItem("name").value;
357                 var ifChild = ifXml[i].firstChild;
358                 while (ifChild) {
359                         if (ifChild.nodeName == "method") {
360                                 var metName = ifChild.attributes.getNamedItem("name").value;
361                                 self._addMethod(ifName, metName, agent);
362                         }
363                         if (ifChild.nodeName == "signal") {
364                                 var metName = ifChild.attributes.getNamedItem("name").value;
365                                 self._addSignal(ifName, metName, agent);
366                         }
367                         ifChild = ifChild.nextSibling;
368                 }
369         }
370 };
371
372
373 cloudeebus.Service.prototype.addAgent = function(agent) {
374         var self = this;
375         
376         var promise = new cloudeebus.Promise(function (resolver) {
377                 function ServiceAddAgentSuccessCB(objPath) {
378                         try { // calling dbus hook object function for un-translated types
379                                 self.agents.push(agent);
380                                 agent.registered = true;
381                                 var result = [ objPath ];
382                                 resolver.fulfill(result[0], true);
383                         }
384                         catch (e) {
385                                 var errorStr = cloudeebus.getError(e);
386                                 cloudeebus.log("Method callback exception: " + errorStr);
387                                 resolver.reject(errorStr, true);
388                         }               
389                 }
390                 
391                 function ServiceAddAgenterrorCB(error) {
392                         var errorStr = cloudeebus.getError(error);
393                         cloudeebus.log("Error adding agent : " + agent.objectPath + ", error: " + errorStr);
394                         self.promise.resolver.reject(errorStr, true);
395                 }
396                 
397                 try {
398                         self._createWrapper(agent);
399                 }
400                 catch (e) {
401                         var errorStr = cloudeebus.getError(e);
402                         cloudeebus.log("Exception creating agent wrapper " + agent.objectPath + " : " + errorStr);
403                         resolver.reject(errorStr, true);
404                         return;
405                 }
406                 
407                 var arglist = [
408                     agent.objectPath,
409                     agent.xml
410                     ];
411         
412                 // call dbusSend with bus type, destination, object, message and arguments
413                 self.wampSession.call("serviceAddAgent", arglist).then(ServiceAddAgentSuccessCB, ServiceAddAgenterrorCB);
414         });
415         
416         return promise;
417 };
418
419
420 cloudeebus.Service.prototype._deleteWrapper = function(agent) {
421         var objJs = agent.jsHdl;
422         if (objJs.methodId[agent.objectPath]) {
423                 for (var idx in objJs.methodId[agent.objectPath]) {
424                         try {
425                                 cloudeebus.log("unsubscribe " + objJs.methodId[agent.objectPath][idx]);
426                                 this.wampSession.unsubscribe(objJs.methodId[agent.objectPath][idx]);
427                                 objJs.methodId[agent.objectPath][idx] = null;
428                         }
429                         catch (e) {
430                                 cloudeebus.log("Unsubscribe error: " + cloudeebus.getError(e));
431                         }
432                 }
433                 delete objJs.methodId[agent.objectPath];
434         }
435 };
436
437
438 cloudeebus.Service.prototype.removeAgent = function(rmAgent, successCB, errorCB) {
439         var self = this;
440         
441         var promise = new cloudeebus.Promise(function (resolver) {
442                 function ServiceRemoveAgentSuccessCB(agent) {
443                         try { // calling dbus hook object function for un-translated types
444                                 self.agents.push(agent);
445                                 agent.registered = true;
446                                 var result = [ agent ];
447                                 resolver.fulfill(result[0], true);
448                         }
449                         catch (e) {
450                                 var errorStr = cloudeebus.getError(e);
451                                 cloudeebus.log("Method callback exception: " + errorStr);
452                                 resolver.reject(errorStr, true);
453                         }               
454                 }
455
456                 function ServiceRemoveAgentErrorCB(error) {
457                         var errorStr = cloudeebus.getError(error);
458                         cloudeebus.log("Error removing agent : " + rmAgent.objectPath + ", error: " + errorStr);
459                         self.promise.resolver.reject(errorStr, true);
460                 }
461
462                 try {
463                         self._deleteWrapper(rmAgent);
464                 }
465                 catch (e) {
466                         var errorStr = cloudeebus.getError(e);
467                         cloudeebus.log("Exception removing wrapper of agent " + rmAgent.objectPath + " : " + errorStr);
468                         errorCB(errorStr);
469                 }
470                 
471                 var arglist = [
472                     rmAgent.objectPath
473                     ];
474         
475                 // call dbusSend with bus type, destination, object, message and arguments
476                 self.wampSession.call("serviceDelAgent", arglist).then(ServiceRemoveAgentSuccessCB, ServiceRemoveAgentErrorCB);
477         });
478         
479         return promise;
480 };
481
482
483 cloudeebus.Service.prototype._returnMethod = function(methodId, callIndex, success, result, successCB, errorCB) {
484         var arglist = [
485             methodId,
486             callIndex,
487             success,
488             result
489             ];
490
491         this.wampSession.call("returnMethod", arglist).then(successCB, errorCB);
492 };
493
494
495 cloudeebus.Service.prototype._emitSignal = function(objectPath, signalName, result, successCB, errorCB) {
496         var arglist = [
497             objectPath,
498             signalName,
499             result
500             ];
501
502         this.wampSession.call("emitSignal", arglist).then(successCB, errorCB);
503 };
504
505
506
507 /*****************************************************************************/
508
509 function _processWrappers(wrappers, value) {
510         for (var i=0; i<wrappers.length; i++)
511                 wrappers[i](value);
512 }
513
514
515 function _processWrappersAsync(wrappers, value) {
516         var taskid = -1;
517         function processAsyncOnce() {
518                 _processWrappers(wrappers, value);
519                 clearInterval(taskid);
520         }
521         taskid = setInterval(processAsyncOnce, 200);
522 }
523
524
525
526 /*****************************************************************************/
527
528 cloudeebus.PromiseResolver = function(promise) {
529         this.promise = promise;
530         this.resolved = null;
531     return this;
532 };
533
534
535 cloudeebus.PromiseResolver.prototype.resolve = function(value, sync) {
536         if (this.resolved)
537                 return;
538         
539         var then = (value && value.then && value.then.apply) ? value.then : null;
540         if (then) {
541                 var self = this;                
542                 var fulfillCallback = function(arg) {
543                         self.resolve(arg, true);
544                 };      
545                 var rejectCallback = function(arg) {
546                         self.reject(arg, true);
547                 };
548                 try {
549                         then.apply(value, [fulfillCallback, rejectCallback]);
550                 }
551                 catch (e) {
552                         this.reject(cloudeebus.getError(e), true);
553                 }
554         }
555         
556         this.fulfill(value, sync);
557 };
558
559
560 cloudeebus.PromiseResolver.prototype.fulfill = function(value, sync) {
561         if (this.resolved)
562                 return;
563         
564         var promise = this.promise;
565         promise.state = "fulfilled";
566         promise.result = value;
567         
568         this.resolved = true;
569         if (sync)
570                 _processWrappers(promise._fulfillWrappers, value);
571         else
572                 _processWrappersAsync(promise._fulfillWrappers, value);
573 };
574
575
576 cloudeebus.PromiseResolver.prototype.reject = function(value, sync) {
577         if (this.resolved)
578                 return;
579         
580         var promise = this.promise;
581         promise.state = "rejected";
582         promise.result = value;
583         
584         this.resolved = true;
585         if (sync)
586                 _processWrappers(promise._rejectWrappers, value);
587         else
588                 _processWrappersAsync(promise._rejectWrappers, value);
589 };
590
591
592
593 /*****************************************************************************/
594
595 cloudeebus.Promise = function(init) {
596         this.state = "pending";
597         this.result = null;
598         this._fulfillWrappers = [];
599         this._rejectWrappers = [];
600         this.resolver = new cloudeebus.PromiseResolver(this);
601         if (init) {
602                 try {
603                         init.apply(this, [this.resolver]);
604                 }
605                 catch (e) {
606                         this.resolver.reject(cloudeebus.getError(e), true);
607                 }
608         }
609     return this;
610 };
611
612
613 cloudeebus.Promise.prototype.appendWrappers = function(fulfillWrapper, rejectWrapper) {
614         if (fulfillWrapper)
615                 this._fulfillWrappers.push(fulfillWrapper);
616         if (rejectWrapper)
617                 this._rejectWrappers.push(rejectWrapper);
618         if (this.state == "fulfilled")
619                 _processWrappersAsync(this._fulfillWrappers, this.result);
620         if (this.state == "rejected")
621                 _processWrappersAsync(this._rejectWrappers, this.result);
622 };
623
624
625 cloudeebus.Promise.prototype.then = function(fulfillCB, rejectCB) {
626         var promise = new cloudeebus.Promise();
627         var resolver = promise.resolver;
628         var fulfillWrapper, rejectWrapper;
629         
630         if (fulfillCB)
631                 fulfillWrapper = function(arg) {
632                         try {
633                                 var value = fulfillCB.apply(promise, [arg]);
634                                 resolver.resolve(value, true);
635                         }
636                         catch (e) {
637                                 resolver.reject(cloudeebus.getError(e), true);
638                         }
639                 };
640         else
641                 fulfillWrapper = function(arg) {
642                         resolver.fulfill(arg, true);
643                 };
644         
645         if (rejectCB)
646                 rejectWrapper = function(arg) {
647                         try {
648                                 var value = rejectCB.apply(promise, [arg]);
649                                 resolver.resolve(value, true);
650                         }
651                         catch (e) {
652                                 resolver.reject(cloudeebus.getError(e), true);
653                         }
654                 };
655         else
656                 rejectWrapper = function(arg) {
657                         resolver.reject(arg, true);
658                 };
659         
660         this.appendWrappers(fulfillWrapper,rejectWrapper);
661         return promise;
662 };
663
664
665 cloudeebus.Promise.prototype["catch"] = function(rejectCB) {
666         return this.then(undefined,rejectCB);
667 };
668
669
670 cloudeebus.Promise.prototype.done = function(fulfillCB, rejectCB) {
671         this.appendWrappers(fulfillCB,rejectCB);
672 };
673
674
675 cloudeebus.Promise.resolve = function(value) {
676         var promise = new cloudeebus.Promise();
677         promise.resolver.resolve(value);
678         return promise;
679 };
680
681
682 cloudeebus.Promise.fulfill = function(value) {
683         var promise = new cloudeebus.Promise();
684         promise.resolver.fulfill(value);
685         return promise;
686 };
687
688
689 cloudeebus.Promise.reject = function(value) {
690         var promise = new cloudeebus.Promise();
691         promise.resolver.reject(value);
692         return promise;
693 };
694
695
696 cloudeebus.Promise.any = function() {
697         var promise = new cloudeebus.Promise();
698         var resolver = promise.resolver;
699         var fulfillCallback = function(arg) {
700                 resolver.resolve(arg, true);
701         };
702         var rejectCallback = function(arg) {
703                 resolver.reject(arg, true);
704         };
705         if (arguments.length == 0)
706                 resolver.resolve(undefined, true);
707         else
708                 for (i in arguments) 
709                         Promise.resolve(arguments[i]).appendWrappers(fulfillCallback,rejectCallback);
710         return promise;
711 };
712
713
714 cloudeebus.Promise.every = function() {
715         var promise = new cloudeebus.Promise();
716         var resolver = promise.resolver;
717         var index = 0;
718         var countdown = arguments.length;
719         var args = new Array(countdown);
720         var rejectCallback = function(arg) {
721                 resolver.reject(arg, true);
722         };
723         if (arguments.length == 0)
724                 resolver.resolve(undefined, true);
725         else
726                 for (i in arguments) {
727                         var fulfillCallback = function(arg) {
728                                 args[index] = arg;
729                                 countdown--;
730                                 if (countdown == 0)
731                                         resolver.resolve(args, true);
732                         };
733                         index++;
734                         Promise.resolve(arguments[i]).appendWrappers(fulfillCallback,rejectCallback);
735                 }
736         
737         return promise;
738 };
739
740
741 cloudeebus.Promise.some = function() {
742         var promise = new cloudeebus.Promise();
743         var resolver = promise.resolver;
744         var index = 0;
745         var countdown = arguments.length;
746         var args = new Array(countdown);
747         var fulfillCallback = function(arg) {
748                 resolver.resolve(arg, true);
749         };
750         if (arguments.length == 0)
751                 resolver.resolve(undefined, true);
752         else
753                 for (i in arguments) {
754                         var rejectCallback = function(arg) {
755                                 args[index] = arg;
756                                 countdown--;
757                                 if (countdown == 0)
758                                         resolver.reject(args, true);
759                         };
760                         index++;
761                         Promise.resolve(arguments[i]).appendWrappers(fulfillCallback,rejectCallback);
762                 }
763         
764         return promise;
765 };
766
767
768
769 /*****************************************************************************/
770
771 cloudeebus.ProxyObject = function(session, busConnection, busName, objectPath) {
772         this.wampSession = session; 
773         this.busConnection = busConnection; 
774         this.busName = busName; 
775         this.objectPath = objectPath; 
776         this.interfaceProxies = {};
777         return this;
778 };
779
780
781 cloudeebus.ProxyObject.prototype.getInterface = function(ifName) {
782         return this.interfaceProxies[ifName];
783 };
784
785
786 cloudeebus.ProxyObject.prototype._introspect = function(successCB, errorCB) {
787         
788         var self = this; 
789
790         function getAllPropertiesSuccessCB(props) {
791                 var ifProxy = self.interfaceProxies[self.propInterfaces[self.propInterfaces.length-1]];
792                 for (var prop in props)
793                         ifProxy[prop] = self[prop] = props[prop];
794                 getAllPropertiesNextInterfaceCB();
795         }
796         
797         function getAllPropertiesNextInterfaceCB() {
798                 self.propInterfaces.pop();
799                 if (self.propInterfaces.length > 0) 
800                         self.callMethod("org.freedesktop.DBus.Properties", 
801                                 "GetAll", 
802                                 [self.propInterfaces[self.propInterfaces.length-1]]).then(getAllPropertiesSuccessCB, 
803                                 errorCB ? errorCB : getAllPropertiesNextInterfaceCB);
804                 else {
805                         self.propInterfaces = null;
806                         if (successCB)
807                                 successCB(self);
808                 }
809         }
810         
811         function introspectSuccessCB(str) {
812                 var parser = new DOMParser();
813                 var xmlDoc = parser.parseFromString(str, "text/xml");
814                 var interfaces = xmlDoc.getElementsByTagName("interface");
815                 self.propInterfaces = [];
816                 var supportDBusProperties = false;
817                 for (var i=0; i < interfaces.length; i++) {
818                         var ifName = interfaces[i].attributes.getNamedItem("name").value;
819                         self.interfaceProxies[ifName] = new cloudeebus.ProxyObject(self.wampSession, self.busConnection, self.busName, self.objectPath);
820                         if (ifName == "org.freedesktop.DBus.Properties")
821                                 supportDBusProperties = true;
822                         var hasProperties = false;
823                         var ifChild = interfaces[i].firstChild;
824                         while (ifChild) {
825                                 if (ifChild.nodeName == "method") {
826                                         var nArgs = 0;
827                                         var signature = "";
828                                         var metChild = ifChild.firstChild;
829                                         while (metChild) {
830                                                 if (metChild.nodeName == "arg" &&
831                                                         metChild.attributes.getNamedItem("direction").value == "in") {
832                                                                 signature += metChild.attributes.getNamedItem("type").value;
833                                                                 nArgs++;
834                                                 }
835                                                 metChild = metChild.nextSibling;
836                                         }
837                                         var metName = ifChild.attributes.getNamedItem("name").value;
838                                         if (!self[metName])
839                                                 self._addMethod(ifName, metName, nArgs, signature);
840                                         self.interfaceProxies[ifName]._addMethod(ifName, metName, nArgs, signature);
841                                 }
842                                 else if (ifChild.nodeName == "property") {
843                                         if (!hasProperties)
844                                                 self.propInterfaces.push(ifName);
845                                         hasProperties = true;
846                                 }
847                                 ifChild = ifChild.nextSibling;
848                         }
849                 }
850                 if (supportDBusProperties && self.propInterfaces.length > 0) {
851                         self.callMethod("org.freedesktop.DBus.Properties", 
852                                 "GetAll", 
853                                 [self.propInterfaces[self.propInterfaces.length-1]]).then(getAllPropertiesSuccessCB, 
854                                 errorCB ? errorCB : getAllPropertiesNextInterfaceCB);
855                 }
856                 else {
857                         self.propInterfaces = null;
858                         if (successCB)
859                                 successCB(self);
860                 }
861         }
862
863         // call Introspect on self
864         self.callMethod("org.freedesktop.DBus.Introspectable", "Introspect", []).then(introspectSuccessCB, errorCB);
865 };
866
867
868 cloudeebus.ProxyObject.prototype._addMethod = function(ifName, method, nArgs, signature) {
869
870         var self = this;
871         
872         self[method] = function() {
873                 var args = [];
874                 for (var i=0; i < nArgs; i++ )
875                         args.push(arguments[i]);
876                 return self.callMethod(ifName, method, args, signature);
877         };      
878 };
879
880
881 cloudeebus.ProxyObject.prototype.callMethod = function(ifName, method, args, signature) {
882         
883         var self = this;
884         
885         var promise = new cloudeebus.Promise(function (resolver) {
886                 function callMethodSuccessCB(str) {
887                         try { // calling dbus hook object function for un-translated types
888                                 var result = eval(str);
889                                 resolver.fulfill(result[0], true);
890                         }
891                         catch (e) {
892                                 var errorStr = cloudeebus.getError(e);
893                                 cloudeebus.log("Method callback exception: " + errorStr);
894                                 resolver.reject(errorStr, true);
895                         }
896                 }
897
898                 function callMethodErrorCB(error) {
899                         var errorStr = cloudeebus.getError(error);
900                         cloudeebus.log("Error calling method: " + method + " on object: " + self.objectPath + " : " + errorStr);
901                         resolver.reject(errorStr, true);
902                 }
903
904                 var arglist = [
905                         self.busConnection.name,
906                         self.busName,
907                         self.objectPath,
908                         ifName,
909                         method,
910                         JSON.stringify(args)
911                 ];
912
913                 // call dbusSend with bus type, destination, object, message and arguments
914                 self.wampSession.call("dbusSend", arglist).then(callMethodSuccessCB, callMethodErrorCB);
915         });
916         
917         return promise;
918 };
919
920
921 cloudeebus.ProxyObject.prototype.connectToSignal = function(ifName, signal, handlerCB, errorCB) {
922         
923         var self = this; 
924
925         function signalHandler(id, data) {
926                 if (handlerCB) {
927                         try { // calling dbus hook object function for un-translated types
928                                 handlerCB.apply(self, eval(data));
929                         }
930                         catch (e) {
931                                 var errorStr = cloudeebus.getError(e);
932                                 cloudeebus.log("Signal handler exception: " + errorStr);
933                                 if (errorCB)
934                                         errorCB(errorStr);
935                         }
936                 }
937         }
938         
939         function connectToSignalSuccessCB(str) {
940                 try {
941                         self.wampSession.subscribe(str, signalHandler);
942                 }
943                 catch (e) {
944                         cloudeebus.log("Subscribe error: " + cloudeebus.getError(e));
945                 }
946         }
947
948         function connectToSignalErrorCB(error) {
949                 var errorStr = cloudeebus.getError(error);
950                 cloudeebus.log("Error connecting to signal: " + signal + " on object: " + self.objectPath + " : " + errorStr);
951                 if (errorCB)
952                         errorCB(errorStr);
953         }
954
955         var arglist = [
956                 self.busConnection.name,
957                 self.busName,
958                 self.objectPath,
959                 ifName,
960                 signal
961         ];
962
963         // call dbusSend with bus type, destination, object, message and arguments
964         self.wampSession.call("dbusRegister", arglist).then(connectToSignalSuccessCB, connectToSignalErrorCB);
965 };
966
967
968 cloudeebus.ProxyObject.prototype.disconnectSignal = function(ifName, signal) {
969         try {
970                 this.wampSession.unsubscribe(this.busConnection.name + "#" + this.busName + "#" + this.objectPath + "#" + ifName + "#" + signal);
971         }
972         catch (e) {
973                 cloudeebus.log("Unsubscribe error: " + cloudeebus.getError(e));
974         }
975 };