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