Fix symlinks
[anna.git] / source / dbms / Database.cpp
1 // ANNA - Anna is Not Nothingness Anymore                                                         //
2 //                                                                                                //
3 // (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo                         //
4 //                                                                                                //
5 // See project site at http://redmine.teslayout.com/projects/anna-suite                           //
6 // See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE //
7
8
9 #include <stdlib.h>
10 #include <locale.h>
11
12 #include <string>
13 #include <algorithm>
14
15 #include <anna/core/tracing/TraceMethod.hpp>
16
17 #include <anna/xml/Node.hpp>
18 #include <anna/xml/Attribute.hpp>
19
20 #include <anna/comm/INetAddress.hpp>
21
22 #include <anna/dbms/Database.hpp>
23 #include <anna/dbms/Statement.hpp>
24 #include <anna/dbms/FailRecoveryHandler.hpp>
25 #include <anna/dbms/internal/sccs.hpp>
26 #include <anna/dbms/Float.hpp>
27 #include <anna/dbms/StatementTranslator.hpp>
28
29 using namespace std;
30 using namespace anna;
31 using namespace anna::dbms;
32
33 Database::Database(const char* className, const char* dbmsName) :
34   Component(className),
35   a_name((dbmsName == NULL) ? "local" : dbmsName),
36   a_type((dbmsName == NULL) ? Type::Local : Type::Remote),
37   a_failRecoveryHandler(NULL),
38   a_statementTranslator(NULL) {
39   dbms::sccs::activate();
40 }
41
42 Database::~Database() {
43   stop();
44 }
45
46 void Database::do_initialize()
47 throw(RuntimeException) {
48   LOGMETHOD(TraceMethod tm("dbms::Database", "do_initialize", ANNA_FILE_LOCATION));
49   int counter(0);
50   bool error = false;
51
52   for(connection_iterator iic = connection_begin(), maxiic = connection_end(); iic != maxiic; iic ++) {
53     try {
54       connection(iic)->open();
55       counter ++;
56     } catch(Exception& ex) {
57       ex.trace();
58       error = true;
59     }
60   }
61
62   if(counter == 0 && error == true) {
63     string msg(asString());
64     msg += " | No available connections";
65     throw RuntimeException(msg, ANNA_FILE_LOCATION);
66   }
67
68   LOGINFORMATION(
69     Logger::information(asString(), ANNA_FILE_LOCATION);
70   );
71 }
72
73 void Database::do_stop()
74 throw() {
75   LOGMETHOD(TraceMethod tm("dbms::Database", "do_stop", ANNA_FILE_LOCATION));
76
77   try {
78     Connection* _connection;
79
80     for(connection_iterator iic = connection_begin(), maxiic = connection_end(); iic != maxiic; iic ++) {
81       _connection = connection(iic);
82       _connection->close();
83       delete _connection;
84     }
85
86     a_connections.clear();
87   } catch(Exception& ex) {
88     ex.trace();
89     a_connections.clear();
90   }
91 }
92
93 /**
94  * Para evitar que todos los clones usen la misma conexion a la base de datos, se realiza en
95  * cada uno de ellos una re-conexion, es decir, se cierra la original (abierta por el proceso
96  * padre) y se abre una nueva conexion con los mismos parametros y contra la misma base de datos.
97  */
98 void Database::do_cloneChild()
99 throw(RuntimeException) {
100   LOGMETHOD(TraceMethod tm("dbms::Database", "do_cloneChild", ANNA_FILE_LOCATION));
101
102   for(connection_iterator ii = connection_begin(), maxii = connection_end(); ii != maxii; ii ++) {
103     dbms::Connection* conn = connection(ii);
104     LOGDEBUG(
105       string msg("dbms::Database::do_cloneChild | ");
106       msg += conn->asString();
107       Logger::debug(msg, ANNA_FILE_LOCATION);
108     );
109     recover(*conn, 0);
110   }
111 }
112
113 Connection* Database::createConnection(const char* name, const char* user, const char* password)
114 throw(RuntimeException, DatabaseException) {
115   Guard guard(this, "dbms::Database (createConnection)");
116
117   if(a_connections.size() >= MaxConnection) {
118     string msg("Database::createConnection | ");
119     msg += asString();
120     msg += functions::asText(" | Cannot create more than %d connections per database", MaxConnection);
121     throw RuntimeException(msg, ANNA_FILE_LOCATION);
122   }
123
124   for(connection_iterator ii = connection_begin(), maxii = connection_end(); ii != maxii; ii ++) {
125     if(connection(ii)->getName() == name) {
126       string msg("Database::createConnection | ");
127       msg += asString();
128       msg += " | Connection: ";
129       msg += name;
130       msg += " | Previously registered";
131       throw RuntimeException(msg, ANNA_FILE_LOCATION);
132     }
133   }
134
135   string strname(name);
136   Connection* result = allocateConnection(strname, user, password);
137
138   if(result == NULL) {
139     string msg(asString());
140     msg += " | ";
141     msg += strname;
142     msg += " | Unable to instance connection";
143     throw RuntimeException(msg, ANNA_FILE_LOCATION);
144   }
145
146   LOGDEBUG(
147     string msg("dbms::Database::createConnection | ");
148     msg += result->asString();
149     Logger::write(Logger::Debug, msg, ANNA_FILE_LOCATION);
150   );
151
152   if(getState() == Component::State::Running) {
153     try {
154       result->open();
155       a_connections.push_back(result);
156     } catch(RuntimeException& ex) {
157       ex.trace();
158       delete result;
159       throw;
160     } catch(DatabaseException& edbms) {
161       edbms.trace();
162       delete result;
163       throw;
164     }
165   } else
166     a_connections.push_back(result);
167
168   return result;
169 }
170
171 Connection& Database::findConnection(const char* name)
172 throw(RuntimeException) {
173   Guard guard(this, "dbms::Database (findConnection)");
174   Connection* result = NULL;
175
176   for(connection_iterator ii = connection_begin(), maxii = connection_end(); ii != maxii; ii ++) {
177     if(anna_strcmp(connection(ii)->getName().c_str(), name) == 0) {
178       result = connection(ii);
179       break;
180     }
181   }
182
183   if(result == NULL) {
184     string msg("Database::findConnection | ");
185     msg += asString();
186     msg += " | Conexion: ";
187     msg += name;
188     msg += " | Unregistered";
189     throw RuntimeException(msg, ANNA_FILE_LOCATION);
190   }
191
192   if(result->isAvailable() == false || result->isEnabled() == false) {
193     string msg("Database::findConnection | ");
194     msg += asString();
195     msg += " | Connection: ";
196     msg += name;
197     msg += " | Unavailable";
198     throw RuntimeException(msg, ANNA_FILE_LOCATION);
199   }
200
201   LOGDEBUG(
202     string msg("Database::findConnection | ");
203     msg += result->asString();
204     Logger::debug(msg, ANNA_FILE_LOCATION);
205   );
206   return *result;
207 }
208
209 Statement* Database::createStatement(const char* name, const char* expression, const bool isCritical)
210 throw(RuntimeException) {
211   if(findStatement(name) != NULL)
212     throw RuntimeException(functions::asString("Sentence: %s | Name already in use", name), ANNA_FILE_LOCATION);
213
214   Guard guard(this, "dbms::Database::createStatement");
215
216   if(a_statementTranslator != NULL)
217     expression = a_statementTranslator->apply(expression);
218
219   Statement* result = allocateStatement(name, expression, isCritical);
220   LOGDEBUG(
221     string msg("dbms::Database::createStatement | ");
222     msg += result->asString();
223
224   if(a_statementTranslator != NULL) {
225   msg += " | Translator: ";
226   msg += a_statementTranslator->getName();
227   }
228   Logger::debug(msg, ANNA_FILE_LOCATION);
229   );
230   a_statements.push_back(result);
231   return result;
232 }
233
234 Statement* Database::findStatement(const char* name)
235 throw() {
236   Guard guard(this, "dbms::Database::findStatement");
237   vector <Statement*>::iterator ii, maxii;
238   Statement* result(NULL);
239
240   for(ii = a_statements.begin(), maxii = a_statements.end(); ii != maxii; ii ++) {
241     if(anna_strcmp((*ii)->getName().c_str(), name) == 0) {
242       result = *ii;
243       break;
244     }
245   }
246
247   return result;
248 }
249
250 void Database::releaseStatement(Statement* statement)
251 throw() {
252   if(statement == NULL) {
253     Logger::write(Logger::Warning, asString(), "Cannot release a NULL SQL sentence", ANNA_FILE_LOCATION);
254     return;
255   }
256
257   LOGDEBUG(
258     string msg("dbms::Database::releaseStatement | ");
259     msg += statement->asString();
260     Logger::debug(msg, ANNA_FILE_LOCATION);
261   );
262   Guard guard(this, "dbms::Database::releaseStatement");
263   vector <Statement*>::iterator end = a_statements.end();
264   vector <Statement*>::iterator ii = find(a_statements.begin(), end, statement);
265
266   if(ii != end) {
267     a_statements.erase(ii);
268     delete statement;
269   }
270 }
271
272 void Database::recover(Connection& connection, const int tryCounter)
273 throw(RuntimeException) {
274   try {
275     connection.close();
276     connection.open();
277   } catch(DatabaseException& edbms) {
278     edbms.trace();
279
280     if(a_failRecoveryHandler != NULL)
281       a_failRecoveryHandler->apply(connection, tryCounter);
282   }
283 }
284
285 string Database::asString() const
286 throw() {
287   string result("dbms::Database { ");
288   result += Component::asString();
289
290   if(a_type ==  Type::Local)
291     result += " | Type: Local";
292   else {
293     result += " | Type: Remote | Name: ";
294     result += a_name;
295   }
296
297   return result += " }";
298 }
299
300 xml::Node* Database::asXML(xml::Node* parent) const
301 throw() {
302   parent = Component::asXML(parent);
303   xml::Node* result = parent->createChild("dbms.Database");
304   xml::Node* node;
305   result->createAttribute("Type", (a_type == Type::Local) ? "Local" : "Remote");
306
307   if(a_type != Type::Local)
308     result->createAttribute("Name", a_name);
309
310   if(a_statementTranslator != NULL)
311     result->createAttribute("Translator", a_statementTranslator->getName());
312
313   node = result-> createChild("Connections");
314
315   for(const_connection_iterator ii = connection_begin(), maxii = connection_end(); ii != maxii; ii ++)
316     connection(ii)->asXML(node);
317
318   node = result-> createChild("Statements");
319
320   for(const_statement_iterator ii = statement_begin(), maxii = statement_end(); ii != maxii; ii ++)
321     statement(ii)->asXML(node);
322
323   return result;
324 }
325