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