Updated license
[anna.git] / include / anna / timex / Context.hpp
1 // ANNA - Anna is Not Nothingness Anymore
2 //
3 // (c) Copyright 2005-2014 Eduardo Ramos Testillano & Francisco Ruiz Rayo
4 //
5 // https://bitbucket.org/testillano/anna
6 //
7 // Redistribution and use in source and binary forms, with or without
8 // modification, are permitted provided that the following conditions
9 // are met:
10 //
11 //     * Redistributions of source code must retain the above copyright
12 // notice, this list of conditions and the following disclaimer.
13 //     * Redistributions in binary form must reproduce the above
14 // copyright notice, this list of conditions and the following disclaimer
15 // in the documentation and/or other materials provided with the
16 // distribution.
17 //     * Neither the name of Google Inc. nor the names of its
18 // contributors may be used to endorse or promote products derived from
19 // this software without specific prior written permission.
20 //
21 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 //
33 // Authors: eduardo.ramos.testillano@gmail.com
34 //          cisco.tierra@gmail.com
35
36
37 #ifndef anna_timex_Context_hpp
38 #define anna_timex_Context_hpp
39
40 #include <string>
41 #include <map>
42
43 #include <anna/core/functions.hpp>
44 #include <anna/core/mt/SafeSortedVector.hpp>
45 #include <anna/core/mt/Guard.hpp>
46 #include <anna/core/tracing/Logger.hpp>
47
48 #include <anna/xml/Node.hpp>
49 #include <anna/xml/Attribute.hpp>
50
51 #include <anna/timex/Engine.hpp>
52 #include <anna/timex/TimeEventObserver.hpp>
53 #include <anna/timex/Transaction.hpp>
54
55 #include <anna/core/util/Millisecond.hpp>
56
57 namespace anna {
58
59 namespace timex {
60
61 /**
62  * Automatiza todas las operaciones relacionadas con el control de las transaciones de nuestra aplicación.
63  *
64  * \param Tid Indica el tipo de dato usado para ordenar las transaciones en el contexto. Es distinto del identificador
65  * de transacción. El identificador de transacción facilita su localización en los niveles de plataforma, mientras que
66  * este \em Tid facilita su localización a niveles de aplicación. Habitualmente este \em Tid será una cadena alfanumérica.
67  *
68  * \warning
69  *    \li El contexto asociado a la anna::timex::Transaction no puede ser usado al nivel de aplicación, ya que esta
70  * clase lo usa internamente.
71  *    \li No se puede establecer un observador para estas transaciones ya que de otro modo el contexto no podría
72  * seguir la pista de las operaciones realizadas sobre ella.
73  *
74  * El tipo de dato Tid debe tener los operadores requeridos para actuar como clave de una map &lt;Tid, Transaction&gt;
75 */
76 template <typename Tid> class Context : public timex::TimeEventObserver {
77 public:
78   /**
79    * Crea una transacción con el identificador recibido como parámetro.
80    * En caso de que el identificador ya esté registrado en el contexto obtendremos una excepción.
81    *
82    * \param tid Identificador de aplicación de la transación.
83    * \param classType Tipo de transación que deseamos crear. Este parámetro sólo será útil en caso de que nuestra aplicación
84    * tenga que gestionar varios tipos de transaciones u operaciones.
85    *
86    * Este método terminará llamando al método #createTransaction.
87    *
88    * \return La nueva transación que ya habrá sido activada.
89    */
90   Transaction* open(const Tid& tid, const int classType = 0)
91   throw(RuntimeException) {
92     Transaction* result = NULL;
93     {
94       Guard guard(a_mutex, "timex::Context::open");
95       transaction_iterator ii = a_transactions.find(tid);
96
97       if(ii != a_transactions.end()) {
98         std::string msg(transaction(ii)->asString());
99         msg += " | Already in progress";
100         throw RuntimeException(msg, ANNA_FILE_LOCATION);
101       }
102
103       if((result = createTransaction(classType)) == NULL) {
104         std::string msg("timex::Context::createTransaction returned NULL | ClassType: ");
105         msg += functions::asString(classType);
106         throw RuntimeException(msg, ANNA_FILE_LOCATION);
107       }
108
109       result->setTimeout(a_timeout);
110       result->setObserver(this);
111       result->setControlPoint();
112       result->setId(anna_ptrnumber_cast(result));
113       // Copia en el contexto de la transacción la clave usada.
114       // Se usará para poder buscar por clave a la hora de liberar la transacción.
115       std::pair <transaction_iterator, bool> rr = a_transactions.insert(value_type(tid, result));
116       result->setContext((void*) &rr.first->first);
117     }
118
119     try {
120       a_timeController.activate(result);
121     } catch(RuntimeException&) {
122       releaseTransaction(result);
123       throw;
124     }
125
126     LOGINFORMATION(
127       std::string msg("timex::Context::open | ");
128       msg += result->asString();
129       Logger::information(msg, ANNA_FILE_LOCATION);
130     );
131     return result;
132   }
133
134   /**
135    * Devuelve al instancia de la transación correspondiente al identificador recibido como parámetro.
136    * \param tid Identificador de aplicación de la transación.
137    * \param emode Modo de actuar en caso de que no se encuentre la transacción correspondiente al identificador.
138    * \return La transación correspondiente al identificador recibido. Puede ser NULL.
139    */
140   Transaction* find(const Tid& tid, const Exception::Mode::_v emode = Exception::Mode::Throw)
141   throw(RuntimeException) {
142     Guard guard(a_mutex, "timex::Context::find");
143     transaction_iterator ii = a_transactions.find(tid);
144     Transaction* result = (ii != a_transactions.end()) ? transaction(ii) : NULL;
145
146     if(result == NULL) {
147       if(Exception::Mode::Throw) {
148         std::string msg("Transaction: ");
149         msg += identifierAsString(tid);
150         msg += " | Tid not found";
151         throw RuntimeException(msg, ANNA_FILE_LOCATION);
152       } else if(Exception::Mode::Trace && Logger::isActive(Logger::Warning)) {
153         std::string msg("Transaction: ");
154         msg += identifierAsString(tid);
155         msg += " | Tid not found";
156         Logger::warning(msg, ANNA_FILE_LOCATION);
157       }
158     }
159
160     return result;
161   }
162
163   /**
164    * Obtiene el identificador de aplicación asociado a la transación recibida como parámetro.
165    * \param transaction Puntero a la transación a procesar. Debe haber sido creada con el método #open.
166    * \return El identificador de aplicación indicado en el método #open.
167    */
168   const Tid& getIdentifier(const Transaction* transaction) const
169   throw(RuntimeException) {
170     if(transaction == NULL)
171       throw RuntimeException("timex::Context::getIdentifier | Can not work with NULL transaction", ANNA_FILE_LOCATION);
172
173     const void* context =  transaction->getContext();
174
175     if(context == NULL) {
176       std::string msg("timex::Context::getIdentifier | ");
177       msg += transaction->asString();
178       msg += " | Can not work with NULL context";
179       throw RuntimeException(msg, ANNA_FILE_LOCATION);
180     }
181
182     return contextAsIdentifier(context);
183   }
184
185   /**
186    * Termina la transación antes de que termine su periodo de espera.
187    * \param transaction Transación a terminar. Si fuera NULL este método no tiene ningún efecto.
188    */
189   void close(Transaction* transaction)
190   throw() {
191     if(transaction == NULL)
192       return;
193
194     LOGINFORMATION(
195       std::string msg("timex::Context::close | ");
196       msg += transaction->asString();
197       Logger::information(msg, ANNA_FILE_LOCATION);
198     );
199
200     try {
201       a_timeController.cancel(transaction);
202     } catch(RuntimeException& ex) {
203       ex.trace();
204     }
205   }
206
207   /**
208    * Devuelve la información relevante de esta clase en forma de documento XML.
209    * \param parent Nodo XML del que colgar la información de esta instancia.
210    * \return Un documento XML con la información relevante de esta clase.
211    */
212   virtual xml::Node* asXML(xml::Node* parent) const
213   throw() {
214     xml::Node* result = parent->createChild("timex.Context");
215     result->createAttribute("Size", a_transactions.size());
216     result->createAttribute("AvgSize", (a_counter == 0) ? 0 : a_accSize / a_counter);
217     result->createChild("Timeout")->createAttribute("Millisecond", a_timeout.getValue());
218     result->createChild("AvgResponseTime")->createAttribute("Millisecond", (a_counter == 0) ? 0 : a_accDelay / a_counter);
219     return result;
220   }
221
222   /**
223    * Obtiene la instancia del gestor del contexto asociado a la transación recibida como parámetro.
224    * Puede ser NULL.
225    * \param transaction Puntero a la transación a procesar. Si no ha sido creada con el método #open
226    * el resultado de esta operación no está determinado.
227    * \return la instancia del gestor del contexto asociado a la transación recibida como parámetro.
228    * Puede ser NULL.
229    */
230   static const Context* getInstance(const Transaction* transaction)
231   throw(RuntimeException) {
232     if(transaction == NULL)
233       throw RuntimeException("timex::Context::getInstance | Can not work with NULL transaction", ANNA_FILE_LOCATION);
234
235     return reinterpret_cast <const Context*>(transaction->getObserver());
236   }
237
238 protected:
239   /**
240    * Constructor.
241    *
242    * \param observerName Esta clase hera de #anna::timex::TimeEventObserver por lo que debe tener un nombre lógico
243    * \param timeController Gestor de temporización usado en la aplicación.
244    * \param timeout Duración de las transaciones creadas por el contexto. En principio todas las transaciones tendrán la misma
245    * duración, pero en próximas versiones se podría ampliar la funcionalidad para que cada transación pueda indicar su duración de
246    * forma independiente.
247    */
248   Context(const char* observerName, timex::Engine& timeController, const Millisecond& timeout) :
249     TimeEventObserver(observerName),
250     a_timeController(timeController),
251     a_timeout(timeout) {
252     a_counter = a_accDelay = a_accSize = 0;
253   }
254
255   /**
256    * Método virtual puro que creará las transaciones de este contexto cuando sea necesario.
257    * Lo más adecuado sería implementar este método mediate el uso del patrón #anna::Recycler.
258    * \param classType Tipo de transación que deseamos crear. Este parámetro sólo será útil en caso de que nuestra aplicación
259    * tenga que gestionar varios tipos de transaciones u operaciones.
260    * \return Una nueva transación que puede ser usada por este contexto.
261    */
262   virtual Transaction* createTransaction(const int classType) throw() = 0;
263
264   /**
265    * Método virtual puro que liberá el espacio asociado a la transación recibida como parámetro.
266    * Lo más adecuado sería implementar este método mediate el uso del patrón #anna::Recycler.
267    * \param transaction Transación a liberar.
268    */
269   virtual void releaseTransaction(Transaction* transaction) throw() = 0;
270
271   /**
272    * Método virtual puro que debe devolver una \em std::string correspondiente al valor del identificador
273    * recibido como parámetro.
274    * \param tid Identificador único que la transación.
275    * \return una \em std::string correspondiente al valor del identificador recibido como parámetro.
276    */
277   virtual std::string identifierAsString(const Tid& tid) const throw() = 0;
278
279   /**
280    * Método virtual puro que debe devolver la clave de aplicación asociada a una transación
281    * \param tid Puntero que apunta a la clave de aplicación usada para acceder a una transacción.
282    * \return La clave de aplicación correspondiente al puntero recibido como parámetro.
283    */
284   virtual const Tid& contextAsIdentifier(const void* tid) const throw() = 0;
285
286 private:
287   typedef typename std::map <Tid, Transaction*> transaction_container;
288   typedef typename transaction_container::iterator transaction_iterator;
289   typedef typename transaction_container::value_type value_type;
290
291   Engine& a_timeController;
292   transaction_container a_transactions;
293   Mutex a_mutex;
294   Millisecond a_timeout;
295   unsigned int a_counter;
296   unsigned int a_accDelay;
297   unsigned int a_accSize;
298
299   /* Método re-implementado de timex::TimeEventObserver */
300   void release(timex::TimeEvent* event)
301   throw() {
302     Transaction* tt = static_cast <Transaction*>(event);
303     Guard guard(a_mutex, "timex::Context::release");
304     // Calcula la duración media de las transaciones de este contexto.
305     const Millisecond &delay = tt->getDelay();
306     const int size = a_transactions.size();
307
308     /* Si desborda el acumulador de tiempo ... */
309     if(++ a_counter == 0 || (a_accDelay + delay) < a_accDelay || (a_accSize + size) < a_accSize) {
310       a_counter = 1;
311       a_accDelay = 0;
312       a_accSize = 0;
313     }
314
315     const Tid& tid(contextAsIdentifier(tt->getContext()));
316
317     if(a_transactions.erase(tid) == 0) {
318       std::string msg("Transaction: ");
319       msg += identifierAsString(tid);
320       msg += " | Tid can not be erased from map";
321       Logger::warning(msg, ANNA_FILE_LOCATION);
322     }
323
324     releaseTransaction(tt);
325     a_accDelay += delay;
326     a_accSize += a_transactions.size();
327   }
328
329   static Transaction* transaction(transaction_iterator ii) throw() { return ii->second; }
330 };
331
332 }
333 }
334
335 #endif
336
337