Pārlūkot izejas kodu

Added timeout handling for command line modules.

Sascha Zelzer 11 gadi atpakaļ
vecāks
revīzija
888a64c730
26 mainītis faili ar 317 papildinājumiem un 53 dzēšanām
  1. 1 0
      Applications/ctkCommandLineModuleExplorer/ctkCmdLineModuleExplorerConstants.cpp
  2. 1 0
      Applications/ctkCommandLineModuleExplorer/ctkCmdLineModuleExplorerConstants.h
  3. 8 1
      Applications/ctkCommandLineModuleExplorer/ctkCmdLineModuleExplorerGeneralModuleSettings.cpp
  4. 6 1
      Applications/ctkCommandLineModuleExplorer/ctkCmdLineModuleExplorerGeneralModuleSettings.h
  5. 30 10
      Applications/ctkCommandLineModuleExplorer/ctkCmdLineModuleExplorerGeneralModuleSettings.ui
  6. 6 5
      Applications/ctkCommandLineModuleExplorer/ctkCmdLineModuleExplorerMainWindow.cpp
  7. 6 2
      Applications/ctkCommandLineModuleExplorer/ctkCmdLineModuleExplorerModulesSettings.cpp
  8. 23 11
      Applications/ctkCommandLineModuleExplorer/ctkCmdLineModuleExplorerUtils.cpp
  9. 2 2
      Applications/ctkCommandLineModuleExplorer/ctkCmdLineModuleExplorerUtils.h
  10. 1 1
      Libs/CommandLineModules/Backend/FunctionPointer/ctkCmdLineModuleBackendFunctionPointer.cpp
  11. 1 1
      Libs/CommandLineModules/Backend/FunctionPointer/ctkCmdLineModuleBackendFunctionPointer.h
  12. 12 4
      Libs/CommandLineModules/Backend/LocalProcess/ctkCmdLineModuleBackendLocalProcess.cpp
  13. 1 1
      Libs/CommandLineModules/Backend/LocalProcess/ctkCmdLineModuleBackendLocalProcess.h
  14. 1 0
      Libs/CommandLineModules/Core/CMakeLists.txt
  15. 1 1
      Libs/CommandLineModules/Core/Testing/Cpp/ctkCmdLineModuleManagerTest.cpp
  16. 8 0
      Libs/CommandLineModules/Core/ctkCmdLineModuleBackend.cpp
  17. 14 2
      Libs/CommandLineModules/Core/ctkCmdLineModuleBackend.h
  18. 13 3
      Libs/CommandLineModules/Core/ctkCmdLineModuleConcurrentHelpers.cpp
  19. 1 1
      Libs/CommandLineModules/Core/ctkCmdLineModuleFrontend.h
  20. 23 3
      Libs/CommandLineModules/Core/ctkCmdLineModuleManager.cpp
  21. 18 0
      Libs/CommandLineModules/Core/ctkCmdLineModuleManager.h
  22. 11 0
      Libs/CommandLineModules/Core/ctkCmdLineModuleRunException.cpp
  23. 3 3
      Libs/CommandLineModules/Core/ctkCmdLineModuleRunException.h
  24. 70 0
      Libs/CommandLineModules/Core/ctkCmdLineModuleTimeoutException.cpp
  25. 55 0
      Libs/CommandLineModules/Core/ctkCmdLineModuleTimeoutException.h
  26. 1 1
      Libs/CommandLineModules/Frontend/QtGui/Testing/Cpp/ctkCmdLineModuleFrontendQtGuiTest.cpp

+ 1 - 0
Applications/ctkCommandLineModuleExplorer/ctkCmdLineModuleExplorerConstants.cpp

@@ -25,3 +25,4 @@ const QString ctkCmdLineModuleExplorerConstants::KEY_SEARCH_PATHS = "ModuleSearc
 const QString ctkCmdLineModuleExplorerConstants::KEY_REGISTERED_MODULES = "RegisteredModules";
 
 const QString ctkCmdLineModuleExplorerConstants::KEY_MAX_PARALLEL_MODULES = "MaxParallelModules";
+const QString ctkCmdLineModuleExplorerConstants::KEY_XML_TIMEOUT_SECONDS = "XmlTimeoutSeconds";

+ 1 - 0
Applications/ctkCommandLineModuleExplorer/ctkCmdLineModuleExplorerConstants.h

@@ -30,6 +30,7 @@ struct ctkCmdLineModuleExplorerConstants
   static const QString KEY_REGISTERED_MODULES;
 
   static const QString KEY_MAX_PARALLEL_MODULES;
+  static const QString KEY_XML_TIMEOUT_SECONDS;
 };
 
 #endif // CTKCMDLINEMODULEEXPLORERCONSTANTS_H

+ 8 - 1
Applications/ctkCommandLineModuleExplorer/ctkCmdLineModuleExplorerGeneralModuleSettings.cpp

@@ -20,21 +20,28 @@
 =============================================================================*/
 
 #include "ctkCmdLineModuleExplorerGeneralModuleSettings.h"
+#include "ctkCmdLineModuleManager.h"
 #include "ctkCmdLineModuleExplorerConstants.h"
 
 #include <QThreadPool>
 #include <QSettings>
 
-ctkCmdLineModuleExplorerGeneralModuleSettings::ctkCmdLineModuleExplorerGeneralModuleSettings()
+ctkCmdLineModuleExplorerGeneralModuleSettings::ctkCmdLineModuleExplorerGeneralModuleSettings(ctkCmdLineModuleManager* cmdLineModuleManager)
+  : CmdLineModuleManager(cmdLineModuleManager)
 {
   this->setupUi(this);
 
   this->registerProperty(ctkCmdLineModuleExplorerConstants::KEY_MAX_PARALLEL_MODULES,
                          this->MaxParallelModules, "value", SIGNAL(valueChanged(int)));
+  this->registerProperty(ctkCmdLineModuleExplorerConstants::KEY_XML_TIMEOUT_SECONDS,
+                         this->XmlTimeout, "value", SIGNAL(valueChanged(int)));
 }
 
 void ctkCmdLineModuleExplorerGeneralModuleSettings::applySettings()
 {
   int maxParallelModules = this->propertyValue(ctkCmdLineModuleExplorerConstants::KEY_MAX_PARALLEL_MODULES).toInt();
   QThreadPool::globalInstance()->setMaxThreadCount(maxParallelModules);
+
+  int timeout = this->propertyValue(ctkCmdLineModuleExplorerConstants::KEY_XML_TIMEOUT_SECONDS).toInt();
+  this->CmdLineModuleManager->setXmlTimeout(timeout*1000);
 }

+ 6 - 1
Applications/ctkCommandLineModuleExplorer/ctkCmdLineModuleExplorerGeneralModuleSettings.h

@@ -26,6 +26,8 @@
 
 #include "ui_ctkCmdLineModuleExplorerGeneralModuleSettings.h"
 
+class ctkCmdLineModuleManager;
+
 class ctkCmdLineModuleExplorerGeneralModuleSettings : public ctkSettingsPanel,
     public Ui::ctkCmdLineModuleExplorerGeneralModuleSettings
 {
@@ -33,10 +35,13 @@ class ctkCmdLineModuleExplorerGeneralModuleSettings : public ctkSettingsPanel,
 
 public:
 
-  ctkCmdLineModuleExplorerGeneralModuleSettings();
+  ctkCmdLineModuleExplorerGeneralModuleSettings(ctkCmdLineModuleManager* cmdLineModuleManager);
 
   void applySettings();
 
+private:
+
+  ctkCmdLineModuleManager* CmdLineModuleManager;
 };
 
 #endif // CTKCMDLINEMODULEEXPLORERGENERALMODULESETTINGS_H

+ 30 - 10
Applications/ctkCommandLineModuleExplorer/ctkCmdLineModuleExplorerGeneralModuleSettings.ui

@@ -32,15 +32,15 @@
       <string>Run Settings</string>
      </property>
      <layout class="QGridLayout" name="gridLayout">
-      <item row="1" column="0">
-       <spacer name="verticalSpacer">
+      <item row="0" column="2">
+       <spacer name="horizontalSpacer">
         <property name="orientation">
-         <enum>Qt::Vertical</enum>
+         <enum>Qt::Horizontal</enum>
         </property>
         <property name="sizeHint" stdset="0">
          <size>
-          <width>20</width>
-          <height>40</height>
+          <width>40</width>
+          <height>20</height>
          </size>
         </property>
        </spacer>
@@ -74,19 +74,39 @@
         </property>
        </widget>
       </item>
-      <item row="0" column="2">
-       <spacer name="horizontalSpacer">
+      <item row="2" column="0">
+       <spacer name="verticalSpacer">
         <property name="orientation">
-         <enum>Qt::Horizontal</enum>
+         <enum>Qt::Vertical</enum>
         </property>
         <property name="sizeHint" stdset="0">
          <size>
-          <width>40</width>
-          <height>20</height>
+          <width>20</width>
+          <height>40</height>
          </size>
         </property>
        </spacer>
       </item>
+      <item row="1" column="0">
+       <widget class="QLabel" name="label_2">
+        <property name="text">
+         <string>XML retrieval timeout (in seconds):</string>
+        </property>
+       </widget>
+      </item>
+      <item row="1" column="1">
+       <widget class="QSpinBox" name="XmlTimeout">
+        <property name="minimum">
+         <number>-1</number>
+        </property>
+        <property name="maximum">
+         <number>999999999</number>
+        </property>
+        <property name="value">
+         <number>30</number>
+        </property>
+       </widget>
+      </item>
      </layout>
     </widget>
    </item>

+ 6 - 5
Applications/ctkCommandLineModuleExplorer/ctkCmdLineModuleExplorerMainWindow.cpp

@@ -123,8 +123,8 @@ ctkCLModuleExplorerMainWindow::ctkCLModuleExplorerMainWindow(QWidget *parent) :
   }
 
   // Register persistent modules
-  QFuture<void> future = QtConcurrent::mapped(settings.value(ctkCmdLineModuleExplorerConstants::KEY_REGISTERED_MODULES).toStringList(),
-                                              ctkCmdLineModuleConcurrentRegister(&moduleManager, true));
+  QFuture<ctkCmdLineModuleReference> future = QtConcurrent::mapped(settings.value(ctkCmdLineModuleExplorerConstants::KEY_REGISTERED_MODULES).toStringList(),
+                                                                   ctkCmdLineModuleConcurrentRegister(&moduleManager, true));
 
   // Start watching directories
   directoryWatcher.setDebug(true);
@@ -134,7 +134,8 @@ ctkCLModuleExplorerMainWindow::ctkCLModuleExplorerMainWindow(QWidget *parent) :
 
   pollPauseTimer.start();
 
-  future.waitForFinished();
+  //ctkCmdLineModuleExplorerUtils::messageBoxModuleRegistration(future,
+  //                                                            moduleManager.validationMode());
 }
 
 ctkCLModuleExplorerMainWindow::~ctkCLModuleExplorerMainWindow()
@@ -235,7 +236,7 @@ void ctkCLModuleExplorerMainWindow::on_actionOptions_triggered()
     settingsDialog = new ctkSettingsDialog(this);
     settings.restoreState(settingsDialog->objectName(), *settingsDialog);
     settingsDialog->setSettings(&settings);
-    ctkSettingsPanel* generalModulePanel = new ctkCmdLineModuleExplorerGeneralModuleSettings();
+    ctkSettingsPanel* generalModulePanel = new ctkCmdLineModuleExplorerGeneralModuleSettings(&moduleManager);
     settingsDialog->addPanel(generalModulePanel);
     settingsDialog->addPanel(new ctkCmdLineModuleExplorerDirectorySettings(&directoryWatcher), generalModulePanel);
     settingsDialog->addPanel(new ctkCmdLineModuleExplorerModulesSettings(&moduleManager), generalModulePanel);
@@ -253,7 +254,7 @@ void ctkCLModuleExplorerMainWindow::on_actionLoad_triggered()
   future.waitForFinished();
   this->unsetCursor();
 
-  ctkCmdLineModuleExplorerUtils::messageBoxModuleRegistration(fileNames, future.results(),
+  ctkCmdLineModuleExplorerUtils::messageBoxModuleRegistration(future,
                                                               this->moduleManager.validationMode());
 }
 

+ 6 - 2
Applications/ctkCommandLineModuleExplorer/ctkCmdLineModuleExplorerModulesSettings.cpp

@@ -83,14 +83,18 @@ void ctkCmdLineModuleExplorerModulesSettings::applySettings()
   this->setCursor(Qt::BusyCursor);
 
   QFuture<void> future1 = QtConcurrent::mapped(removedModules, ctkCmdLineModuleConcurrentUnRegister(this->ModuleManager));
-  QFuture<ctkCmdLineModuleReference> future2 = QtConcurrent::mapped(addedModules, ctkCmdLineModuleConcurrentRegister(this->ModuleManager));
+  QFuture<ctkCmdLineModuleReference> future2 = QtConcurrent::mapped(addedModules, ctkCmdLineModuleConcurrentRegister(this->ModuleManager, true));
 
   ctkSettingsPanel::applySettings();
 
+  future1.waitForFinished();
+
+  /*
   QFutureSynchronizer<void> sync;
   sync.addFuture(future1);
   sync.addFuture(future2);
   sync.waitForFinished();
+*/
 
   this->ModulesRegistered = true;
   this->pathsAdded(addedModules);
@@ -98,7 +102,7 @@ void ctkCmdLineModuleExplorerModulesSettings::applySettings()
 
   this->unsetCursor();
 
-  ctkCmdLineModuleExplorerUtils::messageBoxModuleRegistration(addedModules, future2.results(),
+  ctkCmdLineModuleExplorerUtils::messageBoxModuleRegistration(future2,
                                                               this->ModuleManager->validationMode());
 
 }

+ 23 - 11
Applications/ctkCommandLineModuleExplorer/ctkCmdLineModuleExplorerUtils.cpp

@@ -20,6 +20,7 @@
 =============================================================================*/
 
 #include "ctkCmdLineModuleExplorerUtils.h"
+#include "ctkCmdLineModuleRunException.h"
 
 #include <QPainter>
 #include <QObject>
@@ -39,23 +40,34 @@ QPixmap ctkCmdLineModuleExplorerUtils::createIconOverlay(const QPixmap &base, co
   return result;
 }
 
-void ctkCmdLineModuleExplorerUtils:: messageBoxModuleRegistration(const QStringList& modulePaths,
-                                                                 const QList<ctkCmdLineModuleReference>& moduleRefs,
-                                                                 ctkCmdLineModuleManager::ValidationMode validationMode)
+void ctkCmdLineModuleExplorerUtils::messageBoxModuleRegistration(const QFuture<ctkCmdLineModuleReference>& moduleRefsFuture,
+                                                                  ctkCmdLineModuleManager::ValidationMode validationMode)
 {
-  Q_ASSERT(modulePaths.size() == moduleRefs.size());
-
   QString errorMsg;
-  for(int i = 0; i < modulePaths.size(); ++i)
+  QFutureIterator<ctkCmdLineModuleReference> futureIter(moduleRefsFuture);
+  while(futureIter.hasNext())
   {
-    if (!moduleRefs.at(i))
+    try
     {
-      errorMsg += QObject::tr("Failed to register ") + modulePaths.at(i) + "\n\n";
-    }
-    else if (!moduleRefs.at(i).xmlValidationErrorString().isEmpty() &&
+      const ctkCmdLineModuleReference& moduleRef = futureIter.next();
+      if (!moduleRef)
+      {
+        errorMsg += QObject::tr("Failed to register ") + moduleRef.location().toString() + "\n\n";
+      }
+      else if (!moduleRef.xmlValidationErrorString().isEmpty() &&
              validationMode == ctkCmdLineModuleManager::STRICT_VALIDATION)
+      {
+        errorMsg += QObject::tr("Failed to register ") + moduleRef.location().toString() + ":\n" + moduleRef.xmlValidationErrorString() + "\n\n";
+      }
+    }
+    catch (const ctkCmdLineModuleRunException& e)
+    {
+      errorMsg += QObject::tr("Failed to register module ") + e.location().toString() + ":\n" + e.message() + "\n\\n";
+    }
+
+    catch (const std::exception& e)
     {
-      errorMsg += QObject::tr("Failed to register ") + modulePaths.at(i) + ":\n" + moduleRefs.at(i).xmlValidationErrorString() + "\n\n";
+      errorMsg += QObject::tr("Failed to register module:\n") + e.what() + "\n\n";
     }
   }
 

+ 2 - 2
Applications/ctkCommandLineModuleExplorer/ctkCmdLineModuleExplorerUtils.h

@@ -25,14 +25,14 @@
 #include "ctkCmdLineModuleManager.h"
 
 #include <QPixmap>
+#include <QFuture>
 
 struct ctkCmdLineModuleExplorerUtils
 {
 
   static QPixmap createIconOverlay(const QPixmap& base, const QPixmap& overlay);
 
-  static void messageBoxModuleRegistration(const QStringList& modulePaths,
-                                           const QList<ctkCmdLineModuleReference>& moduleRefs,
+  static void messageBoxModuleRegistration(const QFuture<ctkCmdLineModuleReference>& moduleRefsFuture,
                                            ctkCmdLineModuleManager::ValidationMode validationMode);
 
 };

+ 1 - 1
Libs/CommandLineModules/Backend/FunctionPointer/ctkCmdLineModuleBackendFunctionPointer.cpp

@@ -230,7 +230,7 @@ qint64 ctkCmdLineModuleBackendFunctionPointer::timeStamp(const QUrl &location) c
 }
 
 //----------------------------------------------------------------------------
-QByteArray ctkCmdLineModuleBackendFunctionPointer::rawXmlDescription(const QUrl& location)
+QByteArray ctkCmdLineModuleBackendFunctionPointer::rawXmlDescription(const QUrl& location, int /*timeout*/)
 {
   if (!d->UrlToFpDescription.contains(location)) return QByteArray();
   //qDebug() << d->UrlToFpDescription[location].d->xmlDescription();

+ 1 - 1
Libs/CommandLineModules/Backend/FunctionPointer/ctkCmdLineModuleBackendFunctionPointer.h

@@ -150,7 +150,7 @@ public:
 
   virtual qint64 timeStamp(const QUrl &location) const;
 
-  virtual QByteArray rawXmlDescription(const QUrl& location);
+  virtual QByteArray rawXmlDescription(const QUrl& location, int timeout);
 
   QList<QUrl> registeredFunctionPointers() const;
 

+ 12 - 4
Libs/CommandLineModules/Backend/LocalProcess/ctkCmdLineModuleBackendLocalProcess.cpp

@@ -29,6 +29,7 @@
 #include "ctkCmdLineModuleProcessTask.h"
 #include "ctkCmdLineModuleReference.h"
 #include "ctkCmdLineModuleRunException.h"
+#include "ctkCmdLineModuleTimeoutException.h"
 
 #include "ctkUtils.h"
 #include <iostream>
@@ -165,16 +166,23 @@ qint64 ctkCmdLineModuleBackendLocalProcess::timeStamp(const QUrl &location) cons
 }
 
 //----------------------------------------------------------------------------
-QByteArray ctkCmdLineModuleBackendLocalProcess::rawXmlDescription(const QUrl &location)
+QByteArray ctkCmdLineModuleBackendLocalProcess::rawXmlDescription(const QUrl &location, int timeout)
 {
   QProcess process;
   process.setReadChannel(QProcess::StandardOutput);
   process.start(location.toLocalFile(), QStringList("--xml"));
 
-  if (!process.waitForFinished() || process.exitStatus() == QProcess::CrashExit ||
-      process.error() != QProcess::UnknownError)
+  if (!process.waitForFinished(timeout))
   {
-    throw ctkCmdLineModuleRunException(location, process.exitCode(), process.errorString());
+    if (process.error() == QProcess::Timedout)
+    {
+      throw ctkCmdLineModuleTimeoutException(location, process.errorString());
+    }
+    else if (process.exitStatus() == QProcess::CrashExit ||
+             process.error() != QProcess::UnknownError)
+    {
+      throw ctkCmdLineModuleRunException(location, process.exitCode(), process.errorString());
+    }
   }
 
   process.waitForReadyRead();

+ 1 - 1
Libs/CommandLineModules/Backend/LocalProcess/ctkCmdLineModuleBackendLocalProcess.h

@@ -81,7 +81,7 @@ public:
    * This method always calls the executable with a \c &ndash;&ndash;xml argument and returns
    * the complete data emitted on the standard output channel.
    */
-  virtual QByteArray rawXmlDescription(const QUrl& location);
+  virtual QByteArray rawXmlDescription(const QUrl& location, int timeout);
 
   /**
    * @brief Run a front-end for this module in a local process.

+ 1 - 0
Libs/CommandLineModules/Core/CMakeLists.txt

@@ -40,6 +40,7 @@ set(KIT_SRCS
   ctkCmdLineModuleXmlProgressWatcher.cpp
   ctkCmdLineModuleReference.cpp
   ctkCmdLineModuleRunException.cpp
+  ctkCmdLineModuleTimeoutException.cpp
   ctkCmdLineModuleXmlException.cpp
   ctkCmdLineModuleXmlMsgHandler_p.h
   ctkCmdLineModuleXmlMsgHandler.cpp

+ 1 - 1
Libs/CommandLineModules/Core/Testing/Cpp/ctkCmdLineModuleManagerTest.cpp

@@ -52,7 +52,7 @@ public:
   virtual QString description() const { return "Test Mock-up"; }
   virtual QList<QString> schemes() const { return QList<QString>() << "test"; }
   virtual qint64 timeStamp(const QUrl& /*location*/) const { return 0; }
-  virtual QByteArray rawXmlDescription(const QUrl& location)
+  virtual QByteArray rawXmlDescription(const QUrl& location, int /*timeout*/)
   {
     return UrlToXml[location];
   }

+ 8 - 0
Libs/CommandLineModules/Core/ctkCmdLineModuleBackend.cpp

@@ -21,7 +21,15 @@
 
 #include "ctkCmdLineModuleBackend.h"
 
+#include <qbytearray.h>
+
 //----------------------------------------------------------------------------
 ctkCmdLineModuleBackend::~ctkCmdLineModuleBackend()
 {
 }
+
+//----------------------------------------------------------------------------
+QByteArray ctkCmdLineModuleBackend::rawXmlDescription(const QUrl& location)
+{
+  return this->rawXmlDescription(location, 30000);
+}

+ 14 - 2
Libs/CommandLineModules/Core/ctkCmdLineModuleBackend.h

@@ -46,7 +46,7 @@ class QUrl;
 struct CTK_CMDLINEMODULECORE_EXPORT ctkCmdLineModuleBackend
 {
 
-  ~ctkCmdLineModuleBackend();
+  virtual ~ctkCmdLineModuleBackend();
 
   /**
    * @brief Returns the name of the type of the backend, not the name
@@ -77,6 +77,7 @@ struct CTK_CMDLINEMODULECORE_EXPORT ctkCmdLineModuleBackend
   /**
    * @brief Get the XML parameter description from the given location.
    * @param location The location URL specifying the module.
+   * @param timeout The time-out for retrieving the XML parameter description
    * @return The raw XML parameter description.
    *
    * This method may be concurrently called by the ctkCmdLineModuleManager and
@@ -84,8 +85,19 @@ struct CTK_CMDLINEMODULECORE_EXPORT ctkCmdLineModuleBackend
    * as caching is done by the ctkCmdLineModuleManager itself, checking the
    * return value of timeStamp().
    *
+   * Implementations should also throw either a ctkCmdLineModuleTimeoutException
+   * object if a time-out occured when retrieving the XML parameter description
+   * or a ctkCmdLineModuleRunException for any other error during invocation
+   * of the module.
+   *
+   * @throws ctkCmdLineModuleTimeoutException if a time-out occurred when
+   *         retrieving the XML parameter description.
+   * @throws ctkCmdLineModuleRunException if a runtime error occurred when
+   *         invoking the module to retrieve the XML parameter description.
    */
-  virtual QByteArray rawXmlDescription(const QUrl& location) = 0;
+  virtual QByteArray rawXmlDescription(const QUrl& location, int timeout) = 0;
+  
+  QByteArray rawXmlDescription(const QUrl& location);
 
 protected:
 

+ 13 - 3
Libs/CommandLineModules/Core/ctkCmdLineModuleConcurrentHelpers.cpp

@@ -22,8 +22,9 @@
 #include "ctkCmdLineModuleConcurrentHelpers.h"
 
 #include "ctkCmdLineModuleManager.h"
-#include "ctkException.h"
+#include "ctkCmdLineModuleRunException.h"
 
+#include <qtconcurrentexception.h>
 #include <QUrl>
 #include <QDebug>
 
@@ -46,13 +47,21 @@ ctkCmdLineModuleReference ctkCmdLineModuleConcurrentRegister::operator()(const Q
   {
     return this->ModuleManager->registerModule(moduleUrl);
   }
+  catch (const QtConcurrent::Exception& e)
+  {
+    if (this->Debug)
+    {
+      qDebug() << e.what();
+    }
+    throw e;
+  }
   catch (const ctkException& e)
   {
     if (this->Debug)
     {
       qDebug() << e;
     }
-    return ctkCmdLineModuleReference();
+    throw ctkCmdLineModuleRunException(moduleUrl, 0, e.what());
   }
   catch (...)
   {
@@ -60,8 +69,9 @@ ctkCmdLineModuleReference ctkCmdLineModuleConcurrentRegister::operator()(const Q
     {
       qDebug() << "Registering module" << moduleUrl << "failed with an unknown exception.";
     }
-    return ctkCmdLineModuleReference();
+    throw ctkCmdLineModuleRunException(moduleUrl, 0, "Unknown exception");
   }
+  return ctkCmdLineModuleReference();
 }
 
 //----------------------------------------------------------------------------

+ 1 - 1
Libs/CommandLineModules/Core/ctkCmdLineModuleFrontend.h

@@ -100,7 +100,7 @@ public:
   };
   Q_DECLARE_FLAGS(ParameterFilters, ParameterFilter)
 
-  ~ctkCmdLineModuleFrontend();
+  virtual ~ctkCmdLineModuleFrontend();
 
   /**
    * @brief Returns the GUI representation.

+ 23 - 3
Libs/CommandLineModules/Core/ctkCmdLineModuleManager.cpp

@@ -23,6 +23,7 @@
 
 #include "ctkCmdLineModuleBackend.h"
 #include "ctkCmdLineModuleFrontend.h"
+#include "ctkCmdLineModuleTimeoutException.h"
 #include "ctkCmdLineModuleCache_p.h"
 #include "ctkCmdLineModuleFuture.h"
 #include "ctkCmdLineModuleXmlValidator.h"
@@ -50,7 +51,8 @@ extern int qHash(const QUrl& url);
 struct ctkCmdLineModuleManagerPrivate
 {
   ctkCmdLineModuleManagerPrivate(ctkCmdLineModuleManager::ValidationMode mode, const QString& cacheDir)
-    : ValidationMode(mode)
+    : XmlTimeOut(30000)
+    , ValidationMode(mode)
   {
     QFileInfo fileInfo(cacheDir);
     if (!fileInfo.exists())
@@ -84,6 +86,7 @@ struct ctkCmdLineModuleManagerPrivate
   QHash<QString, ctkCmdLineModuleBackend*> SchemeToBackend;
   QHash<QUrl, ctkCmdLineModuleReference> LocationToRef;
   QScopedPointer<ctkCmdLineModuleCache> ModuleCache;
+  int XmlTimeOut;
 
   const ctkCmdLineModuleManager::ValidationMode ValidationMode;
 };
@@ -106,6 +109,17 @@ ctkCmdLineModuleManager::ValidationMode ctkCmdLineModuleManager::validationMode(
 }
 
 //----------------------------------------------------------------------------
+void ctkCmdLineModuleManager::setXmlTimeout(int xmlTimeout)
+{
+  d->XmlTimeOut = xmlTimeout;
+}
+
+//----------------------------------------------------------------------------
+int ctkCmdLineModuleManager::xmlTimeout() const
+{
+  return d->XmlTimeOut;
+}
+//----------------------------------------------------------------------------
 void ctkCmdLineModuleManager::registerBackend(ctkCmdLineModuleBackend *backend)
 {
   QMutexLocker lock(&d->Mutex);
@@ -159,7 +173,13 @@ ctkCmdLineModuleManager::registerModule(const QUrl &location)
       // newly fetch the XML description
       try
       {
-        xml = backend->rawXmlDescription(location);
+        xml = backend->rawXmlDescription(location, d->XmlTimeOut);
+      }
+      catch (const ctkCmdLineModuleTimeoutException&)
+      {
+        // in case of a time-out, do not cache it as a failed attempt
+        // by recording an empty QByteArray in the cache
+        throw;
       }
       catch (...)
       {
@@ -177,7 +197,7 @@ ctkCmdLineModuleManager::registerModule(const QUrl &location)
   }
   else
   {
-    xml = backend->rawXmlDescription(location);
+    xml = backend->rawXmlDescription(location, d->XmlTimeOut);
   }
 
   if (xml.isEmpty())

+ 18 - 0
Libs/CommandLineModules/Core/ctkCmdLineModuleManager.h

@@ -106,6 +106,21 @@ public:
   ValidationMode validationMode() const;
 
   /**
+   * @brief Set the timeout for retrieving the XML parameter description from a module.
+   *
+   * The default time-out is 30 seconds.
+   *
+   * @param xmlTimeout The timeout in milli seconds.
+   */
+  void setXmlTimeout(int xmlTimeout);
+
+  /**
+   * @brief Get the timeout for retrieving the XML parameter description from a ´module.
+   * @return The timeout in milli seconds.
+   */
+  int xmlTimeout() const;
+
+  /**
    * @brief Registers a new back-end.
    * @param backend The new back-end.
    * @throws ctkInvalidArgumentException if another back-end was already registered handling
@@ -119,6 +134,9 @@ public:
    * @return A module reference.
    * @throws ctkInvalidArgumentException if no back-end for the given URL scheme was registered
    *         or the XML description for the module is invalid.
+   * @throws ctkCmdLineModuleTimeoutException if a time-out occured when retrieving the
+   *         XML description from the module.
+   * @throws ctkCmdLineModuleRunException if a general error occurred when running the module.
    */
   ctkCmdLineModuleReference registerModule(const QUrl& location);
 

+ 11 - 0
Libs/CommandLineModules/Core/ctkCmdLineModuleRunException.cpp

@@ -47,6 +47,17 @@ ctkCmdLineModuleRunException::ctkCmdLineModuleRunException(const ctkCmdLineModul
 }
 
 //----------------------------------------------------------------------------
+ctkCmdLineModuleRunException& ctkCmdLineModuleRunException::operator=(const ctkCmdLineModuleRunException& o)
+{
+  QtConcurrent::Exception::operator=(o);
+  ctkException::operator=(o);
+  this->Location = o.Location;
+  this->ErrorCode = o.ErrorCode;
+  this->ErrorString = o.ErrorString;
+  return *this;
+}
+
+//----------------------------------------------------------------------------
 ctkCmdLineModuleRunException::~ctkCmdLineModuleRunException() throw()
 {
 }

+ 3 - 3
Libs/CommandLineModules/Core/ctkCmdLineModuleRunException.h

@@ -63,9 +63,9 @@ public:
 
 private:
 
-  const QUrl Location;
-  const int ErrorCode;
-  const QString ErrorString;
+  QUrl Location;
+  int ErrorCode;
+  QString ErrorString;
 
 };
 

+ 70 - 0
Libs/CommandLineModules/Core/ctkCmdLineModuleTimeoutException.cpp

@@ -0,0 +1,70 @@
+/*=============================================================================
+  
+  Library: CTK
+  
+  Copyright (c) German Cancer Research Center,
+    Division of Medical and Biological Informatics
+    
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+  
+    http://www.apache.org/licenses/LICENSE-2.0
+    
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  
+=============================================================================*/
+
+#include "ctkCmdLineModuleTimeoutException.h"
+
+//----------------------------------------------------------------------------
+ctkCmdLineModuleTimeoutException::ctkCmdLineModuleTimeoutException(const QUrl &location, const QString &errorString)
+  : ctkCmdLineModuleRunException(location, -1, errorString)
+{
+}
+
+//----------------------------------------------------------------------------
+ctkCmdLineModuleTimeoutException::ctkCmdLineModuleTimeoutException(const QUrl &location, const QString &errorString,
+    const ctkCmdLineModuleRunException& cause)
+  : ctkCmdLineModuleRunException(location, -1, errorString, cause)
+{
+}
+
+//----------------------------------------------------------------------------
+ctkCmdLineModuleTimeoutException::~ctkCmdLineModuleTimeoutException() throw()
+{
+}
+
+//----------------------------------------------------------------------------
+const char* ctkCmdLineModuleTimeoutException::name() const throw()
+{
+  return "CTK CommandLineModule Timeout Exception";
+}
+
+//----------------------------------------------------------------------------
+const char* ctkCmdLineModuleTimeoutException::className() const throw()
+{
+  return "ctkCmdLineModuleTimeoutException";
+}
+
+//----------------------------------------------------------------------------
+ctkCmdLineModuleTimeoutException* ctkCmdLineModuleTimeoutException::clone() const
+{
+  return new ctkCmdLineModuleTimeoutException(*this);
+}
+
+//----------------------------------------------------------------------------
+void ctkCmdLineModuleTimeoutException::rethrow() const
+{
+  throw *this;
+}
+
+//----------------------------------------------------------------------------
+void ctkCmdLineModuleTimeoutException::raise() const
+{
+  throw *this;
+}

+ 55 - 0
Libs/CommandLineModules/Core/ctkCmdLineModuleTimeoutException.h

@@ -0,0 +1,55 @@
+/*=============================================================================
+  
+  Library: CTK
+  
+  Copyright (c) German Cancer Research Center,
+    Division of Medical and Biological Informatics
+    
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+  
+    http://www.apache.org/licenses/LICENSE-2.0
+    
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  
+=============================================================================*/
+
+#ifndef CTKCMDLINEMODULETIMEOUTEXCEPTION_H
+#define CTKCMDLINEMODULETIMEOUTEXCEPTION_H
+
+#include <ctkCmdLineModuleRunException.h>
+
+#include "ctkCommandLineModulesCoreExport.h"
+
+/**
+ * \class ctkCmdLineModuleTimeoutException
+ * \brief Exception class to describe problems with timeouts when running a module.
+ * \ingroup CommandLineModulesCore_API
+ */
+class CTK_CMDLINEMODULECORE_EXPORT ctkCmdLineModuleTimeoutException
+    : public ctkCmdLineModuleRunException
+{
+public:
+
+  explicit ctkCmdLineModuleTimeoutException(const QUrl& location, const QString& errorString);
+
+  ctkCmdLineModuleTimeoutException(const QUrl& location, const QString& errorString,
+                                   const ctkCmdLineModuleRunException& cause);
+
+  ~ctkCmdLineModuleTimeoutException() throw();
+
+  virtual const char* name() const throw();
+  virtual const char* className() const throw();
+  virtual ctkCmdLineModuleTimeoutException* clone() const;
+  virtual void rethrow() const;
+
+  virtual void raise() const;
+
+};
+
+#endif // CTKCMDLINEMODULETIMEOUTEXCEPTION_H

+ 1 - 1
Libs/CommandLineModules/Frontend/QtGui/Testing/Cpp/ctkCmdLineModuleFrontendQtGuiTest.cpp

@@ -58,7 +58,7 @@ public:
   virtual QString description() const { return "Test Mock-up"; }
   virtual QList<QString> schemes() const { return QList<QString>() << "test"; }
   virtual qint64 timeStamp(const QUrl& /*location*/) const { return 0; }
-  virtual QByteArray rawXmlDescription(const QUrl& location)
+  virtual QByteArray rawXmlDescription(const QUrl& location, int /*timeout*/)
   {
     return UrlToXml[location];
   }