1 // ANNA - Anna is Not Nothingness Anymore
3 // (c) Copyright 2005-2014 Eduardo Ramos Testillano & Francisco Ruiz Rayo
5 // http://redmine.teslayout.com/projects/anna-suite
7 // Redistribution and use in source and binary forms, with or without
8 // modification, are permitted provided that the following conditions
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
17 // * Neither the name of the copyright holder 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.
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.
33 // Authors: eduardo.ramos.testillano@gmail.com
34 // cisco.tierra@gmail.com
37 #ifndef anna_comm_ByRangeDelivery_hpp
38 #define anna_comm_ByRangeDelivery_hpp
40 #include <anna/core/functions.hpp>
41 #include <anna/core/tracing/Logger.hpp>
43 #include <anna/xml/Node.hpp>
44 #include <anna/xml/Attribute.hpp>
46 #include <anna/comm/RoundRobinDelivery.hpp>
57 Servicio de reparto de carga permite selecionar el servidor al que enviar la peticion en base al valor de una
58 clave previamente indicada.
60 A diferencia de los comm::Service habituales la asociacion entre el servicio y el proceso no debe hacerse
61 mediante comm::Service::attach, sino mediante comm::ByRangeDelivery::attach.
63 \param TKey Tipo de clave usada para indexar los servicios de reparto.
64 \param TDelivery: Tipo de reparto usado en cada rango.
65 \param TSerializer: Clase que es capaz de convertir la \em TKey en una cadena. Debe implementar un metodo
66 \em asString que recibe una instancia de TKey y devuelva una cadena.
68 La clase \em Delivery debe implementar los mismos metodos que un comm::Service.
70 template < typename TKey, typename TDelivery = RoundRobinDelivery, typename TSerializer = anna::functions > class ByRangeDelivery : public comm::Service {
77 Range(const std::string& baseName, const TKey& _bottom, const TKey& _top) :
78 bottom(_bottom), top(_top) {
79 std::string name(baseName);
81 name += TSerializer::asString(_bottom);
83 name += TSerializer::asString(_top);
85 delivery = new TDelivery(name.c_str(), false);
88 std::string asString() const throw() {
89 std::string result("comm::ByRangeDelivery { Bottom: ");
90 result += TSerializer::asString(bottom);
92 result += TSerializer::asString(top);
94 result += delivery->asString();
95 return result += " }";
98 xml::Node* asXML(xml::Node* parent) const throw() {
99 xml::Node* result = parent->createChild("comm.Range");
100 result->createAttribute("Bottom", TSerializer::asString(bottom));
101 result->createAttribute("Top", TSerializer::asString(top));
102 delivery->asXML(result);
108 typedef std::vector<Range*> range_container;
109 typedef typename range_container::iterator range_iterator;
110 typedef typename range_container::const_iterator const_range_iterator;
114 \param name Nombre logico del servicio de reparto de carga.
115 \param isCritical Indica las caracteristicas del reparto de carga. Debe valor \em true si el
116 reparto de carga a crear es critico o \em false en otro caso.
118 ByRangeDelivery(const char* name, const bool isCritical) :
119 comm::Service(name, isCritical),
126 virtual ~ByRangeDelivery() {
127 for(range_iterator ii = range_begin(), maxii = range_end(); ii != maxii; ii ++)
128 delete range(ii)->delivery;
134 Instancia un rango de reparto con los parametros definidos como parametros. Una vez que
135 obtengamos el comm::Delivery asociado al rango creado debemos asociar tantos comm::Resource
138 \param bottom Valor minimo del rango.
139 \param top Valor maximo del rango.
140 \return La instancia del servicio de reparto asociado a este rango.
142 range_iterator createRange(const TKey& bottom, const TKey& top)
143 throw(RuntimeException) {
147 std::string msg(asString());
148 msg += " | Bottom: ";
149 msg += TSerializer::asString(bottom);
150 msg += " | Invalid range (bottom > top)";
151 throw RuntimeException(msg, ANNA_FILE_LOCATION);
154 if((result = find(bottom)) != NULL) {
155 std::string msg(asString());
156 msg += " | Bottom: ";
157 msg += TSerializer::asString(bottom);
158 msg += " | Overlapped with ";
159 msg += result->asString();
160 throw RuntimeException(msg, ANNA_FILE_LOCATION);
163 if((result = find(top)) != NULL) {
164 std::string msg(asString());
166 msg += TSerializer::asString(top);
167 msg += " | Overlapped with ";
168 msg += result->asString();
169 throw RuntimeException(msg, ANNA_FILE_LOCATION);
172 a_ranges.push_back(result = new Range(getName(), bottom, top));
173 return range_begin() + a_ranges.size() - 1;
177 Asocia el comm::Server recibido como parametro al rango representado por el iterador recibido.
178 \param ii Iterador de rango al que asociar el servicio. Debe ser resultado de invocar a #createRange.
179 \param server Instancia del comm::Server a incorporar dentro del rango.
181 void attach(range_iterator& ii, Server* server) throw(RuntimeException) {
182 range(ii)->delivery->attach(server);
183 comm::Service::attach(server);
187 Establece la clave que se usara para decidir que pareja de maquinas usar para enviar el mensaje.
188 \param key Clave usada para calcular el reparto.
189 \warning Este metodo debe invocarse siempre antes de usar este servicio de reparto.
191 void prepare(const TKey& key) throw(RuntimeException) {
192 Range* w = find(key);
194 if(w == NULL && Logger::isActive(Logger::Warning)) {
195 std::string msg(asString());
197 msg += TSerializer::asString(key);
198 msg += " | No range associated";
199 Logger::warning(msg, ANNA_FILE_LOCATION);
203 if(a_currentRange == NULL) {
204 std::string msg(asString());
206 msg += TSerializer::asString(key);
207 msg += " | No delivery service associated has been found";
208 throw RuntimeException(msg, ANNA_FILE_LOCATION);
212 std::string asString() const
214 std::string result = className();
216 result += comm::Delivery::asString();
217 result += " | Ranges: ";
219 for(const_range_iterator ii = range_begin(), maxii = range_end(); ii != maxii; ii ++) {
221 result += range(ii)->asString();
224 return result += "}";
227 xml::Node* asXML(xml::Node* parent) const
229 xml::Node* result = parent->createChild("anna.comm.ByRangeDelivery");
230 xml::Node* node = comm::Service::asXML(result);
232 for(const_range_iterator ii = range_begin(), maxii = range_end(); ii != maxii; ii ++)
233 range(ii)->asXML(node);
239 Metodo que devuelve el nombre completo de este selector de recursos.
240 Para evitar ambigüedades este nombre incluye la lista completa de \em namespaces
241 a los que pertenece la clase.
242 \return Una cadena con el nombre de este selector.
244 static const char* className() throw() { return "anna::comm::ByRangeDelivery"; }
247 range_iterator range_begin() throw() { return a_ranges.begin(); }
248 range_iterator range_end() throw() { return a_ranges.end(); }
250 const_range_iterator range_begin() const throw() { return a_ranges.begin(); }
251 const_range_iterator range_end() const throw() { return a_ranges.end(); }
253 static Range* range(range_iterator& ii) throw() { return *ii; }
254 static const Range* range(const_range_iterator& ii) throw() { return *ii; }
256 virtual void do_initialize()
257 throw(RuntimeException) {
258 for(range_iterator ii = range_begin(), maxii = range_end(); ii != maxii; ii ++)
259 range(ii)->delivery->initialize();
263 range_container a_ranges;
264 Range* a_currentRange;
266 Range* find(const TKey& key) throw() {
270 for(range_iterator ii = range_begin(), maxii = range_end(); ii != maxii; ii ++) {
273 if(w->bottom <= key && key <= w->top) {
282 comm::Resource* do_apply()
283 throw(RuntimeException) {
284 if(a_currentRange == NULL) {
285 std::string msg(asString());
286 msg += " | Wrong call to anna::comm::ByRangeDelivery::prepare";
287 throw RuntimeException(msg, ANNA_FILE_LOCATION);
290 return a_currentRange->delivery->apply();
293 // Considera que el servicio esta NO-disponible cuando TODOS los servicios fallan
294 bool do_fault(const comm::Resource* resource)
299 for(range_iterator ii = range_begin(), maxii = range_end(); ii != maxii; ii ++) {
302 if(range(ii)->delivery->fault(resource) == true)
306 return (n == nfault);
309 // Considera que el servicio esta recuperado cuando alguno de los servicios esta disponible
310 bool do_recover(const comm::Resource* resource)
314 for(range_iterator ii = range_begin(), maxii = range_end(); ii != maxii; ii ++) {
315 if(range(ii)->delivery->recover(resource) == true)