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
39 #include <anna/core/tracing/TraceMethod.hpp>
40 #include <anna/core/functions.hpp>
41 #include <anna/core/tracing/Logger.hpp>
43 #include <anna/xml/Node.hpp>
45 #include <anna/comm/Delivery.hpp>
46 #include <anna/comm/Resource.hpp>
47 #include <anna/comm/Delivery.hpp>
52 const Millisecond comm::Delivery::DefaultRecoveryTime(60000);
54 void comm::Delivery::initialize()
55 throw(RuntimeException) {
56 LOGMETHOD(TraceMethod tm("comm::Delivery", "initialize", ANNA_FILE_LOCATION));
57 // No es necesario protegerlo porque cuando se ejecuta todavía estarmos en la parte
58 // en la que no se han lanzado los thread's
59 // Guard guard (*this, "comm::Delivery::initialize");
62 if(a_resources.empty() == true) {
63 string msg(asString());
64 msg += " | No resources assigned";
65 Logger::warning(msg, ANNA_FILE_LOCATION);
68 a_isAvailable = isAvailable();
70 string msg("comm::Delivery::initialize | ");
72 Logger::debug(msg, ANNA_FILE_LOCATION);
76 //---------------------------------------------------------------------------------------------
77 // Selecciona alguno de los recursos que tiene asociados este servicio de reparto.
79 // (1) Cada cierto tiempo comprueba si hay que habilitar alguno de los recursos que han sido
80 // desactivados con motivo de errores internos. Los que hayan sido desactivados por motivos
81 // externos seran recuperados por el comm::ConnectionRecover, que se estara encargando de
82 // intentar reconectar cada cierto tiempo.
84 // (2) En principio, supone que va a recuperar todos los recursos con problemas.
85 // (2.1) Si queda algun recurso con problemas, activa el timeStamp para seguir comprobando
86 // los recursos con fallos internos.
87 //---------------------------------------------------------------------------------------------
88 comm::Resource* comm::Delivery::apply()
89 throw(RuntimeException) {
90 LOGMETHOD(TraceMethod tm(Logger::Local7, "comm::Delivery", "apply", ANNA_FILE_LOCATION));
91 Guard guard(*this, "comm::Delivery::apply");
93 if(a_timeStamp != 0) { // (1)
94 const Millisecond now = functions::millisecond();
96 if((now - a_timeStamp) > (a_recoveryTime >> 1)) {
97 comm::Resource* resource;
98 a_timeStamp = 0; // (2)
100 for(iterator ii = begin(), maxii = end(); ii != maxii; ii ++) {
101 resource = Delivery::resource(ii);
103 if(internalErrorDetected(resource) == false)
106 if((now - resource->getTimeStamp()) > a_recoveryTime)
107 unsafe_recover(resource);
108 else if(a_timeStamp == 0)
109 a_timeStamp = now; // (2.1)
114 comm::Resource* result = do_apply();
117 string msg(asString());
118 msg += " | No resources available";
119 throw RuntimeException(msg, ANNA_FILE_LOCATION);
123 string msg("comm::Delivery::apply | ");
126 msg += result->asString();
127 Logger::debug(msg, ANNA_FILE_LOCATION)
132 bool comm::Delivery::fault(const Resource* resource)
134 LOGMETHOD(TraceMethod tm(Logger::Local7, "comm::Delivery", "fault", ANNA_FILE_LOCATION));
137 return !a_isAvailable;
139 Guard guard(*this, "comm::Delivery::fault");
141 if(find(comm::Delivery::begin(), end(), resource) == end()) // (1)
142 return !a_isAvailable;
144 const bool result = do_fault(resource); // (2)
146 if(a_timeStamp == 0 && internalErrorDetected(resource) == true)
147 a_timeStamp = functions::millisecond();
149 const int aux = a_isAvailable;
150 a_isAvailable = !result;
152 string msg("comm::Delivery::fault | ");
155 msg += resource->asString();
156 Logger::warning(msg, ANNA_FILE_LOCATION)
159 if(a_isAvailable == false && a_isAvailable != aux) {
160 string msg("comm::Delivery::fault | ");
162 Logger::error(msg, ANNA_FILE_LOCATION);
168 bool comm::Delivery::recover(const Resource* resource)
171 return a_isAvailable;
173 LOGMETHOD(TraceMethod tm(Logger::Local7, "comm::Delivery", "recover", ANNA_FILE_LOCATION));
174 Guard guard(*this, "comm::Delivery::recover");
175 return unsafe_recover(resource);
178 bool comm::Delivery::unsafe_recover(const Resource* resource)
180 LOGMETHOD(TraceMethod tm(Logger::Local7, "comm::Delivery", "unsafe_recover", ANNA_FILE_LOCATION));
182 if(find(comm::Delivery::begin(), end(), resource) == end()) // (1)
183 return a_isAvailable;
185 a_isAvailable = do_recover(resource);
187 string msg("comm::Delivery::recover | ");
190 msg += resource->asString();
191 Logger::warning(msg, ANNA_FILE_LOCATION)
193 return a_isAvailable;
196 bool comm::Delivery::contains(const Resource* resource) const
201 Guard guard(*this, "comm::Delivery::contains");
202 return do_contains(resource);
205 // Devolverá true si no tiene disponible ninguno de los recursos asociados.
206 bool comm::Delivery::do_fault(const Resource* resource)
208 return (isAvailable() == true) ? false : true;
211 // Devolverá true si tiene disponible alguno de los recursos que tiene asociados.
212 bool comm::Delivery::do_recover(const Resource* resource)
214 return isAvailable();
217 //-------------------------------------------------------------------------------------------
218 // Un recurso puede estar desactivado porque el Communicator a detectado una rotura de
219 // conexion, lo que consideraremos un error externo, o porque se han detectado errores al
220 // intentar enviar, lo que consideraremos un error interno.
222 //-------------------------------------------------------------------------------------------
223 bool comm::Delivery::isAvailable() const
227 for(const_iterator ii = begin(), maxii = end(); ii != maxii; ii ++) {
228 const comm::Resource* resource = Delivery::resource(ii);
230 if(resource->isEnabled() == true && resource->isAvailable() == true) {
239 void comm::Delivery::add(Resource* resource)
240 throw(RuntimeException) {
241 if(resource == NULL) {
242 string msg(asString());
243 msg += " | Cannot attach a NULL resource";
244 throw RuntimeException(msg, ANNA_FILE_LOCATION);
247 Guard guard(*this, "comm::Delivery::add");
249 if(do_contains(resource) == true)
253 string msg("comm::Delivery::add | ");
256 msg += resource->asString();
257 Logger::information(msg, ANNA_FILE_LOCATION);
259 a_resources.push_back(resource);
262 bool comm::Delivery::do_contains(const comm::Resource* resource) const
264 for(const_iterator ii = begin(), maxii = end(); ii != maxii; ii ++)
265 if(*Delivery::resource(ii) == *resource)
271 string comm::Delivery::asString() const
273 string result("comm::Delivery { Name: ");
275 result += functions::asText(" | Available: ", a_isAvailable);
277 result += functions::asString(a_resources.size());
280 result += " | InternalError: Detected";
282 return result += " }";
285 xml::Node* comm::Delivery::asXML(xml::Node* parent) const
287 xml::Node* node = parent->createChild("comm.Delivery");
288 node->createAttribute("Name", a_name);
289 node->createAttribute("Available", functions::asString(a_isAvailable));
292 node->createAttribute("InternalError", "Detected");
294 xml::Node* resources = node->createChild("comm.Resources");
296 for(const_iterator ii = begin(), maxii = end(); ii != maxii; ii ++) {
297 const Resource* commResource = resource(ii);
298 commResource->asAttribute(resources->createChild("comm.Resource"));
305 * Devuelve true si el recurso recibido como parametro ha sido desactivado como
306 * resultado de un error interno, es decir, que se ha intentado enviar un mensaje
307 * y se ha obtenido una excepcion (EAGAIN, EPIPE, etc, etc), o false en otro caso.
309 * Si el 'isAvailable' fuera 'false' => que la conexion con el servidor remoto
310 * esta rota, y por tanto, en caso de que este marcado como de auto-reconexion
311 * estara siendo recuperada por el comm::ConnectionRecover.
314 bool comm::Delivery::internalErrorDetected(const Resource* resource)
316 return resource->isEnabled() == false && resource->isAvailable() == true;