summaryrefslogtreecommitdiff
path: root/src/CommonAPI/DBus/DBusServiceRegistry.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/CommonAPI/DBus/DBusServiceRegistry.cpp')
-rw-r--r--src/CommonAPI/DBus/DBusServiceRegistry.cpp1367
1 files changed, 957 insertions, 410 deletions
diff --git a/src/CommonAPI/DBus/DBusServiceRegistry.cpp b/src/CommonAPI/DBus/DBusServiceRegistry.cpp
index 13c28ab..91eaba1 100644
--- a/src/CommonAPI/DBus/DBusServiceRegistry.cpp
+++ b/src/CommonAPI/DBus/DBusServiceRegistry.cpp
@@ -8,397 +8,890 @@
#include "DBusServiceRegistry.h"
#include "DBusDaemonProxy.h"
#include "DBusProxyAsyncCallbackHandler.h"
+#include "DBusUtils.h"
+
+#include <iostream>
+#include <iterator>
namespace CommonAPI {
namespace DBus {
-DBusServiceRegistry::DBusServiceRegistry(std::shared_ptr<DBusProxyConnection> dbusProxyConnection):
+DBusServiceRegistry::DBusServiceRegistry(std::shared_ptr<DBusProxyConnection> dbusProxyConnection) :
dbusDaemonProxy_(std::make_shared<CommonAPI::DBus::DBusDaemonProxy>(dbusProxyConnection)),
- dbusNameListStatus_(AvailabilityStatus::UNKNOWN),
- initialized_(false), notificationThread_() {
+ initialized_(false),
+ notificationThread_() {
}
DBusServiceRegistry::~DBusServiceRegistry() {
- if(initialized_) {
- dbusDaemonProxy_->getNameOwnerChangedEvent().unsubscribe(dbusDaemonProxyNameOwnerChangedEventSubscription_);
- dbusDaemonProxy_->getProxyStatusEvent().unsubscribe(dbusDaemonProxyStatusEventSubscription_);
- }
+ if (!initialized_) {
+ return;
+ }
+
+ dbusDaemonProxy_->getNameOwnerChangedEvent().unsubscribe(dbusDaemonProxyNameOwnerChangedEventSubscription_);
+ dbusDaemonProxy_->getProxyStatusEvent().unsubscribe(dbusDaemonProxyStatusEventSubscription_);
+
+ // notify only listeners of resolved services (online > offline)
+ for (auto& dbusServiceListenersIterator : dbusServiceListenersMap) {
+ auto& dbusServiceListenersRecord = dbusServiceListenersIterator.second;
+
+ // fulfill all open promises
+ std::promise<DBusRecordState> promiseOnResolve = std::move(dbusServiceListenersRecord.promiseOnResolve);
+ promiseOnResolve.set_value(DBusRecordState::NOT_AVAILABLE);
+
+ if (dbusServiceListenersRecord.uniqueBusNameState == DBusRecordState::RESOLVED) {
+ onDBusServiceNotAvailable(dbusServiceListenersRecord);
+ }
+ }
+
+ // remove all object manager signal member handlers
+ for (auto& dbusServiceUniqueNameIterator : dbusUniqueNamesMap_) {
+ const auto& dbusServiceUniqueName = dbusServiceUniqueNameIterator.first;
+
+ auto dbusProxyConnection = dbusDaemonProxy_->getDBusConnection();
+ const bool isSubscriptionCancelled = dbusProxyConnection->removeObjectManagerSignalMemberHandler(
+ dbusServiceUniqueName,
+ this);
+ assert(isSubscriptionCancelled);
+ }
}
void DBusServiceRegistry::init() {
- dbusDaemonProxyStatusEventSubscription_ =
- dbusDaemonProxy_->getProxyStatusEvent().subscribeCancellableListener(
- std::bind(&DBusServiceRegistry::onDBusDaemonProxyStatusEvent, this, std::placeholders::_1));
+ dbusDaemonProxyStatusEventSubscription_ =
+ dbusDaemonProxy_->getProxyStatusEvent().subscribeCancellableListener(
+ std::bind(&DBusServiceRegistry::onDBusDaemonProxyStatusEvent, shared_from_this(), std::placeholders::_1));
+
+ dbusDaemonProxyNameOwnerChangedEventSubscription_ =
+ dbusDaemonProxy_->getNameOwnerChangedEvent().subscribeCancellableListener(
+ std::bind(&DBusServiceRegistry::onDBusDaemonProxyNameOwnerChangedEvent,
+ this,
+ std::placeholders::_1,
+ std::placeholders::_2,
+ std::placeholders::_3));
- dbusDaemonProxyNameOwnerChangedEventSubscription_ =
- dbusDaemonProxy_->getNameOwnerChangedEvent().subscribeCancellableListener(
- std::bind(&DBusServiceRegistry::onDBusDaemonProxyNameOwnerChangedEvent,
- this,
- std::placeholders::_1,
- std::placeholders::_2,
- std::placeholders::_3));
- initialized_ = true;
+ fetchAllServiceNames(); // initialize list of registered bus names
+
+ initialized_ = true;
}
-bool DBusServiceRegistry::waitDBusServicesAvailable(std::unique_lock<std::mutex>& lock, std::chrono::milliseconds& timeout) {
- bool dbusServicesStatusIsKnown = (dbusNameListStatus_ == AvailabilityStatus::AVAILABLE);
+DBusServiceRegistry::DBusServiceSubscription DBusServiceRegistry::subscribeAvailabilityListener(const std::string& commonApiAddress,
+ DBusServiceListener serviceListener) {
+ std::string dbusInterfaceName;
+ std::string dbusServiceName;
+ std::string dbusObjectPath;
- if(!dbusServicesStatusIsKnown) {
- typedef std::chrono::high_resolution_clock clock;
- clock::time_point startTimePoint = clock::now();
+ DBusAddressTranslator::getInstance().searchForDBusAddress(
+ commonApiAddress,
+ dbusInterfaceName,
+ dbusServiceName,
+ dbusObjectPath);
- while (!dbusServicesStatusIsKnown && timeout.count() > 0) {
- dbusServicesStatusIsKnown = dbusServiceChanged_.wait_for(
- lock,
- timeout / 10,
- [&]{ return dbusNameListStatus_ == AvailabilityStatus::AVAILABLE; });
+ if (notificationThread_ == std::this_thread::get_id()) {
+ std::cerr << "ERROR: You must not build proxies in callbacks of ProxyStatusEvent."
+ << " Refer to the documentation for suggestions how to avoid this.\n";
+ assert(false);
+ }
- std::chrono::milliseconds elapsedWaitTime =
- std::chrono::duration_cast<std::chrono::milliseconds>(clock::now() - startTimePoint);
+ std::lock_guard<std::mutex> dbusServicesLock(dbusServicesMutex_);
+ auto& dbusServiceListenersRecord = dbusServiceListenersMap[dbusServiceName];
+ assert(dbusServiceListenersRecord.uniqueBusNameState != DBusRecordState::AVAILABLE);
+
+ auto& dbusInterfaceNameListenersMap = dbusServiceListenersRecord.dbusObjectPathListenersMap[dbusObjectPath];
+ auto& dbusInterfaceNameListenersRecord = dbusInterfaceNameListenersMap[dbusInterfaceName];
+
+ AvailabilityStatus availabilityStatus = AvailabilityStatus::UNKNOWN;
+
+ if (dbusServiceListenersRecord.uniqueBusNameState == DBusRecordState::UNKNOWN) {
+ dbusInterfaceNameListenersRecord.state = DBusRecordState::UNKNOWN;
+ if (dbusServiceListenersRecord.uniqueBusNameState == DBusRecordState::UNKNOWN) {
+ resolveDBusServiceName(dbusServiceName, dbusServiceListenersRecord);
+ }
+ } else if (dbusServiceListenersRecord.uniqueBusNameState == DBusRecordState::NOT_AVAILABLE) {
+ availabilityStatus = AvailabilityStatus::NOT_AVAILABLE;
+ } else if (dbusServiceListenersRecord.uniqueBusNameState != DBusRecordState::RESOLVING && dbusInterfaceNameListenersRecord.state == DBusRecordState::UNKNOWN) {
+ dbusInterfaceNameListenersRecord.state = resolveDBusInterfaceNameState(
+ dbusInterfaceName,
+ dbusObjectPath,
+ dbusServiceName,
+ dbusServiceListenersRecord);
+ }
- if (elapsedWaitTime > timeout) {
+ if(availabilityStatus == AvailabilityStatus::UNKNOWN) {
+ switch (dbusInterfaceNameListenersRecord.state) {
+ case DBusRecordState::AVAILABLE:
+ availabilityStatus = AvailabilityStatus::AVAILABLE;
+ break;
+ case DBusRecordState::NOT_AVAILABLE:
+ availabilityStatus = AvailabilityStatus::NOT_AVAILABLE;
break;
+ default:
+ availabilityStatus = AvailabilityStatus::UNKNOWN;
+ }
+ }
+
+
+ if (availabilityStatus != AvailabilityStatus::UNKNOWN) {
+ notificationThread_ = std::this_thread::get_id();
+ SubscriptionStatus subscriptionStatus = serviceListener(availabilityStatus);
+ notificationThread_ = std::thread::id();
+
+ if (subscriptionStatus == SubscriptionStatus::CANCEL) {
+ if (dbusInterfaceNameListenersRecord.listenerList.empty()) {
+ dbusInterfaceNameListenersMap.erase(dbusInterfaceName);
+ if (dbusInterfaceNameListenersMap.empty()) {
+ dbusServiceListenersRecord.dbusObjectPathListenersMap.erase(dbusObjectPath);
+ }
}
+
+ return DBusServiceSubscription();
}
}
- return (dbusNameListStatus_ == AvailabilityStatus::AVAILABLE);
+ dbusInterfaceNameListenersRecord.listenerList.push_front(std::move(serviceListener));
+
+ return dbusInterfaceNameListenersRecord.listenerList.begin();
}
-bool DBusServiceRegistry::isServiceInstanceAlive(const std::string& dbusInterfaceName, const std::string& dbusServiceName, const std::string& dbusObjectPath) {
- if (!dbusDaemonProxy_->isAvailable()) {
- return false;
+void DBusServiceRegistry::unsubscribeAvailabilityListener(const std::string& commonApiAddress,
+ DBusServiceSubscription& listenerSubscription) {
+ std::string dbusInterfaceName;
+ std::string dbusServiceName;
+ std::string dbusObjectPath;
+
+ DBusAddressTranslator::getInstance().searchForDBusAddress(
+ commonApiAddress,
+ dbusInterfaceName,
+ dbusServiceName,
+ dbusObjectPath);
+
+ std::lock_guard<std::mutex> dbusServicesLock(dbusServicesMutex_);
+ auto dbusServiceListenersIterator = dbusServiceListenersMap.find(dbusServiceName);
+ const bool isDBusServiceListenersRecordFound = (dbusServiceListenersIterator != dbusServiceListenersMap.end());
+
+ if (!isDBusServiceListenersRecordFound) {
+ return; // already unsubscribed
}
- std::chrono::milliseconds timeout(1000);
- std::unique_lock<std::mutex> dbusServicesLock(dbusServicesMutex_);
+ auto& dbusServiceListenersRecord = dbusServiceListenersIterator->second;
+ auto dbusObjectPathListenersIterator =
+ dbusServiceListenersRecord.dbusObjectPathListenersMap.find(dbusObjectPath);
+ const bool isDBusObjectPathListenersRecordFound =
+ (dbusObjectPathListenersIterator != dbusServiceListenersRecord.dbusObjectPathListenersMap.end());
- if (!waitDBusServicesAvailable(dbusServicesLock, timeout)) {
- return false;
+ if (!isDBusObjectPathListenersRecordFound) {
+ return; // already unsubscribed
}
- auto dbusServiceIterator = dbusServices_.find(dbusServiceName);
- if (dbusServiceIterator == dbusServices_.end()) {
- return false;
+ auto& dbusInterfaceNameListenersMap = dbusObjectPathListenersIterator->second;
+ auto dbusInterfaceNameListenersIterator = dbusInterfaceNameListenersMap.find(dbusInterfaceName);
+ const bool isDBusInterfaceNameListenersRecordFound =
+ (dbusInterfaceNameListenersIterator != dbusInterfaceNameListenersMap.end());
+
+ if (!isDBusInterfaceNameListenersRecordFound) {
+ return; // already unsubscribed
}
- DBusServiceState& dbusServiceState = dbusServiceIterator->second.first;
+ auto& dbusInterfaceNameListenersRecord = dbusInterfaceNameListenersIterator->second;
+
+ dbusInterfaceNameListenersRecord.listenerList.erase(listenerSubscription);
+
+ if (dbusInterfaceNameListenersRecord.listenerList.empty()) {
+ dbusInterfaceNameListenersMap.erase(dbusInterfaceNameListenersIterator);
- if (dbusServiceState == DBusServiceState::AVAILABLE) {
- resolveDBusServiceInstances(dbusServiceIterator);
+ if (dbusInterfaceNameListenersMap.empty()) {
+ dbusServiceListenersRecord.dbusObjectPathListenersMap.erase(dbusObjectPathListenersIterator);
+ }
}
+}
+
+// d-feet mode until service is found
+bool DBusServiceRegistry::isServiceInstanceAlive(const std::string& dbusInterfaceName,
+ const std::string& dbusServiceName,
+ const std::string& dbusObjectPath) {
+ std::chrono::milliseconds timeout(1000);
+ bool uniqueNameFound = false;
+
+ DBusUniqueNameRecord* dbusUniqueNameRecord = NULL;
+ std::string uniqueName;
- if (dbusServiceState == DBusServiceState::RESOLVING) {
- dbusServiceChanged_.wait_for(
- dbusServicesLock,
- timeout,
- [&] { return dbusServiceState != DBusServiceState::RESOLVING; });
+ dbusServicesMutex_.lock();
+
+ findCachedDbusService(dbusServiceName, &dbusUniqueNameRecord);
+
+ if(dbusUniqueNameRecord != NULL) {
+ uniqueName = dbusUniqueNameRecord->uniqueName;
+ uniqueNameFound = true;
}
- const DBusInstanceList& dbusInstanceList = dbusServiceIterator->second.second;
- auto dbusInstanceIterator = dbusInstanceList.find({ dbusObjectPath, dbusInterfaceName });
+ if(!uniqueNameFound) {
+ DBusServiceListenersRecord dbusServiceListenersRecord;
+ dbusServiceListenersRecord.uniqueBusNameState = DBusRecordState::RESOLVING;
+
+ dbusServiceListenersRecord.futureOnResolve = dbusServiceListenersRecord.promiseOnResolve.get_future();
+ std::unordered_map<std::string, DBusServiceListenersRecord>::value_type value(dbusServiceName, std::move(dbusServiceListenersRecord));
+ auto insertedDbusServiceListenerRecord = dbusServiceListenersMap.insert(std::move(value));
+
+ if(insertedDbusServiceListenerRecord.second) { // if dbusServiceListenerRecord was inserted, start resolving
+ resolveDBusServiceName(dbusServiceName, dbusServiceListenersMap[dbusServiceName]);
+ }
+
+ dbusServicesMutex_.unlock();
+
+ std::shared_future<DBusRecordState> futureNameResolved = insertedDbusServiceListenerRecord.first->second.futureOnResolve;
+ futureNameResolved.wait_for(timeout);
- if (dbusInstanceIterator != dbusInstanceList.end()) {
- const AvailabilityStatus& dbusInstanceAvailabilityStatus = dbusInstanceIterator->second.first;
+ if(futureNameResolved.get() != DBusRecordState::RESOLVED) {
+ return false;
+ }
- return (dbusInstanceAvailabilityStatus == AvailabilityStatus::AVAILABLE);
+ dbusServicesMutex_.lock();
+ auto dbusServiceListenersMapIterator = dbusServiceListenersMap.find(dbusServiceName);
+
+ if(dbusServiceListenersMapIterator == dbusServiceListenersMap.end()) {
+ dbusServicesMutex_.unlock();
+ return false;
+ }
+
+ uniqueName = dbusServiceListenersMapIterator->second.uniqueBusName;
+
+ if(uniqueName.empty() || dbusServiceListenersMapIterator->second.uniqueBusNameState != DBusRecordState::RESOLVED) {
+ dbusServicesMutex_.unlock();
+ return false;
+ }
+
+ auto dbusUniqueNameRecordIterator = dbusUniqueNamesMap_.find(uniqueName);
+
+ if(dbusUniqueNameRecordIterator == dbusUniqueNamesMap_.end()) {
+ dbusServicesMutex_.unlock();
+ return false;
+ }
+
+ dbusUniqueNameRecord = &dbusUniqueNameRecordIterator->second;
}
- return false;
+ dbusServicesMutex_.unlock();
+
+ assert(dbusUniqueNameRecord != NULL);
+
+ auto* dbusObjectPathsCache = &(dbusUniqueNameRecord->dbusObjectPathsCache);
+ auto dbusObjectPathCacheIterator = dbusObjectPathsCache->find(dbusObjectPath);
+
+ DBusObjectPathCache* dbusObjectPathCache = NULL;
+
+
+ if(dbusObjectPathCacheIterator != dbusObjectPathsCache->end()) {
+ dbusObjectPathCache = &(dbusObjectPathCacheIterator->second);
+ }
+ else {
+ // try to resolve object paths
+ DBusObjectPathCache newDbusObjectPathCache;
+ newDbusObjectPathCache.state = DBusRecordState::RESOLVING;
+
+ dbusServicesMutex_.lock();
+ //std::unordered_map<std::string, DBusObjectPathCache>::value_type value(dbusObjectPath, std::move(newDbusObjectPathCache));
+ //auto dbusObjectPathCacheInserted = dbusObjectPathsCache->insert(std::move({dbusObjectPath, std::move(newDbusObjectPathCache)}));
+ auto dbusObjectPathCacheInserted =
+ dbusObjectPathsCache->insert(std::make_pair(dbusObjectPath, std::move(newDbusObjectPathCache)));
+
+ dbusObjectPathCacheIterator = dbusObjectPathsCache->find(dbusObjectPath);
+
+ dbusObjectPathCache = &(dbusObjectPathCacheIterator->second);
+
+ std::future<DBusRecordState> futureObjectPathResolved = dbusObjectPathCache->promiseOnResolve.get_future();
+ dbusServicesMutex_.unlock();
+
+ introspectDBusObjectPath(uniqueName, dbusObjectPath);
+ futureObjectPathResolved.wait_for(timeout);
+ }
+
+ assert(dbusObjectPathCache != NULL);
+
+ dbusServicesMutex_.lock();
+ if(dbusObjectPathCache->state != DBusRecordState::RESOLVED) {
+ dbusServicesMutex_.unlock();
+ return false;
+ }
+
+ auto dbusInterfaceNamesIterator = dbusObjectPathCache->dbusInterfaceNamesCache.find(dbusInterfaceName);
+ bool result = dbusInterfaceNamesIterator != dbusObjectPathCache->dbusInterfaceNamesCache.end();
+ dbusServicesMutex_.unlock();
+
+ return(result);
}
+void DBusServiceRegistry::fetchAllServiceNames() {
+ if (!dbusDaemonProxy_->isAvailable()) {
+ return;
+ }
-// Go through the list of available services and check their interface lists
-// If a list is still unknown, then send request to the remote object manager and count it as invalid
-// If a list is in acquiring state, then just count it as invalid and skip over it
-// Add all matching valid services to the available service list
-// If the invalid service count is set, then wait upto waitTimeLimit (2 seconds) for the object manager requests to complete
-// If the timeout expires, then go through the list for last time and add everything matching
-// If the timeout didn't expire, then go through the list again and send requests for new UNKNOWN services, then wait again for them to complete
-// Known limitations:
-// - if the method is called before the first "listNames()" call completes, this request will be blocked
-// - if a single one(!) of the addressed services is broken and doesn't respond correctly to non-handled requests, this request will always block for the default 2 seconds (waitTimeLimit)
-// - the method has to be called many times, if you actually want to wait for all services, otherwise you'll always get a partial response. I.e. the more you call this method, the hotter the internal cache gets.
+ CallStatus callStatus;
+ std::vector<std::string> availableServiceNames;
+
+ dbusDaemonProxy_->listNames(callStatus, availableServiceNames);
+
+ if(callStatus == CallStatus::SUCCESS) {
+ for(std::string serviceName : availableServiceNames) {
+ if(isDBusServiceName(serviceName)) {
+ dbusServiceNameMap_[serviceName];
+ }
+ }
+ }
+}
+
+// d-feet mode
std::vector<std::string> DBusServiceRegistry::getAvailableServiceInstances(const std::string& interfaceName,
const std::string& domainName) {
std::vector<std::string> availableServiceInstances;
- if (!dbusDaemonProxy_->isAvailable()) {
- return availableServiceInstances;
- }
+ // resolve all service names
+ for (auto serviceNameIterator = dbusServiceNameMap_.begin();
+ serviceNameIterator != dbusServiceNameMap_.end();
+ serviceNameIterator++) {
- std::chrono::milliseconds timeout(2000);
- std::unique_lock<std::mutex> dbusServicesLock(dbusServicesMutex_);
+ std::string serviceName = serviceNameIterator->first;
+ DBusUniqueNameRecord* dbusUniqueNameRecord = serviceNameIterator->second;
- if (!waitDBusServicesAvailable(dbusServicesLock, timeout)) {
- return availableServiceInstances;
+ if(dbusUniqueNameRecord == NULL) {
+ DBusServiceListenersRecord& serviceListenerRecord = dbusServiceListenersMap[serviceName];
+ if(serviceListenerRecord.uniqueBusNameState != DBusRecordState::RESOLVING) {
+ resolveDBusServiceName(serviceName, serviceListenerRecord);
+ }
+ }
}
- size_t dbusServiceResolvingCount = getResolvedServiceInstances(interfaceName, availableServiceInstances);
+ std::mutex mutexResolveAllServices;
+ std::unique_lock<std::mutex> lockResolveAllServices(mutexResolveAllServices);
+ std::chrono::milliseconds timeout(5000);
- if (!dbusServiceResolvingCount) {
- return availableServiceInstances;
- }
+ monitorResolveAllServices_.wait_for(lockResolveAllServices, timeout, [&] {
+ mutexServiceResolveCount.lock();
+ bool finished = servicesToResolve == 0;
+ mutexServiceResolveCount.unlock();
- dbusServiceChanged_.wait(
- dbusServicesLock,
- [&] {
- return getNumResolvingServiceInstances() == 0;
- });
+ return finished;
+ });
- getResolvedServiceInstances(interfaceName, availableServiceInstances);
+ for (auto serviceNameIterator = dbusServiceNameMap_.begin();
+ serviceNameIterator != dbusServiceNameMap_.end();
+ serviceNameIterator++) {
+ std::string serviceName = serviceNameIterator->first;
+ DBusUniqueNameRecord* dbusUniqueNameRecord = serviceNameIterator->second;
+
+ if(dbusUniqueNameRecord != NULL) {
+ if(dbusUniqueNameRecord->objectPathsState == DBusRecordState::UNKNOWN) {
+ DBusObjectPathCache& rootObjectPathCache = dbusUniqueNameRecord->dbusObjectPathsCache["/"];
+ if(rootObjectPathCache.state == DBusRecordState::UNKNOWN) {
+ rootObjectPathCache.state = DBusRecordState::RESOLVING;
+ introspectDBusObjectPath(dbusUniqueNameRecord->uniqueName, "/");
+ }
+ }
+ }
+ }
+
+ std::mutex mutexResolveAllObjectPaths;
+ std::unique_lock<std::mutex> lockResolveAllObjectPaths(mutexResolveAllObjectPaths);
+
+ // TODO: should use the remaining timeout not "used" during wait before
+ monitorResolveAllObjectPaths_.wait_for(lockResolveAllObjectPaths, timeout, [&] {
+ mutexServiceResolveCount.lock();
+ bool finished = objectPathsToResolve == 0;
+ mutexServiceResolveCount.unlock();
+
+ return finished;
+ });
+
+ DBusAddressTranslator& dbusAddressTranslator = DBusAddressTranslator::getInstance();
+
+ for (auto serviceNameIterator = dbusServiceNameMap_.begin();
+ serviceNameIterator != dbusServiceNameMap_.end();
+ serviceNameIterator++) {
+
+ std::string serviceName = serviceNameIterator->first;
+ DBusUniqueNameRecord* dbusUniqueNameRecord = serviceNameIterator->second;
+
+ if(dbusUniqueNameRecord != NULL) {
+ if(dbusUniqueNameRecord->objectPathsState == DBusRecordState::RESOLVED) {
+ for (auto dbusObjectPathCacheIterator = dbusUniqueNameRecord->dbusObjectPathsCache.begin();
+ dbusObjectPathCacheIterator != dbusUniqueNameRecord->dbusObjectPathsCache.end();
+ dbusObjectPathCacheIterator++) {
+ if (dbusObjectPathCacheIterator->second.state == DBusRecordState::RESOLVED) {
+ if (dbusObjectPathCacheIterator->second.dbusInterfaceNamesCache.find(interfaceName)
+ != dbusObjectPathCacheIterator->second.dbusInterfaceNamesCache.end()) {
+ std::string commonApiAddress;
+ dbusAddressTranslator.searchForCommonAddress(interfaceName, serviceName, dbusObjectPathCacheIterator->first, commonApiAddress);
+ availableServiceInstances.push_back(commonApiAddress);
+ }
+ }
+ }
+ }
+ }
+ }
// maybe partial list but it contains everything we know for now
return availableServiceInstances;
}
+//std::vector<std::string> DBusServiceRegistry::getManagedObjects(const std::string& connectionName, const std::string& objectpath) {
+// if (auto iter = dbusServiceNameMap_.find(connectionName) != dbusServiceNameMap_.end()) {
+// DBusUniqueNameRecord* rec = iter->second;
+// if (rec->uniqueName != DBusRecordState::RESOLVED) {
+// return std::vector<std::string>();
+// } else {
+// rec->dbusObjectPathsCache
+// }
+//
+// } else {
+// return std::vector<std::string>();
+// }
+//}
void DBusServiceRegistry::getAvailableServiceInstancesAsync(Factory::GetAvailableServiceInstancesCallback callback,
const std::string& interfaceName,
const std::string& domainName) {
- std::vector<std::string> availableServiceInstances;
+ //Necessary as service discovery might need some time, but the async version of "getAvailableServiceInstances"
+ //shall return without delay.
+ std::thread(
+ [this, callback, interfaceName, domainName](std::shared_ptr<DBusServiceRegistry> selfRef) {
+ auto availableServiceInstances = getAvailableServiceInstances(interfaceName, domainName);
+ callback(availableServiceInstances);
+ }, this->shared_from_this()
+ ).detach();
+}
- if (!dbusDaemonProxy_->isAvailable()) {
- callback(availableServiceInstances);
+SubscriptionStatus DBusServiceRegistry::onSignalDBusMessage(const DBusMessage& dbusMessage) {
+ const std::string& dbusServiceUniqueName = dbusMessage.getSenderName();
+
+ assert(dbusMessage.isSignalType());
+ assert(dbusMessage.hasInterfaceName("org.freedesktop.DBus.ObjectManager"));
+ assert(dbusMessage.hasMemberName("InterfacesAdded") || dbusMessage.hasMemberName("InterfacesRemoved"));
+
+ DBusInputStream dbusInputStream(dbusMessage);
+ std::string dbusObjectPath;
+ std::unordered_set<std::string> dbusInterfaceNames;
+ DBusRecordState dbusInterfaceNameState;
+
+ dbusInputStream >> dbusObjectPath;
+
+ bool added = false;
+
+ if (dbusMessage.hasMemberName("InterfacesAdded")) {
+ added = true;
+ dbusInterfaceNameState = DBusRecordState::AVAILABLE;
+
+ typedef std::unordered_map<std::string, bool> DBusPropertiesChangedDict;
+ typedef std::unordered_map<std::string, DBusPropertiesChangedDict> DBusInterfacesAndPropertiesDict;
+ typedef std::unordered_map<std::string, DBusInterfacesAndPropertiesDict> DBusObjectPathAndInterfacesDict;
+ DBusObjectPathAndInterfacesDict dbusObjectPathAndInterfacesDict;
+ dbusInputStream >> dbusObjectPathAndInterfacesDict;
+
+ for (auto& dbusInterfaceIterator : dbusObjectPathAndInterfacesDict) {
+ const auto& dbusInterfaceName = dbusInterfaceIterator.first;
+ dbusInterfaceNames.insert(dbusInterfaceName);
+ }
+ } else {
+ std::vector<std::string> removedDBusInterfaceNames;
+
+ dbusInterfaceNameState = DBusRecordState::NOT_AVAILABLE;
+ dbusInputStream >> removedDBusInterfaceNames;
+ std::move(
+ removedDBusInterfaceNames.begin(),
+ removedDBusInterfaceNames.end(),
+ std::inserter(dbusInterfaceNames, dbusInterfaceNames.begin()));
+ }
+
+ if (dbusInputStream.hasError()) {
+ return SubscriptionStatus::RETAIN;
+ }
+
+ if (dbusInterfaceNames.empty()) {
+ return SubscriptionStatus::RETAIN;
}
std::lock_guard<std::mutex> dbusServicesLock(dbusServicesMutex_);
- size_t stillResolvingCount = getResolvedServiceInstances(interfaceName, availableServiceInstances);
+ auto dbusServiceUniqueNameIterator = dbusUniqueNamesMap_.find(dbusServiceUniqueName);
+ const bool isDBusServiceUniqueNameFound = (dbusServiceUniqueNameIterator != dbusUniqueNamesMap_.end());
- if (stillResolvingCount == 0 && !dbusServices_.empty()) {
- callback(availableServiceInstances);
- } else {
- //Necessary as service discovery might need some time, but the async version of "getAvailableServiceInstances"
- //shall return without delay.
- std::thread(
- [this, callback, interfaceName, domainName](std::shared_ptr<DBusServiceRegistry> selfRef) {
- auto availableServiceInstances = getAvailableServiceInstances(interfaceName, domainName);
- callback(availableServiceInstances);
- }, this->shared_from_this()
- ).detach();
+ if (!isDBusServiceUniqueNameFound) {
+ return SubscriptionStatus::CANCEL;
}
-}
+ auto& dbusUniqueNameRecord = dbusServiceUniqueNameIterator->second;
-size_t DBusServiceRegistry::getNumResolvingServiceInstances() {
- size_t dbusServicesResolvingCount = 0;
+ //auto dbusObjectPathIterator = dbusUniqueNameRecord.dbusObjectPathsCache.find(dbusObjectPath);
+ //const bool isDBusObjectPathFound = (dbusObjectPathIterator != dbusUniqueNameRecord.dbusObjectPathsCache.end());
- // caller must hold lock
- auto dbusServiceIterator = dbusServices_.begin();
- while (dbusServiceIterator != dbusServices_.end()) {
- DBusServiceState& dbusServiceState = dbusServiceIterator->second.first;
+ /*
+ if (!isDBusObjectPathFound) {
+ return SubscriptionStatus::RETAIN;
+ }
+ */
- switch (dbusServiceState) {
- case DBusServiceState::AVAILABLE:
- dbusServicesResolvingCount++;
- break;
+ DBusObjectPathCache& dbusObjectPathRecord = dbusUniqueNameRecord.dbusObjectPathsCache[dbusObjectPath];
+/*
+ if (isDBusObjectPathFound) {
+ dbusObjectPathRecord = &dbusObjectPathIterator->second;
+ }
+ else
+ {
+ DBusObjectPathCache dbusObjectPathRecord;
+ auto insertionResult = dbusUniqueNameRecord.dbusObjectPathsCache.insert(std::make_pair(dbusObjectPath, std::move(dbusObjectPath)));
+ auto objectPathCacheIterator = insertionResult.first;
+ dbusObjectPathRecord = &(objectPathCacheIterator->second);
+ }
+*/
- case DBusServiceState::RESOLVING:
- dbusServicesResolvingCount++;
- break;
+ if (dbusObjectPathRecord.state != DBusRecordState::RESOLVED) {
+ return SubscriptionStatus::RETAIN;
+ }
- default:
- break;
+ for (const auto& dbusInterfaceName : dbusInterfaceNames) {
+ if (dbusInterfaceNameState == DBusRecordState::AVAILABLE) {
+ dbusObjectPathRecord.dbusInterfaceNamesCache.insert(dbusInterfaceName);
+ } else {
+ dbusObjectPathRecord.dbusInterfaceNamesCache.erase(dbusInterfaceName);
}
-
- dbusServiceIterator++;
}
- return dbusServicesResolvingCount;
+ notifyDBusServiceListeners(dbusUniqueNameRecord, dbusObjectPath, dbusInterfaceNames, dbusInterfaceNameState);
+
+ return SubscriptionStatus::RETAIN;
}
-size_t DBusServiceRegistry::getResolvedServiceInstances(const std::string& dbusInterfaceName, std::vector<std::string>& availableServiceInstances) {
- size_t dbusServicesResolvingCount = 0;
+void DBusServiceRegistry::resolveDBusServiceName(const std::string& dbusServiceName,
+ DBusServiceListenersRecord& dbusServiceListenersRecord) {
+ assert(dbusServiceListenersRecord.uniqueBusNameState != DBusRecordState::RESOLVED);
+ assert(dbusServiceListenersRecord.uniqueBusName.empty());
- availableServiceInstances.clear();
+ mutexServiceResolveCount.lock();
+ servicesToResolve++;
+ mutexServiceResolveCount.unlock();
- // caller must hold lock
- auto dbusServiceIterator = dbusServices_.begin();
- while (dbusServiceIterator != dbusServices_.end()) {
- const std::string& dbusServiceName = dbusServiceIterator->first;
- DBusServiceState& dbusServiceState = dbusServiceIterator->second.first;
- const DBusInstanceList& dbusInstanceList = dbusServiceIterator->second.second;
+ if (dbusDaemonProxy_->isAvailable()) {
+ dbusDaemonProxy_->getNameOwnerAsync(
+ dbusServiceName,
+ std::bind(
+ &DBusServiceRegistry::onGetNameOwnerCallback,
+ this->shared_from_this(),
+ std::placeholders::_1,
+ std::placeholders::_2,
+ dbusServiceName));
- // count the resolving services and start acquiring the objects for unknown ones
- switch (dbusServiceState) {
- case DBusServiceState::AVAILABLE:
- resolveDBusServiceInstances(dbusServiceIterator);
- dbusServicesResolvingCount++;
- break;
+ dbusServiceListenersRecord.uniqueBusNameState = DBusRecordState::RESOLVING;
+ }
+}
- case DBusServiceState::RESOLVING:
- case DBusServiceState::RESOLVED:
- if (dbusServiceState == DBusServiceState::RESOLVING) {
- dbusServicesResolvingCount++;
- }
+void DBusServiceRegistry::onGetNameOwnerCallback(const CallStatus& status,
+ std::string dbusServiceUniqueName,
+ const std::string& dbusServiceName) {
+ std::lock_guard<std::mutex> dbusServicesLock(dbusServicesMutex_);
- for (auto& dbusInstanceIterator : dbusInstanceList) {
- const AvailabilityStatus& dbusInstanceAvailabilityStatus = dbusInstanceIterator.second.first;
- const std::string& dbusInstanceObjectPath = dbusInstanceIterator.first.first;
- const std::string& dbusInstanceInterfaceName = dbusInstanceIterator.first.second;
+ auto dbusServiceListenerIterator = dbusServiceListenersMap.find(dbusServiceName);
+ const bool isDBusServiceListenerRecordFound = (dbusServiceListenerIterator != dbusServiceListenersMap.end());
- if (dbusInstanceAvailabilityStatus == AvailabilityStatus::AVAILABLE
- && dbusInstanceInterfaceName == dbusInterfaceName) {
- std::string commonApiAddress;
+ if (!isDBusServiceListenerRecordFound) {
+ return;
+ }
- DBusAddressTranslator::getInstance().searchForCommonAddress(
- dbusInterfaceName,
- dbusServiceName,
- dbusInstanceObjectPath,
- commonApiAddress);
+ DBusServiceListenersRecord& dbusServiceListenersRecord = dbusServiceListenerIterator->second;
- availableServiceInstances.emplace_back(std::move(commonApiAddress));
- }
- }
- break;
+ if (status == CallStatus::SUCCESS) {
+ onDBusServiceAvailable(dbusServiceName, dbusServiceUniqueName);
+ if(dbusServiceListenersRecord.futureOnResolve.valid()) {
+ dbusServiceListenersRecord.promiseOnResolve.set_value(DBusRecordState(dbusServiceListenersRecord.uniqueBusNameState));
+ }
+ } else {
+ // try to fulfill open promises
+ if(dbusServiceListenersRecord.futureOnResolve.valid()) {
+ dbusServiceListenersRecord.promiseOnResolve.set_value(DBusRecordState::NOT_AVAILABLE);
}
- dbusServiceIterator++;
+ onDBusServiceNotAvailable(dbusServiceListenersRecord);
}
- return dbusServicesResolvingCount;
+ mutexServiceResolveCount.lock();
+ servicesToResolve--;
+ mutexServiceResolveCount.unlock();
+ monitorResolveAllServices_.notify_all();
}
+DBusServiceRegistry::DBusRecordState DBusServiceRegistry::resolveDBusInterfaceNameState(const std::string& dbusInterfaceName,
+ const std::string& dbusObjectPath,
+ const std::string& dbusServiceName,
+ DBusServiceListenersRecord& dbusServiceListenersRecord) {
+ assert(dbusServiceListenersRecord.uniqueBusNameState == DBusRecordState::RESOLVED);
+ assert(!dbusServiceListenersRecord.uniqueBusName.empty());
-DBusServiceRegistry::Subscription DBusServiceRegistry::subscribeAvailabilityListener(const std::string& commonApiAddress,
- DBusServiceListener serviceListener) {
- std::string dbusInterfaceName;
- std::string dbusServiceName;
- std::string dbusObjectPath;
+ auto& dbusServiceUniqueNameRecord = dbusUniqueNamesMap_[dbusServiceListenersRecord.uniqueBusName];
+ assert(!dbusServiceUniqueNameRecord.ownedBusNames.empty());
- DBusAddressTranslator::getInstance().searchForDBusAddress(commonApiAddress, dbusInterfaceName, dbusServiceName, dbusObjectPath);
+ auto& dbusObjectPathRecord = getDBusObjectPathCacheReference(
+ dbusObjectPath,
+ dbusServiceListenersRecord.uniqueBusName,
+ dbusServiceUniqueNameRecord);
- if (notificationThread_ == std::this_thread::get_id()) {
- printf("You must not build proxies in callbacks of ProxyStatusEvent.\n"
- "Refer to the documentation for suggestions how to avoid this.\n");
- assert(false);
+ if (dbusObjectPathRecord.state != DBusRecordState::RESOLVED) {
+ return dbusObjectPathRecord.state;
}
- std::lock_guard<std::mutex> dbusServicesLock(dbusServicesMutex_);
- notificationThread_ = std::this_thread::get_id();
- DBusServiceList::iterator dbusServiceIterator = dbusServices_.find(dbusServiceName);
- // Service not known, so just add it to the list of unkown or definitely not available services
- if (dbusServiceIterator == dbusServices_.end()) {
- DBusServiceState dbusConnectionNameState = DBusServiceState::UNKNOWN;
+ auto dbusInterfaceNameIterator = dbusObjectPathRecord.dbusInterfaceNamesCache.find(dbusInterfaceName);
+ const bool isDBusInterfaceNameFound =
+ (dbusInterfaceNameIterator != dbusObjectPathRecord.dbusInterfaceNamesCache.end());
- // Service is definitely not available if the complete list of available services is known and it is not in there
- if (dbusNameListStatus_ == AvailabilityStatus::AVAILABLE) {
- dbusConnectionNameState = DBusServiceState::RESOLVED;
- }
+ return isDBusInterfaceNameFound ? DBusRecordState::AVAILABLE : DBusRecordState::NOT_AVAILABLE;
+}
+
+
+DBusServiceRegistry::DBusObjectPathCache& DBusServiceRegistry::getDBusObjectPathCacheReference(const std::string& dbusObjectPath,
+ const std::string& dbusServiceUniqueName,
+ DBusUniqueNameRecord& dbusUniqueNameRecord) {
+ const bool isFirstDBusObjectPathCache = dbusUniqueNameRecord.dbusObjectPathsCache.empty();
- std::pair<DBusServiceList::iterator, bool> insertResult = dbusServices_.insert({ dbusServiceName, { dbusConnectionNameState, DBusInstanceList() } });
- assert(insertResult.second);
- dbusServiceIterator = insertResult.first;
+ auto dbusObjectPathCacheIterator = dbusUniqueNameRecord.dbusObjectPathsCache.find(dbusObjectPath);
+ if(dbusObjectPathCacheIterator == dbusUniqueNameRecord.dbusObjectPathsCache.end()) {
+ std::unordered_map<std::string, DBusObjectPathCache>::value_type value (dbusObjectPath, DBusObjectPathCache());
+ dbusUniqueNameRecord.dbusObjectPathsCache.insert(std::move(value));
+ dbusObjectPathCacheIterator = dbusUniqueNameRecord.dbusObjectPathsCache.find(dbusObjectPath);
}
- DBusServiceState& dbusConnectionNameState = dbusServiceIterator->second.first;
- DBusInstanceList& dbusInstanceList = dbusServiceIterator->second.second;
+ if (isFirstDBusObjectPathCache) {
+ auto dbusProxyConnection = dbusDaemonProxy_->getDBusConnection();
+ const bool isSubscriptionSuccessful = dbusProxyConnection->addObjectManagerSignalMemberHandler(
+ dbusServiceUniqueName,
+ this);
+ assert(isSubscriptionSuccessful);
+ }
- auto dbusInstanceIterator = addDBusServiceInstance(
- dbusInstanceList,
- dbusObjectPath,
- dbusInterfaceName);
- AvailabilityStatus& dbusInstanceAvailabilityStatus = dbusInstanceIterator->second.first;
- DBusServiceListenerList& dbusServiceListenerList = dbusInstanceIterator->second.second;
+ if (dbusObjectPathCacheIterator->second.state == DBusRecordState::UNKNOWN
+ && introspectDBusObjectPath(dbusServiceUniqueName, dbusObjectPath)) {
+ dbusObjectPathCacheIterator->second.state = DBusRecordState::RESOLVING;
+ }
- if (dbusConnectionNameState == DBusServiceState::RESOLVED
- && dbusInstanceAvailabilityStatus == AvailabilityStatus::UNKNOWN) {
- dbusInstanceAvailabilityStatus = AvailabilityStatus::NOT_AVAILABLE;
+ return dbusObjectPathCacheIterator->second;
+}
+
+void DBusServiceRegistry::releaseDBusObjectPathCacheReference(const std::string& dbusObjectPath,
+ const DBusServiceListenersRecord& dbusServiceListenersRecord) {
+ if (!dbusDaemonProxy_->isAvailable()) {
+ return;
}
- Subscription listenerSubscription = dbusServiceListenerList.insert(
- dbusServiceListenerList.end(), serviceListener);
+ if (dbusServiceListenersRecord.uniqueBusNameState != DBusRecordState::RESOLVED) {
+ return;
+ }
- switch (dbusConnectionNameState) {
- case DBusServiceState::AVAILABLE:
- resolveDBusServiceInstances(dbusServiceIterator);
- break;
+ assert(!dbusServiceListenersRecord.uniqueBusName.empty());
- case DBusServiceState::RESOLVING:
- if (dbusInstanceAvailabilityStatus == AvailabilityStatus::AVAILABLE) {
- const SubscriptionStatus status = serviceListener(dbusInstanceAvailabilityStatus);
- if (status == SubscriptionStatus::CANCEL) {
- dbusServiceListenerList.erase(listenerSubscription);
- return dbusServiceListenerList.end();
- }
- }
- break;
-
- case DBusServiceState::RESOLVED:
- case DBusServiceState::NOT_AVAILABLE:
- const SubscriptionStatus status = serviceListener(dbusInstanceAvailabilityStatus);
- if (status == SubscriptionStatus::CANCEL) {
- dbusServiceListenerList.erase(listenerSubscription);
- return dbusServiceListenerList.end();
- }
- break;
+ auto& dbusUniqueNameRecord = dbusUniqueNamesMap_[dbusServiceListenersRecord.uniqueBusName];
+ assert(!dbusUniqueNameRecord.ownedBusNames.empty());
+ assert(!dbusUniqueNameRecord.dbusObjectPathsCache.empty());
+
+ auto dbusObjectPathCacheIterator = dbusUniqueNameRecord.dbusObjectPathsCache.find(dbusObjectPath);
+ const bool isDBusObjectPathCacheFound = (dbusObjectPathCacheIterator != dbusUniqueNameRecord.dbusObjectPathsCache.end());
+ assert(isDBusObjectPathCacheFound);
+
+ auto& dbusObjectPathCache = dbusObjectPathCacheIterator->second;
+ assert(dbusObjectPathCache.referenceCount > 0);
+
+ dbusObjectPathCache.referenceCount--;
+
+ if (dbusObjectPathCache.referenceCount == 0) {
+ dbusUniqueNameRecord.dbusObjectPathsCache.erase(dbusObjectPathCacheIterator);
+
+ const bool isLastDBusObjectPathCache = dbusUniqueNameRecord.dbusObjectPathsCache.empty();
+ if (isLastDBusObjectPathCache) {
+ auto dbusProxyConnection = dbusDaemonProxy_->getDBusConnection();
+ const bool isSubscriptionCancelled = dbusProxyConnection->removeObjectManagerSignalMemberHandler(
+ dbusServiceListenersRecord.uniqueBusName,
+ this);
+ assert(isSubscriptionCancelled);
+ }
}
- notificationThread_ = std::thread::id();
- return listenerSubscription;
}
-void DBusServiceRegistry::unsubscribeAvailabilityListener(const std::string& commonApiAddress,
- Subscription& listenerSubscription) {
- std::string dbusInterfaceName;
- std::string dbusServiceName;
- std::string dbusObjectPath;
+
+bool DBusServiceRegistry::introspectDBusObjectPath(const std::string& dbusServiceUniqueName,
+ const std::string& dbusObjectPath) {
+ bool isResolvingInProgress = false;
+ auto dbusConnection = dbusDaemonProxy_->getDBusConnection();
+
+ assert(!dbusServiceUniqueName.empty());
+
+ if (dbusConnection->isConnected()) {
+ mutexObjectPathsResolveCount.lock();
+ objectPathsToResolve++;
+ mutexObjectPathsResolveCount.unlock();
+
+ DBusMessage dbusMessageCall = DBusMessage::createMethodCall(
+ dbusServiceUniqueName,
+ dbusObjectPath,
+ "org.freedesktop.DBus.Introspectable",
+ "Introspect");
+ auto instrospectAsyncCallback = std::bind(
+ &DBusServiceRegistry::onIntrospectCallback,
+ this->shared_from_this(),
+ std::placeholders::_1,
+ std::placeholders::_2,
+ dbusServiceUniqueName,
+ dbusObjectPath);
+
+ dbusConnection->sendDBusMessageWithReplyAsync(
+ dbusMessageCall,
+ DBusProxyAsyncCallbackHandler<std::string>::create(instrospectAsyncCallback),
+ 2000);
+
+ isResolvingInProgress = true;
+ }
+
+ return isResolvingInProgress;
+}
+
+/**
+ * Callback for org.freedesktop.DBus.Introspectable.Introspect
+ *
+ * This is the other end of checking if a dbus object path is available.
+ * On success it'll extract all interface names from the xml data response.
+ * Special interfaces that start with org.freedesktop will be ignored.
+ *
+ * @param status
+ * @param xmlData
+ * @param dbusServiceUniqueName
+ * @param dbusObjectPath
+ */
+void DBusServiceRegistry::onIntrospectCallback(const CallStatus& callStatus,
+ std::string xmlData,
+ const std::string& dbusServiceUniqueName,
+ const std::string& dbusObjectPath) {
+ if (callStatus == CallStatus::SUCCESS) {
+ parseIntrospectionData(xmlData, dbusObjectPath, dbusServiceUniqueName);
+ }
std::lock_guard<std::mutex> dbusServicesLock(dbusServicesMutex_);
- DBusAddressTranslator::getInstance().searchForDBusAddress(commonApiAddress, dbusInterfaceName, dbusServiceName, dbusObjectPath);
- auto dbusServiceIterator = dbusServices_.find(dbusServiceName);
+ // Error CallStatus will result in empty parsedDBusInterfaceNameSet (and not available notification)
- if (dbusServiceIterator == dbusServices_.end()) {
+ auto dbusServiceUniqueNameIterator = dbusUniqueNamesMap_.find(dbusServiceUniqueName);
+ const bool isDBusServiceUniqueNameFound = (dbusServiceUniqueNameIterator != dbusUniqueNamesMap_.end());
+
+ if (!isDBusServiceUniqueNameFound) {
return;
}
- DBusServiceState& dbusServiceState = dbusServiceIterator->second.first;
- DBusInstanceList& dbusInstanceList = dbusServiceIterator->second.second;
+ auto& dbusUniqueNameRecord = dbusServiceUniqueNameIterator->second;
+ auto dbusObjectPathIterator = dbusUniqueNameRecord.dbusObjectPathsCache.find(dbusObjectPath);
+ const bool isDBusObjectPathFound = (dbusObjectPathIterator != dbusUniqueNameRecord.dbusObjectPathsCache.end());
- auto dbusInstanceIterator = dbusInstanceList.find({ dbusObjectPath, dbusInterfaceName });
- if (dbusInstanceIterator == dbusInstanceList.end()) {
+ if (!isDBusObjectPathFound) {
return;
}
- const AvailabilityStatus& dbusServiceAvailabilityStatus = dbusInstanceIterator->second.first;
- DBusServiceListenerList& dbusServiceListenerList = dbusInstanceIterator->second.second;
+ auto& dbusObjectPathRecord = dbusObjectPathIterator->second;
+
+ dbusObjectPathRecord.state = DBusRecordState::RESOLVED;
+ dbusObjectPathRecord.promiseOnResolve.set_value(dbusObjectPathRecord.state);
+ mutexObjectPathsResolveCount.lock();
+ objectPathsToResolve++;
+ mutexObjectPathsResolveCount.unlock();
+ monitorResolveAllObjectPaths_.notify_all();
+
+ dbusUniqueNameRecord.objectPathsState = DBusRecordState::RESOLVED;
+
+ notifyDBusServiceListeners(
+ dbusUniqueNameRecord,
+ dbusObjectPath,
+ dbusObjectPathRecord.dbusInterfaceNamesCache,
+ DBusRecordState::RESOLVED);
+}
+
+void DBusServiceRegistry::parseIntrospectionNode(const pugi::xml_node& node, const std::string& rootObjectPath, const std::string& fullObjectPath, const std::string& dbusServiceUniqueName) {
+ std::string nodeName;
- dbusServiceListenerList.erase(listenerSubscription);
+ for(pugi::xml_node& subNode : node.children()) {
+ nodeName = std::string(subNode.name());
- if (dbusServiceListenerList.empty() && dbusServiceAvailabilityStatus != AvailabilityStatus::AVAILABLE) {
- dbusInstanceList.erase(dbusInstanceIterator);
+ if(nodeName == "node") {
+ processIntrospectionObjectPath(subNode, rootObjectPath, dbusServiceUniqueName);
+ }
- if (dbusInstanceList.empty() && dbusServiceState == DBusServiceState::UNKNOWN) {
- dbusServices_.erase(dbusServiceIterator);
+ if(nodeName == "interface") {
+ processIntrospectionInterface(subNode, rootObjectPath, fullObjectPath, dbusServiceUniqueName);
}
}
}
-SubscriptionStatus DBusServiceRegistry::onDBusDaemonProxyStatusEvent(const AvailabilityStatus& availabilityStatus) {
- std::lock_guard<std::mutex> dbusServicesLock(dbusServicesMutex_);
+void DBusServiceRegistry::processIntrospectionObjectPath(const pugi::xml_node& node, const std::string& rootObjectPath, const std::string& dbusServiceUniqueName) {
+ std::string fullObjectPath = rootObjectPath;
- switch (availabilityStatus) {
- case AvailabilityStatus::AVAILABLE:
- dbusNameListStatus_ = AvailabilityStatus::UNKNOWN;
- dbusDaemonProxy_->listNamesAsync(std::bind(
- &DBusServiceRegistry::onListNamesCallback,
- this->shared_from_this(),
- std::placeholders::_1,
- std::placeholders::_2));
- break;
+ if(fullObjectPath.at(fullObjectPath.length()-1) != '/') {
+ fullObjectPath += "/";
+ }
- case AvailabilityStatus::NOT_AVAILABLE:
- auto dbusServiceIterator = dbusServices_.begin();
+ fullObjectPath += std::string(node.attribute("name").as_string());
- while (dbusServiceIterator != dbusServices_.end()) {
- dbusServiceIterator = onDBusServiceOffline(dbusServiceIterator, DBusServiceState::NOT_AVAILABLE);
- }
+ DBusUniqueNameRecord& dbusUniqueNameRecord = dbusUniqueNamesMap_[dbusServiceUniqueName];
+ DBusObjectPathCache& dbusObjectPathCache = dbusUniqueNameRecord.dbusObjectPathsCache[fullObjectPath];
+
+ if(dbusObjectPathCache.state == DBusRecordState::UNKNOWN) {
+ dbusObjectPathCache.state = DBusRecordState::RESOLVING;
+ introspectDBusObjectPath(dbusServiceUniqueName, fullObjectPath);
+ }
+
+ for(pugi::xml_node subNode : node.children()) {
+ parseIntrospectionNode(subNode, fullObjectPath, fullObjectPath, dbusServiceUniqueName);
+ }
+}
+
+void DBusServiceRegistry::processIntrospectionInterface(const pugi::xml_node& node, const std::string& rootObjectPath, const std::string& fullObjectPath, const std::string& dbusServiceUniqueName) {
+ std::string interfaceName = node.attribute("name").as_string();
- dbusNameListStatus_ = AvailabilityStatus::NOT_AVAILABLE;
- break;
+ DBusUniqueNameRecord& dbusUniqueNameRecord = dbusUniqueNamesMap_[dbusServiceUniqueName];
+ DBusObjectPathCache& dbusObjectPathCache = dbusUniqueNameRecord.dbusObjectPathsCache[fullObjectPath];
+
+ if(!isOrgFreedesktopDBusInterface(interfaceName)) {
+ dbusObjectPathCache.dbusInterfaceNamesCache.insert(interfaceName);
+ }
+
+ for(pugi::xml_node subNode : node.children()) {
+ parseIntrospectionNode(subNode, rootObjectPath, fullObjectPath, dbusServiceUniqueName);
+ }
+}
+
+void DBusServiceRegistry::parseIntrospectionData(const std::string& xmlData,
+ const std::string& rootObjectPath,
+ const std::string& dbusServiceUniqueName) {
+ pugi::xml_document xmlDocument;
+ pugi::xml_parse_result parsedResult = xmlDocument.load_buffer(xmlData.c_str(), xmlData.length(), pugi::parse_minimal, pugi::encoding_utf8);
+
+ if(parsedResult.status != pugi::xml_parse_status::status_ok) {
+ return;
+ }
+
+ const pugi::xml_node rootNode = xmlDocument.child("node");
+
+ dbusServicesMutex_.lock();
+
+ parseIntrospectionNode(rootNode, rootObjectPath, rootObjectPath, dbusServiceUniqueName);
+
+ DBusUniqueNameRecord& dbusUniqueNameRecord = dbusUniqueNamesMap_[dbusServiceUniqueName];
+ dbusServicesMutex_.unlock();
+}
+
+
+SubscriptionStatus DBusServiceRegistry::onDBusDaemonProxyStatusEvent(const AvailabilityStatus& availabilityStatus) {
+ assert(availabilityStatus != AvailabilityStatus::UNKNOWN);
+
+ std::lock_guard<std::mutex> dbusServicesLock(dbusServicesMutex_);
+
+ for (auto& dbusServiceListenersIterator : dbusServiceListenersMap) {
+ const auto& dbusServiceName = dbusServiceListenersIterator.first;
+ auto& dbusServiceListenersRecord = dbusServiceListenersIterator.second;
+
+ if (availabilityStatus == AvailabilityStatus::AVAILABLE) {
+ resolveDBusServiceName(dbusServiceName, dbusServiceListenersRecord);
+ } else {
+ onDBusServiceNotAvailable(dbusServiceListenersRecord);
+ }
}
return SubscriptionStatus::RETAIN;
@@ -407,240 +900,294 @@ SubscriptionStatus DBusServiceRegistry::onDBusDaemonProxyStatusEvent(const Avail
SubscriptionStatus DBusServiceRegistry::onDBusDaemonProxyNameOwnerChangedEvent(const std::string& affectedName,
const std::string& oldOwner,
const std::string& newOwner) {
- if (isDBusServiceName(affectedName)) {
- AvailabilityStatus dbusServiceAvailabilityStatus = AvailabilityStatus::AVAILABLE;
+ if (!isDBusServiceName(affectedName)) {
+ return SubscriptionStatus::RETAIN;
+ }
- if (newOwner.empty()) {
- dbusServiceAvailabilityStatus = AvailabilityStatus::NOT_AVAILABLE;
- }
+ const bool isDBusServiceNameLost = newOwner.empty();
+ const std::string& dbusServiceUniqueName = (isDBusServiceNameLost ? oldOwner : newOwner);
- std::lock_guard<std::mutex> dbusServicesLock(dbusServicesMutex_);
- notificationThread_ = std::this_thread::get_id();
- onDBusServiceAvailabilityStatus(affectedName, dbusServiceAvailabilityStatus);
- notificationThread_ = std::thread::id();
+ std::lock_guard<std::mutex> dbusServicesLock(dbusServicesMutex_);
+
+ if (isDBusServiceNameLost) {
+ auto dbusUniqueNameIterator = dbusUniqueNamesMap_.find(dbusServiceUniqueName);
+ const bool isDBusUniqueNameFound = (dbusUniqueNameIterator != dbusUniqueNamesMap_.end());
+
+ if (isDBusUniqueNameFound) {
+ auto& dbusServiceListenersRecord = dbusServiceListenersMap[affectedName];
+ //assert(dbusServiceListenersRecord.uniqueBusNameState == DBusRecordState::RESOLVED);
+ //assert(!dbusServiceListenersRecord.uniqueBusName.empty());
+ onDBusServiceNotAvailable(dbusServiceListenersRecord);
+ }
+ } else {
+ onDBusServiceAvailable(affectedName, dbusServiceUniqueName);
}
return SubscriptionStatus::RETAIN;
}
-void DBusServiceRegistry::onListNamesCallback(const CommonAPI::CallStatus& callStatus, std::vector<std::string> dbusNames) {
- std::lock_guard<std::mutex> dbusServicesLock(dbusServicesMutex_);
- notificationThread_ = std::this_thread::get_id();
- if (callStatus == CallStatus::SUCCESS) {
- for (const std::string& dbusName : dbusNames) {
- if (isDBusServiceName(dbusName)) {
- onDBusServiceAvailabilityStatus(dbusName, AvailabilityStatus::AVAILABLE);
- }
- }
+
+void DBusServiceRegistry::onDBusServiceAvailable(const std::string& dbusServiceName,
+ const std::string& dbusServiceUniqueName) {
+ DBusUniqueNameRecord* dbusUniqueNameRecord = insertServiceNameMapping(dbusServiceUniqueName, dbusServiceName);
+
+ auto& dbusServiceListenersRecord = dbusServiceListenersMap[dbusServiceName];
+ const bool isDBusServiceNameObserved = !dbusServiceListenersRecord.dbusObjectPathListenersMap.empty();
+
+ if (dbusServiceListenersRecord.uniqueBusNameState == DBusRecordState::RESOLVED) {
+ assert(dbusServiceListenersRecord.uniqueBusName == dbusServiceUniqueName);
+ return;
}
- dbusNameListStatus_ = AvailabilityStatus::AVAILABLE;
+ dbusServiceListenersRecord.uniqueBusNameState = DBusRecordState::RESOLVED;
+ dbusServiceListenersRecord.uniqueBusName = dbusServiceUniqueName;
- auto dbusServiceIterator = dbusServices_.begin();
- while (dbusServiceIterator != dbusServices_.end()) {
- const DBusServiceState& dbusServiceState = dbusServiceIterator->second.first;
+ if (!isDBusServiceNameObserved) {
+ return;
+ }
- if (dbusServiceState == DBusServiceState::UNKNOWN) {
- dbusServiceIterator = onDBusServiceOffline(dbusServiceIterator, DBusServiceState::NOT_AVAILABLE);
+ // resolve object path and notify service listners
+ for (auto dbusObjectPathListenersIterator = dbusServiceListenersRecord.dbusObjectPathListenersMap.begin();
+ dbusObjectPathListenersIterator != dbusServiceListenersRecord.dbusObjectPathListenersMap.end();) {
+ const std::string& listenersDBusObjectPath = dbusObjectPathListenersIterator->first;
+ auto& dbusInterfaceNameListenersMap = dbusObjectPathListenersIterator->second;
+ auto& dbusObjectPathRecord = getDBusObjectPathCacheReference(
+ listenersDBusObjectPath,
+ dbusServiceUniqueName,
+ *dbusUniqueNameRecord);
+
+ if (dbusObjectPathRecord.state == DBusRecordState::RESOLVED) {
+ notifyDBusObjectPathResolved(dbusInterfaceNameListenersMap, dbusObjectPathRecord.dbusInterfaceNamesCache);
+ }
+
+ if (dbusInterfaceNameListenersMap.empty()) {
+ dbusObjectPathListenersIterator = dbusServiceListenersRecord.dbusObjectPathListenersMap.erase(
+ dbusObjectPathListenersIterator);
} else {
- dbusServiceIterator++;
+ dbusObjectPathListenersIterator++;
}
}
- notificationThread_ = std::thread::id();
}
+void DBusServiceRegistry::onDBusServiceNotAvailable(DBusServiceListenersRecord& dbusServiceListenersRecord) {
+ const std::unordered_set<std::string> dbusInterfaceNamesCache;
-void DBusServiceRegistry::onDBusServiceAvailabilityStatus(const std::string& dbusServiceName, const AvailabilityStatus& availabilityStatus) {
- auto dbusServiceIterator = dbusServices_.find(dbusServiceName);
+ auto dbusUniqueNameRecordIterator = dbusUniqueNamesMap_.find(dbusServiceListenersRecord.uniqueBusName);
- if (dbusServiceIterator != dbusServices_.end()) {
- onDBusServiceAvailabilityStatus(dbusServiceIterator, availabilityStatus);
- } else if (availabilityStatus == AvailabilityStatus::AVAILABLE) {
- dbusServices_.insert({ dbusServiceName, { DBusServiceState::AVAILABLE, DBusInstanceList() } });
- dbusServiceChanged_.notify_all();
- }
-}
+ // fulfill all open promises on object path resolution
+ if(dbusUniqueNameRecordIterator != dbusUniqueNamesMap_.end()) {
+ DBusUniqueNameRecord& dbusUniqueNameRecord = dbusUniqueNameRecordIterator->second;
+ for (auto dbusObjectPathsCacheIterator = dbusUniqueNameRecord.dbusObjectPathsCache.begin();
+ dbusObjectPathsCacheIterator != dbusUniqueNameRecord.dbusObjectPathsCache.end();
+ dbusObjectPathsCacheIterator++) {
+ auto& dbusObjectPathsCache = dbusObjectPathsCacheIterator->second;
-DBusServiceRegistry::DBusServiceList::iterator DBusServiceRegistry::onDBusServiceAvailabilityStatus(DBusServiceList::iterator& dbusServiceIterator,
- const AvailabilityStatus& availabilityStatus) {
- DBusServiceState& dbusServiceState = dbusServiceIterator->second.first;
- DBusInstanceList& dbusInstanceList = dbusServiceIterator->second.second;
+ std::promise<DBusRecordState> promiseOnResolve = std::move(dbusObjectPathsCache.promiseOnResolve);
- if (availabilityStatus == AvailabilityStatus::AVAILABLE) {
- const std::string& dbusServiceName = dbusServiceIterator->first;
+ try {
+ std::future<DBusRecordState> futureOnResolve = promiseOnResolve.get_future();
+ if(!futureOnResolve.valid()) {
+ promiseOnResolve.set_value(DBusRecordState::NOT_AVAILABLE);
+ }
+ } catch (std::future_error& e) { }
- if (dbusServiceState != DBusServiceState::RESOLVING) {
- resolveDBusServiceInstances(dbusServiceIterator);
}
-
- return dbusServiceIterator;
}
- dbusServiceState = (availabilityStatus == AvailabilityStatus::UNKNOWN) ?
- DBusServiceState::UNKNOWN :
- DBusServiceState::NOT_AVAILABLE;
+ removeUniqueName(dbusServiceListenersRecord.uniqueBusName);
- return onDBusServiceOffline(dbusServiceIterator, dbusServiceState);
-}
-
-DBusServiceRegistry::DBusServiceList::iterator DBusServiceRegistry::onDBusServiceOffline(DBusServiceList::iterator& dbusServiceIterator,
- const DBusServiceState& newDBusServiceState) {
- DBusServiceState& dbusServiceState = dbusServiceIterator->second.first;
- DBusInstanceList& dbusInstanceList = dbusServiceIterator->second.second;
- auto dbusInstanceIterator = dbusInstanceList.begin();
+ dbusServiceListenersRecord.uniqueBusName.clear();
+ dbusServiceListenersRecord.uniqueBusNameState = DBusRecordState::NOT_AVAILABLE;
- assert(newDBusServiceState == DBusServiceState::UNKNOWN || newDBusServiceState == DBusServiceState::NOT_AVAILABLE);
- dbusServiceState = newDBusServiceState;
+ for (auto dbusObjectPathListenersIterator = dbusServiceListenersRecord.dbusObjectPathListenersMap.begin();
+ dbusObjectPathListenersIterator != dbusServiceListenersRecord.dbusObjectPathListenersMap.end(); ) {
+ auto& dbusInterfaceNameListenersMap = dbusObjectPathListenersIterator->second;
- while (dbusInstanceIterator != dbusInstanceList.end()) {
- AvailabilityStatus& dbusInstanceAvailabilityStatus = dbusInstanceIterator->second.first;
- DBusServiceListenerList& dbusServiceListenerList = dbusInstanceIterator->second.second;
+ notifyDBusObjectPathResolved(dbusInterfaceNameListenersMap, dbusInterfaceNamesCache);
- // notify listeners
- if (!dbusServiceListenerList.empty()) {
- // the internal state is unknown until the next time we ask the object manager
- notifyDBusServiceListeners(dbusServiceListenerList, AvailabilityStatus::NOT_AVAILABLE);
- dbusInstanceAvailabilityStatus = AvailabilityStatus::UNKNOWN;
- dbusInstanceIterator++;
+ if (dbusInterfaceNameListenersMap.empty()) {
+ dbusObjectPathListenersIterator = dbusServiceListenersRecord.dbusObjectPathListenersMap.erase(
+ dbusObjectPathListenersIterator);
} else {
- dbusInstanceIterator = dbusInstanceList.erase(dbusInstanceIterator);
+ dbusObjectPathListenersIterator++;
}
}
+}
- dbusServiceChanged_.notify_all();
+void DBusServiceRegistry::notifyDBusServiceListeners(const DBusUniqueNameRecord& dbusUniqueNameRecord,
+ const std::string& dbusObjectPath,
+ const std::unordered_set<std::string>& dbusInterfaceNames,
+ const DBusRecordState& dbusInterfaceNamesState) {
+ notificationThread_ = std::this_thread::get_id();
- if (dbusInstanceList.empty()) {
- return dbusServices_.erase(dbusServiceIterator);
- }
+ for (auto& dbusServiceName : dbusUniqueNameRecord.ownedBusNames) {
+ auto dbusServiceListenersIterator = dbusServiceListenersMap.find(dbusServiceName);
- dbusServiceIterator++;
+ if(dbusServiceListenersIterator == dbusServiceListenersMap.end()) {
+ continue;
+ }
- return dbusServiceIterator;
-}
+ auto& dbusServiceListenersRecord = dbusServiceListenersIterator->second;
+ if(dbusServiceListenersRecord.uniqueBusNameState != DBusRecordState::RESOLVED) {
+ continue;
+ }
-void DBusServiceRegistry::resolveDBusServiceInstances(DBusServiceList::iterator& dbusServiceIterator) {
- const std::string& dbusServiceName = dbusServiceIterator->first;
- DBusServiceState& dbusServiceState = dbusServiceIterator->second.first;
- DBusInstanceList& dbusInstanceList = dbusServiceIterator->second.second;
- std::vector<DBusServiceAddress> predefinedDBusServiceInstances;
+ auto dbusObjectPathListenersIterator = dbusServiceListenersRecord.dbusObjectPathListenersMap.find(dbusObjectPath);
+ const bool isDBusObjectPathListenersRecordFound =
+ (dbusObjectPathListenersIterator != dbusServiceListenersRecord.dbusObjectPathListenersMap.end());
- dbusServiceState = DBusServiceState::RESOLVING;
+ if (!isDBusObjectPathListenersRecordFound) {
+ continue; // skip
+ }
- // add predefined instances
- DBusAddressTranslator::getInstance().getPredefinedInstances(dbusServiceName, predefinedDBusServiceInstances);
+ auto& dbusInterfaceNameListenersMap = dbusObjectPathListenersIterator->second;
- for (auto& dbusServiceAddress : predefinedDBusServiceInstances) {
- const std::string& dbusObjectPath = std::get<1>(dbusServiceAddress);
- const std::string& dbusInterfaceName = std::get<2>(dbusServiceAddress);
+ if (dbusInterfaceNamesState == DBusRecordState::RESOLVED) {
+ notifyDBusObjectPathResolved(dbusInterfaceNameListenersMap, dbusInterfaceNames);
+ } else {
+ notifyDBusObjectPathChanged(dbusInterfaceNameListenersMap, dbusInterfaceNames, dbusInterfaceNamesState);
+ }
- onDBusServiceInstanceAvailable(dbusInstanceList, dbusObjectPath, dbusInterfaceName);
+ if (dbusInterfaceNameListenersMap.empty()) {
+ dbusServiceListenersRecord.dbusObjectPathListenersMap.erase(dbusObjectPathListenersIterator);
+ }
}
- dbusServiceChanged_.notify_all();
-
- // search for remote instances
- DBusDaemonProxy::GetManagedObjectsAsyncCallback callback = std::bind(&DBusServiceRegistry::onGetManagedObjectsCallback,
- this->shared_from_this(),
- std::placeholders::_1,
- std::placeholders::_2,
- dbusServiceName);
- dbusDaemonProxy_->getManagedObjectsAsync(dbusServiceName, callback);
+ notificationThread_ = std::thread::id();
}
+void DBusServiceRegistry::notifyDBusObjectPathResolved(DBusInterfaceNameListenersMap& dbusInterfaceNameListenersMap,
+ const std::unordered_set<std::string>& dbusInterfaceNames) {
+ for (auto dbusObjectPathListenersIterator = dbusInterfaceNameListenersMap.begin();
+ dbusObjectPathListenersIterator != dbusInterfaceNameListenersMap.end();) {
+ const auto& listenersDBusInterfaceName = dbusObjectPathListenersIterator->first;
+ auto& dbusInterfaceNameListenersRecord = dbusObjectPathListenersIterator->second;
-void DBusServiceRegistry::onGetManagedObjectsCallback(const CallStatus& callStatus,
- DBusDaemonProxy::DBusObjectToInterfaceDict managedObjects,
- const std::string& dbusServiceName) {
- std::lock_guard<std::mutex> dbusServicesLock(dbusServicesMutex_);
+ const auto& dbusInterfaceNameIterator = dbusInterfaceNames.find(listenersDBusInterfaceName);
+ const bool isDBusInterfaceNameAvailable = (dbusInterfaceNameIterator != dbusInterfaceNames.end());
- // already offline
- if (dbusNameListStatus_ == AvailabilityStatus::NOT_AVAILABLE) {
- return;
- }
+ notifyDBusInterfaceNameListeners(dbusInterfaceNameListenersRecord, isDBusInterfaceNameAvailable);
- auto dbusServiceIterator = dbusServices_.find(dbusServiceName);
- if (dbusServiceIterator == dbusServices_.end()) {
- return;
+ if (dbusInterfaceNameListenersRecord.listenerList.empty()) {
+ dbusObjectPathListenersIterator = dbusInterfaceNameListenersMap.erase(dbusObjectPathListenersIterator);
+ } else {
+ dbusObjectPathListenersIterator++;
+ }
}
+}
- DBusServiceState& dbusServiceState = dbusServiceIterator->second.first;
- DBusInstanceList& dbusInstanceList = dbusServiceIterator->second.second;
+void DBusServiceRegistry::notifyDBusObjectPathChanged(DBusInterfaceNameListenersMap& dbusInterfaceNameListenersMap,
+ const std::unordered_set<std::string>& dbusInterfaceNames,
+ const DBusRecordState& dbusInterfaceNamesState) {
+ const bool isDBusInterfaceNameAvailable = (dbusInterfaceNamesState == DBusRecordState::AVAILABLE);
- dbusServiceState = DBusServiceState::RESOLVED;
+ assert(
+ dbusInterfaceNamesState == DBusRecordState::AVAILABLE
+ || dbusInterfaceNamesState == DBusRecordState::NOT_AVAILABLE);
- if (callStatus == CallStatus::SUCCESS) {
- for (auto& dbusObjectPathIterator : managedObjects) {
- const std::string& dbusObjectPath = dbusObjectPathIterator.first;
+ for (const auto& dbusInterfaceName : dbusInterfaceNames) {
+ auto dbusInterfaceNameListenersIterator = dbusInterfaceNameListenersMap.find(dbusInterfaceName);
+ const bool isDBusInterfaceNameObserved = (dbusInterfaceNameListenersIterator
+ != dbusInterfaceNameListenersMap.end());
- for (auto& dbusInterfaceNameIterator : dbusObjectPathIterator.second) {
- const std::string& dbusInterfaceName = dbusInterfaceNameIterator.first;
+ if (isDBusInterfaceNameObserved) {
+ auto& dbusInterfaceNameListenersRecord = dbusInterfaceNameListenersIterator->second;
- onDBusServiceInstanceAvailable(dbusInstanceList, dbusObjectPath, dbusInterfaceName);
- }
+ notifyDBusInterfaceNameListeners(dbusInterfaceNameListenersRecord, isDBusInterfaceNameAvailable);
}
}
+}
- dbusServiceChanged_.notify_all();
+void DBusServiceRegistry::notifyDBusInterfaceNameListeners(DBusInterfaceNameListenersRecord& dbusInterfaceNameListenersRecord,
+ const bool& isDBusInterfaceNameAvailable) {
+ // FIXME maybe store simple boolean into the DBusInterfaceNameListenersRecord (only 2 states are allowed)
+ const AvailabilityStatus availabilityStatus = (isDBusInterfaceNameAvailable ?
+ AvailabilityStatus::AVAILABLE : AvailabilityStatus::NOT_AVAILABLE);
+ const DBusRecordState notifyState = (isDBusInterfaceNameAvailable ?
+ DBusRecordState::AVAILABLE : DBusRecordState::NOT_AVAILABLE);
- // notify only UNKNOWN. The predefined and resolved have already been handled
- for (auto& dbusInstanceIterator : dbusInstanceList) {
- AvailabilityStatus& dbusInstanceAvailabilityStatus = dbusInstanceIterator.second.first;
- DBusServiceListenerList& dbusServiceListenerList = dbusInstanceIterator.second.second;
+ if (notifyState == dbusInterfaceNameListenersRecord.state) {
+ return;
+ }
+ dbusInterfaceNameListenersRecord.state = notifyState;
+
+ for (auto dbusServiceListenerIterator = dbusInterfaceNameListenersRecord.listenerList.begin();
+ dbusServiceListenerIterator != dbusInterfaceNameListenersRecord.listenerList.end();) {
+ const auto& dbusServiceListener = *dbusServiceListenerIterator;
- if (dbusInstanceAvailabilityStatus == AvailabilityStatus::UNKNOWN) {
- dbusInstanceAvailabilityStatus = AvailabilityStatus::NOT_AVAILABLE;
- notifyDBusServiceListeners(dbusServiceListenerList, dbusInstanceAvailabilityStatus);
+ if (dbusServiceListener(availabilityStatus) != SubscriptionStatus::RETAIN) {
+ dbusServiceListenerIterator = dbusInterfaceNameListenersRecord.listenerList.erase(
+ dbusServiceListenerIterator);
+ } else {
+ dbusServiceListenerIterator++;
}
}
}
+void DBusServiceRegistry::removeUniqueName(std::string& dbusUniqueName) {
+ if(dbusUniqueName.empty()) {
+ return;
+ }
-void DBusServiceRegistry::onDBusServiceInstanceAvailable(DBusInstanceList& dbusInstanceList,
- const std::string& dbusObjectPath,
- const std::string& dbusInterfaceName) {
- auto dbusInstanceIterator = addDBusServiceInstance(dbusInstanceList, dbusObjectPath, dbusInterfaceName);
- AvailabilityStatus& dbusInstanceAvailabilityStatus = dbusInstanceIterator->second.first;
- DBusServiceListenerList& dbusServiceListenerList = dbusInstanceIterator->second.second;
+ auto dbusUniqueNamesIterator = dbusUniqueNamesMap_.find(dbusUniqueName);
- dbusInstanceAvailabilityStatus = AvailabilityStatus::AVAILABLE;
+ if(dbusUniqueNamesIterator != dbusUniqueNamesMap_.end()) {
+ for (auto dbusServiceNamesIterator = dbusUniqueNamesIterator->second.ownedBusNames.begin();
+ dbusServiceNamesIterator != dbusUniqueNamesIterator->second.ownedBusNames.end();
+ dbusServiceNamesIterator++) {
+ dbusServiceNameMap_.erase(*dbusServiceNamesIterator);
+ }
- notifyDBusServiceListeners(dbusServiceListenerList, dbusInstanceAvailabilityStatus);
+ dbusUniqueNamesMap_.erase(dbusUniqueNamesIterator);
+ }
}
+DBusServiceRegistry::DBusUniqueNameRecord* DBusServiceRegistry::insertServiceNameMapping(const std::string& dbusUniqueName,
+ const std::string& dbusServiceName) {
+ auto* dbusUniqueNameRecord = &(dbusUniqueNamesMap_[dbusUniqueName]);
+ dbusUniqueNameRecord->uniqueName = dbusUniqueName;
+ dbusUniqueNameRecord->ownedBusNames.insert(dbusServiceName);
-DBusServiceRegistry::DBusInstanceList::iterator DBusServiceRegistry::addDBusServiceInstance(DBusInstanceList& dbusInstanceList,
- const std::string& dbusObjectPath,
- const std::string& dbusInterfaceName) {
- auto dbusInstanceIterator = dbusInstanceList.find({ dbusObjectPath, dbusInterfaceName });
+ auto dbusServiceNameMapIterator = dbusServiceNameMap_.find(dbusServiceName);
- // add instance for the first time
- if (dbusInstanceIterator == dbusInstanceList.end()) {
- auto insertIterator = dbusInstanceList.insert(
- { { dbusObjectPath, dbusInterfaceName }, { AvailabilityStatus::UNKNOWN, DBusServiceListenerList() } });
- const bool& insertSuccessfull = insertIterator.second;
+ if(dbusServiceNameMapIterator == dbusServiceNameMap_.end()) {
+ dbusServiceNameMap_.insert({ dbusServiceName, dbusUniqueNameRecord });
+ }
+ else {
+ dbusServiceNameMapIterator->second = dbusUniqueNameRecord;
+ }
- assert(insertSuccessfull);
- dbusInstanceIterator = insertIterator.first;
+ DBusServiceListenersRecord& dbusServiceListenersRecord = dbusServiceListenersMap[dbusServiceName];
+
+ if(dbusServiceListenersRecord.uniqueBusNameState != DBusRecordState::RESOLVED) {
+ dbusServiceListenersRecord.uniqueBusName = dbusUniqueName;
+ dbusServiceListenersRecord.uniqueBusNameState = DBusRecordState::UNKNOWN;
}
- return dbusInstanceIterator;
+ return dbusUniqueNameRecord;
}
-void DBusServiceRegistry::notifyDBusServiceListeners(DBusServiceListenerList& dbusServiceListenerList,
- const AvailabilityStatus& availabilityStatus) {
- for (auto dbusServiceListenerIterator = dbusServiceListenerList.begin(); dbusServiceListenerIterator != dbusServiceListenerList.end(); ++dbusServiceListenerIterator) {
- const SubscriptionStatus status = (*dbusServiceListenerIterator)(availabilityStatus);
- if (status == SubscriptionStatus::CANCEL) {
- dbusServiceListenerIterator = dbusServiceListenerList.erase(dbusServiceListenerIterator);
- }
+/**
+ * finds a DBusUniquNameRecord associated with a given well-known name.
+ * The returned DBusUniquNameRecord* may be a NULL pointer, if the well-known
+ * name is known, but not associated with a unique name yet.
+ *
+ * @return true, if the given well-known name is found
+ */
+bool DBusServiceRegistry::findCachedDbusService(
+ const std::string& dbusServiceName,
+ DBusUniqueNameRecord** uniqueNameRecord) {
+ auto dbusUniqueNameRecordIterator = dbusServiceNameMap_.find(dbusServiceName);
+
+ if(dbusUniqueNameRecordIterator != dbusServiceNameMap_.end()) {
+ *uniqueNameRecord = dbusUniqueNameRecordIterator->second;
+ return true;
}
-}
-bool DBusServiceRegistry::isDBusServiceName(const std::string& name) {
- return name[0] != ':';
+ return false;
}
-}// namespace DBus
-}// namespace CommonAPI
+} // namespace DBus
+} // namespace CommonAPI