From 4b450bab1164b9b1225702238e539201974a68b6 Mon Sep 17 00:00:00 2001 From: Ray Strode Date: Thu, 30 May 2013 10:15:09 -0400 Subject: [PATCH] misc: add objectManager class The D-Bus ObjectManager interface is fairly recent addition to the D-Bus specification. Its purpose is to provide a standardized way to track objects dynamically coming and going for a service, and to track capabilities dynamically coming and going for those objects (by means of interfaces). This commit adds the requisite code needed to make use of the ObjectManager interface. It will ultimately be needed to implement smartcard support in the login screen. https://bugzilla.gnome.org/show_bug.cgi?id=683437 --- js/Makefile.am | 1 + js/misc/objectManager.js | 254 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 255 insertions(+) create mode 100644 js/misc/objectManager.js diff --git a/js/Makefile.am b/js/Makefile.am index 5eaa6a8f7..ea3f44552 100644 --- a/js/Makefile.am +++ b/js/Makefile.am @@ -33,6 +33,7 @@ nobase_dist_js_DATA = \ misc/jsParse.js \ misc/loginManager.js \ misc/modemManager.js \ + misc/objectManager.js \ misc/params.js \ misc/util.js \ perf/core.js \ diff --git a/js/misc/objectManager.js b/js/misc/objectManager.js new file mode 100644 index 000000000..b954f1bee --- /dev/null +++ b/js/misc/objectManager.js @@ -0,0 +1,254 @@ +// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- + +const Gio = imports.gi.Gio; +const GLib = imports.gi.GLib; +const Lang = imports.lang; +const Params = imports.misc.params; +const Signals = imports.signals; + +// Specified in the D-Bus specification here: +// http://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces-objectmanager +const ObjectManagerIface = + + + + + + + + + + + + + +const ObjectManagerInfo = Gio.DBusInterfaceInfo.new_for_xml(ObjectManagerIface); + +const ObjectManager = new Lang.Class({ + Name: 'ObjectManager', + _init: function(params) { + params = Params.parse(params, { connection: null, + name: null, + objectPath: null, + knownInterfaces: null, + cancellable: null, + onLoaded: null }); + + this._connection = params.connection; + this._serviceName = params.name; + this._managerPath = params.objectPath; + this._cancellable = params.cancellable; + + this._managerProxy = new Gio.DBusProxy({ g_connection: this._connection, + g_interface_name: ObjectManagerInfo.name, + g_interface_info: ObjectManagerInfo, + g_name: this._serviceName, + g_object_path: this._managerPath, + g_flags: Gio.DBusProxyFlags.NONE }); + + this._interfaceInfos = {}; + this._objects = {}; + this._interfaces = {}; + this._onLoaded = params.onLoaded; + + if (params.knownInterfaces) + this._registerInterfaces(params.knownInterfaces); + + // Start out inhibiting load until at least the proxy + // manager is loaded and the remote objects are fetched + this._numLoadInhibitors = 1; + this._managerProxy.init_async(GLib.PRIORITY_DEFAULT, + this._cancellable, + Lang.bind(this, this._onManagerProxyLoaded)); + }, + + _tryToCompleteLoad: function() { + this._numLoadInhibitors--; + if (this._numLoadInhibitors == 0) { + if (this._onLoaded) + this._onLoaded(); + } + }, + + _addInterface: function(objectPath, interfaceName, onFinished) { + let info = this._interfaceInfos[interfaceName]; + + if (!info) + return; + + let proxy = new Gio.DBusProxy({ g_connection: this._connection, + g_name: this._serviceName, + g_object_path: objectPath, + g_interface_name: interfaceName, + g_interface_info: info, + g_flags: Gio.DBusProxyFlags.NONE }); + + proxy.init_async(GLib.PRIORITY_DEFAULT, + this._cancellable, + Lang.bind(this, function(initable, result) { + let error = null; + try { + initable.init_finish(result); + } catch(e) { + logError(e, 'could not initialize proxy for interface ' + interfaceName); + + if (onFinished) + onFinished(); + return; + } + + let isNewObject; + if (!this._objects[objectPath]) { + this._objects[objectPath] = {}; + isNewObject = true; + } else { + isNewObject = false; + } + + this._objects[objectPath][interfaceName] = proxy; + + if (!this._interfaces[interfaceName]) + this._interfaces[interfaceName] = []; + + this._interfaces[interfaceName].push(proxy); + + if (isNewObject) + this.emit('object-added', objectPath); + + this.emit('interface-added', interfaceName, proxy); + + if (onFinished) + onFinished(); + })); + }, + + _removeInterface: function(objectPath, interfaceName) { + if (!this._objects[objectPath]) + return; + + let proxy = this._objects[objectPath][interfaceName]; + + if (this._interfaces[interfaceName]) { + let index = this._interfaces[interfaceName].indexOf(proxy); + + if (index >= 0) + this._interfaces[interfaceName].splice(index, 1); + + if (this._interfaces[interfaceName].length == 0) + delete this._interfaces[interfaceName]; + } + + this.emit('interface-removed', interfaceName, proxy); + + this._objects[objectPath][interfaceName] = null; + + if (Object.keys(this._objects[objectPath]).length == 0) { + delete this._objects[objectPath]; + this.emit('object-removed', objectPath); + } + }, + + _onManagerProxyLoaded: function(initable, result) { + let error = null; + try { + initable.init_finish(result); + } catch(e) { + logError(e, 'could not initialize object manager for object ' + params.name); + + this._tryToCompleteLoad(); + return; + } + + this._managerProxy.connectSignal('InterfacesAdded', + Lang.bind(this, function(objectManager, sender, [objectPath, interfaces]) { + let interfaceNames = Object.keys(interfaces); + for (let i = 0; i < interfaceNames.length; i++) + this._addInterface(objectPath, interfaceNames[i]); + })); + this._managerProxy.connectSignal('InterfacesRemoved', + Lang.bind(this, function(objectManager, sender, [objectPath, interfaceNames]) { + for (let i = 0; i < interfaceNames.length; i++) + this._removeInterface(objectPath, interfaceNames[i]); + })); + + if (Object.keys(this._interfaceInfos).length == 0) { + this._tryToCompleteLoad(); + return; + } + + this._managerProxy.GetManagedObjectsRemote(Lang.bind(this, function(result, error) { + if (!result) { + if (error) { + logError(error, 'could not get remote objects for service ' + this._serviceName + ' path ' + this._managerPath); + } + + this._tryToCompleteLoad(); + return; + } + + let [objects] = result; + + let objectPaths = Object.keys(objects); + for (let i = 0; i < objectPaths.length; i++) { + let objectPath = objectPaths[i]; + let object = objects[objectPath]; + + let interfaceNames = Object.getOwnPropertyNames(object); + for (let j = 0; j < interfaceNames.length; j++) { + let interfaceName = interfaceNames[j]; + + // Prevent load from completing until the interface is loaded + this._numLoadInhibitors++; + this._addInterface(objectPath, + interfaceName, + Lang.bind(this, this._tryToCompleteLoad)); + } + } + this._tryToCompleteLoad(); + })); + }, + + _registerInterfaces: function(interfaces) { + for (let i = 0; i < interfaces.length; i++) { + let info = Gio.DBusInterfaceInfo.new_for_xml(interfaces[i]); + this._interfaceInfos[info.name] = info; + } + }, + + getProxy: function(objectPath, interfaceName) { + let object = this._objects[objectPath]; + + if (!object) + return null; + + return object[interfaceName]; + }, + + getProxiesForInterface: function(interfaceName) { + let proxyList = this._interfaces[interfaceName]; + + if (!proxyList) + return []; + + return proxyList; + }, + + getAllProxies: function() { + let proxies = []; + + let objectPaths = Object.keys(this._objects); + for (let i = 0; i < objectPaths.length; i++) { + let object = this._objects[objectPaths]; + + let interfaceNames = Object.keys(object); + for (let j = 0; i < interfaceNames.length; i++) { + let interfaceName = interfaceNames[i]; + if (object[interfaceName]) + proxies.push(object(interfaceName)); + } + } + + return proxies; + } +}); +Signals.addSignalMethods(ObjectManager.prototype);