/*============================================================================= Library: CTK Copyright (c) 2010 German Cancer Research Center, Division of Medical and Biological Informatics Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. =============================================================================*/ #include "ctkServices_p.h" #include #include #include #include #include "ctkServiceFactory.h" #include "ctkPluginConstants.h" #include "ctkPluginFrameworkContext_p.h" #include "ctkServiceException.h" #include "ctkServiceRegistrationPrivate.h" #include "ctkQtServiceRegistration_p.h" #include "ctkLDAPExpr_p.h" using namespace QtMobility; struct ServiceRegistrationComparator { bool operator()(const ctkServiceRegistration* a, const ctkServiceRegistration* b) const { return *a < *b; } }; ServiceProperties ctkServices::createServiceProperties(const ServiceProperties& in, const QStringList& classes, long sid) { static qlonglong nextServiceID = 1; ServiceProperties props; if (!in.isEmpty()) { for (ServiceProperties::const_iterator it = in.begin(); it != in.end(); ++it) { const QString key = it.key(); const QString lcKey = it.key().toLower(); for (QListIterator i(props.keys()); i.hasNext(); ) { if (lcKey == i.next()) { throw std::invalid_argument(std::string("Several entries for property: ") + key.toStdString()); } } props.insert(lcKey, in.value(key)); } } if (!classes.isEmpty()) { props.insert(ctkPluginConstants::OBJECTCLASS, classes); } props.insert(ctkPluginConstants::SERVICE_ID, sid != -1 ? sid : nextServiceID++); return props; } ctkServices::ctkServices(ctkPluginFrameworkContext* fwCtx) : mutex(QMutex::Recursive), framework(fwCtx) { } ctkServices::~ctkServices() { clear(); } void ctkServices::clear() { QList serviceRegs = services.keys(); qDeleteAll(serviceRegs); services.clear(); classServices.clear(); framework = 0; } ctkServiceRegistration* ctkServices::registerService(ctkPluginPrivate* plugin, const QStringList& classes, QObject* service, const ServiceProperties& properties) { if (service == 0) { throw std::invalid_argument("Can't register 0 as a service"); } // Check if service implements claimed classes and that they exist. for (QStringListIterator i(classes); i.hasNext();) { QString cls = i.next(); if (cls.isEmpty()) { throw std::invalid_argument("Can't register as null class"); } if (!(qobject_cast(service))) { if (!checkServiceClass(service, cls)) { throw std::invalid_argument (std::string("Service object is not an instance of ") + cls.toStdString()); } } } ctkServiceRegistration* res = new ctkServiceRegistration(plugin, service, createServiceProperties(properties, classes)); { QMutexLocker lock(&mutex); services.insert(res, classes); for (QStringListIterator i(classes); i.hasNext(); ) { QString currClass = i.next(); QList& s = classServices[currClass]; QList::iterator ip = std::lower_bound(s.begin(), s.end(), res, ServiceRegistrationComparator()); s.insert(ip, res); } } ctkServiceReference r = res->getReference(); plugin->fwCtx->listeners.serviceChanged( plugin->fwCtx->listeners.getMatchingServiceSlots(r), ctkServiceEvent(ctkServiceEvent::REGISTERED, r)); return res; } void ctkServices::registerService(ctkPluginPrivate* plugin, QByteArray serviceDescription) { QMutexLocker lock(&mutex); QBuffer serviceBuffer(&serviceDescription); qServiceManager.addService(&serviceBuffer); QServiceManager::Error error = qServiceManager.error(); if (!(error == QServiceManager::NoError || error == QServiceManager::ServiceAlreadyExists)) { throw std::invalid_argument(std::string("Registering the service descriptor for plugin ") + plugin->symbolicName.toStdString() + " failed: " + getQServiceManagerErrorString(error).toStdString()); } QString serviceName = plugin->symbolicName + "_" + plugin->version.toString(); QList descriptors = qServiceManager.findInterfaces(serviceName); if (descriptors.isEmpty()) { qDebug().nospace() << "Warning: No interfaces found for service name " << serviceName << " in plugin " << plugin->symbolicName << " (ctkVersion " << plugin->version.toString() << ")"; } QListIterator it(descriptors); while (it.hasNext()) { QServiceInterfaceDescriptor descr = it.next(); qDebug() << "Registering:" << descr.interfaceName(); QStringList classes; ServiceProperties props; QStringList customKeys = descr.customAttributes(); QStringListIterator keyIt(customKeys); bool classAttrFound = false; while (keyIt.hasNext()) { QString key = keyIt.next(); if (key == ctkPluginConstants::OBJECTCLASS) { classAttrFound = true; classes << descr.customAttribute(key); } else { props.insert(key, descr.customAttribute(key)); } } if (!classAttrFound) { throw std::invalid_argument(std::string("The custom attribute \"") + ctkPluginConstants::OBJECTCLASS.toStdString() + "\" is missing in the interface description of \"" + descr.interfaceName().toStdString()); } ctkServiceRegistration* res = new ctkQtServiceRegistration(plugin, descr, createServiceProperties(props, classes)); services.insert(res, classes); for (QStringListIterator i(classes); i.hasNext(); ) { QString currClass = i.next(); QList& s = classServices[currClass]; QList::iterator ip = std::lower_bound(s.begin(), s.end(), res, ServiceRegistrationComparator()); s.insert(ip, res); } ctkServiceReference r = res->getReference(); plugin->fwCtx->listeners.serviceChanged( plugin->fwCtx->listeners.getMatchingServiceSlots(r), ctkServiceEvent(ctkServiceEvent::REGISTERED, r)); } } QString ctkServices::getQServiceManagerErrorString(QServiceManager::Error error) { switch (error) { case QServiceManager::NoError: return QString("No error occurred."); case QServiceManager::StorageAccessError: return QString("The service data storage is not accessible. This could be because the caller does not have the required permissions."); case QServiceManager::InvalidServiceLocation: return QString("The service was not found at its specified location."); case QServiceManager::InvalidServiceXml: return QString("The XML defining the service metadata is invalid."); case QServiceManager::InvalidServiceInterfaceDescriptor: return QString("The service interface descriptor is invalid, or refers to an interface implementation that cannot be accessed in the current scope."); case QServiceManager::ServiceAlreadyExists: return QString("Another service has previously been registered with the same location."); case QServiceManager::ImplementationAlreadyExists: return QString("Another service that implements the same interface version has previously been registered."); case QServiceManager::PluginLoadingFailed: return QString("The service plugin cannot be loaded."); case QServiceManager::ComponentNotFound: return QString("The service or interface implementation has not been registered."); case QServiceManager::ServiceCapabilityDenied: return QString("The security session does not allow the service based on its capabilities."); case QServiceManager::UnknownError: return QString("An unknown error occurred."); default: return QString("Unknown error enum."); } } void ctkServices::updateServiceRegistrationOrder(ctkServiceRegistration* sr, const QStringList& classes) { QMutexLocker lock(&mutex); for (QStringListIterator i(classes); i.hasNext(); ) { QList& s = classServices[i.next()]; s.removeAll(sr); s.insert(std::lower_bound(s.begin(), s.end(), sr, ServiceRegistrationComparator()), sr); } } bool ctkServices::checkServiceClass(QObject* service, const QString& cls) const { return service->inherits(cls.toAscii()); } QList ctkServices::get(const QString& clazz) const { QMutexLocker lock(&mutex); return classServices.value(clazz); } ctkServiceReference ctkServices::get(ctkPluginPrivate* plugin, const QString& clazz) const { QMutexLocker lock(&mutex); try { QList srs = get(clazz, QString()); qDebug() << "get service ref" << clazz << "for plugin" << plugin->location << " = " << srs.size() << "refs"; if (!srs.isEmpty()) { return srs.front(); } } catch (const std::invalid_argument& ) { } throw ctkServiceException(QString("No service registered for: ") + clazz); } QList ctkServices::get(const QString& clazz, const QString& filter) const { QMutexLocker lock(&mutex); QListIterator* s = 0; QList v; ctkLDAPExpr ldap; if (clazz.isEmpty()) { if (!filter.isEmpty()) { ldap = ctkLDAPExpr(filter); QSet matched = ldap.getMatchedObjectClasses(); if (!matched.isEmpty()) { v.clear(); foreach (QString className, matched) { const QList& cl = classServices[className]; v += cl; } if (!v.isEmpty()) { s = new QListIterator(v); } else { return QList(); } } else { s = new QListIterator(services.keys()); } } else { s = new QListIterator(services.keys()); } } else { QList v = classServices.value(clazz); if (!v.isEmpty()) { s = new QListIterator(v); } else { return QList(); } if (!filter.isEmpty()) { ldap = ctkLDAPExpr(filter); } } QList res; while (s->hasNext()) { ctkServiceRegistration* sr = s->next(); ctkServiceReference sri = sr->getReference(); if (filter.isEmpty() || ldap.evaluate(sr->d_func()->properties, false)) { res.push_back(sri); } } delete s; return res; } void ctkServices::removeServiceRegistration(ctkServiceRegistration* sr) { QMutexLocker lock(&mutex); QStringList classes = sr->d_func()->properties.value(ctkPluginConstants::OBJECTCLASS).toStringList(); services.remove(sr); for (QStringListIterator i(classes); i.hasNext(); ) { QString currClass = i.next(); QList& s = classServices[currClass]; if (s.size() > 1) { s.removeAll(sr); } else { classServices.remove(currClass); } } } QList ctkServices::getRegisteredByPlugin(ctkPluginPrivate* p) const { QMutexLocker lock(&mutex); QList res; for (QHashIterator i(services); i.hasNext(); ) { ctkServiceRegistration* sr = i.next().key(); if ((sr->d_func()->plugin = p)) { res.push_back(sr); } } return res; } QList ctkServices::getUsedByPlugin(ctkPlugin* p) const { QMutexLocker lock(&mutex); QList res; for (QHashIterator i(services); i.hasNext(); ) { ctkServiceRegistration* sr = i.next().key(); if (sr->d_func()->isUsedByPlugin(p)) { res.push_back(sr); } } return res; }