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