bug in RC
[anna.git] / include / anna / comm / ByRangeDelivery.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_comm_ByRangeDelivery_hpp
10 #define anna_comm_ByRangeDelivery_hpp
11
12 #include <anna/core/functions.hpp>
13 #include <anna/core/tracing/Logger.hpp>
14
15 #include <anna/xml/Node.hpp>
16 #include <anna/xml/Attribute.hpp>
17
18 #include <anna/comm/RoundRobinDelivery.hpp>
19
20 namespace anna {
21
22 namespace comm {
23 class Resource;
24 }
25
26 namespace comm {
27
28 /**
29    Servicio de reparto de carga permite selecionar el servidor al que enviar la peticion en base al valor de una
30    clave previamente indicada.
31
32    A diferencia de los comm::Service habituales la asociacion entre el servicio y el proceso no debe hacerse
33    mediante comm::Service::attach, sino mediante comm::ByRangeDelivery::attach.
34
35    \param TKey Tipo de clave usada para indexar los servicios de reparto.
36    \param TDelivery: Tipo de reparto usado en cada rango.
37    \param TSerializer: Clase que es capaz de convertir la \em TKey en una cadena. Debe implementar un metodo
38    \em asString que recibe una instancia de TKey y devuelva una cadena.
39
40    La clase \em Delivery debe implementar los mismos metodos que un comm::Service.
41 */
42 template < typename TKey, typename TDelivery = RoundRobinDelivery, typename TSerializer = anna::functions > class ByRangeDelivery : public comm::Service {
43 protected:
44   struct Range {
45     const TKey bottom;
46     const TKey top;
47     TDelivery* delivery;
48
49     Range(const std::string& baseName, const TKey& _bottom, const TKey& _top) :
50       bottom(_bottom), top(_top) {
51       std::string name(baseName);
52       name += '[';
53       name += TSerializer::asString(_bottom);
54       name += ',';
55       name += TSerializer::asString(_top);
56       name += ']';
57       delivery = new TDelivery(name.c_str(), false);
58     }
59
60     std::string asString() const throw() {
61       std::string result("comm::ByRangeDelivery { Bottom: ");
62       result += TSerializer::asString(bottom);
63       result += " | Top: ";
64       result += TSerializer::asString(top);
65       result += " | ";
66       result += delivery->asString();
67       return result += " }";
68     }
69
70     xml::Node* asXML(xml::Node* parent) const throw() {
71       xml::Node* result = parent->createChild("comm.Range");
72       result->createAttribute("Bottom", TSerializer::asString(bottom));
73       result->createAttribute("Top", TSerializer::asString(top));
74       delivery->asXML(result);
75       return result;
76     }
77   };
78
79 public:
80   typedef std::vector<Range*> range_container;
81   typedef typename range_container::iterator range_iterator;
82   typedef typename range_container::const_iterator const_range_iterator;
83
84   /**
85      Constructor.
86      \param name Nombre logico del servicio de reparto de carga.
87      \param isCritical Indica las caracteristicas del reparto de carga. Debe valor \em true si el
88      reparto de carga a crear es critico o \em false en otro caso.
89   */
90   ByRangeDelivery(const char* name, const bool isCritical) :
91     comm::Service(name, isCritical),
92     a_currentRange(NULL)
93   {;}
94
95   /**
96      Destructor.
97   */
98   virtual ~ByRangeDelivery() {
99     for(range_iterator ii = range_begin(), maxii = range_end(); ii != maxii; ii ++)
100       delete range(ii)->delivery;
101
102     a_ranges.clear();
103   }
104
105   /**
106      Instancia un rango de reparto con los parametros definidos como parametros. Una vez que
107      obtengamos el comm::Delivery asociado al rango creado debemos asociar tantos comm::Resource
108      como sea necesario.
109
110      \param bottom Valor minimo del rango.
111      \param top Valor maximo del rango.
112      \return La instancia del servicio de reparto asociado a este rango.
113   */
114   range_iterator createRange(const TKey& bottom, const TKey& top)
115   throw(RuntimeException) {
116     Range* result;
117
118     if(bottom > top) {
119       std::string msg(asString());
120       msg += " | Bottom: ";
121       msg += TSerializer::asString(bottom);
122       msg += " | Invalid range (bottom > top)";
123       throw RuntimeException(msg, ANNA_FILE_LOCATION);
124     }
125
126     if((result = find(bottom)) != NULL) {
127       std::string msg(asString());
128       msg += " | Bottom: ";
129       msg += TSerializer::asString(bottom);
130       msg += " | Overlapped with ";
131       msg += result->asString();
132       throw RuntimeException(msg, ANNA_FILE_LOCATION);
133     }
134
135     if((result = find(top)) != NULL) {
136       std::string msg(asString());
137       msg += " | top: ";
138       msg += TSerializer::asString(top);
139       msg += " | Overlapped with ";
140       msg += result->asString();
141       throw RuntimeException(msg, ANNA_FILE_LOCATION);
142     }
143
144     a_ranges.push_back(result = new Range(getName(), bottom, top));
145     return range_begin() + a_ranges.size() - 1;
146   }
147
148   /**
149      Asocia el comm::Server recibido como parametro al rango representado por el iterador recibido.
150      \param ii Iterador de rango al que asociar el servicio. Debe ser resultado de invocar a #createRange.
151      \param server Instancia del comm::Server a incorporar dentro del rango.
152   */
153   void attach(range_iterator& ii, Server* server) throw(RuntimeException) {
154     range(ii)->delivery->attach(server);
155     comm::Service::attach(server);
156   }
157
158   /**
159      Establece la clave que se usara para decidir que pareja de maquinas usar para enviar el mensaje.
160      \param key Clave usada para calcular el reparto.
161      \warning Este metodo debe invocarse siempre antes de usar este servicio de reparto.
162   */
163   void prepare(const TKey& key) throw(RuntimeException) {
164     Range* w = find(key);
165
166     if(w == NULL && Logger::isActive(Logger::Warning)) {
167       std::string msg(asString());
168       msg += " | TKey: ";
169       msg += TSerializer::asString(key);
170       msg += " | No range associated";
171       Logger::warning(msg, ANNA_FILE_LOCATION);
172     } else
173       a_currentRange = w;
174
175     if(a_currentRange == NULL) {
176       std::string msg(asString());
177       msg += " | TKey: ";
178       msg += TSerializer::asString(key);
179       msg += " | No delivery service associated has been found";
180       throw RuntimeException(msg, ANNA_FILE_LOCATION);
181     }
182   }
183
184   std::string asString() const
185   throw() {
186     std::string result = className();
187     result += " { ";
188     result += comm::Delivery::asString();
189     result += " | Ranges: ";
190
191     for(const_range_iterator ii = range_begin(), maxii = range_end(); ii != maxii; ii ++)  {
192       result += "\n\t";
193       result += range(ii)->asString();
194     }
195
196     return result += "}";
197   }
198
199   xml::Node* asXML(xml::Node* parent) const
200   throw() {
201     xml::Node* result = parent->createChild("anna.comm.ByRangeDelivery");
202     xml::Node* node = comm::Service::asXML(result);
203
204     for(const_range_iterator ii = range_begin(), maxii = range_end(); ii != maxii; ii ++)
205       range(ii)->asXML(node);
206
207     return result;
208   }
209
210   /**
211      Metodo que devuelve el nombre completo de este selector de recursos.
212      Para evitar ambigüedades este nombre incluye la lista completa de \em namespaces
213      a los que pertenece la clase.
214      \return Una cadena con el nombre de este selector.
215   */
216   static const char* className() throw() { return "anna::comm::ByRangeDelivery"; }
217
218 protected:
219   range_iterator range_begin() throw() { return a_ranges.begin(); }
220   range_iterator range_end() throw() { return a_ranges.end(); }
221
222   const_range_iterator range_begin() const throw() { return a_ranges.begin(); }
223   const_range_iterator range_end() const throw() { return a_ranges.end(); }
224
225   static Range* range(range_iterator& ii) throw() { return *ii; }
226   static const Range* range(const_range_iterator& ii) throw() { return *ii; }
227
228   virtual void do_initialize()
229   throw(RuntimeException) {
230     for(range_iterator ii = range_begin(), maxii = range_end(); ii != maxii; ii ++)
231       range(ii)->delivery->initialize();
232   }
233
234 private:
235   range_container a_ranges;
236   Range* a_currentRange;
237
238   Range* find(const TKey& key) throw() {
239     Range* result(NULL);
240     Range* w;
241
242     for(range_iterator ii = range_begin(), maxii = range_end(); ii != maxii; ii ++) {
243       w = range(ii);
244
245       if(w->bottom <= key && key <= w->top) {
246         result = w;
247         break;
248       }
249     }
250
251     return result;
252   }
253
254   comm::Resource* do_apply()
255   throw(RuntimeException) {
256     if(a_currentRange == NULL) {
257       std::string msg(asString());
258       msg += " | Wrong call to anna::comm::ByRangeDelivery::prepare";
259       throw RuntimeException(msg, ANNA_FILE_LOCATION);
260     }
261
262     return a_currentRange->delivery->apply();
263   }
264
265   // Considera que el servicio esta NO-disponible cuando TODOS los servicios fallan
266   bool do_fault(const comm::Resource* resource)
267   throw() {
268     int n, nfault;
269     n = nfault = 0;
270
271     for(range_iterator ii = range_begin(), maxii = range_end(); ii != maxii; ii ++) {
272       n ++;
273
274       if(range(ii)->delivery->fault(resource) == true)
275         nfault ++;
276     }
277
278     return (n == nfault);
279   }
280
281   // Considera que el servicio esta recuperado cuando alguno de los servicios esta disponible
282   bool do_recover(const comm::Resource* resource)
283   throw() {
284     bool result(false);
285
286     for(range_iterator ii = range_begin(), maxii = range_end(); ii != maxii; ii ++) {
287       if(range(ii)->delivery->recover(resource) == true)
288         result = true;
289     }
290
291     return result;
292   }
293 };
294
295 }
296 }
297
298 #endif
299