Bläddra i källkod

Merge branch 'add-pluginfw-stop'

* add-pluginfw-stop:
  Added convenience method for stopping plug-ins or the framework.
  Fixed memory leak in test suite.
  Properly shut down the plugin framework after running tests.
  Added plugin framework stop support.
  Use QSharedPointer objects as list elements.
  Added qDebug() stream operator for ctkRuntimeException.
  Throw std::invalid_argument instead of generic std::logic_error.
  Use ctkIllegalStateException instead of generic std::logic_error.
Sascha Zelzer 13 år sedan
förälder
incheckning
2c42979d5c

+ 9 - 4
Libs/PluginFramework/Testing/Cpp/ctkPluginFrameworkTestRunner.cpp

@@ -73,11 +73,16 @@ public:
       ++count;
     }
 
-    // stop the specified plugins
-    foreach(StartPluginPair pluginInfo, startPluginInfos)
+    // stop the framework
+    QSharedPointer<ctkPluginFramework> fw = qSharedPointerCast<ctkPluginFramework>(context->getPlugin(0));
+    fw->stop();
+    // wait for 30 secs
+    ctkPluginFrameworkEvent event = fw->waitForStop(30000);
+
+    if (result == 0 && event.getType() == ctkPluginFrameworkEvent::WAIT_TIMEDOUT)
     {
-      QSharedPointer<ctkPlugin> plugin = context->getPlugin(pluginInfo.first);
-      plugin->stop();
+      qWarning() << "Framework shutdown wait timed out";
+      result = 1;
     }
 
     qDebug() << "#########" << count << "out of" << refs.size() << "test suites passed #########";

+ 2 - 1
Libs/PluginFramework/Testing/FrameworkTest/ctkServiceTrackerTestSuite.cpp

@@ -213,9 +213,11 @@ void ctkServiceTrackerTestSuite::runTest()
   QTest::qWait(100);
   // register "org.commontk.pluginStest.TestPluginSService3"
   emit serviceControl(3, "register", 2);
+  // wait until the thread is finished
   QVERIFY(worker.wait());
   QVERIFY(worker.waitSuccess);
 
+  delete st1;
 }
 
 ctkServiceTrackerTestWorker::ctkServiceTrackerTestWorker(ctkPluginContext* pc)
@@ -234,4 +236,3 @@ void ctkServiceTrackerTestWorker::run()
   if (obj != 0) waitSuccess = true;
   quit();
 }
-

+ 94 - 96
Libs/PluginFramework/ctkPlugin.cpp

@@ -22,6 +22,7 @@
 #include "ctkPlugin.h"
 
 #include "ctkPluginContext.h"
+#include "ctkPluginContext_p.h"
 #include "ctkPluginFrameworkUtil_p.h"
 #include "ctkPluginPrivate_p.h"
 #include "ctkPluginArchive_p.h"
@@ -74,7 +75,7 @@ void ctkPlugin::start(const StartOptions& options)
 
   if (d->state == UNINSTALLED)
   {
-    throw std::logic_error("ctkPlugin is uninstalled");
+    throw ctkIllegalStateException("ctkPlugin is uninstalled");
   }
 
   // Initialize the activation; checks initialization of lazy
@@ -119,24 +120,22 @@ void ctkPlugin::stop(const StopOptions& options)
 {
   Q_D(ctkPlugin);
 
-  const std::exception* savedException = 0;
+  const ctkRuntimeException* savedException = 0;
 
   //1:
   if (d->state == UNINSTALLED)
   {
-    throw std::logic_error("Plugin is uninstalled");
+    throw ctkIllegalStateException("ctkPlugin is uninstalled");
   }
 
-  //2: If activating or deactivating, wait a litle
-  // We don't support threaded start/stop methods, so we don't need to wait
-  //waitOnActivation(fwCtx.packages, "Plugin::stop", false);
+  // 2: If an operation is in progress, wait a little
+  d->waitOnOperation(&d->operationLock, "Plugin::stop", false);
 
   //3:
   if ((options & STOP_TRANSIENT) == 0)
   {
     d->ignoreAutostartSetting();
   }
-  bool wasStarted = false;
 
   switch (d->state)
   {
@@ -148,28 +147,24 @@ void ctkPlugin::stop(const StopOptions& options)
     return;
 
   case ACTIVE:
-    wasStarted = true;
   case STARTING: // Lazy start...
-    try
-    {
-      d->stop0(wasStarted);
-    }
-    catch (const std::exception* exc)
-    {
-      savedException = exc;
-    }
+    savedException = d->stop0();
     break;
   };
 
-  if (savedException)
+  if (savedException != 0)
   {
     if (const ctkPluginException* pluginExc = dynamic_cast<const ctkPluginException*>(savedException))
     {
-      throw pluginExc;
+      ctkPluginException pe(*pluginExc);
+      delete savedException;
+      throw pe;
     }
     else
     {
-      throw dynamic_cast<const std::logic_error*>(savedException);
+      ctkRuntimeException re(*savedException);
+      delete savedException;
+      throw re;
     }
   }
 }
@@ -177,106 +172,109 @@ void ctkPlugin::stop(const StopOptions& options)
 //----------------------------------------------------------------------------
 void ctkPlugin::uninstall()
 {
-  bool wasResolved = false;
-
   Q_D(ctkPlugin);
-  if (d->archive)
   {
-    try
+    ctkPluginPrivate::Locker sync(&d->operationLock);
+
+    if (d->archive)
     {
-      d->archive->setStartLevel(-2); // Mark as uninstalled
+      try
+      {
+        d->archive->setStartLevel(-2); // Mark as uninstalled
+      }
+      catch (...)
+      { }
     }
-    catch (...)
-    { }
-  }
 
-  d->cachedHeaders = getHeaders();
-
-  switch (d->state)
-  {
-  case UNINSTALLED:
-    throw std::logic_error("Plugin is in UNINSTALLED state");
+    switch (d->state)
+    {
+    case UNINSTALLED:
+      throw ctkIllegalStateException("Plugin is in UNINSTALLED state");
 
-  case STARTING: // Lazy start
-  case ACTIVE:
-  case STOPPING:
-    try
+    case STARTING: // Lazy start
+    case ACTIVE:
+    case STOPPING:
     {
-      //TODO: If activating or deactivating, wait a litle
-      // we don't use mutliple threads to start plugins for now
-      //d->waitOnActivation(fwCtx.packages, "Bundle.uninstall", false);
-      if (d->state & (ACTIVE | STARTING))
+      const ctkRuntimeException* exception = 0;
+      try
       {
-        try
-        {
-          d->stop0(d->state == ACTIVE);
-        }
-        catch (const std::exception& exception)
+        d->waitOnOperation(&d->operationLock, "ctkPlugin::uninstall", true);
+        if (d->state & (ACTIVE | STARTING))
         {
-          // NYI! not call inside lock
-          d->fwCtx->listeners.frameworkError(this->d_func()->q_func(), exception);
+          exception = d->stop0();
         }
       }
+      catch (const std::exception& e)
+      {
+        // Force to install
+        d->setStateInstalled(false);
+        d->operationLock.wakeAll();
+        exception = new ctkRuntimeException("Stopping plug-in failed", &e);
+      }
+      d->operation.fetchAndStoreOrdered(ctkPluginPrivate::UNINSTALLING);
+      if (exception != 0)
+      {
+        d->fwCtx->listeners.frameworkError(this->d_func()->q_func(), *exception);
+        delete exception;
+      }
     }
-    catch (const std::exception& e)
-    {
-      d->deactivating = false;
-      //fwCtx.packages.notifyAll();
-      d->fwCtx->listeners.frameworkError(this->d_func()->q_func(), e);
-    }
-    // Fall through
-  case RESOLVED:
-    wasResolved = true;
-    // Fall through
-  case INSTALLED:
-    d->fwCtx->plugins->remove(d->location);
-    d->pluginActivator = 0;
-
-    if (d->pluginDir.exists())
-    {
-      if (!ctk::removeDirRecursively(d->pluginDir.absolutePath()))
+      // Fall through
+    case RESOLVED:
+    case INSTALLED:
+      d->fwCtx->plugins->remove(d->location);
+      if (d->operation.fetchAndAddOrdered(0) != ctkPluginPrivate::UNINSTALLING)
       {
-        // Plugin dir is not deleted completely, make sure we mark
-        // it as uninstalled for next framework restart
-        if (d->archive)
+        try
         {
-          try
-          {
-            d->archive->setStartLevel(-2); // Mark as uninstalled
-          }
-          catch (const std::exception& e)
+          d->waitOnOperation(&d->operationLock, "Plugin::uninstall", true);
+          d->operation.fetchAndStoreOrdered(ctkPluginPrivate::UNINSTALLING);
+        }
+        catch (const ctkPluginException& pe)
+        {
+          // Make sure that the context is invalid
+          if (d->pluginContext != 0)
           {
-            // NYI! Generate FrameworkError if dir still exists!?
-            qDebug() << "Failed to mark plugin" <<  d->id
-                     << "as uninstalled," << d->pluginDir.absoluteFilePath()
-                     << "must be deleted manually:" << e.what();
+            d->pluginContext->d_func()->invalidate();
+            d->pluginContext.reset();
           }
+          d->operation.fetchAndStoreOrdered(ctkPluginPrivate::UNINSTALLING);
+          d->fwCtx->listeners.frameworkError(this->d_func()->q_func(), pe);
         }
       }
-      d->pluginDir.setFile("");
-    }
-    if (d->archive)
-    {
-      d->archive->purge();
-    }
 
-    // id, location and headers survive after uninstall.
-    // TODO: UNRESOLVED must be sent out during installed state
-    // This needs to be reviewed. See OSGi bug #1374
-    d->state = INSTALLED;
-    d->modified();
+      d->state = INSTALLED;
+      // TODO: use thread
+      // bundleThread().bundleChanged(new BundleEvent(BundleEvent.UNRESOLVED, this));
+      d->fwCtx->listeners.emitPluginChanged(ctkPluginEvent(ctkPluginEvent::UNRESOLVED, d->q_func()));
+      d->cachedHeaders = getHeaders();
+      d->pluginActivator = 0;
+      d->state = UNINSTALLED;
 
-    // Broadcast events
-    if (wasResolved)
-    {
-      d->fwCtx->listeners.emitPluginChanged(ctkPluginEvent(ctkPluginEvent::UNRESOLVED, d->q_ptr));
-    }
+      // Purge old archive
+      if (d->archive)
+      {
+        d->archive->purge();
+      }
+      d->operation.fetchAndStoreOrdered(ctkPluginPrivate::IDLE);
+
+      if (d->pluginDir.exists())
+      {
+        if (!ctk::removeDirRecursively(d->pluginDir.absolutePath()))
+        {
+          d->fwCtx->listeners.frameworkError(this->d_func()->q_func(), std::runtime_error("Failed to delete plugin data"));
+        }
+        d->pluginDir.setFile("");
+      }
 
-    d->state = UNINSTALLED;
-    d->fwCtx->listeners.emitPluginChanged(ctkPluginEvent(ctkPluginEvent::UNINSTALLED, d->q_ptr));
+      // id, location and headers survive after uninstall.
 
-    break;
+      // There might be plug-in threads that are running start or stop
+      // operations. This will wake them and give them a chance to terminate.
+      d->operationLock.wakeAll();
+      break;
+    }
   }
+  d->fwCtx->listeners.emitPluginChanged(ctkPluginEvent(ctkPluginEvent::UNINSTALLED, d->q_ptr));
 }
 
 //----------------------------------------------------------------------------

+ 8 - 7
Libs/PluginFramework/ctkPlugin.h

@@ -354,7 +354,7 @@ public:
    *         be because a code dependency could not be resolved or the
    *         <code>ctkPluginActivator</code> could not be loaded or
    *         threw an exception.
-   * @throws std::logic_error If this plugin has been uninstalled or this
+   * @throws ctkIllegalStateException If this plugin has been uninstalled or this
    *         plugin tries to change its own state.
    */
   virtual void start(const StartOptions& options = START_ACTIVATION_POLICY);
@@ -428,7 +428,7 @@ public:
    *        {@link #STOP_TRANSIENT}.
    * @throws ctkPluginException If this plugin's <code>ctkPluginActivator</code>
    *         threw an exception.
-   * @throws std::logic_error If this plugin has been uninstalled or this
+   * @throws ctkIllegalStateException If this plugin has been uninstalled or this
    *         plugin tries to change its own state.
    */
   virtual void stop(const StopOptions& options = 0);
@@ -488,11 +488,11 @@ public:
    * @throws ctkPluginException If the uninstall failed. This can occur if
    *         another thread is attempting to change this plugin's state and
    *         does not complete in a timely manner.
-   * @throws std::logic_error If this plugin has been uninstalled or this
+   * @throws ctkIllegalStateException If this plugin has been uninstalled or this
    *         plugin tries to change its own state.
    * @see #stop()
    */
-  void uninstall();
+  virtual void uninstall();
 
   /**
    * Returns this plugin's {@link ctkPluginContext}. The returned
@@ -616,7 +616,7 @@ public:
    * @return A list of the resource paths (<code>QString</code>
    *         objects) or an empty list if no entry could be found.
 
-   * @throws std::logic_error If this plugin has been
+   * @throws ctkIllegalStateException If this plugin has been
    *         uninstalled.
    */
   virtual QStringList getResourceList(const QString& path) const;
@@ -658,7 +658,7 @@ public:
    *         does not have the appropriate
    *         <code>AdminPermission[this,RESOURCE]</code>, and the Plugin
    *         Framework supports permissions.
-   * @throws std::logic_error If this plugin has been uninstalled.
+   * @throws ctkIllegalStateException If this plugin has been uninstalled.
    */
   virtual QStringList findResources(const QString& path, const QString& filePattern, bool recurse) const;
 
@@ -675,7 +675,7 @@ public:
    * @param path The path name of the resource.
    * @return A QByteArray to the resource, or a null QByteArray if no resource could be
    *         found.
-   * @throws std::logic_error If this plugin has been
+   * @throws ctkIllegalStateException If this plugin has been
    *         uninstalled.
    */
   virtual QByteArray getResource(const QString& path) const;
@@ -718,6 +718,7 @@ public:
 protected:
 
   friend class ctkPluginFramework;
+  friend class ctkPluginFrameworkPrivate;
   friend class ctkPluginFrameworkContext;
   friend class ctkPlugins;
   friend class ctkServiceReferencePrivate;

+ 1 - 1
Libs/PluginFramework/ctkPluginContext.cpp

@@ -40,7 +40,7 @@ ctkPluginContextPrivate::ctkPluginContextPrivate(ctkPluginPrivate* plugin)
 void ctkPluginContextPrivate::isPluginContextValid() const
 {
   if (!plugin) {
-    throw std::logic_error("This plugin context is no longer valid");
+    throw ctkIllegalStateException("This plugin context is no longer valid");
   }
 }
 

+ 15 - 15
Libs/PluginFramework/ctkPluginContext.h

@@ -120,7 +120,7 @@ public:
    *
    * @return The <code>ctkPlugin</code> object associated with this
    *         <code>ctkPluginContext</code>.
-   * @throws std::logic_error If this ctkPluginContext is no
+   * @throws ctkIllegalStateException If this ctkPluginContext is no
    *         longer valid.
    */
   QSharedPointer<ctkPlugin> getPlugin() const;
@@ -209,7 +209,7 @@ public:
    *         <code>properties</code> contains case variants of the same key
    *         name.
    *         </ul>
-   * @throws std::logic_error If this ctkPluginContext is no longer valid.
+   * @throws ctkIllegalStateException If this ctkPluginContext is no longer valid.
    * @see ctkServiceRegistration
    * @see ctkServiceFactory
    */
@@ -233,7 +233,7 @@ public:
    * @return A ctkServiceRegistration object for use by the plugin
    *         registering the service to update the service's properties or to
    *         unregister the service.
-   * @throws std::logic_error If this ctkPluginContext is no longer valid.
+   * @throws ctkIllegalStateException If this ctkPluginContext is no longer valid.
    * @see registerService(const QStringList&, QObject*, const ctkDictionary&)
    */
   ctkServiceRegistration registerService(const char* clazz, QObject* service, const ctkDictionary& properties = ctkDictionary());
@@ -293,7 +293,7 @@ public:
    *         search.
    * @throws std::invalid_argument If the specified <code>filter</code>
    *         contains an invalid filter expression that cannot be parsed.
-   * @throws std::logic_error If this ctkPluginContext is no longer valid.
+   * @throws ctkIllegalStateException If this ctkPluginContext is no longer valid.
    */
   QList<ctkServiceReference> getServiceReferences(const QString& clazz, const QString& filter = QString());
 
@@ -314,7 +314,7 @@ public:
    *         search.
    * @throws std::invalid_argument If the specified <code>filter</code>
    *         contains an invalid filter expression that cannot be parsed.
-   * @throws std::logic_error If this ctkPluginContext is no longer valid.
+   * @throws ctkIllegalStateException If this ctkPluginContext is no longer valid.
    * @see getServiceReferences(const QString&, const QString&)
    */
   template<class S>
@@ -351,7 +351,7 @@ public:
    * @param clazz The class name with which the service was registered.
    * @return A <code>ctkServiceReference</code> object, or <code>0</code> if
    *         no services are registered which implement the named class.
-   * @throws std::logic_error If this ctkPluginContext is no longer valid.
+   * @throws ctkIllegalStateException If this ctkPluginContext is no longer valid.
    * @throws ctkServiceException It no service was registered under the given class name.
    * @see #getServiceReferences(const QString&, const QString&)
    */
@@ -367,7 +367,7 @@ public:
    *
    * @return A <code>ctkServiceReference</code> object, or <code>0</code> if
    *         no services are registered which implement the named class.
-   * @throws std::logic_error If this ctkPluginContext is no longer valid.
+   * @throws ctkIllegalStateException If this ctkPluginContext is no longer valid.
    * @throws ctkServiceException It no service was registered under the given class name.
    * @see #getServiceReference(const QString&)
    * @see #getServiceReferences(const QString&)
@@ -429,7 +429,7 @@ public:
    *         <code>ctkServiceFactory</code> does not implement the classes under
    *         which it was registered or the <code>ctkServiceFactory</code> threw
    *         an exception.
-   * @throws std::logic_error If this ctkPluginContext is no
+   * @throws ctkIllegalStateException If this ctkPluginContext is no
    *         longer valid.
    * @throws std::invalid_argument If the specified
    *         <code>ctkServiceReference</code> was not created by the same
@@ -453,7 +453,7 @@ public:
    *         <code>ctkServiceFactory</code> does not implement the classes under
    *         which it was registered, the <code>ctkServiceFactory</code> threw
    *         an exception or the service could not be casted to the desired type.
-   * @throws std::logic_error If this ctkPluginContext is no
+   * @throws ctkIllegalStateException If this ctkPluginContext is no
    *         longer valid.
    * @throws std::invalid_argument If the specified
    *         <code>ctkServiceReference</code> was not created by the same
@@ -500,7 +500,7 @@ public:
    * @return <code>false</code> if the context plugin's use count for the
    *         service is zero or if the service has been unregistered;
    *         <code>true</code> otherwise.
-   * @throws std::logic_error If this ctkPluginContext is no
+   * @throws ctkIllegalStateException If this ctkPluginContext is no
    *         longer valid.
    * @throws std::invalid_argument If the specified
    *         <code>ctkServiceReference</code> was not created by the same
@@ -530,7 +530,7 @@ public:
    * @param filename A relative name to the file or directory to be accessed.
    * @return A <code>QFileInfo</code> object that represents the requested file
    *         or directory.
-   * @throws std::logic_error If this ctkPluginContext is no longer valid.
+   * @throws ctkIllegalStateException If this ctkPluginContext is no longer valid.
    */
   QFileInfo getDataFile(const QString& filename);
 
@@ -590,7 +590,7 @@ public:
    * @return The <code>ctkPlugin</code> object of the installed plugin.
    * @throws ctkPluginException If the I/O device cannot be read or the
    *         installation failed.
-   * @throws std::logic_error If this ctkPluginContext is no longer valid.
+   * @throws ctkIllegalStateException If this ctkPluginContext is no longer valid.
    */
   QSharedPointer<ctkPlugin> installPlugin(const QUrl& location, QIODevice* input = 0);
 
@@ -606,7 +606,7 @@ public:
    *        Qt::QueuedConnection, or Qt::BlockingQueuedConnection is allowed.
    * @returns <code>true</code> if the connection was successfull;
    *          <code>false</code> otherwise.
-   * @throws std::logic_error If this ctkPluginContext is no
+   * @throws ctkIllegalStateException If this ctkPluginContext is no
    *         longer valid.
    * @see ctkPluginEvent
    * @see ctkEventBus
@@ -623,7 +623,7 @@ public:
    * @param type The Qt connection type.
    * @returns <code>true</code> if the connection was successfull;
    *          <code>false</code> otherwise.
-   * @throws std::logic_error If this ctkPluginContext is no
+   * @throws ctkIllegalStateException If this ctkPluginContext is no
    *         longer valid.
    * @see ctkPluginFrameworkEvent
    * @see ctkEventBus
@@ -675,7 +675,7 @@ public:
    * @param filter The filter criteria.
    * @throws std::invalid_argument If <code>filter</code> contains an
    *         invalid filter string that cannot be parsed.
-   * @throws std::logic_error If this ctkPluginContext is no
+   * @throws ctkIllegalStateException If this ctkPluginContext is no
    *         longer valid.
    * @see ctkServiceEvent
    * @see disconnectServiceListener()

+ 52 - 10
Libs/PluginFramework/ctkPluginFramework.cpp

@@ -44,8 +44,8 @@ void ctkPluginFramework::init()
 {
   Q_D(ctkPluginFramework);
 
-  QMutexLocker sync(&d->lock);
-  // waitOnActivation(d->lock, "Framework.init", true);
+  ctkPluginPrivate::Locker sync(&d->lock);
+  d->waitOnOperation(&d->lock, "Framework.init", true);
   switch (d->state)
   {
   case ctkPlugin::INSTALLED:
@@ -55,12 +55,38 @@ void ctkPluginFramework::init()
   case ctkPlugin::ACTIVE:
     return;
   default:
-    throw std::logic_error("INTERNAL ERROR, Illegal state");
+    throw ctkIllegalStateException("INTERNAL ERROR, Illegal state");
   }
   d->init();
 }
 
 //----------------------------------------------------------------------------
+ctkPluginFrameworkEvent ctkPluginFramework::waitForStop(unsigned long timeout)
+{
+  Q_D(ctkPluginFramework);
+
+  ctkPluginPrivate::Locker sync(&d->operationLock);
+
+  // Already stopped?
+  if ((d->state & (INSTALLED | RESOLVED)) == 0)
+  {
+    d->stopEvent = ctkPluginFrameworkEvent();
+    d->lock.wait(timeout ? timeout : ULONG_MAX);
+
+    if (d->stopEvent.isNull())
+    {
+      return ctkPluginFrameworkEvent(ctkPluginFrameworkEvent::WAIT_TIMEDOUT, this->d_func()->q_func());
+    }
+  }
+  else if (d->stopEvent.isNull())
+  {
+    // Return this if stop or update have not been called and framework is stopped.
+    d->stopEvent = ctkPluginFrameworkEvent(ctkPluginFrameworkEvent::STOPPED, this->d_func()->q_func());
+  }
+  return d->stopEvent;
+}
+
+//----------------------------------------------------------------------------
 void ctkPluginFramework::start(const ctkPlugin::StartOptions& options)
 {
   Q_UNUSED(options);
@@ -68,9 +94,8 @@ void ctkPluginFramework::start(const ctkPlugin::StartOptions& options)
 
   QStringList pluginsToStart;
   {
-    QMutexLocker sync(&d->lock);
-    // TODO: parallel start
-    //waitOnActivation(lock, "ctkPluginFramework::start", true);
+    ctkPluginPrivate::Locker sync(&d->lock);
+    d->waitOnOperation(&d->lock, "ctkPluginFramework::start", true);
 
     switch (d->state)
     {
@@ -78,12 +103,12 @@ void ctkPluginFramework::start(const ctkPlugin::StartOptions& options)
     case RESOLVED:
       d->init();
     case STARTING:
-      d->activating = true;
+      d->operation.fetchAndStoreOrdered(ctkPluginPrivate::ACTIVATING);
       break;
     case ACTIVE:
       return;
     default:
-      throw std::logic_error("INTERNAL ERROR, Illegal state");
+      throw ctkIllegalStateException("INTERNAL ERROR, Illegal state");
     }
 
     pluginsToStart = d->fwCtx->storage->getStartOnLaunchPlugins();
@@ -112,15 +137,32 @@ void ctkPluginFramework::start(const ctkPlugin::StartOptions& options)
   }
 
   {
-    QMutexLocker sync(&d->lock);
+    ctkPluginPrivate::Locker sync(&d->lock);
     d->state = ACTIVE;
-    d->activating = false;
+    d->operation = ctkPluginPrivate::IDLE;
+    d->lock.wakeAll();
     d->fwCtx->listeners.emitFrameworkEvent(
         ctkPluginFrameworkEvent(ctkPluginFrameworkEvent::STARTED, this->d_func()->q_func()));
   }
 }
 
 //----------------------------------------------------------------------------
+void ctkPluginFramework::stop(const StopOptions& options)
+{
+  Q_UNUSED(options)
+
+  Q_D(ctkPluginFramework);
+  d->shutdown(false);
+}
+
+//----------------------------------------------------------------------------
+void ctkPluginFramework::uninstall()
+{
+  throw ctkPluginException("uninstall of System plugin is not allowed",
+                           ctkPluginException::INVALID_OPERATION);
+}
+
+//----------------------------------------------------------------------------
 QStringList ctkPluginFramework::getResourceList(const QString& path) const
 {
   QString resourcePath = QString(":/") + ctkPluginConstants::SYSTEM_PLUGIN_SYMBOLICNAME;

+ 83 - 4
Libs/PluginFramework/ctkPluginFramework.h

@@ -22,11 +22,10 @@
 #ifndef CTKPLUGINFRAMEWORK_H
 #define CTKPLUGINFRAMEWORK_H
 
-#include "ctkPlugin.h"
-
-
-#include "ctkPluginFrameworkExport.h"
+#include <ctkPluginFrameworkExport.h>
 
+#include "ctkPlugin.h"
+#include "ctkPluginFrameworkEvent.h"
 
 class ctkPluginFrameworkContext;
 class ctkPluginFrameworkPrivate;
@@ -76,6 +75,42 @@ public:
   void init();
 
   /**
+   * Wait until this %ctkPluginFramework has completely stopped. The <code>stop</code>
+   * and <code>update</code> methods perform an asynchronous
+   * stop of the Framework. This method can be used to wait until the
+   * asynchronous stop of this Framework has completed. This method will only
+   * wait if called when this Framework is in the {@link #STARTING},
+   * {@link #ACTIVE}, or {@link #STOPPING} states. Otherwise it will return
+   * immediately.
+   * <p>
+   * A Framework Event is returned to indicate why this Framework has stopped.
+   *
+   * @param timeout Maximum number of milliseconds to wait until this
+   *        Framework has completely stopped. A value of zero will wait
+   *        indefinitely.
+   * @return A Framework Event indicating the reason this method returned. The
+   *         following <code>ctkFrameworkEvent</code> types may be returned by
+   *         this method.
+   *         <ul>
+   *         <li>{@link ctkFrameworkEvent#STOPPED STOPPED} - This Framework has
+   *         been stopped. </li>
+   *
+   *         <li>{@link ctkFrameworkEvent#STOPPED_UPDATE STOPPED_UPDATE} - This
+   *         Framework has been updated which has shutdown and will now
+   *         restart.</li>
+   *
+   *         <li>{@link ctkFrameworkEvent#ERROR ERROR} - The Framework
+   *         encountered an error while shutting down or an error has occurred
+   *         which forced the framework to shutdown. </li>
+   *
+   *         <li> {@link ctkFrameworkEvent#WAIT_TIMEDOUT WAIT_TIMEDOUT} - This
+   *         method has timed out and returned before this Framework has
+   *         stopped.</li>
+   *         </ul>
+   */
+  ctkPluginFrameworkEvent waitForStop(unsigned long timeout);
+
+  /**
    * Start this %ctkPluginFramework.
    *
    * <p>
@@ -100,6 +135,50 @@ public:
   void start(const ctkPlugin::StartOptions& options = 0);
 
   /**
+   * Stop this %ctkPluginFramework.
+   *
+   * <p>
+   * The method returns immediately to the caller after initiating the
+   * following steps to be taken on another thread.
+   * <ol>
+   * <li>This Framework's state is set to {@link #STOPPING}.</li>
+   * <li>All installed plugins must be stopped without changing each plugin's
+   * persistent <i>autostart setting</i>. If this Framework implements the
+   * optional <i>Start Level Service Specification</i>, then the start level
+   * of this Framework is moved to start level zero (0), as described in the
+   * <i>Start Level Service Specification</i>. Any exceptions that occur
+   * during plugin stopping must be wrapped in a {@link ctkPluginException} and
+   * then published as a framework event of type {@link ctkPluginFrameworkEvent#ERROR}</li>
+   * <li>Unregister all services registered by this Framework.</li>
+   * <li>Event handling is disabled.</li>
+   * <li>This Framework's state is set to {@link #RESOLVED}.</li>
+   * <li>All resources held by this Framework are released. This includes
+   * threads, loaded libraries, open files, etc.</li>
+   * <li>Notify all threads that are waiting at {@link #waitForStop(long)
+   * waitForStop} that the stop operation has completed.</li>
+   * </ol>
+   * <p>
+   * After being stopped, this Framework may be discarded, initialized or
+   * started.
+   *
+   * @param options Ignored. There are no stop options for the Framework.
+   * @throws ctkPluginException If stopping this Framework could not be
+   *         initiated.
+   * @see "Start Level Service Specification"
+   */
+  void stop(const StopOptions& options = 0);
+
+  /**
+   * The %ctkPluginFramework cannot be uninstalled.
+   *
+   * <p>
+   * This method always throws a ctkPluginException.
+   *
+   * @throws ctkPluginException This Framework cannot be uninstalled.
+   */
+  void uninstall();
+
+  /**
    * @see ctkPlugin::getHeaders()
    */
   QHash<QString, QString> getHeaders();

+ 63 - 0
Libs/PluginFramework/ctkPluginFrameworkLauncher.cpp

@@ -163,6 +163,69 @@ bool ctkPluginFrameworkLauncher::start(const QString& symbolicName, ctkPlugin::S
 }
 
 //----------------------------------------------------------------------------
+bool ctkPluginFrameworkLauncher::stop(const QString& symbolicName,
+                                      ctkPlugin::StopOptions options, ctkPluginContext* context)
+{
+  if (d->fwFactory == 0) return true;
+
+  ctkPluginContext* pc = context ? context : getPluginContext();
+  if (pc == 0)
+  {
+    qWarning() << "No valid plug-in context available";
+    return false;
+  }
+
+  if(!symbolicName.isEmpty())
+  {
+    QString pluginPath = getPluginPath(symbolicName);
+    if (pluginPath.isEmpty()) return false;
+
+    try
+    {
+      QList<QSharedPointer<ctkPlugin> > plugins = pc->getPlugins();
+      foreach(QSharedPointer<ctkPlugin> plugin, plugins)
+      {
+        if (plugin->getSymbolicName() == symbolicName)
+        {
+          plugin->stop(options);
+          return true;
+        }
+      }
+      qWarning() << "Plug-in" << symbolicName << "not found";
+      return false;
+    }
+    catch (const ctkPluginException& exc)
+    {
+      qWarning() << "Failed to stop plug-in:" << exc;
+      return false;
+    }
+  }
+  else
+  {
+    // stop the framework
+    QSharedPointer<ctkPluginFramework> fw =
+        qSharedPointerCast<ctkPluginFramework>(pc->getPlugin(0));
+    try
+    {
+      fw->stop();
+      ctkPluginFrameworkEvent fe = fw->waitForStop(5000);
+      if (fe.getType() == ctkPluginFrameworkEvent::WAIT_TIMEDOUT)
+      {
+        qWarning() << "Stopping the plugin framework timed out";
+        return false;
+      }
+    }
+    catch (const ctkRuntimeException& e)
+    {
+      qWarning() << "Stopping the plugin framework failed: " << e;
+      return false;
+    }
+
+    return true;
+  }
+}
+
+//----------------------------------------------------------------------------
 ctkPluginContext* ctkPluginFrameworkLauncher::getPluginContext()
 {
   if (d->fwFactory == 0) return 0;

+ 22 - 1
Libs/PluginFramework/ctkPluginFrameworkLauncher.h

@@ -100,13 +100,34 @@ public:
    * \return <code>true</code> if the plugin was found and successfully
    *         installed, <code>false</code> otherwise.
    *
-   * \see ctkPlugin::StartOption
+   * \see ctkPlugin::StartOptions
    */
   static bool start(const QString& symbolicName = QString(),
                     ctkPlugin::StartOptions options = ctkPlugin::START_ACTIVATION_POLICY,
                     ctkPluginContext* context = 0);
 
   /**
+   * This method either stops the plug-in with the given <code>symbolicName</code> using
+   * the supplied stop options <code>options</code>
+   * or the complete framework (if <code>symbolicName</code> is empty).
+   *
+   * <p>
+   * If a plugin context is provided, this context is used to find the plug-in,
+   * otherwise the Plugin Framework context is used.
+   *
+   * \param symbolicName The symbolic name of the plugin to stop.
+   * \param options The options used to stop the plugin.
+   * \param context The plugin context to use for finding the plugin.
+   * \return <code>true</code> if the plugin was found and successfully
+   *         stopped or the complete framework was successfully stopped,
+   *         <code>false</code> otherwise.
+   *
+   * \see ctkPlugin::StopOptions
+   */
+  static bool stop(const QString& symbolicName = QString(),
+                    ctkPlugin::StopOptions options = 0, ctkPluginContext* context = 0);
+
+  /**
    * Get the plugin context for the Plugin Framework.
    *
    * \return The context associated to the Plugin Framework, or <code>null</code>

+ 1 - 1
Libs/PluginFramework/ctkPluginFrameworkListeners.cpp

@@ -61,7 +61,7 @@ void ctkPluginFrameworkListeners::addServiceSlot(
   serviceSet.insert(sse);
   checkSimple(sse);
 
-  connect(receiver, SIGNAL(destroyed(QObject*)), this, SLOT(serviceListenerDestroyed(QObject*)));
+  connect(receiver, SIGNAL(destroyed(QObject*)), this, SLOT(serviceListenerDestroyed(QObject*)), Qt::DirectConnection);
 }
 
 //----------------------------------------------------------------------------

+ 158 - 1
Libs/PluginFramework/ctkPluginFrameworkPrivate.cpp

@@ -26,13 +26,17 @@
 #include "ctkPluginContext.h"
 #include "ctkPluginContext_p.h"
 #include "ctkPluginFrameworkContext_p.h"
+#include "ctkPluginFrameworkUtil_p.h"
+
+#include <QtConcurrentRun>
 
 //----------------------------------------------------------------------------
 ctkPluginFrameworkPrivate::ctkPluginFrameworkPrivate(QWeakPointer<ctkPlugin> qq, ctkPluginFrameworkContext* fw)
   : ctkPluginPrivate(qq, fw, 0, ctkPluginConstants::SYSTEM_PLUGIN_LOCATION,
                      ctkPluginConstants::SYSTEM_PLUGIN_SYMBOLICNAME,
                      // TODO: read version from the manifest resource
-                     ctkVersion(0, 9, 0))
+                     ctkVersion(0, 9, 0)),
+    shuttingDown(0)
 {
   systemHeaders.insert(ctkPluginConstants::PLUGIN_SYMBOLICNAME, symbolicName);
   systemHeaders.insert(ctkPluginConstants::PLUGIN_NAME, location);
@@ -58,3 +62,156 @@ void ctkPluginFrameworkPrivate::uninitSystemPlugin()
   this->pluginContext->d_func()->invalidate();
 }
 
+//----------------------------------------------------------------------------
+void ctkPluginFrameworkPrivate::shutdown(bool restart)
+{
+  Locker sync(&lock);
+
+  bool wasActive = false;
+  switch (state)
+  {
+  case ctkPlugin::INSTALLED:
+  case ctkPlugin::RESOLVED:
+    shutdownDone_unlocked(false);
+    break;
+  case ctkPlugin::ACTIVE:
+    wasActive = true;
+    // Fall through
+  case ctkPlugin::STARTING:
+    if (shuttingDown.fetchAndAddOrdered(0) == 0)
+    {
+      try
+      {
+        const bool wa = wasActive;
+        shuttingDown.fetchAndStoreOrdered(1);
+        QtConcurrent::run(this, &ctkPluginFrameworkPrivate::shutdown0, restart, wa);
+      }
+      catch (const std::exception& e)
+      {
+        systemShuttingdownDone(ctkPluginFrameworkEvent(ctkPluginFrameworkEvent::ERROR, this->q_func(), e));
+      }
+    }
+    break;
+  case ctkPlugin::STOPPING:
+    // Shutdown already inprogress
+    break;
+  }
+}
+
+//----------------------------------------------------------------------------
+void ctkPluginFrameworkPrivate::shutdown0(bool restart, bool wasActive)
+{
+  try
+  {
+    {
+      Locker sync(&lock);
+      waitOnOperation(&lock, QString("Framework::") + (restart ? "update" : "stop"), true);
+      operation = DEACTIVATING;
+      state = ctkPlugin::STOPPING;
+    }
+
+    fwCtx->listeners.emitPluginChanged(
+        ctkPluginEvent(ctkPluginEvent::STOPPING, this->q_func()));
+
+    if (wasActive)
+    {
+      stopAllPlugins();
+    }
+
+    {
+      Locker sync(&lock);
+      fwCtx->uninit();
+      shuttingDown.fetchAndStoreOrdered(0);
+      shutdownDone_unlocked(restart);
+    }
+
+    if (restart)
+    {
+      if (wasActive)
+      {
+        q_func().toStrongRef()->start();
+      }
+      else
+      {
+        init();
+      }
+    }
+  }
+  catch (const std::exception& e)
+  {
+    shuttingDown.fetchAndStoreOrdered(0);
+    systemShuttingdownDone(ctkPluginFrameworkEvent(ctkPluginFrameworkEvent::ERROR, this->q_func(), e));
+  }
+}
+
+//----------------------------------------------------------------------------
+void ctkPluginFrameworkPrivate::shutdownDone_unlocked(bool restart)
+{
+  ctkPluginFrameworkEvent::Type t = restart ? ctkPluginFrameworkEvent::STOPPED_UPDATE : ctkPluginFrameworkEvent::STOPPED;
+  systemShuttingdownDone_unlocked(ctkPluginFrameworkEvent(t, this->q_func()));
+}
+
+//----------------------------------------------------------------------------
+void ctkPluginFrameworkPrivate::stopAllPlugins()
+{
+  // TODO start level
+//  if (fwCtx.startLevelController != null)
+//  {
+//    fwCtx.startLevelController.shutdown();
+//  }
+
+  // Stop all active plug-ins, in reverse plug-in ID order
+  // The list will be empty when the start level service is in use.
+  QList<QSharedPointer<ctkPlugin> > activePlugins = fwCtx->plugins->getActivePlugins();
+  qSort(activePlugins.begin(), activePlugins.end(), pluginIdLessThan);
+  QListIterator<QSharedPointer<ctkPlugin> > i(activePlugins);
+  i.toBack();
+  while(i.hasPrevious())
+  {
+    QSharedPointer<ctkPlugin> p = i.previous();
+    try
+    {
+      if (p->getState() & (ctkPlugin::ACTIVE | ctkPlugin::STARTING))
+      {
+        // Stop plugin without changing its autostart setting.
+        p->stop(ctkPlugin::STOP_TRANSIENT);
+      }
+    }
+    catch (const ctkPluginException& pe)
+    {
+      fwCtx->listeners.frameworkError(p, pe);
+    }
+  }
+
+  QList<QSharedPointer<ctkPlugin> > allPlugins = fwCtx->plugins->getPlugins();
+
+  // Set state to INSTALLED and purge any unrefreshed bundles
+  foreach (QSharedPointer<ctkPlugin> p, allPlugins)
+  {
+    if (p->getPluginId() != 0)
+    {
+      p->d_func()->setStateInstalled(false);
+      p->d_func()->purge();
+    }
+  }
+}
+
+//----------------------------------------------------------------------------
+void ctkPluginFrameworkPrivate::systemShuttingdownDone(const ctkPluginFrameworkEvent& fe)
+{
+  Locker sync(&lock);
+  systemShuttingdownDone_unlocked(fe);
+}
+
+//----------------------------------------------------------------------------
+void ctkPluginFrameworkPrivate::systemShuttingdownDone_unlocked(const ctkPluginFrameworkEvent& fe)
+{
+
+  if (state != ctkPlugin::INSTALLED)
+  {
+    state = ctkPlugin::RESOLVED;
+    operation.fetchAndStoreOrdered(IDLE);
+    lock.wakeAll();
+  }
+  stopEvent = fe;
+}

+ 75 - 2
Libs/PluginFramework/ctkPluginFrameworkPrivate_p.h

@@ -27,7 +27,6 @@
 
 #include <QMutex>
 
-
 class ctkPluginFrameworkContext;
 
 /**
@@ -37,7 +36,22 @@ class ctkPluginFrameworkPrivate : public ctkPluginPrivate
 {
 public:
 
-  QMutex lock;
+  /**
+   * The event to return to callers waiting in ctkPluginFramework::waitForStop()
+   * when the framework has been stopped.
+   */
+  ctkPluginFrameworkEvent stopEvent;
+
+  /**
+   * The flag indicating that the thread that performs shutdown of this
+   * framework instance is running.
+   */
+  QAtomicInt shuttingDown;
+
+  /**
+   * Lock object
+   */
+  LockObject lock;
 
   ctkPluginFrameworkPrivate(QWeakPointer<ctkPlugin> qq, ctkPluginFrameworkContext* fw);
 
@@ -47,8 +61,67 @@ public:
 
   void uninitSystemPlugin();
 
+  /**
+   * This method starts a thread that stop this Framework,
+   * stopping all started plug-ins.
+   *
+   * <p>If the framework is not started, this method does nothing.
+   * If the framework is started, this method will:
+   * <ol>
+   * <li>Set the state of the ctkPluginFrameworkContext to <i>inactive</i>.</li>
+   * <li>Suspended all started plug-ins as described in the
+   * {@link ctkPlugin#stop()} method except that the persistent
+   * state of the plug-in will continue to be started.
+   * Reports any exceptions that occur during stopping using
+   * <code>ctkPluginFramework</code>s.</li>
+   * <li>Disable event handling.</li>
+   * </ol></p>
+   *
+   */
+  void shutdown(bool restart);
+
   QHash<QString, QString> systemHeaders;
 
+private:
+
+  /**
+   * Stop this FrameworkContext, suspending all started contexts.
+   * This method suspends all started contexts so that they can be
+   * automatically restarted when this FrameworkContext is next launched.
+   *
+   * <p>If the framework is not started, this method does nothing.
+   * If the framework is started, this method will:
+   * <ol>
+   * <li>Set the state of the FrameworkContext to <i>inactive</i>.</li>
+   * <li>Stop all started bundles as described in the
+   * {@link Bundle#stop(int)} method except that the persistent
+   * state of the bundle will continue to be started.
+   * Reports any exceptions that occur during stopping using
+   * <code>FrameworkErrorEvents</code>.</li>
+   * <li>Disable event handling.</li>
+   * </ol>
+   * </p>
+   *
+   */
+  void shutdown0(bool restart, bool wasActive);
+
+  /**
+   * Tell system plugin shutdown finished.
+   */
+  void shutdownDone_unlocked(bool restart);
+
+  /**
+   * Stop and unresolve all plug-ins.
+   */
+  void stopAllPlugins();
+
+  /**
+   * Shutting down is done.
+   */
+  void systemShuttingdownDone(const ctkPluginFrameworkEvent& fe);
+
+  void systemShuttingdownDone_unlocked(const ctkPluginFrameworkEvent& fe);
+
 };
 
 

+ 4 - 0
Libs/PluginFramework/ctkPluginFrameworkUtil.cpp

@@ -410,3 +410,7 @@ bool ctkPluginFrameworkUtil::patSubstr(const QString& s, int si, const QString&
   }
 }
 
+bool pluginIdLessThan(const QSharedPointer<ctkPlugin>& p1, const QSharedPointer<ctkPlugin>& p2)
+{
+  return p1->getPluginId() < p2->getPluginId();
+}

+ 6 - 0
Libs/PluginFramework/ctkPluginFrameworkUtil_p.h

@@ -78,6 +78,12 @@ private:
    *
    */
   static bool patSubstr(const QString& s, int si, const QString& pat, int pi);
+
 };
 
+#include <QSharedPointer>
+class ctkPlugin;
+
+bool pluginIdLessThan(const QSharedPointer<ctkPlugin>& p1, const QSharedPointer<ctkPlugin>& p2);
+
 #endif // CTKPLUGINFRAMEWORKUTIL_P_H

+ 257 - 86
Libs/PluginFramework/ctkPluginPrivate.cpp

@@ -36,6 +36,48 @@
 const ctkPlugin::States ctkPluginPrivate::RESOLVED_FLAGS = ctkPlugin::RESOLVED | ctkPlugin::STARTING | ctkPlugin::ACTIVE | ctkPlugin::STOPPING;
 
 //----------------------------------------------------------------------------
+void ctkPluginPrivate::LockObject::lock()
+{
+  m_Lock.lock();
+}
+
+//----------------------------------------------------------------------------
+bool ctkPluginPrivate::LockObject::tryLock()
+{
+  return m_Lock.tryLock();
+}
+
+//----------------------------------------------------------------------------
+bool ctkPluginPrivate::LockObject::tryLock(int timeout)
+{
+  return m_Lock.tryLock(timeout);
+}
+
+//----------------------------------------------------------------------------
+void ctkPluginPrivate::LockObject::unlock()
+{
+  m_Lock.unlock();
+}
+
+//----------------------------------------------------------------------------
+bool ctkPluginPrivate::LockObject::wait(unsigned long time)
+{
+  return m_Condition.wait(&m_Lock, time);
+}
+
+//----------------------------------------------------------------------------
+void ctkPluginPrivate::LockObject::wakeAll()
+{
+  m_Condition.wakeAll();
+}
+
+//----------------------------------------------------------------------------
+void ctkPluginPrivate::LockObject::wakeOne()
+{
+  m_Condition.wakeOne();
+}
+
+//----------------------------------------------------------------------------
 ctkPluginPrivate::ctkPluginPrivate(
     QWeakPointer<ctkPlugin> qq,
     ctkPluginFrameworkContext* fw,
@@ -43,7 +85,7 @@ ctkPluginPrivate::ctkPluginPrivate(
       : q_ptr(qq), fwCtx(fw), id(pa->getPluginId()),
       location(pa->getPluginLocation().toString()), state(ctkPlugin::INSTALLED),
       archive(pa), pluginContext(0), pluginActivator(0), pluginLoader(pa->getLibLocation()),
-      eagerActivation(false), activating(false), deactivating(false)
+      resolveFailException(0), eagerActivation(false), wasStarted(false)
 {
   //TODO
   //checkCertificates(pa);
@@ -111,9 +153,9 @@ ctkPluginPrivate::ctkPluginPrivate(QWeakPointer<ctkPlugin> qq,
                                    ctkPluginFrameworkContext* fw,
                                    long id, const QString& loc, const QString& sym, const ctkVersion& ver)
                                      : q_ptr(qq), fwCtx(fw), id(id), location(loc), symbolicName(sym), version(ver),
-                                     state(ctkPlugin::INSTALLED), archive(0), pluginContext(0),
-                                     pluginActivator(0), eagerActivation(false), activating(false),
-                                     deactivating(false)
+                                       state(ctkPlugin::INSTALLED), archive(0), pluginContext(0),
+                                       pluginActivator(0), resolveFailException(0),
+                                       eagerActivation(false), wasStarted(false)
 {
   modified();
 }
@@ -132,21 +174,26 @@ ctkPlugin::State ctkPluginPrivate::getUpdatedState()
   {
     try
     {
+      // NYI, fix double locking
+      Locker sync(&operationLock);
       if (state == ctkPlugin::INSTALLED)
       {
         fwCtx->resolvePlugin(this);
         state = ctkPlugin::RESOLVED;
+        operation.fetchAndStoreOrdered(RESOLVING);
+        // TODO plugin threading
+        //bundleThread().bundleChanged(new BundleEvent(BundleEvent.RESOLVED, this));
         fwCtx->listeners.emitPluginChanged(ctkPluginEvent(ctkPluginEvent::RESOLVED, this->q_func()));
+        operation.fetchAndStoreOrdered(IDLE);
       }
-
     }
     catch (const ctkPluginException& pe)
     {
+      if (resolveFailException) delete resolveFailException;
+      resolveFailException = new ctkPluginException(pe);
       this->fwCtx->listeners.frameworkError(this->q_func(), pe);
-      throw;
     }
   }
-
   return state;
 }
 
@@ -157,6 +204,44 @@ QFileInfo ctkPluginPrivate::getDataRoot()
 }
 
 //----------------------------------------------------------------------------
+void ctkPluginPrivate::setStateInstalled(bool sendEvent)
+{
+  Locker sync(&operationLock);
+
+  // Make sure that the context is invalid
+  if (pluginContext != 0)
+  {
+    pluginContext->d_func()->invalidate();
+    pluginContext.reset();
+  }
+  state = ctkPlugin::INSTALLED;
+  if (sendEvent)
+  {
+    operation.fetchAndStoreOrdered(UNRESOLVING);
+    // TODO: plugin thread
+    //bundleThread().bundleChanged(new BundleEvent(BundleEvent.UNRESOLVED, this));
+    fwCtx->listeners.emitPluginChanged(ctkPluginEvent(ctkPluginEvent::UNRESOLVED, this->q_func()));
+  }
+  operation.fetchAndStoreOrdered(IDLE);
+}
+
+//----------------------------------------------------------------------------
+void ctkPluginPrivate::purge()
+{
+  if (state == ctkPlugin::UNINSTALLED)
+  {
+    fwCtx->plugins->remove(location);
+  }
+//  Vector fix = oldGenerations;
+//  if (fix != null) {
+//    oldGenerations = null;
+//    for (Iterator i = fix.iterator(); i.hasNext();) {
+//      ((BundleGeneration)i.next()).purge(true);
+//    }
+//  }
+}
+
+//----------------------------------------------------------------------------
 void ctkPluginPrivate::setAutostartSetting(const ctkPlugin::StartOptions& setting) {
   try
   {
@@ -241,19 +326,22 @@ void ctkPluginPrivate::checkManifestHeaders()
 //----------------------------------------------------------------------------
 void ctkPluginPrivate::finalizeActivation()
 {
+  Locker sync(&operationLock);
+
+  // 4: Resolve plugin (if needed)
   switch (getUpdatedState())
   {
   case ctkPlugin::INSTALLED:
-    // we shouldn't be here, getUpdatedState should have thrown
-    // an exception during resolving the plugin
-    throw ctkPluginException("Internal error: expected exception on plugin resolve not thrown!");
+    Q_ASSERT_X(resolveFailException != 0, Q_FUNC_INFO, "no resolveFailException");
+    throw ctkPluginException(*resolveFailException);
   case ctkPlugin::STARTING:
-    if (activating) return; // finalization already in progress.
+    if (operation.fetchAndAddOrdered(0) == ACTIVATING) return; // finalization already in progress.
     // Lazy activation; fall through to RESOLVED.
   case ctkPlugin::RESOLVED:
+  {
     //6:
     state = ctkPlugin::STARTING;
-    activating = true;
+    operation.fetchAndStoreOrdered(ACTIVATING);
     if (fwCtx->debug.lazy_activation)
     {
       qDebug() << "activating #" << this->id;
@@ -263,106 +351,161 @@ void ctkPluginPrivate::finalizeActivation()
     {
       pluginContext.reset(new ctkPluginContext(this));
     }
-    try
-    {
-      // start dependencies
-      startDependencies();
-      //TODO maybe call this in its own thread
-      start0();
-    }
-    catch (...)
+    // start dependencies
+    startDependencies();
+    //TODO plugin threading
+    //ctkRuntimeException* e = bundleThread().callStart0(this);
+    ctkRuntimeException* e = start0();
+    operation.fetchAndStoreOrdered(IDLE);
+    operationLock.wakeAll();
+    if (e)
     {
-      //8:
-      state = ctkPlugin::STOPPING;
-      // NYI, call outside lock
-      fwCtx->listeners.emitPluginChanged(ctkPluginEvent(ctkPluginEvent::STOPPING, this->q_func()));
-      removePluginResources();
-      pluginContext.reset();
-
-      state = ctkPlugin::RESOLVED;
-      // NYI, call outside lock
-      fwCtx->listeners.emitPluginChanged(ctkPluginEvent(ctkPluginEvent::STOPPED, this->q_func()));
-      activating = false;
-      throw;
+      ctkRuntimeException re(*e);
+      delete e;
+      throw re;
     }
-    activating = false;
     break;
-      case ctkPlugin::ACTIVE:
+  }
+  case ctkPlugin::ACTIVE:
     break;
-      case ctkPlugin::STOPPING:
+  case ctkPlugin::STOPPING:
     // This happens if start is called from inside the ctkPluginActivator::stop method.
     // Don't allow it.
     throw ctkPluginException("start called from ctkPluginActivator::stop",
                              ctkPluginException::ACTIVATOR_ERROR);
-      case ctkPlugin::UNINSTALLED:
-    throw std::logic_error("ctkPlugin is in UNINSTALLED state");
+  case ctkPlugin::UNINSTALLED:
+    throw ctkIllegalStateException("ctkPlugin is in UNINSTALLED state");
   }
 }
 
 //----------------------------------------------------------------------------
-void ctkPluginPrivate::stop0(bool wasStarted)
+const ctkRuntimeException* ctkPluginPrivate::stop0()
 {
-  //5:
+  wasStarted = state == ctkPlugin::ACTIVE;
+  // 5:
   state = ctkPlugin::STOPPING;
-  deactivating = true;
-  //6-13:
-  ctkPluginActivator* activator = 0;
-  const std::exception* savedException = 0;
-  try
+  operation.fetchAndStoreOrdered(DEACTIVATING);
+  // 6-13:
+  // TODO plugin threading
+  //const ctkRuntimeException* savedException = pluginThread().callStop1(this);
+  const ctkRuntimeException* savedException = stop1();
+  if (state != ctkPlugin::UNINSTALLED)
   {
-    //6:
-    fwCtx->listeners.emitPluginChanged(ctkPluginEvent(ctkPluginEvent::STOPPING, q_func()));
+    state = ctkPlugin::RESOLVED;
+    // TODO plugin threading
+    //bundleThread().bundleChanged(new BundleEvent(BundleEvent.STOPPED, this));
+    fwCtx->listeners.emitPluginChanged(ctkPluginEvent(ctkPluginEvent::STOPPED, this->q_func()));
 
-    //7:
-    if (wasStarted && pluginActivator)
+    operationLock.wakeAll();
+    operation.fetchAndStoreOrdered(IDLE);
+  }
+  return savedException;
+}
+
+//----------------------------------------------------------------------------
+const ctkRuntimeException* ctkPluginPrivate::stop1()
+{
+  ctkPluginActivator* activator = 0;
+  const ctkRuntimeException* res = 0;
+
+  //6:
+  fwCtx->listeners.emitPluginChanged(ctkPluginEvent(ctkPluginEvent::STOPPING, q_func()));
+
+  //7:
+  if (wasStarted && pluginActivator)
+  {
+    try
     {
-      try
-      {
-        pluginActivator->stop(pluginContext.data());
-      }
-      catch (const std::exception& e)
+      pluginActivator->stop(pluginContext.data());
+      if (state != ctkPlugin::STOPPING)
       {
-        savedException = new ctkPluginException("ctkPlugin::stop: PluginActivator stop failed",
-                                                ctkPluginException::ACTIVATOR_ERROR, &e);
+        if (state == ctkPlugin::UNINSTALLED)
+        {
+          return new ctkIllegalStateException("Plug-in is uninstalled");
+        }
+        else
+        {
+          return new ctkIllegalStateException("Plug-in changed state because of refresh during stop");
+        }
       }
-      if (state == ctkPlugin::UNINSTALLED)
-      {
-        throw std::logic_error("Plugin is uninstalled");
-      }
-      activator = pluginActivator;
-      pluginActivator = 0;
     }
+    catch (const std::exception& e)
+    {
+      res = new ctkPluginException("ctkPlugin::stop: PluginActivator stop failed",
+                                              ctkPluginException::ACTIVATOR_ERROR, &e);
+    }
+    activator = pluginActivator;
+    pluginActivator = 0;
+  }
 
-    // Call hooks after we've called PluginActivator::stop(), but before we've cleared all resources
-    // TODO service listener hooks
-    //fwCtx->listeners.serviceListeners.hooksBundleStopped(this);
-
+  if (operation.fetchAndAddOrdered(0) == DEACTIVATING)
+  {
+    // Call hooks after we've called PluginActivator::stop(), but before we've
+    // cleared all resources
     if (pluginContext)
     {
+      // TODO service listener hooks
+      //fwCtx->listeners.serviceListeners.hooksBundleStopped(this);
+
+      //8-10:
+      removePluginResources();
       pluginContext->d_func()->invalidate();
       pluginContext.reset();
     }
-    //8-10:
-    removePluginResources();
-  }
-  catch (const std::exception* exc)
-  {
-    savedException = exc;
   }
 
   delete activator;
 
-  if (state != ctkPlugin::UNINSTALLED)
-  {
-    state = ctkPlugin::RESOLVED;
-    deactivating = false;
-    //fwCtx.packages.notifyAll();
-    fwCtx->listeners.emitPluginChanged(ctkPluginEvent(ctkPluginEvent::STOPPED, q_func()));
-  }
+  return res;
+}
 
-  if (savedException)
+//----------------------------------------------------------------------------
+void ctkPluginPrivate::waitOnOperation(LockObject* lock, const QString& src, bool longWait)
+{
+  if (operation.fetchAndAddOrdered(0) != IDLE)
   {
-    throw savedException;
+    qint64 left = longWait ? 20000 : 500;
+    QDateTime waitUntil = QDateTime::currentDateTime().addMSecs(left);
+    do
+    {
+      lock->wait(left);
+      if (operation.fetchAndAddOrdered(0) == IDLE)
+      {
+        return;
+      }
+      left = QDateTime::currentDateTime().msecsTo(waitUntil);
+    } while (left > 0);
+
+    QString op;
+    switch (operation.fetchAndAddOrdered(0))
+    {
+    case IDLE:
+      // Should not happen!
+      return;
+    case ACTIVATING:
+      op = "start";
+      break;
+    case DEACTIVATING:
+      op = "stop";
+      break;
+    case RESOLVING:
+      op = "resolve";
+      break;
+    case UNINSTALLING:
+      op = "uninstall";
+      break;
+    case UNRESOLVING:
+      op = "unresolve";
+      break;
+    case UPDATING:
+      op = "update";
+      break;
+    default:
+      op = "unknown operation";
+      break;
+    }
+    throw ctkPluginException(src + " called during " + op + " of plug-in",
+                             ctkPluginException::STATECHANGE_ERROR);
   }
 }
 
@@ -426,14 +569,18 @@ void ctkPluginPrivate::startDependencies()
 }
 
 //----------------------------------------------------------------------------
-void ctkPluginPrivate::start0()
+ctkPluginException* ctkPluginPrivate::start0()
 {
+  ctkPluginException* res = 0;
+
   fwCtx->listeners.emitPluginChanged(ctkPluginEvent(ctkPluginEvent::STARTING, this->q_func()));
 
+  ctkPluginException::Type error_type = ctkPluginException::MANIFEST_ERROR;
   try {
     pluginLoader.load();
     if (!pluginLoader.isLoaded())
     {
+      error_type = ctkPluginException::ACTIVATOR_ERROR;
       throw ctkPluginException(QString("Loading plugin %1 failed: %2").arg(pluginLoader.fileName()).arg(pluginLoader.errorString()),
                                ctkPluginException::ACTIVATOR_ERROR);
     }
@@ -447,15 +594,23 @@ void ctkPluginPrivate::start0()
 
     pluginActivator->start(pluginContext.data());
 
-    if (ctkPlugin::UNINSTALLED == state)
+    if (state != ctkPlugin::STARTING)
     {
-      throw ctkPluginException("ctkPlugin uninstalled during start()", ctkPluginException::STATECHANGE_ERROR);
+      error_type = ctkPluginException::STATECHANGE_ERROR;
+      if (ctkPlugin::UNINSTALLED == state)
+      {
+        throw ctkPluginException("ctkPlugin uninstalled during start()", ctkPluginException::STATECHANGE_ERROR);
+      }
+      else
+      {
+        throw ctkPluginException("ctkPlugin changed state because of refresh during start()", ctkPluginException::STATECHANGE_ERROR);
+      }
     }
     state = ctkPlugin::ACTIVE;
   }
   catch (const std::exception& e)
   {
-    throw ctkPluginException("ctkPlugin start failed", ctkPluginException::ACTIVATOR_ERROR, &e);
+    res = new ctkPluginException("ctkPlugin start failed", error_type, &e);
   }
 
   if (fwCtx->debug.lazy_activation)
@@ -463,8 +618,24 @@ void ctkPluginPrivate::start0()
     qDebug() << "activating #" << id << "completed.";
   }
 
-  //10:
-  fwCtx->listeners.emitPluginChanged(ctkPluginEvent(ctkPluginEvent::STARTED, this->q_func()));
+  if (res == 0)
+  {
+    //10:
+    fwCtx->listeners.emitPluginChanged(ctkPluginEvent(ctkPluginEvent::STARTED, this->q_func()));
+  }
+  else if (operation.fetchAndAddOrdered(0) == ACTIVATING)
+  {
+    // 8:
+    state = ctkPlugin::STOPPING;
+    fwCtx->listeners.emitPluginChanged(ctkPluginEvent(ctkPluginEvent::STOPPING, this->q_func()));
+    removePluginResources();
+    pluginContext->d_func()->invalidate();
+    pluginContext.reset();
+    state = ctkPlugin::RESOLVED;
+    fwCtx->listeners.emitPluginChanged(ctkPluginEvent(ctkPluginEvent::STOPPED, this->q_func()));
+  }
+
+  return res;
 }
 
 //----------------------------------------------------------------------------

+ 82 - 12
Libs/PluginFramework/ctkPluginPrivate_p.h

@@ -30,6 +30,8 @@
 #include <QPluginLoader>
 #include <QDateTime>
 #include <QFileInfo>
+#include <QMutex>
+#include <QWaitCondition>
 
 
 class ctkPluginActivator;
@@ -45,12 +47,43 @@ protected:
 
   const QWeakPointer<ctkPlugin> q_ptr;
 
+  class LockObject
+  {
+  public:
+
+    LockObject() {};
+
+    void lock();
+    bool tryLock();
+    bool tryLock(int timeout);
+    void unlock();
+
+    bool wait(unsigned long time = ULONG_MAX);
+    void wakeAll();
+    void wakeOne();
+
+  private:
+
+    Q_DISABLE_COPY(LockObject)
+
+    QMutex m_Lock;
+    QWaitCondition m_Condition;
+  };
+
 public:
 
   inline QWeakPointer<ctkPlugin> q_func() { return q_ptr; }
   inline QWeakPointer<const ctkPlugin> q_func() const { return q_ptr; }
   friend class ctkPlugin;
 
+  struct Locker
+  {
+    Locker(LockObject* obj) : m_Obj(obj) { m_Obj->lock(); }
+    ~Locker() { m_Obj->unlock(); }
+  private:
+      LockObject* m_Obj;
+  };
+
   /**
    * Construct a new plugin based on a ctkPluginArchive.
    *
@@ -94,6 +127,17 @@ public:
   QFileInfo getDataRoot();
 
   /**
+   * Set state to INSTALLED. We assume that the plug-in is resolved
+   * when entering this method.
+   */
+  void setStateInstalled(bool sendEvent);
+
+  /**
+   * Purge any old files associated with this plug-in.
+   */
+  void purge();
+
+  /**
    * Save the autostart setting to the persistent plugin storage.
    *
    * @param setting The autostart options to save.
@@ -109,10 +153,24 @@ public:
    */
   void finalizeActivation();
 
+  const ctkRuntimeException* stop0();
+
   /**
-   * Performs the actual stopping.
+   * Stop code that is executed in the pluginThread without holding the
+   * operationLock.
    */
-  void stop0(bool wasStarted);
+  const ctkRuntimeException* stop1();
+
+  /**
+   * Wait for an ongoing operation to finish.
+   *
+   * @param lock QMutex used for locking.
+   * @param src Caller to include in exception message.
+   * @param longWait True, if we should wait extra long before aborting.
+   * @throws ctkPluginException if the ongoing (de-)activation does not finish
+   *         within reasonable time.
+   */
+  void waitOnOperation(LockObject* lock, const QString& src, bool longWait);
 
   /**
    *
@@ -193,25 +251,37 @@ public:
   QHash<QString, QString> cachedRawHeaders;
 
   /**
-   * True when this plugin has its activation policy
-   * set to "eager"
+   * Type of operation in progress. Blocks bundle calls during activator and
+   * listener calls
    */
-  bool eagerActivation;
+  QAtomicInt operation;
+  static const int IDLE = 0;
+  static const int ACTIVATING = 1;
+  static const int DEACTIVATING = 2;
+  static const int RESOLVING = 3;
+  static const int UNINSTALLING = 4;
+  static const int UNRESOLVING = 5;
+  static const int UPDATING = 6;
 
-  /** True during the finalization of an activation. */
-  bool activating;
+  LockObject operationLock;
 
-  /** True during the state change from active to resolved. */
-  bool deactivating;
+  /** Saved exception of resolve failure. */
+  ctkPluginException* resolveFailException;
 
-  /** Saved exception of resolve failure */
-  //ctkPluginException resolveFailException;
+  /**
+   * True when this plugin has its activation policy
+   * set to "eager"
+   */
+  bool eagerActivation;
 
   /** List of ctkRequirePlugin entries. */
   QList<ctkRequirePlugin*> require;
 
 private:
 
+  /** Rember if plugin was started */
+  bool wasStarted;
+
   /**
    * Check manifest and cache certain manifest headers as variables.
    */
@@ -219,7 +289,7 @@ private:
 
   // This could potentially be run in its own thread,
   // parallelizing plugin activations
-  void start0();
+  ctkPluginException* start0();
 
   void startDependencies();
 

+ 1 - 1
Libs/PluginFramework/ctkPluginTracker.h

@@ -102,7 +102,7 @@ public:
    * <code>ctkPluginTracker</code> was created are now tracked by this
    * <code>ctkPluginTracker</code>.
    *
-   * @throws std::logic_error If the <code>ctkPluginContext</code>
+   * @throws ctkIllegalStateException If the <code>ctkPluginContext</code>
    *         with which this <code>ctkPluginTracker</code> was created is no
    *         longer valid.
    */

+ 19 - 31
Libs/PluginFramework/ctkPlugins.cpp

@@ -33,6 +33,15 @@
 #include <QUrl>
 
 //----------------------------------------------------------------------------
+void ctkPlugins::checkIllegalState() const
+{
+  if (!fwCtx)
+  {
+    throw ctkIllegalStateException("This framework instance is not active");
+  }
+}
+
+//----------------------------------------------------------------------------
 ctkPlugins::ctkPlugins(ctkPluginFrameworkContext* fw)
 {
   fwCtx = fw;
@@ -50,10 +59,7 @@ void ctkPlugins::clear()
 //----------------------------------------------------------------------------
 QSharedPointer<ctkPlugin> ctkPlugins::install(const QUrl& location, QIODevice* in)
 {
-  if (!fwCtx)
-  { // This ctkPlugins instance has been closed!
-    throw std::logic_error("ctkPlugins::install(location, inputStream) called on closed plugins object.");
-  }
+  checkIllegalState();
 
   QSharedPointer<ctkPlugin> res;
   {
@@ -148,10 +154,7 @@ void ctkPlugins::remove(const QUrl& location)
 //----------------------------------------------------------------------------
 QSharedPointer<ctkPlugin> ctkPlugins::getPlugin(int id) const
 {
-  if (!fwCtx)
-  { // This plugins instance has been closed!
-    throw std::logic_error("ctkPlugins::getPlugin(id) called on closed plugins object.");
-  }
+  checkIllegalState();
 
   {
     QReadLocker lock(&pluginsLock);
@@ -172,10 +175,7 @@ QSharedPointer<ctkPlugin> ctkPlugins::getPlugin(int id) const
 //----------------------------------------------------------------------------
 QSharedPointer<ctkPlugin> ctkPlugins::getPlugin(const QString& location) const
 {
-  if (!fwCtx)
-  { // This plugins instance has been closed!
-    throw std::logic_error("ctkPlugins::getPlugin(location) called on closed plugins object.");
-  }
+  checkIllegalState();
 
   QReadLocker lock(&pluginsLock);
   QHash<QString, QSharedPointer<ctkPlugin> >::const_iterator it = plugins.find(location);
@@ -186,10 +186,7 @@ QSharedPointer<ctkPlugin> ctkPlugins::getPlugin(const QString& location) const
 //----------------------------------------------------------------------------
 QSharedPointer<ctkPlugin> ctkPlugins::getPlugin(const QString& name, const ctkVersion& version) const
 {
-  if (!fwCtx)
-  { // This ctkPlugins instance has been closed!
-    throw std::logic_error("ctkPlugins::getPlugin(name, version) called on closed plugins object.");
-  }
+  checkIllegalState();
 
   {
     QReadLocker lock(&pluginsLock);
@@ -210,10 +207,7 @@ QSharedPointer<ctkPlugin> ctkPlugins::getPlugin(const QString& name, const ctkVe
 //----------------------------------------------------------------------------
 QList<QSharedPointer<ctkPlugin> > ctkPlugins::getPlugins() const
 {
-  if (!fwCtx)
-  { // This plugins instance has been closed!
-    throw std::logic_error("ctkPlugins::getPlugins() called on closed plugins object.");
-  }
+  checkIllegalState();
 
   {
     QReadLocker lock(&pluginsLock);
@@ -245,10 +239,7 @@ QList<ctkPlugin*> ctkPlugins::getPlugins(const QString& name) const
 //----------------------------------------------------------------------------
 QList<ctkPlugin*> ctkPlugins::getPlugins(const QString& name, const ctkVersionRange& range) const
 {
-  if (!fwCtx)
-  { // This plugins instance has been closed!
-    throw std::logic_error("ctkPlugins::getPlugins(name, versionRange) called on closed plugins object.");
-  }
+  checkIllegalState();
 
   QList<ctkPlugin*> pluginsWithName = getPlugins(name);
   QList<ctkPlugin*> res;
@@ -274,14 +265,11 @@ QList<ctkPlugin*> ctkPlugins::getPlugins(const QString& name, const ctkVersionRa
 }
 
 //----------------------------------------------------------------------------
-QList<ctkPlugin*> ctkPlugins::getActivePlugins() const
+QList<QSharedPointer<ctkPlugin> > ctkPlugins::getActivePlugins() const
 {
-  if (!fwCtx)
-  { // This plugins instance has been closed!
-    throw std::logic_error("ctkPlugins::getActivePlugins() called on closed plugins object.");
-  }
+  checkIllegalState();
 
-  QList<ctkPlugin*> slist;
+  QList<QSharedPointer<ctkPlugin> > slist;
   {
     QReadLocker lock(&pluginsLock);
     QHashIterator<QString, QSharedPointer<ctkPlugin> > it(plugins);
@@ -290,7 +278,7 @@ QList<ctkPlugin*> ctkPlugins::getActivePlugins() const
       QSharedPointer<ctkPlugin> plugin = it.next().value();
       ctkPlugin::State s = plugin->getState();
       if (s == ctkPlugin::ACTIVE || s == ctkPlugin::STARTING) {
-        slist.push_back(plugin.data());
+        slist.push_back(plugin);
       }
     }
   }

+ 3 - 1
Libs/PluginFramework/ctkPlugins_p.h

@@ -67,6 +67,8 @@ private:
    */
   QMutex objectLock;
 
+  void checkIllegalState() const;
+
 public:
 
   /**
@@ -157,7 +159,7 @@ public:
    *
    * @return A List of ctkPlugins.
    */
-  QList<ctkPlugin*> getActivePlugins() const;
+  QList<QSharedPointer<ctkPlugin> > getActivePlugins() const;
 
 
   /**

+ 10 - 0
Libs/PluginFramework/ctkRuntimeException.cpp

@@ -22,6 +22,8 @@
 
 #include "ctkRuntimeException.h"
 
+#include <QDebug>
+
 //----------------------------------------------------------------------------
 ctkRuntimeException::ctkRuntimeException(const QString& msg, const std::exception* cause)
   : std::runtime_error(msg.toStdString())
@@ -71,3 +73,11 @@ const char* ctkRuntimeException::what() const throw()
 
   return fullMsg.c_str();
 }
+
+//----------------------------------------------------------------------------
+QDebug operator<<(QDebug dbg, const ctkRuntimeException& exc)
+{
+  dbg << "ctkRuntimeException:" << exc.what();
+
+  return dbg.maybeSpace();
+}

+ 10 - 0
Libs/PluginFramework/ctkRuntimeException.h

@@ -53,4 +53,14 @@ private:
   QString  cause;
 };
 
+/**
+ * \ingroup PluginFramework
+ */
+typedef ctkRuntimeException ctkIllegalStateException;
+
+/**
+ * \ingroup PluginFramework
+ */
+CTK_PLUGINFW_EXPORT QDebug operator<<(QDebug dbg, const ctkRuntimeException& exc);
+
 #endif // CTKRUNTIMEEXCEPTION_H

+ 6 - 6
Libs/PluginFramework/ctkServiceRegistration.cpp

@@ -94,8 +94,8 @@ ctkServiceReference ctkServiceRegistration::getReference() const
 {
   Q_D(const ctkServiceRegistration);
 
-  if (!d) throw std::logic_error("ctkServiceRegistration object invalid");
-  if (!d->available) throw std::logic_error("Service is unregistered");
+  if (!d) throw ctkIllegalStateException("ctkServiceRegistration object invalid");
+  if (!d->available) throw ctkIllegalStateException("Service is unregistered");
 
   return d->reference;
 }
@@ -104,7 +104,7 @@ ctkServiceReference ctkServiceRegistration::getReference() const
 void ctkServiceRegistration::setProperties(const ctkDictionary& props)
 {
   Q_D(ctkServiceRegistration);
-  if (!d) throw std::logic_error("ctkServiceRegistration object invalid");
+  if (!d) throw ctkIllegalStateException("ctkServiceRegistration object invalid");
 
   QMutexLocker lock(&d->eventLock);
 
@@ -130,7 +130,7 @@ void ctkServiceRegistration::setProperties(const ctkDictionary& props)
     }
     else
     {
-      throw std::logic_error("Service is unregistered");
+      throw ctkIllegalStateException("Service is unregistered");
     }
   }
   d->plugin->fwCtx->listeners.serviceChanged(
@@ -146,7 +146,7 @@ void ctkServiceRegistration::setProperties(const ctkDictionary& props)
 void ctkServiceRegistration::unregister()
 {
   Q_D(ctkServiceRegistration);
-  if (!d) throw std::logic_error("ctkServiceRegistration object invalid");
+  if (!d) throw ctkIllegalStateException("ctkServiceRegistration object invalid");
 
   if (d->unregistering) return; // Silently ignore redundant unregistration.
   {
@@ -163,7 +163,7 @@ void ctkServiceRegistration::unregister()
     }
     else
     {
-      throw std::logic_error("Service is unregistered");
+      throw ctkIllegalStateException("Service is unregistered");
     }
   }
 

+ 3 - 3
Libs/PluginFramework/ctkServiceRegistration.h

@@ -79,7 +79,7 @@ public:
    * The <code>ctkServiceReference</code> object may be shared with other
    * plugins.
    *
-   * @throws std::logic_error If this
+   * @throws ctkIllegalStateException If this
    *         <code>ctkServiceRegistration</code> object has already been
    *         unregistered or if it is invalid.
    * @return <code>ctkServiceReference</code> object.
@@ -106,7 +106,7 @@ public:
    *        be made to this object after calling this method. To update the
    *        service's properties this method should be called again.
    *
-   * @throws std::logic_error If this <code>ctkServiceRegistration</code>
+   * @throws ctkIllegalStateException If this <code>ctkServiceRegistration</code>
    *         object has already been unregistered or if it is invalid.
    * @throws std::invalid_argument If <code>properties</code> contains
    *         case variants of the same key name.
@@ -138,7 +138,7 @@ public:
    * the service object for the plugin.
    * </ol>
    *
-   * @throws std::logic_error If this
+   * @throws ctkIllegalStateException If this
    *         <code>ctkServiceRegistration</code> object has already been
    *         unregistered or if it is invalid.
    * @see ctkPluginContext#ungetService

+ 1 - 1
Libs/PluginFramework/ctkServiceTracker.h

@@ -171,7 +171,7 @@ public:
    * <code>ctkServiceTracker</code> was created are now tracked by this
    * <code>ctkServiceTracker</code>.
    *
-   * @throws std::logic_error If the <code>ctkPluginContext</code>
+   * @throws ctkIllegalStateException If the <code>ctkPluginContext</code>
    *         with which this <code>ctkServiceTracker</code> was created is no
    *         longer valid.
    */

+ 1 - 1
Libs/PluginFramework/service/cm/ctkConfigurationEvent.cpp

@@ -92,7 +92,7 @@ ctkConfigurationEvent::ctkConfigurationEvent(const ctkServiceReference& referenc
 {
   if (pid.isNull())
   {
-    throw std::logic_error("pid must not be null");
+    throw std::invalid_argument("pid must not be null");
   }
 }