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