changeset 1247:32fcc5dc7562

abstraction
author Sebastien Jodogne <s.jodogne@gmail.com>
date Mon, 08 Dec 2014 13:54:27 +0100
parents 54bf0f0245f4
children db753e57934f
files CMakeLists.txt OrthancServer/DatabaseWrapper.cpp OrthancServer/DatabaseWrapper.h OrthancServer/ExportedResource.cpp OrthancServer/ExportedResource.h OrthancServer/IDatabaseWrapper.h OrthancServer/OrthancRestApi/OrthancRestChanges.cpp OrthancServer/ServerContext.cpp OrthancServer/ServerContext.h OrthancServer/ServerIndex.cpp OrthancServer/ServerIndex.h OrthancServer/main.cpp UnitTestsSources/ServerIndexTests.cpp
diffstat 13 files changed, 553 insertions(+), 328 deletions(-) [+]
line wrap: on
line diff
--- a/CMakeLists.txt	Mon Dec 08 12:56:30 2014 +0100
+++ b/CMakeLists.txt	Mon Dec 08 13:54:27 2014 +0100
@@ -164,6 +164,7 @@
   OrthancServer/ServerToolbox.cpp
   OrthancServer/OrthancFindRequestHandler.cpp
   OrthancServer/OrthancMoveRequestHandler.cpp
+  OrthancServer/ExportedResource.cpp
 
   # From "lua-scripting" branch
   OrthancServer/DicomInstanceToStore.cpp
--- a/OrthancServer/DatabaseWrapper.cpp	Mon Dec 08 12:56:30 2014 +0100
+++ b/OrthancServer/DatabaseWrapper.cpp	Mon Dec 08 13:54:27 2014 +0100
@@ -39,6 +39,7 @@
 
 #include <glog/logging.h>
 #include <stdio.h>
+#include <boost/lexical_cast.hpp>
 
 namespace Orthanc
 {
@@ -359,10 +360,11 @@
     s.BindInt64(0, id);
     s.Run();
 
-    if (signalRemainingAncestor_->HasRemainingAncestor())
+    if (signalRemainingAncestor_->HasRemainingAncestor() &&
+        listener_ != NULL)
     {
-      listener_.SignalRemainingAncestor(signalRemainingAncestor_->GetRemainingAncestorType(),
-                                        signalRemainingAncestor_->GetRemainingAncestorId());
+      listener_->SignalRemainingAncestor(signalRemainingAncestor_->GetRemainingAncestorType(),
+                                         signalRemainingAncestor_->GetRemainingAncestorId());
     }
   }
 
@@ -610,7 +612,8 @@
       s.Run();
     }
 
-    listener_.SignalChange(change);
+    assert(listener_ != NULL);
+    listener_->SignalChange(change);
   }
 
 
@@ -785,16 +788,13 @@
   }
 
 
-  DatabaseWrapper::DatabaseWrapper(const std::string& path,
-                                   IServerIndexListener& listener) :
-    listener_(listener)
+  DatabaseWrapper::DatabaseWrapper(const std::string& path) : listener_(NULL)
   {
     db_.Open(path);
     Open();
   }
 
-  DatabaseWrapper::DatabaseWrapper(IServerIndexListener& listener) :
-    listener_(listener)
+  DatabaseWrapper::DatabaseWrapper() : listener_(NULL)
   {
     db_.OpenInMemory();
     Open();
@@ -870,8 +870,13 @@
 
     signalRemainingAncestor_ = new Internals::SignalRemainingAncestor;
     db_.Register(signalRemainingAncestor_);
-    db_.Register(new Internals::SignalFileDeleted(listener_));
-    db_.Register(new Internals::SignalResourceDeleted(listener_));
+  }
+
+  void DatabaseWrapper::SetListener(IServerIndexListener& listener)
+  {
+    listener_ = &listener;
+    db_.Register(new Internals::SignalFileDeleted(listener));
+    db_.Register(new Internals::SignalResourceDeleted(listener));
   }
 
   uint64_t DatabaseWrapper::GetResourceCount(ResourceType resourceType)
--- a/OrthancServer/DatabaseWrapper.h	Mon Dec 08 12:56:30 2014 +0100
+++ b/OrthancServer/DatabaseWrapper.h	Mon Dec 08 13:54:27 2014 +0100
@@ -32,16 +32,10 @@
 
 #pragma once
 
+#include "IDatabaseWrapper.h"
+
 #include "../Core/SQLite/Connection.h"
 #include "../Core/SQLite/Transaction.h"
-#include "../Core/DicomFormat/DicomInstanceHasher.h"
-#include "../Core/FileStorage/FileInfo.h"
-#include "IServerIndexListener.h"
-
-#include <list>
-#include <boost/date_time/posix_time/posix_time.hpp>
-
-#include "ExportedResource.h"
 
 namespace Orthanc
 {
@@ -55,10 +49,10 @@
    * translates low-level requests into SQL statements. Mutual
    * exclusion MUST be implemented at a higher level.
    **/
-  class DatabaseWrapper
+  class DatabaseWrapper : public IDatabaseWrapper
   {
   private:
-    IServerIndexListener& listener_;
+    IServerIndexListener* listener_;
     SQLite::Connection db_;
     Internals::SignalRemainingAncestor* signalRemainingAncestor_;
 
@@ -75,141 +69,137 @@
                                       unsigned int maxResults);
 
   public:
-    void SetGlobalProperty(GlobalProperty property,
-                           const std::string& value);
+    DatabaseWrapper(const std::string& path);
+
+    DatabaseWrapper();
+
+    virtual void SetListener(IServerIndexListener& listener);
 
-    bool LookupGlobalProperty(std::string& target,
-                              GlobalProperty property);
+    virtual void SetGlobalProperty(GlobalProperty property,
+                                   const std::string& value);
 
-    int64_t CreateResource(const std::string& publicId,
-                           ResourceType type);
+    virtual bool LookupGlobalProperty(std::string& target,
+                                      GlobalProperty property);
+
+    virtual int64_t CreateResource(const std::string& publicId,
+                                   ResourceType type);
 
-    bool LookupResource(const std::string& publicId,
-                        int64_t& id,
-                        ResourceType& type);
+    virtual bool LookupResource(const std::string& publicId,
+                                int64_t& id,
+                                ResourceType& type);
 
-    bool LookupParent(int64_t& parentId,
-                      int64_t resourceId);
+    virtual bool LookupParent(int64_t& parentId,
+                              int64_t resourceId);
 
-    std::string GetPublicId(int64_t resourceId);
+    virtual std::string GetPublicId(int64_t resourceId);
+
+    virtual ResourceType GetResourceType(int64_t resourceId);
 
-    ResourceType GetResourceType(int64_t resourceId);
+    virtual void AttachChild(int64_t parent,
+                             int64_t child);
 
-    void AttachChild(int64_t parent,
-                     int64_t child);
+    virtual void DeleteResource(int64_t id);
 
-    void DeleteResource(int64_t id);
+    virtual void SetMetadata(int64_t id,
+                             MetadataType type,
+                             const std::string& value);
+
+    virtual void DeleteMetadata(int64_t id,
+                                MetadataType type);
 
-    void SetMetadata(int64_t id,
-                     MetadataType type,
-                     const std::string& value);
+    virtual bool LookupMetadata(std::string& target,
+                                int64_t id,
+                                MetadataType type);
 
-    void DeleteMetadata(int64_t id,
-                        MetadataType type);
+    virtual void ListAvailableMetadata(std::list<MetadataType>& target,
+                                       int64_t id);
 
-    bool LookupMetadata(std::string& target,
-                        int64_t id,
-                        MetadataType type);
+    virtual void AddAttachment(int64_t id,
+                               const FileInfo& attachment);
 
-    void ListAvailableMetadata(std::list<MetadataType>& target,
-                               int64_t id);
+    virtual void DeleteAttachment(int64_t id,
+                                  FileContentType attachment);
+
+    virtual void ListAvailableAttachments(std::list<FileContentType>& result,
+                                          int64_t id);
 
-    void AddAttachment(int64_t id,
-                       const FileInfo& attachment);
+    virtual bool LookupAttachment(FileInfo& attachment,
+                                  int64_t id,
+                                  FileContentType contentType);
 
-    void DeleteAttachment(int64_t id,
-                          FileContentType attachment);
+    virtual void SetMainDicomTags(int64_t id,
+                                  const DicomMap& tags);
 
-    void ListAvailableAttachments(std::list<FileContentType>& result,
+    virtual void GetMainDicomTags(DicomMap& map,
                                   int64_t id);
 
-    bool LookupAttachment(FileInfo& attachment,
-                          int64_t id,
-                          FileContentType contentType);
+    virtual void GetChildrenPublicId(std::list<std::string>& result,
+                                     int64_t id);
 
-    void SetMainDicomTags(int64_t id,
-                          const DicomMap& tags);
+    virtual void GetChildrenInternalId(std::list<int64_t>& result,
+                                       int64_t id);
 
-    void GetMainDicomTags(DicomMap& map,
-                          int64_t id);
-
-    void GetChildrenPublicId(std::list<std::string>& result,
-                             int64_t id);
+    virtual void LogChange(int64_t internalId,
+                           const ServerIndexChange& change);
 
-    void GetChildrenInternalId(std::list<int64_t>& result,
-                               int64_t id);
-
-    void LogChange(int64_t internalId,
-                   const ServerIndexChange& change);
+    virtual void GetChanges(std::list<ServerIndexChange>& target /*out*/,
+                            bool& done /*out*/,
+                            int64_t since,
+                            unsigned int maxResults);
 
-    void GetChanges(std::list<ServerIndexChange>& target /*out*/,
-                    bool& done /*out*/,
-                    int64_t since,
-                    unsigned int maxResults);
+    virtual void GetLastChange(std::list<ServerIndexChange>& target /*out*/);
 
-    void GetLastChange(std::list<ServerIndexChange>& target /*out*/);
-
-    void LogExportedResource(const ExportedResource& resource);
+    virtual void LogExportedResource(const ExportedResource& resource);
     
-    void GetExportedResources(std::list<ExportedResource>& target /*out*/,
-                              bool& done /*out*/,
-                              int64_t since,
-                              unsigned int maxResults);
-
-    void GetLastExportedResource(std::list<ExportedResource>& target /*out*/);
+    virtual void GetExportedResources(std::list<ExportedResource>& target /*out*/,
+                                      bool& done /*out*/,
+                                      int64_t since,
+                                      unsigned int maxResults);
 
-    uint64_t GetTotalCompressedSize();
-    
-    uint64_t GetTotalUncompressedSize();
+    virtual void GetLastExportedResource(std::list<ExportedResource>& target /*out*/);
 
-    uint64_t GetResourceCount(ResourceType resourceType);
+    virtual uint64_t GetTotalCompressedSize();
+    
+    virtual uint64_t GetTotalUncompressedSize();
 
-    void GetAllPublicIds(std::list<std::string>& target,
-                         ResourceType resourceType);
+    virtual uint64_t GetResourceCount(ResourceType resourceType);
 
-    bool SelectPatientToRecycle(int64_t& internalId);
+    virtual void GetAllPublicIds(std::list<std::string>& target,
+                                 ResourceType resourceType);
 
-    bool SelectPatientToRecycle(int64_t& internalId,
-                                int64_t patientIdToAvoid);
-
-    bool IsProtectedPatient(int64_t internalId);
+    virtual bool SelectPatientToRecycle(int64_t& internalId);
 
-    void SetProtectedPatient(int64_t internalId, 
-                             bool isProtected);
+    virtual bool SelectPatientToRecycle(int64_t& internalId,
+                                        int64_t patientIdToAvoid);
+
+    virtual bool IsProtectedPatient(int64_t internalId);
 
-    DatabaseWrapper(const std::string& path,
-                    IServerIndexListener& listener);
+    virtual void SetProtectedPatient(int64_t internalId, 
+                                     bool isProtected);
 
-    DatabaseWrapper(IServerIndexListener& listener);
-
-    SQLite::ITransaction* StartTransaction()
+    virtual SQLite::ITransaction* StartTransaction()
     {
       return new SQLite::Transaction(db_);
     }
 
-    const char* GetErrorMessage() const
-    {
-      return db_.GetErrorMessage();
-    }
-
-    void FlushToDisk()
+    virtual void FlushToDisk()
     {
       db_.FlushToDisk();
     }
 
-    void ClearTable(const std::string& tableName);
+    virtual void ClearTable(const std::string& tableName);
 
-    bool IsExistingResource(int64_t internalId);
+    virtual bool IsExistingResource(int64_t internalId);
 
-    void LookupIdentifier(std::list<int64_t>& result,
-                          const DicomTag& tag,
-                          const std::string& value);
+    virtual void LookupIdentifier(std::list<int64_t>& result,
+                                  const DicomTag& tag,
+                                  const std::string& value);
 
-    void LookupIdentifier(std::list<int64_t>& result,
-                          const std::string& value);
+    virtual void LookupIdentifier(std::list<int64_t>& result,
+                                  const std::string& value);
 
-    void GetAllMetadata(std::map<MetadataType, std::string>& result,
-                        int64_t id);
+    virtual void GetAllMetadata(std::map<MetadataType, std::string>& result,
+                                int64_t id);
 
 
 
@@ -218,6 +208,11 @@
      * The methods declared below are for unit testing only!
      **/
 
+    const char* GetErrorMessage() const
+    {
+      return db_.GetErrorMessage();
+    }
+
     void GetChildren(std::list<std::string>& childrenPublicIds,
                      int64_t id);
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/OrthancServer/ExportedResource.cpp	Mon Dec 08 13:54:27 2014 +0100
@@ -0,0 +1,69 @@
+/**
+ * Orthanc - A Lightweight, RESTful DICOM Store
+ * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege,
+ * Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * In addition, as a special exception, the copyright holders of this
+ * program give permission to link the code of its release with the
+ * OpenSSL project's "OpenSSL" library (or with modified versions of it
+ * that use the same license as the "OpenSSL" library), and distribute
+ * the linked executables. You must obey the GNU General Public License
+ * in all respects for all of the code used other than "OpenSSL". If you
+ * modify file(s) with this exception, you may extend this exception to
+ * your version of the file(s), but you are not obligated to do so. If
+ * you do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source files
+ * in the program, then also delete it here.
+ * 
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#include "ExportedResource.h"
+
+#include "../Core/OrthancException.h"
+
+namespace Orthanc
+{
+  void ExportedResource::Format(Json::Value& item) const
+  {
+    item = Json::objectValue;
+    item["Seq"] = static_cast<int>(seq_);
+    item["ResourceType"] = EnumerationToString(resourceType_);
+    item["ID"] = publicId_;
+    item["Path"] = GetBasePath(resourceType_, publicId_);
+    item["RemoteModality"] = modality_;
+    item["Date"] = date_;
+
+    // WARNING: Do not add "break" below and do not reorder the case items!
+    switch (resourceType_)
+    {
+      case ResourceType_Instance:
+        item["SOPInstanceUID"] = sopInstanceUid_;
+
+      case ResourceType_Series:
+        item["SeriesInstanceUID"] = seriesInstanceUid_;
+
+      case ResourceType_Study:
+        item["StudyInstanceUID"] = studyInstanceUid_;
+
+      case ResourceType_Patient:
+        item["PatientID"] = patientId_;
+        break;
+
+      default:
+        throw OrthancException(ErrorCode_InternalError);
+    }
+  }
+}
--- a/OrthancServer/ExportedResource.h	Mon Dec 08 12:56:30 2014 +0100
+++ b/OrthancServer/ExportedResource.h	Mon Dec 08 13:54:27 2014 +0100
@@ -40,7 +40,7 @@
 
 namespace Orthanc
 {
-  struct ExportedResource
+  class ExportedResource
   {
   private:
     int64_t      seq_;
@@ -120,35 +120,6 @@
       return sopInstanceUid_;
     }
 
-    void Format(Json::Value& item) const
-    {
-      item = Json::objectValue;
-      item["Seq"] = static_cast<int>(seq_);
-      item["ResourceType"] = EnumerationToString(resourceType_);
-      item["ID"] = publicId_;
-      item["Path"] = GetBasePath(resourceType_, publicId_);
-      item["RemoteModality"] = modality_;
-      item["Date"] = date_;
-
-      // WARNING: Do not add "break" below and do not reorder the case items!
-      switch (resourceType_)
-      {
-      case ResourceType_Instance:
-        item["SOPInstanceUID"] = sopInstanceUid_;
-
-      case ResourceType_Series:
-        item["SeriesInstanceUID"] = seriesInstanceUid_;
-
-      case ResourceType_Study:
-        item["StudyInstanceUID"] = studyInstanceUid_;
-
-      case ResourceType_Patient:
-        item["PatientID"] = patientId_;
-        break;
-
-      default:
-        throw OrthancException(ErrorCode_InternalError);
-      }
-    }
+    void Format(Json::Value& item) const;
   };
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/OrthancServer/IDatabaseWrapper.h	Mon Dec 08 13:54:27 2014 +0100
@@ -0,0 +1,176 @@
+/**
+ * Orthanc - A Lightweight, RESTful DICOM Store
+ * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege,
+ * Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * In addition, as a special exception, the copyright holders of this
+ * program give permission to link the code of its release with the
+ * OpenSSL project's "OpenSSL" library (or with modified versions of it
+ * that use the same license as the "OpenSSL" library), and distribute
+ * the linked executables. You must obey the GNU General Public License
+ * in all respects for all of the code used other than "OpenSSL". If you
+ * modify file(s) with this exception, you may extend this exception to
+ * your version of the file(s), but you are not obligated to do so. If
+ * you do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source files
+ * in the program, then also delete it here.
+ * 
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#pragma once
+
+#include "../Core/DicomFormat/DicomMap.h"
+#include "../Core/SQLite/ITransaction.h"
+#include "../Core/FileStorage/FileInfo.h"
+#include "IServerIndexListener.h"
+#include "ExportedResource.h"
+
+#include <list>
+#include <boost/noncopyable.hpp>
+
+namespace Orthanc
+{
+  class IDatabaseWrapper : public boost::noncopyable
+  {
+  public:
+    virtual ~IDatabaseWrapper()
+    {
+    }
+
+    virtual void AddAttachment(int64_t id,
+                               const FileInfo& attachment) = 0;
+
+    virtual void AttachChild(int64_t parent,
+                             int64_t child) = 0;
+
+    virtual void ClearTable(const std::string& tableName) = 0;
+
+    virtual int64_t CreateResource(const std::string& publicId,
+                                   ResourceType type) = 0;
+
+    virtual void DeleteAttachment(int64_t id,
+                                  FileContentType attachment) = 0;
+
+    virtual void DeleteMetadata(int64_t id,
+                                MetadataType type) = 0;
+
+    virtual void DeleteResource(int64_t id) = 0;
+
+    virtual void FlushToDisk() = 0;
+
+    virtual void GetAllMetadata(std::map<MetadataType, std::string>& result,
+                                int64_t id) = 0;
+
+    virtual void GetAllPublicIds(std::list<std::string>& target,
+                                 ResourceType resourceType) = 0;
+
+
+    virtual void GetChanges(std::list<ServerIndexChange>& target /*out*/,
+                            bool& done /*out*/,
+                            int64_t since,
+                            unsigned int maxResults) = 0;
+
+    virtual void GetChildrenInternalId(std::list<int64_t>& result,
+                                       int64_t id) = 0;
+
+    virtual void GetChildrenPublicId(std::list<std::string>& result,
+                                     int64_t id) = 0;
+
+    virtual void GetExportedResources(std::list<ExportedResource>& target /*out*/,
+                                      bool& done /*out*/,
+                                      int64_t since,
+                                      unsigned int maxResults) = 0;
+
+    virtual void GetLastChange(std::list<ServerIndexChange>& target /*out*/) = 0;
+
+    virtual void GetLastExportedResource(std::list<ExportedResource>& target /*out*/) = 0;
+
+    virtual void GetMainDicomTags(DicomMap& map,
+                                  int64_t id) = 0;
+
+    virtual std::string GetPublicId(int64_t resourceId) = 0;
+
+    virtual uint64_t GetResourceCount(ResourceType resourceType) = 0;
+
+    virtual ResourceType GetResourceType(int64_t resourceId) = 0;
+
+    virtual uint64_t GetTotalCompressedSize() = 0;
+    
+    virtual uint64_t GetTotalUncompressedSize() = 0;
+
+    virtual bool IsExistingResource(int64_t internalId) = 0;
+
+    virtual bool IsProtectedPatient(int64_t internalId) = 0;
+
+    virtual void ListAvailableMetadata(std::list<MetadataType>& target,
+                                       int64_t id) = 0;
+
+    virtual void ListAvailableAttachments(std::list<FileContentType>& result,
+                                          int64_t id) = 0;
+
+    virtual void LogChange(int64_t internalId,
+                           const ServerIndexChange& change) = 0;
+
+    virtual void LogExportedResource(const ExportedResource& resource) = 0;
+    
+    virtual bool LookupAttachment(FileInfo& attachment,
+                                  int64_t id,
+                                  FileContentType contentType) = 0;
+
+    virtual bool LookupGlobalProperty(std::string& target,
+                                      GlobalProperty property) = 0;
+
+    virtual void LookupIdentifier(std::list<int64_t>& result,
+                                  const DicomTag& tag,
+                                  const std::string& value) = 0;
+
+    virtual void LookupIdentifier(std::list<int64_t>& result,
+                                  const std::string& value) = 0;
+
+    virtual bool LookupMetadata(std::string& target,
+                                int64_t id,
+                                MetadataType type) = 0;
+
+    virtual bool LookupParent(int64_t& parentId,
+                              int64_t resourceId) = 0;
+
+    virtual bool LookupResource(const std::string& publicId,
+                                int64_t& id,
+                                ResourceType& type) = 0;
+
+    virtual bool SelectPatientToRecycle(int64_t& internalId) = 0;
+
+    virtual bool SelectPatientToRecycle(int64_t& internalId,
+                                        int64_t patientIdToAvoid) = 0;
+
+    virtual void SetGlobalProperty(GlobalProperty property,
+                                   const std::string& value) = 0;
+
+    virtual void SetMainDicomTags(int64_t id,
+                                  const DicomMap& tags) = 0;
+
+    virtual void SetMetadata(int64_t id,
+                             MetadataType type,
+                             const std::string& value) = 0;
+
+    virtual void SetProtectedPatient(int64_t internalId, 
+                                     bool isProtected) = 0;
+
+    virtual SQLite::ITransaction* StartTransaction() = 0;
+
+    virtual void SetListener(IServerIndexListener& listener) = 0;
+  };
+}
--- a/OrthancServer/OrthancRestApi/OrthancRestChanges.cpp	Mon Dec 08 12:56:30 2014 +0100
+++ b/OrthancServer/OrthancRestApi/OrthancRestChanges.cpp	Mon Dec 08 13:54:27 2014 +0100
@@ -81,11 +81,16 @@
     GetSinceAndLimit(since, limit, last, call);
 
     Json::Value result;
-    if ((!last && context.GetIndex().GetChanges(result, since, limit)) ||
-        ( last && context.GetIndex().GetLastChange(result)))
+    if (last)
     {
-      call.GetOutput().AnswerJson(result);
+      context.GetIndex().GetLastChange(result);
     }
+    else
+    {
+      context.GetIndex().GetChanges(result, since, limit);
+    }
+
+    call.GetOutput().AnswerJson(result);
   }
 
 
@@ -108,11 +113,16 @@
     GetSinceAndLimit(since, limit, last, call);
 
     Json::Value result;
-    if ((!last && context.GetIndex().GetExportedResources(result, since, limit)) ||
-        ( last && context.GetIndex().GetLastExportedResource(result)))
+    if (last)
     {
-      call.GetOutput().AnswerJson(result);
+      context.GetIndex().GetLastExportedResource(result);
     }
+    else
+    {
+      context.GetIndex().GetExportedResources(result, since, limit);
+    }
+
+    call.GetOutput().AnswerJson(result);
   }
 
 
--- a/OrthancServer/ServerContext.cpp	Mon Dec 08 12:56:30 2014 +0100
+++ b/OrthancServer/ServerContext.cpp	Mon Dec 08 13:54:27 2014 +0100
@@ -71,8 +71,8 @@
 
 namespace Orthanc
 {
-  ServerContext::ServerContext(const boost::filesystem::path& indexPath) :
-    index_(*this, indexPath.string()),
+  ServerContext::ServerContext(IDatabaseWrapper& database) :
+    index_(*this, database),
     compressionEnabled_(false),
     provider_(*this),
     dicomCache_(provider_, DICOM_CACHE_SIZE),
--- a/OrthancServer/ServerContext.h	Mon Dec 08 12:56:30 2014 +0100
+++ b/OrthancServer/ServerContext.h	Mon Dec 08 13:54:27 2014 +0100
@@ -137,7 +137,7 @@
     };
 
 
-    ServerContext(const boost::filesystem::path& indexPath);
+    ServerContext(IDatabaseWrapper& database);
 
     void SetStorageArea(IStorageArea& storage)
     {
--- a/OrthancServer/ServerIndex.cpp	Mon Dec 08 12:56:30 2014 +0100
+++ b/OrthancServer/ServerIndex.cpp	Mon Dec 08 13:54:27 2014 +0100
@@ -221,9 +221,9 @@
       index_(index),
       isCommitted_(false)
     {
-      assert(index_.currentStorageSize_ == index_.db_->GetTotalCompressedSize());
+      assert(index_.currentStorageSize_ == index_.db_.GetTotalCompressedSize());
 
-      transaction_.reset(index_.db_->StartTransaction());
+      transaction_.reset(index_.db_.StartTransaction());
       transaction_->Begin();
 
       index_.listener_->StartTransaction();
@@ -250,7 +250,7 @@
         assert(index_.currentStorageSize_ >= index_.listener_->GetSizeOfFilesToRemove());
         index_.currentStorageSize_ -= index_.listener_->GetSizeOfFilesToRemove();
 
-        assert(index_.currentStorageSize_ == index_.db_->GetTotalCompressedSize());
+        assert(index_.currentStorageSize_ == index_.db_.GetTotalCompressedSize());
 
         // Send all the pending changes to the Orthanc plugins
         index_.listener_->CommitChanges();
@@ -308,13 +308,13 @@
 
     int64_t id;
     ResourceType type;
-    if (!db_->LookupResource(uuid, id, type) ||
+    if (!db_.LookupResource(uuid, id, type) ||
         expectedType != type)
     {
       return false;
     }
       
-    db_->DeleteResource(id);
+    db_.DeleteResource(id);
 
     if (listener_->HasRemainingLevel())
     {
@@ -347,7 +347,7 @@
       boost::mutex::scoped_lock lock(that->mutex_);
       std::string sleepString;
 
-      if (that->db_->LookupGlobalProperty(sleepString, GlobalProperty_FlushSleep) &&
+      if (that->db_.LookupGlobalProperty(sleepString, GlobalProperty_FlushSleep) &&
           Toolbox::IsInteger(sleepString))
       {
         sleep = boost::lexical_cast<unsigned int>(sleepString);
@@ -371,7 +371,7 @@
       }
 
       boost::mutex::scoped_lock lock(that->mutex_);
-      that->db_->FlushToDisk();
+      that->db_.FlushToDisk();
       count = 0;
     }
 
@@ -379,7 +379,7 @@
   }
 
 
-  static void ComputeExpectedNumberOfInstances(DatabaseWrapper& db,
+  static void ComputeExpectedNumberOfInstances(IDatabaseWrapper& db,
                                                int64_t series,
                                                const DicomMap& dicomSummary)
   {
@@ -426,7 +426,7 @@
                                          MetadataType type)
   {
     std::string s;
-    if (!db_->LookupMetadata(s, id, type))
+    if (!db_.LookupMetadata(s, id, type))
     {
       return false;
     }
@@ -448,14 +448,14 @@
   {
     std::string oldValue;
 
-    if (db_->LookupGlobalProperty(oldValue, property))
+    if (db_.LookupGlobalProperty(oldValue, property))
     {
       uint64_t oldNumber;
 
       try
       {
         oldNumber = boost::lexical_cast<uint64_t>(oldValue);
-        db_->SetGlobalProperty(property, boost::lexical_cast<std::string>(oldNumber + 1));
+        db_.SetGlobalProperty(property, boost::lexical_cast<std::string>(oldNumber + 1));
         return oldNumber + 1;
       }
       catch (boost::bad_lexical_cast&)
@@ -466,7 +466,7 @@
     else
     {
       // Initialize the sequence at "1"
-      db_->SetGlobalProperty(property, "1");
+      db_.SetGlobalProperty(property, "1");
       return 1;
     }
   }
@@ -474,33 +474,16 @@
 
 
   ServerIndex::ServerIndex(ServerContext& context,
-                           const std::string& dbPath) : 
+                           IDatabaseWrapper& db) : 
     done_(false),
+    db_(db),
     maximumStorageSize_(0),
     maximumPatients_(0)
   {
     listener_.reset(new Internals::ServerIndexListener(context));
-
-    if (dbPath == ":memory:")
-    {
-      db_.reset(new DatabaseWrapper(*listener_));
-    }
-    else
-    {
-      boost::filesystem::path p = dbPath;
+    db_.SetListener(*listener_);
 
-      try
-      {
-        boost::filesystem::create_directories(p);
-      }
-      catch (boost::filesystem::filesystem_error)
-      {
-      }
-
-      db_.reset(new DatabaseWrapper(p.string() + "/index", *listener_));
-    }
-
-    currentStorageSize_ = db_->GetTotalCompressedSize();
+    currentStorageSize_ = db_.GetTotalCompressedSize();
 
     // Initial recycling if the parameters have changed since the last
     // execution of Orthanc
@@ -547,10 +530,10 @@
       {
         ResourceType type;
         int64_t tmp;
-        if (db_->LookupResource(hasher.HashInstance(), tmp, type))
+        if (db_.LookupResource(hasher.HashInstance(), tmp, type))
         {
           assert(type == ResourceType_Instance);
-          db_->GetAllMetadata(instanceMetadata, tmp);
+          db_.GetAllMetadata(instanceMetadata, tmp);
           return StoreStatus_AlreadyStored;
         }
       }
@@ -566,11 +549,11 @@
       Recycle(instanceSize, hasher.HashPatient());
 
       // Create the instance
-      int64_t instance = db_->CreateResource(hasher.HashInstance(), ResourceType_Instance);
+      int64_t instance = db_.CreateResource(hasher.HashInstance(), ResourceType_Instance);
 
       DicomMap dicom;
       dicomSummary.ExtractInstanceInformation(dicom);
-      db_->SetMainDicomTags(instance, dicom);
+      db_.SetMainDicomTags(instance, dicom);
 
       // Detect up to which level the patient/study/series/instance
       // hierarchy must be created
@@ -582,26 +565,26 @@
       {
         ResourceType dummy;
 
-        if (db_->LookupResource(hasher.HashSeries(), series, dummy))
+        if (db_.LookupResource(hasher.HashSeries(), series, dummy))
         {
           assert(dummy == ResourceType_Series);
           // The patient, the study and the series already exist
 
-          bool ok = (db_->LookupResource(hasher.HashPatient(), patient, dummy) &&
-                     db_->LookupResource(hasher.HashStudy(), study, dummy));
+          bool ok = (db_.LookupResource(hasher.HashPatient(), patient, dummy) &&
+                     db_.LookupResource(hasher.HashStudy(), study, dummy));
           assert(ok);
         }
-        else if (db_->LookupResource(hasher.HashStudy(), study, dummy))
+        else if (db_.LookupResource(hasher.HashStudy(), study, dummy))
         {
           assert(dummy == ResourceType_Study);
 
           // New series: The patient and the study already exist
           isNewSeries = true;
 
-          bool ok = db_->LookupResource(hasher.HashPatient(), patient, dummy);
+          bool ok = db_.LookupResource(hasher.HashPatient(), patient, dummy);
           assert(ok);
         }
-        else if (db_->LookupResource(hasher.HashPatient(), patient, dummy))
+        else if (db_.LookupResource(hasher.HashPatient(), patient, dummy))
         {
           assert(dummy == ResourceType_Patient);
 
@@ -621,38 +604,38 @@
       // Create the series if needed
       if (isNewSeries)
       {
-        series = db_->CreateResource(hasher.HashSeries(), ResourceType_Series);
+        series = db_.CreateResource(hasher.HashSeries(), ResourceType_Series);
         dicomSummary.ExtractSeriesInformation(dicom);
-        db_->SetMainDicomTags(series, dicom);
+        db_.SetMainDicomTags(series, dicom);
       }
 
       // Create the study if needed
       if (isNewStudy)
       {
-        study = db_->CreateResource(hasher.HashStudy(), ResourceType_Study);
+        study = db_.CreateResource(hasher.HashStudy(), ResourceType_Study);
         dicomSummary.ExtractStudyInformation(dicom);
-        db_->SetMainDicomTags(study, dicom);
+        db_.SetMainDicomTags(study, dicom);
       }
 
       // Create the patient if needed
       if (isNewPatient)
       {
-        patient = db_->CreateResource(hasher.HashPatient(), ResourceType_Patient);
+        patient = db_.CreateResource(hasher.HashPatient(), ResourceType_Patient);
         dicomSummary.ExtractPatientInformation(dicom);
-        db_->SetMainDicomTags(patient, dicom);
+        db_.SetMainDicomTags(patient, dicom);
       }
 
       // Create the parent-to-child links
-      db_->AttachChild(series, instance);
+      db_.AttachChild(series, instance);
 
       if (isNewSeries)
       {
-        db_->AttachChild(study, series);
+        db_.AttachChild(study, series);
       }
 
       if (isNewStudy)
       {
-        db_->AttachChild(patient, study);
+        db_.AttachChild(patient, study);
       }
 
       // Sanity checks
@@ -665,7 +648,7 @@
       for (Attachments::const_iterator it = attachments.begin();
            it != attachments.end(); ++it)
       {
-        db_->AddAttachment(instance, *it);
+        db_.AddAttachment(instance, *it);
       }
 
       // Attach the user-specified metadata
@@ -675,19 +658,19 @@
         switch (it->first.first)
         {
           case ResourceType_Patient:
-            db_->SetMetadata(patient, it->first.second, it->second);
+            db_.SetMetadata(patient, it->first.second, it->second);
             break;
 
           case ResourceType_Study:
-            db_->SetMetadata(study, it->first.second, it->second);
+            db_.SetMetadata(study, it->first.second, it->second);
             break;
 
           case ResourceType_Series:
-            db_->SetMetadata(series, it->first.second, it->second);
+            db_.SetMetadata(series, it->first.second, it->second);
             break;
 
           case ResourceType_Instance:
-            db_->SetMetadata(instance, it->first.second, it->second);
+            db_.SetMetadata(instance, it->first.second, it->second);
             instanceMetadata[it->first.second] = it->second;
             break;
 
@@ -698,30 +681,30 @@
 
       // Attach the auto-computed metadata for the patient/study/series levels
       std::string now = Toolbox::GetNowIsoString();
-      db_->SetMetadata(series, MetadataType_LastUpdate, now);
-      db_->SetMetadata(study, MetadataType_LastUpdate, now);
-      db_->SetMetadata(patient, MetadataType_LastUpdate, now);
+      db_.SetMetadata(series, MetadataType_LastUpdate, now);
+      db_.SetMetadata(study, MetadataType_LastUpdate, now);
+      db_.SetMetadata(patient, MetadataType_LastUpdate, now);
 
       // Attach the auto-computed metadata for the instance level,
       // reflecting these additions into the input metadata map
-      db_->SetMetadata(instance, MetadataType_Instance_ReceptionDate, now);
+      db_.SetMetadata(instance, MetadataType_Instance_ReceptionDate, now);
       instanceMetadata[MetadataType_Instance_ReceptionDate] = now;
 
-      db_->SetMetadata(instance, MetadataType_Instance_RemoteAet, remoteAet);
+      db_.SetMetadata(instance, MetadataType_Instance_RemoteAet, remoteAet);
       instanceMetadata[MetadataType_Instance_RemoteAet] = remoteAet;
 
       const DicomValue* value;
       if ((value = dicomSummary.TestAndGetValue(DICOM_TAG_INSTANCE_NUMBER)) != NULL ||
           (value = dicomSummary.TestAndGetValue(DICOM_TAG_IMAGE_INDEX)) != NULL)
       {
-        db_->SetMetadata(instance, MetadataType_Instance_IndexInSeries, value->AsString());
+        db_.SetMetadata(instance, MetadataType_Instance_IndexInSeries, value->AsString());
         instanceMetadata[MetadataType_Instance_IndexInSeries] = value->AsString();
       }
 
       // Check whether the series of this new instance is now completed
       if (isNewSeries)
       {
-        ComputeExpectedNumberOfInstances(*db_, series, dicomSummary);
+        ComputeExpectedNumberOfInstances(db_, series, dicomSummary);
       }
 
       SeriesStatus seriesStatus = GetSeriesStatus(series);
@@ -741,8 +724,7 @@
     }
     catch (OrthancException& e)
     {
-      LOG(ERROR) << "EXCEPTION [" << e.What() << "]" 
-                 << " (SQLite status: " << db_->GetErrorMessage() << ")";
+      LOG(ERROR) << "EXCEPTION [" << e.What() << "]";
     }
 
     return StoreStatus_Failure;
@@ -755,17 +737,17 @@
     target = Json::objectValue;
 
     uint64_t cs = currentStorageSize_;
-    assert(cs == db_->GetTotalCompressedSize());
-    uint64_t us = db_->GetTotalUncompressedSize();
+    assert(cs == db_.GetTotalCompressedSize());
+    uint64_t us = db_.GetTotalUncompressedSize();
     target["TotalDiskSize"] = boost::lexical_cast<std::string>(cs);
     target["TotalUncompressedSize"] = boost::lexical_cast<std::string>(us);
     target["TotalDiskSizeMB"] = boost::lexical_cast<unsigned int>(cs / MEGA_BYTES);
     target["TotalUncompressedSizeMB"] = boost::lexical_cast<unsigned int>(us / MEGA_BYTES);
 
-    target["CountPatients"] = static_cast<unsigned int>(db_->GetResourceCount(ResourceType_Patient));
-    target["CountStudies"] = static_cast<unsigned int>(db_->GetResourceCount(ResourceType_Study));
-    target["CountSeries"] = static_cast<unsigned int>(db_->GetResourceCount(ResourceType_Series));
-    target["CountInstances"] = static_cast<unsigned int>(db_->GetResourceCount(ResourceType_Instance));
+    target["CountPatients"] = static_cast<unsigned int>(db_.GetResourceCount(ResourceType_Patient));
+    target["CountStudies"] = static_cast<unsigned int>(db_.GetResourceCount(ResourceType_Study));
+    target["CountSeries"] = static_cast<unsigned int>(db_.GetResourceCount(ResourceType_Series));
+    target["CountInstances"] = static_cast<unsigned int>(db_.GetResourceCount(ResourceType_Instance));
   }          
 
 
@@ -781,7 +763,7 @@
 
     // Loop over the instances of this series
     std::list<int64_t> children;
-    db_->GetChildrenInternalId(children, id);
+    db_.GetChildrenInternalId(children, id);
 
     std::set<int64_t> instances;
     for (std::list<int64_t>::const_iterator 
@@ -825,7 +807,7 @@
                                         int64_t resourceId)
   {
     DicomMap tags;
-    db_->GetMainDicomTags(tags, resourceId);
+    db_.GetMainDicomTags(tags, resourceId);
     target["MainDicomTags"] = Json::objectValue;
     FromDcmtkBridge::ToJson(target["MainDicomTags"], tags);
   }
@@ -841,7 +823,7 @@
     // Lookup for the requested resource
     int64_t id;
     ResourceType type;
-    if (!db_->LookupResource(publicId, id, type) ||
+    if (!db_.LookupResource(publicId, id, type) ||
         type != expectedType)
     {
       return false;
@@ -851,12 +833,12 @@
     if (type != ResourceType_Patient)
     {
       int64_t parentId;
-      if (!db_->LookupParent(parentId, id))
+      if (!db_.LookupParent(parentId, id))
       {
         throw OrthancException(ErrorCode_InternalError);
       }
 
-      std::string parent = db_->GetPublicId(parentId);
+      std::string parent = db_.GetPublicId(parentId);
 
       switch (type)
       {
@@ -879,7 +861,7 @@
 
     // List the children resources
     std::list<std::string> children;
-    db_->GetChildrenPublicId(children, id);
+    db_.GetChildrenPublicId(children, id);
 
     if (type != ResourceType_Instance)
     {
@@ -940,7 +922,7 @@
         result["Type"] = "Instance";
 
         FileInfo attachment;
-        if (!db_->LookupAttachment(attachment, id, FileContentType_Dicom))
+        if (!db_.LookupAttachment(attachment, id, FileContentType_Dicom))
         {
           throw OrthancException(ErrorCode_InternalError);
         }
@@ -967,12 +949,12 @@
 
     std::string tmp;
 
-    if (db_->LookupMetadata(tmp, id, MetadataType_AnonymizedFrom))
+    if (db_.LookupMetadata(tmp, id, MetadataType_AnonymizedFrom))
     {
       result["AnonymizedFrom"] = tmp;
     }
 
-    if (db_->LookupMetadata(tmp, id, MetadataType_ModifiedFrom))
+    if (db_.LookupMetadata(tmp, id, MetadataType_ModifiedFrom))
     {
       result["ModifiedFrom"] = tmp;
     }
@@ -983,7 +965,7 @@
     {
       result["IsStable"] = !unstableResources_.Contains(id);
 
-      if (db_->LookupMetadata(tmp, id, MetadataType_LastUpdate))
+      if (db_.LookupMetadata(tmp, id, MetadataType_LastUpdate))
       {
         result["LastUpdate"] = tmp;
       }
@@ -1001,12 +983,12 @@
 
     int64_t id;
     ResourceType type;
-    if (!db_->LookupResource(instanceUuid, id, type))
+    if (!db_.LookupResource(instanceUuid, id, type))
     {
       throw OrthancException(ErrorCode_UnknownResource);
     }
 
-    if (db_->LookupAttachment(attachment, id, contentType))
+    if (db_.LookupAttachment(attachment, id, contentType))
     {
       assert(attachment.GetContentType() == contentType);
       return true;
@@ -1026,7 +1008,7 @@
 
     {
       boost::mutex::scoped_lock lock(mutex_);
-      db_->GetAllPublicIds(lst, resourceType);
+      db_.GetAllPublicIds(lst, resourceType);
     }
 
     target = Json::arrayValue;
@@ -1063,7 +1045,7 @@
   }
 
 
-  bool ServerIndex::GetChanges(Json::Value& target,
+  void ServerIndex::GetChanges(Json::Value& target,
                                int64_t since,                               
                                unsigned int maxResults)
   {
@@ -1072,25 +1054,23 @@
 
     {
       boost::mutex::scoped_lock lock(mutex_);
-      db_->GetChanges(changes, done, since, maxResults);
+      db_.GetChanges(changes, done, since, maxResults);
     }
 
     FormatLog(target, changes, "Changes", done, since);
-    return true;
   }
 
 
-  bool ServerIndex::GetLastChange(Json::Value& target)
+  void ServerIndex::GetLastChange(Json::Value& target)
   {
     std::list<ServerIndexChange> changes;
 
     {
       boost::mutex::scoped_lock lock(mutex_);
-      db_->GetLastChange(changes);
+      db_.GetLastChange(changes);
     }
 
     FormatLog(target, changes, "Changes", true, 0);
-    return true;
   }
 
 
@@ -1101,7 +1081,7 @@
 
     int64_t id;
     ResourceType type;
-    if (!db_->LookupResource(publicId, id, type))
+    if (!db_.LookupResource(publicId, id, type))
     {
       throw OrthancException(ErrorCode_InternalError);
     }
@@ -1119,7 +1099,7 @@
     while (!done)
     {
       DicomMap map;
-      db_->GetMainDicomTags(map, currentId);
+      db_.GetMainDicomTags(map, currentId);
 
       switch (currentType)
       {
@@ -1151,7 +1131,7 @@
       // the current resource
       if (!done)
       {
-        bool ok = db_->LookupParent(currentId, currentId);
+        bool ok = db_.LookupParent(currentId, currentId);
         assert(ok);
       }
     }
@@ -1166,11 +1146,11 @@
                               studyInstanceUid,
                               seriesInstanceUid,
                               sopInstanceUid);
-    db_->LogExportedResource(resource);
+    db_.LogExportedResource(resource);
   }
 
 
-  bool ServerIndex::GetExportedResources(Json::Value& target,
+  void ServerIndex::GetExportedResources(Json::Value& target,
                                          int64_t since,
                                          unsigned int maxResults)
   {
@@ -1179,25 +1159,23 @@
 
     {
       boost::mutex::scoped_lock lock(mutex_);
-      db_->GetExportedResources(exported, done, since, maxResults);
+      db_.GetExportedResources(exported, done, since, maxResults);
     }
 
     FormatLog(target, exported, "Exports", done, since);
-    return true;
   }
 
 
-  bool ServerIndex::GetLastExportedResource(Json::Value& target)
+  void ServerIndex::GetLastExportedResource(Json::Value& target)
   {
     std::list<ExportedResource> exported;
 
     {
       boost::mutex::scoped_lock lock(mutex_);
-      db_->GetLastExportedResource(exported);
+      db_.GetLastExportedResource(exported);
     }
 
     FormatLog(target, exported, "Exports", true, 0);
-    return true;
   }
 
 
@@ -1206,7 +1184,7 @@
     if (maximumStorageSize_ != 0)
     {
       uint64_t currentSize = currentStorageSize_ - listener_->GetSizeOfFilesToRemove();
-      assert(db_->GetTotalCompressedSize() == currentSize);
+      assert(db_.GetTotalCompressedSize() == currentSize);
 
       if (currentSize + instanceSize > maximumStorageSize_)
       {
@@ -1216,7 +1194,7 @@
 
     if (maximumPatients_ != 0)
     {
-      uint64_t patientCount = db_->GetResourceCount(ResourceType_Patient);
+      uint64_t patientCount = db_.GetResourceCount(ResourceType_Patient);
       if (patientCount > maximumPatients_)
       {
         return true;
@@ -1239,7 +1217,7 @@
     // already stored
     int64_t patientToAvoid;
     ResourceType type;
-    bool hasPatientToAvoid = db_->LookupResource(newPatientId, patientToAvoid, type);
+    bool hasPatientToAvoid = db_.LookupResource(newPatientId, patientToAvoid, type);
 
     if (hasPatientToAvoid && type != ResourceType_Patient)
     {
@@ -1254,8 +1232,8 @@
       // If other instances of this patient are already in the store,
       // we must avoid to recycle them
       bool ok = hasPatientToAvoid ?
-        db_->SelectPatientToRecycle(patientToRecycle, patientToAvoid) :
-        db_->SelectPatientToRecycle(patientToRecycle);
+        db_.SelectPatientToRecycle(patientToRecycle, patientToAvoid) :
+        db_.SelectPatientToRecycle(patientToRecycle);
         
       if (!ok)
       {
@@ -1263,7 +1241,7 @@
       }
       
       LOG(INFO) << "Recycling one patient";
-      db_->DeleteResource(patientToRecycle);
+      db_.DeleteResource(patientToRecycle);
 
       if (!IsRecyclingNeeded(instanceSize))
       {
@@ -1323,13 +1301,13 @@
     // Lookup for the requested resource
     int64_t id;
     ResourceType type;
-    if (!db_->LookupResource(publicId, id, type) ||
+    if (!db_.LookupResource(publicId, id, type) ||
         type != ResourceType_Patient)
     {
       throw OrthancException(ErrorCode_ParameterOutOfRange);
     }
 
-    return db_->IsProtectedPatient(id);
+    return db_.IsProtectedPatient(id);
   }
      
 
@@ -1341,14 +1319,14 @@
     // Lookup for the requested resource
     int64_t id;
     ResourceType type;
-    if (!db_->LookupResource(publicId, id, type) ||
+    if (!db_.LookupResource(publicId, id, type) ||
         type != ResourceType_Patient)
     {
       throw OrthancException(ErrorCode_ParameterOutOfRange);
     }
 
     // No need for a SQLite::ITransaction here, as we only make 1 write to the DB
-    db_->SetProtectedPatient(id, isProtected);
+    db_.SetProtectedPatient(id, isProtected);
 
     if (isProtected)
       LOG(INFO) << "Patient " << publicId << " has been protected";
@@ -1366,7 +1344,7 @@
 
     ResourceType type;
     int64_t resource;
-    if (!db_->LookupResource(publicId, resource, type))
+    if (!db_.LookupResource(publicId, resource, type))
     {
       throw OrthancException(ErrorCode_UnknownResource);
     }
@@ -1378,12 +1356,12 @@
     }
 
     std::list<int64_t> tmp;
-    db_->GetChildrenInternalId(tmp, resource);
+    db_.GetChildrenInternalId(tmp, resource);
 
     for (std::list<int64_t>::const_iterator 
            it = tmp.begin(); it != tmp.end(); ++it)
     {
-      result.push_back(db_->GetPublicId(*it));
+      result.push_back(db_.GetPublicId(*it));
     }
   }
 
@@ -1397,7 +1375,7 @@
 
     ResourceType type;
     int64_t top;
-    if (!db_->LookupResource(publicId, top, type))
+    if (!db_.LookupResource(publicId, top, type))
     {
       throw OrthancException(ErrorCode_UnknownResource);
     }
@@ -1420,14 +1398,14 @@
       int64_t resource = toExplore.top();
       toExplore.pop();
 
-      if (db_->GetResourceType(resource) == ResourceType_Instance)
+      if (db_.GetResourceType(resource) == ResourceType_Instance)
       {
-        result.push_back(db_->GetPublicId(resource));
+        result.push_back(db_.GetPublicId(resource));
       }
       else
       {
         // Tag all the children of this resource as to be explored
-        db_->GetChildrenInternalId(tmp, resource);
+        db_.GetChildrenInternalId(tmp, resource);
         for (std::list<int64_t>::const_iterator 
                it = tmp.begin(); it != tmp.end(); ++it)
         {
@@ -1446,12 +1424,12 @@
 
     ResourceType rtype;
     int64_t id;
-    if (!db_->LookupResource(publicId, id, rtype))
+    if (!db_.LookupResource(publicId, id, rtype))
     {
       throw OrthancException(ErrorCode_UnknownResource);
     }
 
-    db_->SetMetadata(id, type, value);
+    db_.SetMetadata(id, type, value);
   }
 
 
@@ -1462,12 +1440,12 @@
 
     ResourceType rtype;
     int64_t id;
-    if (!db_->LookupResource(publicId, id, rtype))
+    if (!db_.LookupResource(publicId, id, rtype))
     {
       throw OrthancException(ErrorCode_UnknownResource);
     }
 
-    db_->DeleteMetadata(id, type);
+    db_.DeleteMetadata(id, type);
   }
 
 
@@ -1479,12 +1457,12 @@
 
     ResourceType rtype;
     int64_t id;
-    if (!db_->LookupResource(publicId, id, rtype))
+    if (!db_.LookupResource(publicId, id, rtype))
     {
       throw OrthancException(ErrorCode_UnknownResource);
     }
 
-    return db_->LookupMetadata(target, id, type);
+    return db_.LookupMetadata(target, id, type);
   }
 
 
@@ -1495,12 +1473,12 @@
 
     ResourceType rtype;
     int64_t id;
-    if (!db_->LookupResource(publicId, id, rtype))
+    if (!db_.LookupResource(publicId, id, rtype))
     {
       throw OrthancException(ErrorCode_UnknownResource);
     }
 
-    db_->ListAvailableMetadata(target, id);
+    db_.ListAvailableMetadata(target, id);
   }
 
 
@@ -1512,13 +1490,13 @@
 
     ResourceType type;
     int64_t id;
-    if (!db_->LookupResource(publicId, id, type) ||
+    if (!db_.LookupResource(publicId, id, type) ||
         expectedType != type)
     {
       throw OrthancException(ErrorCode_UnknownResource);
     }
 
-    db_->ListAvailableAttachments(target, id);
+    db_.ListAvailableAttachments(target, id);
   }
 
 
@@ -1529,15 +1507,15 @@
 
     ResourceType type;
     int64_t id;
-    if (!db_->LookupResource(publicId, id, type))
+    if (!db_.LookupResource(publicId, id, type))
     {
       throw OrthancException(ErrorCode_UnknownResource);
     }
 
     int64_t parentId;
-    if (db_->LookupParent(parentId, id))
+    if (db_.LookupParent(parentId, id))
     {
-      target = db_->GetPublicId(parentId);
+      target = db_.GetPublicId(parentId);
       return true;
     }
     else
@@ -1551,7 +1529,7 @@
   {
     boost::mutex::scoped_lock lock(mutex_);
 
-    std::auto_ptr<SQLite::ITransaction> transaction(db_->StartTransaction());
+    std::auto_ptr<SQLite::ITransaction> transaction(db_.StartTransaction());
 
     transaction->Begin();
     uint64_t seq = IncrementGlobalSequenceInternal(sequence);
@@ -1566,12 +1544,12 @@
                               const std::string& publicId)
   {
     boost::mutex::scoped_lock lock(mutex_);
-    std::auto_ptr<SQLite::ITransaction> transaction(db_->StartTransaction());
+    std::auto_ptr<SQLite::ITransaction> transaction(db_.StartTransaction());
     transaction->Begin();
 
     int64_t id;
     ResourceType type;
-    if (!db_->LookupResource(publicId, id, type))
+    if (!db_.LookupResource(publicId, id, type))
     {
       throw OrthancException(ErrorCode_UnknownResource);
     }
@@ -1585,13 +1563,13 @@
   void ServerIndex::DeleteChanges()
   {
     boost::mutex::scoped_lock lock(mutex_);
-    db_->ClearTable("Changes");
+    db_.ClearTable("Changes");
   }
 
   void ServerIndex::DeleteExportedResources()
   {
     boost::mutex::scoped_lock lock(mutex_);
-    db_->ClearTable("ExportedResources");
+    db_.ClearTable("ExportedResources");
   }
 
 
@@ -1618,16 +1596,16 @@
       int64_t resource = toExplore.top();
       toExplore.pop();
 
-      ResourceType thisType = db_->GetResourceType(resource);
+      ResourceType thisType = db_.GetResourceType(resource);
 
       std::list<FileContentType> f;
-      db_->ListAvailableAttachments(f, resource);
+      db_.ListAvailableAttachments(f, resource);
 
       for (std::list<FileContentType>::const_iterator
              it = f.begin(); it != f.end(); ++it)
       {
         FileInfo attachment;
-        if (db_->LookupAttachment(attachment, resource, *it))
+        if (db_.LookupAttachment(attachment, resource, *it))
         {
           compressedSize += attachment.GetCompressedSize();
           uncompressedSize += attachment.GetUncompressedSize();
@@ -1656,7 +1634,7 @@
 
         // Tag all the children of this resource as to be explored
         std::list<int64_t> tmp;
-        db_->GetChildrenInternalId(tmp, resource);
+        db_.GetChildrenInternalId(tmp, resource);
         for (std::list<int64_t>::const_iterator 
                it = tmp.begin(); it != tmp.end(); ++it)
         {
@@ -1685,7 +1663,7 @@
 
     ResourceType type;
     int64_t top;
-    if (!db_->LookupResource(publicId, top, type))
+    if (!db_.LookupResource(publicId, top, type))
     {
       throw OrthancException(ErrorCode_UnknownResource);
     }
@@ -1734,7 +1712,7 @@
 
     ResourceType type;
     int64_t top;
-    if (!db_->LookupResource(publicId, top, type))
+    if (!db_.LookupResource(publicId, top, type))
     {
       throw OrthancException(ErrorCode_UnknownResource);
     }
@@ -1771,7 +1749,7 @@
         int64_t id = that->unstableResources_.RemoveOldest(payload);
 
         // Ensure that the resource is still existing before logging the change
-        if (that->db_->IsExistingResource(id))
+        if (that->db_.IsExistingResource(id))
         {
           switch (payload.GetResourceType())
           {
@@ -1829,14 +1807,14 @@
     boost::mutex::scoped_lock lock(mutex_);
 
     std::list<int64_t> id;
-    db_->LookupIdentifier(id, tag, value);
+    db_.LookupIdentifier(id, tag, value);
 
     for (std::list<int64_t>::const_iterator 
            it = id.begin(); it != id.end(); ++it)
     {
-      if (db_->GetResourceType(*it) == type)
+      if (db_.GetResourceType(*it) == type)
       {
-        result.push_back(db_->GetPublicId(*it));
+        result.push_back(db_.GetPublicId(*it));
       }
     }
   }
@@ -1851,12 +1829,12 @@
     boost::mutex::scoped_lock lock(mutex_);
 
     std::list<int64_t> id;
-    db_->LookupIdentifier(id, tag, value);
+    db_.LookupIdentifier(id, tag, value);
 
     for (std::list<int64_t>::const_iterator 
            it = id.begin(); it != id.end(); ++it)
     {
-      result.push_back(db_->GetPublicId(*it));
+      result.push_back(db_.GetPublicId(*it));
     }
   }
 
@@ -1869,13 +1847,13 @@
     boost::mutex::scoped_lock lock(mutex_);
 
     std::list<int64_t> id;
-    db_->LookupIdentifier(id, value);
+    db_.LookupIdentifier(id, value);
 
     for (std::list<int64_t>::const_iterator 
            it = id.begin(); it != id.end(); ++it)
     {
-      result.push_back(std::make_pair(db_->GetResourceType(*it),
-                                      db_->GetPublicId(*it)));
+      result.push_back(std::make_pair(db_.GetResourceType(*it),
+                                      db_.GetPublicId(*it)));
     }
   }
 
@@ -1889,20 +1867,20 @@
 
     ResourceType resourceType;
     int64_t resourceId;
-    if (!db_->LookupResource(publicId, resourceId, resourceType))
+    if (!db_.LookupResource(publicId, resourceId, resourceType))
     {
       return StoreStatus_Failure;  // Inexistent resource
     }
 
     // Remove possible previous attachment
-    db_->DeleteAttachment(resourceId, attachment.GetContentType());
+    db_.DeleteAttachment(resourceId, attachment.GetContentType());
 
     // Locate the patient of the target resource
     int64_t patientId = resourceId;
     for (;;)
     {
       int64_t parent;
-      if (db_->LookupParent(parent, patientId))
+      if (db_.LookupParent(parent, patientId))
       {
         // We have not reached the patient level yet
         patientId = parent;
@@ -1915,10 +1893,10 @@
     }
 
     // Possibly apply the recycling mechanism while preserving this patient
-    assert(db_->GetResourceType(patientId) == ResourceType_Patient);
-    Recycle(attachment.GetCompressedSize(), db_->GetPublicId(patientId));
+    assert(db_.GetResourceType(patientId) == ResourceType_Patient);
+    Recycle(attachment.GetCompressedSize(), db_.GetPublicId(patientId));
 
-    db_->AddAttachment(resourceId, attachment);
+    db_.AddAttachment(resourceId, attachment);
 
     t.Commit(attachment.GetCompressedSize());
 
@@ -1935,12 +1913,12 @@
 
     ResourceType rtype;
     int64_t id;
-    if (!db_->LookupResource(publicId, id, rtype))
+    if (!db_.LookupResource(publicId, id, rtype))
     {
       throw OrthancException(ErrorCode_UnknownResource);
     }
 
-    db_->DeleteAttachment(id, type);
+    db_.DeleteAttachment(id, type);
 
     t.Commit(0);
   }
@@ -1955,13 +1933,13 @@
 
     ResourceType type;
     int64_t id;
-    if (!db_->LookupResource(publicId, id, type))
+    if (!db_.LookupResource(publicId, id, type))
     {
       return false;
     }
 
     std::list<MetadataType> metadata;
-    db_->ListAvailableMetadata(metadata, id);
+    db_.ListAvailableMetadata(metadata, id);
 
     for (std::list<MetadataType>::const_iterator
            it = metadata.begin(); it != metadata.end(); it++)
@@ -1969,7 +1947,7 @@
       std::string key = EnumerationToString(*it);
 
       std::string value;
-      if (!db_->LookupMetadata(value, id, *it))
+      if (!db_.LookupMetadata(value, id, *it))
       {
         value.clear();
       }
@@ -1987,7 +1965,7 @@
     boost::mutex::scoped_lock lock(mutex_);
 
     std::string value;
-    if (db_->LookupGlobalProperty(value, property))
+    if (db_.LookupGlobalProperty(value, property))
     {
       return value;
     }
--- a/OrthancServer/ServerIndex.h	Mon Dec 08 12:56:30 2014 +0100
+++ b/OrthancServer/ServerIndex.h	Mon Dec 08 13:54:27 2014 +0100
@@ -40,7 +40,7 @@
 #include "../Core/DicomFormat/DicomInstanceHasher.h"
 #include "ServerEnumerations.h"
 
-#include "DatabaseWrapper.h"
+#include "IDatabaseWrapper.h"
 
 
 namespace Orthanc
@@ -68,7 +68,7 @@
     boost::thread unstableResourcesMonitorThread_;
 
     std::auto_ptr<Internals::ServerIndexListener> listener_;
-    std::auto_ptr<DatabaseWrapper> db_;
+    IDatabaseWrapper& db_;
     LeastRecentlyUsedIndex<int64_t, UnstableResourcePayload>  unstableResources_;
 
     uint64_t currentStorageSize_;
@@ -113,7 +113,7 @@
                    const std::string& publicId)
     {
       ServerIndexChange change(changeType, resourceType, publicId);
-      db_->LogChange(internalId, change);
+      db_.LogChange(internalId, change);
     }
 
     uint64_t IncrementGlobalSequenceInternal(GlobalProperty property);
@@ -121,7 +121,7 @@
 
   public:
     ServerIndex(ServerContext& context,
-                const std::string& dbPath);
+                IDatabaseWrapper& database);
 
     ~ServerIndex();
 
@@ -164,20 +164,20 @@
                         const std::string& uuid,
                         ResourceType expectedType);
 
-    bool GetChanges(Json::Value& target,
+    void GetChanges(Json::Value& target,
                     int64_t since,
                     unsigned int maxResults);
 
-    bool GetLastChange(Json::Value& target);
+    void GetLastChange(Json::Value& target);
 
     void LogExportedResource(const std::string& publicId,
                              const std::string& remoteModality);
 
-    bool GetExportedResources(Json::Value& target,
+    void GetExportedResources(Json::Value& target,
                               int64_t since,
                               unsigned int maxResults);
 
-    bool GetLastExportedResource(Json::Value& target);
+    void GetLastExportedResource(Json::Value& target);
 
     bool IsProtectedPatient(const std::string& publicId);
 
--- a/OrthancServer/main.cpp	Mon Dec 08 12:56:30 2014 +0100
+++ b/OrthancServer/main.cpp	Mon Dec 08 13:54:27 2014 +0100
@@ -50,6 +50,7 @@
 #include "OrthancFindRequestHandler.h"
 #include "OrthancMoveRequestHandler.h"
 #include "ServerToolbox.h"
+#include "DatabaseWrapper.h"
 #include "../Plugins/Engine/PluginsManager.h"
 #include "../Plugins/Engine/OrthancPlugins.h"
 
@@ -431,13 +432,29 @@
 static bool StartOrthanc()
 {
   std::string storageDirectoryStr = Configuration::GetGlobalStringParameter("StorageDirectory", "OrthancStorage");
+
+
+  // Open the database
   boost::filesystem::path indexDirectory = Configuration::InterpretStringParameterAsPath(
     Configuration::GetGlobalStringParameter("IndexDirectory", storageDirectoryStr));
 
+  std::auto_ptr<IDatabaseWrapper> database;
+  try
+  {
+    boost::filesystem::create_directories(indexDirectory);
+  }
+  catch (boost::filesystem::filesystem_error)
+  {
+  }
+
+  database.reset(new DatabaseWrapper(indexDirectory.string() + "/index"));
+
+
   // "storage" must be declared BEFORE "ServerContext context", to
   // avoid mess in the invokation order of the destructors.
   std::auto_ptr<IStorageArea>  storage;
-  ServerContext context(indexDirectory);
+
+  ServerContext context(*database);
 
   LOG(WARNING) << "Index directory: " << indexDirectory;
 
--- a/UnitTestsSources/ServerIndexTests.cpp	Mon Dec 08 12:56:30 2014 +0100
+++ b/UnitTestsSources/ServerIndexTests.cpp	Mon Dec 08 13:54:27 2014 +0100
@@ -110,7 +110,8 @@
     virtual void SetUp() 
     {
       listener_.reset(new ServerIndexListener);
-      index_.reset(new DatabaseWrapper(*listener_));
+      index_.reset(new DatabaseWrapper());
+      index_->SetListener(*listener_);
     }
 
     virtual void TearDown()
@@ -547,7 +548,8 @@
 
   Toolbox::RemoveFile(path + "/index");
   FilesystemStorage storage(path);
-  ServerContext context(":memory:");   // The SQLite DB is in memory
+  DatabaseWrapper db;   // The SQLite DB is in memory
+  ServerContext context(db);
   context.SetStorageArea(storage);
   ServerIndex& index = context.GetIndex();
 
@@ -614,7 +616,8 @@
 
   Toolbox::RemoveFile(path + "/index");
   FilesystemStorage storage(path);
-  ServerContext context(":memory:");   // The SQLite DB is in memory
+  DatabaseWrapper db;   // The SQLite DB is in memory
+  ServerContext context(db);
   context.SetStorageArea(storage);
   ServerIndex& index = context.GetIndex();