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