Updated license
[anna.git] / source / comm / Delivery.cpp
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 #include <algorithm>
38
39 #include <anna/core/tracing/TraceMethod.hpp>
40 #include <anna/core/functions.hpp>
41 #include <anna/core/tracing/Logger.hpp>
42
43 #include <anna/xml/Node.hpp>
44
45 #include <anna/comm/Delivery.hpp>
46 #include <anna/comm/Resource.hpp>
47 #include <anna/comm/Delivery.hpp>
48
49 using namespace std;
50 using namespace anna;
51
52 const Millisecond comm::Delivery::DefaultRecoveryTime(60000);
53
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");
60   do_initialize();
61
62   if(a_resources.empty() == true) {
63     string msg(asString());
64     msg += " | No resources assigned";
65     Logger::warning(msg, ANNA_FILE_LOCATION);
66   }
67
68   a_isAvailable = isAvailable();
69   LOGDEBUG(
70     string msg("comm::Delivery::initialize | ");
71     msg += asString();
72     Logger::debug(msg, ANNA_FILE_LOCATION);
73   );
74 }
75
76 //---------------------------------------------------------------------------------------------
77 // Selecciona alguno de los recursos que tiene asociados este servicio de reparto.
78 //
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.
83 //
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");
92
93   if(a_timeStamp != 0) {                                      // (1)
94     const Millisecond now = functions::millisecond();
95
96     if((now - a_timeStamp) > (a_recoveryTime >> 1)) {
97       comm::Resource* resource;
98       a_timeStamp = 0;                                     // (2)
99
100       for(iterator ii = begin(), maxii = end(); ii != maxii; ii ++) {
101         resource = Delivery::resource(ii);
102
103         if(internalErrorDetected(resource) == false)
104           continue;
105
106         if((now - resource->getTimeStamp()) > a_recoveryTime)
107           unsafe_recover(resource);
108         else if(a_timeStamp == 0)
109           a_timeStamp = now;                           // (2.1)
110       }
111     }
112   }
113
114   comm::Resource* result = do_apply();
115
116   if(result == NULL) {
117     string msg(asString());
118     msg += " | No resources available";
119     throw RuntimeException(msg, ANNA_FILE_LOCATION);
120   }
121
122   LOGDEBUG(
123     string msg("comm::Delivery::apply | ");
124     msg += asString();
125     msg += " | ";
126     msg += result->asString();
127     Logger::debug(msg, ANNA_FILE_LOCATION)
128   );
129   return result;
130 }
131
132 bool comm::Delivery::fault(const Resource* resource)
133 throw() {
134   LOGMETHOD(TraceMethod tm(Logger::Local7, "comm::Delivery", "fault", ANNA_FILE_LOCATION));
135
136   if(resource == NULL)
137     return !a_isAvailable;
138
139   Guard guard(*this, "comm::Delivery::fault");
140
141   if(find(comm::Delivery::begin(), end(), resource) == end())                // (1)
142     return !a_isAvailable;
143
144   const bool result = do_fault(resource);                                    // (2)
145
146   if(a_timeStamp == 0 && internalErrorDetected(resource) == true)
147     a_timeStamp = functions::millisecond();
148
149   const int aux = a_isAvailable;
150   a_isAvailable = !result;
151   LOGWARNING(
152     string msg("comm::Delivery::fault | ");
153     msg += asString();
154     msg += " | ";
155     msg += resource->asString();
156     Logger::warning(msg, ANNA_FILE_LOCATION)
157   );
158
159   if(a_isAvailable == false && a_isAvailable != aux) {
160     string msg("comm::Delivery::fault | ");
161     msg += asString();
162     Logger::error(msg, ANNA_FILE_LOCATION);
163   }
164
165   return result;
166 }
167
168 bool comm::Delivery::recover(const Resource* resource)
169 throw() {
170   if(resource == NULL)
171     return a_isAvailable;
172
173   LOGMETHOD(TraceMethod tm(Logger::Local7, "comm::Delivery", "recover", ANNA_FILE_LOCATION));
174   Guard guard(*this, "comm::Delivery::recover");
175   return unsafe_recover(resource);
176 }
177
178 bool comm::Delivery::unsafe_recover(const Resource* resource)
179 throw() {
180   LOGMETHOD(TraceMethod tm(Logger::Local7, "comm::Delivery", "unsafe_recover", ANNA_FILE_LOCATION));
181
182   if(find(comm::Delivery::begin(), end(), resource) == end())                // (1)
183     return a_isAvailable;
184
185   a_isAvailable = do_recover(resource);
186   LOGWARNING(
187     string msg("comm::Delivery::recover | ");
188     msg += asString();
189     msg += " | ";
190     msg += resource->asString();
191     Logger::warning(msg, ANNA_FILE_LOCATION)
192   );
193   return a_isAvailable;
194 }
195
196 bool comm::Delivery::contains(const Resource* resource) const
197 throw() {
198   if(resource == NULL)
199     return false;
200
201   Guard guard(*this, "comm::Delivery::contains");
202   return do_contains(resource);
203 }
204
205 // Devolverá true si no tiene disponible ninguno de los recursos asociados.
206 bool comm::Delivery::do_fault(const Resource* resource)
207 throw() {
208   return (isAvailable() == true) ? false : true;
209 }
210
211 // Devolverá true si tiene disponible alguno de los recursos que tiene asociados.
212 bool comm::Delivery::do_recover(const Resource* resource)
213 throw() {
214   return isAvailable();
215 }
216
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.
221 //
222 //-------------------------------------------------------------------------------------------
223 bool comm::Delivery::isAvailable() const
224 throw() {
225   bool result(false);
226
227   for(const_iterator ii = begin(), maxii = end(); ii != maxii; ii ++) {
228     const comm::Resource* resource = Delivery::resource(ii);
229
230     if(resource->isEnabled() == true && resource->isAvailable() == true) {
231       result = true;
232       break;
233     }
234   }
235
236   return result;
237 }
238
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);
245   }
246
247   Guard guard(*this, "comm::Delivery::add");
248
249   if(do_contains(resource) == true)
250     return;
251
252   LOGINFORMATION(
253     string msg("comm::Delivery::add | ");
254     msg += asString();
255     msg += " | ";
256     msg += resource->asString();
257     Logger::information(msg, ANNA_FILE_LOCATION);
258   );
259   a_resources.push_back(resource);
260 }
261
262 bool comm::Delivery::do_contains(const comm::Resource* resource) const
263 throw() {
264   for(const_iterator ii = begin(), maxii = end(); ii != maxii; ii ++)
265     if(*Delivery::resource(ii) == *resource)
266       return true;
267
268   return false;
269 }
270
271 string comm::Delivery::asString() const
272 throw() {
273   string result("comm::Delivery { Name: ");
274   result += a_name;
275   result += functions::asText(" | Available: ", a_isAvailable);
276   result += " | N: ";
277   result += functions::asString(a_resources.size());
278
279   if(a_timeStamp != 0)
280     result += " | InternalError: Detected";
281
282   return result += " }";
283 }
284
285 xml::Node* comm::Delivery::asXML(xml::Node* parent) const
286 throw() {
287   xml::Node* node = parent->createChild("comm.Delivery");
288   node->createAttribute("Name", a_name);
289   node->createAttribute("Available", functions::asString(a_isAvailable));
290
291   if(a_timeStamp != 0)
292     node->createAttribute("InternalError", "Detected");
293
294   xml::Node* resources = node->createChild("comm.Resources");
295
296   for(const_iterator ii = begin(), maxii = end(); ii != maxii; ii ++) {
297     const Resource* commResource = resource(ii);
298     commResource->asAttribute(resources->createChild("comm.Resource"));
299   }
300
301   return node;
302 }
303
304 /*
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.
308  *
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.
312  */
313 /*static*/
314 bool comm::Delivery::internalErrorDetected(const Resource* resource)
315 throw() {
316   return resource->isEnabled() == false && resource->isAvailable() == true;
317 }
318