瀏覽代碼

Merge branch 'pluginfw-update-support'

* pluginfw-update-support:
  Setting debug mode for tracking plug-ins to false.
  Removed obsolete file.
  Added a test for plug-in updates.
  Added support for a proper plug-in update mechanism.
Sascha Zelzer 13 年之前
父節點
當前提交
34a638b17c
共有 32 個文件被更改,包括 1584 次插入801 次删除
  1. 4 3
      Libs/PluginFramework/CMakeLists.txt
  2. 1 0
      Libs/PluginFramework/Testing/CMakeLists.txt
  3. 0 145
      Libs/PluginFramework/Testing/Cpp/ctkLDAPExpreTest.cpp
  4. 30 0
      Libs/PluginFramework/Testing/FrameworkTestPlugins/pluginA1_test/CMakeLists.txt
  5. 32 0
      Libs/PluginFramework/Testing/FrameworkTestPlugins/pluginA1_test/ctkTestPluginA.cpp
  6. 42 0
      Libs/PluginFramework/Testing/FrameworkTestPlugins/pluginA1_test/ctkTestPluginAActivator.cpp
  7. 47 0
      Libs/PluginFramework/Testing/FrameworkTestPlugins/pluginA1_test/ctkTestPluginAActivator_p.h
  8. 35 0
      Libs/PluginFramework/Testing/FrameworkTestPlugins/pluginA1_test/ctkTestPluginAService.h
  9. 42 0
      Libs/PluginFramework/Testing/FrameworkTestPlugins/pluginA1_test/ctkTestPluginA_p.h
  10. 7 0
      Libs/PluginFramework/Testing/FrameworkTestPlugins/pluginA1_test/manifest_headers.cmake
  11. 9 0
      Libs/PluginFramework/Testing/FrameworkTestPlugins/pluginA1_test/target_libraries.cmake
  12. 94 0
      Libs/PluginFramework/Testing/org.commontk.pluginfwtest/ctkPluginFrameworkTestSuite.cpp
  13. 3 0
      Libs/PluginFramework/Testing/org.commontk.pluginfwtest/ctkPluginFrameworkTestSuite_p.h
  14. 31 0
      Libs/PluginFramework/ctkPlugin.cpp
  15. 81 0
      Libs/PluginFramework/ctkPlugin.h
  16. 1 1
      Libs/PluginFramework/ctkPluginAbstractTracked.tpp
  17. 0 110
      Libs/PluginFramework/ctkPluginArchive.cpp
  18. 182 0
      Libs/PluginFramework/ctkPluginArchiveSQL.cpp
  19. 204 0
      Libs/PluginFramework/ctkPluginArchiveSQL_p.h
  20. 33 48
      Libs/PluginFramework/ctkPluginArchive_p.h
  21. 1 0
      Libs/PluginFramework/ctkPluginConstants.cpp
  22. 10 0
      Libs/PluginFramework/ctkPluginConstants.h
  23. 2 0
      Libs/PluginFramework/ctkPluginFramework.cpp
  24. 2 1
      Libs/PluginFramework/ctkPluginFrameworkContext.cpp
  25. 1 0
      Libs/PluginFramework/ctkPluginFrameworkContext_p.h
  26. 1 0
      Libs/PluginFramework/ctkPluginFrameworkUtil.cpp
  27. 137 3
      Libs/PluginFramework/ctkPluginPrivate.cpp
  28. 12 0
      Libs/PluginFramework/ctkPluginPrivate_p.h
  29. 0 188
      Libs/PluginFramework/ctkPluginStorage.cpp
  30. 395 174
      Libs/PluginFramework/ctkPluginDatabase.cpp
  31. 134 51
      Libs/PluginFramework/ctkPluginDatabase_p.h
  32. 11 77
      Libs/PluginFramework/ctkPluginStorage_p.h

+ 4 - 3
Libs/PluginFramework/CMakeLists.txt

@@ -20,11 +20,11 @@ set(KIT_SRCS
   ctkPluginActivator.h
   ctkPluginArchive.cpp
   ctkPluginArchive_p.h
+  ctkPluginArchiveSQL_p.h
+  ctkPluginArchiveSQL.cpp
   ctkPluginConstants.cpp
   ctkPluginContext.cpp
   ctkPluginContext_p.h
-  ctkPluginDatabase.cpp
-  ctkPluginDatabase_p.h
   ctkPluginDatabaseException.cpp
   ctkPluginEvent.cpp
   ctkPluginException.cpp
@@ -50,8 +50,9 @@ set(KIT_SRCS
   ctkPluginPrivate_p.h
   ctkPlugins.cpp
   ctkPlugins_p.h
-  ctkPluginStorage.cpp
   ctkPluginStorage_p.h
+  ctkPluginStorageSQL.cpp
+  ctkPluginStorageSQL_p.h
   ctkPluginTracker.h
   ctkPluginTracker.tpp
   ctkPluginTrackerPrivate.h

+ 1 - 0
Libs/PluginFramework/Testing/CMakeLists.txt

@@ -7,6 +7,7 @@ include_directories(${CMAKE_CURRENT_SOURCE_DIR}/Cpp)
 
 set(fwtest_plugins
   pluginA_test
+  pluginA1_test
   pluginS_test
   pluginA2_test
   pluginD_test

+ 0 - 145
Libs/PluginFramework/Testing/Cpp/ctkLDAPExpreTest.cpp

@@ -1,145 +0,0 @@
-/*=========================================================================
-
-  Library:   CTK
-
-  Copyright (c) Kitware Inc.
-
-  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.txt
-
-  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.
-
-=========================================================================*/
-
-// CTK includes
-#include "ctkLDAPExpr_p.h"
-
-#include <iostream>
-#include <cstdlib>
-
-#include <QVariant>
-
-
-int TestParsing( );
-int TestEvaluate( );
-
-//-----------------------------------------------------------------------------
-int ctkLDAPExpreTest(int argc, char * argv [] )
-{
-  Q_UNUSED(argc);
-  Q_UNUSED(argv);  
-
-  if ( TestParsing( ) != EXIT_SUCCESS )
-  {
-    return EXIT_FAILURE;
-  }
-
-  if ( TestEvaluate( ) != EXIT_SUCCESS )
-  {
-    return EXIT_FAILURE;
-  }
-
-  return EXIT_SUCCESS;
-}
-
-int TestParsing( ) 
-{
-
-  // WELL FORMED Expr
-  try
-  {
-    ctkLDAPExpr ldap( "(cn=Babs Jensen)" );
-    ldap = ctkLDAPExpr( "(!(cn=Tim Howes))" );
-    ldap = ctkLDAPExpr( "(&(" + ctkPluginConstants::OBJECTCLASS + "=Person)(|(sn=Jensen)(cn=Babs J*)))" );
-    ldap = ctkLDAPExpr( "(o=univ*of*mich*)" );
-    ldap = ctkLDAPExpr( "(cn=Babs Jensen)" );
-  }
-  catch ( std::invalid_argument &e )
-  {
-    std::cerr << e.what() << std::endl;
-    return EXIT_FAILURE;
-  }
-
-
-  // MALFORMED Expre
-  try
-  {
-    ctkLDAPExpr ldap( "cn=Babs Jensen)" );
-    return EXIT_FAILURE;
-  }
-  catch ( std::invalid_argument &e )
-  {
-    // Nothing to do
-    int i = 0;
-  }
-
-  return EXIT_SUCCESS;
-}
-
-
-int TestEvaluate( )
-{
-  // EVALUATE
-  try
-  {
-    ctkLDAPExpr ldap( "(cn=Babs Jensen)" );
-    ctkDictionary dict;
-    bool eval = false;
-
-    // Several values
-    dict.insert( "cn", "Babs Jensen" );
-    dict.insert( "unused", "Jansen" );
-    eval = ldap.evaluate( dict, true );
-    if ( !eval )
-    {
-      return EXIT_FAILURE;
-    }
-
-    // WILDCARD
-    ldap = ctkLDAPExpr( "(cn=Babs *)" );
-    dict.clear();
-    dict.insert( "cn", "Babs Jensen" );
-    eval = ldap.evaluate( dict, true );
-    if ( !eval )
-    {
-      return EXIT_FAILURE;
-    }
-
-    // NOT FOUND
-    ldap = ctkLDAPExpr( "(cn=Babs *)" );
-    dict.clear();
-    dict.insert( "unused", "New" );
-    eval = ldap.evaluate( dict, true );
-    if ( eval )
-    {
-      return EXIT_FAILURE;
-    }
-
-    // QList with integer values
-    ldap = ctkLDAPExpr( "  ( |(cn=Babs *)(sn=1) )" );
-    dict.clear();
-    QList<QVariant> list;
-    list.append( "Babs Jensen" );
-    list.append( "1" );
-    dict.insert( "sn", list );
-    eval = ldap.evaluate( dict, true );
-    if ( !eval )
-    {
-      return EXIT_FAILURE;
-    }
-  }
-  catch ( std::invalid_argument &e )
-  {
-    std::cerr << e.what() << std::endl;
-    return EXIT_FAILURE;
-  }
-
-  return EXIT_SUCCESS;
-}

+ 30 - 0
Libs/PluginFramework/Testing/FrameworkTestPlugins/pluginA1_test/CMakeLists.txt

@@ -0,0 +1,30 @@
+project(pluginA1_test)
+
+set(PLUGIN_export_directive "pluginA1_test_EXPORT")
+
+set(PLUGIN_SRCS
+  ctkTestPluginA.cpp
+  ctkTestPluginAActivator.cpp
+  ctkTestPluginAService.h
+)
+
+set(PLUGIN_MOC_SRCS
+  ctkTestPluginA_p.h
+  ctkTestPluginAActivator_p.h
+)
+
+set(PLUGIN_resources
+  
+)
+
+ctkFunctionGetTargetLibraries(PLUGIN_target_libraries)
+
+ctkMacroBuildPlugin(
+  NAME ${PROJECT_NAME}
+  EXPORT_DIRECTIVE ${PLUGIN_export_directive}
+  SRCS ${PLUGIN_SRCS}
+  MOC_SRCS ${PLUGIN_MOC_SRCS}
+  RESOURCES ${PLUGIN_resources}
+  TARGET_LIBRARIES ${PLUGIN_target_libraries}
+  TEST_PLUGIN
+)

+ 32 - 0
Libs/PluginFramework/Testing/FrameworkTestPlugins/pluginA1_test/ctkTestPluginA.cpp

@@ -0,0 +1,32 @@
+/*=============================================================================
+
+  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 "ctkTestPluginA_p.h"
+
+#include <ctkPluginContext.h>
+
+#include <QStringList>
+
+ctkTestPluginA::ctkTestPluginA(ctkPluginContext* pc)
+{
+  pc->registerService<ctkTestPluginAService>(this);
+}

+ 42 - 0
Libs/PluginFramework/Testing/FrameworkTestPlugins/pluginA1_test/ctkTestPluginAActivator.cpp

@@ -0,0 +1,42 @@
+/*=============================================================================
+
+  Library: CTK
+
+  Copyright (c) 2010 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 "ctkTestPluginAActivator_p.h"
+#include "ctkTestPluginA_p.h"
+
+#include <ctkPluginContext.h>
+
+#include <QtPlugin>
+
+//----------------------------------------------------------------------------
+void ctkTestPluginAActivator::start(ctkPluginContext* context)
+{
+  s.reset(new ctkTestPluginA(context));
+}
+
+//----------------------------------------------------------------------------
+void ctkTestPluginAActivator::stop(ctkPluginContext* context)
+{
+  Q_UNUSED(context)
+}
+
+Q_EXPORT_PLUGIN2(pluginA_test, ctkTestPluginAActivator)

+ 47 - 0
Libs/PluginFramework/Testing/FrameworkTestPlugins/pluginA1_test/ctkTestPluginAActivator_p.h

@@ -0,0 +1,47 @@
+/*=============================================================================
+
+  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 CTKTESTPLUGINAACTIVATOR_P_H
+#define CTKTESTPLUGINAACTIVATOR_P_H
+
+#include <QScopedPointer>
+
+#include <ctkPluginActivator.h>
+#include <ctkTestPluginAService.h>
+
+class ctkTestPluginAActivator : public QObject,
+                                public ctkPluginActivator
+{
+  Q_OBJECT
+  Q_INTERFACES(ctkPluginActivator)
+
+public:
+
+  void start(ctkPluginContext* context);
+  void stop(ctkPluginContext* context);
+
+private:
+
+  QScopedPointer<ctkTestPluginAService> s;
+
+};
+
+#endif // CTKTESTPLUGINAACTIVATOR_P_H

+ 35 - 0
Libs/PluginFramework/Testing/FrameworkTestPlugins/pluginA1_test/ctkTestPluginAService.h

@@ -0,0 +1,35 @@
+/*=============================================================================
+
+  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 CTKTESTPLUGINASERVICE_H
+#define CTKTESTPLUGINASERVICE_H
+
+#include <qglobal.h>
+
+struct ctkTestPluginAService
+{
+  virtual ~ctkTestPluginAService() {}
+};
+
+Q_DECLARE_INTERFACE(ctkTestPluginAService, "org.commontk.pluginAtest.TestPluginAService")
+
+#endif // CTKTESTPLUGINASERVICE_H

+ 42 - 0
Libs/PluginFramework/Testing/FrameworkTestPlugins/pluginA1_test/ctkTestPluginA_p.h

@@ -0,0 +1,42 @@
+/*=============================================================================
+
+  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 CTKTESTPLUGINA_P_H
+#define CTKTESTPLUGINA_P_H
+
+#include <QObject>
+
+#include "ctkTestPluginAService.h"
+
+class ctkPluginContext;
+
+class ctkTestPluginA : public QObject,
+                       public ctkTestPluginAService
+{
+  Q_OBJECT
+  Q_INTERFACES(ctkTestPluginAService)
+
+public:
+  ctkTestPluginA(ctkPluginContext* pc);
+};
+
+#endif // CTKTESTPLUGINA_P_H

+ 7 - 0
Libs/PluginFramework/Testing/FrameworkTestPlugins/pluginA1_test/manifest_headers.cmake

@@ -0,0 +1,7 @@
+set(Plugin-ActivationPolicy "eager")
+set(Plugin-Name "pluginA1_test")
+set(Plugin-Version "1.0.1")
+set(Plugin-Description "Test plugin for framework, pluginA1_test")
+set(Plugin-Vendor "CommonTK")
+set(Plugin-ContactAddress "http://www.commontk.org")
+set(Plugin-Category "test")

+ 9 - 0
Libs/PluginFramework/Testing/FrameworkTestPlugins/pluginA1_test/target_libraries.cmake

@@ -0,0 +1,9 @@
+#
+# See CMake/ctkFunctionGetTargetLibraries.cmake
+# 
+# This file should list the libraries required to build the current CTK plugin.
+# 
+
+set(target_libraries
+  CTKPluginFramework
+  )

+ 94 - 0
Libs/PluginFramework/Testing/org.commontk.pluginfwtest/ctkPluginFrameworkTestSuite.cpp

@@ -27,6 +27,7 @@
 #include <ctkPluginException.h>
 #include <ctkServiceException.h>
 
+#include <QDir>
 #include <QTest>
 #include <QDebug>
 
@@ -455,6 +456,90 @@ void ctkPluginFrameworkTestSuite::frame045a()
 }
 
 //----------------------------------------------------------------------------
+// Reinstalls and the updates testbundle_A.
+// The version is checked to see if an update has been made.
+void ctkPluginFrameworkTestSuite::frame070a()
+{
+  QString pluginA = "pluginA_test";
+  QString pluginA1 = "libpluginA1_test";
+  //InputStream fis;
+  QString versionA;
+  QString versionA1;
+
+  pA.clear();
+
+  clearEvents();
+
+  try
+  {
+    pA = ctkPluginFrameworkTestUtil::installPlugin(pc, pluginA);
+  }
+  catch (const ctkPluginException& pexcA)
+  {
+    qDebug() << "framework test plugin" << pexcA << ":FRAME070A:FAIL";
+  }
+//  catch (const ctkSecurityException& secA)
+//  {
+//    qDebug() << "framework test plugin" << secA << ":FRAME070A:FAIL";
+//    teststatus = false;
+//  }
+
+  QHash<QString,QString> ai = pA->getHeaders();
+  versionA = ai["Plugin-Version"];
+  qDebug() << "Before version =" << versionA;
+
+  QDir testPluginDir(pc->getProperty("pluginfw.testDir").toString());
+  QString pluginA1Path;
+
+  QStringList libSuffixes;
+  libSuffixes << ".so" << ".dll" << ".dylib";
+  foreach(QString libSuffix, libSuffixes)
+  {
+    QFileInfo info(testPluginDir, pluginA1 + libSuffix);
+    if (info.exists())
+    {
+      pluginA1Path = info.absoluteFilePath();
+      break;
+    }
+  }
+
+  if (pluginA1Path.isEmpty())
+  {
+    qDebug() << "Plug-in" << pluginA1 << "not found in" << testPluginDir;
+    QFAIL("Test plug-in not found");
+  }
+
+  QUrl urk = QUrl::fromLocalFile(pluginA1Path);
+  qDebug() << "update from" << urk;
+
+  try
+  {
+    pA->update(urk);
+  }
+  catch (const ctkPluginException& pe)
+  {
+    QFAIL("framework test plug-in, update without new plug-in source :FRAME070A:FAIL");
+  }
+
+  QHash<QString,QString> a1i = pA->getHeaders();
+  versionA1 = a1i["Plugin-Version"];
+  qDebug() << "After version =" << versionA1;
+
+  QList<ctkPluginEvent> pEvts;
+  pEvts << ctkPluginEvent(ctkPluginEvent::INSTALLED, pA);
+  pEvts << ctkPluginEvent(ctkPluginEvent::RESOLVED, pA);
+  pEvts << ctkPluginEvent(ctkPluginEvent::UNRESOLVED, pA);
+  pEvts << ctkPluginEvent(ctkPluginEvent::UPDATED, pA);
+
+
+  QVERIFY2(checkListenerEvents(QList<ctkPluginFrameworkEvent>(),
+                               pEvts, QList<ctkServiceEvent>()),
+           "Unexpected events");
+
+  QVERIFY2(versionA1 != versionA, "framework test plug-in, update of plug-in failed, version info unchanged :FRAME070A:Fail");
+}
+
+//----------------------------------------------------------------------------
 void ctkPluginFrameworkTestSuite::frameworkListener(const ctkPluginFrameworkEvent& fwEvent)
 {
   frameworkEvents.push_back(fwEvent);
@@ -718,6 +803,15 @@ bool ctkPluginFrameworkTestSuite::checkSyncListenerEvents(
   return listenState;
 }
 
+void ctkPluginFrameworkTestSuite::clearEvents()
+{
+  QTest::qWait(300);
+  pluginEvents.clear();
+  syncPluginEvents.clear();
+  frameworkEvents.clear();
+  serviceEvents.clear();
+}
+
 //----------------------------------------------------------------------------
 ctkServiceEvent ctkServiceListenerPFW::getEvent() const
 {

+ 3 - 0
Libs/PluginFramework/Testing/org.commontk.pluginfwtest/ctkPluginFrameworkTestSuite_p.h

@@ -66,6 +66,7 @@ private Q_SLOTS:
   void frame040a();
   void frame042a();
   void frame045a();
+  void frame070a();
 
 private:
 
@@ -99,6 +100,8 @@ private:
   // reset the events
   bool checkSyncListenerEvents(const QList<ctkPluginEvent>& pEvts);
 
+  void clearEvents();
+
   static int nRunCount;
 
   QList<ctkPluginEvent> pluginEvents;

+ 31 - 0
Libs/PluginFramework/ctkPlugin.cpp

@@ -170,6 +170,37 @@ void ctkPlugin::stop(const StopOptions& options)
 }
 
 //----------------------------------------------------------------------------
+void ctkPlugin::update(const QUrl& updateLocation)
+{
+  Q_D(ctkPlugin);
+  ctkPluginPrivate::Locker sync(&d->operationLock);
+  const bool wasActive = d->state == ACTIVE;
+
+  switch (d->getUpdatedState_unlocked())
+  {
+  case ACTIVE:
+    stop(STOP_TRANSIENT);
+    // Fall through
+  case RESOLVED:
+  case INSTALLED:
+    // Load new plugin
+    //secure.callUpdate0(this, in, wasActive);
+    d->update0(updateLocation, wasActive);
+    break;
+  case STARTING:
+    // Wait for RUNNING state, this doesn't happen now
+    // since we are synchronized.
+    throw std::logic_error("Plugin is in STARTING state");
+  case STOPPING:
+    // Wait for RESOLVED state, this doesn't happen now
+    // since we are synchronized.
+    throw std::logic_error("Plugin is in STOPPING state");
+  case UNINSTALLED:
+    throw std::logic_error("Plugin is in UNINSTALLED state");
+  }
+}
+
+//----------------------------------------------------------------------------
 void ctkPlugin::uninstall()
 {
   Q_D(ctkPlugin);

+ 81 - 0
Libs/PluginFramework/ctkPlugin.h

@@ -25,6 +25,7 @@
 #include <QHash>
 #include <QWeakPointer>
 #include <QMetaType>
+#include <QUrl>
 
 #include "ctkVersion.h"
 #include "ctkPluginLocalization.h"
@@ -434,6 +435,86 @@ public:
   virtual void stop(const StopOptions& options = 0);
 
   /**
+   * Updates this plugin from a <code>QUrl</code>.
+   *
+   * <p>
+   * If the specified <code>QURl</code> is <code>empty</code>, the
+   * Framework creates the <code>QUrl</code> from which to read the
+   * updated plugin by interpreting, in an implementation dependent manner,
+   * this plugin's {@link ctkPluginConstants#PLUGIN_UPDATELOCATION
+   * Plugin-UpdateLocation} Manifest header, if present, or this plugin's
+   * original location.
+   *
+   * <p>
+   * If this plugin's state is <code>ACTIVE</code>, it must be stopped before
+   * the update and started after the update successfully completes.
+   *
+   * <p>
+   * If this plugin has exported any classes that are used by another
+   * plugin, these classes remain available until the Framework is relaunched.
+   *
+   * <p>
+   * The following steps are required to update a plugin:
+   * <ol>
+   * <li>If this plugin's state is <code>UNINSTALLED</code> then an
+   * <code>std::logic_error</code> is thrown.
+   *
+   * <li>If this plugin's state is <code>ACTIVE</code>, <code>STARTING</code>
+   * or <code>STOPPING</code>, this plugin is stopped as described in the
+   * <code>ctkPlugin::stop()</code> method. If <code>ctkPlugin::stop()</code> throws an
+   * exception, the exception is rethrown terminating the update.
+   *
+   * <li>The updated version of this plugin is read from the URL and
+   * installed. If the Framework is unable to install the updated version of
+   * this plugin, the original version of this plugin is restored and a
+   * <code>ctkPluginException</code> is thrown after completion of the
+   * remaining steps.
+   *
+   * <li>This plugin's state is set to <code>INSTALLED</code>.
+   *
+   * <li>If the updated version of this plugin was successfully installed, a
+   * plugin event of type {@link ctkPluginEvent#UPDATED} is fired.
+   *
+   * <li>If this plugin's state was originally <code>ACTIVE</code>, the
+   * updated plugin is started as described in the <code>ctkPlugin::start()</code>
+   * method. If <code>ctkPlugin::start()</code> throws an exception, a Framework
+   * event of type {@link ctkPluginFrameworkEvent#ERROR} is fired containing the
+   * exception.
+   * </ol>
+   *
+   * <b>Preconditions </b>
+   * <ul>
+   * <li><code>getState()</code> not in &#x007B; <code>UNINSTALLED</code>
+   * &#x007D;.
+   * </ul>
+   * <b>Postconditions, no exceptions thrown </b>
+   * <ul>
+   * <li><code>getState()</code> in &#x007B; <code>INSTALLED</code>,
+   * <code>RESOLVED</code>, <code>ACTIVE</code> &#x007D;.
+   * <li>This plugin has been updated.
+   * </ul>
+   * <b>Postconditions, when an exception is thrown </b>
+   * <ul>
+   * <li><code>getState()</code> in &#x007B; <code>INSTALLED</code>,
+   * <code>RESOLVED</code>, <code>ACTIVE</code> &#x007D;.
+   * <li>Original plugin is still used; no update occurred.
+   * </ul>
+   *
+   * @param input The <code>QUrl</code> from which to read the new
+   *        plugin or <code>null</code> to indicate the Framework must create
+   *        the URL from this plugin's
+   *        {@link ctkPluginConstants#PLUGIN_UPDATELOCATION Plugin-UpdateLocation}
+   *        Manifest header, if present, or this plugin's original location.
+   * @throws ctkPluginException If the update location cannot be read or the update
+   *         fails.
+   * @throws std::logic_error If this plugin has been uninstalled or this
+   *         plugin tries to change its own state.
+   * @see #stop()
+   * @see #start()
+   */
+  void update(const QUrl &updateLocation = QUrl());
+
+  /**
    * Uninstalls this plugin.
    *
    * <p>

+ 1 - 1
Libs/PluginFramework/ctkPluginAbstractTracked.tpp

@@ -26,7 +26,7 @@
 
 //----------------------------------------------------------------------------
 template<class S, class T, class R>
-const bool ctkPluginAbstractTracked<S,T,R>::DEBUG	= true;
+const bool ctkPluginAbstractTracked<S,T,R>::DEBUG = false;
 
 //----------------------------------------------------------------------------
 template<class S, class T, class R>

+ 0 - 110
Libs/PluginFramework/ctkPluginArchive.cpp

@@ -31,113 +31,3 @@
 const QString ctkPluginArchive::AUTOSTART_SETTING_STOPPED("stopped");
 const QString ctkPluginArchive::AUTOSTART_SETTING_EAGER("eager");
 const QString ctkPluginArchive::AUTOSTART_SETTING_ACTIVATION_POLICY("activation_policy");
-
-//----------------------------------------------------------------------------
-ctkPluginArchive::ctkPluginArchive(ctkPluginStorage* pluginStorage,
-                                   const QUrl& pluginLocation, const QString& localPluginPath,
-                                   int pluginId, int startLevel, const QDateTime& lastModified,
-                                   int autostartSetting)
-  : autostartSetting(autostartSetting), id(pluginId), startLevel(startLevel),
-    lastModified(lastModified), location(pluginLocation),
-    localPluginPath(localPluginPath), storage(pluginStorage)
-{
-  QByteArray manifestResource = this->getPluginResource("META-INF/MANIFEST.MF");
-  if (manifestResource.isEmpty())
-  {
-    throw ctkPluginException(QString("ctkPlugin has no MANIFEST.MF resource, location=") + pluginLocation.toString());
-  }
-  manifest.read(manifestResource);
-}
-
-//----------------------------------------------------------------------------
-QString ctkPluginArchive::getAttribute(const QString& key) const
-{
-  return manifest.getAttribute(key);
-}
-
-//----------------------------------------------------------------------------
-QHash<QString,QString> ctkPluginArchive::getUnlocalizedAttributes() const
-{
-  return manifest.getMainAttributes();
-}
-
-//----------------------------------------------------------------------------
-int ctkPluginArchive::getPluginId() const
-{
-  return id;
-}
-
-//----------------------------------------------------------------------------
-QUrl ctkPluginArchive::getPluginLocation() const
-{
-  return location;
-}
-
-//----------------------------------------------------------------------------
-QString ctkPluginArchive::getLibLocation() const
-{
-  return localPluginPath;
-}
-
-//----------------------------------------------------------------------------
-QByteArray ctkPluginArchive::getPluginResource(const QString& component) const
-{
-  return storage->getPluginResource(getPluginId(), component);
-}
-
-//----------------------------------------------------------------------------
-QStringList ctkPluginArchive::findResourcesPath(const QString& path) const
-{
-  return storage->findResourcesPath(getPluginId(), path);
-}
-
-//----------------------------------------------------------------------------
-int ctkPluginArchive::getStartLevel() const
-{
-  return startLevel;
-}
-
-//----------------------------------------------------------------------------
-void ctkPluginArchive::setStartLevel(int level)
-{
-  if (startLevel != level)
-  {
-    startLevel = level;
-    storage->setStartLevel(this);
-  }
-}
-
-//----------------------------------------------------------------------------
-QDateTime ctkPluginArchive::getLastModified() const
-{
-  return lastModified;
-}
-
-//----------------------------------------------------------------------------
-void ctkPluginArchive::setLastModified(const QDateTime& dateTime)
-{
-  lastModified = dateTime;
-  storage->setLastModified(this);
-}
-
-//----------------------------------------------------------------------------
-int ctkPluginArchive::getAutostartSetting() const
-{
-  return autostartSetting;
-}
-
-//----------------------------------------------------------------------------
-void ctkPluginArchive::setAutostartSetting(int setting)
-{
-  if (autostartSetting != setting)
-  {
-    autostartSetting = setting;
-    storage->setAutostartSetting(this);
-  }
-}
-
-//----------------------------------------------------------------------------
-void ctkPluginArchive::purge()
-{
-  storage->removeArchive(this);
-}

+ 182 - 0
Libs/PluginFramework/ctkPluginArchiveSQL.cpp

@@ -0,0 +1,182 @@
+/*=============================================================================
+
+  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 "ctkPluginArchiveSQL_p.h"
+
+#include "ctkPluginException.h"
+#include "ctkPluginStorageSQL_p.h"
+#include "ctkPluginDatabaseException.h"
+
+#include <QStringList>
+#include <QFile>
+
+
+//----------------------------------------------------------------------------
+ctkPluginArchiveSQL::ctkPluginArchiveSQL(ctkPluginStorageSQL* pluginStorage,
+                                         const QUrl& pluginLocation, const QString& localPluginPath,
+                                         int pluginId, int startLevel, const QDateTime& lastModified,
+                                         int autostartSetting)
+  : key(-1), autostartSetting(autostartSetting), id(pluginId), generation(0)
+  , startLevel(startLevel), lastModified(lastModified), location(pluginLocation)
+  , localPluginPath(localPluginPath), storage(pluginStorage)
+{
+}
+
+//----------------------------------------------------------------------------
+ctkPluginArchiveSQL::ctkPluginArchiveSQL(ctkPluginArchiveSQL* old, int generation,
+                                         const QUrl &pluginLocation, const QString &localPluginPath)
+  : key(-1), autostartSetting(old->autostartSetting), id(old->id), generation(generation)
+  , startLevel(0), location(pluginLocation), localPluginPath(localPluginPath)
+  , storage(old->storage)
+{
+}
+
+void ctkPluginArchiveSQL::readManifest(const QByteArray& manifestResource)
+{
+  QByteArray manifestRes = manifestResource.isNull() ? this->getPluginResource("META-INF/MANIFEST.MF")
+                                                  : manifestResource;
+  if (manifestRes.isEmpty())
+  {
+    throw ctkPluginException(QString("ctkPlugin has no MANIFEST.MF resource, location=") + localPluginPath);
+  }
+
+  manifest.read(manifestRes);
+}
+
+//----------------------------------------------------------------------------
+QString ctkPluginArchiveSQL::getAttribute(const QString& key) const
+{
+  return manifest.getAttribute(key);
+}
+
+//----------------------------------------------------------------------------
+QHash<QString,QString> ctkPluginArchiveSQL::getUnlocalizedAttributes() const
+{
+  return manifest.getMainAttributes();
+}
+
+int ctkPluginArchiveSQL::getPluginGeneration() const
+{
+  return generation;
+}
+
+//----------------------------------------------------------------------------
+int ctkPluginArchiveSQL::getPluginId() const
+{
+  return id;
+}
+
+//----------------------------------------------------------------------------
+QUrl ctkPluginArchiveSQL::getPluginLocation() const
+{
+  return location;
+}
+
+//----------------------------------------------------------------------------
+QString ctkPluginArchiveSQL::getLibLocation() const
+{
+  return localPluginPath;
+}
+
+//----------------------------------------------------------------------------
+QByteArray ctkPluginArchiveSQL::getPluginResource(const QString& component) const
+{
+  try
+  {
+    return storage->getPluginResource(key, component);
+  }
+  catch (const ctkPluginDatabaseException& exc)
+  {
+    qDebug() << QString("Getting plugin resource %1 failed:").arg(component) << exc;
+    return QByteArray();
+  }
+}
+
+//----------------------------------------------------------------------------
+QStringList ctkPluginArchiveSQL::findResourcesPath(const QString& path) const
+{
+  try
+  {
+    return storage->findResourcesPath(key, path);
+  }
+  catch (const ctkPluginDatabaseException& exc)
+  {
+    qDebug() << QString("Getting plugin resource paths for %1 failed:").arg(path) << exc;
+  }
+  return QStringList();
+}
+
+//----------------------------------------------------------------------------
+int ctkPluginArchiveSQL::getStartLevel() const
+{
+  return startLevel;
+}
+
+//----------------------------------------------------------------------------
+void ctkPluginArchiveSQL::setStartLevel(int level)
+{
+  if (startLevel != level)
+  {
+    startLevel = level;
+    storage->setStartLevel(key, level);
+  }
+}
+
+//----------------------------------------------------------------------------
+QDateTime ctkPluginArchiveSQL::getLastModified() const
+{
+  return lastModified;
+}
+
+//----------------------------------------------------------------------------
+void ctkPluginArchiveSQL::setLastModified(const QDateTime& dateTime)
+{
+  lastModified = dateTime;
+  storage->setLastModified(key, dateTime);
+}
+
+//----------------------------------------------------------------------------
+int ctkPluginArchiveSQL::getAutostartSetting() const
+{
+  return autostartSetting;
+}
+
+//----------------------------------------------------------------------------
+void ctkPluginArchiveSQL::setAutostartSetting(int setting)
+{
+  if (autostartSetting != setting)
+  {
+    autostartSetting = setting;
+    storage->setAutostartSetting(key, setting);
+  }
+}
+
+//----------------------------------------------------------------------------
+void ctkPluginArchiveSQL::purge()
+{
+  storage->removeArchive(this);
+}
+
+//----------------------------------------------------------------------------
+void ctkPluginArchiveSQL::close()
+{
+
+}

+ 204 - 0
Libs/PluginFramework/ctkPluginArchiveSQL_p.h

@@ -0,0 +1,204 @@
+/*=============================================================================
+
+  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 CTKPLUGINARCHIVESQL_P_H
+#define CTKPLUGINARCHIVESQL_P_H
+
+#include <QString>
+#include <QHash>
+#include <QUrl>
+#include <QDateTime>
+
+#include "ctkPluginArchive_p.h"
+#include "ctkPluginManifest_p.h"
+
+// CTK foraward declarations
+class ctkPluginStorageSQL;
+
+/**
+ * \ingroup PluginFramework
+ *
+ * Class for managing plugin data.
+ *
+ */
+class ctkPluginArchiveSQL : public ctkPluginArchive
+{
+
+public:
+
+  /**
+   * Construct new plugin archive.
+   *
+   */
+  ctkPluginArchiveSQL(ctkPluginStorageSQL* pluginStorage, const QUrl& pluginLocation,
+                      const QString& localPluginPath, int pluginId,
+                      int startLevel = -1, const QDateTime &lastModified = QDateTime(),
+                      int autostartSetting = -1);
+
+  /**
+   * Construct new bundle archive in an existing bundle archive.
+   *
+   */
+  ctkPluginArchiveSQL(ctkPluginArchiveSQL* old, int generation, const QUrl& pluginLocation, const QString& localPluginPath);
+
+
+  /**
+   * Get an attribute from the manifest of a plugin.
+   *
+   * Not localized
+   *
+   * @param key Name of attribute to get.
+   * @return A string with result or null if the entry doesn't exists.
+   */
+  QString getAttribute(const QString& key) const;
+
+  /**
+   * @returns the (raw/unlocalized) attributes
+   */
+  QHash<QString,QString> getUnlocalizedAttributes() const;
+
+  /**
+   * Get the plugin generation associated with this plugin archive.
+   *
+   * @return A integer representing the generation.
+   */
+  int getPluginGeneration() const;
+
+  /**
+   * Get plugin identifier for this plugin archive.
+   *
+   * @return ctkPlugin identifier.
+   */
+  int getPluginId() const;
+
+  /**
+   * Get plugin location for this plugin archive.
+   *
+   * @return Bundle location.
+   */
+  QUrl getPluginLocation() const;
+
+  /**
+   * Get the path to the plugin library on the local
+   * file system
+   *
+   * @return Path to the plugin library
+   */
+  QString getLibLocation() const;
+
+
+  /**
+   * Get a Qt resource as a byte array from a plugin. The resource
+   * is cached and may be aquired even if the plugin is not active.
+   *
+   * @param component Resource to get the byte array from.
+   * @return QByteArray to the entry (empty if it doesn't exist).
+   */
+  QByteArray getPluginResource(const QString& component) const;
+
+
+  /**
+   * Returns a QStringList of all the paths
+   * to entries within the plugin whose longest sub-path matches the supplied
+   * path argument.
+   *
+   * @param name
+   * @return
+   */
+  QStringList findResourcesPath(const QString& path) const;
+
+
+  /**
+   * Get stored plugin start level.
+   */
+  int getStartLevel() const;
+
+
+  /**
+   * Set stored plugin start level.
+   */
+  void setStartLevel(int level);
+
+
+  /**
+   * Get last modified timestamp.
+   */
+  QDateTime getLastModified() const;
+
+
+  /**
+   * Set stored last modified timestamp.
+   */
+  void setLastModified(const QDateTime& timemillisecs);
+
+
+  /**
+   * Get auto-start setting.
+   *
+   * @return the autostart setting. "-1" if the plugin is not started.
+   */
+  int getAutostartSetting() const;
+
+
+  /**
+   * Set the auto-start setting.
+   *
+   * @param setting the autostart setting to use.
+   */
+  void setAutostartSetting(int setting);
+
+  /**
+   * Remove plugin from persistent storage.
+   * This will delete the current ctkPluginArchiveSQL instance.
+   */
+  void purge();
+
+  /**
+   * Close archive and all its open files.
+   */
+  void close();
+
+  /**
+   * Create a ctkPluginManifest using the Qt resource under META-INF/MANIFEST.MF
+   */
+  void readManifest(const QByteArray &manifestResource = QByteArray());
+
+public:
+
+  int key;
+
+private:
+
+  int autostartSetting;
+  int id;
+  int generation;
+  int startLevel;
+  QDateTime lastModified;
+  QUrl location;
+  QString localPluginPath;
+  ctkPluginManifest manifest;
+  ctkPluginStorageSQL* storage;
+
+};
+
+
+
+#endif // CTKPLUGINARCHIVESQL_P_H

+ 33 - 48
Libs/PluginFramework/ctkPluginArchive_p.h

@@ -22,23 +22,14 @@
 #ifndef CTKPLUGINARCHIVE_P_H
 #define CTKPLUGINARCHIVE_P_H
 
-#include <QString>
 #include <QHash>
 #include <QUrl>
 #include <QDateTime>
 
-#include "ctkPluginManifest_p.h"
-
-// Qt forward declarations
-class QIODevice;
-
-// CTK foraward declarations
-class ctkPluginStorage;
-
 /**
  * \ingroup PluginFramework
  *
- * Class for managing plugin data.
+ * Interface for managing plugin data.
  *
  */
 class ctkPluginArchive
@@ -48,43 +39,22 @@ public:
 
   /**
    * Autostart setting stopped.
-   * @see PluginArchiveInterface#setAutostartSetting(const QString&)
+   * @see ctkPluginArchive#setAutostartSetting(const QString&)
    */
   static const QString AUTOSTART_SETTING_STOPPED; // = "stopped"
 
   /**
    * Autostart setting eager.
-   * @see PluginArchiveInterface#setAutostartSetting(const QString&)
+   * @see ctkPluginArchive#setAutostartSetting(const QString&)
    */
   static const QString AUTOSTART_SETTING_EAGER; // = "eager"
 
   /**
    * Autostart setting declared activation policy.
-   * @see PluginArchiveInterface#setAutostartSetting(const QString&)
+   * @see ctkPluginArchive#setAutostartSetting(const QString&)
    */
   static const QString AUTOSTART_SETTING_ACTIVATION_POLICY; // = "activation_policy"
 
-private:
-
-  int autostartSetting;
-  int id;
-  int startLevel;
-  QDateTime lastModified;
-  QUrl location;
-  QString localPluginPath;
-  ctkPluginManifest manifest;
-  ctkPluginStorage* storage;
-
-public:
-
-  /**
-   * Construct new plugin archive.
-   *
-   */
-  ctkPluginArchive(ctkPluginStorage* pluginStorage, const QUrl& pluginLocation,
-                   const QString& localPluginPath, int pluginId,
-                   int startLevel = -1, const QDateTime& lastModified = QDateTime(),
-                   int autostartSetting = -1);
 
   /**
    * Get an attribute from the manifest of a plugin.
@@ -94,12 +64,21 @@ public:
    * @param key Name of attribute to get.
    * @return A string with result or null if the entry doesn't exists.
    */
-  QString getAttribute(const QString& key) const;
+  virtual QString getAttribute(const QString& key) const = 0;
+
 
   /**
    * @returns the (raw/unlocalized) attributes
    */
-  QHash<QString,QString> getUnlocalizedAttributes() const;
+  virtual QHash<QString,QString> getUnlocalizedAttributes() const = 0;
+
+
+  /**
+   * Get the plugin generation associated with this plugin archive.
+   *
+   * @return A integer representing the generation.
+   */
+  virtual int getPluginGeneration() const = 0;
 
 
   /**
@@ -107,7 +86,7 @@ public:
    *
    * @return ctkPlugin identifier.
    */
-  int getPluginId() const;
+  virtual int getPluginId() const = 0;
 
 
   /**
@@ -115,7 +94,8 @@ public:
    *
    * @return Bundle location.
    */
-  QUrl getPluginLocation() const;
+  virtual QUrl getPluginLocation() const = 0;
+
 
   /**
    * Get the path to the plugin library on the local
@@ -123,7 +103,7 @@ public:
    *
    * @return Path to the plugin library
    */
-  QString getLibLocation() const;
+  virtual QString getLibLocation() const = 0;
 
 
   /**
@@ -133,7 +113,7 @@ public:
    * @param component Resource to get the byte array from.
    * @return QByteArray to the entry (empty if it doesn't exist).
    */
-  QByteArray getPluginResource(const QString& component) const;
+  virtual QByteArray getPluginResource(const QString& component) const = 0;
 
 
   /**
@@ -144,31 +124,31 @@ public:
    * @param name
    * @return
    */
-  QStringList findResourcesPath(const QString& path) const;
+  virtual QStringList findResourcesPath(const QString& path) const = 0;
 
 
   /**
    * Get stored plugin start level.
    */
-  int getStartLevel() const;
+  virtual int getStartLevel() const = 0;
 
 
   /**
    * Set stored plugin start level.
    */
-  void setStartLevel(int level);
+  virtual void setStartLevel(int level) = 0;
 
 
   /**
    * Get last modified timestamp.
    */
-  QDateTime getLastModified() const;
+  virtual QDateTime getLastModified() const = 0;
 
 
   /**
    * Set stored last modified timestamp.
    */
-  void setLastModified(const QDateTime& timemillisecs);
+  virtual void setLastModified(const QDateTime& timemillisecs) = 0;
 
 
   /**
@@ -176,7 +156,7 @@ public:
    *
    * @return the autostart setting. "-1" if the plugin is not started.
    */
-  int getAutostartSetting() const;
+  virtual int getAutostartSetting() const = 0;
 
 
   /**
@@ -184,7 +164,7 @@ public:
    *
    * @param setting the autostart setting to use.
    */
-  void setAutostartSetting(int setting);
+  virtual void setAutostartSetting(int setting) = 0;
 
 
   /**
@@ -207,7 +187,12 @@ public:
    * Remove plugin from persistent storage.
    * This will delete the current ctkPluginArchive instance.
    */
-  void purge();
+  virtual void purge() = 0;
+
+  /**
+   * Close archive and all its open files.
+   */
+  virtual void close() = 0;
 
 };
 

+ 1 - 0
Libs/PluginFramework/ctkPluginConstants.cpp

@@ -43,6 +43,7 @@ const QString ctkPluginConstants::REQUIRE_PLUGIN = "Require-Plugin";
 const QString ctkPluginConstants::PLUGIN_VERSION_ATTRIBUTE = "plugin-version";
 const QString ctkPluginConstants::PLUGIN_VERSION = "Plugin-Version";
 const QString ctkPluginConstants::PLUGIN_ACTIVATIONPOLICY = "Plugin-ActivationPolicy";
+const QString ctkPluginConstants::PLUGIN_UPDATELOCATION = "Plugin-UpdateLocation";
 
 const QString ctkPluginConstants::ACTIVATION_EAGER = "eager";
 const QString ctkPluginConstants::ACTIVATION_LAZY = "lazy";

+ 10 - 0
Libs/PluginFramework/ctkPluginConstants.h

@@ -240,6 +240,16 @@ struct CTK_PLUGINFW_EXPORT ctkPluginConstants {
   static const QString PLUGIN_ACTIVATIONPOLICY; // = "Plugin-ActivationPolicy"
 
   /**
+   * Manifest header identifying the location from which a new plugin version
+   * is obtained during a plugin update operation.
+   *
+   * <p>
+   * The attribute value may be retrieved from the <code>ctkDictionary</code>
+   * object returned by the <code>ctkPlugin::getHeaders()</code> method.
+   */
+  static const QString PLUGIN_UPDATELOCATION; // = "Plugin-UpdateLocation"
+
+  /**
    * Plugin activation policy declaring the plugin must be activated immediately.
    *
    * <p>

+ 2 - 0
Libs/PluginFramework/ctkPluginFramework.cpp

@@ -37,6 +37,8 @@ ctkPluginFramework::ctkPluginFramework()
   qRegisterMetaType<ctkPluginEvent>("ctkPluginEvent");
   qRegisterMetaType<ctkServiceEvent>("ctkServiceEvent");
   qRegisterMetaType<ctkEvent>("ctkEvent");
+  qRegisterMetaType<ctkProperties>("ctkProperties");
+  qRegisterMetaType<ctkDictionary>("ctkDictionary");
 }
 
 //----------------------------------------------------------------------------

+ 2 - 1
Libs/PluginFramework/ctkPluginFrameworkContext.cpp

@@ -24,6 +24,7 @@
 #include "ctkPluginFrameworkUtil_p.h"
 #include "ctkPluginFrameworkPrivate_p.h"
 #include "ctkPluginArchive_p.h"
+#include "ctkPluginStorageSQL_p.h"
 #include "ctkPluginConstants.h"
 #include "ctkServices_p.h"
 #include "ctkUtils.h"
@@ -81,7 +82,7 @@ void ctkPluginFrameworkContext::init()
   ctkPluginFrameworkPrivate* const systemPluginPrivate = systemPlugin->d_func();
   systemPluginPrivate->initSystemPlugin();
 
-  storage = new ctkPluginStorage(this);
+  storage = new ctkPluginStorageSQL(this);
   dataStorage = ctkPluginFrameworkUtil::getFileStorage(this, "data");
   services = new ctkServices(this);
   plugins = new ctkPlugins(this);

+ 1 - 0
Libs/PluginFramework/ctkPluginFrameworkContext_p.h

@@ -24,6 +24,7 @@
 
 #include <QDebug>
 #include <QMutex>
+#include <QDir>
 
 #include "ctkPluginFrameworkFactory.h"
 #include "ctkPluginFramework.h"

+ 1 - 0
Libs/PluginFramework/ctkPluginFrameworkUtil.cpp

@@ -23,6 +23,7 @@
 #include "ctkPluginFrameworkContext_p.h"
 
 #include <QString>
+#include <QCoreApplication>
 
 #include <stdexcept>
 

+ 137 - 3
Libs/PluginFramework/ctkPluginPrivate.cpp

@@ -36,6 +36,8 @@
 // for ctk::msecsTo() - remove after switching to Qt 4.7
 #include <ctkUtils.h>
 
+#include <typeinfo>
+
 const ctkPlugin::States ctkPluginPrivate::RESOLVED_FLAGS = ctkPlugin::RESOLVED | ctkPlugin::STARTING | ctkPlugin::ACTIVE | ctkPlugin::STOPPING;
 
 //----------------------------------------------------------------------------
@@ -175,10 +177,19 @@ ctkPlugin::State ctkPluginPrivate::getUpdatedState()
 {
   if (state & ctkPlugin::INSTALLED)
   {
+    Locker sync(&operationLock);
+    getUpdatedState_unlocked();
+  }
+  return state;
+}
+
+//----------------------------------------------------------------------------
+ctkPlugin::State ctkPluginPrivate::getUpdatedState_unlocked()
+{
+  if (state & ctkPlugin::INSTALLED)
+  {
     try
     {
-      // NYI, fix double locking
-      Locker sync(&operationLock);
       if (state == ctkPlugin::INSTALLED)
       {
         operation.fetchAndStoreOrdered(RESOLVING);
@@ -332,7 +343,7 @@ void ctkPluginPrivate::finalizeActivation()
   Locker sync(&operationLock);
 
   // 4: Resolve plugin (if needed)
-  switch (getUpdatedState())
+  switch (getUpdatedState_unlocked())
   {
   case ctkPlugin::INSTALLED:
     Q_ASSERT_X(resolveFailException != 0, Q_FUNC_INFO, "no resolveFailException");
@@ -469,6 +480,129 @@ const ctkRuntimeException* ctkPluginPrivate::stop1()
 }
 
 //----------------------------------------------------------------------------
+void ctkPluginPrivate::update0(const QUrl& updateLocation, bool wasActive)
+{
+  const bool wasResolved = state == ctkPlugin::RESOLVED;
+  const int oldStartLevel = getStartLevel();
+  ctkPluginArchive* newArchive = 0;
+
+  operation.fetchAndStoreOrdered(UPDATING);
+  try
+  {
+    // New plugin as stream supplied?
+    QUrl updateUrl(updateLocation);
+    if (updateUrl.isEmpty())
+    {
+      // Try Plugin-UpdateLocation
+      QString update = archive != 0 ? archive->getAttribute(ctkPluginConstants::PLUGIN_UPDATELOCATION) : QString();
+      if (update.isEmpty())
+      {
+        // Take original location
+        updateUrl = location;
+      }
+    }
+
+    if(updateUrl.scheme() != "file")
+    {
+      QString msg = "Unsupported update URL:";
+      msg += updateUrl.toString();
+      throw ctkPluginException(msg);
+    }
+
+    newArchive = fwCtx->storage->updatePluginArchive(archive, updateUrl, updateUrl.toLocalFile());
+    //checkCertificates(newArchive);
+    checkManifestHeaders();
+    newArchive->setStartLevel(oldStartLevel);
+    fwCtx->storage->replacePluginArchive(archive, newArchive);
+  }
+  catch (const std::exception& e)
+  {
+    if (newArchive != 0)
+    {
+      newArchive->purge();
+    }
+    operation.fetchAndStoreOrdered(IDLE);
+    if (wasActive)
+    {
+      try
+      {
+        this->q_func().data()->start();
+      }
+      catch (const ctkPluginException& pe)
+      {
+        fwCtx->listeners.frameworkError(this->q_func(), e);
+      }
+    }
+    try
+    {
+      const ctkPluginException& pe = dynamic_cast<const ctkPluginException&>(e);
+      throw pe;
+    }
+    catch (std::bad_cast)
+    {
+      throw ctkPluginException("Failed to get update plugin",
+                               ctkPluginException::UNSPECIFIED, &e);
+    }
+  }
+
+  bool purgeOld = false;
+  // TODO: check if dependent plug-ins are started. If not, set purgeOld to true.
+
+  // Activate new plug-in
+  ctkPluginArchive* oldArchive = archive;
+  archive = newArchive;
+  cachedRawHeaders.clear();
+  state = ctkPlugin::INSTALLED;
+
+  // Purge old archive
+  if (purgeOld)
+  {
+    //secure.purge(this, oldProtectionDomain);
+    if (oldArchive != 0)
+    {
+      oldArchive->purge();
+    }
+  }
+
+  // Broadcast events
+  if (wasResolved)
+  {
+    // TODO: use plugin threading
+    //bundleThread().bundleChanged(new BundleEvent(BundleEvent.UNRESOLVED, this));
+    fwCtx->listeners.emitPluginChanged(ctkPluginEvent(ctkPluginEvent::UNRESOLVED, this->q_func()));
+  }
+  //bundleThread().bundleChanged(new BundleEvent(BundleEvent.UPDATED, this));
+  fwCtx->listeners.emitPluginChanged(ctkPluginEvent(ctkPluginEvent::UPDATED, this->q_func()));
+  operation.fetchAndStoreOrdered(IDLE);
+
+   // Restart plugin previously stopped in the operation
+   if (wasActive)
+   {
+     try
+     {
+       this->q_func().data()->start();
+     }
+     catch (const ctkPluginException& pe)
+     {
+       fwCtx->listeners.frameworkError(this->q_func(), pe);
+     }
+   }
+ }
+
+//----------------------------------------------------------------------------
+int ctkPluginPrivate::getStartLevel()
+{
+  if (archive != 0)
+  {
+    return archive->getStartLevel();
+  }
+  else
+  {
+    return 0;
+  }
+}
+
+//----------------------------------------------------------------------------
 void ctkPluginPrivate::waitOnOperation(LockObject* lock, const QString& src, bool longWait)
 {
   if (operation.fetchAndAddOrdered(0) != IDLE)

+ 12 - 0
Libs/PluginFramework/ctkPluginPrivate_p.h

@@ -162,6 +162,16 @@ public:
   const ctkRuntimeException* stop1();
 
   /**
+   *
+   */
+  void update0(const QUrl &updateLocation, bool wasActive);
+
+  /**
+   *
+   */
+  int getStartLevel();
+
+  /**
    * Wait for an ongoing operation to finish.
    *
    * @param lock QMutex used for locking.
@@ -300,6 +310,8 @@ private:
    */
   void removePluginResources();
 
+  ctkPlugin::State getUpdatedState_unlocked();
+
 };
 
 

+ 0 - 188
Libs/PluginFramework/ctkPluginStorage.cpp

@@ -1,188 +0,0 @@
-/*=============================================================================
-
-  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 "ctkPluginStorage_p.h"
-
-#include <QPluginLoader>
-#include <QFileInfo>
-#include <QUrl>
-#include <QDir>
-
-// CTK includes
-#include "ctkPluginArchive_p.h"
-#include "ctkPluginFrameworkContext_p.h"
-#include "ctkPluginFrameworkUtil_p.h"
-#include "ctkPluginDatabaseException.h"
-
-//----------------------------------------------------------------------------
-ctkPluginStorage::ctkPluginStorage(ctkPluginFrameworkContext* framework)
-  : framework(framework), pluginDatabase(this)
-{
-  // See if we have a storage database
-  QString path = ctkPluginFrameworkUtil::getFileStorage(framework, "").absoluteFilePath("plugins.db");
-
-  pluginDatabase.setDatabasePath(path);
-  pluginDatabase.open();
-  archives << pluginDatabase.getPluginArchives();
-}
-
-//----------------------------------------------------------------------------
-ctkPluginFrameworkContext* ctkPluginStorage::getFrameworkContext() const
-{
-  return framework;
-}
-
-//----------------------------------------------------------------------------
-ctkPluginArchive* ctkPluginStorage::insertPlugin(const QUrl& location, const QString& localPath)
-{
-  ctkPluginArchive* pa = pluginDatabase.insertPlugin(location, localPath);
-  archives.push_back(pa);
-  return pa;
-}
-
-//----------------------------------------------------------------------------
-ctkPluginArchive* ctkPluginStorage::updatePluginArchive(ctkPluginArchive* old, const QString& localPath)
-{
-  Q_UNUSED(old)
-  Q_UNUSED(localPath)
-  //TODO: updatePluginArchive
-  //return new BundleArchiveImpl((BundleArchiveImpl)old, is);
-  return 0;
-}
-
-//----------------------------------------------------------------------------
-void ctkPluginStorage::replacePluginArchive(ctkPluginArchive* oldPA, ctkPluginArchive* newPA)
-{
-  Q_UNUSED(oldPA)
-  Q_UNUSED(newPA)
-  //TODO: replacePluginArchive
-  //    int pos;
-  //    long id = oldBA.getBundleId();
-  //    synchronized (archives) {
-  //      pos = find(id);
-  //      if (pos >= archives.size() || archives.get(pos) != oldBA) {
-  //        throw new Exception("replaceBundleJar: Old bundle archive not found, pos=" + pos);
-  //      }
-  //      archives.set(pos, newBA);
-  //    }
-}
-
-//----------------------------------------------------------------------------
-void ctkPluginStorage::setStartLevel(ctkPluginArchive* pa)
-{
-  pluginDatabase.setStartLevel(pa->getPluginId(), pa->getStartLevel());
-}
-
-//----------------------------------------------------------------------------
-void ctkPluginStorage::setLastModified(ctkPluginArchive* pa)
-{
-  pluginDatabase.setLastModified(pa->getPluginId(), pa->getLastModified());
-}
-
-//----------------------------------------------------------------------------
-void ctkPluginStorage::setAutostartSetting(ctkPluginArchive* pa)
-{
-  pluginDatabase.setAutostartSetting(pa->getPluginId(), pa->getAutostartSetting());
-}
-
-//----------------------------------------------------------------------------
-QList<ctkPluginArchive*> ctkPluginStorage::getAllPluginArchives() const
-{
-  return archives;
-}
-
-//----------------------------------------------------------------------------
-QList<QString> ctkPluginStorage::getStartOnLaunchPlugins()
-{
-  QList<QString> res;
-  QListIterator<ctkPluginArchive*> i(archives);
-  while(i.hasNext())
-  {
-    ctkPluginArchive* pa = i.next();
-    if (pa->getAutostartSetting() != -1)
-    {
-      res.push_back(pa->getPluginLocation().toString());
-    }
-  }
-  return res;
-}
-
-//----------------------------------------------------------------------------
-ctkPluginStorage::~ctkPluginStorage()
-{
-  close();
-  // ctkPluginArchive pointers in archives list are deleted
-  // in ~ctkPluginPrivate()
-}
-
-//----------------------------------------------------------------------------
-void ctkPluginStorage::close()
-{
-  pluginDatabase.close();
-}
-
-//----------------------------------------------------------------------------
-bool ctkPluginStorage::removeArchive(ctkPluginArchive* pa)
-{
-  QMutexLocker lock(&archivesLock);
-
-  bool removed = false;
-  try
-  {
-    pluginDatabase.removeArchive(pa);
-    removed = archives.removeAll(pa);
-  }
-  catch (const ctkPluginDatabaseException& exc)
-  {
-    qDebug() << "Removing plugin archive failed:" << exc;
-    removed = false;
-  }
-
-  return removed;
-}
-
-//----------------------------------------------------------------------------
-QByteArray ctkPluginStorage::getPluginResource(long pluginId, const QString& res) const
-{
-  try
-  {
-    return pluginDatabase.getPluginResource(pluginId, res);
-  }
-  catch (const ctkPluginDatabaseException& exc)
-  {
-    qDebug() << QString("Getting plugin resource %1 failed:").arg(res) << exc;
-    return QByteArray();
-  }
-}
-
-//----------------------------------------------------------------------------
-QStringList ctkPluginStorage::findResourcesPath(long pluginId, const QString& path) const
-{
-  try
-  {
-    return pluginDatabase.findResourcesPath(pluginId, path);
-  }
-  catch (const ctkPluginDatabaseException& exc)
-  {
-    qDebug() << QString("Getting plugin resource paths for %1 failed:").arg(path) << exc;
-    return QStringList();
-  }
-}

+ 395 - 174
Libs/PluginFramework/ctkPluginDatabase.cpp

@@ -19,12 +19,12 @@
 
 =============================================================================*/
 
-#include "ctkPluginDatabase_p.h"
+#include "ctkPluginStorageSQL_p.h"
 #include "ctkPluginDatabaseException.h"
 #include "ctkPlugin.h"
 #include "ctkPluginConstants.h"
 #include "ctkPluginException.h"
-#include "ctkPluginArchive_p.h"
+#include "ctkPluginArchiveSQL_p.h"
 #include "ctkPluginStorage_p.h"
 #include "ctkPluginFrameworkUtil_p.h"
 #include "ctkPluginFrameworkContext_p.h"
@@ -34,8 +34,6 @@
 #include <QFileInfo>
 #include <QUrl>
 
-#include <QDebug>
-
 //database table names
 #define PLUGINS_TABLE "Plugins"
 #define PLUGIN_RESOURCES_TABLE "PluginResources"
@@ -54,19 +52,27 @@ enum TBindIndexes
 };
 
 //----------------------------------------------------------------------------
-ctkPluginDatabase::ctkPluginDatabase(ctkPluginStorage* storage)
-:m_isDatabaseOpen(false), m_inTransaction(false), m_PluginStorage(storage)
+ctkPluginStorageSQL::ctkPluginStorageSQL(ctkPluginFrameworkContext *framework)
+  : m_isDatabaseOpen(false)
+  , m_inTransaction(false)
+  , m_framework(framework)
+  , m_nextFreeId(-1)
 {
+  // See if we have a storage database
+  m_databasePath = ctkPluginFrameworkUtil::getFileStorage(framework, "").absoluteFilePath("plugins.db");
+
+  this->open();
+  restorePluginArchives();
 }
 
 //----------------------------------------------------------------------------
-ctkPluginDatabase::~ctkPluginDatabase()
+ctkPluginStorageSQL::~ctkPluginStorageSQL()
 {
   close();
 }
 
 //----------------------------------------------------------------------------
-void ctkPluginDatabase::open()
+void ctkPluginStorageSQL::open()
 {
   if (m_isDatabaseOpen)
     return;
@@ -75,7 +81,7 @@ void ctkPluginDatabase::open()
 
   //Create full path to database
   if(m_databasePath.isEmpty ())
-      m_databasePath = getDatabasePath();
+    m_databasePath = getDatabasePath();
 
   path = m_databasePath;
   QFileInfo dbFileInfo(path);
@@ -165,14 +171,50 @@ void ctkPluginDatabase::open()
     }
   }
 
-  removeUninstalledPlugins();
+  // silently remove any plugin marked as uninstalled
+  cleanupDB();
 
   //Update database based on the recorded timestamps
   updateDB();
+
+  initNextFreeIds();
 }
 
 //----------------------------------------------------------------------------
-void ctkPluginDatabase::removeUninstalledPlugins()
+void ctkPluginStorageSQL::initNextFreeIds()
+{
+  checkConnection();
+
+  QSqlDatabase database = QSqlDatabase::database(m_connectionName);
+  QSqlQuery query(database);
+
+  QString statement = "SELECT ID,MAX(Generation) FROM " PLUGINS_TABLE " GROUP BY ID";
+  executeQuery(&query, statement);
+
+  while (query.next())
+  {
+    m_generations.insert(query.value(EBindIndex).toInt(),
+                         query.value(EBindIndex1).toInt()+1);
+  }
+
+  query.finish();
+  query.clear();
+
+  statement = "SELECT MAX(ID) FROM " PLUGINS_TABLE;
+  executeQuery(&query, statement);
+  QVariant id = query.isValid() ? query.value(EBindIndex) : QVariant();
+  if (id.isValid())
+  {
+    m_nextFreeId = id.toInt() + 1;
+  }
+  else
+  {
+    m_nextFreeId = 1;
+  }
+}
+
+//----------------------------------------------------------------------------
+void ctkPluginStorageSQL::cleanupDB()
 {
   checkConnection();
 
@@ -183,8 +225,13 @@ void ctkPluginDatabase::removeUninstalledPlugins()
 
   try
   {
-    QString statement = "DELETE FROM Plugins WHERE StartLevel==-2";
+    // remove all plug-ins marked as UNINSTALLED
+    QString statement = "DELETE FROM " PLUGINS_TABLE " WHERE StartLevel==-2";
     executeQuery(&query, statement);
+
+    // remove all old plug-in generations
+    statement = "DELETE FROM " PLUGINS_TABLE
+                " WHERE K NOT IN (SELECT K FROM (SELECT K, MAX(Generation) FROM " PLUGINS_TABLE " GROUP BY ID))";
   }
   catch (...)
   {
@@ -196,7 +243,7 @@ void ctkPluginDatabase::removeUninstalledPlugins()
 }
 
 //----------------------------------------------------------------------------
-void ctkPluginDatabase::updateDB()
+void ctkPluginStorageSQL::updateDB()
 {
   checkConnection();
 
@@ -205,27 +252,42 @@ void ctkPluginDatabase::updateDB()
 
   beginTransaction(&query, Write);
 
-  QString statement = "SELECT ID, Location, LocalPath, Timestamp, SymbolicName, Version FROM Plugins WHERE State != ?";
-  QList<QVariant> bindValues;
-  bindValues.append(ctkPlugin::UNINSTALLED);
+  // 1. Get the state information of all plug-ins (it is assumed that
+  //    plug-ins marked as UNINSTALLED (startlevel == -2) are already removed
 
-  QList<qlonglong> outdatedIds;
-  QList<QPair<QString,QString> > outdatedPlugins;
-  QStringList outdatedServiceNames;
+  QString statement = "SELECT ID,MAX(Generation),Location,LocalPath,Timestamp,StartLevel,AutoStart,K "
+                      "FROM " PLUGINS_TABLE " GROUP BY ID";
+
+  QList<int> outdatedIds;
+  QList<ctkPluginArchiveSQL*> updatedPluginArchives;
   try
   {
-    executeQuery(&query, statement, bindValues);
+    executeQuery(&query, statement);
+
+    // 2. Check the timestamp for each plug-in
 
     while (query.next())
     {
-      QFileInfo pluginInfo(query.value(EBindIndex2).toString());
+      QFileInfo pluginInfo(query.value(EBindIndex3).toString());
       QDateTime pluginLastModified = pluginInfo.lastModified();
       // Make sure the QDateTime has the same accuracy as the one in the database
-      pluginLastModified = getQDateTimeFromString(getStringFromQDateTime(pluginLastModified));      
-      if (pluginLastModified > getQDateTimeFromString(query.value(EBindIndex3).toString()))
+      pluginLastModified = getQDateTimeFromString(getStringFromQDateTime(pluginLastModified));
+
+      if (pluginLastModified > getQDateTimeFromString(query.value(EBindIndex4).toString()))
       {
-        outdatedIds.append(query.value(EBindIndex).toLongLong());
-        outdatedPlugins.append(qMakePair(query.value(EBindIndex1).toString(), query.value(EBindIndex2).toString()));
+        ctkPluginArchiveSQL* updatedPA =
+            new ctkPluginArchiveSQL(this,
+                                    query.value(EBindIndex2).toUrl(),    // plug-in location url
+                                    query.value(EBindIndex3).toString(), // plugin local path
+                                    query.value(EBindIndex).toInt(),     // plugin id
+                                    query.value(EBindIndex5).toInt(),    // start level
+                                    QDateTime(),                         // last modififed
+                                    query.value(EBindIndex6).toInt());   // auto start setting
+        updatedPA->key = query.value(EBindIndex7).toInt();
+        updatedPluginArchives << updatedPA;
+
+        // remember the plug-in ids for deletion
+        outdatedIds << query.value(EBindIndex).toInt();
       }
     }
   }
@@ -238,39 +300,56 @@ void ctkPluginDatabase::updateDB()
   query.finish();
   query.clear();
 
-  try
+  if (!outdatedIds.isEmpty())
   {
-    statement = "DELETE FROM Plugins WHERE ID=?";
-    QListIterator<qlonglong> idIter(outdatedIds);
-    while (idIter.hasNext())
+
+    // 3. Remove all traces from outdated plug-in data. Due to cascaded delete,
+    //    it is sufficient to remove the records from the main table
+
+    statement = "DELETE FROM " PLUGINS_TABLE " WHERE K IN (%1)";
+    QString idStr;
+    foreach(int id, outdatedIds)
     {
-      bindValues.clear();
-      bindValues.append(idIter.next());
-      executeQuery(&query, statement, bindValues);
+      idStr += QString::number(id) + ",";
     }
-  }
-  catch (...)
-  {
-    rollbackTransaction(&query);
-    throw;
-  }
+    idStr.chop(1);
 
-  commitTransaction(&query);
+    try
+    {
+      executeQuery(&query, statement.arg(idStr));
+    }
+    catch (...)
+    {
+      rollbackTransaction(&query);
+      throw;
+    }
 
-  QListIterator<QPair<QString,QString> > locationIter(outdatedPlugins);
-  while (locationIter.hasNext())
-  {
-    const QPair<QString,QString>& locations = locationIter.next();
-    insertPlugin(QUrl(locations.first), locations.second, false);
+    query.finish();
+    query.clear();
+
+    try
+    {
+      foreach (ctkPluginArchiveSQL* updatedPA, updatedPluginArchives)
+      {
+        insertArchive(updatedPA, &query);
+      }
+    }
+    catch (...)
+    {
+      rollbackTransaction(&query);
+      throw;
+    }
   }
+
+  commitTransaction(&query);
 }
 
 //----------------------------------------------------------------------------
-QLibrary::LoadHints ctkPluginDatabase::getPluginLoadHints() const
+QLibrary::LoadHints ctkPluginStorageSQL::getPluginLoadHints() const
 {
-  if (m_PluginStorage->getFrameworkContext()->props.contains(ctkPluginConstants::FRAMEWORK_PLUGIN_LOAD_HINTS))
+  if (m_framework->props.contains(ctkPluginConstants::FRAMEWORK_PLUGIN_LOAD_HINTS))
   {
-    QVariant loadHintsVariant = m_PluginStorage->getFrameworkContext()->props[ctkPluginConstants::FRAMEWORK_PLUGIN_LOAD_HINTS]; 
+    QVariant loadHintsVariant = m_framework->props[ctkPluginConstants::FRAMEWORK_PLUGIN_LOAD_HINTS];
     if (loadHintsVariant.isValid())
     {
       return loadHintsVariant.value<QLibrary::LoadHints>();
@@ -280,11 +359,10 @@ QLibrary::LoadHints ctkPluginDatabase::getPluginLoadHints() const
 }
 
 //----------------------------------------------------------------------------
-ctkPluginArchive* ctkPluginDatabase::insertPlugin(const QUrl& location, const QString& localPath, bool createArchive)
+ctkPluginArchive* ctkPluginStorageSQL::insertPlugin(const QUrl& location, const QString& localPath)
 {
-  checkConnection();
+  QMutexLocker lock(&m_archivesLock);
 
-  // Assemble the data for the sql record
   QFileInfo fileInfo(localPath);
   if (!fileInfo.exists())
   {
@@ -293,40 +371,36 @@ ctkPluginArchive* ctkPluginDatabase::insertPlugin(const QUrl& location, const QS
 
   const QString libTimestamp = getStringFromQDateTime(fileInfo.lastModified());
 
-  QString resourcePrefix = fileInfo.baseName();
-  if (resourcePrefix.startsWith("lib"))
+  ctkPluginArchiveSQL* archive = new ctkPluginArchiveSQL(this, location, localPath,
+                                                         m_nextFreeId++);
+  try
   {
-    resourcePrefix = resourcePrefix.mid(3);
+    insertArchive(archive);
+    m_archives << archive;
+    return archive;
   }
-  resourcePrefix.replace("_", ".");
+  catch(...)
+  {
+    delete archive;
+    m_nextFreeId--;
+    throw;
+  }
+  return 0;
+}
 
-  resourcePrefix = QString(":/") + resourcePrefix + "/";
+//----------------------------------------------------------------------------
+void ctkPluginStorageSQL::insertArchive(ctkPluginArchiveSQL* pa)
+{
+  checkConnection();
 
   QSqlDatabase database = QSqlDatabase::database(m_connectionName);
   QSqlQuery query(database);
 
   beginTransaction(&query, Write);
 
-  QString statement = "INSERT INTO Plugins(Location,LocalPath,SymbolicName,Version,State,LastModified,Timestamp,StartLevel,AutoStart)"
-      "VALUES(?,?,?,?,?,'',?,-1,-1)";
-
-  QList<QVariant> bindValues;
-  bindValues.append(location.toString());
-  bindValues.append(localPath);
-  bindValues.append(QString("na"));
-  bindValues.append(QString("na"));
-  bindValues.append(ctkPlugin::INSTALLED);
-  bindValues.append(libTimestamp);
-
-  qlonglong pluginId = -1;
   try
   {
-    executeQuery(&query, statement, bindValues);
-    QVariant lastId = query.lastInsertId();
-    if (lastId.isValid())
-    {
-      pluginId = lastId.toLongLong();
-    }
+    insertArchive(pa, &query);
   }
   catch (...)
   {
@@ -334,18 +408,69 @@ ctkPluginArchive* ctkPluginDatabase::insertPlugin(const QUrl& location, const QS
     throw;
   }
 
+  commitTransaction(&query);
+}
+
+//----------------------------------------------------------------------------
+void ctkPluginStorageSQL::insertArchive(ctkPluginArchiveSQL* pa, QSqlQuery* query)
+{
+
+  QFileInfo fileInfo(pa->getLibLocation());
+  QString libTimestamp = getStringFromQDateTime(fileInfo.lastModified());
+
+  QString resourcePrefix = fileInfo.baseName();
+  if (resourcePrefix.startsWith("lib"))
+  {
+    resourcePrefix = resourcePrefix.mid(3);
+  }
+  resourcePrefix.replace("_", ".");
+  resourcePrefix = QString(":/") + resourcePrefix + "/";
+
   // Load the plugin and cache the resources
+
   QPluginLoader pluginLoader;
   pluginLoader.setLoadHints(getPluginLoadHints());
-  pluginLoader.setFileName(localPath);
+  pluginLoader.setFileName(pa->getLibLocation());
   if (!pluginLoader.load())
   {
-    rollbackTransaction(&query);
-    ctkPluginException exc(QString("The plugin could not be loaded: %1").arg(localPath));
+    ctkPluginException exc(QString("The plugin could not be loaded: %1").arg(pa->getLibLocation()));
     exc.setCause(pluginLoader.errorString());
     throw exc;
   }
 
+  QFile manifestResource(resourcePrefix + "META-INF/MANIFEST.MF");
+  manifestResource.open(QIODevice::ReadOnly);
+  QByteArray manifest = manifestResource.readAll();
+  manifestResource.close();
+
+  // Finally, complete the ctkPluginArchive information by reading the MANIFEST.MF resource
+  pa->readManifest(manifest);
+
+  // Assemble the data for the sql records
+
+  QString version = pa->getAttribute(ctkPluginConstants::PLUGIN_VERSION);
+  if (version.isEmpty()) version = "na";
+
+  QString statement = "INSERT INTO " PLUGINS_TABLE " (ID,Generation,Location,LocalPath,SymbolicName,Version,LastModified,Timestamp,StartLevel,AutoStart) "
+                      "VALUES (?,?,?,?,?,?,?,?,?,?)";
+
+  QList<QVariant> bindValues;
+  bindValues << pa->getPluginId();
+  bindValues << pa->getPluginGeneration();
+  bindValues << pa->getPluginLocation();
+  bindValues << pa->getLibLocation();
+  bindValues << pa->getAttribute(ctkPluginConstants::PLUGIN_SYMBOLICNAME);
+  bindValues << version;
+  bindValues << "na";
+  bindValues << libTimestamp;
+  bindValues << pa->getStartLevel();
+  bindValues << pa->getAutostartSetting();
+
+  executeQuery(query, statement, bindValues);
+
+  pa->key = query->lastInsertId().toInt();
+
+  // Write the plug-in resource data into the database
   QDirIterator dirIter(resourcePrefix, QDirIterator::Subdirectories);
   while (dirIter.hasNext())
   {
@@ -357,105 +482,188 @@ ctkPluginArchive* ctkPluginDatabase::insertPlugin(const QUrl& location, const QS
     QByteArray resourceData = resourceFile.readAll();
     resourceFile.close();
 
-    statement = "INSERT INTO PluginResources(PluginID, ResourcePath, Resource) VALUES(?,?,?)";
+    statement = "INSERT INTO " PLUGIN_RESOURCES_TABLE " (K,ResourcePath,Resource) VALUES(?,?,?)";
     bindValues.clear();
-    bindValues.append(QVariant::fromValue<qlonglong>(pluginId));
-    bindValues.append(resourcePath.mid(resourcePrefix.size()-1));
-    bindValues.append(resourceData);
+    bindValues << pa->key;
+    bindValues << resourcePath.mid(resourcePrefix.size()-1);
+    bindValues << resourceData;
 
-    try
-    {
-      executeQuery(&query, statement, bindValues);
-    }
-    catch (...)
-    {
-      rollbackTransaction(&query);
-      throw;
-    }
+    executeQuery(query, statement, bindValues);
   }
 
   pluginLoader.unload();
+}
+
+//----------------------------------------------------------------------------
+ctkPluginArchive* ctkPluginStorageSQL::updatePluginArchive(ctkPluginArchive* old,
+                                                           const QUrl& updateLocation,
+                                                           const QString& localPath)
+{
+  ctkPluginArchiveSQL* newPA = new ctkPluginArchiveSQL(
+                                 static_cast<ctkPluginArchiveSQL*>(old),
+                                 m_generations[old->getPluginId()]++,
+                                 updateLocation, localPath);
+  return newPA;
+
+}
+
+void ctkPluginStorageSQL::replacePluginArchive(ctkPluginArchive *oldPA, ctkPluginArchive *newPA)
+{
+  QMutexLocker lock(&m_archivesLock);
+
+  int pos;
+  long id = oldPA->getPluginId();
+  pos = find(id);
+  if (pos >= m_archives.size() || m_archives[pos] != oldPA)
+  {
+    throw ctkRuntimeException(QString("replacePluginArchive: Old plugin archive not found, pos=").append(pos));
+  }
+
+  checkConnection();
+
+  QSqlDatabase database = QSqlDatabase::database(m_connectionName);
+  QSqlQuery query(database);
+
+  beginTransaction(&query, Write);
 
   try
   {
-    ctkPluginArchive* archive = new ctkPluginArchive(m_PluginStorage, location, localPath,
-                                                     pluginId);
+    removeArchiveFromDB(static_cast<ctkPluginArchiveSQL*>(oldPA), &query);
+    insertArchive(static_cast<ctkPluginArchiveSQL*>(newPA), &query);
 
-    statement = "UPDATE Plugins SET SymbolicName=?,Version=? WHERE ID=?";
-    QString versionString = archive->getAttribute(ctkPluginConstants::PLUGIN_VERSION);
-    bindValues.clear();
-    bindValues.append(archive->getAttribute(ctkPluginConstants::PLUGIN_SYMBOLICNAME));
-    bindValues.append(versionString.isEmpty() ? "0.0.0" : versionString);
-    bindValues.append(pluginId);
+    commitTransaction(&query);
+    m_archives[pos] = newPA;
+  }
+  catch (const ctkRuntimeException& re)
+  {
+    rollbackTransaction(&query);
+    qWarning() << "Removing plug-in archive failed:" << re;
+    throw;
+  }
+  catch (...)
+  {
+    rollbackTransaction(&query);
+    throw;
+  }
+}
 
-    if (!createArchive)
-    {
-      delete archive;
-      archive = 0;
-    }
+//----------------------------------------------------------------------------
+bool ctkPluginStorageSQL::removeArchive(ctkPluginArchive *pa)
+{
+  checkConnection();
 
-    executeQuery(&query, statement, bindValues);
+  QSqlDatabase database = QSqlDatabase::database(m_connectionName);
+  QSqlQuery query(database);
 
+  beginTransaction(&query, Write);
+
+  try
+  {
+    removeArchiveFromDB(static_cast<ctkPluginArchiveSQL*>(pa), &query);
     commitTransaction(&query);
 
-    return archive;
+    QMutexLocker lock(&m_archivesLock);
+    m_archives.removeAll(pa);
+    return true;
+  }
+  catch (const ctkRuntimeException& re)
+  {
+    rollbackTransaction(&query);
+    qWarning() << "Removing plug-in archive failed:" << re;
   }
   catch (...)
   {
-      rollbackTransaction(&query);
-      throw;
+    qWarning() << "Removing plug-in archive failed: Unexpected exception";
+    rollbackTransaction(&query);
   }
+  return false;
+}
+
+//----------------------------------------------------------------------------
+void ctkPluginStorageSQL::removeArchiveFromDB(ctkPluginArchiveSQL* pa, QSqlQuery* query)
+{
+  QString statement = "DELETE FROM " PLUGINS_TABLE " WHERE K=?";
+
+  QList<QVariant> bindValues;
+  bindValues.append(pa->key);
 
+  executeQuery(query, statement, bindValues);
+}
+
+QList<ctkPluginArchive*> ctkPluginStorageSQL::getAllPluginArchives() const
+{
+  return m_archives;
+}
+
+QList<QString> ctkPluginStorageSQL::getStartOnLaunchPlugins() const
+{
+  QList<QString> res;
+  QListIterator<ctkPluginArchive*> i(m_archives);
+  while(i.hasNext())
+  {
+    ctkPluginArchive* pa = i.next();
+    if (pa->getAutostartSetting() != -1)
+    {
+      res.push_back(pa->getPluginLocation().toString());
+    }
+  }
+  return res;
 }
 
 //----------------------------------------------------------------------------
-void ctkPluginDatabase::setStartLevel(long pluginId, int startLevel)
+void ctkPluginStorageSQL::setStartLevel(int key, int startLevel)
 {
+  checkConnection();
+
   QSqlDatabase database = QSqlDatabase::database(m_connectionName);
   QSqlQuery query(database);
 
-  QString statement = "UPDATE Plugins SET StartLevel=? WHERE ID=?";
+  QString statement = "UPDATE " PLUGINS_TABLE " SET StartLevel=? WHERE K=?";
   QList<QVariant> bindValues;
   bindValues.append(startLevel);
-  bindValues.append(QVariant::fromValue(pluginId));
+  bindValues.append(key);
 
   executeQuery(&query, statement, bindValues);
 }
 
 //----------------------------------------------------------------------------
-void ctkPluginDatabase::setLastModified(long pluginId, const QDateTime& lastModified)
+void ctkPluginStorageSQL::setLastModified(int key, const QDateTime& lastModified)
 {
+  checkConnection();
+
   QSqlDatabase database = QSqlDatabase::database(m_connectionName);
   QSqlQuery query(database);
 
-  QString statement = "UPDATE Plugins SET LastModified=? WHERE ID=?";
+  QString statement = "UPDATE " PLUGINS_TABLE " SET LastModified=? WHERE K=?";
   QList<QVariant> bindValues;
   bindValues.append(getStringFromQDateTime(lastModified));
-  bindValues.append(QVariant::fromValue(pluginId));
+  bindValues.append(key);
 
   executeQuery(&query, statement, bindValues);
 }
 
 //----------------------------------------------------------------------------
-void ctkPluginDatabase::setAutostartSetting(long pluginId, int autostart)
+void ctkPluginStorageSQL::setAutostartSetting(int key, int autostart)
 {
+  checkConnection();
+
   QSqlDatabase database = QSqlDatabase::database(m_connectionName);
   QSqlQuery query(database);
 
-  QString statement = "UPDATE Plugins SET AutoStart=? WHERE ID=?";
+  QString statement = "UPDATE " PLUGINS_TABLE " SET AutoStart=? WHERE K=?";
   QList<QVariant> bindValues;
   bindValues.append(autostart);
-  bindValues.append(QVariant::fromValue(pluginId));
+  bindValues.append(key);
 
   executeQuery(&query, statement, bindValues);
 }
 
 //----------------------------------------------------------------------------
-QStringList ctkPluginDatabase::findResourcesPath(long pluginId, const QString& path) const
+QStringList ctkPluginStorageSQL::findResourcesPath(int archiveKey, const QString& path) const
 {
   checkConnection();
 
-  QString statement = "SELECT SUBSTR(ResourcePath,?) FROM PluginResources WHERE PluginID=? AND SUBSTR(ResourcePath,1,?)=?";
+  QString statement = "SELECT SUBSTR(ResourcePath,?) FROM PluginResources WHERE K=? AND SUBSTR(ResourcePath,1,?)=?";
 
   QString resourcePath = path.startsWith('/') ? path : QString("/") + path;
   if (!resourcePath.endsWith('/'))
@@ -463,7 +671,7 @@ QStringList ctkPluginDatabase::findResourcesPath(long pluginId, const QString& p
 
   QList<QVariant> bindValues;
   bindValues.append(resourcePath.size()+1);
-  bindValues.append(qlonglong(pluginId));
+  bindValues.append(archiveKey);
   bindValues.append(resourcePath.size());
   bindValues.append(resourcePath);
 
@@ -491,23 +699,7 @@ QStringList ctkPluginDatabase::findResourcesPath(long pluginId, const QString& p
 }
 
 //----------------------------------------------------------------------------
-void ctkPluginDatabase::removeArchive(const ctkPluginArchive *pa)
-{
-  checkConnection();
-
-  QSqlDatabase database = QSqlDatabase::database(m_connectionName);
-  QSqlQuery query(database);
-
-  QString statement = "DELETE FROM Plugins WHERE ID=?";
-
-  QList<QVariant> bindValues;
-  bindValues.append(pa->getPluginId());
-
-  executeQuery(&query, statement, bindValues);
-}
-
-//----------------------------------------------------------------------------
-void ctkPluginDatabase::executeQuery(QSqlQuery *query, const QString &statement, const QList<QVariant> &bindValues) const
+void ctkPluginStorageSQL::executeQuery(QSqlQuery *query, const QString &statement, const QList<QVariant> &bindValues) const
 {
   Q_ASSERT(query != 0);
 
@@ -567,7 +759,7 @@ void ctkPluginDatabase::executeQuery(QSqlQuery *query, const QString &statement,
 }
 
 //----------------------------------------------------------------------------
-void ctkPluginDatabase::close()
+void ctkPluginStorageSQL::close()
 {
   if (m_isDatabaseOpen)
   {
@@ -589,13 +781,13 @@ void ctkPluginDatabase::close()
 }
 
 //----------------------------------------------------------------------------
-void ctkPluginDatabase::setDatabasePath(const QString &databasePath)
+void ctkPluginStorageSQL::setDatabasePath(const QString &databasePath)
 {
     m_databasePath = QDir::toNativeSeparators(databasePath);
 }
 
 //----------------------------------------------------------------------------
-QString ctkPluginDatabase::getDatabasePath() const
+QString ctkPluginStorageSQL::getDatabasePath() const
 {
   QString path = m_databasePath;
   if(path.isEmpty())
@@ -610,18 +802,18 @@ QString ctkPluginDatabase::getDatabasePath() const
 }
 
 //----------------------------------------------------------------------------
-QByteArray ctkPluginDatabase::getPluginResource(long pluginId, const QString& res) const
+QByteArray ctkPluginStorageSQL::getPluginResource(int key, const QString& res) const
 {
   checkConnection();
 
   QSqlDatabase database = QSqlDatabase::database(m_connectionName);
   QSqlQuery query(database);
 
-  QString statement = "SELECT Resource FROM PluginResources WHERE PluginID=? AND ResourcePath=?";
+  QString statement = "SELECT Resource FROM PluginResources WHERE K=? AND ResourcePath=?";
 
   QString resourcePath = res.startsWith('/') ? res : QString("/") + res;
   QList<QVariant> bindValues;
-  bindValues.append(qlonglong(pluginId));
+  bindValues.append(key);
   bindValues.append(resourcePath);
 
   executeQuery(&query, statement, bindValues);
@@ -635,7 +827,7 @@ QByteArray ctkPluginDatabase::getPluginResource(long pluginId, const QString& re
 }
 
 //----------------------------------------------------------------------------
-void ctkPluginDatabase::createTables()
+void ctkPluginStorageSQL::createTables()
 {
     QSqlDatabase database = QSqlDatabase::database(m_connectionName);
     QSqlQuery query(database);
@@ -643,13 +835,14 @@ void ctkPluginDatabase::createTables()
     //Begin Transaction
     beginTransaction(&query, Write);
 
-    QString statement("CREATE TABLE Plugins("
-                      "ID INTEGER PRIMARY KEY,"
-                      "Location TEXT NOT NULL UNIQUE,"
-                      "LocalPath TEXT NOT NULL UNIQUE,"
+    QString statement("CREATE TABLE " PLUGINS_TABLE " ("
+                      "K INTEGER PRIMARY KEY,"
+                      "ID INTEGER NOT NULL,"
+                      "Generation INTEGER NOT NULL,"
+                      "Location TEXT NOT NULL,"
+                      "LocalPath TEXT NOT NULL,"
                       "SymbolicName TEXT NOT NULL,"
                       "Version TEXT NOT NULL,"
-                      "State INTEGER NOT NULL,"
                       "LastModified TEXT NOT NULL,"
                       "Timestamp TEXT NOT NULL,"
                       "StartLevel INTEGER NOT NULL,"
@@ -664,11 +857,11 @@ void ctkPluginDatabase::createTables()
       throw;
     }
 
-    statement = "CREATE TABLE PluginResources("
-                "PluginID INTEGER NOT NULL,"
-                "ResourcePath TEXT NOT NULL, "
+    statement = "CREATE TABLE " PLUGIN_RESOURCES_TABLE " ("
+                "K INTEGER NOT NULL,"
+                "ResourcePath TEXT NOT NULL,"
                 "Resource BLOB NOT NULL,"
-                "FOREIGN KEY(PluginID) REFERENCES Plugins(ID) ON DELETE CASCADE)";
+                "FOREIGN KEY(K) REFERENCES " PLUGINS_TABLE "(K) ON DELETE CASCADE)";
     try
     {
       executeQuery(&query, statement);
@@ -692,12 +885,12 @@ void ctkPluginDatabase::createTables()
 }
 
 //----------------------------------------------------------------------------
-bool ctkPluginDatabase::checkTables() const
+bool ctkPluginStorageSQL::checkTables() const
 {
   bool bTables(false);
   QStringList tables = QSqlDatabase::database(m_connectionName).tables();
-  if (tables.contains(PLUGINS_TABLE)
-      && tables.contains(PLUGIN_RESOURCES_TABLE))
+  if (tables.contains(PLUGINS_TABLE) &&
+      tables.contains(PLUGIN_RESOURCES_TABLE))
   {
     bTables = true;
   }
@@ -705,7 +898,7 @@ bool ctkPluginDatabase::checkTables() const
 }
 
 //----------------------------------------------------------------------------
-bool ctkPluginDatabase::dropTables()
+bool ctkPluginStorageSQL::dropTables()
 {
   //Execute transaction for deleting the database tables
   QSqlDatabase database = QSqlDatabase::database(m_connectionName);
@@ -747,13 +940,42 @@ bool ctkPluginDatabase::dropTables()
 }
 
 //----------------------------------------------------------------------------
-bool ctkPluginDatabase::isOpen() const
+bool ctkPluginStorageSQL::isOpen() const
 {
   return m_isDatabaseOpen;
 }
 
+int ctkPluginStorageSQL::find(long id) const
+{
+  int lb = 0;
+  int ub = m_archives.size() - 1;
+  int x = 0;
+  while (lb < ub)
+  {
+    x = (lb + ub) / 2;
+    long xid = m_archives[x]->getPluginId();
+    if (id == xid)
+    {
+      return x;
+    }
+    else if (id < xid)
+    {
+      ub = x;
+    }
+    else
+    {
+      lb = x+1;
+    }
+  }
+  if (lb < m_archives.size() && m_archives[lb]->getPluginId() < id)
+  {
+    return lb + 1;
+  }
+  return lb;
+}
+
 //----------------------------------------------------------------------------
-void ctkPluginDatabase::checkConnection() const
+void ctkPluginStorageSQL::checkConnection() const
 {
   if(!m_isDatabaseOpen)
   {
@@ -768,7 +990,7 @@ void ctkPluginDatabase::checkConnection() const
 }
 
 //----------------------------------------------------------------------------
-void ctkPluginDatabase::beginTransaction(QSqlQuery *query, TransactionType type)
+void ctkPluginStorageSQL::beginTransaction(QSqlQuery *query, TransactionType type)
 {
   bool success;
   if (type == Read)
@@ -796,7 +1018,7 @@ void ctkPluginDatabase::beginTransaction(QSqlQuery *query, TransactionType type)
 }
 
 //----------------------------------------------------------------------------
-void ctkPluginDatabase::commitTransaction(QSqlQuery *query)
+void ctkPluginStorageSQL::commitTransaction(QSqlQuery *query)
 {
   Q_ASSERT(query != 0);
   query->finish();
@@ -809,7 +1031,7 @@ void ctkPluginDatabase::commitTransaction(QSqlQuery *query)
 }
 
 //----------------------------------------------------------------------------
-void ctkPluginDatabase::rollbackTransaction(QSqlQuery *query)
+void ctkPluginStorageSQL::rollbackTransaction(QSqlQuery *query)
 {
   Q_ASSERT(query !=0);
   query->finish();
@@ -823,18 +1045,17 @@ void ctkPluginDatabase::rollbackTransaction(QSqlQuery *query)
 }
 
 //----------------------------------------------------------------------------
-QList<ctkPluginArchive*> ctkPluginDatabase::getPluginArchives() const
+void ctkPluginStorageSQL::restorePluginArchives()
 {
   checkConnection();
 
   QSqlQuery query(QSqlDatabase::database(m_connectionName));
-  QString statement("SELECT ID, Location, LocalPath, StartLevel, LastModified, AutoStart FROM Plugins WHERE State != ?");
-  QList<QVariant> bindValues;
-  bindValues.append(ctkPlugin::UNINSTALLED);
+  QString statement = "SELECT ID, Location, LocalPath, StartLevel, LastModified, AutoStart, K, MAX(Generation)"
+                      " FROM " PLUGINS_TABLE " WHERE StartLevel != -2 GROUP BY ID"
+                      " ORDER BY ID";
 
-  executeQuery(&query, statement, bindValues);
+  executeQuery(&query, statement);
 
-  QList<ctkPluginArchive*> archives;
   while (query.next())
   {
     const long id = query.value(EBindIndex).toLongLong();
@@ -848,32 +1069,32 @@ QList<ctkPluginArchive*> ctkPluginDatabase::getPluginArchives() const
     }
 
     const int startLevel = query.value(EBindIndex3).toInt();
-    const QDateTime lastModified = query.value(EBindIndex4).toDateTime();
+    const QDateTime lastModified = getQDateTimeFromString(query.value(EBindIndex4).toString());
     const int autoStart = query.value(EBindIndex5).toInt();
 
     try
     {
-      ctkPluginArchive* pa = new ctkPluginArchive(m_PluginStorage, location, localPath, id,
-                                                  startLevel, lastModified, autoStart);
-      archives.append(pa);
+      ctkPluginArchiveSQL* pa = new ctkPluginArchiveSQL(this, location, localPath, id,
+                                                        startLevel, lastModified, autoStart);
+      pa->key = query.value(EBindIndex6).toInt();
+      pa->readManifest();
+      m_archives.append(pa);
     }
     catch (const ctkPluginException& exc)
     {
       qWarning() << exc;
     }
   }
-
-  return archives;
 }
 
 //----------------------------------------------------------------------------
-QString ctkPluginDatabase::getStringFromQDateTime(const QDateTime& dateTime) const
+QString ctkPluginStorageSQL::getStringFromQDateTime(const QDateTime& dateTime) const
 {
   return dateTime.toString(Qt::ISODate);
 }
 
 //----------------------------------------------------------------------------
-QDateTime ctkPluginDatabase::getQDateTimeFromString(const QString& dateTimeString) const
+QDateTime ctkPluginStorageSQL::getQDateTimeFromString(const QString& dateTimeString) const
 {
   return QDateTime::fromString(dateTimeString, Qt::ISODate);
 }

+ 134 - 51
Libs/PluginFramework/ctkPluginDatabase_p.h

@@ -19,39 +19,90 @@
 
 =============================================================================*/
 
-#ifndef CTKPLUGINDATABASE_P_H
-#define CTKPLUGINDATABASE_P_H
+#ifndef ctkPluginStorageSQL_P_H
+#define ctkPluginStorageSQL_P_H
+
+#include "ctkPluginStorage_p.h"
 
 #include <QtSql>
-#include <QList>
 
 // CTK class forward declarations
-class ctkPluginStorage;
-class ctkPluginArchive;
+class ctkPluginFrameworkContext;
+class ctkPluginArchiveSQL;
 
 /**
  * \ingroup PluginFramework
  */
-class ctkPluginDatabase
+class ctkPluginStorageSQL : public ctkPluginStorage
 {
 
 public:
 
-  ctkPluginDatabase(ctkPluginStorage* storage);
+  /**
+   * Create a container for all plugin data in this framework.
+   * Try to restore all saved plugin archive state.
+   *
+   */
+  ctkPluginStorageSQL(ctkPluginFrameworkContext* framework);
 
-  virtual ~ctkPluginDatabase();
+  virtual ~ctkPluginStorageSQL();
 
   /**
-   * Opens the plugin database. If the database does not
-   * yet exist, it is created using the path from getDatabasePath().
+   * Inserts a new plugin into the database. This method assumes that
+   * the an entry with the same \a location and \a localPath does not
+   * yet exist in the database.
    *
-   * @see setDatabasePath(const QString&)
-   * @see getDatabasePath()
-   * @see ctkPluginDatabaseException
+   * @param location The URL to the plugin.
+   * @param updateLocation Location of the updated plugin.
+   * @param localPath The path to the plugin library on the local file system.
+   * @param createArchive If \c true (default) a new ctkPluginArchive instance is returned.
    *
    * @throws ctkPluginDatabaseException
    */
-  void open();
+  ctkPluginArchive* insertPlugin(const QUrl& location, const QString& localPath);
+
+  /**
+   * Insert a new plugin (shared library) into the persistent
+   * storagedata as an update
+   * to an existing plugin archive. To commit this data a call to
+   * <code>replacePluginArchive</code> is needed.
+   *
+   * @param old ctkPluginArchive to be replaced.
+   * @param localPath Path to a plugin on the local file system.
+   * @return Plugin archive object.
+   */
+  ctkPluginArchive* updatePluginArchive(ctkPluginArchive* old, const QUrl& updateLocation, const QString& localPath);
+
+  /**
+   * Replace old plugin archive with a new updated plugin archive, that
+   * was created with updatePluginArchive.
+   *
+   * @param oldPA ctkPluginArchive to be replaced.
+   * @param newPA new ctkPluginArchive.
+   */
+  void replacePluginArchive(ctkPluginArchive* oldPA, ctkPluginArchive* newPA);
+
+  /**
+   * Removes all persisted data related to the given ctkPluginArchive.
+   *
+   * @throws ctkPluginDatabaseException
+   */
+  bool removeArchive(ctkPluginArchive* pa);
+
+  /**
+   * Get all plugin archive objects.
+   *
+   * @return QList of all PluginArchives.
+   */
+  QList<ctkPluginArchive*> getAllPluginArchives() const;
+
+  /**
+   * Get all plugins to start at next launch of framework.
+   * This list is sorted in increasing plugin id order.
+   *
+   * @return A List with plugin locations.
+   */
+  QList<QString> getStartOnLaunchPlugins() const;
 
   /**
    * Closes the plugin database. Throws a ctkPluginDatabaseException
@@ -61,10 +112,9 @@ public:
    */
   void close();
 
-  /**
-   * Checks if the database is open
-   */
-  bool isOpen() const;
+  // -------------------------------------------------------------
+  // end ctkPluginStorage interface
+  // -------------------------------------------------------------
 
   /**
    * Sets the path of the service database to \a databasePath
@@ -87,7 +137,7 @@ public:
    *
    * @throws ctkPluginDatabaseException
    */
-  QByteArray getPluginResource(long pluginId, const QString& res) const;
+  QByteArray getPluginResource(int key, const QString& res) const;
 
   /**
    * Get a list of resource entries under the given path.
@@ -98,27 +148,7 @@ public:
    *
    * @throws ctkPluginDatabaseException
    */
-  QStringList findResourcesPath(long pluginId, const QString& path) const;
-
-  /**
-   * Inserts a new plugin into the database. This method assumes that
-   * the an entry with the same \a location and \a localPath does not
-   * yet exist in the database.
-   *
-   * @param location The URL to the plugin.
-   * @param localPath The path to the plugin library on the local file system.
-   * @param createArchive If \c true (default) a new ctkPluginArchive instance is returned.
-   *
-   * @throws ctkPluginDatabaseException
-   */
-  ctkPluginArchive* insertPlugin(const QUrl& location, const QString& localPath, bool createArchive = true);
-
-  /**
-   * Removes all persisted data related to the given ctkPluginArchive.
-   *
-   * @throws ctkPluginDatabaseException
-   */
-  void removeArchive(const ctkPluginArchive* pa);
+  QStringList findResourcesPath(int archiveKey, const QString& path) const;
 
   /**
    * Persist the start level
@@ -126,7 +156,7 @@ public:
    * @param pluginId The Plugin id
    * @param startLevel The new start level
    */
-  void setStartLevel(long pluginId, int startLevel);
+  void setStartLevel(int key, int startLevel);
 
   /**
    * Persist the last modification (state change) time
@@ -134,7 +164,7 @@ public:
    * @param pluginId The Plugin id
    * @param lastModified The modification time
    */
-  void setLastModified(long pluginId, const QDateTime& lastModified);
+  void setLastModified(int key, const QDateTime& lastModified);
 
   /**
    * Persist the auto start setting.
@@ -142,20 +172,46 @@ public:
    * @param pluginId The Plugin id
    * @param autostart The new auto start setting
    */
-  void setAutostartSetting(long pluginId, int autostart);
+  void setAutostartSetting(int key, int autostart);
+
+private:
+
+  enum TransactionType{Read, Write};
 
   /**
-   * Reads the persisted plugin data and returns a ctkPluginArchive object
-   * for each plugin which is not in state UNINSTALLED.
+   * Opens the plugin database. If the database does not
+   * yet exist, it is created using the path from getDatabasePath().
+   *
+   * @see setDatabasePath(const QString&)
+   * @see getDatabasePath()
+   * @see ctkPluginDatabaseException
    *
    * @throws ctkPluginDatabaseException
    */
-  QList<ctkPluginArchive*> getPluginArchives() const;
+  void open();
 
+  /**
+   * Checks if the database is open
+   */
+  bool isOpen() const;
 
-private:
+  /**
+   * Find posisition for BundleArchive with specified id
+   *
+   * @param id Bundle archive id to find.
+   * @return String to write
+   */
+  int find(long id) const;
 
-  enum TransactionType{Read, Write};
+  void initNextFreeIds();
+
+  /**
+   * Reads the persisted plugin data and creates a ctkPluginArchive object
+   * for each plugin which is not in state UNINSTALLED.
+   *
+   * @throws ctkPluginDatabaseException
+   */
+  void restorePluginArchives();
   
   /**
    * Get load hints from the framework for plugins.
@@ -174,7 +230,7 @@ private:
    * Remove all plugins which have been marked as uninstalled
    * (startLevel == -2).
    */
-  void removeUninstalledPlugins();
+  void cleanupDB();
 
   /**
    * Helper method that checks if all the expected tables exist in the database.
@@ -199,6 +255,12 @@ private:
    */
   void updateDB();
 
+  void insertArchive(ctkPluginArchiveSQL *pa);
+
+  void insertArchive(ctkPluginArchiveSQL *pa, QSqlQuery* query);
+
+  void removeArchiveFromDB(ctkPluginArchiveSQL *pa, QSqlQuery *query);
+
   /**
    * Helper function that executes the sql query specified in \a statement.
    * It is assumed that the \a statement uses positional placeholders and
@@ -248,8 +310,29 @@ private:
   QString m_connectionName;
   bool m_isDatabaseOpen;
   bool m_inTransaction;
-  ctkPluginStorage* m_PluginStorage;
+
+  QMutex m_archivesLock;
+
+  /**
+   * Plugin id sorted list of all active plugin archives.
+   */
+  QList<ctkPluginArchive*> m_archives;
+
+  /**
+   * Framework handle.
+   */
+  ctkPluginFrameworkContext* m_framework;
+
+  /**
+   * Keep track of the next free plug-in id
+   */
+  long m_nextFreeId;
+
+  /**
+   * Keep track of the next free generation for each plugin
+   */
+  QHash<int,int> /* <plugin id, generation> */ m_generations;
 };
 
 
-#endif // CTKPLUGINDATABASE_P_H
+#endif // ctkPluginStorageSQL_P_H

+ 11 - 77
Libs/PluginFramework/ctkPluginStorage_p.h

@@ -22,59 +22,23 @@
 #ifndef CTKPLUGINSTORAGE_P_H
 #define CTKPLUGINSTORAGE_P_H
 
-#include <QList>
+#include <QUrl>
 #include <QStringList>
 
-#include "ctkPluginDatabase_p.h"
-
-// Qt class forward declarations
-class QIODevice;
-
-
 // CTK class forward declarations
 class ctkPluginArchive;
-class ctkPluginFrameworkContext;
 
 /**
  * \ingroup PluginFramework
  *
- * Storage of all plugin meta-data and resources
+ * Interface for managing all plugin meta-data and resources
  */
 class ctkPluginStorage
 {
 
-private:
-
-  QMutex archivesLock;
-
-  /**
-   * Plugin id sorted list of all active plugin archives.
-   */
-  QList<ctkPluginArchive*> archives;
-
-  /**
-   * Framework handle.
-   */
-  ctkPluginFrameworkContext* framework;
-
-  /**
-   * SQLite db caching plug-in metadata and resources
-   */
-  ctkPluginDatabase pluginDatabase;
-
 public:
 
-  /**
-   * Create a container for all plugin data in this framework.
-   * Try to restore all saved plugin archive state.
-   *
-   */
-  ctkPluginStorage(ctkPluginFrameworkContext* framework);
-
-  /**
-   * Return the framework context.
-   */
-  ctkPluginFrameworkContext* getFrameworkContext() const;
+   virtual ~ctkPluginStorage() {}
 
   /**
    * Insert a plugin (shared library) into the persistent storage
@@ -83,8 +47,7 @@ public:
    * @param localPath Path to the plugin on the local file system
    * @return Plugin archive object.
    */
-  ctkPluginArchive* insertPlugin(const QUrl& location, const QString& localPath);
-
+  virtual ctkPluginArchive* insertPlugin(const QUrl& location, const QString& localPath) = 0;
 
   /**
    * Insert a new plugin (shared library) into the persistent
@@ -93,11 +56,11 @@ public:
    * <code>replacePluginArchive</code> is needed.
    *
    * @param old ctkPluginArchive to be replaced.
+   * @param updateLocation Location of the updated plugin.
    * @param localPath Path to a plugin on the local file system.
    * @return Plugin archive object.
    */
-  ctkPluginArchive* updatePluginArchive(ctkPluginArchive* old, const QString& localPath);
-
+  virtual ctkPluginArchive* updatePluginArchive(ctkPluginArchive* old, const QUrl& updateLocation, const QString& localPath) = 0;
 
   /**
    * Replace old plugin archive with a new updated plugin archive, that
@@ -106,28 +69,7 @@ public:
    * @param oldPA ctkPluginArchive to be replaced.
    * @param newPA new ctkPluginArchive.
    */
-  void replacePluginArchive(ctkPluginArchive* oldPA, ctkPluginArchive* newPA);
-
-  /**
-   * Persist the plugin start level.
-   *
-   * @param Plugin archive object
-   */
-  void setStartLevel(ctkPluginArchive* pa);
-
-  /**
-   * Persist the last modification (state change) time
-   *
-   * @param Plugin archive object
-   */
-  void setLastModified(ctkPluginArchive* pa);
-
-  /**
-   * Persist the auto start setting.
-   *
-   * @param Plugin archive object
-   */
-  void setAutostartSetting(ctkPluginArchive* pa);
+  virtual void replacePluginArchive(ctkPluginArchive* oldPA, ctkPluginArchive* newPA) = 0;
 
   /**
    * Remove plugin archive from archives list and persistent storage.
@@ -137,16 +79,14 @@ public:
    * @param pa Plugin archive to remove.
    * @return true if element was removed.
    */
-  bool removeArchive(ctkPluginArchive* pa);
-
+  virtual bool removeArchive(ctkPluginArchive* pa) = 0;
 
   /**
    * Get all plugin archive objects.
    *
    * @return QList of all PluginArchives.
    */
-  QList<ctkPluginArchive*> getAllPluginArchives() const;
-
+  virtual QList<ctkPluginArchive*> getAllPluginArchives() const = 0;
 
   /**
    * Get all plugins to start at next launch of framework.
@@ -154,18 +94,12 @@ public:
    *
    * @return A List with plugin locations.
    */
-  QList<QString> getStartOnLaunchPlugins();
-
-  QByteArray getPluginResource(long pluginId, const QString& res) const;
-
-  QStringList findResourcesPath(long pluginId, const QString& path) const;
+  virtual QList<QString> getStartOnLaunchPlugins() const = 0;
 
   /**
    * Close this plugin storage and all bundles in it.
    */
-  void close();
-
-  ~ctkPluginStorage();
+  virtual void close() = 0;
 
 };