New feature to allow to register components with different names for same class:...
[anna.git] / source / diameter.comm / Engine.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 <anna/diameter.comm/Engine.hpp>
10
11 #include <anna/core/tracing/Logger.hpp>
12 #include <anna/core/tracing/TraceMethod.hpp>
13 #include <anna/xml/Node.hpp>
14 #include <anna/comm/Network.hpp>
15 #include <anna/comm/Host.hpp>
16 #include <anna/comm/ClientSocket.hpp>
17
18 #include <anna/diameter.comm/Transport.hpp>
19 #include <anna/diameter.comm/Engine.hpp>
20 #include <anna/diameter.comm/Entity.hpp>
21 #include <anna/diameter.comm/Server.hpp>
22 #include <anna/diameter.comm/ClientSession.hpp>
23 #include <anna/diameter.comm/LocalServer.hpp>
24 #include <anna/core/functions.hpp>
25 #include <anna/diameter/internal/sccs.hpp>
26 #include <anna/diameter.comm/OamModule.hpp>
27 #include <anna/diameter/codec/functions.hpp>
28 #include <anna/diameter/helpers/base/functions.hpp>
29 #include <anna/diameter/helpers/helpers.hpp>
30 #include <anna/diameter/codec/Message.hpp>
31 #include <anna/diameter/codec/Avp.hpp>
32
33 // STD
34 #include <map>
35
36 using namespace std;
37 using namespace anna::diameter::comm;
38
39
40 Engine::Engine(const char *className) :
41   anna::app::Component(className),
42   a_autoBind(true),
43   a_availableForEntities(false),
44   a_availableForLocalServers(false),
45   a_cer(true),
46   a_dwr(true),
47 //      a_cea(true),
48 //      a_dwa(true),
49   a_watchdogPeriod(ClientSession::DefaultWatchdogPeriod),
50   a_maxConnectionDelay(anna::comm::ClientSocket::DefaultMaxConnectionDelay /* 200 ms*/),
51   a_numberOfClientSessionsPerServer(1) {
52   anna::diameter::sccs::activate();
53   a_realm = anna::functions::getDomainname();
54   a_host = anna::functions::getHostname();
55 }
56
57 Server* Engine::allocateServer() throw() { return a_serversRecycler.create(); }
58 void Engine::releaseServer(Server *server) throw() { a_serversRecycler.release(server); }
59 ClientSession* Engine::allocateClientSession() throw() { return a_clientSessionsRecycler.create(); }
60 void Engine::releaseClientSession(ClientSession *clientSession) throw() { a_clientSessionsRecycler.release(clientSession); }
61
62
63 void Engine::setCERandDWR(const anna::DataBlock & cer, const anna::DataBlock & dwr) throw(anna::RuntimeException) {
64   if(codec::functions::getCommandId(cer) != helpers::base::COMMANDID__Capabilities_Exchange_Request) {
65     throw anna::RuntimeException("The message provided as 'CER' is not a Capabilities-Exchange-Request", ANNA_FILE_LOCATION);
66   }
67
68   if(codec::functions::getCommandId(dwr) != helpers::base::COMMANDID__Device_Watchdog_Request) {
69     throw anna::RuntimeException("The message provided as 'DWR' is not a Device-Watchdog-Request", ANNA_FILE_LOCATION);
70   }
71
72   a_cer = cer;
73   a_dwr = dwr;
74 }
75
76 //void Engine::setCEAandDWA(const anna::DataBlock & cea, const anna::DataBlock & dwa) throw(anna::RuntimeException) {
77 //   if (codec::functions::getCommandId(cea) != helpers::base::COMMANDID__Capabilities_Exchange_Answer) {
78 //      throw anna::RuntimeException("The message provided as 'CEA' is not a Capabilities-Exchange-Answer", ANNA_FILE_LOCATION);
79 //   }
80 //
81 //   if (codec::functions::getCommandId(dwa) != helpers::base::COMMANDID__Device_Watchdog_Answer) {
82 //      throw anna::RuntimeException("The message provided as 'DWA' is not a Device-Watchdog-Answer", ANNA_FILE_LOCATION);
83 //   }
84 //
85 //   a_cea = cea;
86 //   a_dwa = dwa;
87 //}
88
89 void Engine::setWatchdogPeriod(const anna::Millisecond & wp) throw(anna::RuntimeException) {
90   if(wp < ClientSession::DefaultWatchdogPeriod) {
91     throw anna::RuntimeException(anna::functions::asString("Please set watchdog period over %s", ClientSession::DefaultWatchdogPeriod.asString().c_str()), ANNA_FILE_LOCATION);
92   }
93
94   a_watchdogPeriod = wp;
95 }
96
97 void Engine::checkEntityCollision(const socket_v &v) throw(anna::RuntimeException) {
98   socket_v::const_iterator it;
99   socket_v::const_iterator it_min(v.begin());
100   socket_v::const_iterator it_max(v.end());
101
102   for(it = it_min; it != it_max; it++) {
103     server_iterator ii = server_find(*it);
104
105     if(ii != server_end())
106       throw anna::RuntimeException("diameter::comm::Engine::checkEntityCollision: Server is already reserved by a former created entity. Use another", ANNA_FILE_LOCATION);
107   }
108
109   // Check repetitions:
110   std::map < socket_t, int/*dummy*/ > auxMap;
111
112   for(it = it_min; it != it_max; it++) auxMap[(*it)] = 0;
113
114   if(auxMap.size() != v.size())
115     throw anna::RuntimeException("diameter::comm::Engine::checkEntityCollision: Provided addresses list (sockets) must have all items different", ANNA_FILE_LOCATION);
116 }
117
118 Entity* Engine::createEntity(const socket_v & socketList, const std::string &description)
119 throw(anna::RuntimeException) {
120   Entity* result(NULL);
121   anna::Guard guard(this, "anna::diameter::comm::Engine::createEntity");
122
123   if(socketList.size() == 0)
124     throw anna::RuntimeException("diameter::comm::Engine::createEntity Address/Port server list provided is empty", ANNA_FILE_LOCATION);
125
126   // Proteccion antes de reservar memoria para una entidad (allocateEntity):
127   checkEntityCollision(socketList);
128
129   if((result = allocateEntity()) == NULL)
130     throw anna::RuntimeException("diameter::comm::Engine::allocateEntity returns NULL (perhaps virtual method was not implemented)", ANNA_FILE_LOCATION);
131
132   // Initialize:
133   result->initialize(); // warning: recycler does not initialize its objects and at least, is important to empty servers vector.
134   // Assignments (it could be done at allocate):
135   result->setEngine(this); // lo podia haber asignado en el allocateEntity (no importa)
136   result->setMaxServers(socketList.size());
137   result->setDescription(description);
138   entity_key key(getEntityKey(socketList));
139   result->a_socketListLiteral = key;
140   // Create associated servers:
141   socket_v::const_iterator it;
142   socket_v::const_iterator it_min(socketList.begin());
143   socket_v::const_iterator it_max(socketList.end());
144   int count = 1;
145
146   for(it = it_min; it != it_max; it++) {
147     result->addServer(*it);
148
149     if(count == 1) result->a_primarySocketLiteral = anna::functions::socketLiteralAsString((*it).first, (*it).second);
150
151     if(count == 2) result->a_secondarySocketLiteral = anna::functions::socketLiteralAsString((*it).first, (*it).second);
152
153     count++;
154   }
155
156   a_entities.insert(entity_value_type(key, result));
157   LOGDEBUG(
158     string msg("diameter::comm::Engine::createEntity | ");
159     msg += result->asString();
160     msg += anna::functions::asText(" | AutoBind: ", a_autoBind);
161     anna::Logger::debug(msg, ANNA_FILE_LOCATION);
162   );
163   return result;
164 }
165
166
167 LocalServer *Engine::createLocalServer(const std::string & addr, int port, int maxConnections, const anna::Millisecond & allowedInactivityTime, int category, const std::string & description)
168 throw(anna::RuntimeException) {
169   LocalServer* result(NULL);
170   anna::Guard guard(this, "anna::diameter::comm::Engine::createLocalServer");
171   // Proteccion antes de reservar memoria para un LocalServer
172   socket_t key(addr, port);
173
174   if(a_localServers.find(key) != a_localServers.end())
175     throw anna::RuntimeException("LocalServer is already reserved by a former created access point. Cannot create again", ANNA_FILE_LOCATION);
176
177   if((result = allocateLocalServer()) == NULL)
178     throw anna::RuntimeException("diameter::comm::Engine::allocateLocalServer returns NULL (perhaps virtual method was not implemented)", ANNA_FILE_LOCATION);
179
180   result->setEngine(this); // lo podia haber asignado en el allocateLocalServer (no importa)
181   result->setKey(key);
182   result->setCategory(category);
183   result->setDescription(description);
184   result->setAllowedInactivityTime(allowedInactivityTime);
185   result->initializeStatisticConcepts();
186 // Los saco con metodos virtuales readXXX del motor:
187 //   if ((a_cea.isEmpty()) || (a_dwa.isEmpty()))
188 //      throw anna::RuntimeException("Must define valid CEA and DWA messages by mean setCEAandDWA()", ANNA_FILE_LOCATION);
189   a_localServers.insert(localServer_value_type(key, result));
190   LOGDEBUG(
191     string msg("diameter::comm::Engine::createLocalServer | ");
192     msg += result->asString();
193     anna::Logger::debug(msg, ANNA_FILE_LOCATION);
194   );
195 //   // Listen: (*)
196 //   /*if (a_autoListen) */result->enable(); // creates server socket
197   result->setMaxConnections(maxConnections); // (*) this enables the listen port ... or not
198   return result;
199 }
200
201
202 Entity* Engine::createEntity(const std::string & addr1, int port1, const std::string & addr2, int port2, const std::string &description)
203 throw(anna::RuntimeException) {
204   socket_v dualList;
205   dualList.push_back(socket_t(addr1, port1));
206   dualList.push_back(socket_t(addr2, port2));
207   return (createEntity(dualList, description));
208 }
209
210
211 Server* Engine::createServer(Entity *entity, const socket_t & socket)
212 throw(anna::RuntimeException) {
213   Server* result(NULL);
214   anna::Guard guard(this, "anna::diameter::comm::Engine::createServer");
215
216   if((result = allocateServer()) == NULL)
217     throw anna::RuntimeException("diameter::comm::Engine::allocateServer returns NULL", ANNA_FILE_LOCATION);
218
219   // Initialize:
220   result->initialize(); // warning: recycler does not initialize its objects and at least, is important to empty client-sessions vector.
221   // Assignments (it could be done at allocate):
222   result->a_parent = entity;
223   result->a_socket = socket;
224   result->setMaxClientSessions(a_numberOfClientSessionsPerServer /* engine */);
225   result->a_engine = this;
226   result->initializeStatisticConcepts();
227
228   for(int k = 0; k < a_numberOfClientSessionsPerServer; k++)
229     result->addClientSession(k);
230
231   a_servers.insert(server_value_type(socket, result));
232 //      LOGDEBUG( Lo comento, porque ya se tracea en el createEntity
233 //         string msg("diameter::comm::Engine::resolveServer | ");
234 //         msg += result->asString();
235 //         msg += anna::functions::asText(" | AutoBind: ", a_autoBind);
236 //         anna::Logger::debug(msg, ANNA_FILE_LOCATION);
237 //      );
238   return result;
239 }
240
241
242 // Lohacemos privado
243 ClientSession* Engine::createClientSession(Server *server, int socketId)
244 throw(anna::RuntimeException) {
245   ClientSession* result(NULL);
246   anna::Guard guard(this, "anna::diameter::comm::Engine::createClientSession");
247
248   if((result = allocateClientSession()) == NULL)
249     throw anna::RuntimeException("diameter::comm::Engine::allocateClientSession returns NULL", ANNA_FILE_LOCATION);
250
251   // Initialize:
252   result->initialize(); // warning: recycler does not initialize its objects and at least...
253   // Assignments (it could be done at allocate):
254
255   if((a_cer.isEmpty()) || (a_dwr.isEmpty()))
256     throw anna::RuntimeException("Must define valid CER and DWR messages by mean setCERandDWR()", ANNA_FILE_LOCATION);
257
258   result->a_cer.setBody(a_cer);
259   result->a_dwr.setBody(a_dwr);
260   result->setWatchdogPeriod(a_watchdogPeriod);
261   result->a_parent = server;
262   result->a_socketId = socketId;
263   result->initializeSequences(); // despu�s de asignar el server y el socketId (*)
264   // (*) Las secuencias se basan en la semilla:    srand(::time(NULL) + anna::functions::exclusiveHash(anna::functions::asString("%s:%d|%d", getAddress().c_str(), getPort(), a_socketId)));
265   result->a_engine = this;
266   clientSession_key key = ClientSession::getKey(server->getAddress(), server->getPort(), socketId);
267   a_clientSessions.insert(clientSession_value_type(key, result));
268 //      LOGDEBUG( Lo comento, porque ya se tracea en el createEntity
269 //         string msg("diameter::comm::Engine::createClientSession | ");
270 //         msg += result->asString();
271 //         msg += anna::functions::asText(" | AutoBind: ", a_autoBind);
272 //         anna::Logger::debug(msg, ANNA_FILE_LOCATION);
273 //      );
274   // Creation:
275   anna::comm::Network& network = anna::comm::Network::instantiate();
276   result->a_server = network.resolveServer(server->getAddress().c_str(), server->getPort(), true /* autoRecovery */,
277                      result->a_receiverFactory, &anna::diameter::comm::Transport::getFactory(),
278                      anna::comm::Network::Port::Multiple, anna::comm::Network::DoConnect::No /* (*) */);
279   // Delay time on tcp connect:
280   result->a_server->setMaxConnectionDelay(a_maxConnectionDelay); // (*)
281
282   // Bind:
283   if(a_autoBind) result->bind();
284
285   return result;
286 }
287
288
289 bool Engine::broadcastEntities(const Message* message) throw(anna::RuntimeException) {
290   LOGMETHOD(anna::TraceMethod tttm("diameter::comm::Engine", "broadcastEntities", ANNA_FILE_LOCATION));
291   bool allok = true;
292   bool ok;
293
294   for(entity_iterator it = entity_begin(), maxii = entity_end(); it != maxii; it ++) {
295     try {
296       ok = entity(it)->broadcast(message);
297
298       if(!ok) allok = false;
299     } catch(anna::RuntimeException &ex) {
300       ex.trace();
301       allok = false;
302     }
303   }
304
305   return allok;
306 }
307
308 bool Engine::broadcastLocalServers(const Message* message) throw(anna::RuntimeException) {
309   LOGMETHOD(anna::TraceMethod tttm("diameter::comm::Engine", "broadcastLocalServers", ANNA_FILE_LOCATION));
310   bool allok = true;
311   bool ok;
312
313   for(localServer_iterator it = localServer_begin(), maxii = localServer_end(); it != maxii; it ++) {
314     try {
315       ok = localServer(it)->broadcast(message);
316
317       if(!ok) allok = false;
318     } catch(anna::RuntimeException &ex) {
319       ex.trace();
320       allok = false;
321     }
322   }
323
324   return allok;
325 }
326
327 bool Engine::bind() throw(anna::RuntimeException) {
328   LOGMETHOD(anna::TraceMethod tttm("diameter::comm::Engine", "bind", ANNA_FILE_LOCATION));
329   bool result = true; // all OK return
330
331   for(entity_iterator it = entity_begin(), maxii = entity_end(); it != maxii; it ++) {
332     try {
333       entity(it)->bind();
334     } catch(anna::RuntimeException &ex) {
335       ex.trace();
336       result = false;
337     }
338   }
339
340   return result;
341 }
342
343 ClientSession* Engine::findClientSession(const std::string & addr, int port, int socketId, anna::Exception::Mode::_v emode)
344 throw(anna::RuntimeException) {
345   return findClientSession(ClientSession::getKey(addr, port, socketId), emode);
346 }
347
348 ClientSession* Engine::findClientSession(const std::string & key, anna::Exception::Mode::_v emode)
349 throw(anna::RuntimeException) {
350   anna::Guard guard(this, "anna::diameter::comm::Engine::findClientSession");
351   clientSession_iterator ii = clientSession_find(key);
352
353   if(ii != clientSession_end())
354     return clientSession(ii);
355
356   if(emode != anna::Exception::Mode::Ignore) {
357     string msg("diameter::comm::Engine::findClientSession | [addr:port|socketId] = ");
358     msg += key;
359     msg += " | ClientSession not found";
360     anna::RuntimeException ex(msg, ANNA_FILE_LOCATION);
361
362     if(emode == anna::Exception::Mode::Throw)
363       throw ex;
364
365     ex.trace();
366   }
367
368   return NULL;
369 }
370
371
372 Server* Engine::findServer(const std::string & addr, int port, anna::Exception::Mode::_v emode)
373 throw(anna::RuntimeException) {
374   anna::Guard guard(this, "anna::diameter::comm::Engine::findServer");
375   server_iterator ii = server_find(server_key(addr, port));
376
377   if(ii != server_end())
378     return server(ii);
379
380   if(emode != anna::Exception::Mode::Ignore) {
381     string msg("diameter::comm::Engine::findServer | addr: ");
382     msg += addr;
383     msg += anna::functions::asText(" | port: ", port);
384     msg += " | Server not found";
385     anna::RuntimeException ex(msg, ANNA_FILE_LOCATION);
386
387     if(emode == anna::Exception::Mode::Throw)
388       throw ex;
389
390     ex.trace();
391   }
392
393   return NULL;
394 }
395
396 Entity* Engine::findEntity(const socket_v & socketList, anna::Exception::Mode::_v emode)
397 throw(anna::RuntimeException) {
398   anna::Guard guard(this, "anna::diameter::comm::Engine::findEntity");
399   entity_key key(getEntityKey(socketList));
400   entity_iterator ii = entity_find(key);
401
402   if(ii != entity_end())
403     return entity(ii);
404
405   if(emode != anna::Exception::Mode::Ignore) {
406     string msg("diameter::comm::Engine::findEntity | socket list: ");
407     msg += key;
408     msg += " | Entity not found";
409     anna::RuntimeException ex(msg, ANNA_FILE_LOCATION);
410
411     if(emode == anna::Exception::Mode::Throw)
412       throw ex;
413
414     ex.trace();
415   }
416
417   return NULL;
418 }
419
420 Entity* Engine::findEntity(const std::string & addr1, int port1, const std::string & addr2, int port2, anna::Exception::Mode::_v emode)
421 throw(anna::RuntimeException) {
422   socket_v dualList;
423   dualList.push_back(socket_t(addr1, port1));
424   dualList.push_back(socket_t(addr2, port2));
425   return (findEntity(dualList, emode));
426 }
427
428
429 //Entity* Engine::findEntity(int category, anna::Exception::Mode::_v emode)
430 //throw(anna::RuntimeException) {
431 //
432 //   Entity *entity;
433 //
434 //   for (entity_iterator it = entity_begin(), maxii = entity_end(); it != maxii; it ++) {
435 //      entity = entity(it);
436 //      if (entity->getCategory() == category) return entity;
437 //   }
438 //
439 //   return NULL;
440 //}
441
442
443 LocalServer* Engine::findLocalServer(const std::string & addr, int port, anna::Exception::Mode::_v emode)
444 throw(anna::RuntimeException) {
445   anna::Guard guard(this, "anna::diameter::comm::Engine::findLocalServer");
446   socket_t key(addr, port);
447   localServer_iterator ii = localServer_find(key);
448
449   if(ii != localServer_end())
450     return localServer(ii);
451
452   if(emode != anna::Exception::Mode::Ignore) {
453     string msg("diameter::comm::Engine::findLocalServer | addr: ");
454     msg += addr;
455     msg += anna::functions::asText(" | port: ", port);
456     msg += " | LocalServer not found";
457     anna::RuntimeException ex(msg, ANNA_FILE_LOCATION);
458
459     if(emode == anna::Exception::Mode::Throw)
460       throw ex;
461
462     ex.trace();
463   }
464
465   return NULL;
466 }
467
468
469 ServerSession* Engine::findServerSession(int socketId, anna::Exception::Mode::_v emode) throw(anna::RuntimeException) {
470   anna::Guard guard(this, "anna::diameter::comm::Engine::findServerSession");
471   ServerSession *result;
472
473   // Search at each local server:
474   for(localServer_iterator it = localServer_begin(), maxii = localServer_end(); it != maxii; it ++) {
475     result = localServer(it)->findServerSession(socketId, anna::Exception::Mode::Ignore);
476
477     if(result) return result;
478   }
479
480   if(emode != anna::Exception::Mode::Ignore) {
481     string msg("diameter::comm::Engine::findServerSession | socketId: ");
482     msg += anna::functions::asString(socketId);
483     msg += " | ServerSession not found";
484     anna::RuntimeException ex(msg, ANNA_FILE_LOCATION);
485
486     if(emode == anna::Exception::Mode::Throw)
487       throw ex;
488
489     ex.trace();
490   }
491
492   return NULL;
493 }
494
495
496 void Engine::closeClientSession(ClientSession* clientSession, bool destroy)
497 throw(anna::RuntimeException) {
498   if(clientSession == NULL)
499     return;
500
501   LOGDEBUG(
502     string msg("diameter::comm::Engine::closeClientSession | ");
503     msg += clientSession->asString();
504     msg += " | Destroy: ";
505     msg += (destroy ? "yes" : "no");
506     anna::Logger::debug(msg, ANNA_FILE_LOCATION);
507   );
508   anna::Guard guard(this, "anna::diameter::comm::Engine::closeClientSession");
509   clientSession_iterator ii = clientSession_find(clientSession->getKey());
510
511   if(ii == clientSession_end())
512     return;
513
514   try {
515     clientSession->setState(ClientSession::State::Closing);
516
517     if(destroy) clientSession->setAutoRecovery(false);
518
519     clientSession->unbind(destroy /* destroy needs to perform immediate close */);
520
521     if(!destroy) return;
522
523     releaseClientSession(clientSession);
524   } catch(anna::RuntimeException& ex) {
525     ex.trace();
526   }
527
528   a_clientSessions.erase(ii);
529 }
530
531
532
533
534 void Engine::closeServer(Server* server, bool destroy)
535 throw(anna::RuntimeException) {
536   if(server == NULL)
537     return;
538
539   LOGDEBUG(
540     string msg("diameter::comm::Engine::closeServer | ");
541     msg += server->asString();
542     msg += " | Destroy: ";
543     msg += (destroy ? "yes" : "no");
544     anna::Logger::debug(msg, ANNA_FILE_LOCATION);
545   );
546   anna::Guard guard(this, "anna::diameter::comm::Engine::closeServer");
547   server_iterator ii = server_find(server->a_socket);
548
549   if(ii == server_end())
550     return;
551
552   try {
553     server->close(destroy);
554
555     if(!destroy) return;
556
557     releaseServer(server);
558   } catch(anna::RuntimeException& ex) {
559     ex.trace();
560   }
561
562   a_servers.erase(ii);
563 }
564
565
566 void Engine::closeEntity(Entity* entity, bool destroy)
567 throw(anna::RuntimeException) {
568   if(entity == NULL)
569     return;
570
571   LOGDEBUG(
572     string msg("diameter::comm::Engine::closeEntity | ");
573     msg += entity->asString();
574     msg += " | Destroy: ";
575     msg += (destroy ? "yes" : "no");
576     anna::Logger::debug(msg, ANNA_FILE_LOCATION);
577   );
578   anna::Guard guard(this, "anna::diameter::comm::Engine::closeEntity");
579   entity_iterator ii = entity_find(entity->a_socketListLiteral);
580
581   if(ii == entity_end())
582     return;
583
584   try {
585     entity->close(destroy);
586
587     if(!destroy) return;
588
589     if(!entity->idle()) { entity->setDeprecated(true); return; }
590
591     releaseEntity(entity);
592   } catch(anna::RuntimeException& ex) {
593     ex.trace();
594   }
595
596   a_entities.erase(ii);
597 }
598
599
600
601 void Engine::closeLocalServer(LocalServer* localServer, bool destroy)
602 throw(anna::RuntimeException) {
603   if(localServer == NULL)
604     return;
605
606   LOGDEBUG(
607     string msg("diameter::comm::Engine::closeLocalServer | ");
608     msg += localServer->asString();
609     msg += " | Destroy: ";
610     msg += (destroy ? "yes" : "no");
611     anna::Logger::debug(msg, ANNA_FILE_LOCATION);
612   );
613   anna::Guard guard(this, "anna::diameter::comm::Engine::closeLocalServer");
614   localServer_iterator ii = localServer_find(localServer->getKey());
615
616   if(ii == localServer_end())
617     return;
618
619   try {
620     localServer->close();
621
622     if(!destroy) return;
623
624     releaseLocalServer(localServer);
625   } catch(anna::RuntimeException& ex) {
626     ex.trace();
627   }
628
629   a_localServers.erase(ii);
630 }
631
632
633
634 void Engine::closeEntities(bool destroy) throw(anna::RuntimeException) {
635   LOGMETHOD(anna::TraceMethod tttm("diameter::comm::Engine", "closeEntities", ANNA_FILE_LOCATION));
636   anna::Guard guard(this, "anna::diameter::comm::Engine::closeEntities");
637
638   for(entity_iterator it = entity_begin(), maxii = entity_end(); it != maxii; it ++)
639     closeEntity(entity(it), destroy);
640 }
641
642 void Engine::closeLocalServers(bool destroy) throw(anna::RuntimeException) {
643   LOGMETHOD(anna::TraceMethod tttm("diameter::comm::Engine", "closeLocalServers", ANNA_FILE_LOCATION));
644   anna::Guard guard(this, "anna::diameter::comm::Engine::closeLocalServers");
645
646   for(localServer_iterator it = localServer_begin(), maxii = localServer_end(); it != maxii; it ++)
647     closeLocalServer(localServer(it), destroy);
648 }
649
650 void Engine::eraseDeprecatedIdleEntities() throw() {
651   LOGMETHOD(anna::TraceMethod tttm("diameter::comm::Engine", "eraseDeprecatedIdleEntities", ANNA_FILE_LOCATION));
652   Entity *et;
653
654   for(entity_iterator it = entity_begin(), maxii = entity_end(); it != maxii; it ++) {
655     et = entity(it);
656
657     if(et->isDeprecated() && et->idle()) closeEntity(et, true /* destroy */);
658   }
659 }
660
661 int Engine::getOTARequestsForEntities() const throw() {
662   int result = 0;
663
664   for(const_entity_iterator it = entity_begin(), maxii = entity_end(); it != maxii; it ++)
665     result += entity(it)->getOTARequests();
666
667   return result;
668 }
669
670 int Engine::getOTARequestsForLocalServers() const throw() {
671   int result = 0;
672
673   for(const_localServer_iterator it = localServer_begin(), maxii = localServer_end(); it != maxii; it ++)
674     result += localServer(it)->getOTARequests();
675
676   return result;
677 }
678
679
680 void Engine::setRealm(const std::string & name) throw() {
681   a_realm = ((name != "") ? name : anna::functions::getDomainname());
682 }
683
684
685 void Engine::setHost(const std::string & name) throw() {
686   a_host = ((name != "") ? name : anna::functions::getHostname());
687 }
688
689
690
691 void Engine::raiseAutoRecovery(bool autoRecovery) throw(anna::RuntimeException) {
692   LOGMETHOD(anna::TraceMethod tttm("diameter::comm::Engine", "raiseAutoRecovery", ANNA_FILE_LOCATION));
693
694   for(entity_iterator it = entity_begin(), maxii = entity_end(); it != maxii; it ++)
695     entity(it)->raiseAutoRecovery(autoRecovery);
696 }
697
698 void Engine::do_stop()
699 throw() {
700   LOGMETHOD(anna::TraceMethod tttm("diameter::comm::Engine", "do_stop", ANNA_FILE_LOCATION));
701   close(true /* destroy */);
702 }
703
704 std::string Engine::asString(void) const throw() {
705   std::string trace;
706   trace =  "\n================================";
707   trace += "\nDiameter comm Engine information";
708   trace += "\n================================";
709   trace += "\nAutoBind: ";
710   trace += a_autoBind ? "yes" : "no";
711   trace += "\nMaxConnectionDelay: ";
712   trace += a_maxConnectionDelay.asString();
713   trace += "\nAvailable for entities: ";
714   trace += a_availableForEntities ? "yes" : "no";
715   trace += "\nAvailable for local servers: ";
716   trace += a_availableForLocalServers ? "yes" : "no";
717   trace += "\nOTA requests: ";
718   trace += anna::functions::asString("%d%s", getOTARequests(), idle() ? " (idle)" : "");
719   trace += "\nOTA requests for entities: ";
720   trace += anna::functions::asString("%d%s", getOTARequestsForEntities(), idleForEntities() ? " (idle)" : "");
721   trace += "\nOTA requests for local servers: ";
722   trace += anna::functions::asString("%d%s", getOTARequestsForLocalServers(), idleForLocalServers() ? " (idle)" : "");
723   // Entities
724   trace += "\nNumber of entities: ";
725   trace += anna::functions::asString(a_entities.size());
726
727   for(const_entity_iterator it = entity_begin(); it != entity_end(); it++) {
728     trace += "\n";
729     trace += entity(it)->asString();
730   }
731
732   // Server sockets
733   trace += "\nNumber of LocalServers: ";
734   trace += anna::functions::asString(a_localServers.size());
735
736   for(const_localServer_iterator it = localServer_begin(); it != localServer_end(); it++) {
737     trace += "\n";
738     trace += localServer(it)->asString();
739   }
740
741   return trace;
742 }
743
744
745 anna::xml::Node* Engine::asXML(anna::xml::Node* parent) const
746 throw() {
747   parent = anna::app::Component::asXML(parent);
748   anna::xml::Node* result = parent->createChild("diameter.comm.Engine");
749   result->createAttribute("AutoBind", a_autoBind ? "yes" : "no");
750   result->createAttribute("MaxConnectionDelay", a_maxConnectionDelay.asString());
751   result->createAttribute("AvailableForEntities", a_availableForEntities ? "yes" : "no");
752   result->createAttribute("AvailableForLocalServers", a_availableForLocalServers ? "yes" : "no");
753   result->createAttribute("OTArequests", anna::functions::asString("%d%s", getOTARequests(), idle() ? " (idle)" : ""));
754   result->createAttribute("OTArequestsForEntities", anna::functions::asString("%d%s", getOTARequestsForEntities(), idleForEntities() ? " (idle)" : ""));
755   result->createAttribute("OTArequestsForLocalServers", anna::functions::asString("%d%s", getOTARequestsForLocalServers(), idleForLocalServers() ? " (idle)" : ""));
756   result->createAttribute("NumberOfEntities", a_entities.size());
757   anna::xml::Node* entities = result->createChild("Engine.Entities");
758
759   for(const_entity_iterator it = entity_begin(); it != entity_end(); it++)
760     entity(it)->asXML(entities);
761
762   result->createAttribute("NumberOfLocalServers", a_localServers.size());
763   anna::xml::Node* localServers = result->createChild("Engine.LocalServers");
764
765   for(const_localServer_iterator it = localServer_begin(); it != localServer_end(); it++)
766     localServer(it)->asXML(localServers);
767
768   return result;
769 }
770
771 Engine::clientSession_iterator Engine::clientSession_find(const clientSession_key &key) throw() {
772   return a_clientSessions.find(key);
773 }
774
775 Engine::server_iterator Engine::server_find(const server_key &key) throw() {
776   return a_servers.find(key);
777 }
778
779 Engine::entity_iterator Engine::entity_find(const entity_key &key) throw() {
780   return a_entities.find(key);
781 }
782
783 Engine::localServer_iterator Engine::localServer_find(const socket_t &key) throw() {
784   return a_localServers.find(key);
785 }
786
787 Engine::entity_key Engine::getEntityKey(const std::string & addr1, int port1, const std::string & addr2, int port2) const throw() {
788   socket_v dualList;
789   dualList.push_back(socket_t(addr1, port1));
790   dualList.push_back(socket_t(addr2, port2));
791   return (getEntityKey(dualList));
792 }
793
794 Engine::entity_key Engine::getEntityKey(const socket_v &v) const throw() {
795   std::string result;
796   socket_v::const_iterator it;
797   socket_v::const_iterator it_min(v.begin());
798   socket_v::const_iterator it_max(v.end());
799
800   for(it = it_min; it != it_max; it++) {
801     result += anna::functions::socketLiteralAsString((*it).first, (*it).second);
802     result += " ";
803   }
804
805   result.erase(result.size() - 1, 1);  // remove last space
806   //return anna::functions::exclusiveHash(result);
807   return result;
808 }
809
810
811 void Engine::availabilityLostForEntities() throw() {
812   a_availableForEntities = false;
813   LOGDEBUG(
814     std::string msg = "diameter::comm::Engine { Realm: ";
815     msg += getRealm();
816     msg += " } has lost its availability for entities";
817     anna::Logger::debug(msg, ANNA_FILE_LOCATION);
818   );
819   // OAM
820   OamModule &oamModule = OamModule::instantiate();
821   oamModule.activateAlarm(OamModule::Alarm::c_LostAvailabilityOverEntitiesForEngineWithClassName__s__, getClassName());
822   oamModule.count(OamModule::Counter::LostAvailabilityOverEngineForEntities);
823   // Virtual
824   availabilityLostForEntities(this);
825 }
826
827
828 void Engine::availabilityRecoveredForEntities() throw() {
829   a_availableForEntities = true;
830   LOGDEBUG(
831     std::string msg = "diameter::comm::Engine { Realm: ";
832     msg += getRealm();
833     msg += " } has recovered its availability for entities";
834     anna::Logger::debug(msg, ANNA_FILE_LOCATION);
835   );
836   // OAM
837   OamModule &oamModule = OamModule::instantiate();
838   oamModule.cancelAlarm(OamModule::Alarm::c_LostAvailabilityOverEntitiesForEngineWithClassName__s__, getClassName());
839   oamModule.count(OamModule::Counter::RecoveredAvailabilityOverEngineForEntities);
840   // Virtual
841   availabilityRecoveredForEntities(this);
842 }
843
844
845 void Engine::availabilityLostForLocalServers() throw() {
846   a_availableForLocalServers = false;
847   LOGDEBUG(
848     std::string msg = "diameter::comm::Engine { Realm: ";
849     msg += getRealm();
850     msg += " } has lost its availability for local servers";
851     anna::Logger::debug(msg, ANNA_FILE_LOCATION);
852   );
853   // OAM
854   OamModule &oamModule = OamModule::instantiate();
855   oamModule.activateAlarm(OamModule::Alarm::c_LostAvailabilityOverLocalServersForEngineWithClassName__s__, getClassName());
856   oamModule.count(OamModule::Counter::LostAvailabilityOverEngineForLocalServers);
857   // Virtual
858   availabilityLostForLocalServers(this);
859 }
860
861
862 void Engine::availabilityRecoveredForLocalServers() throw() {
863   a_availableForLocalServers = true;
864   LOGDEBUG(
865     std::string msg = "diameter::comm::Engine { Realm: ";
866     msg += getRealm();
867     msg += " } has recovered its availability for local servers";
868     anna::Logger::debug(msg, ANNA_FILE_LOCATION);
869   );
870   // OAM
871   OamModule &oamModule = OamModule::instantiate();
872   oamModule.cancelAlarm(OamModule::Alarm::c_LostAvailabilityOverLocalServersForEngineWithClassName__s__, getClassName());
873   oamModule.count(OamModule::Counter::RecoveredAvailabilityOverEngineForLocalServers);
874   // Virtual
875   availabilityRecoveredForLocalServers(this);
876 }
877
878
879 bool Engine::refreshAvailabilityForEntities() throw() {
880   // Here available
881   if(a_availableForEntities) {  // check not-bound state for all client-sessions:
882     bool isolate = true;
883
884     for(const_entity_iterator it = entity_begin(); it != entity_end(); it++)
885       if(entity(it)->isAvailable()) { isolate = false; break; }
886
887     if(isolate) {
888       availabilityLostForEntities();
889       return true;
890     }
891
892     return false;
893   }
894
895   // Here not available
896   for(const_entity_iterator it = entity_begin(); it != entity_end(); it++)
897     if(entity(it)->isAvailable()) {
898       availabilityRecoveredForEntities();
899       return true;
900     }
901
902   return false;
903 }
904
905 bool Engine::refreshAvailabilityForLocalServers() throw() {
906   // Here available
907   if(a_availableForLocalServers) {  // check not-bound state for all client-sessions:
908     bool isolate = true;
909
910     for(const_localServer_iterator it = localServer_begin(); it != localServer_end(); it++)
911       if(localServer(it)->isAvailable()) { isolate = false; break; }
912
913     if(isolate) {
914       availabilityLostForLocalServers();
915       return true;
916     }
917
918     return false;
919   }
920
921   // Here not available
922   for(const_localServer_iterator it = localServer_begin(); it != localServer_end(); it++)
923     if(localServer(it)->isAvailable()) {
924       availabilityRecoveredForLocalServers();
925       return true;
926     }
927
928   return false;
929 }
930
931
932 void Engine::readDPA(anna::DataBlock &dpa, const anna::DataBlock & dpr) throw() {
933   // Default DPA implementation:
934   //
935   //   'Disconnect-Peer-Answer' (282,answer)
936   //        {Result-Code}...................................(268,0)
937   //        {Origin-Host}...................................(264,0)
938   //        {Origin-Realm}..................................(296,0)
939   //        [Error-Message].................................(281,0)
940   //       *[Failed-AVP]....................................(279,0)
941   try {
942     anna::diameter::codec::Message diameterDPA;
943     anna::diameter::codec::Avp avpRC;
944     anna::diameter::codec::Avp avpOH;
945     anna::diameter::codec::Avp avpOR;
946     // Message header
947     diameterDPA.setId(anna::diameter::helpers::base::COMMANDID__Disconnect_Peer_Answer);
948     diameterDPA.setVersion(1);
949     diameterDPA.setApplicationId(codec::functions::getApplicationId(dpr));
950     diameterDPA.setHopByHop(codec::functions::getHopByHop(dpr));
951     diameterDPA.setEndToEnd(codec::functions::getEndToEnd(dpr));
952     // Result-Code
953     avpRC.setId(anna::diameter::helpers::base::AVPID__Result_Code);
954     avpRC.setMandatoryBit();
955     avpRC.getUnsigned32()->setValue(helpers::base::AVPVALUES__Result_Code::DIAMETER_SUCCESS);
956     // Origin-Host
957     avpOH.setId(anna::diameter::helpers::base::AVPID__Origin_Host);
958     avpOH.setMandatoryBit();
959     avpOH.getDiameterIdentity()->fromPrintableString(a_host.c_str());
960     // Origin-Realm
961     avpOR.setId(anna::diameter::helpers::base::AVPID__Origin_Realm);
962     avpOR.setMandatoryBit();
963     avpOR.getDiameterIdentity()->fromPrintableString(a_realm.c_str());
964     diameterDPA.addAvp(&avpRC);
965     diameterDPA.addAvp(&avpOH);
966     diameterDPA.addAvp(&avpOR);
967     // Encode
968     dpa = diameterDPA.code();
969   } catch(anna::RuntimeException &ex) {
970     ex.trace();
971   }
972 }
973
974
975 void Engine::readCEA(anna::DataBlock &cea, const anna::DataBlock & cer) throw() {
976   // Default CEA implementation:
977   //
978   //   'Capabilities-Exchange-Answer' (257,answer)
979   //        {Result-Code}...................................(268,0)
980   //        {Origin-Host}...................................(264,0)
981   //        {Origin-Realm}..................................(296,0)
982   //      1*{Host-IP-Address}...............................(257,0)
983   //        {Vendor-Id}.....................................(266,0)
984   //        {Product-Name}..................................(269,0)
985   //        [Origin-State-Id]...............................(278,0)
986   //        [Error-Message].................................(281,0)
987   //       *[Failed-AVP]....................................(279,0)
988   //       *[Supported-Vendor-Id]...........................(265,0)
989   //       *[Auth-Application-Id]...........................(258,0)
990   //       *[Inband-Security-Id]............................(299,0)
991   //       *[Acct-Application-Id]...........................(259,0)
992   //        [Vendor-Specific-Application-Id]................(260,0)
993   //        [Firmware-Revision].............................(267,0)
994   //       *[AVP]...........................................(0,0)
995   try {
996     anna::diameter::codec::Message diameterCEA;
997     anna::diameter::codec::Avp avpRC;
998     anna::diameter::codec::Avp avpOH;
999     anna::diameter::codec::Avp avpOR;
1000     // Message header
1001     diameterCEA.setId(anna::diameter::helpers::base::COMMANDID__Capabilities_Exchange_Answer);
1002     diameterCEA.setVersion(1);
1003     diameterCEA.setApplicationId(codec::functions::getApplicationId(cer));
1004     diameterCEA.setHopByHop(codec::functions::getHopByHop(cer));
1005     diameterCEA.setEndToEnd(codec::functions::getEndToEnd(cer));
1006     // Result-Code
1007     avpRC.setId(anna::diameter::helpers::base::AVPID__Result_Code);
1008     avpRC.setMandatoryBit();
1009     avpRC.getUnsigned32()->setValue(helpers::base::AVPVALUES__Result_Code::DIAMETER_SUCCESS); // re-implementations could analyze CER to accept or not
1010     // Origin-Host
1011     avpOH.setId(anna::diameter::helpers::base::AVPID__Origin_Host);
1012     avpOH.setMandatoryBit();
1013     avpOH.getDiameterIdentity()->fromPrintableString(a_host.c_str());
1014     // Origin-Realm
1015     avpOR.setId(anna::diameter::helpers::base::AVPID__Origin_Realm);
1016     avpOR.setMandatoryBit();
1017     avpOR.getDiameterIdentity()->fromPrintableString(a_realm.c_str());
1018     diameterCEA.addAvp(&avpRC);
1019     diameterCEA.addAvp(&avpOH);
1020     diameterCEA.addAvp(&avpOR);
1021     // Host-IP-Address
1022     std::string hostIP = anna::functions::getHostnameIP(); // Address
1023     diameterCEA.addAvp(anna::diameter::helpers::base::AVPID__Host_IP_Address)->getAddress()->fromPrintableString(hostIP.c_str());
1024     // Vendor-Id
1025     int vendorId = anna::diameter::helpers::VENDORID__tgpp; // Unsigned32
1026     diameterCEA.addAvp(anna::diameter::helpers::base::AVPID__Vendor_Id)->getUnsigned32()->setValue(vendorId);
1027     // Product-Name
1028     std::string productName = "OCS Diameter Server"; // UTF8String
1029     diameterCEA.addAvp(anna::diameter::helpers::base::AVPID__Product_Name)->getUTF8String()->setValue(productName);
1030     // Encode
1031     cea = diameterCEA.code();
1032   } catch(anna::RuntimeException &ex) {
1033     ex.trace();
1034   }
1035 }
1036
1037
1038 void Engine::readDWA(anna::DataBlock &dwa, const anna::DataBlock & dwr) throw() {
1039   // Default DWA implementation:
1040   //
1041   //   'Device-Watchdog-Answer' (280,answer)
1042   //        {Result-Code}...................................(268,0)
1043   //        {Origin-Host}...................................(264,0)
1044   //        {Origin-Realm}..................................(296,0)
1045   //        [Error-Message].................................(281,0)
1046   //       *[Failed-AVP]....................................(279,0)
1047   //        [Origin-State-Id]...............................(278,0)
1048   try {
1049     anna::diameter::codec::Message diameterDWA;
1050     anna::diameter::codec::Avp avpRC;
1051     anna::diameter::codec::Avp avpOH;
1052     anna::diameter::codec::Avp avpOR;
1053     // Message header
1054     diameterDWA.setId(anna::diameter::helpers::base::COMMANDID__Device_Watchdog_Answer);
1055     diameterDWA.setVersion(1);
1056     diameterDWA.setApplicationId(codec::functions::getApplicationId(dwr));
1057     diameterDWA.setHopByHop(codec::functions::getHopByHop(dwr));
1058     diameterDWA.setEndToEnd(codec::functions::getEndToEnd(dwr));
1059     // Result-Code
1060     avpRC.setId(anna::diameter::helpers::base::AVPID__Result_Code);
1061     avpRC.setMandatoryBit();
1062     avpRC.getUnsigned32()->setValue(helpers::base::AVPVALUES__Result_Code::DIAMETER_SUCCESS);
1063     // Origin-Host
1064     avpOH.setId(anna::diameter::helpers::base::AVPID__Origin_Host);
1065     avpOH.setMandatoryBit();
1066     avpOH.getDiameterIdentity()->fromPrintableString(a_host.c_str());
1067     // Origin-Realm
1068     avpOR.setId(anna::diameter::helpers::base::AVPID__Origin_Realm);
1069     avpOR.setMandatoryBit();
1070     avpOR.getDiameterIdentity()->fromPrintableString(a_realm.c_str());
1071     diameterDWA.addAvp(&avpRC);
1072     diameterDWA.addAvp(&avpOH);
1073     diameterDWA.addAvp(&avpOR);
1074     // Encode
1075     dwa = diameterDWA.code();
1076   } catch(anna::RuntimeException &ex) {
1077     ex.trace();
1078   }
1079 }
1080
1081 void Engine::resetStatistics() throw() {
1082   for(server_iterator it = server_begin(), maxii = server_end(); it != maxii; it ++)
1083     server(it)->resetStatistics();
1084
1085   for(localServer_iterator it = localServer_begin(), maxii = localServer_end(); it != maxii; it ++)
1086     localServer(it)->resetStatistics();
1087 }
1088
1089