--- /dev/null
+// 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;
+}
+