Revert "Remove mysql and oracle resources for anna-ericsson project"
[anna.git] / source / dbms / Database.cpp
diff --git a/source/dbms/Database.cpp b/source/dbms/Database.cpp
new file mode 100644 (file)
index 0000000..987038f
--- /dev/null
@@ -0,0 +1,325 @@
+// 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 <stdlib.h>
+#include <locale.h>
+
+#include <string>
+#include <algorithm>
+
+#include <anna/core/tracing/TraceMethod.hpp>
+
+#include <anna/xml/Node.hpp>
+#include <anna/xml/Attribute.hpp>
+
+#include <anna/comm/INetAddress.hpp>
+
+#include <anna/dbms/Database.hpp>
+#include <anna/dbms/Statement.hpp>
+#include <anna/dbms/FailRecoveryHandler.hpp>
+#include <anna/dbms/internal/sccs.hpp>
+#include <anna/dbms/Float.hpp>
+#include <anna/dbms/StatementTranslator.hpp>
+
+using namespace std;
+using namespace anna;
+using namespace anna::dbms;
+
+Database::Database(const char* className, const char* dbmsName) :
+  Component(className),
+  a_name((dbmsName == NULL) ? "local" : dbmsName),
+  a_type((dbmsName == NULL) ? Type::Local : Type::Remote),
+  a_failRecoveryHandler(NULL),
+  a_statementTranslator(NULL) {
+  dbms::sccs::activate();
+}
+
+Database::~Database() {
+  stop();
+}
+
+void Database::do_initialize()
+throw(RuntimeException) {
+  LOGMETHOD(TraceMethod tm("dbms::Database", "do_initialize", ANNA_FILE_LOCATION));
+  int counter(0);
+  bool error = false;
+
+  for(connection_iterator iic = connection_begin(), maxiic = connection_end(); iic != maxiic; iic ++) {
+    try {
+      connection(iic)->open();
+      counter ++;
+    } catch(Exception& ex) {
+      ex.trace();
+      error = true;
+    }
+  }
+
+  if(counter == 0 && error == true) {
+    string msg(asString());
+    msg += " | No available connections";
+    throw RuntimeException(msg, ANNA_FILE_LOCATION);
+  }
+
+  LOGINFORMATION(
+    Logger::information(asString(), ANNA_FILE_LOCATION);
+  );
+}
+
+void Database::do_stop()
+throw() {
+  LOGMETHOD(TraceMethod tm("dbms::Database", "do_stop", ANNA_FILE_LOCATION));
+
+  try {
+    Connection* _connection;
+
+    for(connection_iterator iic = connection_begin(), maxiic = connection_end(); iic != maxiic; iic ++) {
+      _connection = connection(iic);
+      _connection->close();
+      delete _connection;
+    }
+
+    a_connections.clear();
+  } catch(Exception& ex) {
+    ex.trace();
+    a_connections.clear();
+  }
+}
+
+/**
+ * Para evitar que todos los clones usen la misma conexion a la base de datos, se realiza en
+ * cada uno de ellos una re-conexion, es decir, se cierra la original (abierta por el proceso
+ * padre) y se abre una nueva conexion con los mismos parametros y contra la misma base de datos.
+ */
+void Database::do_cloneChild()
+throw(RuntimeException) {
+  LOGMETHOD(TraceMethod tm("dbms::Database", "do_cloneChild", ANNA_FILE_LOCATION));
+
+  for(connection_iterator ii = connection_begin(), maxii = connection_end(); ii != maxii; ii ++) {
+    dbms::Connection* conn = connection(ii);
+    LOGDEBUG(
+      string msg("dbms::Database::do_cloneChild | ");
+      msg += conn->asString();
+      Logger::debug(msg, ANNA_FILE_LOCATION);
+    );
+    recover(*conn, 0);
+  }
+}
+
+Connection* Database::createConnection(const char* name, const char* user, const char* password)
+throw(RuntimeException, DatabaseException) {
+  Guard guard(this, "dbms::Database (createConnection)");
+
+  if(a_connections.size() >= MaxConnection) {
+    string msg("Database::createConnection | ");
+    msg += asString();
+    msg += functions::asText(" | Cannot create more than %d connections per database", MaxConnection);
+    throw RuntimeException(msg, ANNA_FILE_LOCATION);
+  }
+
+  for(connection_iterator ii = connection_begin(), maxii = connection_end(); ii != maxii; ii ++) {
+    if(connection(ii)->getName() == name) {
+      string msg("Database::createConnection | ");
+      msg += asString();
+      msg += " | Connection: ";
+      msg += name;
+      msg += " | Previously registered";
+      throw RuntimeException(msg, ANNA_FILE_LOCATION);
+    }
+  }
+
+  string strname(name);
+  Connection* result = allocateConnection(strname, user, password);
+
+  if(result == NULL) {
+    string msg(asString());
+    msg += " | ";
+    msg += strname;
+    msg += " | Unable to instance connection";
+    throw RuntimeException(msg, ANNA_FILE_LOCATION);
+  }
+
+  LOGDEBUG(
+    string msg("dbms::Database::createConnection | ");
+    msg += result->asString();
+    Logger::write(Logger::Debug, msg, ANNA_FILE_LOCATION);
+  );
+
+  if(getState() == Component::State::Running) {
+    try {
+      result->open();
+      a_connections.push_back(result);
+    } catch(RuntimeException& ex) {
+      ex.trace();
+      delete result;
+      throw;
+    } catch(DatabaseException& edbms) {
+      edbms.trace();
+      delete result;
+      throw;
+    }
+  } else
+    a_connections.push_back(result);
+
+  return result;
+}
+
+Connection& Database::findConnection(const char* name)
+throw(RuntimeException) {
+  Guard guard(this, "dbms::Database (findConnection)");
+  Connection* result = NULL;
+
+  for(connection_iterator ii = connection_begin(), maxii = connection_end(); ii != maxii; ii ++) {
+    if(anna_strcmp(connection(ii)->getName().c_str(), name) == 0) {
+      result = connection(ii);
+      break;
+    }
+  }
+
+  if(result == NULL) {
+    string msg("Database::findConnection | ");
+    msg += asString();
+    msg += " | Conexion: ";
+    msg += name;
+    msg += " | Unregistered";
+    throw RuntimeException(msg, ANNA_FILE_LOCATION);
+  }
+
+  if(result->isAvailable() == false || result->isEnabled() == false) {
+    string msg("Database::findConnection | ");
+    msg += asString();
+    msg += " | Connection: ";
+    msg += name;
+    msg += " | Unavailable";
+    throw RuntimeException(msg, ANNA_FILE_LOCATION);
+  }
+
+  LOGDEBUG(
+    string msg("Database::findConnection | ");
+    msg += result->asString();
+    Logger::debug(msg, ANNA_FILE_LOCATION);
+  );
+  return *result;
+}
+
+Statement* Database::createStatement(const char* name, const char* expression, const bool isCritical)
+throw(RuntimeException) {
+  if(findStatement(name) != NULL)
+    throw RuntimeException(functions::asString("Sentence: %s | Name already in use", name), ANNA_FILE_LOCATION);
+
+  Guard guard(this, "dbms::Database::createStatement");
+
+  if(a_statementTranslator != NULL)
+    expression = a_statementTranslator->apply(expression);
+
+  Statement* result = allocateStatement(name, expression, isCritical);
+  LOGDEBUG(
+    string msg("dbms::Database::createStatement | ");
+    msg += result->asString();
+
+  if(a_statementTranslator != NULL) {
+  msg += " | Translator: ";
+  msg += a_statementTranslator->getName();
+  }
+  Logger::debug(msg, ANNA_FILE_LOCATION);
+  );
+  a_statements.push_back(result);
+  return result;
+}
+
+Statement* Database::findStatement(const char* name)
+throw() {
+  Guard guard(this, "dbms::Database::findStatement");
+  vector <Statement*>::iterator ii, maxii;
+  Statement* result(NULL);
+
+  for(ii = a_statements.begin(), maxii = a_statements.end(); ii != maxii; ii ++) {
+    if(anna_strcmp((*ii)->getName().c_str(), name) == 0) {
+      result = *ii;
+      break;
+    }
+  }
+
+  return result;
+}
+
+void Database::releaseStatement(Statement* statement)
+throw() {
+  if(statement == NULL) {
+    Logger::write(Logger::Warning, asString(), "Cannot release a NULL SQL sentence", ANNA_FILE_LOCATION);
+    return;
+  }
+
+  LOGDEBUG(
+    string msg("dbms::Database::releaseStatement | ");
+    msg += statement->asString();
+    Logger::debug(msg, ANNA_FILE_LOCATION);
+  );
+  Guard guard(this, "dbms::Database::releaseStatement");
+  vector <Statement*>::iterator end = a_statements.end();
+  vector <Statement*>::iterator ii = find(a_statements.begin(), end, statement);
+
+  if(ii != end) {
+    a_statements.erase(ii);
+    delete statement;
+  }
+}
+
+void Database::recover(Connection& connection, const int tryCounter)
+throw(RuntimeException) {
+  try {
+    connection.close();
+    connection.open();
+  } catch(DatabaseException& edbms) {
+    edbms.trace();
+
+    if(a_failRecoveryHandler != NULL)
+      a_failRecoveryHandler->apply(connection, tryCounter);
+  }
+}
+
+string Database::asString() const
+throw() {
+  string result("dbms::Database { ");
+  result += Component::asString();
+
+  if(a_type ==  Type::Local)
+    result += " | Type: Local";
+  else {
+    result += " | Type: Remote | Name: ";
+    result += a_name;
+  }
+
+  return result += " }";
+}
+
+xml::Node* Database::asXML(xml::Node* parent) const
+throw() {
+  parent = Component::asXML(parent);
+  xml::Node* result = parent->createChild("dbms.Database");
+  xml::Node* node;
+  result->createAttribute("Type", (a_type == Type::Local) ? "Local" : "Remote");
+
+  if(a_type != Type::Local)
+    result->createAttribute("Name", a_name);
+
+  if(a_statementTranslator != NULL)
+    result->createAttribute("Translator", a_statementTranslator->getName());
+
+  node = result-> createChild("Connections");
+
+  for(const_connection_iterator ii = connection_begin(), maxii = connection_end(); ii != maxii; ii ++)
+    connection(ii)->asXML(node);
+
+  node = result-> createChild("Statements");
+
+  for(const_statement_iterator ii = statement_begin(), maxii = statement_end(); ii != maxii; ii ++)
+    statement(ii)->asXML(node);
+
+  return result;
+}
+