Revert "Remove mysql and oracle resources for anna-ericsson project"
[anna.git] / source / dbos / StorageArea.cpp
diff --git a/source/dbos/StorageArea.cpp b/source/dbos/StorageArea.cpp
new file mode 100644 (file)
index 0000000..830f647
--- /dev/null
@@ -0,0 +1,1081 @@
+// ANNA - Anna is Not Nothingness Anymore                                                         //
+//                                                                                                //
+// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo                         //
+//                                                                                                //
+// See project site at http://redmine.teslayout.com/projects/anna-suite                           //
+// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE //
+
+
+#include <typeinfo>
+
+#include <algorithm>
+
+#include <anna/core/tracing/Logger.hpp>
+#include <anna/core/functions.hpp>
+
+#include <anna/dbms/dbms.hpp>
+#include <anna/dbos/dbos.hpp>
+
+#include <anna/xml/Node.hpp>
+#include <anna/xml/Attribute.hpp>
+
+using namespace std;
+using namespace anna;
+
+StorageArea::StorageArea(const char* name, const Size maxSize, ObjectAllocator objectAllocator, const StorageArea::AccessMode::_v accessMode, const int errorCode) :
+  a_name(name),
+  a_maxSize(maxSize),
+  a_objectAllocator(objectAllocator),
+  a_accessMode(accessMode),
+  a_errorCode(errorCode),
+  a_indexBlock(0),
+  a_sizeof(0),
+  a_doneReuse(0) {
+  a_blocks.push_back(a_currentBlock = new Block(objectAllocator, maxSize));
+  a_hit = a_fault = 0;
+}
+
+StorageArea::~StorageArea() {
+}
+
+//------------------------------------------------------------------------------------------------
+// Carga los datos del objeto y los guarda en el area de almacemiento.
+//
+// (1) Si no esta en memoria => se carga.
+//     (1.1) Si el registro no existe => se progresa la posible excepcion.
+//
+// (2) Cambia la politica de refresco de las areas de almacenamiento ReadWrite/Dirty. La carga solo se
+// realizara cuando la copia de utilizacion sea 0. Ademas de la mejora de rendimiento aseguramos que
+// la estabilidad de una instancia se mantiene durante toda la vida de esta. Por ejmplo evitamos que
+// una instancia A este trabajando con una copia de una instancia que estamos liberando y volviendo
+// a cargar con datos totalmente distintos ... imaginad que estamos recorriendo un vector asociado
+// o algo asi.
+//
+// (3) Si no ha podido ser recargada (seguramente tiene mas de una referencia) debe evitar el
+// uso en caso de que este marcada como Dirty.
+// (4) Si el contador de uso es cero => que ya esta marcado como memoria libre, pero recordar que
+// tambien puede estar en la memoria activa (pendiente de liberar y/o volver a usar). En este
+// caso se ha reusado.
+//------------------------------------------------------------------------------------------------
+Object* StorageArea::instance(Connection* connection, Loader& loader)
+throw(RuntimeException, DatabaseException) {
+  const Index index = loader.getIndex();
+  Object* result(NULL);
+  Instance* instance(NULL);
+  LOGDEBUG(
+    string msg("Instantiate (init) | ");
+    msg += asString();
+    msg += " | ";
+    msg += loader.asString();
+    msg += " | Index: ";
+    msg += functions::asHexString(index);
+    Logger::write(Logger::Debug, msg, ANNA_FILE_LOCATION)
+  );
+  loader.a_connection = connection;
+  std::string name("dbos::StorageArea::instance with ");
+  name += typeid(loader).name();
+  Guard guard(this, name.c_str());
+  iterator ii = a_directory.find(index);
+
+  if(ii == a_directory.end()) {                                                 // (1)
+    a_fault ++;
+
+    if(loader.load(connection, this) == true) {                                     // (1.1)
+      pair <iterator, bool> rr;
+      bool wasInserted = false;
+      instance = allocate();
+
+      try {
+        instance->object->setIndex(index);
+        /* Al añadir la instancia antes de invocar al método Object::initialize
+         * nos aseguramos de que se permitan implementar relaciones circulares
+         */
+        rr = a_directory.insert(value_type(index, instance));
+        wasInserted = true;
+        result = instance->object;
+        instance->flags |= Flag::InProgress;
+        instance->object->initialize(loader);
+        instance->object->a_isStored = true;
+        instance->flags &= Flag::Done;
+      } catch(DatabaseException&) {
+        instance->flags &= Flag::Done;
+
+        if(wasInserted)
+          a_directory.erase(rr.first);
+
+        a_holes.insert(instance, Holes::Mode::ReadyToReuse);
+        throw;
+      } catch(RuntimeException&) {
+        instance->flags &= Flag::Done;
+
+        if(wasInserted)
+          a_directory.erase(rr.first);
+
+        a_holes.insert(instance, Holes::Mode::ReadyToReuse);
+        throw;
+      }
+    }
+  } else {
+    static const bool IgnoreDirty = true;
+    verifyStatus(instance = StorageArea::instance(ii), IgnoreDirty);
+
+    switch(a_accessMode) {
+    case AccessMode::ReadOnly:
+      result = ((instance->flags & Flag::Dirty) == 0) ? instance->object : reload(connection, loader, instance);
+      break;
+    case AccessMode::ReadWrite:
+      result = (instance->copyCounter > 0) ? instance->object : reload(connection, loader, instance);
+      break;
+    case AccessMode::ReadEver:
+      result = reload(connection, loader, instance);
+      break;
+    }
+
+    if(instance->flags & Flag::Dirty) {                                        // (3)
+      string msg(asString());
+      msg += " | ";
+      msg += asString(instance);
+      msg += " | Instance selected as unusable";
+      throw RuntimeException(msg, ANNA_FILE_LOCATION);
+    }
+
+    if(result != NULL) {
+      a_holes.erase(instance);                                                // (4)
+      instance->copyCounter ++;
+      a_hit ++;
+    }
+  }
+
+  LOGINFORMATION(
+    string msg("Instantiate (final) | ");
+    msg += asString();
+    msg += " | ";
+    msg += asString(instance);
+    Logger::information(msg, ANNA_FILE_LOCATION)
+  );
+  return result;
+}
+
+Object* StorageArea::instance(Connection* connection, CrossedLoader& crossedLoader, Loader& loader)
+throw(RuntimeException, DatabaseException) {
+  Object* result = NULL;
+  crossedLoader.a_connection = connection;
+  // Si el seek devuelve 'true' es que ya tiene cargada la correspondencia entre la clave alternativa y la
+  // clave principal usada en el Loader recibido como parámetro.
+  bool loaded = (crossedLoader.seek() == false) ? crossedLoader.load(connection, this) : true;
+
+  if(loaded == true) {
+    /*
+     * Transfiere la clave principal conseguida por el cargador cruzado.
+     */
+    loader.upload(crossedLoader);
+    result = instance(connection, loader);
+    /*
+     * Da la posibilidad de que el cargador cruzado mantenga la correspondencia entre sus claves y las claves primarias
+     */
+    crossedLoader.download(loader);
+  }
+
+  return result;
+}
+
+//-------------------------------------------------------------------------
+// Crea un nuevo objeto en el area de almacenamiento.
+//-------------------------------------------------------------------------
+Object* StorageArea::create(Connection* connection, Creator& creator)
+throw(RuntimeException, DatabaseException) {
+  const Index index = creator.getIndex();
+  Instance* instance = NULL;
+  Object* result = NULL;
+
+  if(a_accessMode == AccessMode::ReadOnly) {
+    string msg(asString());
+    msg += " | Cannot create object with AccessMode::ReadOnly";
+    throw RuntimeException(msg, ANNA_FILE_LOCATION);
+  }
+
+  LOGDEBUG(
+    string msg(asString());
+    msg += " | ";
+    msg += creator.asString();
+    msg += " | Index: ";
+    msg += functions::asHexString(index);
+    Logger::write(Logger::Debug, msg, ANNA_FILE_LOCATION)
+  );
+  creator.a_connection = connection;
+  std::string name("dbos::StorageArea::create with ");
+  name += typeid(creator).name();
+  Guard guard(this, name.c_str());
+  iterator ii = a_directory.find(index);
+
+  if(ii == a_directory.end()) {
+    pair <iterator, bool> rr;
+    bool wasInserted = false;
+    a_fault ++;
+    instance = allocate();
+
+    try {
+      instance->object->setIndex(index);
+      instance->copyCounter = 1;
+      result = instance->object;
+      rr = a_directory.insert(value_type(index, instance));
+      wasInserted = true;
+      instance->flags |= Flag::InProgress;
+      instance->object->create(creator);
+      instance->flags &= Flag::Done;
+      instance->object->a_isStored = false;
+    } catch(DatabaseException&) {
+      instance->flags &= Flag::Done;
+
+      if(wasInserted)
+        a_directory.erase(rr.first);
+
+      a_holes.insert(instance, Holes::Mode::ReadyToReuse);
+      throw;
+    } catch(RuntimeException&) {
+      instance->flags &= Flag::Done;
+
+      if(wasInserted)
+        a_directory.erase(rr.first);
+
+      a_holes.insert(instance, Holes::Mode::ReadyToReuse);
+      throw;
+    }
+  } else {
+    verifyStatus(instance = StorageArea::instance(ii));
+    a_hit ++;
+    a_holes.erase(instance);
+    instance->copyCounter ++;
+    result = instance->object;
+  }
+
+  LOGINFORMATION(
+    string msg("Create | ");
+    msg += asString();
+    msg += " | ";
+    msg += asString(instance);
+    Logger::information(msg, ANNA_FILE_LOCATION)
+  );
+  return result;
+}
+
+//-------------------------------------------------------------------------
+// Carga los datos del objeto y los guarda en el area de almacemiento.
+//
+// (1) Si tiene cuenta 0 => estaba en la lista de objetos liberados =>
+//     lo sacamos de ah�
+//-------------------------------------------------------------------------
+Object* StorageArea::find(Loader& loader)
+throw(RuntimeException) {
+  const Index index = loader.getIndex();
+  Instance* instance = NULL;
+  Object* result = NULL;
+  LOGDEBUG(
+    string msg(asString());
+    msg += " | ";
+    msg += loader.asString();
+    msg += " | Index: ";
+    msg += functions::asHexString(index);
+    Logger::write(Logger::Debug, msg, ANNA_FILE_LOCATION)
+  );
+  std::string name("dbos::StorageArea::find with ");
+  name += typeid(loader).name();
+  Guard guard(this, name.c_str());
+  iterator ii = a_directory.find(index);
+
+  if(ii != a_directory.end()) {
+    verifyStatus(instance = StorageArea::instance(ii));
+    a_hit ++;
+    a_holes.erase(instance);
+    instance->copyCounter ++;
+    result = instance->object;
+  } else
+    a_fault ++;
+
+  LOGDEBUG(
+    string msg("Find | ");
+    msg += asString();
+    msg += " | ";
+    msg += asString(instance);
+    Logger::write(Logger::Debug, msg, ANNA_FILE_LOCATION)
+  );
+  return result;
+}
+
+Object* StorageArea::duplicate(const Object* object)
+throw(RuntimeException) {
+  if(object == NULL) return NULL;
+
+  std::string name("dbos::StorageArea::duplicate with ");
+  name += typeid(*object).name();
+  Guard guard(this, name.c_str());
+  iterator ii = a_directory.find(object->getIndex());
+
+  if(ii == a_directory.end()) {
+    a_fault ++;
+    string msg(asString());
+    msg += " | Index: ";
+    msg += functions::asHexString(object->getIndex());
+    msg += " | Invalid instance";
+    throw RuntimeException(msg, ANNA_FILE_LOCATION);
+  }
+
+  Instance* instance = NULL;
+  verifyStatus(instance = StorageArea::instance(ii));
+  a_holes.erase(instance);
+  instance->copyCounter ++;
+  a_hit ++;
+  LOGINFORMATION(
+    string msg("Duplicate | ");
+    msg += asString();
+    msg += " | ";
+    msg += asString(instance);
+    Logger::information(msg, ANNA_FILE_LOCATION)
+  );
+  return instance->object;
+}
+
+bool StorageArea::isLoaded(const Loader& loader)
+throw(RuntimeException) {
+  const Index index = loader.getIndex();
+  std::string name("dbos::StorageArea::isLoaded with ");
+  name += typeid(loader).name();
+  Guard guard(this, name.c_str());
+  iterator ii = a_directory.find(index);
+  const bool result = (ii != a_directory.end());
+  LOGDEBUG(
+    string msg(asString());
+    msg += " | ";
+    msg += loader.asString();
+    msg += " | Index: ";
+    msg += functions::asHexString((int) index);
+    msg += functions::asText(" | isLoaded: ", result);
+    Logger::debug(msg, ANNA_FILE_LOCATION);
+  );
+  return result;
+}
+
+void StorageArea::apply(Connection& connection, Recorder& recorder)
+throw(RuntimeException, DatabaseException) {
+  ResultCode resultCode = connection.execute(recorder.getStatement());
+
+  if(resultCode.successful() == false)
+    throw DatabaseException(resultCode, ANNA_FILE_LOCATION);
+}
+
+//------------------------------------------------------------------------------------------------
+// Borra un objeto del medio fisico => lo borra tambien de la cache
+//
+// (1) Como copyCounter = 0 => Lo metera en lista de huecos, le quitara del directorio y
+// si fuera necesario invocara al 'destroy'.
+// (2) No la puede sacar de la memoria porque tiene referencias activas, pero por lo menos la
+// marca como no usable para intentar provocar los avisos de uso incorrecto y expulsar la
+// instancia en cuanto pueda.
+//------------------------------------------------------------------------------------------------
+void StorageArea::apply(Connection& connection, Eraser& eraser)
+throw(RuntimeException, DatabaseException) {
+  if(a_accessMode == AccessMode::ReadOnly) {
+    string msg(asString());
+    msg += " | Cannot erase object with AccessMode::ReadOnly";
+    throw RuntimeException(msg, ANNA_FILE_LOCATION);
+  }
+
+  Object* object = eraser.getObject();
+  eraser.a_connection = &connection;
+  std::string name("dbos::StorageArea::apply with ");
+  name += typeid(eraser).name();
+  Guard guard(this, name.c_str());
+  Instance* instance = NULL;
+
+  if(object != NULL) {
+    iterator ii = a_directory.find(object->getIndex());
+
+    if(ii != a_directory.end()) {
+      instance = StorageArea::instance(ii);
+
+      if(instance->copyCounter > 1) {
+        instance->flags |= Flag::Incoherent;                         // (2)
+        string msg(eraser.asString());
+        msg += " | Instances: ";
+        msg += functions::asString(instance->copyCounter);
+        msg += " | Cannot delete object";
+        throw RuntimeException(msg, ANNA_FILE_LOCATION);
+      }
+    }
+  }
+
+  ResultCode resultCode = connection.execute(eraser.getStatement());
+
+  if(resultCode.successful() == false)
+    throw DatabaseException(resultCode, ANNA_FILE_LOCATION);
+
+  if(instance != NULL) {
+    instance->copyCounter = 0;
+    quickReusing(instance);                                             // (1)
+  }
+}
+
+//------------------------------------------------------------------------------------------------
+// Decrementa la cuenta de utilizacin del objeto recibido como parametro.
+//
+// (1) Si la instancia que estamos liberando esta marcada como 'Incoherente' y es la ultima
+// referencia => es el momento de expulsarla de la memoria.
+// (2) Queda a la espera de que se vuelva a usar la referencia o que el numero de registros en
+// memoria alcance un numero tal que implique comenzar a reusar objetos liberados.
+//------------------------------------------------------------------------------------------------
+void StorageArea::release(Object** object)
+throw(RuntimeException) {
+  if(object == NULL) return;
+
+  if(*object == NULL) return;
+
+  std::string name("dbos::StorageArea::release with ");
+  name += typeid(**object).name();
+  Guard guard(this, name.c_str());
+  iterator ii = a_directory.find((*object)->getIndex());
+
+  if(ii == a_directory.end())
+    return;
+
+  Instance* instance = StorageArea::instance(ii);
+
+  if(instance->copyCounter > 0) {
+    if(-- instance->copyCounter == 0) {
+      if(instance->flags & Flag::Incoherent)                                   // (1)
+        quickReusing(instance);
+      else
+        a_holes.insert(instance, Holes::Mode::TimeWait);                     // (2)
+    }
+  }
+
+  LOGINFORMATION(
+    string msg("Release | ");
+    msg += asString();
+    msg += " | ";
+    msg += asString(instance);
+    Logger::information(msg, ANNA_FILE_LOCATION)
+  );
+  *object = NULL;
+}
+
+//------------------------------------------------------------------------------------------------
+// Elimina toda la informacin referente al objeto recibido como parametro.
+//
+// (1) No la puede sacar de la memoria porque tiene referencias activas, pero por lo menos la
+// marca como no usable para intentar provocar los avisos de uso incorrecto y expulsar la
+// instancia en cuanto pueda.
+// (2) Como copyCounter = 0 => Lo metera en lista de huecos, le quitara del directorio y
+// si fuera necesario invocara al 'destroy'.
+//------------------------------------------------------------------------------------------------
+void StorageArea::erase(Object** object)
+throw(RuntimeException) {
+  if(object == NULL) return;
+
+  if(*object == NULL) return;
+
+  std::string name("dbos::StorageArea::erase with ");
+  name += typeid(**object).name();
+  Guard guard(this, name.c_str());
+  iterator ii = a_directory.find((*object)->getIndex());
+
+  if(ii == a_directory.end())
+    return;
+
+  Instance* instance = StorageArea::instance(ii);
+
+  if(instance->copyCounter > 1) {
+    instance->flags |= Flag::Incoherent;                                 // (1)
+    string msg(asString());
+    msg += " | ";
+    msg += asString(instance);
+    msg += " | Cannot dump instance";
+    throw RuntimeException(msg, ANNA_FILE_LOCATION);
+  }
+
+  LOGDEBUG(
+    string msg("Erase | ");
+    msg += asString();
+    msg += " | ";
+    msg += asString(instance);
+    Logger::debug(msg, ANNA_FILE_LOCATION)
+  );
+  instance->copyCounter = 0;
+  quickReusing(instance);                                                 // (2)
+  *object = NULL;
+}
+
+void StorageArea::dirty(Object* object)
+throw(RuntimeException) {
+  if(object == NULL) return;
+
+  std::string name("dbos::StorageArea::dirty with ");
+  name += typeid(*object).name();
+  Guard guard(this, name.c_str());
+  iterator ii = a_directory.find(object->getIndex());
+
+  if(ii == a_directory.end())
+    return;
+
+  Instance* instance = StorageArea::instance(ii);
+
+  if(instance->copyCounter > 1 && Logger::isActive(Logger::Warning)) {
+    string msg(asString());
+    msg += " | ";
+    msg += asString(instance);
+    msg += " | More than one copy on instance to be selected";
+    Logger::warning(msg, ANNA_FILE_LOCATION);
+  }
+
+  LOGDEBUG(
+    string msg("Dirty | ");
+    msg += asString();
+    msg += " | ";
+    msg += asString(instance);
+    Logger::debug(msg, ANNA_FILE_LOCATION)
+  );
+}
+
+//--------------------------------------------------------------------------------------------
+// Descarga todos los objetos de este area de almacenamiento.
+//
+// (1) Para que permite incluirla en los huecos.
+// (3) Si fuera necesario invoca al destroy => cuando se reuse no tendra que hacerlo.
+// (4) Si la entrada estaba marcada como 'incoherente' la desmarca, ya que hemos conseguido
+// expulsarla de cache.
+//--------------------------------------------------------------------------------------------
+void StorageArea::clear()
+throw(RuntimeException) {
+  Guard guard(this, "dbos::StorageArea::clear");
+  Instance* instance;
+  int n = 0;
+
+  for(iterator ii = begin(), maxii = end(); ii != maxii; ii ++) {
+    instance = StorageArea::instance(ii);
+    instance->copyCounter = 0;                                              // (1)
+    a_holes.insert(instance, Holes::Mode::ReadyToReuse);
+
+    if((instance->flags & Flag::Empty) == 0) {                              // (3)
+      LOGINFORMATION(
+        string msg("Destroy (clear) | ");
+        msg += asString();
+        msg += " | ";
+        msg += asString(instance);
+        Logger::information(msg, ANNA_FILE_LOCATION);
+      );
+      instance->object->destroy();
+      instance->flags |= Flag::Empty;
+    }
+
+    instance->flags &= Flag::NoIncoherent;                                  // (4)
+    n ++;
+  }
+
+  a_directory.clear();
+  LOGWARNING(
+    string msg("Clear | ");
+    msg += asString();
+    msg += functions::asText(" | Destroyed objects: ", n);
+    Logger::warning(msg, ANNA_FILE_LOCATION)
+  );
+}
+
+//---------------------------------------------------------------------------------------------------
+// Intenta recargar la informacion del medio fisico en la instancia del objeto.
+// Si hay algun problema y la instancia no puede guardarse en la de objetos disponibles
+// (a_holes.insert == false) => se marca como corrupto y no se podra usar hasta que
+// que no se libere su ultima instancia.
+//
+// (1) Si hubiera algun problema al invocar al 'initialize' no habria que volver a invocar
+// a este metodo.
+// (2) Recordar que la recarga solo se intenta cuando la copyCounter = 0, por tanto en el caso de
+// intentar recargar un registro que ha sido borrado no se vuelve a grabar como Ready porque
+// YA esta en la lista de huecos y seria necesario borrarlo de esta, cambiarle el msHoleTime y
+// volverlo a grabar, pero la cosa funciona porque se saca del directorio de objetos cargados
+// y se libera su memoria.
+//---------------------------------------------------------------------------------------------------
+Object* StorageArea::reload(dbms::Connection* connection, Loader& loader, StorageArea::Instance* instance)
+throw(RuntimeException, dbms::DatabaseException) {
+  const bool enableUpdate = (instance->flags & Flag::Dirty) ? true : instance->object->enableUpdate();
+  bool hasChanges(false);
+
+  if(enableUpdate == true) {
+    try {
+      if(loader.load(connection, this) == false) {
+        checkIncoherence(instance);                                          // (2)
+        return NULL;
+      }
+    } catch(RuntimeException&) {
+      checkIncoherence(instance);
+      throw;
+    } catch(dbms::DatabaseException&) {
+      checkIncoherence(instance);
+      throw;
+    }
+
+    hasChanges = (instance->flags & Flag::Dirty) ? true : instance->object->hasChanges(loader);
+  }
+
+  LOGDEBUG(
+    string msg("Reload | ");
+    msg += asString();
+    msg += " | ";
+    msg += asString(instance);
+    msg += anna::functions::asText(" | EnableUpdate: ", enableUpdate);
+    msg += anna::functions::asText(" | HasChanges: ", hasChanges);
+    Logger::debug(msg, ANNA_FILE_LOCATION);
+  );
+
+  if(hasChanges == true) {
+    LOGINFORMATION(
+      string msg("Destroy (reload) | ");
+      msg += asString();
+      msg += " | ";
+      msg += asString(instance);
+      Logger::information(msg, ANNA_FILE_LOCATION);
+    );
+    instance->object->destroy();
+    instance->flags |= Flag::Empty;                                            // (1)
+    instance->object->initialize(loader);
+    instance->flags &= Flag::NoEmpty;
+    instance->flags &= Flag::NoDirty;
+  }
+
+  return instance->object;
+}
+
+//------------------------------------------------------------------------------------------------
+// Cuando intenta recargar la informacion de una instancia desde el medio fisico, pero el
+// registro ha sido borrado =>
+// 1.- Si hay alguna instancia en uso => no puede liberar la instancia porque algun otro
+// objeto la tiene referenciada => la marca como corrupta.
+// 2.- Si hay una unica instancia en uso => puede liberarla
+//
+// (1) si la puede grabar en los huecos o la instancia ya esta en los huecos => saca el objeto
+// del directorio porque yo no es valido. Recupera la coherencia memoria-medio_fisico
+// porque ha conseguido "expulsar" de la cache el registro borrado.
+// (2) No puede "expulsar" el registro de la cache porque hay algun otro objeto que lo esta
+// referenciando => Marca ESTA instancia como incoherente => no se podra duplicar y volver
+// a instanciar, etc, etc
+//------------------------------------------------------------------------------------------------
+void StorageArea::checkIncoherence(StorageArea::Instance* instance)
+throw() {
+  if(quickReusing(instance) == true) {
+    LOGWARNING(
+      string msg("dbos::StorageArea::checkIncoherence | ");
+      msg += asString();
+      msg += " | ";
+      msg += asString(instance);
+      msg += " | Recover coherence between physical media and memory";
+      Logger::warning(msg, ANNA_FILE_LOCATION);
+    );
+  } else {                                                             // (2)
+    instance->flags |= Flag::Incoherent;
+    LOGWARNING(
+      string msg("dbos::StorageArea::checkIncoherence | ");
+      msg += asString();
+      msg += " | ";
+      msg += asString(instance);
+      msg += " | Detected incoherence between physical media and memory";
+      Logger::warning(msg, ANNA_FILE_LOCATION);
+    );
+  }
+}
+
+//------------------------------------------------------------------------------------------------
+// (1) si la puede grabar en los huecos o la instancia ya esta en los huecos => (2)
+// (2) saca el objeto del directorio porque yo no es valido.
+// (3) Si fuera necesario invoca al destroy => cuando se reuse no tendra que hacerlo.
+// (4) Si la entrada estaba marcada como 'incoherente' la desmarca, ya que hemos conseguido
+// expulsarla de cache.
+//
+// Recordar que en el caso de intentar recargar un registro que ha sido borrado no se vuelve
+// a grabar como Ready porque ya esta en la lista de huecos y seria necesario borrarlo de
+// esta, cambiarle el msHoleTime y volverlo a grabar.
+//------------------------------------------------------------------------------------------------
+bool StorageArea::quickReusing(StorageArea::Instance* instance)
+throw() {
+  bool result(false);
+
+  if(a_holes.insert(instance, Holes::Mode::ReadyToReuse) == true) {          // (1)
+    iterator ii = a_directory.find(instance->object->getIndex());
+
+    if(ii != a_directory.end())                                             // (2)
+      a_directory.erase(ii);
+
+    if((instance->flags & Flag::Empty) == 0) {                              // (3)
+      LOGINFORMATION(
+        string msg("Destroy (quickreusing) | ");
+        msg += asString();
+        msg += " | ";
+        msg += asString(instance);
+        Logger::information(msg, ANNA_FILE_LOCATION);
+      );
+      instance->object->destroy();
+      instance->flags |= Flag::Empty;
+    }
+
+    instance->flags &= Flag::NoIncoherent;                                  // (4)
+    result = true;
+  }
+
+  return result;
+}
+
+void StorageArea::verifyStatus(StorageArea::Instance* instance, const bool ignoreDirty)
+throw(RuntimeException) {
+  if(instance->flags & Flag::Incoherent) {
+    string msg(asString());
+    msg += " | ";
+    msg += asString(instance);
+    msg += " | Physical media and memory do not match";
+    throw RuntimeException(msg, ANNA_FILE_LOCATION);
+  }
+
+  if(instance->flags & Flag::Empty) {
+    string msg(asString());
+    msg += " | ";
+    msg += asString(instance);
+    msg += " | Instance is empty";
+    throw RuntimeException(msg, ANNA_FILE_LOCATION);
+  }
+
+  if(ignoreDirty == false && (instance->flags & Flag::Dirty)) {
+    string msg(asString());
+    msg += " | ";
+    msg += asString(instance);
+    msg += " | Instance is temporary locked";
+    throw RuntimeException(msg, ANNA_FILE_LOCATION);
+  }
+
+  if(instance->flags & Flag::InProgress) {
+    string msg(asString());
+    msg += " | ";
+    msg += asString(instance);
+    msg += " | Instance already has word in progress";
+    throw RuntimeException(msg, ANNA_FILE_LOCATION);
+  }
+}
+
+string StorageArea::asString() const
+throw() {
+  string result("dbos::StorageArea { Name: ");
+  const int ratio = (a_hit == 0) ? 0 : (a_hit * 100) / (a_fault + a_hit);
+  /* Nº real de objetos en uso. En el directorio también se mantienen los que tienen la cuenta de utilización a 0 */
+  const int n = a_directory.size() - a_holes.size();
+  result += a_name;
+  result += " | N: ";
+  result += functions::asString(a_maxSize);
+  result += " | n: ";
+  result += functions::asString(n);
+  result += " | r: ";
+  result += functions::asString(a_directory.size());
+  result += " | AccessMode: ";
+  result += AccessMode::asString(a_accessMode);
+  result += " | Holes: ";
+  result += functions::asString(a_holes.size());
+  result += " | Reuse: ";
+  result += functions::asString(a_doneReuse);
+  result += " | Hit: ";
+  result += functions::asString(a_hit);
+  result += " | Fault: ";
+  result += functions::asString(a_fault);
+  result += " | Ratio: ";
+  result += functions::asString(ratio);
+  return result += "% }";
+}
+
+xml::Node* StorageArea::asXML(xml::Node* parent) const
+throw() {
+  xml::Node* result = parent->createChild("dbos.StorageArea");
+  xml::Node* node;
+  const int ratio = (a_hit == 0) ? 0 : (a_hit * 100) / (a_fault + a_hit);
+  result->createAttribute("Name", a_name);
+  result->createAttribute("AccessMode", AccessMode::asString(a_accessMode));
+  node = result->createChild("Size");
+  node->createAttribute("Value", a_directory.size() - a_holes.size());
+  node->createAttribute("Reserve", a_directory.size());
+  node->createAttribute("MaxValue", a_maxSize);
+  node->createAttribute("Holes", a_holes.size());
+  node = result->createChild("SizeOf");
+  node->createAttribute("Value", asMemorySize(getSizeOf()));
+  node->createAttribute("MaxValue", asMemorySize(getMaxSizeOf()));
+  node = result->createChild("Statistics");
+  node->createAttribute("Hit", a_hit);
+  node->createAttribute("Fault", a_fault);
+  node->createAttribute("Ratio", anna::functions::asString("%d%%", ratio));
+  node->createAttribute("DoneReuse", a_doneReuse);
+  return result;
+}
+
+string StorageArea::asMemorySize(const Size size)
+throw() {
+  string result;
+
+  if(size < 1024) {
+    result = functions::asString("%u bytes", size);
+  } else if(size < 1024 * 1024) {
+    static const Size ka = 1024;
+    result += functions::asString("%u Kb", size / ka);
+  } else {
+    static const Size mega = 1024 * 1024;
+    result += functions::asString("%u Mb", size / mega);
+  }
+
+  return result;
+}
+
+StorageArea::Instance* StorageArea::allocate()
+throw() {
+  Instance* result = NULL;
+
+  if((result = reuse()) == NULL) {
+    if((result = a_currentBlock->getInstance()) == NULL) {
+      if(++ a_indexBlock == a_blocks.size())
+        a_blocks.push_back(a_currentBlock = new Block(a_objectAllocator, a_maxSize));
+      else
+        a_currentBlock = a_blocks [a_indexBlock];
+
+      result = a_currentBlock->getInstance();
+    }
+  }
+
+  result->copyCounter = 1;
+//xxx   result->msHoleTime = 0;
+  result->flags = Flag::None;
+//xxx   result->hasHole = false;
+  return result;
+}
+
+//-----------------------------------------------------------------------------------------------------
+// Reusa un objeto que lleva demasiado tiempo sin ser utilizado.
+//
+// (0) Si el tiempo que lleva en la lista de objetos liberados es cero => Podemos usar el re-usado
+// rapido; ya ha sido eliminado del directorio, invoca al destroy, etc, etc.
+//     (0.1) Para asegurar que NO desborde los 32 bits.
+// (1) Cuando n -> Nmax => Talpha = now => a poco tiempo que pase sin reusar los registros, estos
+// se comienzan a resuar. Cuando n -> 0 => talpha = 0 => No se reusan registros, sino que se crearan
+// nuevos.
+// (2) Si el primero de los registros disponibles no es suficientemente antiguo => no hay huecos libres
+//-----------------------------------------------------------------------------------------------------
+StorageArea::Instance* StorageArea::reuse()
+throw() {
+  if(a_holes.empty() == true)
+    return NULL;
+
+  Instance* result = NULL;
+  Instance* front = a_holes.front();
+  bool quickReuse;
+
+  if((front->flags & Flag::Ready) == false) {
+    if(a_directory.size() >= a_maxSize) {   // El directorio contiene los que tienen cuenta 0 + los que están activos.
+      result = front;
+      iterator ii = a_directory.find(result->object->getIndex());
+
+      if(ii != a_directory.end())
+        a_directory.erase(ii);
+
+      if((result->flags & Flag::Empty) == 0) {
+        LOGINFORMATION(
+          string msg("Destroy (reuse) | ");
+          msg += asString();
+          msg += " | ";
+          msg += asString(result);
+          Logger::information(msg, ANNA_FILE_LOCATION);
+        );
+        result->object->destroy();
+        result->flags &= Flag::NoEmpty;
+      }
+    }
+
+    quickReuse = false;
+  } else {
+    // Si la entrada se cargó en los huecos como "usado rápido" se toma
+    result = front;
+    quickReuse = true;
+  }
+
+  if(result != NULL) {
+    a_doneReuse ++;
+    result->flags &= Flag::NoReady;
+    result->flags &= Flag::NoHasHole;
+    a_holes.pop_front();
+  }
+
+  LOGDEBUG(
+    string msg("dbos::StorageArea::reuse | ");
+    msg += asString();
+    msg += " | ";
+    msg += StorageArea::asString(result);
+    msg += functions::asText(" | QuickReuse: ", quickReuse);
+    Logger::debug(msg, ANNA_FILE_LOCATION);
+  );
+  return result;
+}
+
+std::string StorageArea::asString(const Instance* instance)
+throw() {
+  std::string result("Instance { ");
+
+  if(instance == NULL)
+    return result += "<null> } ";
+
+  result += "Reference: ";
+  result += functions::asHexString(anna_ptrnumber_cast(instance->object));
+  result += " | Index: ";
+  result += functions::asHexString(instance->object->getIndex());
+  result += functions::asText(" | Stored: ", instance->object->a_isStored);
+  result += " | CopyCounter: ";
+  result += functions::asString((unsigned int) instance->copyCounter);
+  result += " | Flags (";
+  result += functions::asHexString(instance->flags);
+  result += "):";
+
+  if(instance->flags == Flag::None)
+    result += " None";
+  else {
+    if(instance->flags & Flag::Dirty)
+      result += " Dirty";
+
+    if(instance->flags & Flag::Incoherent)
+      result += " Incoherent";
+
+    if(instance->flags & Flag::Empty)
+      result += " Empty";
+
+    if(instance->flags & Flag::HasHole)
+      result += " HasHole";
+
+    if(instance->flags & Flag::InProgress)
+      result += " InProgress";
+
+    result += (instance->flags & Flag::Ready) ? " Ready" : " TimeWait";
+  }
+
+  return result += " }";
+}
+
+const char* StorageArea::AccessMode::asString(const AccessMode::_v v)
+throw() {
+  static const char* text [] = { "ReadOnly", "ReadWrite", "ReadEver" };
+  return text [v];
+}
+
+/****************************************************************************************
+* Bloque de memoria.
+****************************************************************************************/
+StorageArea::Block::Block(ObjectAllocator objectAllocator, const Size maxSize) :
+  a_size(0) {
+  a_maxSize = std::min(8U, std::max(256U, maxSize >> 7));
+  a_instances = new Instance [a_maxSize];
+
+  for(int i = 0; i < a_maxSize; i ++)
+    a_instances [i].object = (*objectAllocator)();
+}
+
+bool StorageArea::Holes::insert(Instance* instance, const StorageArea::Holes::Mode::_v mode)
+throw() {
+  if(instance->copyCounter > 0)
+    return false;
+
+  /* Si la instancia ya ha sido registrada en la lista de huecos, sale sin más */
+  if((instance->flags & Flag::HasHole) == true)
+    return true;
+
+  switch(mode)  {
+  case Mode::ReadyToReuse:
+    instance->holeIterator = a_holes.insert(a_holes.begin(), instance);
+    instance->flags |= Flag::HasHole;
+    instance->flags |= Flag::Ready;
+    a_size ++;
+    break;
+  case Mode::TimeWait:
+    instance->holeIterator = a_holes.insert(a_holes.end(), instance);
+    instance->flags |= Flag::HasHole;
+    instance->flags &= Flag::NoReady;
+    a_size ++;
+    break;
+  }
+
+  LOGLOCAL6(
+    string msg("dbos::StorageArea::Holes::insert | This: ");
+    msg += functions::asHexString(anna_ptrnumber_cast(this));
+    msg += " | ";
+    msg += StorageArea::asString(instance);
+    Logger::write(Logger::Local6, msg, ANNA_FILE_LOCATION);
+  );
+  return true;
+}
+
+void StorageArea::Holes::erase(Instance* instance)
+throw() {
+  //   instance->msHoleTime = 0;
+  instance->flags |= Flag::Ready;
+
+  if(instance->copyCounter != 0)
+    return;
+
+  /* Si la instancia NO ha sido registrada en la lista de huecos, sale sin más */
+  if((instance->flags & Flag::HasHole) == false)
+    return;
+
+  LOGLOCAL6(
+    string msg("dbos::StorageArea::Holes::erase | This: ");
+    msg += functions::asHexString(anna_ptrnumber_cast(this));
+    msg += " | ";
+    msg += StorageArea::asString(instance);
+    Logger::write(Logger::Local6, msg, ANNA_FILE_LOCATION);
+  );
+  a_holes.erase(instance->holeIterator);
+  instance->flags &= Flag::NoHasHole;
+  a_size --;
+}
+
+/****************************************************************************************
+* Iterador
+
+class A {
+public:
+   A () : a (0) { cout << "C0: " << (int) this << endl; }
+   A (const int _a): a (_a) { cout << "C1: " << (int) this << " " << a << endl; }
+   A (const A& other) : a (other.a) { cout << "C2: " << (int) this << " " << a << endl; }
+
+   A& operator = (const A& other) {
+      cout << "CP: " << (int) this << " " << (a = other.a) << endl;
+      return *this;
+   }
+
+   int get () const { return a; }
+private:
+   int a;
+};
+
+A fx () { return A (2000); }
+
+int main ()
+{
+   A aa = 100;
+   A bb (200);
+   A xx = aa;
+   A cc = fx ();
+
+   cout << "CC: " << (int) &cc << " " << cc.get () << endl;
+
+}
+
+La salida del programucho es la siguiente:
+
+C1: -4198808 100
+C1: -4198812 200
+C2: -4198816 100
+C1: -4198820 2000
+CC: -4198820 2000
+
+Lo que quiere decir que la "cc" la crea directamente sobre la pila y la asigna en fx sin aplicar ningn
+otro constructor.
+
+Por eso cuando hac� StorageArea::Iterator ii = xxx->begin (), maxi = xxx->end (); .....
+no estaba pasando por el contructor copia ni por ningn otro constructor.
+
+****************************************************************************************/
+// Dejo todo el ejemplo para que sirva de recordatorio.
+