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