1/*============================================================================= 2 3 Library: CTK 4 5 Copyright (c) German Cancer Research Center, 6 Division of Medical and Biological Informatics 7 8 Licensed under the Apache License, Version 2.0 (the "License"); 9 you may not use this file except in compliance with the License. 10 You may obtain a copy of the License at 11 12 http://www.apache.org/licenses/LICENSE-2.0 13 14 Unless required by applicable law or agreed to in writing, software 15 distributed under the License is distributed on an "AS IS" BASIS, 16 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 See the License for the specific language governing permissions and 18 limitations under the License. 19 20=============================================================================*/ 21 22 23#include "ctkServiceTracker_p.h" 24#include "ctkTrackedService_p.h" 25#include "ctkServiceException.h" 26#include "ctkPluginConstants.h" 27#include "ctkPluginContext.h" 28 29#include <QVarLengthArray> 30#include <QDebug> 31 32#include <stdexcept> 33#include <limits> 34 35//---------------------------------------------------------------------------- 36template<class S, class T> 37ctkServiceTracker<S,T>::~ctkServiceTracker() 38{ 39} 40 41//---------------------------------------------------------------------------- 42template<class S, class T> 43ctkServiceTracker<S,T>::ctkServiceTracker(ctkPluginContext* context, 44 const ctkServiceReference& reference, 45 ServiceTrackerCustomizer* customizer) 46 : d_ptr(new ServiceTrackerPrivate(this, context, reference, customizer)) 47{ 48} 49 50//---------------------------------------------------------------------------- 51template<class S, class T> 52ctkServiceTracker<S,T>::ctkServiceTracker(ctkPluginContext* context, const QString& clazz, 53 ServiceTrackerCustomizer* customizer) 54 : d_ptr(new ServiceTrackerPrivate(this, context, clazz, customizer)) 55{ 56} 57 58//---------------------------------------------------------------------------- 59template<class S, class T> 60ctkServiceTracker<S,T>::ctkServiceTracker(ctkPluginContext* context, const ctkLDAPSearchFilter& filter, 61 ServiceTrackerCustomizer* customizer) 62 : d_ptr(new ServiceTrackerPrivate(this, context, filter, customizer)) 63{ 64} 65 66//---------------------------------------------------------------------------- 67template<class S, class T> 68ctkServiceTracker<S,T>::ctkServiceTracker(ctkPluginContext *context, ctkServiceTrackerCustomizer<T> *customizer) 69 : d_ptr(new ServiceTrackerPrivate(this, context, qobject_interface_iid<S>(), customizer)) 70{ 71 const char* clazz = qobject_interface_iid<S>(); 72 if (clazz == 0) throw ctkServiceException("The service interface class has no Q_DECLARE_INTERFACE macro"); 73} 74 75//---------------------------------------------------------------------------- 76template<class S, class T> 77void ctkServiceTracker<S,T>::open() 78{ 79 Q_D(ServiceTracker); 80 QSharedPointer<TrackedService> t; 81 { 82 QMutexLocker lock(&d->mutex); 83 if (d->trackedService) 84 { 85 return; 86 } 87 88 if (d->DEBUG_FLAG) 89 { 90 qDebug() << "ctkServiceTracker<S,T>::open: " << d->filter; 91 } 92 93 t = QSharedPointer<TrackedService>( 94 new TrackedService(this, d->customizer)); 95 { 96 QMutexLocker lockT(t.data()); 97 try { 98 d->context->connectServiceListener(t.data(), "serviceChanged", d->listenerFilter); 99 QList<ctkServiceReference> references; 100 if (!d->trackClass.isEmpty()) 101 { 102 references = d->getInitialReferences(d->trackClass, QString()); 103 } 104 else 105 { 106 if (!d->trackReference.getPlugin().isNull()) 107 { 108 references.push_back(d->trackReference); 109 } 110 else 111 { /* user supplied filter */ 112 references = d->getInitialReferences(QString(), 113 (d->listenerFilter.isNull()) ? d->filter.toString() : d->listenerFilter); 114 } 115 } 116 /* set tracked with the initial references */ 117 t->setInitial(references); 118 } 119 catch (const ctkInvalidArgumentException& e) 120 { 121 throw ctkRuntimeException(QString("unexpected ctkInvalidArgumentException exception: %1").arg(e.what())); 122 } 123 } 124 d->trackedService = t; 125 } 126 /* Call tracked outside of synchronized region */ 127 t->trackInitial(); /* process the initial references */ 128} 129 130//---------------------------------------------------------------------------- 131template<class S, class T> 132void ctkServiceTracker<S,T>::close() 133{ 134 Q_D(ServiceTracker); 135 QSharedPointer<TrackedService> outgoing; 136 QList<ctkServiceReference> references; 137 { 138 QMutexLocker lock(&d->mutex); 139 outgoing = d->trackedService; 140 if (outgoing.isNull()) 141 { 142 return; 143 } 144 if (d->DEBUG_FLAG) 145 { 146 qDebug() << "ctkServiceTracker<S,T>::close:" << d->filter; 147 } 148 outgoing->close(); 149 references = getServiceReferences(); 150 d->trackedService.clear();; 151 try 152 { 153 d->context->disconnectServiceListener(outgoing.data(), "serviceChanged"); 154 } 155 catch (const ctkIllegalStateException& /*e*/) 156 { 157 /* In case the context was stopped. */ 158 } 159 } 160 d->modified(); /* clear the cache */ 161 { 162 QMutexLocker lockT(outgoing.data()); 163 outgoing->wakeAll(); /* wake up any waiters */ 164 } 165 foreach (ctkServiceReference ref, references) 166 { 167 outgoing->untrack(ref, ctkServiceEvent()); 168 } 169 170 if (d->DEBUG_FLAG) 171 { 172 QMutexLocker lock(&d->mutex); 173 if ((d->cachedReference.getPlugin().isNull()) && (d->cachedService == 0)) 174 { 175 qDebug() << "ctkServiceTracker<S,T>::close[cached cleared]:" 176 << d->filter; 177 } 178 } 179} 180 181//---------------------------------------------------------------------------- 182template<class S, class T> 183T ctkServiceTracker<S,T>::waitForService(unsigned long timeout) 184{ 185 Q_D(ServiceTracker); 186 T object = getService(); 187 while (object == 0) 188 { 189 QSharedPointer<TrackedService> t = d->tracked(); 190 if (t.isNull()) 191 { /* if ServiceTracker is not open */ 192 return 0; 193 } 194 { 195 QMutexLocker lockT(t.data()); 196 if (t->size() == 0) 197 { 198 t->wait(timeout); 199 } 200 } 201 object = getService(); 202 if (timeout > 0) 203 { 204 return object; 205 } 206 } 207 return object; 208} 209 210//---------------------------------------------------------------------------- 211template<class S, class T> 212QList<ctkServiceReference> ctkServiceTracker<S,T>::getServiceReferences() const 213{ 214 Q_D(const ServiceTracker); 215 QSharedPointer<TrackedService> t = d->tracked(); 216 if (t.isNull()) 217 { /* if ServiceTracker is not open */ 218 return QList<ctkServiceReference>(); 219 } 220 { 221 QMutexLocker lockT(t.data()); 222 return d->getServiceReferences_unlocked(t.data()); 223 } 224} 225 226//---------------------------------------------------------------------------- 227template<class S, class T> 228ctkServiceReference ctkServiceTracker<S,T>::getServiceReference() const 229{ 230 Q_D(const ServiceTracker); 231 ctkServiceReference reference(0); 232 { 233 QMutexLocker lock(&d->mutex); 234 reference = d->cachedReference; 235 } 236 if (!reference.getPlugin().isNull()) 237 { 238 if (d->DEBUG_FLAG) 239 { 240 qDebug() << "ctkServiceTracker<S,T>::getServiceReference[cached]:" 241 << d->filter; 242 } 243 return reference; 244 } 245 if (d->DEBUG_FLAG) 246 { 247 qDebug() << "ctkServiceTracker<S,T>::getServiceReference:" << d->filter; 248 } 249 QList<ctkServiceReference> references = getServiceReferences(); 250 int length = references.size(); 251 if (length == 0) 252 { /* if no service is being tracked */ 253 throw ctkServiceException("No service is being tracked"); 254 } 255 int index = 0; 256 if (length > 1) 257 { /* if more than one service, select highest ranking */ 258 QVarLengthArray<int, 10> rankings(length); 259 int count = 0; 260 int maxRanking = std::numeric_limits<int>::min(); 261 for (int i = 0; i < length; i++) 262 { 263 bool ok = false; 264 int ranking = references[i].getProperty(ctkPluginConstants::SERVICE_RANKING).toInt(&ok); 265 if (!ok) ranking = 0; 266 267 rankings[i] = ranking; 268 if (ranking > maxRanking) 269 { 270 index = i; 271 maxRanking = ranking; 272 count = 1; 273 } 274 else 275 { 276 if (ranking == maxRanking) 277 { 278 count++; 279 } 280 } 281 } 282 if (count > 1) 283 { /* if still more than one service, select lowest id */ 284 qlonglong minId = std::numeric_limits<qlonglong>::max(); 285 for (int i = 0; i < length; i++) 286 { 287 if (rankings[i] == maxRanking) 288 { 289 qlonglong id = references[i].getProperty(ctkPluginConstants::SERVICE_ID).toLongLong(); 290 if (id < minId) 291 { 292 index = i; 293 minId = id; 294 } 295 } 296 } 297 } 298 } 299 300 { 301 QMutexLocker lock(&d->mutex); 302 d->cachedReference = references[index]; 303 return d->cachedReference; 304 } 305} 306 307//---------------------------------------------------------------------------- 308template<class S, class T> 309T ctkServiceTracker<S,T>::getService(const ctkServiceReference& reference) const 310{ 311 Q_D(const ServiceTracker); 312 QSharedPointer<TrackedService> t = d->tracked(); 313 if (t.isNull()) 314 { /* if ServiceTracker is not open */ 315 return 0; 316 } 317 { 318 QMutexLocker lockT(t.data()); 319 return t->getCustomizedObject(reference); 320 } 321} 322 323//---------------------------------------------------------------------------- 324template<class S, class T> 325QList<T> ctkServiceTracker<S,T>::getServices() const 326{ 327 Q_D(const ServiceTracker); 328 QSharedPointer<TrackedService> t = d->tracked(); 329 if (t.isNull()) 330 { /* if ServiceTracker is not open */ 331 return QList<T>(); 332 } 333 { 334 QMutexLocker lockT(t.data()); 335 QList<ctkServiceReference> references = d->getServiceReferences_unlocked(t.data()); 336 QList<T> objects; 337 foreach (ctkServiceReference ref, references) 338 { 339 //objects << getService(ref); 340 objects << t->getCustomizedObject(ref); 341 } 342 return objects; 343 } 344} 345 346//---------------------------------------------------------------------------- 347template<class S, class T> 348T ctkServiceTracker<S,T>::getService() const 349{ 350 Q_D(const ServiceTracker); 351 T service = d->cachedService; 352 if (service != 0) 353 { 354 if (d->DEBUG_FLAG) 355 { 356 qDebug() << "ctkServiceTracker<S,T>::getService[cached]:" 357 << d->filter; 358 } 359 return service; 360 } 361 if (d->DEBUG_FLAG) 362 { 363 qDebug() << "ctkServiceTracker<S,T>::getService:" << d->filter; 364 } 365 366 try 367 { 368 ctkServiceReference reference = getServiceReference(); 369 if (reference.getPlugin().isNull()) 370 { 371 return 0; 372 } 373 return d->cachedService = getService(reference); 374 } 375 catch (const ctkServiceException&) 376 { 377 return 0; 378 } 379} 380 381//---------------------------------------------------------------------------- 382template<class S, class T> 383void ctkServiceTracker<S,T>::remove(const ctkServiceReference& reference) 384{ 385 Q_D(ServiceTracker); 386 QSharedPointer<TrackedService> t = d->tracked(); 387 if (t.isNull()) 388 { /* if ServiceTracker is not open */ 389 return; 390 } 391 t->untrack(reference, ctkServiceEvent()); 392} 393 394//---------------------------------------------------------------------------- 395template<class S, class T> 396int ctkServiceTracker<S,T>::size() const 397{ 398 Q_D(const ServiceTracker); 399 QSharedPointer<TrackedService> t = d->tracked(); 400 if (t.isNull()) 401 { /* if ServiceTracker is not open */ 402 return 0; 403 } 404 { 405 QMutexLocker lockT(t.data()); 406 return t->size(); 407 } 408} 409 410//---------------------------------------------------------------------------- 411template<class S, class T> 412int ctkServiceTracker<S,T>::getTrackingCount() const 413{ 414 Q_D(const ServiceTracker); 415 QSharedPointer<TrackedService> t = d->tracked(); 416 if (t.isNull()) 417 { /* if ServiceTracker is not open */ 418 return -1; 419 } 420 { 421 QMutexLocker lockT(t.data()); 422 return t->getTrackingCount(); 423 } 424} 425 426//---------------------------------------------------------------------------- 427template<class S, class T> 428QMap<ctkServiceReference, T> ctkServiceTracker<S,T>::getTracked() const 429{ 430 QMap<ctkServiceReference, T> map; 431 Q_D(const ServiceTracker); 432 QSharedPointer<TrackedService> t = d->tracked(); 433 if (t.isNull()) 434 { /* if ServiceTracker is not open */ 435 return map; 436 } 437 { 438 QMutexLocker lockT(t.data()); 439 return t->copyEntries(map); 440 } 441} 442 443//---------------------------------------------------------------------------- 444template<class S, class T> 445bool ctkServiceTracker<S,T>::isEmpty() const 446{ 447 Q_D(const ServiceTracker); 448 QSharedPointer<TrackedService> t = d->tracked(); 449 if (t.isNull()) 450 { /* if ServiceTracker is not open */ 451 return true; 452 } 453 { 454 QMutexLocker lockT(t.data()); 455 return t->isEmpty(); 456 } 457} 458 459//---------------------------------------------------------------------------- 460template<class S, class T> 461T ctkServiceTracker<S,T>::addingService(const ctkServiceReference& reference) 462{ 463 Q_D(ServiceTracker); 464 return qobject_cast<T>(d->context->getService(reference)); 465} 466 467//---------------------------------------------------------------------------- 468template<class S, class T> 469void ctkServiceTracker<S,T>::modifiedService(const ctkServiceReference& reference, T service) 470{ 471 Q_UNUSED(reference) 472 Q_UNUSED(service) 473 /* do nothing */ 474} 475 476//---------------------------------------------------------------------------- 477template<class S, class T> 478void ctkServiceTracker<S,T>::removedService(const ctkServiceReference& reference, T service) 479{ 480 Q_UNUSED(service) 481 482 Q_D(ServiceTracker); 483 d->context->ungetService(reference); 484} 485