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