3da7af9a5281dab41d9568bfab4e5735f6a1af44
[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; // error cloudeebus (from python)
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; // error from Autobahn
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) {
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.pop(agent);
445                                 agent.registered = false;
446                                 resolver.fulfill(agent, true);
447                         }
448                         catch (e) {
449                                 var errorStr = cloudeebus.getError(e);
450                                 cloudeebus.log("Method callback exception: " + errorStr);
451                                 resolver.reject(errorStr, true);
452                         }               
453                 }
454
455                 function ServiceRemoveAgentErrorCB(error) {
456                         var errorStr = cloudeebus.getError(error);
457                         cloudeebus.log("Error removing agent : " + rmAgent.objectPath + ", error: " + errorStr);
458                         self.promise.resolver.reject(errorStr, true);
459                 }
460
461                 try {
462                         self._deleteWrapper(rmAgent);
463                 }
464                 catch (e) {
465                         var errorStr = cloudeebus.getError(e);
466                         cloudeebus.log("Exception removing wrapper of agent " + rmAgent.objectPath + " : " + errorStr);
467                         errorCB(errorStr);
468                 }
469                 
470                 var arglist = [
471                     rmAgent.objectPath
472                     ];
473         
474                 // call dbusSend with bus type, destination, object, message and arguments
475                 self.wampSession.call("serviceDelAgent", arglist).then(ServiceRemoveAgentSuccessCB, ServiceRemoveAgentErrorCB);
476         });
477         
478         return promise;
479 };
480
481
482 cloudeebus.Service.prototype._returnMethod = function(methodId, callIndex, success, result, successCB, errorCB) {
483         var arglist = [
484             methodId,
485             callIndex,
486             success,
487             result
488             ];
489
490         this.wampSession.call("returnMethod", arglist).then(successCB, errorCB);
491 };
492
493
494 cloudeebus.Service.prototype._emitSignal = function(objectPath, signalName, result, successCB, errorCB) {
495         var arglist = [
496             objectPath,
497             signalName,
498             result
499             ];
500
501         this.wampSession.call("emitSignal", arglist).then(successCB, errorCB);
502 };
503
504
505
506 /*****************************************************************************/
507
508 function _processWrappers(wrappers, value) {
509         for (var i=0; i<wrappers.length; i++)
510                 wrappers[i](value);
511 }
512
513
514 function _processWrappersAsync(wrappers, value) {
515         var taskid = -1;
516         function processAsyncOnce() {
517                 _processWrappers(wrappers, value);
518                 clearInterval(taskid);
519         }
520         taskid = setInterval(processAsyncOnce, 200);
521 }
522
523
524
525 /*****************************************************************************/
526
527 cloudeebus.PromiseResolver = function(promise) {
528         this.promise = promise;
529         this.resolved = null;
530     return this;
531 };
532
533
534 cloudeebus.PromiseResolver.prototype.resolve = function(value, sync) {
535         if (this.resolved)
536                 return;
537         
538         var then = (value && value.then && value.then.apply) ? value.then : null;
539         if (then) {
540                 var self = this;                
541                 var fulfillCallback = function(arg) {
542                         self.resolve(arg, true);
543                 };      
544                 var rejectCallback = function(arg) {
545                         self.reject(arg, true);
546                 };
547                 try {
548                         then.apply(value, [fulfillCallback, rejectCallback]);
549                 }
550                 catch (e) {
551                         this.reject(cloudeebus.getError(e), true);
552                 }
553         }
554         
555         this.fulfill(value, sync);
556 };
557
558
559 cloudeebus.PromiseResolver.prototype.fulfill = function(value, sync) {
560         if (this.resolved)
561                 return;
562         
563         var promise = this.promise;
564         promise.state = "fulfilled";
565         promise.result = value;
566         
567         this.resolved = true;
568         if (sync)
569                 _processWrappers(promise._fulfillWrappers, value);
570         else
571                 _processWrappersAsync(promise._fulfillWrappers, value);
572 };
573
574
575 cloudeebus.PromiseResolver.prototype.reject = function(value, sync) {
576         if (this.resolved)
577                 return;
578         
579         var promise = this.promise;
580         promise.state = "rejected";
581         promise.result = value;
582         
583         this.resolved = true;
584         if (sync)
585                 _processWrappers(promise._rejectWrappers, value);
586         else
587                 _processWrappersAsync(promise._rejectWrappers, value);
588 };
589
590
591
592 /*****************************************************************************/
593
594 cloudeebus.Promise = function(init) {
595         this.state = "pending";
596         this.result = null;
597         this._fulfillWrappers = [];
598         this._rejectWrappers = [];
599         this.resolver = new cloudeebus.PromiseResolver(this);
600         if (init) {
601                 try {
602                         init.apply(this, [this.resolver]);
603                 }
604                 catch (e) {
605                         this.resolver.reject(cloudeebus.getError(e), true);
606                 }
607         }
608     return this;
609 };
610
611
612 cloudeebus.Promise.prototype.appendWrappers = function(fulfillWrapper, rejectWrapper) {
613         if (fulfillWrapper)
614                 this._fulfillWrappers.push(fulfillWrapper);
615         if (rejectWrapper)
616                 this._rejectWrappers.push(rejectWrapper);
617         if (this.state == "fulfilled")
618                 _processWrappersAsync(this._fulfillWrappers, this.result);
619         if (this.state == "rejected")
620                 _processWrappersAsync(this._rejectWrappers, this.result);
621 };
622
623
624 cloudeebus.Promise.prototype.then = function(fulfillCB, rejectCB) {
625         var promise = new cloudeebus.Promise();
626         var resolver = promise.resolver;
627         var fulfillWrapper, rejectWrapper;
628         
629         if (fulfillCB)
630                 fulfillWrapper = function(arg) {
631                         try {
632                                 var value = fulfillCB.apply(promise, [arg]);
633                                 resolver.resolve(value, true);
634                         }
635                         catch (e) {
636                                 resolver.reject(cloudeebus.getError(e), true);
637                         }
638                 };
639         else
640                 fulfillWrapper = function(arg) {
641                         resolver.fulfill(arg, true);
642                 };
643         
644         if (rejectCB)
645                 rejectWrapper = function(arg) {
646                         try {
647                                 var value = rejectCB.apply(promise, [arg]);
648                                 resolver.resolve(value, true);
649                         }
650                         catch (e) {
651                                 resolver.reject(cloudeebus.getError(e), true);
652                         }
653                 };
654         else
655                 rejectWrapper = function(arg) {
656                         resolver.reject(arg, true);
657                 };
658         
659         this.appendWrappers(fulfillWrapper,rejectWrapper);
660         return promise;
661 };
662
663
664 cloudeebus.Promise.prototype["catch"] = function(rejectCB) {
665         return this.then(undefined,rejectCB);
666 };
667
668
669 cloudeebus.Promise.prototype.done = function(fulfillCB, rejectCB) {
670         this.appendWrappers(fulfillCB,rejectCB);
671 };
672
673
674 cloudeebus.Promise.resolve = function(value) {
675         var promise = new cloudeebus.Promise();
676         promise.resolver.resolve(value);
677         return promise;
678 };
679
680
681 cloudeebus.Promise.fulfill = function(value) {
682         var promise = new cloudeebus.Promise();
683         promise.resolver.fulfill(value);
684         return promise;
685 };
686
687
688 cloudeebus.Promise.reject = function(value) {
689         var promise = new cloudeebus.Promise();
690         promise.resolver.reject(value);
691         return promise;
692 };
693
694
695 cloudeebus.Promise.any = function() {
696         var promise = new cloudeebus.Promise();
697         var resolver = promise.resolver;
698         var fulfillCallback = function(arg) {
699                 resolver.resolve(arg, true);
700         };
701         var rejectCallback = function(arg) {
702                 resolver.reject(arg, true);
703         };
704         if (arguments.length == 0)
705                 resolver.resolve(undefined, true);
706         else
707                 for (i in arguments) 
708                         Promise.resolve(arguments[i]).appendWrappers(fulfillCallback,rejectCallback);
709         return promise;
710 };
711
712
713 cloudeebus.Promise.every = function() {
714         var promise = new cloudeebus.Promise();
715         var resolver = promise.resolver;
716         var index = 0;
717         var countdown = arguments.length;
718         var args = new Array(countdown);
719         var rejectCallback = function(arg) {
720                 resolver.reject(arg, true);
721         };
722         if (arguments.length == 0)
723                 resolver.resolve(undefined, true);
724         else
725                 for (i in arguments) {
726                         var fulfillCallback = function(arg) {
727                                 args[index] = arg;
728                                 countdown--;
729                                 if (countdown == 0)
730                                         resolver.resolve(args, true);
731                         };
732                         index++;
733                         Promise.resolve(arguments[i]).appendWrappers(fulfillCallback,rejectCallback);
734                 }
735         
736         return promise;
737 };
738
739
740 cloudeebus.Promise.some = function() {
741         var promise = new cloudeebus.Promise();
742         var resolver = promise.resolver;
743         var index = 0;
744         var countdown = arguments.length;
745         var args = new Array(countdown);
746         var fulfillCallback = function(arg) {
747                 resolver.resolve(arg, true);
748         };
749         if (arguments.length == 0)
750                 resolver.resolve(undefined, true);
751         else
752                 for (i in arguments) {
753                         var rejectCallback = function(arg) {
754                                 args[index] = arg;
755                                 countdown--;
756                                 if (countdown == 0)
757                                         resolver.reject(args, true);
758                         };
759                         index++;
760                         Promise.resolve(arguments[i]).appendWrappers(fulfillCallback,rejectCallback);
761                 }
762         
763         return promise;
764 };
765
766
767
768 /*****************************************************************************/
769
770 cloudeebus.ProxyObject = function(session, busConnection, busName, objectPath) {
771         this.wampSession = session; 
772         this.busConnection = busConnection; 
773         this.busName = busName; 
774         this.objectPath = objectPath; 
775         this.interfaceProxies = {};
776         return this;
777 };
778
779
780 cloudeebus.ProxyObject.prototype.getInterface = function(ifName) {
781         return this.interfaceProxies[ifName];
782 };
783
784
785 cloudeebus.ProxyObject.prototype._introspect = function(successCB, errorCB) {
786         
787         var self = this; 
788
789         function getAllPropertiesSuccessCB(props) {
790                 var ifProxy = self.interfaceProxies[self.propInterfaces[self.propInterfaces.length-1]];
791                 for (var prop in props)
792                         ifProxy[prop] = self[prop] = props[prop];
793                 getAllPropertiesNextInterfaceCB();
794         }
795         
796         function getAllPropertiesNextInterfaceCB() {
797                 self.propInterfaces.pop();
798                 if (self.propInterfaces.length > 0) 
799                         self.callMethod("org.freedesktop.DBus.Properties", 
800                                 "GetAll", 
801                                 [self.propInterfaces[self.propInterfaces.length-1]]).then(getAllPropertiesSuccessCB, 
802                                 errorCB ? errorCB : getAllPropertiesNextInterfaceCB);
803                 else {
804                         self.propInterfaces = null;
805                         if (successCB)
806                                 successCB(self);
807                 }
808         }
809         
810         function introspectSuccessCB(str) {
811                 var parser = new DOMParser();
812                 var xmlDoc = parser.parseFromString(str, "text/xml");
813                 var interfaces = xmlDoc.getElementsByTagName("interface");
814                 self.propInterfaces = [];
815                 var supportDBusProperties = false;
816                 for (var i=0; i < interfaces.length; i++) {
817                         var ifName = interfaces[i].attributes.getNamedItem("name").value;
818                         self.interfaceProxies[ifName] = new cloudeebus.ProxyObject(self.wampSession, self.busConnection, self.busName, self.objectPath);
819                         if (ifName == "org.freedesktop.DBus.Properties")
820                                 supportDBusProperties = true;
821                         var hasProperties = false;
822                         var ifChild = interfaces[i].firstChild;
823                         while (ifChild) {
824                                 if (ifChild.nodeName == "method") {
825                                         var nArgs = 0;
826                                         var signature = "";
827                                         var metChild = ifChild.firstChild;
828                                         while (metChild) {
829                                                 if (metChild.nodeName == "arg" &&
830                                                         metChild.attributes.getNamedItem("direction").value == "in") {
831                                                                 signature += metChild.attributes.getNamedItem("type").value;
832                                                                 nArgs++;
833                                                 }
834                                                 metChild = metChild.nextSibling;
835                                         }
836                                         var metName = ifChild.attributes.getNamedItem("name").value;
837                                         if (!self[metName])
838                                                 self._addMethod(ifName, metName, nArgs, signature);
839                                         self.interfaceProxies[ifName]._addMethod(ifName, metName, nArgs, signature);
840                                 }
841                                 else if (ifChild.nodeName == "property") {
842                                         if (!hasProperties)
843                                                 self.propInterfaces.push(ifName);
844                                         hasProperties = true;
845                                 }
846                                 ifChild = ifChild.nextSibling;
847                         }
848                 }
849                 if (supportDBusProperties && self.propInterfaces.length > 0) {
850                         self.callMethod("org.freedesktop.DBus.Properties", 
851                                 "GetAll", 
852                                 [self.propInterfaces[self.propInterfaces.length-1]]).then(getAllPropertiesSuccessCB, 
853                                 errorCB ? errorCB : getAllPropertiesNextInterfaceCB);
854                 }
855                 else {
856                         self.propInterfaces = null;
857                         if (successCB)
858                                 successCB(self);
859                 }
860         }
861
862         // call Introspect on self
863         self.callMethod("org.freedesktop.DBus.Introspectable", "Introspect", []).then(introspectSuccessCB, errorCB);
864 };
865
866
867 cloudeebus.ProxyObject.prototype._addMethod = function(ifName, method, nArgs, signature) {
868
869         var self = this;
870         
871         self[method] = function() {
872                 var args = [];
873                 for (var i=0; i < nArgs; i++ )
874                         args.push(arguments[i]);
875                 return self.callMethod(ifName, method, args, signature);
876         };      
877 };
878
879
880 cloudeebus.ProxyObject.prototype.callMethod = function(ifName, method, args, signature) {
881         
882         var self = this;
883         
884         var promise = new cloudeebus.Promise(function (resolver) {
885                 function callMethodSuccessCB(str) {
886                         try { // calling dbus hook object function for un-translated types
887                                 var result = eval(str);
888                                 resolver.fulfill(result[0], true);
889                         }
890                         catch (e) {
891                                 var errorStr = cloudeebus.getError(e);
892                                 cloudeebus.log("Method callback exception: " + errorStr);
893                                 resolver.reject(errorStr, true);
894                         }
895                 }
896
897                 function callMethodErrorCB(error) {
898                         var errorStr = cloudeebus.getError(error);
899                         cloudeebus.log("Error calling method: " + method + " on object: " + self.objectPath + " : " + errorStr);
900                         resolver.reject(errorStr, true);
901                 }
902
903                 var arglist = [
904                         self.busConnection.name,
905                         self.busName,
906                         self.objectPath,
907                         ifName,
908                         method,
909                         JSON.stringify(args)
910                 ];
911
912                 // call dbusSend with bus type, destination, object, message and arguments
913                 self.wampSession.call("dbusSend", arglist).then(callMethodSuccessCB, callMethodErrorCB);
914         });
915         
916         return promise;
917 };
918
919
920 cloudeebus.ProxyObject.prototype.connectToSignal = function(ifName, signal, handlerCB, errorCB) {
921         
922         var self = this; 
923
924         function signalHandler(id, data) {
925                 if (handlerCB) {
926                         try { // calling dbus hook object function for un-translated types
927                                 handlerCB.apply(self, eval(data));
928                         }
929                         catch (e) {
930                                 var errorStr = cloudeebus.getError(e);
931                                 cloudeebus.log("Signal handler exception: " + errorStr);
932                                 if (errorCB)
933                                         errorCB(errorStr);
934                         }
935                 }
936         }
937         
938         function connectToSignalSuccessCB(str) {
939                 try {
940                         self.wampSession.subscribe(str, signalHandler);
941                 }
942                 catch (e) {
943                         cloudeebus.log("Subscribe error: " + cloudeebus.getError(e));
944                 }
945         }
946
947         function connectToSignalErrorCB(error) {
948                 var errorStr = cloudeebus.getError(error);
949                 cloudeebus.log("Error connecting to signal: " + signal + " on object: " + self.objectPath + " : " + errorStr);
950                 if (errorCB)
951                         errorCB(errorStr);
952         }
953
954         var arglist = [
955                 self.busConnection.name,
956                 self.busName,
957                 self.objectPath,
958                 ifName,
959                 signal
960         ];
961
962         // call dbusSend with bus type, destination, object, message and arguments
963         self.wampSession.call("dbusRegister", arglist).then(connectToSignalSuccessCB, connectToSignalErrorCB);
964 };
965
966
967 cloudeebus.ProxyObject.prototype.disconnectSignal = function(ifName, signal) {
968         try {
969                 this.wampSession.unsubscribe(this.busConnection.name + "#" + this.busName + "#" + this.objectPath + "#" + ifName + "#" + signal);
970         }
971         catch (e) {
972                 cloudeebus.log("Unsubscribe error: " + cloudeebus.getError(e));
973         }
974 };