/*============================================================================= Library: CTK Copyright (c) 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 "ctkServiceTrackerPrivate.h" #include "ctkTrackedService_p.h" #include "ctkServiceException.h" #include "ctkPluginConstants.h" #include "ctkPluginContext.h" #include #include #include #include template ctkServiceTracker::~ctkServiceTracker() { } template ctkServiceTracker::ctkServiceTracker(ctkPluginContext* context, const ctkServiceReference& reference, ServiceTrackerCustomizer* customizer) : d_ptr(new ServiceTrackerPrivate(this, context, reference, customizer)) { } template ctkServiceTracker::ctkServiceTracker(ctkPluginContext* context, const QString& clazz, ServiceTrackerCustomizer* customizer) : d_ptr(new ServiceTrackerPrivate(this, context, clazz, customizer)) { } template ctkServiceTracker::ctkServiceTracker(ctkPluginContext* context, const ctkLDAPSearchFilter& filter, ServiceTrackerCustomizer* customizer) : d_ptr(new ServiceTrackerPrivate(this, context, filter, customizer)) { } template ctkServiceTracker::ctkServiceTracker(ctkPluginContext *context, ctkServiceTrackerCustomizer *customizer) : d_ptr(new ServiceTrackerPrivate(this, context, qobject_interface_iid(), customizer)) { const char* clazz = qobject_interface_iid(); if (clazz == 0) throw ctkServiceException("The service interface class has no Q_DECLARE_INTERFACE macro"); } template void ctkServiceTracker::open() { Q_D(ServiceTracker); QSharedPointer t; { QMutexLocker lock(&d->mutex); if (d->trackedService) { return; } if (d->DEBUG) { qDebug() << "ctkServiceTracker::open: " << d->filter; } t = QSharedPointer( new TrackedService(this, d->customizer)); { QMutexLocker lockT(t.data()); try { d->context->connectServiceListener(t.data(), "serviceChanged", d->listenerFilter); QList references; if (!d->trackClass.isEmpty()) { references = d->getInitialReferences(d->trackClass, QString()); } else { if (!d->trackReference.getPlugin().isNull()) { references.push_back(d->trackReference); } else { /* user supplied filter */ references = d->getInitialReferences(QString(), (d->listenerFilter.isNull()) ? d->filter.toString() : d->listenerFilter); } } /* set tracked with the initial references */ t->setInitial(references); } catch (const std::invalid_argument& e) { throw std::runtime_error(std::string("unexpected std::invalid_argument exception: ") + e.what()); } } d->trackedService = t; } /* Call tracked outside of synchronized region */ t->trackInitial(); /* process the initial references */ } template void ctkServiceTracker::close() { Q_D(ServiceTracker); QSharedPointer outgoing; QList references; { QMutexLocker lock(&d->mutex); outgoing = d->trackedService; if (outgoing.isNull()) { return; } if (d->DEBUG) { qDebug() << "ctkServiceTracker::close:" << d->filter; } outgoing->close(); references = getServiceReferences(); d->trackedService.clear();; try { d->context->disconnectServiceListener(outgoing.data(), "serviceChanged"); } catch (const std::logic_error& /*e*/) { /* In case the context was stopped. */ } } d->modified(); /* clear the cache */ { QMutexLocker lockT(outgoing.data()); outgoing->wakeAll(); /* wake up any waiters */ } foreach (ctkServiceReference ref, references) { outgoing->untrack(ref, ctkServiceEvent()); } if (d->DEBUG) { QMutexLocker lock(&d->mutex); if ((d->cachedReference.getPlugin().isNull()) && (d->cachedService == 0)) { qDebug() << "ctkServiceTracker::close[cached cleared]:" << d->filter; } } } template T ctkServiceTracker::waitForService(unsigned long timeout) { Q_D(ServiceTracker); T object = getService(); while (object == 0) { QSharedPointer t = d->tracked(); if (t.isNull()) { /* if ServiceTracker is not open */ return 0; } { QMutexLocker lockT(t.data()); if (t->size() == 0) { t->wait(timeout); } } object = getService(); if (timeout > 0) { return object; } } return object; } template QList ctkServiceTracker::getServiceReferences() const { Q_D(const ServiceTracker); QSharedPointer t = d->tracked(); if (t.isNull()) { /* if ServiceTracker is not open */ return QList(); } { QMutexLocker lockT(t.data()); if (t->size() == 0) { return QList(); } return t->getTracked(); } } template ctkServiceReference ctkServiceTracker::getServiceReference() const { Q_D(const ServiceTracker); ctkServiceReference reference(0); { QMutexLocker lock(&d->mutex); reference = d->cachedReference; } if (!reference.getPlugin().isNull()) { if (d->DEBUG) { qDebug() << "ctkServiceTracker::getServiceReference[cached]:" << d->filter; } return reference; } if (d->DEBUG) { qDebug() << "ctkServiceTracker::getServiceReference:" << d->filter; } QList references = getServiceReferences(); int length = references.size(); if (length == 0) { /* if no service is being tracked */ throw ctkServiceException("No service is being tracked"); } int index = 0; if (length > 1) { /* if more than one service, select highest ranking */ QVarLengthArray rankings(length); int count = 0; int maxRanking = std::numeric_limits::min(); for (int i = 0; i < length; i++) { bool ok = false; int ranking = references[i].getProperty(ctkPluginConstants::SERVICE_RANKING).toInt(&ok); if (!ok) ranking = 0; rankings[i] = ranking; if (ranking > maxRanking) { index = i; maxRanking = ranking; count = 1; } else { if (ranking == maxRanking) { count++; } } } if (count > 1) { /* if still more than one service, select lowest id */ qlonglong minId = std::numeric_limits::max(); for (int i = 0; i < length; i++) { if (rankings[i] == maxRanking) { qlonglong id = references[i].getProperty(ctkPluginConstants::SERVICE_ID).toLongLong(); if (id < minId) { index = i; minId = id; } } } } } { QMutexLocker lock(&d->mutex); d->cachedReference = references[index]; return d->cachedReference; } } template T ctkServiceTracker::getService(const ctkServiceReference& reference) const { Q_D(const ServiceTracker); QSharedPointer t = d->tracked(); if (t.isNull()) { /* if ServiceTracker is not open */ return 0; } { QMutexLocker lockT(t.data()); return t->getCustomizedObject(reference); } } template QList ctkServiceTracker::getServices() const { Q_D(const ServiceTracker); QSharedPointer t = d->tracked(); if (t.isNull()) { /* if ServiceTracker is not open */ return QList(); } { QMutexLocker lockT(t.data()); QList references = getServiceReferences(); QList objects; foreach (ctkServiceReference ref, references) { objects << getService(ref); } return objects; } } template T ctkServiceTracker::getService() const { Q_D(const ServiceTracker); T service = d->cachedService; if (service != 0) { if (d->DEBUG) { qDebug() << "ctkServiceTracker::getService[cached]:" << d->filter; } return service; } if (d->DEBUG) { qDebug() << "ctkServiceTracker::getService:" << d->filter; } try { ctkServiceReference reference = getServiceReference(); if (reference.getPlugin().isNull()) { return 0; } return d->cachedService = getService(reference); } catch (const ctkServiceException&) { return 0; } } template void ctkServiceTracker::remove(const ctkServiceReference& reference) { Q_D(ServiceTracker); QSharedPointer t = d->tracked(); if (t.isNull()) { /* if ServiceTracker is not open */ return; } t->untrack(reference, ctkServiceEvent()); } template int ctkServiceTracker::size() const { Q_D(const ServiceTracker); QSharedPointer t = d->tracked(); if (t.isNull()) { /* if ServiceTracker is not open */ return 0; } { QMutexLocker lockT(t.data()); return t->size(); } } template int ctkServiceTracker::getTrackingCount() const { Q_D(const ServiceTracker); QSharedPointer t = d->tracked(); if (t.isNull()) { /* if ServiceTracker is not open */ return -1; } { QMutexLocker lockT(t.data()); return t->getTrackingCount(); } } template QMap ctkServiceTracker::getTracked() const { QMap map; Q_D(const ServiceTracker); QSharedPointer t = d->tracked(); if (t.isNull()) { /* if ServiceTracker is not open */ return map; } { QMutexLocker lockT(t.data()); return t->copyEntries(map); } } template bool ctkServiceTracker::isEmpty() const { Q_D(const ServiceTracker); QSharedPointer t = d->tracked(); if (t.isNull()) { /* if ServiceTracker is not open */ return true; } { QMutexLocker lockT(t.data()); return t->isEmpty(); } } template T ctkServiceTracker::addingService(const ctkServiceReference& reference) { Q_D(ServiceTracker); return qobject_cast(d->context->getService(reference)); } template void ctkServiceTracker::modifiedService(const ctkServiceReference& reference, T service) { Q_UNUSED(reference) Q_UNUSED(service) /* do nothing */ } template void ctkServiceTracker::removedService(const ctkServiceReference& reference, T service) { Q_UNUSED(service) Q_D(ServiceTracker); d->context->ungetService(reference); }