Remove dynamic exceptions
[anna.git] / source / comm / Delivery.cpp
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 #include <algorithm>
10
11 #include <anna/core/tracing/TraceMethod.hpp>
12 #include <anna/core/functions.hpp>
13 #include <anna/core/tracing/Logger.hpp>
14
15 #include <anna/xml/Node.hpp>
16
17 #include <anna/comm/Delivery.hpp>
18 #include <anna/comm/Resource.hpp>
19 #include <anna/comm/Delivery.hpp>
20
21 using namespace std;
22 using namespace anna;
23
24 const Millisecond comm::Delivery::DefaultRecoveryTime(60000);
25
26 void comm::Delivery::initialize()
27 noexcept(false) {
28   LOGMETHOD(TraceMethod tm("comm::Delivery", "initialize", ANNA_FILE_LOCATION));
29 // No es necesario protegerlo porque cuando se ejecuta todavía estarmos en la parte
30 // en la que no se han lanzado los thread's
31 //   Guard guard (*this, "comm::Delivery::initialize");
32   do_initialize();
33
34   if(a_resources.empty() == true) {
35     string msg(asString());
36     msg += " | No resources assigned";
37     Logger::warning(msg, ANNA_FILE_LOCATION);
38   }
39
40   a_isAvailable = isAvailable();
41   LOGDEBUG(
42     string msg("comm::Delivery::initialize | ");
43     msg += asString();
44     Logger::debug(msg, ANNA_FILE_LOCATION);
45   );
46 }
47
48 //---------------------------------------------------------------------------------------------
49 // Selecciona alguno de los recursos que tiene asociados este servicio de reparto.
50 //
51 // (1) Cada cierto tiempo comprueba si hay que habilitar alguno de los recursos que han sido
52 // desactivados con motivo de errores internos. Los que hayan sido desactivados por motivos
53 // externos seran recuperados por el comm::ConnectionRecover, que se estara encargando de
54 // intentar reconectar cada cierto tiempo.
55 //
56 // (2) En principio, supone que va a recuperar todos los recursos con problemas.
57 //     (2.1) Si queda algun recurso con problemas, activa el timeStamp para seguir comprobando
58 //           los recursos con fallos internos.
59 //---------------------------------------------------------------------------------------------
60 comm::Resource* comm::Delivery::apply()
61 noexcept(false) {
62   LOGMETHOD(TraceMethod tm(Logger::Local7, "comm::Delivery", "apply", ANNA_FILE_LOCATION));
63   Guard guard(*this, "comm::Delivery::apply");
64
65   if(a_timeStamp != 0) {                                      // (1)
66     const Millisecond now = functions::millisecond();
67
68     if((now - a_timeStamp) > (a_recoveryTime >> 1)) {
69       comm::Resource* resource;
70       a_timeStamp = 0;                                     // (2)
71
72       for(iterator ii = begin(), maxii = end(); ii != maxii; ii ++) {
73         resource = Delivery::resource(ii);
74
75         if(internalErrorDetected(resource) == false)
76           continue;
77
78         if((now - resource->getTimeStamp()) > a_recoveryTime)
79           unsafe_recover(resource);
80         else if(a_timeStamp == 0)
81           a_timeStamp = now;                           // (2.1)
82       }
83     }
84   }
85
86   comm::Resource* result = do_apply();
87
88   if(result == NULL) {
89     string msg(asString());
90     msg += " | No resources available";
91     throw RuntimeException(msg, ANNA_FILE_LOCATION);
92   }
93
94   LOGDEBUG(
95     string msg("comm::Delivery::apply | ");
96     msg += asString();
97     msg += " | ";
98     msg += result->asString();
99     Logger::debug(msg, ANNA_FILE_LOCATION)
100   );
101   return result;
102 }
103
104 bool comm::Delivery::fault(const Resource* resource)
105 {
106   LOGMETHOD(TraceMethod tm(Logger::Local7, "comm::Delivery", "fault", ANNA_FILE_LOCATION));
107
108   if(resource == NULL)
109     return !a_isAvailable;
110
111   Guard guard(*this, "comm::Delivery::fault");
112
113   if(find(comm::Delivery::begin(), end(), resource) == end())                // (1)
114     return !a_isAvailable;
115
116   const bool result = do_fault(resource);                                    // (2)
117
118   if(a_timeStamp == 0 && internalErrorDetected(resource) == true)
119     a_timeStamp = functions::millisecond();
120
121   const int aux = a_isAvailable;
122   a_isAvailable = !result;
123   LOGWARNING(
124     string msg("comm::Delivery::fault | ");
125     msg += asString();
126     msg += " | ";
127     msg += resource->asString();
128     Logger::warning(msg, ANNA_FILE_LOCATION)
129   );
130
131   if(a_isAvailable == false && a_isAvailable != aux) {
132     string msg("comm::Delivery::fault | ");
133     msg += asString();
134     Logger::error(msg, ANNA_FILE_LOCATION);
135   }
136
137   return result;
138 }
139
140 bool comm::Delivery::recover(const Resource* resource)
141 {
142   if(resource == NULL)
143     return a_isAvailable;
144
145   LOGMETHOD(TraceMethod tm(Logger::Local7, "comm::Delivery", "recover", ANNA_FILE_LOCATION));
146   Guard guard(*this, "comm::Delivery::recover");
147   return unsafe_recover(resource);
148 }
149
150 bool comm::Delivery::unsafe_recover(const Resource* resource)
151 {
152   LOGMETHOD(TraceMethod tm(Logger::Local7, "comm::Delivery", "unsafe_recover", ANNA_FILE_LOCATION));
153
154   if(find(comm::Delivery::begin(), end(), resource) == end())                // (1)
155     return a_isAvailable;
156
157   a_isAvailable = do_recover(resource);
158   LOGWARNING(
159     string msg("comm::Delivery::recover | ");
160     msg += asString();
161     msg += " | ";
162     msg += resource->asString();
163     Logger::warning(msg, ANNA_FILE_LOCATION)
164   );
165   return a_isAvailable;
166 }
167
168 bool comm::Delivery::contains(const Resource* resource) const
169 {
170   if(resource == NULL)
171     return false;
172
173   Guard guard(*this, "comm::Delivery::contains");
174   return do_contains(resource);
175 }
176
177 // Devolverá true si no tiene disponible ninguno de los recursos asociados.
178 bool comm::Delivery::do_fault(const Resource* resource)
179 {
180   return (isAvailable() == true) ? false : true;
181 }
182
183 // Devolverá true si tiene disponible alguno de los recursos que tiene asociados.
184 bool comm::Delivery::do_recover(const Resource* resource)
185 {
186   return isAvailable();
187 }
188
189 //-------------------------------------------------------------------------------------------
190 // Un recurso puede estar desactivado porque el Communicator a detectado una rotura de
191 // conexion, lo que consideraremos un error externo, o porque se han detectado errores al
192 // intentar enviar, lo que consideraremos un error interno.
193 //
194 //-------------------------------------------------------------------------------------------
195 bool comm::Delivery::isAvailable() const
196 {
197   bool result(false);
198
199   for(const_iterator ii = begin(), maxii = end(); ii != maxii; ii ++) {
200     const comm::Resource* resource = Delivery::resource(ii);
201
202     if(resource->isEnabled() == true && resource->isAvailable() == true) {
203       result = true;
204       break;
205     }
206   }
207
208   return result;
209 }
210
211 void comm::Delivery::add(Resource* resource)
212 noexcept(false) {
213   if(resource == NULL) {
214     string msg(asString());
215     msg += " | Cannot attach a NULL resource";
216     throw RuntimeException(msg, ANNA_FILE_LOCATION);
217   }
218
219   Guard guard(*this, "comm::Delivery::add");
220
221   if(do_contains(resource) == true)
222     return;
223
224   LOGINFORMATION(
225     string msg("comm::Delivery::add | ");
226     msg += asString();
227     msg += " | ";
228     msg += resource->asString();
229     Logger::information(msg, ANNA_FILE_LOCATION);
230   );
231   a_resources.push_back(resource);
232 }
233
234 bool comm::Delivery::do_contains(const comm::Resource* resource) const
235 {
236   for(const_iterator ii = begin(), maxii = end(); ii != maxii; ii ++)
237     if(*Delivery::resource(ii) == *resource)
238       return true;
239
240   return false;
241 }
242
243 string comm::Delivery::asString() const
244 {
245   string result("comm::Delivery { Name: ");
246   result += a_name;
247   result += functions::asText(" | Available: ", a_isAvailable);
248   result += " | N: ";
249   result += functions::asString(a_resources.size());
250
251   if(a_timeStamp != 0)
252     result += " | InternalError: Detected";
253
254   return result += " }";
255 }
256
257 xml::Node* comm::Delivery::asXML(xml::Node* parent) const
258 {
259   xml::Node* node = parent->createChild("comm.Delivery");
260   node->createAttribute("Name", a_name);
261   node->createAttribute("Available", functions::asString(a_isAvailable));
262
263   if(a_timeStamp != 0)
264     node->createAttribute("InternalError", "Detected");
265
266   xml::Node* resources = node->createChild("comm.Resources");
267
268   for(const_iterator ii = begin(), maxii = end(); ii != maxii; ii ++) {
269     const Resource* commResource = resource(ii);
270     commResource->asAttribute(resources->createChild("comm.Resource"));
271   }
272
273   return node;
274 }
275
276 /*
277  * Devuelve true si el recurso recibido como parametro ha sido desactivado como
278  * resultado de un error interno, es decir, que se ha intentado enviar un mensaje
279  * y se ha obtenido una excepcion (EAGAIN, EPIPE, etc, etc), o false en otro caso.
280  *
281  * Si el 'isAvailable' fuera 'false' => que la conexion con el servidor remoto
282  * esta rota, y por tanto, en caso de que este marcado como de auto-reconexion
283  * estara siendo recuperada por el comm::ConnectionRecover.
284  */
285 /*static*/
286 bool comm::Delivery::internalErrorDetected(const Resource* resource)
287 {
288   return resource->isEnabled() == false && resource->isAvailable() == true;
289 }
290