Register accumulators on stat engine to centralize reports. TODO remove from engine...
[anna.git] / source / diameter.comm / LocalServer.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/OamModule.hpp>
10 #include <anna/diameter.comm/Engine.hpp>
11 #include <anna/diameter.comm/LocalServer.hpp>
12 #include <anna/diameter.comm/ServerSession.hpp>
13 #include <anna/diameter.comm/ServerSocket.hpp>
14 #include <anna/core/functions.hpp>
15 #include <anna/statistics/Engine.hpp>
16 #include <anna/diameter.comm/TimerManager.hpp>
17
18 #include <anna/core/tracing/Logger.hpp>
19 #include <anna/core/tracing/TraceMethod.hpp>
20 #include <anna/core/functions.hpp>
21 #include <anna/app/functions.hpp>
22 #include <anna/xml/Node.hpp>
23 #include <anna/comm/Communicator.hpp>
24 #include <anna/comm/Network.hpp>
25 #include <anna/comm/Host.hpp>
26 #include <anna/comm/ClientSocket.hpp>
27
28 // STL
29 #include <string>
30
31
32
33 using namespace anna::diameter::comm;
34
35 LocalServer::LocalServer() :
36   //a_key(key),
37   a_description(""),
38   a_maxConnections(-1),
39   a_currentConnections(0),
40   a_allowedInactivityTime(ServerSession::DefaultAllowedInactivityTime),
41   a_engine(NULL),
42   a_serverSocket(NULL),
43   a_category(0),
44   a_lock(false),
45   a_available(false),
46   a_lastUsedResource(NULL) {
47   a_statisticsAccumulator = anna::statistics::Engine::instantiate().createAccumulator();
48 }
49
50
51 void LocalServer::initializeStatisticConcepts() throw() {
52   // Realm name
53   std::string realmName = a_engine ? a_engine->getRealm() : "unknown"; // it should be known (createServer)
54
55   // Statistics:
56   anna::statistics::Engine& statsEngine = anna::statistics::Engine::instantiate();
57   // Concepts descriptions:
58   std::string serverAsString = anna::functions::socketLiteralAsString(a_key.first, a_key.second);
59   std::string c1desc = "Diameter processing time (for requests) at clients connected to "; c1desc += serverAsString; c1desc += " for realm '"; c1desc += realmName; c1desc += "'";
60   std::string c2desc = "Diameter message sizes received from clients connected to "; c2desc += serverAsString; c2desc += " for realm '"; c2desc += realmName; c2desc += "'";
61   // Registering
62   a_processing_time__StatisticConceptId = statsEngine.addConcept(c1desc.c_str(), "ms", true/* integer values */);
63   a_received_message_size__StatisticConceptId = statsEngine.addConcept(c2desc.c_str(), "bytes", true/* integer values */);
64 }
65
66 void LocalServer::resetStatistics() throw() {
67   a_statisticsAccumulator->reset();
68 }
69
70 void LocalServer::updateProcessingTimeStatisticConcept(const double &value) throw() {
71   a_statisticsAccumulator->process(a_processing_time__StatisticConceptId, value);
72   LOGDEBUG(anna::Logger::debug(a_statisticsAccumulator->asString(), ANNA_FILE_LOCATION));
73 }
74
75 void LocalServer::updateReceivedMessageSizeStatisticConcept(const double &value) throw() {
76   a_statisticsAccumulator->process(a_received_message_size__StatisticConceptId, value);
77   //LOGDEBUG(anna::Logger::debug(a_statisticsAccumulator->asString(), ANNA_FILE_LOCATION));
78 }
79
80
81 ServerSession* LocalServer::allocateServerSession() throw() { return a_serverSessionsRecycler.create(); }
82 void LocalServer::releaseServerSession(ServerSession *serverSession) throw() { a_serverSessionsRecycler.release(serverSession); }
83
84
85 LocalServer::serverSession_iterator LocalServer::serverSession_find(const serverSession_key &key) throw() {
86   return a_serverSessions.find(key);
87 }
88
89
90 LocalServer::serverSession_key LocalServer::getServerSessionKey(const anna::comm::ClientSocket &clientSocket) const throw() {
91   return (anna::functions::exclusiveHash(clientSocket.getRemoteAccessPoint().getINetAddress().serialize()));
92 }
93
94
95 void LocalServer::availabilityLost() throw() {
96   a_available = false;
97   std::string socket = anna::functions::socketLiteralAsString(a_key.first, a_key.second);
98   LOGDEBUG(
99     std::string msg = "diameter::comm::LocalServer { Socket: ";
100     msg += socket;
101     msg += " } has lost its availability";
102     anna::Logger::debug(msg, ANNA_FILE_LOCATION);
103   );
104   // OAM
105   OamModule &oamModule = OamModule::instantiate();
106   oamModule.activateAlarm(OamModule::Alarm::c_LostAvailabilityOverLocalServerDefinedAs__s__, socket.c_str());
107   oamModule.count(OamModule::Counter::LostAvailabilityOverLocalServer);
108   a_engine->availabilityLost(this);
109   a_engine->refreshAvailabilityForLocalServers();
110 }
111
112
113 void LocalServer::availabilityRecovered() throw() {
114   a_available = true;
115   std::string socket = anna::functions::socketLiteralAsString(a_key.first, a_key.second);
116   LOGDEBUG(
117     std::string msg = "diameter::comm::LocalServer { Socket: ";
118     msg += socket;
119     msg += " } has recovered its availability";
120     anna::Logger::debug(msg, ANNA_FILE_LOCATION);
121   );
122   // OAM
123   OamModule &oamModule = OamModule::instantiate();
124   oamModule.cancelAlarm(OamModule::Alarm::c_LostAvailabilityOverLocalServerDefinedAs__s__, socket.c_str());
125   oamModule.count(OamModule::Counter::RecoveredAvailabilityOverLocalServer);
126   a_engine->availabilityRecovered(this);
127   a_engine->refreshAvailabilityForLocalServers();
128 }
129
130
131
132 bool LocalServer::refreshAvailability() throw() {
133   // Here available
134   if(a_available) {  // check not-bound state for all server-sessions:
135 //      bool isolate = true;
136 //
137 //      for (const_serverSession_iterator it = serverSession_begin(); it != serverSession_end(); it++)
138 //         if (serverSession(it)->getState() != ServerSession::State::Closed) { isolate = false; break; }
139 //
140 //      if (isolate) {
141 // El problema de lo anterior, es que cuando se acepta una conexion, aun no ha llegado el CER (receive). Un server session
142 // esta en estado "Bound" cuando llega dicho CER y consecuentemente envio un CEA. Nos basaremos en 'a_currentConnections':
143     if(a_currentConnections == 0) {
144       availabilityLost();
145       return true;
146     }
147
148     return false;
149   }
150
151   // Here not available
152 //   for (const_serverSession_iterator it = serverSession_begin(); it != serverSession_end(); it++)
153 //      if (serverSession(it)->getState() == ServerSession::State::Bound) {
154   if(a_currentConnections > 0) {  // really == 0
155     availabilityRecovered();
156     return true;
157   }
158
159   return false;
160 }
161
162
163 void LocalServer::enable(bool unlock) throw(anna::RuntimeException) {
164   // Unlock ?
165   if(unlock) a_lock = false;
166
167   if(a_lock) return;
168
169   if(a_serverSocket && a_serverSocket->isOpened()) return;  // communicator attach twice gets poll bad file descriptor and application stops !
170
171   // Resolve local address:
172   anna::comm::Network& network = anna::comm::Network::instantiate();
173   // Little tricky:
174   anna::comm::Host *host = network.resolve(a_key.first /* addr */);
175   const anna::comm::Device *device = *(host->device_begin());
176   anna::comm::INetAddress localAddress(device, a_key.second /* port */);
177   // Create server socket and assign receiver factory
178   a_serverSocket = new ServerSocket(localAddress, this);
179   a_serverSocket->setCategory(a_category);
180   attach();
181 }
182
183 void LocalServer::attach() throw() {
184   try {
185     // Attach to communicator
186     anna::comm::Communicator * communicator = anna::app::functions::component <anna::comm::Communicator> (ANNA_FILE_LOCATION);
187     communicator->attach((anna::comm::ServerSocket*)a_serverSocket); // invokes handler insert and then initialize -> server socket bind (*)
188     // OAM
189     OamModule &oamModule = OamModule::instantiate();
190     oamModule.count(OamModule::Counter::ServerSocketsOpened);
191   } catch(anna::RuntimeException& ex) {
192     ex.trace(); // fails on (*) (i.e. Address already in use), within communicator attach
193     attachPlanning();
194     a_serverSocket->close();
195   }
196 }
197
198 void LocalServer::attachPlanning() throw() {
199   LOGMETHOD(anna::TraceMethod tttm("diameter::comm::LocalServer", "attachPlanning", ANNA_FILE_LOCATION));
200
201   try {
202     TimerManager::instantiate().createTimer(this);
203   } catch(anna::RuntimeException& ex) {
204     ex.trace();
205     anna::Logger::error("CAPTURED EXCEPTION activating attachPlanning timer", ANNA_FILE_LOCATION);
206   }
207 }
208
209
210 void LocalServer::disable(bool lock) throw(anna::RuntimeException) {
211   // Permanent ?
212   a_lock = lock;
213   anna::comm::Communicator * communicator = anna::app::functions::component <anna::comm::Communicator> (ANNA_FILE_LOCATION);
214   communicator->detach((anna::comm::ServerSocket*)a_serverSocket);
215   //delete(a_serverSocket);
216   // OAM
217   OamModule &oamModule = OamModule::instantiate();
218   oamModule.count(OamModule::Counter::ServerSocketsClosed);
219 }
220
221
222 void LocalServer::lostConnection() throw() {
223   a_currentConnections--;
224   enable();
225 }
226
227
228 void LocalServer::newConnection() throw(anna::RuntimeException) {
229   a_currentConnections++;
230
231   // Check capacity
232   if(a_currentConnections == a_maxConnections) {
233     LOGWARNING(anna::Logger::warning("The maximum number of connections allowed over diameter server socket have already been served", ANNA_FILE_LOCATION));
234     disable();
235   }
236
237   // Inform local server (availability changes):
238   bool changes = refreshAvailability();
239   // OAM
240   OamModule &oamModule = OamModule::instantiate();
241   oamModule.count(OamModule::Counter::CreatedConnectionForServerSession);
242 }
243
244
245
246 ServerSession *LocalServer::createServerSession(const anna::comm::ClientSocket &clientSocket) throw(anna::RuntimeException) {
247   LOGMETHOD(anna::TraceMethod tttm("diameter::comm::LocalServer", "createServerSession", ANNA_FILE_LOCATION));
248   ServerSession* result(NULL);
249   // First erase deprecated ones:
250   std::vector<const ServerSession*> deprecated_server_sessions;
251   const ServerSession* ss;
252
253   for(const_serverSession_iterator it = serverSession_begin(); it != serverSession_end(); it++) {
254     ss = serverSession(it);
255
256     if(ss->a_deprecated)
257       deprecated_server_sessions.push_back(ss);
258   }
259
260   std::vector<const ServerSession*>::iterator dc_ncit;
261   std::vector<const ServerSession*>::iterator dc_min(deprecated_server_sessions.begin());
262   std::vector<const ServerSession*>::iterator dc_max(deprecated_server_sessions.end());
263   serverSession_iterator ii;
264
265   for(dc_ncit = dc_min; dc_ncit != dc_max; dc_ncit++) {
266     ii = serverSession_find((*dc_ncit)->getSocketId());
267     a_serverSessions.erase(ii);
268   }
269
270   // End erase deprecated server sessions
271
272   if((result = allocateServerSession()) == NULL)
273     throw anna::RuntimeException("diameter::comm::LocalServer::allocateServerSession returns NULL", ANNA_FILE_LOCATION);
274
275   // Initialize:
276   result->initialize(); // warning: recycler does not initialize its objects and at least...
277   // Assignments (it could be done at allocate):
278   serverSession_key key = getServerSessionKey(clientSocket);
279   result->setAllowedInactivityTime(getAllowedInactivityTime());
280   result->setClientSocket((anna::comm::ClientSocket*)(&clientSocket));
281   result->a_parent = this;
282   result->a_socketId = key; // de momento...
283   result->initializeSequences(); // despu�s de asignar el LocalServer y el socketId (*)
284   // (*) 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)));
285   result->a_engine = a_engine;
286   a_serverSessions.insert(serverSession_value_type(key, result));
287   newConnection();
288   a_deliveryIterator = serverSession_begin();
289   return result;
290 }
291
292
293
294 void LocalServer::closeServerSession(ServerSession* serverSession)
295 throw(anna::RuntimeException) {
296   if(serverSession == NULL)
297     return;
298
299   LOGDEBUG(
300     std::string msg("diameter::comm::LocalServer::closeServerSession | ");
301     msg += serverSession->asString();
302 //      msg += " | Destroy: ";
303 //      msg += (destroy ? "yes" : "no");
304     anna::Logger::debug(msg, ANNA_FILE_LOCATION);
305   );
306   serverSession_iterator ii = serverSession_find(serverSession->getSocketId());
307
308   if(ii == serverSession_end())
309     return;
310
311   // Remove origin-realm / origin-host for server session in delivery map
312   // This is related to http://redmine.teslayout.com/issues/41
313   a_engine->manageDrDhServerSession(serverSession, false /* desregister */);
314
315   try {
316     //serverSession->setState(ServerSession::State::Closing); NOT MANAGED WITH SERVER SESSIONS
317     serverSession->unbind(true /* always forceDisconnect on server sessions ... */);
318     releaseServerSession(serverSession);
319   } catch(anna::RuntimeException& ex) {
320     ex.trace();
321   }
322
323   //a_serverSessions.erase(ii); // IMPORTANTE: posible fuente de cores de este tipo, en relacion con ServerSession::finalize() => delete(this)
324   //   #0  0x0000003ca1c2e26d in raise () from /lib64/tls/libc.so.6
325   //   (gdb) bt
326   //   #0  0x0000003ca1c2e26d in raise () from /lib64/tls/libc.so.6
327   //   #1  0x0000003ca1c2fa6e in abort () from /lib64/tls/libc.so.6
328   //   #2  0x0000003ca8cb1148 in __gnu_cxx::__verbose_terminate_handler () from /usr/lib64/libstdc++.so.6
329   //   #3  0x0000003ca8caf176 in __cxa_call_unexpected () from /usr/lib64/libstdc++.so.6
330   //   #4  0x0000003ca8caf1a3 in std::terminate () from /usr/lib64/libstdc++.so.6
331   //   #5  0x0000003ca8caf1b6 in std::terminate () from /usr/lib64/libstdc++.so.6
332   //   #6  0x0000003ca8caf0c8 in __cxa_call_unexpected () from /usr/lib64/libstdc++.so.6
333   //   #7  0x000000000047a4a7 in anna::diameter::comm::LocalServer::lostConnection (this=0x8aeb10) at comm.db/diameter.comm.LocalServer.cc:200
334   //   #8  0x000000000047a9e6 in anna::diameter::comm::LocalServer::closeServerSession (this=0x8aeb10, serverSession=0xc37a00)
335   //       at comm.db/diameter.comm.LocalServer.cc:275
336   //   #9  0x000000000048d288 in anna::diameter::comm::ServerSession::finalize (this=0xc37a00) at comm.db/diameter.comm.ServerSession.cc:510
337   //   #10 0x0000000000494e4f in anna::diameter::comm::ServerSessionReceiver::eventBreakLocalConnection (this=0xc119c0, clientSocket=@0xb0ea00)
338   // SOLUCION: no borrar aqui, marcar como "deprecated". Este estado no se necesita realmente puesto que nadie volvera a usar este recurso.
339   // Pero simplemente se podria usar para purgar mediante temporizacion (entonces s� se har�a el erase)
340   serverSession->a_deprecated = true;
341   // WE WILL ERASE AT createServerSession
342   a_deliveryIterator = serverSession_begin();
343   lostConnection();
344 }
345
346
347 ServerSession* LocalServer::findServerSession(int socketId, anna::Exception::Mode::_v emode)
348 throw(anna::RuntimeException) {
349   serverSession_iterator ii = serverSession_find(socketId);
350
351   if(ii != serverSession_end())
352     return serverSession(ii);
353
354   if(emode != anna::Exception::Mode::Ignore) {
355     std::string msg("diameter::comm::LocalServer::findServerSession | SocketId: ");
356     msg += anna::functions::asString(socketId);
357     msg += " | ServerSession not found";
358     anna::RuntimeException ex(msg, ANNA_FILE_LOCATION);
359
360     if(emode == anna::Exception::Mode::Throw)
361       throw ex;
362
363     ex.trace();
364   }
365
366   return NULL;
367 }
368
369 ServerSession* LocalServer::findServerSession(const anna::comm::ClientSocket &clientSocket, anna::Exception::Mode::_v emode)
370 throw(anna::RuntimeException) {
371   return findServerSession(getServerSessionKey(clientSocket), emode);
372 }
373
374
375 int LocalServer::getOTARequests() const throw() {
376   int result = 0;
377
378   for(const_serverSession_iterator it = serverSession_begin(); it != serverSession_end(); it++)
379     result += serverSession(it)->getOTARequests();
380
381   return result;
382 }
383
384 void LocalServer::close() throw(anna::RuntimeException) {
385   LOGMETHOD(anna::TraceMethod tttm("diameter::comm::LocalServer", "close", ANNA_FILE_LOCATION));
386   // Close listener (permanently to avoid reopening when local connections are being deleted):
387   disable(true /* lock */);
388
389   for(serverSession_iterator it = serverSession_begin(); it != serverSession_end(); it++)
390     closeServerSession(serverSession(it));
391 }
392
393
394 void LocalServer::setMaxConnections(int maxConnections) throw(anna::RuntimeException) {
395   LOGMETHOD(anna::TraceMethod tttm("anna::diameter::comm::LocalServer", "setMaxConnections", ANNA_FILE_LOCATION));
396
397   // Negative & initial
398   if(maxConnections < 0) {
399     LOGDEBUG(anna::Logger::debug("Provided negative value means no limit accepting connections over server socket. Opening listen port (if closed)...", ANNA_FILE_LOCATION));
400     a_maxConnections = -1;
401     enable();
402     return;
403   }
404
405   // No changes
406   if(maxConnections == a_maxConnections) {
407     LOGDEBUG(anna::Logger::debug("Provided equal to current. Ignore operation", ANNA_FILE_LOCATION));
408     return;
409   }
410
411   // No margin
412   if(maxConnections < a_currentConnections) {
413     std::string msg = "There are more current connections (";
414     msg += anna::functions::entriesAsString(a_currentConnections);
415     msg += ") than provided maximum (";
416     msg += anna::functions::entriesAsString(maxConnections);
417     msg += "). Command rejected (you should release connections before logical limitation)";
418     throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
419   }
420
421   // Updating
422   if(maxConnections > a_currentConnections) {
423     LOGDEBUG(anna::Logger::debug("Increasing connection margin (new limit is greater than current connections). Opening listen port (if closed)...", ANNA_FILE_LOCATION));
424     enable();
425   } else { // maxConnections == a_currentConnections: listen port must be closed if it is opened
426     LOGDEBUG(
427
428       if(maxConnections == 0)
429       anna::Logger::debug("Provided zero value means disabling diameter server", ANNA_FILE_LOCATION);
430       anna::Logger::debug("Zeroing connections margin (new limit is equal to current connections). Closing listen port (if opened)...", ANNA_FILE_LOCATION);
431     );
432
433     disable();
434   }
435
436   // Trace
437   LOGDEBUG(
438     std::string msg("Updating max connections from ");
439     msg += (a_maxConnections == -1) ? "'no limit'" : anna::functions::asString(a_maxConnections);
440     msg += " to ";
441     msg += (maxConnections == -1) ? "'no limit'" : anna::functions::asString(maxConnections);
442     anna::Logger::debug(msg, ANNA_FILE_LOCATION);
443   );
444   // Assignment
445   a_maxConnections = maxConnections;
446 }
447
448
449 bool LocalServer::send(const Message* message, int socketId) throw(anna::RuntimeException) {
450   LOGMETHOD(anna::TraceMethod tttm("diameter::comm::LocalServer", "send", ANNA_FILE_LOCATION));
451
452   if(!isAvailable()) {
453     LOGWARNING(
454       std::string msg = "The local server ";
455
456     if(a_description != "") {
457     msg += "'";
458     msg += a_description;
459     msg += "' ";
460   }
461   msg += "is currently unavailable (no server sessions to send the message)";
462   anna::Logger::warning(msg, ANNA_FILE_LOCATION);
463   );
464     return false;
465   }
466
467   if(socketId != -1) {  // socket id provided
468     // Send (it was a request because of key (socketId) != -1, we forward the answer):
469     try {
470       ServerSession * fixedServerSession = a_engine->findServerSession(socketId); // exception if not found
471       fixedServerSession->send(message);
472       return true;
473     } catch(anna::RuntimeException &ex) {
474       std::string msg = "Cannot deliver answer through a fixed server session (socket id ";
475       msg += anna::functions::asString(socketId);
476       msg += "). Perhaps it existed but not now. Ignore";
477       anna::Logger::error(msg, ANNA_FILE_LOCATION);
478       ex.trace();
479       return false;
480     }
481   }
482
483   // Socket is not provided: use readSocketId
484   socketId = (a_currentConnections > 1) ? readSocketId(message) : -1; // optimization
485
486   if(a_deliveryIterator == serverSession_end()) a_deliveryIterator = serverSession_begin();
487
488   a_lastUsedResource = (*a_deliveryIterator).second;
489
490   if(socketId != -1) {
491     a_lastUsedResource = findServerSession(socketId); // exception if not found
492   } else { // Round Robin delivery between client-sessions
493     if(getCurrentConnections() != 1) {  // optimize
494       // Next server-session:
495       a_deliveryIterator++;
496     }
497   }
498
499   // Send:
500   try {
501     const Response* response = a_lastUsedResource->send(message);
502     return true; // no matter if response is NULL (answers, i.e.) or not.
503   } catch(anna::RuntimeException &ex) {
504     ex.trace();
505   }
506
507   // Here, sent has failed:
508   // OAM
509   OamModule &oamModule = OamModule::instantiate();
510   std::string socket = anna::functions::socketLiteralAsString(getKey().first, getKey().second);
511   oamModule.activateAlarm(OamModule::Alarm::UnableToDeliverDiameterMessageToClientFromLocalServer__s__, socket.c_str());
512   oamModule.count(OamModule::Counter::UnableToDeliverToClient);
513   return false;
514 }
515
516
517 bool LocalServer::broadcast(const Message* message) throw(anna::RuntimeException) {
518   LOGMETHOD(anna::TraceMethod tttm("diameter::comm::LocalServer", "broadcast", ANNA_FILE_LOCATION));
519   const Response* response;
520   bool allok = true;
521
522   for(serverSession_iterator it = serverSession_begin(); it != serverSession_end(); it++) {
523     try {
524       response = serverSession(it)->send(message);
525     } catch(anna::RuntimeException &ex) {
526       ex.trace();
527       allok = false;
528     }
529   }
530
531   return allok;
532 }
533
534 void LocalServer::eventPeerShutdown(const ServerSession* serverSession) throw() {
535   LOGWARNING(
536     std::string msg(serverSession->asString());
537     msg += " | eventPeerShutdown";
538     anna::Logger::warning(msg, ANNA_FILE_LOCATION);
539   );
540 }
541
542 void LocalServer::eventRequestRetransmission(const ServerSession* serverSession, Message *request) throw() {
543   LOGWARNING(
544     std::string msg(serverSession->asString());
545     msg += " | eventRequestRetransmission";
546     anna::Logger::warning(msg, ANNA_FILE_LOCATION);
547   );
548 }
549
550 std::string LocalServer::asString() const throw() {
551   std::string result("diameter::comm::LocalServer { ");
552   result += "Description: ";
553   result += (a_description != "") ? a_description : "undefined";
554   result += " | Available (any server session bound): ";
555   result += a_available ? "yes" : "no";
556   result += " | Max Connections: ";
557   result += anna::functions::asString(a_maxConnections);
558   result += " | Current Connections: ";
559   result += anna::functions::asString(a_currentConnections);
560   // Current connections ??
561   result += " | Allowed inactivity time for server sessions: ";
562   result += a_allowedInactivityTime.asString();
563   result += " | Server socket: ";
564   result += a_serverSocket ? a_serverSocket->asString() : "closed";
565   result += anna::functions::asString(" | OTA requests: %d%s", getOTARequests(), idle() ? " (idle)" : "");
566   result += " | Last Incoming Activity Time: ";
567   result += a_lastIncomingActivityTime.asString();
568   result += " | Last Outgoing Activity Time: ";
569   result += a_lastOutgoingActivityTime.asString();
570 //   result += "\n";
571 //   result += a_statisticsAccumulator->asString();
572   // ServerSessions only in xml
573   return result += " }";
574 }
575
576
577 anna::xml::Node* LocalServer::asXML(anna::xml::Node* parent) const throw() {
578   anna::xml::Node* result = parent->createChild("diameter.LocalServer");
579   result->createAttribute("Description", (a_description != "") ? a_description : "undefined");
580   result->createAttribute("Available", a_available ? "yes" : "no");
581   result->createAttribute("MaxConnections", a_maxConnections);
582   result->createAttribute("CurrentConnections", a_currentConnections);
583   // Current connections ??
584   result->createAttribute("AllowedInactivityTimeForServerSessions", a_allowedInactivityTime.asString());
585
586   if(a_serverSocket)
587     a_serverSocket->asXML(result);
588   else
589     result->createAttribute("ServerSocket", "closed");
590
591   result->createAttribute("OTArequests", anna::functions::asString("%d%s", getOTARequests(), idle() ? " (idle)" : ""));
592   result->createAttribute("LastIncomingActivityTime", a_lastIncomingActivityTime.asString());
593   result->createAttribute("LastOutgoingActivityTime", a_lastOutgoingActivityTime.asString());
594   // Statistics
595   anna::xml::Node* stats = result->createChild("Statistics");
596   a_statisticsAccumulator->asXML(stats);
597   anna::xml::Node* serverSessions = result->createChild("ServerSessions"); // LocalServer.ServerSessions
598
599   for(const_serverSession_iterator it = serverSession_begin(); it != serverSession_end(); it++)
600     serverSession(it)->asXML(serverSessions);
601
602   return result;
603 }
604
605 //------------------------------------------------------------------------------
606 //------------------------------------ LocalServer::updateIncomingActivityTime()
607 //------------------------------------------------------------------------------
608 void LocalServer::updateIncomingActivityTime() throw() {
609   a_lastIncomingActivityTime = anna::functions::millisecond();
610   LOGDEBUG
611   (
612     std::string msg = "Updated INCOMING activity on local server (milliseconds unix): ";
613     msg += anna::functions::asString(a_lastIncomingActivityTime.getValue());
614     anna::Logger::debug(msg, ANNA_FILE_LOCATION);
615   );
616 }
617
618
619 //------------------------------------------------------------------------------
620 //------------------------------------ LocalServer::updateOutgoingActivityTime()
621 //------------------------------------------------------------------------------
622 void LocalServer::updateOutgoingActivityTime(void) throw() {
623   a_lastOutgoingActivityTime = anna::functions::millisecond();
624   LOGDEBUG
625   (
626     std::string msg = "Updated OUTGOING activity on local server (milliseconds unix): ";
627     msg += anna::functions::asString(a_lastOutgoingActivityTime.getValue());
628     anna::Logger::debug(msg, ANNA_FILE_LOCATION);
629   );
630 }