1 /******************************************************************************
2 * Copyright 2012 Intel Corporation.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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 *****************************************************************************/
19 /*****************************************************************************/
21 var dbus = { // hook object for dbus types not translated by python-json
22 Double: function(value, level) {
29 /*****************************************************************************/
31 var cloudeebus = window.cloudeebus = {
36 cloudeebus.reset = function() {
37 cloudeebus.sessionBus = null;
38 cloudeebus.systemBus = null;
39 cloudeebus.wampSession = null;
40 cloudeebus.uri = null;
44 cloudeebus.log = function(msg) {
47 cloudeebus.getError = function(error) {
48 if (error.desc && error.uri)
49 return error.desc + " : " + error.uri; // Python exception (cloudeebus.py)
54 if (error.name && error.message)
55 return error.name + " : " + error.message; // Javascript exception
60 return error; // Autobahn error
63 cloudeebus.versionCheck = function(version) {
64 var ver = version.split(".");
65 var min = cloudeebus.minVersion.split(".");
66 for (var i=0; i<ver.length; i++) {
67 if (Number(ver[i]) > Number(min[i]))
69 if (Number(ver[i]) < Number(min[i]))
76 cloudeebus.connect = function(uri, manifest, successCB, errorCB) {
80 function onCloudeebusVersionCheckCB(version) {
81 if (cloudeebus.versionCheck(version)) {
82 cloudeebus.log("Connected to " + cloudeebus.uri);
86 var errorMsg = "Cloudeebus server version " + version + " unsupported, need version " + cloudeebus.minVersion + " or superior";
87 cloudeebus.log(errorMsg);
93 function onWAMPSessionAuthErrorCB(error) {
94 var errorStr = cloudeebus.getError(error);
95 cloudeebus.log("Authentication error: " + errorStr);
100 function onWAMPSessionAuthenticatedCB(permissions) {
101 cloudeebus.sessionBus = new cloudeebus.BusConnection("session", cloudeebus.wampSession);
102 cloudeebus.systemBus = new cloudeebus.BusConnection("system", cloudeebus.wampSession);
103 cloudeebus.wampSession.call("getVersion").then(onCloudeebusVersionCheckCB, errorCB);
106 function onWAMPSessionChallengedCB(challenge) {
107 var signature = cloudeebus.wampSession.authsign(challenge, manifest.key);
108 cloudeebus.wampSession.auth(signature).then(onWAMPSessionAuthenticatedCB, onWAMPSessionAuthErrorCB);
111 function onWAMPSessionConnectedCB(session) {
112 cloudeebus.wampSession = session;
114 cloudeebus.wampSession.authreq(
116 {permissions: manifest.permissions,
117 services: manifest.services}
118 ).then(onWAMPSessionChallengedCB, onWAMPSessionAuthErrorCB);
120 cloudeebus.wampSession.authreq().then(function() {
121 cloudeebus.wampSession.auth().then(onWAMPSessionAuthenticatedCB, onWAMPSessionAuthErrorCB);
122 }, onWAMPSessionAuthErrorCB);
125 function onWAMPSessionErrorCB(code, reason) {
126 if (code == ab.CONNECTION_UNSUPPORTED) {
127 cloudeebus.log("Browser is not supported");
130 cloudeebus.log("Failed to open session, code = " + code + ", reason = " + reason);
136 return ab.connect(cloudeebus.uri, onWAMPSessionConnectedCB, onWAMPSessionErrorCB);
140 cloudeebus.SessionBus = function() {
141 return cloudeebus.sessionBus;
145 cloudeebus.SystemBus = function() {
146 return cloudeebus.systemBus;
151 /*****************************************************************************/
153 cloudeebus.BusConnection = function(name, session) {
155 this.wampSession = session;
160 cloudeebus.BusConnection.prototype.getObject = function(busName, objectPath, introspectCB, errorCB) {
161 var proxy = new cloudeebus.ProxyObject(this.wampSession, this, busName, objectPath);
163 proxy._introspect(introspectCB, errorCB);
168 cloudeebus.BusConnection.prototype.addService = function(serviceName) {
171 var promise = new cloudeebus.Promise(function (resolver) {
172 var cloudeebusService = new cloudeebus.Service(self.wampSession, self, serviceName);
174 function ServiceAddedSuccessCB(serviceName) {
175 cloudeebusService.isCreated = true;
176 resolver.fulfill(cloudeebusService, true);
179 function ServiceAddedErrorCB(error) {
180 var errorStr = cloudeebus.getError(error);
181 cloudeebus.log("Error adding service method: " + self.name + ", error: " + errorStr);
182 resolver.reject(errorStr, true);
190 // call dbusSend with bus type, destination, object, message and arguments
191 self.wampSession.call("serviceAdd", arglist).then(ServiceAddedSuccessCB, ServiceAddedErrorCB);
199 /*****************************************************************************/
201 cloudeebus.Agent = function(objectPath, handler, xml) {
203 this.objectPath = objectPath;
204 this.handler = handler;
209 cloudeebus.Service = function(session, busConnection, name) {
210 this.wampSession = session;
211 this.busConnection = busConnection;
214 this.isCreated = false;
219 cloudeebus.Service.prototype.remove = function() {
222 var promise = new cloudeebus.Promise(function (resolver) {
223 function ServiceRemovedSuccessCB(serviceName) {
224 resolver.fulfill(serviceName, true);
227 function ServiceRemovedErrorCB(error) {
228 var errorStr = cloudeebus.getError(error);
229 resolver.reject(errorStr, true);
236 // call dbusSend with bus type, destination, object, message and arguments
237 self.wampSession.call("serviceRelease", arglist).then(ServiceRemovedSuccessCB, ServiceRemovedErrorCB);
244 cloudeebus.Service.prototype._searchMethod = function(ifName, method, objectJS) {
246 var funcToCall = null;
248 // Check if 'objectJS' has a member 'interfaceProxies' with an interface named 'ifName'
249 // and a method named 'method'
250 if (objectJS.interfaceProxies && objectJS.interfaceProxies[ifName] &&
251 objectJS.interfaceProxies[ifName][method]) {
252 funcToCall = objectJS.interfaceProxies[ifName][method];
254 // retrieve the method directly from 'root' of objectJs
255 funcToCall = objectJS[method];
262 cloudeebus.Service.prototype._addMethod = function(ifName, method, agent) {
265 var methodId = this.name + "#" + agent.objectPath + "#" + ifName + "#" + method;
266 var funcToCall = this._searchMethod(ifName, method, agent.handler);
268 if (funcToCall == null)
269 cloudeebus.log("Method " + method + " doesn't exist in Javascript object");
271 agent.handler.wrapperFunc[method] = function() {
273 var methodId = arguments[0];
275 // affectation of callDict in eval, otherwise dictionary(='{}') interpreted as block of code by eval
276 eval("callDict = " + arguments[1]);
278 result = funcToCall.apply(agent.handler, callDict.args);
279 service._returnMethod(methodId, callDict.callIndex, true, result);
282 var errorStr = cloudeebus.getError(e);
283 cloudeebus.log("Method " + ifName + "." + method + " call on " + agent.objectPath + " exception: " + errorStr);
284 service._returnMethod(methodId, callDict.callIndex, false, errorStr);
287 agent.handler.methodId[agent.objectPath].push(methodId);
288 this.wampSession.subscribe(methodId, agent.handler.wrapperFunc[method]);
293 cloudeebus.Service.prototype._addSignal = function(ifName, signal, agent) {
296 if (agent.handler[signal])
297 cloudeebus.log("Signal '" + signal + "' emitter already implemented");
299 agent.handler[signal] = function() {
301 for (var i=0; i < arguments.length; i++ )
302 args.push(arguments[i]);
303 service._emitSignal(agent.objectPath, signal, args);
309 cloudeebus.Service.prototype._createWrapper = function(agent) {
311 var parser = new DOMParser();
312 var xmlDoc = parser.parseFromString(agent.xml, "text/xml");
313 var ifXml = xmlDoc.getElementsByTagName("interface");
314 agent.handler.wrapperFunc = {};
315 agent.handler.methodId = {};
316 agent.handler.methodId[agent.objectPath] = [];
317 for (var i=0; i < ifXml.length; i++) {
318 var ifName = ifXml[i].attributes.getNamedItem("name").value;
319 var ifChild = ifXml[i].firstChild;
321 if (ifChild.nodeName == "method") {
322 var metName = ifChild.attributes.getNamedItem("name").value;
323 self._addMethod(ifName, metName, agent);
325 if (ifChild.nodeName == "signal") {
326 var metName = ifChild.attributes.getNamedItem("name").value;
327 self._addSignal(ifName, metName, agent);
329 ifChild = ifChild.nextSibling;
335 cloudeebus.Service.prototype.addAgent = function(agent) {
338 var promise = new cloudeebus.Promise(function (resolver) {
339 function ServiceAddAgentSuccessCB(objPath) {
340 self.agents.push(agent);
342 self._createWrapper(agent);
345 var errorStr = cloudeebus.getError(e);
346 cloudeebus.log("Exception creating agent wrapper " + agent.objectPath + " : " + errorStr);
347 resolver.reject(errorStr, true);
350 resolver.fulfill(objPath, true);
353 function ServiceAddAgenterrorCB(error) {
354 var errorStr = cloudeebus.getError(error);
355 cloudeebus.log("Error adding agent : " + agent.objectPath + ", error: " + errorStr);
356 self.promise.resolver.reject(errorStr, true);
365 // call dbusSend with bus type, destination, object, message and arguments
366 self.wampSession.call("serviceAddAgent", arglist).then(ServiceAddAgentSuccessCB, ServiceAddAgenterrorCB);
373 cloudeebus.Service.prototype._deleteWrapper = function(agent) {
374 var objJs = agent.handler;
375 if (objJs.methodId[agent.objectPath]) {
376 while (objJs.methodId[agent.objectPath].length) {
378 this.wampSession.unsubscribe( objJs.methodId[agent.objectPath].pop() );
381 cloudeebus.log("Unsubscribe error: " + cloudeebus.getError(e));
384 objJs.methodId[agent.objectPath] = null;
389 cloudeebus.Service.prototype.removeAgent = function(rmAgent) {
392 var promise = new cloudeebus.Promise(function (resolver) {
393 function ServiceRemoveAgentSuccessCB(objectPath) {
394 // Searching agent in list
395 for (var idx in self.agents)
396 if (self.agents[idx].objectPath == objectPath) {
397 agent = self.agents[idx];
401 self.agents.splice(idx, 1);
402 self._deleteWrapper(agent);
403 resolver.fulfill(agent, true);
406 function ServiceRemoveAgentErrorCB(error) {
407 var errorStr = cloudeebus.getError(error);
408 cloudeebus.log("Error removing agent : " + rmAgent.objectPath + ", error: " + errorStr);
409 resolver.reject(errorStr, true);
416 // call dbusSend with bus type, destination, object, message and arguments
417 self.wampSession.call("serviceDelAgent", arglist).then(ServiceRemoveAgentSuccessCB, ServiceRemoveAgentErrorCB);
424 cloudeebus.Service.prototype._returnMethod = function(methodId, callIndex, success, result, successCB, errorCB) {
432 this.wampSession.call("returnMethod", arglist).then(successCB, errorCB);
436 cloudeebus.Service.prototype._emitSignal = function(objectPath, signalName, args, successCB, errorCB) {
443 this.wampSession.call("emitSignal", arglist).then(successCB, errorCB);
448 /*****************************************************************************/
450 function _processWrappers(wrappers, value) {
451 for (var i=0; i<wrappers.length; i++)
456 function _processWrappersAsync(wrappers, value) {
458 function processAsyncOnce() {
459 _processWrappers(wrappers, value);
460 clearInterval(taskid);
462 taskid = setInterval(processAsyncOnce, 200);
467 /*****************************************************************************/
469 cloudeebus.PromiseResolver = function(promise) {
470 this.promise = promise;
471 this.resolved = null;
476 cloudeebus.PromiseResolver.prototype.resolve = function(value, sync) {
480 var then = (value && value.then && value.then.apply) ? value.then : null;
483 var fulfillCallback = function(arg) {
484 self.resolve(arg, true);
486 var rejectCallback = function(arg) {
487 self.reject(arg, true);
490 then.apply(value, [fulfillCallback, rejectCallback]);
493 this.reject(cloudeebus.getError(e), true);
497 this.fulfill(value, sync);
501 cloudeebus.PromiseResolver.prototype.fulfill = function(value, sync) {
505 var promise = this.promise;
506 promise.state = "fulfilled";
507 promise.result = value;
509 this.resolved = true;
511 _processWrappers(promise._fulfillWrappers, value);
513 _processWrappersAsync(promise._fulfillWrappers, value);
517 cloudeebus.PromiseResolver.prototype.reject = function(value, sync) {
521 var promise = this.promise;
522 promise.state = "rejected";
523 promise.result = value;
525 this.resolved = true;
527 _processWrappers(promise._rejectWrappers, value);
529 _processWrappersAsync(promise._rejectWrappers, value);
534 /*****************************************************************************/
536 cloudeebus.Promise = function(init) {
537 this.state = "pending";
539 this._fulfillWrappers = [];
540 this._rejectWrappers = [];
541 this.resolver = new cloudeebus.PromiseResolver(this);
544 init.apply(this, [this.resolver]);
547 this.resolver.reject(cloudeebus.getError(e), true);
554 cloudeebus.Promise.prototype.appendWrappers = function(fulfillWrapper, rejectWrapper) {
556 this._fulfillWrappers.push(fulfillWrapper);
558 this._rejectWrappers.push(rejectWrapper);
559 if (this.state == "fulfilled")
560 _processWrappersAsync(this._fulfillWrappers, this.result);
561 if (this.state == "rejected")
562 _processWrappersAsync(this._rejectWrappers, this.result);
566 cloudeebus.Promise.prototype.then = function(fulfillCB, rejectCB) {
567 var promise = new cloudeebus.Promise();
568 var resolver = promise.resolver;
569 var fulfillWrapper, rejectWrapper;
572 fulfillWrapper = function(arg) {
574 var value = fulfillCB.apply(promise, [arg]);
575 resolver.resolve(value, true);
578 resolver.reject(cloudeebus.getError(e), true);
582 fulfillWrapper = function(arg) {
583 resolver.fulfill(arg, true);
587 rejectWrapper = function(arg) {
589 var value = rejectCB.apply(promise, [arg]);
590 resolver.resolve(value, true);
593 resolver.reject(cloudeebus.getError(e), true);
597 rejectWrapper = function(arg) {
598 resolver.reject(arg, true);
601 this.appendWrappers(fulfillWrapper,rejectWrapper);
606 cloudeebus.Promise.prototype["catch"] = function(rejectCB) {
607 return this.then(undefined,rejectCB);
611 cloudeebus.Promise.prototype.done = function(fulfillCB, rejectCB) {
612 this.appendWrappers(fulfillCB,rejectCB);
616 cloudeebus.Promise.resolve = function(value) {
617 var promise = new cloudeebus.Promise();
618 promise.resolver.resolve(value);
623 cloudeebus.Promise.fulfill = function(value) {
624 var promise = new cloudeebus.Promise();
625 promise.resolver.fulfill(value);
630 cloudeebus.Promise.reject = function(value) {
631 var promise = new cloudeebus.Promise();
632 promise.resolver.reject(value);
637 cloudeebus.Promise.any = function() {
638 var promise = new cloudeebus.Promise();
639 var resolver = promise.resolver;
640 var fulfillCallback = function(arg) {
641 resolver.resolve(arg, true);
643 var rejectCallback = function(arg) {
644 resolver.reject(arg, true);
646 if (arguments.length == 0)
647 resolver.resolve(undefined, true);
650 cloudeebus.Promise.resolve(arguments[i]).appendWrappers(fulfillCallback,rejectCallback);
655 cloudeebus.Promise.every = function() {
656 var promise = new cloudeebus.Promise();
657 var resolver = promise.resolver;
659 var countdown = arguments.length;
660 var args = new Array(countdown);
661 var rejectCallback = function(arg) {
662 resolver.reject(arg, true);
664 if (arguments.length == 0)
665 resolver.resolve(undefined, true);
667 for (i in arguments) {
668 var fulfillCallback = function(arg) {
672 resolver.resolve(args, true);
675 cloudeebus.Promise.resolve(arguments[i]).appendWrappers(fulfillCallback,rejectCallback);
682 cloudeebus.Promise.some = function() {
683 var promise = new cloudeebus.Promise();
684 var resolver = promise.resolver;
686 var countdown = arguments.length;
687 var args = new Array(countdown);
688 var fulfillCallback = function(arg) {
689 resolver.resolve(arg, true);
691 if (arguments.length == 0)
692 resolver.resolve(undefined, true);
694 for (i in arguments) {
695 var rejectCallback = function(arg) {
699 resolver.reject(args, true);
702 cloudeebus.Promise.resolve(arguments[i]).appendWrappers(fulfillCallback,rejectCallback);
710 /*****************************************************************************/
712 cloudeebus.ProxyObject = function(session, busConnection, busName, objectPath) {
713 this.wampSession = session;
714 this.busConnection = busConnection;
715 this.busName = busName;
716 this.objectPath = objectPath;
717 this.interfaceProxies = {};
718 this.childNodeNames = [];
723 cloudeebus.ProxyObject.prototype.getInterface = function(ifName) {
724 return this.interfaceProxies[ifName];
728 cloudeebus.ProxyObject.prototype._introspect = function(successCB, errorCB) {
732 function getAllPropertiesSuccessCB(props) {
733 var ifProxy = self.interfaceProxies[self.propInterfaces[self.propInterfaces.length-1]];
734 for (var prop in props)
735 ifProxy[prop] = self[prop] = props[prop];
736 getAllPropertiesNextInterfaceCB();
739 function getAllPropertiesNextInterfaceCB() {
740 self.propInterfaces.pop();
741 if (self.propInterfaces.length > 0)
742 self.callMethod("org.freedesktop.DBus.Properties",
744 [self.propInterfaces[self.propInterfaces.length-1]]).then(getAllPropertiesSuccessCB,
745 errorCB ? errorCB : getAllPropertiesNextInterfaceCB);
747 self.propInterfaces = null;
753 function introspectSuccessCB(str) {
754 var parser = new DOMParser();
755 var xmlDoc = parser.parseFromString(str, "text/xml");
756 var nodes = xmlDoc.getElementsByTagName("node");
757 // first node is the parent/head node
758 for(var i=1; i < nodes.length; i++)
759 self.childNodeNames.push(nodes[i].getAttribute("name"));
760 var interfaces = xmlDoc.getElementsByTagName("interface");
761 self.propInterfaces = [];
762 var supportDBusProperties = false;
763 for (var i=0; i < interfaces.length; i++) {
764 var ifName = interfaces[i].attributes.getNamedItem("name").value;
765 self.interfaceProxies[ifName] = new cloudeebus.ProxyObject(self.wampSession, self.busConnection, self.busName, self.objectPath);
766 if (ifName == "org.freedesktop.DBus.Properties")
767 supportDBusProperties = true;
768 var hasProperties = false;
769 var ifChild = interfaces[i].firstChild;
771 if (ifChild.nodeName == "method") {
774 var metChild = ifChild.firstChild;
776 if (metChild.nodeName == "arg" &&
777 metChild.attributes.getNamedItem("direction").value == "in") {
778 signature += metChild.attributes.getNamedItem("type").value;
781 metChild = metChild.nextSibling;
783 var metName = ifChild.attributes.getNamedItem("name").value;
785 self._addMethod(ifName, metName, nArgs, signature);
786 self.interfaceProxies[ifName]._addMethod(ifName, metName, nArgs, signature);
788 else if (ifChild.nodeName == "property") {
790 self.propInterfaces.push(ifName);
791 hasProperties = true;
793 ifChild = ifChild.nextSibling;
796 if (supportDBusProperties && self.propInterfaces.length > 0) {
797 self.callMethod("org.freedesktop.DBus.Properties",
799 [self.propInterfaces[self.propInterfaces.length-1]]).then(getAllPropertiesSuccessCB,
800 errorCB ? errorCB : getAllPropertiesNextInterfaceCB);
803 self.propInterfaces = null;
809 // call Introspect on self
810 self.callMethod("org.freedesktop.DBus.Introspectable", "Introspect", []).then(introspectSuccessCB, errorCB);
814 cloudeebus.ProxyObject.prototype._addMethod = function(ifName, method, nArgs, signature) {
818 self[method] = function() {
820 for (var i=0; i < nArgs; i++ )
821 args.push(arguments[i]);
822 return self.callMethod(ifName, method, args, signature);
827 cloudeebus.ProxyObject.prototype.callMethod = function(ifName, method, args, signature) {
831 var promise = new cloudeebus.Promise(function (resolver) {
832 function callMethodSuccessCB(str) {
833 try { // calling dbus hook object function for un-translated types
834 var result = eval(str);
835 resolver.fulfill(result[0], true);
838 var errorStr = cloudeebus.getError(e);
839 cloudeebus.log("Method callback exception: " + errorStr);
840 resolver.reject(errorStr, true);
844 function callMethodErrorCB(error) {
845 var errorStr = cloudeebus.getError(error);
846 cloudeebus.log("Error calling method: " + method + " on object: " + self.objectPath + " : " + errorStr);
847 resolver.reject(errorStr, true);
851 self.busConnection.name,
859 // call dbusSend with bus type, destination, object, message and arguments
860 self.wampSession.call("dbusSend", arglist).then(callMethodSuccessCB, callMethodErrorCB);
867 cloudeebus.ProxyObject.prototype.connectToSignal = function(ifName, signal, handlerCB, errorCB) {
871 function signalHandler(id, data) {
873 try { // calling dbus hook object function for un-translated types
874 handlerCB.apply(self, eval(data));
877 var errorStr = cloudeebus.getError(e);
878 cloudeebus.log("Signal handler exception: " + errorStr);
885 function connectToSignalSuccessCB(str) {
887 self.wampSession.subscribe(str, signalHandler);
890 cloudeebus.log("Subscribe error: " + cloudeebus.getError(e));
894 function connectToSignalErrorCB(error) {
895 var errorStr = cloudeebus.getError(error);
896 cloudeebus.log("Error connecting to signal: " + signal + " on object: " + self.objectPath + " : " + errorStr);
902 self.busConnection.name,
909 // call dbusSend with bus type, destination, object, message and arguments
910 self.wampSession.call("dbusRegister", arglist).then(connectToSignalSuccessCB, connectToSignalErrorCB);
914 cloudeebus.ProxyObject.prototype.disconnectSignal = function(ifName, signal) {
916 this.wampSession.unsubscribe(this.busConnection.name + "#" + this.busName + "#" + this.objectPath + "#" + ifName + "#" + signal);
919 cloudeebus.log("Unsubscribe error: " + cloudeebus.getError(e));