Remove warnings (work package 1)
[anna.git] / include / anna / timex / Context.hpp
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 #ifndef anna_timex_Context_hpp
10 #define anna_timex_Context_hpp
11
12 #include <string>
13 #include <map>
14
15 #include <anna/core/functions.hpp>
16 #include <anna/core/mt/SafeSortedVector.hpp>
17 #include <anna/core/mt/Guard.hpp>
18 #include <anna/core/tracing/Logger.hpp>
19
20 #include <anna/xml/Node.hpp>
21 #include <anna/xml/Attribute.hpp>
22
23 #include <anna/timex/Engine.hpp>
24 #include <anna/timex/TimeEventObserver.hpp>
25 #include <anna/timex/Transaction.hpp>
26
27 #include <anna/core/util/Millisecond.hpp>
28
29 namespace anna {
30
31 namespace timex {
32
33 /**
34  * Automatiza todas las operaciones relacionadas con el control de las transaciones de nuestra aplicación.
35  *
36  * \param Tid Indica el tipo de dato usado para ordenar las transaciones en el contexto. Es distinto del identificador
37  * de transacción. El identificador de transacción facilita su localización en los niveles de plataforma, mientras que
38  * este \em Tid facilita su localización a niveles de aplicación. Habitualmente este \em Tid será una cadena alfanumérica.
39  *
40  * \warning
41  *    \li El contexto asociado a la anna::timex::Transaction no puede ser usado al nivel de aplicación, ya que esta
42  * clase lo usa internamente.
43  *    \li No se puede establecer un observador para estas transaciones ya que de otro modo el contexto no podría
44  * seguir la pista de las operaciones realizadas sobre ella.
45  *
46  * El tipo de dato Tid debe tener los operadores requeridos para actuar como clave de una map &lt;Tid, Transaction&gt;
47 */
48 template <typename Tid> class Context : public timex::TimeEventObserver {
49 public:
50   /**
51    * Crea una transacción con el identificador recibido como parámetro.
52    * En caso de que el identificador ya esté registrado en el contexto obtendremos una excepción.
53    *
54    * \param tid Identificador de aplicación de la transación.
55    * \param classType Tipo de transación que deseamos crear. Este parámetro sólo será útil en caso de que nuestra aplicación
56    * tenga que gestionar varios tipos de transaciones u operaciones.
57    *
58    * Este método terminará llamando al método #createTransaction.
59    *
60    * \return La nueva transación que ya habrá sido activada.
61    */
62   Transaction* open(const Tid& tid, const int classType = 0)
63   throw(RuntimeException) {
64     Transaction* result = NULL;
65     {
66       Guard guard(a_mutex, "timex::Context::open");
67       transaction_iterator ii = a_transactions.find(tid);
68
69       if(ii != a_transactions.end()) {
70         std::string msg(transaction(ii)->asString());
71         msg += " | Already in progress";
72         throw RuntimeException(msg, ANNA_FILE_LOCATION);
73       }
74
75       if((result = createTransaction(classType)) == NULL) {
76         std::string msg("timex::Context::createTransaction returned NULL | ClassType: ");
77         msg += functions::asString(classType);
78         throw RuntimeException(msg, ANNA_FILE_LOCATION);
79       }
80
81       result->setTimeout(a_timeout);
82       result->setObserver(this);
83       result->setControlPoint();
84       result->setId(anna_ptrnumber_cast(result));
85       // Copia en el contexto de la transacción la clave usada.
86       // Se usará para poder buscar por clave a la hora de liberar la transacción.
87       std::pair <transaction_iterator, bool> rr = a_transactions.insert(value_type(tid, result));
88       result->setContext((void*) &rr.first->first);
89     }
90
91     try {
92       a_timeController.activate(result);
93     } catch(RuntimeException&) {
94       releaseTransaction(result);
95       throw;
96     }
97
98     LOGINFORMATION(
99       std::string msg("timex::Context::open | ");
100       msg += result->asString();
101       Logger::information(msg, ANNA_FILE_LOCATION);
102     );
103     return result;
104   }
105
106   /**
107    * Devuelve al instancia de la transación correspondiente al identificador recibido como parámetro.
108    * \param tid Identificador de aplicación de la transación.
109    * \param emode Modo de actuar en caso de que no se encuentre la transacción correspondiente al identificador.
110    * \return La transación correspondiente al identificador recibido. Puede ser NULL.
111    */
112   Transaction* find(const Tid& tid, const Exception::Mode::_v emode = Exception::Mode::Throw)
113   throw(RuntimeException) {
114     Guard guard(a_mutex, "timex::Context::find");
115     transaction_iterator ii = a_transactions.find(tid);
116     Transaction* result = (ii != a_transactions.end()) ? transaction(ii) : NULL;
117
118     if(result == NULL) {
119       if(emode == Exception::Mode::Throw) {
120         std::string msg("Transaction: ");
121         msg += identifierAsString(tid);
122         msg += " | Tid not found";
123         throw RuntimeException(msg, ANNA_FILE_LOCATION);
124       } else if(emode == Exception::Mode::Trace && Logger::isActive(Logger::Warning)) {
125         std::string msg("Transaction: ");
126         msg += identifierAsString(tid);
127         msg += " | Tid not found";
128         Logger::warning(msg, ANNA_FILE_LOCATION);
129       }
130     }
131
132     return result;
133   }
134
135   /**
136    * Obtiene el identificador de aplicación asociado a la transación recibida como parámetro.
137    * \param transaction Puntero a la transación a procesar. Debe haber sido creada con el método #open.
138    * \return El identificador de aplicación indicado en el método #open.
139    */
140   const Tid& getIdentifier(const Transaction* transaction) const
141   throw(RuntimeException) {
142     if(transaction == NULL)
143       throw RuntimeException("timex::Context::getIdentifier | Can not work with NULL transaction", ANNA_FILE_LOCATION);
144
145     const void* context =  transaction->getContext();
146
147     if(context == NULL) {
148       std::string msg("timex::Context::getIdentifier | ");
149       msg += transaction->asString();
150       msg += " | Can not work with NULL context";
151       throw RuntimeException(msg, ANNA_FILE_LOCATION);
152     }
153
154     return contextAsIdentifier(context);
155   }
156
157   /**
158    * Termina la transación antes de que termine su periodo de espera.
159    * \param transaction Transación a terminar. Si fuera NULL este método no tiene ningún efecto.
160    */
161   void close(Transaction* transaction)
162   throw() {
163     if(transaction == NULL)
164       return;
165
166     LOGINFORMATION(
167       std::string msg("timex::Context::close | ");
168       msg += transaction->asString();
169       Logger::information(msg, ANNA_FILE_LOCATION);
170     );
171
172     try {
173       a_timeController.cancel(transaction);
174     } catch(RuntimeException& ex) {
175       ex.trace();
176     }
177   }
178
179   /**
180    * Devuelve la información relevante de esta clase en forma de documento XML.
181    * \param parent Nodo XML del que colgar la información de esta instancia.
182    * \return Un documento XML con la información relevante de esta clase.
183    */
184   virtual xml::Node* asXML(xml::Node* parent) const
185   throw() {
186     xml::Node* result = parent->createChild("timex.Context");
187     result->createAttribute("Size", a_transactions.size());
188     result->createAttribute("AvgSize", (a_counter == 0) ? 0 : a_accSize / a_counter);
189     result->createChild("Timeout")->createAttribute("Millisecond", a_timeout.getValue());
190     result->createChild("AvgResponseTime")->createAttribute("Millisecond", (a_counter == 0) ? 0 : a_accDelay / a_counter);
191     return result;
192   }
193
194   /**
195    * Obtiene la instancia del gestor del contexto asociado a la transación recibida como parámetro.
196    * Puede ser NULL.
197    * \param transaction Puntero a la transación a procesar. Si no ha sido creada con el método #open
198    * el resultado de esta operación no está determinado.
199    * \return la instancia del gestor del contexto asociado a la transación recibida como parámetro.
200    * Puede ser NULL.
201    */
202   static const Context* getInstance(const Transaction* transaction)
203   throw(RuntimeException) {
204     if(transaction == NULL)
205       throw RuntimeException("timex::Context::getInstance | Can not work with NULL transaction", ANNA_FILE_LOCATION);
206
207     return reinterpret_cast <const Context*>(transaction->getObserver());
208   }
209
210 protected:
211   /**
212    * Constructor.
213    *
214    * \param observerName Esta clase hera de #anna::timex::TimeEventObserver por lo que debe tener un nombre lógico
215    * \param timeController Gestor de temporización usado en la aplicación.
216    * \param timeout Duración de las transaciones creadas por el contexto. En principio todas las transaciones tendrán la misma
217    * duración, pero en próximas versiones se podría ampliar la funcionalidad para que cada transación pueda indicar su duración de
218    * forma independiente.
219    */
220   Context(const char* observerName, timex::Engine& timeController, const Millisecond& timeout) :
221     TimeEventObserver(observerName),
222     a_timeController(timeController),
223     a_timeout(timeout) {
224     a_counter = a_accDelay = a_accSize = 0;
225   }
226
227   /**
228    * Método virtual puro que creará las transaciones de este contexto cuando sea necesario.
229    * Lo más adecuado sería implementar este método mediate el uso del patrón #anna::Recycler.
230    * \param classType Tipo de transación que deseamos crear. Este parámetro sólo será útil en caso de que nuestra aplicación
231    * tenga que gestionar varios tipos de transaciones u operaciones.
232    * \return Una nueva transación que puede ser usada por este contexto.
233    */
234   virtual Transaction* createTransaction(const int classType) throw() = 0;
235
236   /**
237    * Método virtual puro que liberá el espacio asociado a la transación recibida como parámetro.
238    * Lo más adecuado sería implementar este método mediate el uso del patrón #anna::Recycler.
239    * \param transaction Transación a liberar.
240    */
241   virtual void releaseTransaction(Transaction* transaction) throw() = 0;
242
243   /**
244    * Método virtual puro que debe devolver una \em std::string correspondiente al valor del identificador
245    * recibido como parámetro.
246    * \param tid Identificador único que la transación.
247    * \return una \em std::string correspondiente al valor del identificador recibido como parámetro.
248    */
249   virtual std::string identifierAsString(const Tid& tid) const throw() = 0;
250
251   /**
252    * Método virtual puro que debe devolver la clave de aplicación asociada a una transación
253    * \param tid Puntero que apunta a la clave de aplicación usada para acceder a una transacción.
254    * \return La clave de aplicación correspondiente al puntero recibido como parámetro.
255    */
256   virtual const Tid& contextAsIdentifier(const void* tid) const throw() = 0;
257
258 private:
259   typedef typename std::map <Tid, Transaction*> transaction_container;
260   typedef typename transaction_container::iterator transaction_iterator;
261   typedef typename transaction_container::value_type value_type;
262
263   Engine& a_timeController;
264   transaction_container a_transactions;
265   Mutex a_mutex;
266   Millisecond a_timeout;
267   unsigned int a_counter;
268   unsigned int a_accDelay;
269   unsigned int a_accSize;
270
271   /* Método re-implementado de timex::TimeEventObserver */
272   void release(timex::TimeEvent* event)
273   throw() {
274     Transaction* tt = static_cast <Transaction*>(event);
275     Guard guard(a_mutex, "timex::Context::release");
276     // Calcula la duración media de las transaciones de este contexto.
277     const Millisecond &delay = tt->getDelay();
278     const int size = a_transactions.size();
279
280     /* Si desborda el acumulador de tiempo ... */
281     if(++ a_counter == 0 || (a_accDelay + delay) < a_accDelay || (a_accSize + size) < a_accSize) {
282       a_counter = 1;
283       a_accDelay = 0;
284       a_accSize = 0;
285     }
286
287     const Tid& tid(contextAsIdentifier(tt->getContext()));
288
289     if(a_transactions.erase(tid) == 0) {
290       std::string msg("Transaction: ");
291       msg += identifierAsString(tid);
292       msg += " | Tid can not be erased from map";
293       Logger::warning(msg, ANNA_FILE_LOCATION);
294     }
295
296     releaseTransaction(tt);
297     a_accDelay += delay;
298     a_accSize += a_transactions.size();
299   }
300
301   static Transaction* transaction(transaction_iterator ii) throw() { return ii->second; }
302 };
303
304 }
305 }
306
307 #endif
308
309