1 // ANNA - Anna is Not Nothingness Anymore //
3 // (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo //
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 //
9 #ifndef anna_comm_ByRangeDelivery_hpp
10 #define anna_comm_ByRangeDelivery_hpp
12 #include <anna/core/functions.hpp>
13 #include <anna/core/tracing/Logger.hpp>
15 #include <anna/xml/Node.hpp>
16 #include <anna/xml/Attribute.hpp>
18 #include <anna/comm/RoundRobinDelivery.hpp>
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.
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.
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.
40 La clase \em Delivery debe implementar los mismos metodos que un comm::Service.
42 template < typename TKey, typename TDelivery = RoundRobinDelivery, typename TSerializer = anna::functions > class ByRangeDelivery : public comm::Service {
49 Range(const std::string& baseName, const TKey& _bottom, const TKey& _top) :
50 bottom(_bottom), top(_top) {
51 std::string name(baseName);
53 name += TSerializer::asString(_bottom);
55 name += TSerializer::asString(_top);
57 delivery = new TDelivery(name.c_str(), false);
60 std::string asString() const throw() {
61 std::string result("comm::ByRangeDelivery { Bottom: ");
62 result += TSerializer::asString(bottom);
64 result += TSerializer::asString(top);
66 result += delivery->asString();
67 return result += " }";
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);
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;
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.
90 ByRangeDelivery(const char* name, const bool isCritical) :
91 comm::Service(name, isCritical),
98 virtual ~ByRangeDelivery() {
99 for(range_iterator ii = range_begin(), maxii = range_end(); ii != maxii; ii ++)
100 delete range(ii)->delivery;
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
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.
114 range_iterator createRange(const TKey& bottom, const TKey& top)
115 throw(RuntimeException) {
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);
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);
135 if((result = find(top)) != NULL) {
136 std::string msg(asString());
138 msg += TSerializer::asString(top);
139 msg += " | Overlapped with ";
140 msg += result->asString();
141 throw RuntimeException(msg, ANNA_FILE_LOCATION);
144 a_ranges.push_back(result = new Range(getName(), bottom, top));
145 return range_begin() + a_ranges.size() - 1;
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.
153 void attach(range_iterator& ii, Server* server) throw(RuntimeException) {
154 range(ii)->delivery->attach(server);
155 comm::Service::attach(server);
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.
163 void prepare(const TKey& key) throw(RuntimeException) {
164 Range* w = find(key);
166 if(w == NULL && Logger::isActive(Logger::Warning)) {
167 std::string msg(asString());
169 msg += TSerializer::asString(key);
170 msg += " | No range associated";
171 Logger::warning(msg, ANNA_FILE_LOCATION);
175 if(a_currentRange == NULL) {
176 std::string msg(asString());
178 msg += TSerializer::asString(key);
179 msg += " | No delivery service associated has been found";
180 throw RuntimeException(msg, ANNA_FILE_LOCATION);
184 std::string asString() const
186 std::string result = className();
188 result += comm::Delivery::asString();
189 result += " | Ranges: ";
191 for(const_range_iterator ii = range_begin(), maxii = range_end(); ii != maxii; ii ++) {
193 result += range(ii)->asString();
196 return result += "}";
199 xml::Node* asXML(xml::Node* parent) const
201 xml::Node* result = parent->createChild("anna.comm.ByRangeDelivery");
202 xml::Node* node = comm::Service::asXML(result);
204 for(const_range_iterator ii = range_begin(), maxii = range_end(); ii != maxii; ii ++)
205 range(ii)->asXML(node);
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.
216 static const char* className() throw() { return "anna::comm::ByRangeDelivery"; }
219 range_iterator range_begin() throw() { return a_ranges.begin(); }
220 range_iterator range_end() throw() { return a_ranges.end(); }
222 const_range_iterator range_begin() const throw() { return a_ranges.begin(); }
223 const_range_iterator range_end() const throw() { return a_ranges.end(); }
225 static Range* range(range_iterator& ii) throw() { return *ii; }
226 static const Range* range(const_range_iterator& ii) throw() { return *ii; }
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();
235 range_container a_ranges;
236 Range* a_currentRange;
238 Range* find(const TKey& key) throw() {
242 for(range_iterator ii = range_begin(), maxii = range_end(); ii != maxii; ii ++) {
245 if(w->bottom <= key && key <= w->top) {
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);
262 return a_currentRange->delivery->apply();
265 // Considera que el servicio esta NO-disponible cuando TODOS los servicios fallan
266 bool do_fault(const comm::Resource* resource)
271 for(range_iterator ii = range_begin(), maxii = range_end(); ii != maxii; ii ++) {
274 if(range(ii)->delivery->fault(resource) == true)
278 return (n == nfault);
281 // Considera que el servicio esta recuperado cuando alguno de los servicios esta disponible
282 bool do_recover(const comm::Resource* resource)
286 for(range_iterator ii = range_begin(), maxii = range_end(); ii != maxii; ii ++) {
287 if(range(ii)->delivery->recover(resource) == true)