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