Fix local server for multiple applications
[anna.git] / example / diameter / launcher / Launcher.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 // Standard
10 #include <sstream>      // std::istringstream
11 #include <iostream>     // std::cout
12 #include <math.h> // ceil
13 #include <climits>
14 #include <unistd.h> // chdir
15 //#include <regex> TODO: use this from gcc4.9.0: http://stackoverflow.com/questions/8060025/is-this-c11-regex-error-me-or-the-compiler
16 #include <stdio.h>
17
18 // Project
19 #include <anna/timex/Engine.hpp>
20 #include <anna/statistics/Engine.hpp>
21 #include <anna/diameter/codec/functions.hpp>
22 #include <anna/diameter/codec/Engine.hpp>
23 #include <anna/diameter/codec/EngineManager.hpp>
24 #include <anna/http/Transport.hpp>
25 #include <anna/diameter/stack/Engine.hpp>
26 #include <anna/diameter/helpers/base/functions.hpp>
27 #include <anna/time/functions.hpp>
28 #include <anna/diameter.comm/ApplicationMessageOamModule.hpp>
29 #include <anna/testing/defines.hpp>
30 #include <anna/xml/xml.hpp>
31 #include <anna/diameter.comm/OriginHost.hpp>
32 #include <anna/diameter.comm/OriginHostManager.hpp>
33
34 // Process
35 #include <Launcher.hpp>
36 #include <Procedure.hpp>
37 #include <EventOperation.hpp>
38 #include <MyDiameterEngine.hpp>
39 #include <anna/testing/TestManager.hpp>
40 #include <anna/testing/TestCase.hpp>
41
42
43 #define SIGUSR2_TASKS_INPUT_FILENAME "sigusr2.in"
44 #define SIGUSR2_TASKS_OUTPUT_FILENAME "sigusr2.out"
45
46
47 const char *ServicesDTD = "\
48 <?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\
49 <!-- Diameter services DTD -->\n\
50 \n\
51 <!ELEMENT services ((stack*, node*)|(node*, stack*))>\n\
52 \n\
53 <!ELEMENT stack EMPTY>\n\
54 <!ATTLIST stack id CDATA #REQUIRED dictionary CDATA #REQUIRED validationMode (BeforeEncoding | AfterDecoding | Always | Never) #IMPLIED validationDepth (Complete | FirstError) #IMPLIED fixMode (BeforeEncoding | AfterDecoding | Always | Never) #IMPLIED ignoreFlagsOnValidation (yes | no) #IMPLIED>\n\
55 <!--\n\
56    Stack record\n\
57 \n\
58     id:                      Normally the id corresponds to the Application-Id for which the dictionary provided is designed\n\
59                              (in multistack applications, it shall be mandatory respect such association to know the stack used\n\
60                              for processed messages).\n\
61     dictionary:              Path to the dictionary file.\n\
62     validationMode:          Sets the validation mode. Default is 'AfterDecoding'.\n\
63     validationDepth:         Sets the validation depth. Default is 'FirstError'.\n\
64     fixMode:                 Sets the fix mode. Default is 'BeforeEncoding'.\n\
65     ignoreFlagsOnValidation: Ignore flags during message validation. Default is 'no'.\n\
66 -->\n\
67 \n\
68 <!ELEMENT node EMPTY>\n\
69 <!ATTLIST node originHost CDATA #REQUIRED applicationId CDATA #REQUIRED originRealm CDATA #IMPLIED cer CDATA #IMPLIED dwr CDATA #IMPLIED cea CDATA #IMPLIED allowedInactivityTime CDATA #IMPLIED tcpConnectDelay CDATA #IMPLIED answersTimeout CDATA #IMPLIED ceaTimeout CDATA #IMPLIED watchdogPeriod CDATA #IMPLIED entity CDATA #IMPLIED entityServerSessions CDATA #IMPLIED diameterServer CDATA #IMPLIED diameterServerSessions CDATA #IMPLIED balance (yes | no) #IMPLIED sessionBasedModelsClientSocketSelection (SessionIdLowPart | SessionIdHighPart | SessionIdOptionalPart | RoundRobin) #IMPLIED retries CDATA #IMPLIED log CDATA #IMPLIED splitLog (yes | no) #IMPLIED detailedLog (yes | no) #IMPLIED dumpLog (yes | no) #IMPLIED burstLog (yes | no) #IMPLIED>\n\
70 <!--\n\
71    Node record\n\
72 \n\
73    originHost:                              Node identifier as diameter application host name.\n\
74    applicationId:                           The Application-Id provided must exists as a registered 'stack id'.\n\
75    originRealm:                             Origin-Realm (domain-name internally used by default).\n\
76 \n\
77 \n\
78    - Diameter clients:\n\
79 \n\
80    entity:                                  Target diameter entity (comma-separated '<address>:<port>' format).\n\
81                                             For example: 10.20.30.40:3868,10.20.30.41:3868. If missing, no entity will be enabled.\n\
82    entityServerSessions:                    Diameter entity server sessions (0: diameter entity disabled). Default value of 1.\n\
83    cer:                                     User defined CER path file to be encoded to establish diameter connections.\n\
84                                             If missing, will be harcoded.\n\
85    dwr:                                     User defined DWR path file to be encoded for diameter protocol keep alive.\n\
86                                             If missing, will be harcoded.\n\
87    tcpConnectDelay:                         Milliseconds to wait TCP connect to any server. If missing, default value of 200 will\n\
88                                             be assigned\n\
89    ceaTimeout:                              Milliseconds to wait CEA from diameter server. If missing, default value of 'answersTimeout'\n\
90                                             will be assigned.\n\
91    watchdogPeriod:                          Milliseconds for watchdog timer (Tw) for diameter keep-alive procedure. If missing, default\n\
92                                             value of 30000 will be assigned.\n\
93    balance:                                 Balance over entity servers instead of doing standard behaviour (first primary, secondary\n\
94                                             if fails, etc.). Default value 'no'.\n\
95    sessionBasedModelsClientSocketSelection: By default, round-robin will be applied for IEC model (SMS/MMS), and Session-Id Low Part\n\
96                                             will be analyzed for ECUR/SCUR model (data, voice and content). You could change ECUR/SCUR\n\
97                                             analysis behaviour providing 'SessionIdHighPart', 'SessionIdOptionalPart' (atoi applied;\n\
98                                             usually subscriber id data, i.e. MSISDN or IMSI) and 'RoundRobin' (also 'SessionIdLowPart').\n\
99 \n\
100 \n\
101    - Diameter servers:\n\
102 \n\
103    diameterServer:                          Diameter own server address in '<address>:<port>' format. For example: 10.20.30.40:3868.\n\
104                                             If missing, no local server will be enabled.\n\
105    diameterServerSessions:                  Diameter own server available connections (0: diameter server disabled). Default value of 1.\n\
106                                             Negative value implies no limit accepting incoming connections.\n\
107    cea:                                     User defined CEA path file to be encoded to answer client CERs.\n\
108                                             If missing, will be harcoded.\n\
109    allowedInactivityTime:                   Milliseconds for the maximum allowed inactivity time on server sessions born over the\n\
110                                             local server before being reset. If missing, default value of 90000 will be assigned.\n\
111 \n\
112 \n\
113    - General:\n\
114 \n\
115    answersTimeout:                          Milliseconds to wait pending application answers from diameter peers. If missing,\n\
116                                             default value of 10000 will be assigned.\n\
117    retries:                                 Expired responses will cause a number of request retransmissions. Disabled by default (0 retries).\n\
118    log:                                     Process log file (operations result, traffic log, etc.). By default '<originHost>.launcher.log'.\n\
119                                             Empty string or \"null\" name, to disable. Warning: there is no rotation for log files\n\
120                                             (use logrotate or whatever you consider).\n\
121    splitLog:                                Splits log file (appends to log filename, extensions with the type of event.\n\
122                                             (Check 'HELP.md' for more information). No log files for code/decode and load operations are created.\n\
123                                             Default value 'no'.\n\
124    detailedLog:                             Insert detailed information at log files (timestamps, communication resources, etc.). Useful\n\
125                                             to analyze the messages flow along the sockets (specially on 'balance' mode). Default 'no'.\n\
126    dumpLog:                                 Write to disk every incoming/outcoming message named as:\n\
127                                                '<unix ms timestamp>.<originHost>.<hop by hop>.<end to end>.<message code>.<request|answer>.<type of event>.xml'\n\
128                                             Default value 'no'.\n\
129    burstLog:                                Burst operations log file. By default '<originHost>.launcher.burst'. Empty string or \"null\" name, to disable.\n\
130                                             Warning: there is no rotation for log files (use logrotate or whatever). Output: dot (.) for each\n\
131                                             burst message sent/pushed, cross (x) for popped ones, and order number when multiple of 1% of burst\n\
132                                             list size, plus OTA requests when changed.\n\
133 \n\
134 -->\n\
135 \n\
136 ";
137
138
139 Launcher::Launcher() : anna::comm::Application("launcher", "DiameterLauncher", "1.1"), a_communicator(NULL) {
140   a_timeEngine = NULL;
141   a_counterRecorder = NULL;
142   a_admlMinResolution = 2 * anna::timex::Engine::minResolution; // 2*10 = 20 ms; 1000/20 = 50 ticks per second;
143   //a_admlMinResolution = (anna::Millisecond)100;
144   a_counterRecorderClock = NULL;
145
146   a_workingNode = NULL;
147   a_operatedHost = NULL;
148
149   a_httpServerSocket = NULL;
150 }
151
152
153 std::string Launcher::getSignalUSR2InputFile() const {
154   return (getInitialWorkingDirectory() + "/" + SIGUSR2_TASKS_INPUT_FILENAME);
155 }
156
157 std::string Launcher::getSignalUSR2OutputFile() const {
158   return (getInitialWorkingDirectory() + "/" + SIGUSR2_TASKS_OUTPUT_FILENAME);
159 }
160
161
162 void Launcher::servicesFromXML(const anna::xml::Node* servicesNode, bool bindResources) noexcept(false) {
163
164   CommandLine& cl(anna::CommandLine::instantiate());
165   bool allLogsDisabled = cl.exists("disableLogs");
166
167     //<!ATTLIST stack id CDATA #REQUIRED dictionary CDATA #REQUIRED>
168   const anna::xml::Attribute *id, *dictionary;
169
170   // <!ATTLIST node originHost CDATA #REQUIRED applicationId CDATA #REQUIRED cer CDATA #IMPLIED dwr CDATA #IMPLIED allowedInactivityTime CDATA #IMPLIED tcpConnectDelay CDATA #IMPLIED answersTimeout CDATA #IMPLIED ceaTimeout CDATA #IMPLIED watchdogPeriod CDATA #IMPLIED entity CDATA #IMPLIED entityServerSessions CDATA #IMPLIED diameterServer CDATA #IMPLIED diameterServerSessions CDATA #IMPLIED balance (yes | no) #IMPLIED sessionBasedModelsClientSocketSelection (SessionIdLowPart | SessionIdHighPart | SessionIdOptionalPart | RoundRobin) #IMPLIED retries CDATA #IMPLIED log CDATA #IMPLIED splitLog (yes | no) #IMPLIED detailedLog (yes | no) #IMPLIED dumpLog (yes | no) #IMPLIED burstLog (yes | no) #IMPLIED>
171   const anna::xml::Attribute *originHost, *appId, *originRealm, *cer, *dwr, *cea, *allowedInactivityTime, *tcpConnectDelay,
172   *answersTimeout, *ceaTimeout, *watchdogPeriod, *entity, *entityServerSessions,
173   *diameterServer, *diameterServerSessions, *balance, *sessionBasedModelsClientSocketSelection,
174   *retries, *log, *splitLog, *detailedLog, *dumpLog, *burstLog;
175   // Never clear services content from here (append new data from xml). At the moment no node removing is implemented in this process
176
177   // Stacks
178   anna::diameter::stack::Engine &stackEngine = anna::diameter::stack::Engine::instantiate();
179   anna::diameter::stack::Dictionary *d;
180   const anna::diameter::stack::Dictionary *bpd = NULL; // base protocol dictionary
181
182   // Codec engine manager:
183   anna::diameter::codec::EngineManager &em = anna::diameter::codec::EngineManager::instantiate();
184   anna::diameter::codec::Engine *ce;
185
186   ///////////////////////////////////////////
187   // APPLICATION MESSAGE OAM MODULE SCOPES //
188   ///////////////////////////////////////////
189   // We will register a scope per stack id registered. The counters will be dynamically registered at count method.
190   anna::diameter::comm::ApplicationMessageOamModule & appMsgOamModule = anna::diameter::comm::ApplicationMessageOamModule::instantiate();
191   appMsgOamModule.enableCounters(); // this special module is disabled by default (the only)
192   static int scope_id = 3 + appMsgOamModule.scopes();
193
194   unsigned int id_value;
195   std::string codecEngineName;
196
197   for(anna::xml::Node::const_child_iterator it = servicesNode->child_begin(); it != servicesNode->child_end(); it++) {
198     std::string nodeName = (*it)->getName();
199
200     if(nodeName == "stack") {
201       // Input data:
202       id = (*it)->getAttribute("id");
203       dictionary = (*it)->getAttribute("dictionary");
204       id_value = id->getIntegerValue();
205
206       if (stackEngine.getDictionary(id_value)) { // Ignore (but don't fail) dictionary load with same stack id already registered
207         LOGWARNING(anna::Logger::warning(anna::functions::asString("Ignore dictionary load for stack id already registered: %llu", id_value), ANNA_FILE_LOCATION));
208         // Delta loads, in case we provide base protocol already registered (comm::Engine will need 'bpd')
209         if (id_value == 0)
210           bpd = stackEngine.getDictionary(0);
211         continue;
212       }
213
214       try {
215         d = stackEngine.createDictionary(id_value, dictionary->getValue());
216         LOGDEBUG(anna::Logger::debug(anna::functions::asString("Created dictionary (%p) for stack id %llu", d, id_value), ANNA_FILE_LOCATION));
217
218         // OAM module for counters:
219         appMsgOamModule.createStackCounterScope(scope_id, id_value /* application-id */);
220         scope_id++;
221
222       } catch(anna::RuntimeException &ex) {
223         //_exit(ex.asString());
224         throw ex;
225       }
226
227       bpd = d; // base protocol dictionary in case of monostack. If multistack, will be calculated later
228
229       // Create codec engine and register it in the codec engine manager:
230       codecEngineName = anna::functions::asString("CodecEngineForStackId_%llu", id_value);
231       ce = new anna::diameter::codec::Engine(codecEngineName.c_str(), d);
232       em.registerCodecEngine(id_value, ce);
233
234       // Codec engine configuration:
235       const anna::xml::Attribute *vm_attr = (*it)->getAttribute("validationMode", false /* no exception */);
236       const anna::xml::Attribute *vd_attr = (*it)->getAttribute("validationDepth", false /* no exception */);
237       const anna::xml::Attribute *fm_attr = (*it)->getAttribute("fixMode", false /* no exception */);
238       const anna::xml::Attribute *if_attr = (*it)->getAttribute("ignoreFlagsOnValidation", false /* no exception */);
239
240       std::string vm_value = vm_attr ? vm_attr->getValue() : "AfterDecoding";
241       std::string vd_value = vd_attr ? vd_attr->getValue() : "FirstError";
242       std::string fm_value = fm_attr ? fm_attr->getValue() : "BeforeEncoding";
243
244       anna::diameter::codec::Engine::ValidationMode::_v vm;
245       if (vm_value == "BeforeEncoding") vm = anna::diameter::codec::Engine::ValidationMode::BeforeEncoding;
246       else if (vm_value == "AfterDecoding") vm = anna::diameter::codec::Engine::ValidationMode::AfterDecoding;
247       else if (vm_value == "Always") vm = anna::diameter::codec::Engine::ValidationMode::Always;
248       else if (vm_value == "Never") vm = anna::diameter::codec::Engine::ValidationMode::Never;
249       ce->setValidationMode(vm);
250
251       anna::diameter::codec::Engine::ValidationDepth::_v vd;
252       if (vd_value == "Complete") vd = anna::diameter::codec::Engine::ValidationDepth::Complete;
253       else if (vd_value == "FirstError") vd = anna::diameter::codec::Engine::ValidationDepth::FirstError;
254       ce->setValidationDepth(vd);
255
256       anna::diameter::codec::Engine::FixMode::_v fm;
257       if (fm_value == "BeforeEncoding") fm = anna::diameter::codec::Engine::FixMode::BeforeEncoding;
258       else if (fm_value == "AfterDecoding") fm = anna::diameter::codec::Engine::FixMode::AfterDecoding;
259       else if (fm_value == "Always") fm = anna::diameter::codec::Engine::FixMode::Always;
260       else if (fm_value == "Never") fm = anna::diameter::codec::Engine::FixMode::Never;
261       ce->setFixMode(fm);
262
263       bool if_value = (if_attr ? (if_attr->getValue() == "yes") : false);
264       ce->ignoreFlagsOnValidation(if_value);
265     }
266   }
267
268   // Show loaded stacks:
269   std::cout << "Stacks currently loaded:" << std::endl;
270   std::cout << anna::functions::tab(stackEngine.asString(false /* light */)) << std::endl;
271
272   // Basic checking for multistack:
273   bool multistack = (stackEngine.stack_size() > 1);
274   if (multistack) {
275     bpd = stackEngine.getDictionary(0);
276     if(!bpd)
277       throw anna::RuntimeException("In multistack applications is mandatory register a stack id = 0 using a dictionary which contains the needed elements to build base protocol messages (CER/A, DWR/A, DPR/A, STR/A, etc.)", ANNA_FILE_LOCATION);
278   }
279
280   // REALMS:
281   for(anna::xml::Node::const_child_iterator it = servicesNode->child_begin(); it != servicesNode->child_end(); it++) {
282     std::string nodeName = (*it)->getName();
283
284     if(nodeName == "node") {
285       // Input data:
286       originHost = (*it)->getAttribute("originHost");
287       appId = (*it)->getAttribute("applicationId");
288       originRealm = (*it)->getAttribute("originRealm", false /* no exception */);
289       cer = (*it)->getAttribute("cer", false /* no exception */);
290       dwr = (*it)->getAttribute("dwr", false /* no exception */);
291       cea = (*it)->getAttribute("cea", false /* no exception */);
292       allowedInactivityTime = (*it)->getAttribute("allowedInactivityTime", false /* no exception */);
293       tcpConnectDelay = (*it)->getAttribute("tcpConnectDelay", false /* no exception */);
294       answersTimeout = (*it)->getAttribute("answersTimeout", false /* no exception */);
295       ceaTimeout = (*it)->getAttribute("ceaTimeout", false /* no exception */);
296       watchdogPeriod = (*it)->getAttribute("watchdogPeriod", false /* no exception */);
297       entity = (*it)->getAttribute("entity", false /* no exception */);
298       entityServerSessions = (*it)->getAttribute("entityServerSessions", false /* no exception */);
299       diameterServer = (*it)->getAttribute("diameterServer", false /* no exception */);
300       diameterServerSessions = (*it)->getAttribute("diameterServerSessions", false /* no exception */);
301       balance = (*it)->getAttribute("balance", false /* no exception */); // (yes | no)
302       sessionBasedModelsClientSocketSelection = (*it)->getAttribute("sessionBasedModelsClientSocketSelection", false /* no exception */); // (SessionIdHighPart | SessionIdOptionalPart | RoundRobin)
303       retries = (*it)->getAttribute("retries", false /* no exception */);
304       log = (*it)->getAttribute("log", false /* no exception */);
305       splitLog = (*it)->getAttribute("splitLog", false /* no exception */); // (yes | no)
306       detailedLog = (*it)->getAttribute("detailedLog", false /* no exception */); // (yes | no)
307       dumpLog = (*it)->getAttribute("dumpLog", false /* no exception */); // (yes | no)
308       burstLog = (*it)->getAttribute("burstLog", false /* no exception */); // (yes | no)
309
310       // Basic checkings:
311       anna::diameter::comm::OriginHostManager &ohm = anna::diameter::comm::OriginHostManager::instantiate();
312       anna::diameter::comm::OriginHost *oh = ohm.getOriginHost(originHost->getValue());
313       if (oh) {
314         std::string msg = "Already registered such Origin-Host: "; msg += originHost->getValue();
315         throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
316       }
317
318       unsigned int applicationId = appId->getIntegerValue();
319       if (!stackEngine.getDictionary(applicationId)) {
320         std::string msg = "Cannot found a registered stack id with the value of applicationId provided: "; msg += appId->getValue();
321         throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
322       }
323
324       // Engine time measures checking & assignment:
325       anna::Millisecond allowedInactivityTimeMs(90000);
326       anna::Millisecond tcpConnectDelayMs(200);
327       anna::Millisecond answersTimeoutMs(10000);
328       anna::Millisecond ceaTimeoutMs(10000);
329       anna::Millisecond watchdogPeriodMs(30000);
330
331       if (allowedInactivityTime) allowedInactivityTimeMs = checkTimeMeasure("allowedInactivityTime", allowedInactivityTime->getValue());
332       if (tcpConnectDelay)       tcpConnectDelayMs =       checkTimeMeasure("tcpConnectDelay",       tcpConnectDelay->getValue());
333       if (answersTimeout)        answersTimeoutMs =        checkTimeMeasure("answersTimeout",        answersTimeout->getValue());
334       if (ceaTimeout)            ceaTimeoutMs =            checkTimeMeasure("ceaTimeout",            ceaTimeout->getValue());
335       if (watchdogPeriod)        watchdogPeriodMs =        checkTimeMeasure("watchdogPeriod",        watchdogPeriod->getValue());
336
337       // Checking command line parameters
338       std::string sessionBasedModelsType;
339       anna::diameter::comm::Entity::SessionBasedModelsType::_v sessionBasedModelsTypeEnum = anna::diameter::comm::Entity::SessionBasedModelsType::SessionIdLowPart;
340       if(sessionBasedModelsClientSocketSelection) {
341         sessionBasedModelsType = sessionBasedModelsClientSocketSelection->getValue();
342         if (sessionBasedModelsType == "RoundRobin") {
343           sessionBasedModelsTypeEnum = anna::diameter::comm::Entity::SessionBasedModelsType::RoundRobin;
344         }
345         else if (sessionBasedModelsType == "SessionIdOptionalPart") {
346           sessionBasedModelsTypeEnum = anna::diameter::comm::Entity::SessionBasedModelsType::SessionIdOptionalPart;
347         }
348         else if (sessionBasedModelsType == "SessionIdHighPart") {
349           sessionBasedModelsTypeEnum = anna::diameter::comm::Entity::SessionBasedModelsType::SessionIdHighPart;
350         }
351         else if (sessionBasedModelsType == "SessionIdLowPart") {
352           sessionBasedModelsTypeEnum = anna::diameter::comm::Entity::SessionBasedModelsType::SessionIdLowPart;
353         }
354         else {
355           throw anna::RuntimeException("Parameter 'sessionBasedModelsClientSocketSelection' only accepts 'SessionIdLowPart'/'SessionIdHighPart'/'SessionIdOptionalPart'/'RoundRobin' as parameter values", ANNA_FILE_LOCATION);
356         }
357       }
358
359       int retransmissions = retries ? retries->getIntegerValue() : 0;
360       if(retransmissions < 0) {
361         throw anna::RuntimeException("Parameter 'retries' must be non-negative", ANNA_FILE_LOCATION);
362       }
363
364       /////////////////////////////////////////////////////////////////////////////////////////////
365       // Diameter communication engine: ONE ENGINE PER OWN ORIGIN HOST
366       std::string commEngineName = originHost->getValue() + "_DiameterCommEngine";
367       MyDiameterEngine *commEngine = new MyDiameterEngine(commEngineName.c_str(), bpd);
368       commEngine->setAutoBind(false);  // allow to create client-sessions without binding them, in order to set timeouts.
369       commEngine->setMaxConnectionDelay(tcpConnectDelayMs);
370       commEngine->setWatchdogPeriod(watchdogPeriodMs);
371       commEngine->setOriginHostName(originHost->getValue());
372       if (originRealm) commEngine->setOriginRealmName(originRealm->getValue());
373
374       // Origin host node:
375       a_workingNode = new anna::diameter::comm::OriginHost((anna::diameter::comm::Engine*)commEngine, applicationId /* OriginHost constructor extracts corresponding stack codec engine */);
376       a_workingNode->setRequestRetransmissions(retransmissions);
377       /////////////////////////////////////////////////////////////////////////////////////////////
378
379
380       // Diameter entity:
381       if(entity) {
382         int sessions = entityServerSessions ? entityServerSessions->getIntegerValue() : 1;
383
384         if(sessions > 0) {
385           // Number of sessions:
386           commEngine->setNumberOfClientSessionsPerServer(sessions);
387
388           // Client CER and DWR
389           if (cer) { // pathfile provided
390             commEngine->setClientCER(cer->getValue());
391           }
392           else {
393             commEngine->setClientCER(applicationId); // default engine CER
394           }
395           commEngine->setClientDWR(dwr ? dwr->getValue() : "" /* default DWR */);
396
397           // Register one entity for this engine:
398           a_workingNode->createEntity(entity->getValue(), ceaTimeoutMs, answersTimeoutMs);
399           a_workingNode->getEntity()->setSessionBasedModelsType(sessionBasedModelsTypeEnum);
400           a_workingNode->getEntity()->setBalance(balance ? (balance->getValue() == "yes") : false); // for sendings
401           if (bindResources) a_workingNode->getEntity()->bind();
402         }
403       }
404
405       // Diameter Server:
406       if(diameterServer) {
407         // Server CEA
408         std::string ceaPathfile = cea ? cea->getValue() : "";
409
410         int sessions = diameterServerSessions ? diameterServerSessions->getIntegerValue() : 1;
411         a_workingNode->createDiameterServer(diameterServer->getValue(), sessions, allowedInactivityTimeMs, answersTimeoutMs, ceaPathfile);
412       }
413
414       // Logs:
415       if (!allLogsDisabled) {
416         std::string host = commEngine->getOriginHostName();
417         std::string s_log = host + ".launcher.log"; if (log) s_log = log->getValue();
418         bool b_splitLog = (splitLog ? (splitLog->getValue() == "yes") : false);
419         bool b_detailedLog = (detailedLog ? (detailedLog->getValue() == "yes") : false);
420         bool b_dumpLog = (dumpLog ? (dumpLog->getValue() == "yes") : false);
421         std::string s_burstLog = host + ".launcher.burst"; if (burstLog) s_burstLog = burstLog->getValue();
422         a_workingNode->setLogs(s_log, b_splitLog, b_detailedLog, b_dumpLog, s_burstLog);
423       }
424
425
426       // Lazy initialization for comm engine:
427       if (bindResources) commEngine->lazyInitialize();
428
429       // Node and Codec Engine registration ///////////////////////////////////////////////////////
430       ohm.registerOriginHost(originHost->getValue(), a_workingNode);
431       /////////////////////////////////////////////////////////////////////////////////////////////
432     }
433   }
434
435   if (!uniqueOriginHost())
436     a_workingNode = NULL; // by default, mode auto
437
438   // Diameter comm engines which are loaded after application start (via management operation 'services') are not really started,
439   //  but this don't care because application startComponents() -> initialize() -> do_initialize() -> do nothing.
440   // And when stopped, running state is not checked and it will be stopped anyway.
441 }
442
443
444 void Launcher::loadServicesFromFile(const std::string & xmlPathFile, bool bindResources) noexcept(false) {
445
446   if (xmlPathFile == "null" || xmlPathFile == "") {
447     LOGWARNING(anna::Logger::warning("Ignoring services configuration on start: empty or 'null' string provided as xml file. Use management interface (operation 'services') in order to add services", ANNA_FILE_LOCATION));
448     return;
449   }
450
451   LOGDEBUG(
452       std::string trace = "Loading ADML services file '";
453   trace += xmlPathFile;
454   trace += "'";
455   anna::Logger::debug(trace, ANNA_FILE_LOCATION);
456   );
457   anna::xml::DocumentFile xmlDocument; // has private copy constructor defined but not implemented to avoid inhenrit/copy (is very heavy)
458   anna::xml::DTDMemory xmlDTD;
459   const anna::xml::Node *rootNode;
460   xmlDocument.initialize(xmlPathFile.c_str()); // fail here is i/o error
461   xmlDTD.initialize(ServicesDTD);
462   try {
463     rootNode = xmlDocument.parse(xmlDTD); // Parsing: fail here if xml violates dtd
464   }
465   catch (anna::RuntimeException &ex) {
466     LOGWARNING(
467         std::string msg = "Services DTD schema:\n\n";
468     msg += ServicesDTD;
469     anna::Logger::warning(msg, ANNA_FILE_LOCATION);
470     );
471     throw ex;
472   }
473
474   LOGDEBUG(
475       std::string trace = "Loaded XML file (";
476   trace += xmlPathFile;
477   trace += "):\n";
478   trace += anna::xml::Compiler().apply(rootNode);
479   anna::Logger::debug(trace, ANNA_FILE_LOCATION);
480   );
481   servicesFromXML(rootNode, bindResources);
482 }
483
484
485 void Launcher::loadServicesFromXMLString(const std::string & xmlString, bool bindResources) noexcept(false) {
486
487   anna::xml::DocumentMemory xmlDocument; // has private copy constructor defined but not implemented to avoid inhenrit/copy (is very heavy)
488   anna::xml::DTDMemory xmlDTD;
489   const anna::xml::Node *rootNode;
490   xmlDocument.initialize(xmlString.c_str());
491   xmlDTD.initialize(ServicesDTD);
492   try {
493     rootNode = xmlDocument.parse(xmlDTD); // Parsing: fail here if xml violates dtd
494   }
495   catch (anna::RuntimeException &ex) {
496     LOGWARNING(
497         std::string msg = "Services DTD schema:\n\n";
498     msg += ServicesDTD;
499     anna::Logger::warning(msg, ANNA_FILE_LOCATION);
500     );
501     throw ex;
502   }
503
504   LOGDEBUG(
505       std::string trace = "Loaded XML String:\n";
506   trace += anna::xml::Compiler().apply(rootNode);
507   anna::Logger::debug(trace, ANNA_FILE_LOCATION);
508   );
509   servicesFromXML(rootNode, bindResources);
510 }
511
512
513 anna::Millisecond Launcher::checkTimeMeasure(const std::string &parameter, const std::string &value) noexcept(false) {
514
515   if(anna::functions::isLike("^[0-9]+$", value)) {  // para incluir numeros decimales: ^[0-9]+(.[0-9]+)?$
516     int msecs;
517     std::istringstream ( value ) >> msecs;
518
519     if(msecs > a_timeEngine->getMaxTimeout()) { // 600000 ms
520       std::string msg = "Configuration value for '";
521       msg += parameter;
522       msg += "' ("; msg += value; msg += " msecs) is greater than allowed max timeout for timming engine: ";
523       msg += anna::functions::asString(a_timeEngine->getMaxTimeout());
524       throw RuntimeException(msg, ANNA_FILE_LOCATION);
525     }
526
527     if(msecs > 300000) {
528       std::string msg = "Configuration value for '";
529       msg += parameter;
530       msg += "' ("; msg += value; msg += " msecs) is perhaps very big (over 5 minutes).";
531       LOGWARNING(anna::Logger::warning(msg, ANNA_FILE_LOCATION));
532     }
533
534     if(msecs <= a_timeEngine->getResolution()) {
535       std::string msg = "Configuration value for '";
536       msg += parameter;
537       msg += "' ("; msg += value; msg += " msecs) as any other time measure, must be greater than timming engine resolution: ";
538       msg += anna::functions::asString(a_timeEngine->getResolution());
539       throw RuntimeException(msg, ANNA_FILE_LOCATION);
540     }
541
542     return (anna::Millisecond)msecs; // ok
543   }
544
545   // Non-integer exception:
546   std::string msg = "Configuration error for '";
547   msg += parameter;
548   msg += "' = '";
549   msg += value;
550   msg += "': must be a non-negative integer number";
551   throw RuntimeException(msg, ANNA_FILE_LOCATION);
552 }
553
554 bool Launcher::setWorkingNode(const std::string &name) {
555   bool result = false;
556
557   anna::diameter::comm::OriginHostManager &ohm = anna::diameter::comm::OriginHostManager::instantiate();
558   anna::diameter::comm::OriginHost *oh = ohm.getOriginHost(name);
559
560   if (oh) {
561     a_workingNode = const_cast<anna::diameter::comm::OriginHost*>(oh);
562     result = true;
563   }
564
565   return result;
566 }
567
568 anna::diameter::comm::OriginHost *Launcher::getOriginHost(const std::string &name) const noexcept(false) {
569   anna::diameter::comm::OriginHostManager &ohm = anna::diameter::comm::OriginHostManager::instantiate();
570   anna::diameter::comm::OriginHost *result = ohm.getOriginHost(name);
571
572   if (!result)
573   throw anna::RuntimeException(anna::functions::asString("There is no origin host registered as '%s' (set Origin-Host avp correctly or force a specific host with 'node' operation)", name.c_str()), ANNA_FILE_LOCATION);
574
575   return result;
576 }
577
578 anna::diameter::comm::OriginHost *Launcher::getOriginHost(const anna::diameter::codec::Message &message) const noexcept(false) {
579   std::string originHost = message.getAvp(anna::diameter::helpers::base::AVPID__Origin_Host)->getDiameterIdentity()->getValue();
580   return (getOriginHost(originHost));
581 }
582
583 anna::diameter::comm::OriginHost *Launcher::getOriginHost(const anna::DataBlock &messageDataBlock) const noexcept(false) {
584   std::string originHost = anna::diameter::helpers::base::functions::getOriginHost(messageDataBlock);
585   return (getOriginHost(originHost));
586 }
587
588 bool Launcher::uniqueOriginHost() const {
589   anna::diameter::comm::OriginHostManager &ohm = anna::diameter::comm::OriginHostManager::instantiate();
590   return (ohm.size() == 1);
591 }
592
593 void Launcher::updateOperatedOriginHostWithMessage(const anna::diameter::codec::Message &message) noexcept(false) {
594   if (!a_operatedHost) // priority for working node by mean 'node' operation
595     a_operatedHost = getOriginHost(message);
596 }
597
598 void Launcher::updateOperatedOriginHostWithMessage(const anna::DataBlock &messageDataBlock) noexcept(false) {
599   if (!a_operatedHost) // priority for working node by mean 'node' operation
600     a_operatedHost = getOriginHost(messageDataBlock);
601 }
602
603 anna::diameter::comm::OriginHost *Launcher::getWorkingNode() const noexcept(false) {
604   if(!a_workingNode)
605     throw anna::RuntimeException("Working node not identified (try to load services)", ANNA_FILE_LOCATION);
606
607   return a_workingNode;
608 }
609
610 anna::diameter::comm::OriginHost *Launcher::getOperatedHost() const noexcept(false) {
611   if(!a_operatedHost)
612     throw anna::RuntimeException("Node not identified (try to force a specific Origin-Host with 'node' operation)", ANNA_FILE_LOCATION);
613
614   return a_operatedHost;
615 }
616
617 void Launcher::setOperatedHost(anna::diameter::comm::OriginHost *op) {
618   a_operatedHost = op;
619 }
620
621 MyDiameterEntity *Launcher::getOperatedEntity() const noexcept(false) {
622   MyDiameterEntity *result = (MyDiameterEntity *)(getOperatedHost()->getEntity());
623   if (!result)
624     throw anna::RuntimeException("No entity configured for the operated node", ANNA_FILE_LOCATION);
625   return result;
626 }
627
628 MyLocalServer *Launcher::getOperatedServer() const noexcept(false) {
629   MyLocalServer *result = (MyLocalServer *)(getOperatedHost()->getDiameterServer());
630   if (!result)
631     throw anna::RuntimeException("No local server configured for the operated node", ANNA_FILE_LOCATION);
632   return result;
633 }
634
635 MyDiameterEngine *Launcher::getOperatedEngine() const noexcept(false) {
636   return (MyDiameterEngine *)getOperatedHost()->getCommEngine(); // never will be NULL
637 }
638
639 void Launcher::initialize()
640 noexcept(false) {
641   anna::comm::Application::initialize();
642   CommandLine& cl(anna::CommandLine::instantiate());
643   anna::comm::Communicator::WorkMode::_v workMode(anna::comm::Communicator::WorkMode::Single);
644   a_communicator = new MyCommunicator(workMode);
645   a_timeEngine = new anna::timex::Engine((anna::Millisecond)600000, a_admlMinResolution);
646   anna::testing::TestManager::instantiate().setTimerController(a_timeEngine);
647
648   // Counters record procedure:
649   const char *varname = "cntRecordPeriod";
650   anna::Millisecond cntRecordPeriod;
651   try {
652     cntRecordPeriod = (cl.exists(varname)) ? checkTimeMeasure(varname, cl.getValue(varname)) : (anna::Millisecond)300000;
653   }
654   catch(anna::RuntimeException &ex) {
655     if (cntRecordPeriod != 0) throw ex;
656   }
657
658   if(cntRecordPeriod != 0) {
659     a_counterRecorderClock = new MyCounterRecorderClock("Counters record procedure clock", cntRecordPeriod); // clock
660     std::string cntDir = ".";
661     if(cl.exists("cntDir")) cntDir = cl.getValue("cntDir");
662     a_counterRecorder = new MyCounterRecorder(cntDir + anna::functions::asString("/Counters.Pid%d", (int)getPid()));
663   }
664
665   // Testing framework:
666   std::string tmDir = ".";
667   if(cl.exists("tmDir")) tmDir = cl.getValue("tmDir");
668   anna::testing::TestManager::instantiate().setReportsDirectory(tmDir);
669
670   // Tracing:
671   if(cl.exists("trace"))
672     anna::Logger::setLevel(anna::Logger::asLevel(cl.getValue("trace")));
673
674   // Load launcher services:
675   loadServicesFromFile(cl.getValue("services"), false /* no bind at the moment */); // before run (have components to be created)
676 }
677
678 void Launcher::run()
679 noexcept(false) {
680   LOGMETHOD(anna::TraceMethod tm("Launcher", "run", ANNA_FILE_LOCATION));
681   CommandLine& cl(anna::CommandLine::instantiate());
682   anna::diameter::stack::Engine::instantiate();
683
684   // Start time:
685   a_start_time.setNow();
686
687   // Initial working directory:
688   char cwd[1024];
689   if (getcwd(cwd, sizeof(cwd)) == NULL)
690     throw anna::RuntimeException("Cannot retrieve initial working directory !!", ANNA_FILE_LOCATION);
691   a_initialWorkingDirectory = cwd;
692
693   // Statistics:
694   anna::statistics::Engine::instantiate().enable();
695
696   LOGINFORMATION(
697   // Test messages dtd:
698   std::string msg = "\n                     ------------- TESTMESSAGES DTD -------------\n";
699   msg += anna::diameter::codec::MessageDTD;
700   anna::Logger::information(msg, ANNA_FILE_LOCATION);
701   );
702
703   // HTTP Server:
704   if(cl.exists("httpServer")) {
705     anna::comm::Network& network = anna::comm::Network::instantiate();
706     std::string address;
707     int port;
708     anna::functions::getAddressAndPortFromSocketLiteral(cl.getValue("httpServer"), address, port);
709     //const anna::comm::Device* device = network.find(Device::asAddress(address)); // here provide IP
710     const anna::comm::Device* device = *((network.resolve(address)->device_begin())); // trick to solve
711     a_httpServerSocket = new anna::comm::ServerSocket(anna::comm::INetAddress(device, port), cl.exists("httpServerShared") /* shared bind */, &anna::http::Transport::getFactory());
712   }
713
714   ///////////////////////////////
715   // Diameter library COUNTERS //
716   ///////////////////////////////
717   anna::diameter::comm::OamModule & oamDiameterComm = anna::diameter::comm::OamModule::instantiate();
718   oamDiameterComm.initializeCounterScope(1);  // 1000 - 1999
719   oamDiameterComm.enableCounters();
720   oamDiameterComm.enableAlarms();
721   anna::diameter::codec::OamModule & oamDiameterCodec = anna::diameter::codec::OamModule::instantiate();
722   oamDiameterCodec.initializeCounterScope(2);  // 2000 - 2999
723   oamDiameterCodec.enableCounters();
724   oamDiameterCodec.enableAlarms();
725   /////////////////
726   // COMM MODULE //
727   /////////////////
728   /* Main events */
729   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::RequestReceived, "" /* get defaults for enum type*/, 0 /*1000*/);
730   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::AnswerReceived,                 "", 1 /*1001*/);
731   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::RequestReceivedOnClientSession, "", 2 /*1002*/);
732   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::AnswerReceivedOnClientSession,  "", 3 /*1003*/);
733   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::RequestReceivedOnServerSession, "", 4 /* etc. */);
734   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::AnswerReceivedOnServerSession,  "", 5);
735   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::RequestSentOK,                  "", 6);
736   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::RequestSentNOK,                 "", 7);
737   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::AnswerSentOK,                   "", 8);
738   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::AnswerSentNOK,                  "", 9);
739   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::RequestSentOnClientSessionOK,   "", 10);
740   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::RequestSentOnClientSessionNOK,  "", 11);
741   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::AnswerSentOnClientSessionOK,    "", 12);
742   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::AnswerSentOnClientSessionNOK,   "", 13);
743   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::RequestSentOnServerSessionOK,   "", 14);
744   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::RequestSentOnServerSessionNOK,  "", 15);
745   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::AnswerSentOnServerSessionOK,    "", 16);
746   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::AnswerSentOnServerSessionNOK,   "", 17);
747   /* Diameter Base (capabilities exchange & keep alive) */
748   // as client
749   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::CERSentOK,   "", 18);
750   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::CERSentNOK,  "", 19);
751   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::CEAReceived, "", 20);
752   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::DWRSentOK,   "", 21);
753   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::DWRSentNOK,  "", 22);
754   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::DWAReceived, "", 23);
755   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::DPRSentOK,   "", 24);
756   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::DPRSentNOK,  "", 25);
757   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::DPAReceived, "", 26);
758   // as server
759   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::CERReceived, "", 27);
760   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::CEASentOK,   "", 28);
761   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::CEASentNOK,  "", 29);
762   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::DWRReceived, "", 30);
763   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::DWASentOK,   "", 31);
764   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::DWASentNOK,  "", 32);
765   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::DPRReceived, "", 33);
766   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::DPASentOK,   "", 34);
767   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::DPASentNOK,  "", 35);
768   /* server socket operations (enable/disable listening port for any local server) */
769   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::ServerSocketsOpened, "", 36);
770   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::ServerSocketsClosed, "", 37);
771   /* Connectivity */
772   // clients
773   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::UnableToDeliverOverEntity,                  "", 38);
774   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::LostAvailabilityOverClientSession,          "", 39);
775   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::RecoveredAvailabilityOverClientSession,     "", 40);
776   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::LostAvailabilityOverServer,                 "", 41);
777   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::RecoveredAvailabilityOverServer,            "", 42);
778   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::LostAvailabilityOverEntity,                 "", 43);
779   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::RecoveredAvailabilityOverEntity,            "", 44);
780   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::LostAvailabilityOverEngineForEntities,      "", 45);
781   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::RecoveredAvailabilityOverEngineForEntities, "", 46);
782   // servers
783   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::UnableToDeliverToClient,                                    "", 47);
784   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::LostConnectionForServerSession,                             "", 48);
785   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::UnbindConnectionForServerSessionDueToInactivityTimeAnomaly, "", 49);
786   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::CreatedConnectionForServerSession,                          "", 50);
787   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::LostAvailabilityOverLocalServer,                            "", 51);
788   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::RecoveredAvailabilityOverLocalServer,                       "", 52);
789   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::LostAvailabilityOverEngineForLocalServers,                  "", 53);
790   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::RecoveredAvailabilityOverEngineForLocalServers,             "", 54);
791   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::RequestSentExpired,  "", 55);
792   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::RequestSentOnClientSessionExpired,  "", 56);
793   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::RequestSentOnServerSessionExpired,  "", 57);
794   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::RequestRetransmitted,  "", 58);
795   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::RequestRetransmittedOnClientSession,  "", 59);
796   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::RequestRetransmittedOnServerSession,  "", 60);
797   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::AnswerReceivedUnknown,  "", 61);
798   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::AnswerReceivedOnClientSessionUnknown,  "", 62);
799   oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::AnswerReceivedOnServerSessionUnknown,  "", 63);
800   //////////////////
801   // CODEC MODULE //
802   //////////////////
803   /* Avp decoding */
804   oamDiameterCodec.registerCounter(anna::diameter::codec::OamModule::Counter::AvpDecode__NotEnoughBytesToCoverAvpHeaderLength,                          "", 0 /*2000*/);
805   oamDiameterCodec.registerCounter(anna::diameter::codec::OamModule::Counter::AvpDecode__IncoherenceBetweenActivatedVBitAndZeroedVendorIDValueReceived, "", 1 /*2001*/);
806   oamDiameterCodec.registerCounter(anna::diameter::codec::OamModule::Counter::AvpDecode__IncorrectLength,                                               "", 2 /*2002*/);
807   oamDiameterCodec.registerCounter(anna::diameter::codec::OamModule::Counter::AvpDecode__DataPartInconsistence,                                         "", 3 /*2003*/);
808   oamDiameterCodec.registerCounter(anna::diameter::codec::OamModule::Counter::AvpDecode__UnknownAvpWithMandatoryBit,                                    "", 4 /*2004*/);
809   /* Message decoding */
810   oamDiameterCodec.registerCounter(anna::diameter::codec::OamModule::Counter::MessageDecode__NotEnoughBytesToCoverMessageHeaderLength, "", 5 /*2005*/);
811   oamDiameterCodec.registerCounter(anna::diameter::codec::OamModule::Counter::MessageDecode__NotEnoughBytesToCoverMessageLength,       "", 6 /*2006*/);
812   /* Avp validation */
813   oamDiameterCodec.registerCounter(anna::diameter::codec::OamModule::Counter::AvpValidation__EnumeratedAvpWithValueDoesNotComplyRestriction, "", 10 /*2010*/);
814   oamDiameterCodec.registerCounter(anna::diameter::codec::OamModule::Counter::AvpValidation__AvpFlagsDoesNotFulfillTheDefinedFlagRules,      "", 11 /*2011*/);
815   /* Message validation */
816   oamDiameterCodec.registerCounter(anna::diameter::codec::OamModule::Counter::MessageValidation__UnknownOperationUnableToValidate, "", 12 /*2012*/);
817   oamDiameterCodec.registerCounter(anna::diameter::codec::OamModule::Counter::MessageValidation__OperationHaveIncoherentFlags,     "", 13 /*2013*/);
818   /* Level validation */
819   oamDiameterCodec.registerCounter(anna::diameter::codec::OamModule::Counter::LevelValidation__MissingFixedRule,                                       "", 14 /*2014*/);
820   oamDiameterCodec.registerCounter(anna::diameter::codec::OamModule::Counter::LevelValidation__FailedRuleForCardinality,                               "", 15 /*2015*/);
821   oamDiameterCodec.registerCounter(anna::diameter::codec::OamModule::Counter::LevelValidation__FailedRuleForCardinalityLessThanNeeded,                 "", 16 /*2016*/);
822   oamDiameterCodec.registerCounter(anna::diameter::codec::OamModule::Counter::LevelValidation__FailedRuleForCardinalityMoreThanNeeded,                 "", 17 /*2017*/);
823   oamDiameterCodec.registerCounter(anna::diameter::codec::OamModule::Counter::LevelValidation__FailedGenericAvpRuleForCardinalityFoundDisregardedItem, "", 18 /*2018*/);
824   oamDiameterCodec.registerCounter(anna::diameter::codec::OamModule::Counter::LevelValidation__FoundDisregardedItemsAndGenericAVPWasNotSpecified,      "", 19 /*2019*/);
825
826
827   /////////////////////////////////
828   // Counter recorder associated //
829   /////////////////////////////////
830   if(a_counterRecorderClock) {
831     oamDiameterComm.setCounterRecorder(a_counterRecorder);
832     oamDiameterCodec.setCounterRecorder(a_counterRecorder);
833     anna::diameter::comm::ApplicationMessageOamModule::instantiate().setCounterRecorder(a_counterRecorder);
834     a_timeEngine->activate(a_counterRecorderClock); // start clock
835   }
836
837   /////////////////////////////
838   // Log statistics concepts //
839   /////////////////////////////
840   if(cl.exists("logStatisticSamples"))
841     logStatisticsSamples(cl.getValue("logStatisticSamples"));
842
843   // Start client connections //////////////////////////////////////////////////////////////////////////////////
844   MyDiameterEntity *entity;
845   anna::diameter::comm::OriginHostManager &ohm = anna::diameter::comm::OriginHostManager::instantiate();
846   for (diameter::comm::origin_hosts_it it = ohm.begin(); it != ohm.end(); it++) {
847     entity = (MyDiameterEntity *)(it->second->getEntity());
848     if (entity) entity->bind();
849   }
850
851   // Go into communicator poll
852   // Reconnection period (tcp reconnect retry time):
853   const char *varname = "reconnectionPeriod";
854   anna::Millisecond reconnectionPeriod = (cl.exists(varname)) ? checkTimeMeasure(varname, cl.getValue(varname)) : (anna::Millisecond)10000;
855
856   a_communicator->setRecoveryTime(reconnectionPeriod);
857   if(cl.exists("httpServer")) a_communicator->attach(a_httpServerSocket);  // HTTP
858   a_communicator->accept();
859 }
860
861 bool Launcher::getDataBlockFromHexFile(const std::string &pathfile, anna::DataBlock &db) const noexcept(false) {
862   // Get hex string
863   static char buffer[8192];
864   std::ifstream infile(pathfile.c_str(), std::ifstream::in);
865
866   if(infile.is_open()) {
867     infile >> buffer;
868     std::string hexString(buffer, strlen(buffer));
869     // Allow colon separator in hex string: we have to remove them before processing with 'fromHexString':
870     hexString.erase(std::remove(hexString.begin(), hexString.end(), ':'), hexString.end());
871     LOGDEBUG(
872         std::string msg = "Hex string (remove colons if exists): ";
873     msg += hexString;
874     anna::Logger::debug(msg, ANNA_FILE_LOCATION);
875     );
876
877     anna::functions::fromHexString(hexString, db); // could launch exception
878     // Close file
879     infile.close();
880     return true;
881   }
882
883   return false;
884 }
885
886 void Launcher::resetStatistics() {
887   if (a_workingNode) {
888     a_workingNode->getCommEngine()->resetStatistics();
889   }
890   else {
891     anna::diameter::comm::OriginHostManager &ohm = anna::diameter::comm::OriginHostManager::instantiate();
892     for (diameter::comm::origin_hosts_it it = ohm.begin(); it != ohm.end(); it++) {
893       it->second->getCommEngine()->resetStatistics();
894     }
895   }
896 }
897
898 void Launcher::resetCounters() {
899   anna::diameter::comm::OamModule::instantiate().resetCounters();
900   anna::diameter::comm::ApplicationMessageOamModule::instantiate().resetCounters();
901   anna::diameter::codec::OamModule::instantiate().resetCounters();
902 }
903
904 void Launcher::signalTerminate() noexcept(false) {
905   LOGMETHOD(anna::TraceMethod tm("Launcher", "signalTerminate", ANNA_FILE_LOCATION));
906
907   forceCountersRecord();
908
909   a_communicator->terminate ();
910   comm::Application::signalTerminate ();
911 }
912
913 void Launcher::signalUSR2() noexcept(false) {
914
915   std::string inputFile = getSignalUSR2InputFile();
916   std::string outputFile = getSignalUSR2OutputFile();
917
918   LOGNOTICE(
919   std::string msg = "Captured signal SIGUSR2. Reading tasks at '";
920   msg += inputFile;
921   msg += "' (results will be written at '";
922   msg += outputFile;
923   msg += "')";
924   anna::Logger::notice(msg, ANNA_FILE_LOCATION);
925   );
926
927   // Operation:
928   std::string line;
929   std::string response_content;
930   std::ifstream in_file(inputFile);
931   std::ofstream out_file(outputFile);
932
933   if(!in_file.is_open()) throw RuntimeException("Unable to read tasks", ANNA_FILE_LOCATION);
934   if(!out_file.is_open()) throw RuntimeException("Unable to write tasks", ANNA_FILE_LOCATION);
935
936   while(getline(in_file, line)) {
937
938     // Ignore comments and blank lines:
939     if (line[0] == '#')  continue;
940     if (std::string::npos == line.find_first_not_of(" \t")) continue;
941
942     LOGDEBUG(
943         std::string msg = "Processing line: ";
944     msg += line;
945     anna::Logger::debug(msg, ANNA_FILE_LOCATION);
946     );
947
948     try {
949       eventOperation(line, response_content);
950     } catch(RuntimeException &ex) {
951       ex.trace();
952     }
953
954     out_file << response_content << "\n";
955   }
956
957   in_file.close();
958   out_file << "EOF\n";
959   out_file.close();
960 }
961
962
963 void Launcher::logStatisticsSamples(const std::string &conceptsList) {
964   anna::statistics::Engine &statEngine = anna::statistics::Engine::instantiate();
965
966   if(conceptsList == "all") {
967     if(statEngine.enableSampleLog(/* -1: all concepts */))
968       LOGDEBUG(anna::Logger::debug("Sample log activation for all statistic concepts", ANNA_FILE_LOCATION));
969   }
970   else if(conceptsList == "none") {
971       if(statEngine.disableSampleLog(/* -1: all concepts */))
972         LOGDEBUG(anna::Logger::debug("Sample log deactivation for all statistic concepts", ANNA_FILE_LOCATION));
973   } else {
974     anna::Tokenizer lst;
975     lst.apply(conceptsList, ",");
976
977     if(lst.size() >= 1) {
978       anna::Tokenizer::const_iterator tok_min(lst.begin());
979       anna::Tokenizer::const_iterator tok_max(lst.end());
980       anna::Tokenizer::const_iterator tok_iter;
981       int conceptId;
982
983       for(tok_iter = tok_min; tok_iter != tok_max; tok_iter++) {
984         conceptId = atoi(anna::Tokenizer::data(tok_iter));
985
986         if(statEngine.enableSampleLog(conceptId))
987           LOGDEBUG(anna::Logger::debug(anna::functions::asString("Sample log activation for statistic concept id = %d", conceptId), ANNA_FILE_LOCATION));
988       }
989     }
990   }
991 }
992
993
994 bool Launcher::eventOperation(const std::string &operation, std::string &response) noexcept(false) {
995
996   bool result = true;
997
998   LOGMETHOD(anna::TraceMethod tm("Launcher", "eventOperation", ANNA_FILE_LOCATION));
999   if (operation == "") return result; // ignore
1000   LOGDEBUG(anna::Logger::debug(anna::functions::asString("Operation: %s", operation.c_str()), ANNA_FILE_LOCATION));
1001
1002   EventOperation eop(false /* not HTTP, it is SIGUSR2 */);
1003
1004   // Default response:
1005   //response = "Operation processed with exception: ";
1006   response = "Internal error (check ADML traces): ";
1007   response += operation;
1008   std::string opt_response = ""; // aditional response content
1009   anna::DataBlock db_aux(true);
1010   anna::diameter::codec::Message codecMsg; // auxiliary codec message
1011
1012   // Singletons:
1013   anna::testing::TestManager &testManager = anna::testing::TestManager::instantiate();
1014
1015
1016   ///////////////////////////////////////////////////////////////////
1017   // Simple operations without arguments:
1018
1019   // Dynamic operation:
1020   if(operation.find("dynamic") == 0) {
1021     Procedure p(this);
1022     int op_size = operation.size();
1023     std::string args = ((operation.find("dynamic|") == 0) && (op_size > 8)) ? operation.substr(8) : "";
1024     if (args == "" && op_size != 7)
1025       throw anna::RuntimeException("Wrong body content format on HTTP Request. Check 'HELP.md' for more information.", ANNA_FILE_LOCATION);
1026     try {
1027       p.execute(args, response);
1028     }
1029     catch(anna::RuntimeException &ex) {
1030       ex.trace();
1031       response = ex.asString();
1032       return false;
1033     }
1034     return true; // OK
1035   }
1036
1037   // Reset performance data:
1038   if(operation == "collect") {
1039     return eop.collect(response);
1040   }
1041
1042   // Counters dump on demand:
1043   if(operation == "forceCountersRecord") {
1044     return eop.forceCountersRecord(response);
1045   }
1046
1047   // OAM & statistics:
1048   if(operation == "show-oam") {
1049     return eop.show_oam(response);
1050   }
1051   if(operation == "show-stats") {
1052     return eop.show_stats(response);
1053   }
1054
1055   ///////////////////////////////////////////////////////////////////
1056   // Tokenize operation
1057   Tokenizer params;
1058   params.apply(operation, "|", "<null>" /* allow contiguous separators */);
1059   int numParams = params.size() - 1;
1060
1061   // Get the operation type and parameters:
1062   Tokenizer::const_iterator tok_iter = params.begin();
1063   std::string opType = Tokenizer::data(tok_iter);
1064   std::string param1, param2, param3, param4, param5, param6, param7, param8, param9, param10, param11;
1065   if(numParams >= 1) { tok_iter++; param1 = Tokenizer::data(tok_iter); }
1066   if(numParams >= 2) { tok_iter++; param2 = Tokenizer::data(tok_iter); }
1067   if(numParams >= 3) { tok_iter++; param3 = Tokenizer::data(tok_iter); }
1068   // Tests conditions
1069   if(numParams >= 4) { tok_iter++; param4 = Tokenizer::data(tok_iter); }
1070   if(numParams >= 5) { tok_iter++; param5 = Tokenizer::data(tok_iter); }
1071   if(numParams >= 6) { tok_iter++; param6 = Tokenizer::data(tok_iter); }
1072   if(numParams >= 7) { tok_iter++; param7 = Tokenizer::data(tok_iter); }
1073   if(numParams >= 8) { tok_iter++; param8 = Tokenizer::data(tok_iter); }
1074   if(numParams >= 9) { tok_iter++; param9 = Tokenizer::data(tok_iter); }
1075   if(numParams >= 10) { tok_iter++; param10 = Tokenizer::data(tok_iter); }
1076   if(numParams >= 11) { tok_iter++; param11 = Tokenizer::data(tok_iter); }
1077   // Remove '<null>' artificial token to ease further checkings:
1078   if (param1 == "<null>") param1 = "";
1079   if (param2 == "<null>") param2 = "";
1080   if (param3 == "<null>") param3 = "";
1081   if (param4 == "<null>") param4 = "";
1082   if (param5 == "<null>") param5 = "";
1083   if (param6 == "<null>") param6 = "";
1084   if (param7 == "<null>") param7 = "";
1085   if (param8 == "<null>") param8 = "";
1086   if (param9 == "<null>") param9 = "";
1087   if (param10 == "<null>") param10 = "";
1088   if (param11 == "<null>") param11 = "";
1089
1090   // No operation has more than 2 arguments except 'test' ...
1091   if(opType != "test" && numParams > 2)
1092     throw anna::RuntimeException("Wrong body content format on HTTP Request. Check 'HELP.md' for more information.", ANNA_FILE_LOCATION);
1093
1094
1095   // Check the number of parameters:
1096   bool wrongBody = false;
1097
1098   if((opType == "change-dir") && (numParams > 1)) wrongBody = true;
1099   if((opType == "log-statistics-samples") && (numParams != 1)) wrongBody = true;
1100   if((opType == "node") && (numParams > 1)) wrongBody = true;
1101
1102   if((opType == "node-auto") && (numParams > 0)) wrongBody = true;
1103
1104   if(((opType == "code") || (opType == "decode")) && (numParams != 2)) wrongBody = true;
1105
1106   if(((opType == "sendxml2e") || (opType == "sendhex2e")) && (numParams != 1)) wrongBody = true;
1107
1108   if((opType == "burst") && (numParams < 1)) wrongBody = true;
1109
1110   if((opType == "test") && (numParams < 1)) wrongBody = true;
1111
1112   if(((opType == "sendxml2c") || (opType == "sendhex2c") || (opType == "loadxml") || (opType == "diameterServerSessions")) && (numParams != 1)) wrongBody = true;
1113
1114   if(wrongBody) {
1115     // Launch exception
1116     std::string msg = "Wrong body content format on HTTP Request for '";
1117     msg += opType;
1118     msg += "' operation (missing parameter/s). Check 'HELP.md' for more information.";
1119     throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
1120   }
1121
1122   // Operations:
1123   if(opType == "context") {
1124     return eop.context(response, (numParams == 1) ? param1 : "");
1125   }
1126
1127   if(opType == "log-statistics-samples") {
1128     return eop.log_statistics_samples(response, param1);
1129   }
1130
1131   // Change execution directory:
1132   if(opType == "change-dir") {
1133     return eop.change_dir(response, param1);
1134   }
1135
1136   if(opType == "services") {
1137     std::string servicesFile = ((numParams == 1) ? param1 : "services.xml");
1138     try {
1139       loadServicesFromFile(servicesFile, true /* bind entities */);
1140     }
1141     catch(anna::RuntimeException &ex) {
1142       ex.trace();
1143       response = anna::functions::asString("Loaded services from file '%s' with errors", servicesFile.c_str());
1144       return false;
1145     }
1146     response = anna::functions::asString("Loaded services from file '%s'", servicesFile.c_str());
1147     return true; // OK
1148   }
1149
1150   // Host switch:
1151   if(opType == "node") {
1152     return eop.node(response, param1);
1153   }
1154   else if(opType == "node-auto") {
1155     return eop.node_auto(response);
1156   }
1157
1158   if(opType == "code") {
1159     codecMsg.loadXMLFile(param1);
1160     std::string hexString = anna::functions::asHexString(codecMsg.code());
1161     // write to outfile
1162     std::ofstream outfile(param2.c_str(), std::ifstream::out);
1163     outfile.write(hexString.c_str(), hexString.size());
1164     outfile.close();
1165   } else if(opType == "decode") {
1166     // Get DataBlock from file with hex content:
1167     if(!getDataBlockFromHexFile(param1, db_aux))
1168       throw anna::RuntimeException("Error reading hex file provided", ANNA_FILE_LOCATION);
1169
1170     // Decode
1171     try { codecMsg.decode(db_aux); } catch(anna::RuntimeException &ex) { ex.trace(); }
1172
1173     std::string xmlString = codecMsg.asXMLString();
1174     // write to outfile
1175     std::ofstream outfile(param2.c_str(), std::ifstream::out);
1176     outfile.write(xmlString.c_str(), xmlString.size());
1177     outfile.close();
1178   } else if((opType == "hide") || (opType == "show") || (opType == "hidden") || (opType == "shown")) {
1179     result = eop.visibility(opt_response, opType, param1, (param2 != "") ? atoi(param2.c_str()) : -1);
1180
1181   } else if((opType == "sendxml2e") || (opType == "sendhex2e")) {
1182     anna::diameter::comm::Message *msg;
1183
1184     if(opType == "sendxml2e") {
1185       codecMsg.loadXMLFile(param1);
1186       updateOperatedOriginHostWithMessage(codecMsg);
1187       msg = getOperatedHost()->createCommMessage();
1188       msg->clearBody();
1189       try { codecMsg.valid(); } catch(anna::RuntimeException &ex) { ex.trace(); }  // at least we need to see validation errors although it will continue sending (see validation mode configured in launcher)
1190       msg->setBody(codecMsg.code());
1191     } else {
1192       // Get DataBlock from file with hex content:
1193       if(!getDataBlockFromHexFile(param1, db_aux))
1194         throw anna::RuntimeException("Error reading hex file provided", ANNA_FILE_LOCATION);
1195       updateOperatedOriginHostWithMessage(db_aux);
1196       msg = getOperatedHost()->createCommMessage();
1197       msg->setBody(db_aux);
1198       try { if(getOperatedHost()->logEnabled()) codecMsg.decode(db_aux); } catch(anna::RuntimeException &ex) { ex.trace(); }
1199     }
1200
1201     bool success = getOperatedEntity()->send(msg);
1202     getOperatedHost()->releaseCommMessage(msg);
1203
1204     // Detailed log:
1205     if(getOperatedHost()->logEnabled()) {
1206       anna::diameter::comm::Server *usedServer = getOperatedEntity()->getLastUsedResource();
1207       anna::diameter::comm::ClientSession *usedClientSession = usedServer ? usedServer->getLastUsedResource() : NULL;
1208       std::string detail = usedClientSession ? usedClientSession->asString() : "[null client session]"; // shouldn't happen
1209       getOperatedHost()->writeLogFile(codecMsg, (success ? "sent2e" : "send2eError"), detail);
1210     }
1211   } else if((opType == "sendxml2c") || (opType == "sendhex2c")) {
1212     anna::diameter::comm::Message *msg;
1213
1214     if(opType == "sendxml2c") {
1215       codecMsg.loadXMLFile(param1);
1216       updateOperatedOriginHostWithMessage(codecMsg);
1217       msg = getOperatedHost()->createCommMessage();
1218       msg->clearBody();
1219       try { codecMsg.valid(); } catch(anna::RuntimeException &ex) { ex.trace(); }  // at least we need to see validation errors although it will continue sending (see validation mode configured in launcher)
1220
1221       msg->setBody(codecMsg.code());
1222     } else {
1223       // Get DataBlock from file with hex content:
1224       if(!getDataBlockFromHexFile(param1, db_aux))
1225         throw anna::RuntimeException("Error reading hex file provided", ANNA_FILE_LOCATION);
1226       updateOperatedOriginHostWithMessage(db_aux);
1227       msg = getOperatedHost()->createCommMessage();
1228       msg->setBody(db_aux);
1229     }
1230
1231     bool success = getOperatedServer()->send(msg);
1232     getOperatedHost()->releaseCommMessage(msg);
1233
1234     // Detailed log:
1235     if(getOperatedHost()->logEnabled()) {
1236       anna::diameter::comm::ServerSession *usedServerSession = getOperatedServer()->getLastUsedResource();
1237       std::string detail = usedServerSession ? usedServerSession->asString() : "[null server session]"; // shouldn't happen
1238       getOperatedHost()->writeLogFile(codecMsg, (success ? "sent2c" : "send2cError"), detail);
1239     }
1240   } else if(opType == "answerxml2e") {
1241
1242     if(param1 == "") { // programmed answers FIFO's to stdout
1243       response = getOperatedHost()->getReactingAnswers()->asString("ANSWERS TO ENTITY");
1244       return true; // OK
1245     } else if (param1 == "rotate") {
1246       getOperatedHost()->getReactingAnswers()->rotate(true);
1247     } else if (param1 == "exhaust") {
1248       getOperatedHost()->getReactingAnswers()->rotate(false);
1249     } else if (param1 == "clear") {
1250       getOperatedHost()->getReactingAnswers()->clear();
1251     } else if (param1 == "dump") {
1252       getOperatedHost()->getReactingAnswers()->dump("programmed_answer");
1253     } else {
1254       codecMsg.loadXMLFile(param1);
1255       updateOperatedOriginHostWithMessage(codecMsg);
1256       anna::diameter::codec::Message *message = getOperatedHost()->getCodecEngine()->createMessage(param1); // loads xml again, lesser of two evils
1257       LOGDEBUG(anna::Logger::debug(message->asXMLString(), ANNA_FILE_LOCATION));
1258
1259       if(message->isRequest())
1260         throw anna::RuntimeException("Cannot program diameter requests. Answer type must be provided", ANNA_FILE_LOCATION);
1261
1262       int code = message->getId().first;
1263       LOGDEBUG(anna::Logger::debug("Adding a new programed 'answer to entity' to the FIFO queue corresponding to its message code ...", ANNA_FILE_LOCATION));
1264       getOperatedHost()->getReactingAnswers()->addMessage(code, message);
1265     }
1266   } else if(opType == "answerxml2c") {
1267
1268     if(param1 == "") { // programmed answers FIFO's to stdout
1269       response = getOperatedHost()->getReactingAnswers()->asString("ANSWERS TO CLIENT");
1270       return true; // OK
1271     } else if (param1 == "rotate") {
1272       getOperatedHost()->getReactingAnswers()->rotate(true);
1273     } else if (param1 == "exhaust") {
1274       getOperatedHost()->getReactingAnswers()->rotate(false);
1275     } else if (param1 == "clear") {
1276       getOperatedHost()->getReactingAnswers()->clear();
1277     } else if (param1 == "dump") {
1278       getOperatedHost()->getReactingAnswers()->dump("programmed_answer");
1279     } else {
1280       codecMsg.loadXMLFile(param1);
1281       updateOperatedOriginHostWithMessage(codecMsg);
1282       anna::diameter::codec::Message *message = getOperatedHost()->getCodecEngine()->createMessage(param1); // loads xml again, lesser of two evils
1283       LOGDEBUG(anna::Logger::debug(message->asXMLString(), ANNA_FILE_LOCATION));
1284
1285       if(message->isRequest())
1286         throw anna::RuntimeException("Cannot program diameter requests. Answer type must be provided", ANNA_FILE_LOCATION);
1287
1288       int code = message->getId().first;
1289       LOGDEBUG(anna::Logger::debug("Adding a new programed 'answer to client' to the FIFO queue corresponding to its message code ...", ANNA_FILE_LOCATION));
1290       getOperatedHost()->getReactingAnswers()->addMessage(code, message);
1291     }
1292   } else if((opType == "burst")) {
1293
1294     if (!uniqueOriginHost())
1295       throw anna::RuntimeException("Burst category operations are only allowed in single host node configuration. This is only to simplify user experience.", ANNA_FILE_LOCATION);
1296
1297     // burst|clear                     clears all loaded burst messages.
1298     // burst|load|<source_file>        loads the next diameter message into launcher burst.
1299     // burst|start|<initial load>      starts the message sending with a certain initial load.
1300     // burst|push|<load amount>        sends specific non-aynchronous load.
1301     // burst|stop                      stops the burst cycle.
1302     // burst|repeat|[[yes]|no]         restarts the burst launch when finish.
1303     // burst|send|<amount>             send messages from burst list. The main difference with
1304     //                                 start/push operations is that burst won't be awaken.
1305     //                                 Externally we could control sending time (no request
1306     //                                 will be sent for answers).
1307     // burst|goto|<order>              Updates current burst pointer position.
1308     // burst|look|<order>              Show programmed burst message for order provided, current when missing.
1309
1310     if(param1 == "clear") {
1311       opt_response = "removed ";
1312       opt_response += anna::functions::asString(getOperatedHost()->clearBurst());
1313       opt_response += " elements";
1314     } else if(param1 == "load") {
1315       if(param2 == "") throw anna::RuntimeException("Missing xml path file for burst load operation", ANNA_FILE_LOCATION);
1316
1317       codecMsg.loadXMLFile(param2);
1318       if(codecMsg.isAnswer()) throw anna::RuntimeException("Cannot load diameter answers for burst feature", ANNA_FILE_LOCATION);
1319       try { codecMsg.valid(); } catch(anna::RuntimeException &ex) { ex.trace(); }  // at least we need to see validation errors although it will continue loading (see validation mode configured in launcher)
1320
1321       int position = getOperatedHost()->loadBurstMessage(codecMsg.code());
1322       opt_response = "loaded '";
1323       opt_response += param2;
1324       opt_response += "' file into burst list position ";
1325       opt_response += anna::functions::asString(position);
1326     } else if(param1 == "start") {
1327       if(param2 == "") throw anna::RuntimeException("Missing initial load for burst start operation", ANNA_FILE_LOCATION);
1328
1329       int initialLoad = atoi(param2.c_str());
1330       int processed = getOperatedHost()->startBurst(initialLoad);
1331
1332       if(processed > 0) {
1333         opt_response = "initial load completed for ";
1334         opt_response += anna::functions::entriesAsString(processed, "message");
1335       }
1336     } else if(param1 == "push") {
1337       if(param2 == "") throw anna::RuntimeException("Missing load amount for burst push operation", ANNA_FILE_LOCATION);
1338
1339       int pushed = getOperatedHost()->pushBurst(atoi(param2.c_str()));
1340
1341       if(pushed > 0) {
1342         opt_response = "pushed ";
1343         opt_response += anna::functions::entriesAsString(pushed, "message");
1344       }
1345     } else if(param1 == "pop") {
1346       if(param2 == "") throw anna::RuntimeException("Missing amount for burst pop operation", ANNA_FILE_LOCATION);
1347
1348       int releaseLoad = atoi(param2.c_str());
1349       int popped = getOperatedHost()->popBurst(releaseLoad);
1350
1351       if(popped > 0) {
1352         opt_response = "burst popped for ";
1353         opt_response += anna::functions::entriesAsString(popped, "message");
1354       }
1355     } else if(param1 == "stop") {
1356       int left = getOperatedHost()->stopBurst();
1357
1358       if(left != -1) {
1359         opt_response += anna::functions::entriesAsString(left, "message");
1360         opt_response += " left to the end of the cycle";
1361       }
1362     } else if(param1 == "repeat") {
1363       if(param2 == "") param2 = "yes";
1364
1365       bool repeat = (param2 == "yes");
1366       getOperatedHost()->repeatBurst(repeat);
1367       opt_response += (repeat ? "repeat enabled" : "repeat disabled");
1368     } else if(param1 == "send") {
1369       if(param2 == "") throw anna::RuntimeException("Missing amount for burst send operation", ANNA_FILE_LOCATION);
1370
1371       int sent = getOperatedHost()->sendBurst(atoi(param2.c_str()));
1372
1373       if(sent > 0) {
1374         opt_response = "sent ";
1375         opt_response += anna::functions::entriesAsString(sent, "message");
1376       }
1377     } else if(param1 == "goto") {
1378       if(param2 == "") throw anna::RuntimeException("Missing order position for burst goto operation", ANNA_FILE_LOCATION);
1379
1380       opt_response = getOperatedHost()->gotoBurst(atoi(param2.c_str()));
1381     } else if(param1 == "look") {
1382       int order = ((param2 != "") ? atoi(param2.c_str()) : -1);
1383       opt_response = "\n\n";
1384       opt_response += getOperatedHost()->lookBurst(order);
1385     } else {
1386       throw anna::RuntimeException("Wrong body content format on HTTP Request for 'burst' operation (unexpected action parameter). Check 'HELP.md' for more information.", ANNA_FILE_LOCATION);
1387     }
1388
1389   } else if((opType == "test")) {
1390     // test|<id>|<command>[|parameters]         Add a new step to the test case ...
1391     // test|ttps|<amount>                       Starts/resume the provided number of time ticks per second (ttps). The ADML starts ...
1392     // test|next[|<sync-amount>]                Forces the execution of the next test case(s) without waiting for test manager tick ...
1393     // test|ip-limit[|amount]                   In-progress limit of test cases. No new test cases will be launched over this value ...
1394     // test|repeats|<amount>                    Restarts the whole programmed test list when finished the amount number of times ...
1395     // test|report|<initialized/in-progress/failed/success/[all]/none>[|[yes]|no]
1396     //                                          Enables/disables report generation for a certain test case state: initialized, in-progress ...
1397     // test|report-hex[|[yes]|no]               Reports could include the diameter messages in hexadecimal format. Disabled by default.
1398     // test|goto|<id>                           Updates current test pointer position.
1399     // test|run|<id>                            Executes the given test case
1400     // test|look[|id]                           Show programmed test case for id provided, current when missing ...
1401     // test|state[|id]                          Show test case state for id provided, current when missing ...
1402     // test|interact|amount|id                  Makes interactive a specific test case id. The amount is the margin of execution steps ...
1403     // test|reset|<[soft]/hard>[|id]            Reset the test case for id provided, all the tests when missing ...
1404     // test|auto-reset|<soft|hard>              When cycling, current test cases can be soft (default) or hard reset ...
1405     // test|clear                               Clears all the programmed test cases.
1406     // test|summary                             Test manager general report (number of test cases, counts by state ...
1407
1408
1409     if(param1 == "ttps") {
1410       if (numParams > 2)
1411         throw anna::RuntimeException("Wrong body content format on HTTP Request. Check 'HELP.md' for more information.", ANNA_FILE_LOCATION);
1412
1413       bool success = ((param2 != "") ? testManager.configureTTPS(atoi(param2.c_str())) : false);
1414       if (success) {
1415         opt_response = "assigned new test launch rate to ";
1416         opt_response += anna::functions::asString(atoi(param2.c_str()));
1417         opt_response += " events per second";
1418       }
1419       else {
1420         opt_response += "unable to configure the test rate provided";
1421       }
1422     }
1423     else if (param1 == "next") {
1424       if (numParams > 2)
1425         throw anna::RuntimeException("Wrong body content format on HTTP Request. Check 'HELP.md' for more information.", ANNA_FILE_LOCATION);
1426
1427       int sync_amount = ((param2 != "") ? atoi(param2.c_str()) : 1);
1428
1429       if (sync_amount < 1)
1430         throw anna::RuntimeException("The parameter 'sync-amount' must be a positive integer value", ANNA_FILE_LOCATION);
1431
1432       bool success = testManager.execTestCases(sync_amount);
1433
1434       opt_response = (success ? "" : "not completely " /* completed cycle and no repeats, rare case */);
1435       opt_response += "processed ";
1436       opt_response += anna::functions::asString(sync_amount);
1437       opt_response += ((sync_amount > 1) ? " test cases synchronously" : " test case");
1438     }
1439     else if(param1 == "ip-limit") {
1440       if (numParams > 2)
1441         throw anna::RuntimeException("Wrong body content format on HTTP Request. Check 'HELP.md' for more information.", ANNA_FILE_LOCATION);
1442
1443       unsigned int limit;
1444       if (param2 != "") {
1445         limit = atoi(param2.c_str());
1446         testManager.setInProgressLimit(limit);
1447         opt_response = "new in-progress limit: ";
1448         opt_response += (limit != UINT_MAX) ? anna::functions::asString(limit) : "[no limit]";
1449       }
1450       else {
1451         opt_response = "in-progress limit amount: ";
1452         limit = testManager.getInProgressLimit();
1453         opt_response += (limit != UINT_MAX) ? anna::functions::asString(limit) : "[no limit]";
1454         opt_response += "; currently there are ";
1455         opt_response += anna::functions::asString(testManager.getInProgressCount());
1456         opt_response += " test cases running";
1457       }
1458     }
1459     else if(param1 == "repeats") {
1460       if (numParams != 2)
1461         throw anna::RuntimeException("Wrong body content format on HTTP Request. Check 'HELP.md' for more information.", ANNA_FILE_LOCATION);
1462       int repeats = atoi(param2.c_str());
1463       if (repeats < 0) repeats = -1;
1464       testManager.setPoolRepeats(repeats);
1465       std::string nolimit = (repeats != -1) ? "":" [no limit]";
1466       opt_response += anna::functions::asString("Pool repeats: %d%s (current cycle: %d)", repeats, nolimit.c_str(), testManager.getPoolCycle());
1467     }
1468     else if(param1 == "report") {
1469       if (numParams > 3)
1470         throw anna::RuntimeException("Wrong body content format on HTTP Request. Check 'HELP.md' for more information.", ANNA_FILE_LOCATION);
1471
1472       if(param2 == "") param2 = "all";
1473       if(param3 == "") param3 = "yes";
1474       bool enable = (param3 == "yes");
1475
1476       if(param2 == "initialized")
1477         testManager.setDumpInitializedReports(enable);
1478       else if(param2 == "in-progress")
1479         testManager.setDumpInProgressReports(enable);
1480       else if(param2 == "failed")
1481         testManager.setDumpFailedReports(enable);
1482       else if(param2 == "success")
1483         testManager.setDumpSuccessReports(enable);
1484       else if(param2 == "all") {
1485         param2 = "any";
1486         testManager.setDumpAllReports(enable);
1487       }
1488       else if(param2 == "none") {
1489         enable = !enable;
1490         param2 = "any";
1491         testManager.setDumpAllReports(enable);
1492       }
1493       else
1494         throw anna::RuntimeException("Wrong body content format on HTTP Request. Check 'HELP.md' for more information.", ANNA_FILE_LOCATION);
1495
1496       opt_response += (enable ? "report enabled " : "report disabled ");
1497       opt_response += "for tests in '";
1498       opt_response += param2;
1499       opt_response += "' state";
1500     }
1501     else if(param1 == "report-hex") {
1502       if (numParams > 2)
1503         throw anna::RuntimeException("Wrong body content format on HTTP Request. Check 'HELP.md' for more information.", ANNA_FILE_LOCATION);
1504
1505       if(param2 == "") param2 = "yes";
1506       testManager.setDumpHex((param2 == "yes"));
1507       opt_response += (testManager.getDumpHex() ? "report includes hexadecimal messages" : "report excludes hexadecimal messages");
1508     }
1509     else if(param1 == "dump-stdout") {
1510       if (numParams > 2)
1511         throw anna::RuntimeException("Wrong body content format on HTTP Request. Check 'HELP.md' for more information.", ANNA_FILE_LOCATION);
1512
1513       if(param2 == "") param2 = "yes";
1514       testManager.setDumpStdout((param2 == "yes"));
1515       opt_response += (testManager.getDumpHex() ? "test manager dumps progress into stdout" : "test manager does not dump progress into stdout");
1516     }
1517     else if(param1 == "goto") {
1518       if (numParams > 2)
1519         throw anna::RuntimeException("Wrong body content format on HTTP Request. Check 'HELP.md' for more information.", ANNA_FILE_LOCATION);
1520
1521       if(param2 == "") throw anna::RuntimeException("Missing id for test goto operation", ANNA_FILE_LOCATION);
1522       int id = atoi(param2.c_str());
1523       if (testManager.gotoTestCase(id)) {
1524         opt_response = "position updated for id provided (";
1525       }
1526       else {
1527         opt_response = "cannot found test id (";
1528       }
1529       opt_response += anna::functions::asString(id);
1530       opt_response += ")";
1531     }
1532     else if(param1 == "run") {
1533       if (numParams > 2)
1534         throw anna::RuntimeException("Wrong body content format on HTTP Request. Check 'HELP.md' for more information.", ANNA_FILE_LOCATION);
1535
1536       if(param2 == "") throw anna::RuntimeException("Missing id for test run operation", ANNA_FILE_LOCATION);
1537       int id = atoi(param2.c_str());
1538       if (testManager.runTestCase(id)) {
1539         opt_response = "test executed for id provided (";
1540       }
1541       else {
1542         opt_response = "cannot found test id (";
1543       }
1544       opt_response += anna::functions::asString(id);
1545       opt_response += ")";
1546     }
1547     else if(param1 == "look") {
1548       if (numParams > 2)
1549         throw anna::RuntimeException("Wrong body content format on HTTP Request. Check 'HELP.md' for more information.", ANNA_FILE_LOCATION);
1550
1551       int id = ((param2 != "") ? atoi(param2.c_str()) : -1);
1552       anna::testing::TestCase *testCase = testManager.findTestCase(id);
1553
1554       if (testCase) {
1555         response = testCase->asXMLString();
1556         return true; // OK
1557       }
1558       else {
1559         result = false;
1560         if (id == -1) {
1561           opt_response = "no current test case detected (testing started ?)";
1562         }
1563         else {
1564           opt_response = "cannot found test id (";
1565           opt_response += anna::functions::asString(id);
1566           opt_response += ")";
1567         }
1568       }
1569     }
1570     else if(param1 == "state") {
1571       if (numParams > 2)
1572         throw anna::RuntimeException("Wrong body content format on HTTP Request. Check 'HELP.md' for more information.", ANNA_FILE_LOCATION);
1573
1574       int id = ((param2 != "") ? atoi(param2.c_str()) : -1);
1575       anna::testing::TestCase *testCase = testManager.findTestCase(id);
1576
1577       if (testCase) {
1578         response = anna::testing::TestCase::asText(testCase->getState());
1579         return testCase->isSuccess();
1580       }
1581       else {
1582         result = false;
1583         if (id == -1) {
1584           opt_response = "no current test case detected (testing started ?)";
1585         }
1586         else {
1587           opt_response = "cannot found test id (";
1588           opt_response += anna::functions::asString(id);
1589           opt_response += ")";
1590         }
1591       }
1592     }
1593     else if (param1 == "interact") {
1594       if (numParams != 2)
1595         throw anna::RuntimeException("Wrong body content format on HTTP Request. Check 'HELP.md' for more information.", ANNA_FILE_LOCATION);
1596
1597       int amount = atoi(param2.c_str());
1598       if (amount < -1)
1599         throw anna::RuntimeException("Interactive amount must be -1 (to disable interactive mode) or a positive number.", ANNA_FILE_LOCATION);
1600
1601       int id = ((param3 != "") ? atoi(param3.c_str()) : -1);
1602       anna::testing::TestCase *testCase = testManager.findTestCase(id);
1603       if (testCase) {
1604         if (amount == -1) {
1605           testCase->makeInteractive(false);
1606           opt_response = "interactive mode disabled";
1607         }
1608         else {
1609           testCase->addInteractiveAmount(amount);
1610           opt_response = "added interactive amount of ";
1611           opt_response += anna::functions::asString(amount);
1612           opt_response += " units";
1613           if (amount == 0) opt_response += " (0: freezing a non-interactive testcase, no effect on already interactive)";
1614         }
1615         opt_response += " for test case id ";
1616         opt_response += anna::functions::asString(id);
1617       }
1618       else {
1619         result = false;
1620         opt_response = "cannot found test id (";
1621         opt_response += anna::functions::asString(id);
1622         opt_response += ")";
1623       }
1624     }
1625     else if(param1 == "reset") {
1626       if (numParams > 3)
1627         throw anna::RuntimeException("Wrong body content format on HTTP Request. Check 'HELP.md' for more information.", ANNA_FILE_LOCATION);
1628
1629       if(param2 == "") param2 = "soft";
1630      if (param2 != "soft" && param2 != "hard")
1631         throw anna::RuntimeException("Wrong body content format on HTTP Request. Check 'HELP.md' for more information.", ANNA_FILE_LOCATION);
1632
1633       int id = ((param3 != "") ? atoi(param3.c_str()) : -1);
1634       anna::testing::TestCase *testCase = ((id != -1) ? testManager.findTestCase(id) : NULL);
1635
1636       if (testCase) {
1637         bool done = testCase->reset(param2 == "hard");
1638         opt_response = "test ";
1639         opt_response += param2;
1640         opt_response += " reset for id ";
1641         opt_response += anna::functions::asString(id);
1642         opt_response += done ? ": done": ": not done";
1643       }
1644       else {
1645         if (id == -1) {
1646           bool anyReset = testManager.resetPool(param2 == "hard");
1647           opt_response = param2; opt_response += " reset have been sent to all programmed tests: "; opt_response += anyReset ? "some/all have been reset" : "nothing was reset";
1648         }
1649         else {
1650           result = false;
1651           opt_response = "cannot found test id (";
1652           opt_response += anna::functions::asString(id);
1653           opt_response += ")";
1654         }
1655       }
1656     }
1657     else if(param1 == "auto-reset") {
1658       if (numParams != 2)
1659         throw anna::RuntimeException("Wrong body content format on HTTP Request. Check 'HELP.md' for more information.", ANNA_FILE_LOCATION);
1660
1661       if (param2 != "soft" && param2 != "hard")
1662         throw anna::RuntimeException("Wrong body content format on HTTP Request. Check 'HELP.md' for more information.", ANNA_FILE_LOCATION);
1663
1664       testManager.setAutoResetHard(param2 == "hard");
1665       opt_response += anna::functions::asString("Auto-reset configured to '%s'", param2.c_str());
1666     }
1667     else if(param1 == "initialized") {
1668       if (numParams > 1)
1669         throw anna::RuntimeException("Wrong body content format on HTTP Request. Check 'HELP.md' for more information.", ANNA_FILE_LOCATION);
1670
1671       opt_response = anna::functions::asString("%lu", testManager.getInitializedCount());
1672     }
1673     else if(param1 == "finished") {
1674       if (numParams > 1)
1675         throw anna::RuntimeException("Wrong body content format on HTTP Request. Check 'HELP.md' for more information.", ANNA_FILE_LOCATION);
1676
1677       opt_response = anna::functions::asString("%lu", testManager.getFinishedCount());
1678     }
1679     else if(param1 == "clear") {
1680       if (numParams > 1)
1681         throw anna::RuntimeException("Wrong body content format on HTTP Request. Check 'HELP.md' for more information.", ANNA_FILE_LOCATION);
1682
1683       result = testManager.clearPool(opt_response);
1684     }
1685     else if(param1 == "junit") {
1686       response = testManager.junitAsXMLString();
1687       return true; // OK
1688     }
1689     else if(param1 == "summary-counts") {
1690       response = testManager.summaryCounts();
1691       return true; // OK
1692     }
1693     else if(param1 == "summary-states") {
1694       response = testManager.summaryStates();
1695       return true; // OK
1696     }
1697     else if(param1 == "summary") {
1698       response = testManager.asXMLString();
1699       return true; // OK
1700     }
1701     else {
1702       int id = atoi(param1.c_str());
1703       if(id < 0)
1704         throw anna::RuntimeException("Invalid test case identifier: must be a non-negative number", ANNA_FILE_LOCATION);
1705
1706       // PARAM: 1     2            3      4          5           6             7           8          9       10         11
1707       // test|<id>|<command>
1708       //             description|<description>
1709       //             ip-limit[|<iplimit>]
1710       //             timeout|    <msecs>
1711       //             sendxml2e|  <file>[|<step number>]
1712       //             sendxml2c|  <file>[|<step number>]
1713       //             delay|      [msecs]
1714       //             wait<fe/fc>|[code]|[bitR]|[hopByHop]|[applicationId]|[sessionId]|[resultCode]|[msisdn]|[imsi]|[serviceContextId]
1715       //      wait<fe/fc>-answer|<step number>
1716       //      wait<fe/fc>-xml   |<source_file>[|strict]
1717       //      wait<fe/fc>-hex   |<source_file>[|strict]
1718       if(param2 == "") throw anna::RuntimeException("Missing command for test id operation", ANNA_FILE_LOCATION);
1719
1720       // Commands:
1721       if (param2 == "description") {
1722         if (numParams > 3)
1723           throw anna::RuntimeException("Wrong body content format on HTTP Request. Check 'HELP.md' for more information.", ANNA_FILE_LOCATION);
1724         if(param3 == "") throw anna::RuntimeException("Missing description for test case", ANNA_FILE_LOCATION);
1725         testManager.getTestCase(id)->setDescription(param3); // creates / reuses
1726       }
1727       else if (param2 == "ip-limit") {
1728         if (numParams > 3)
1729           throw anna::RuntimeException("Wrong body content format on HTTP Request. Check 'HELP.md' for more information.", ANNA_FILE_LOCATION);
1730         unsigned int limit = (param3 == "") ? 1 : atoi(param3.c_str());
1731         testManager.getTestCase(id)->addIpLimit(limit); // creates / reuses
1732       }
1733       else if (param2 == "timeout") {
1734         if (numParams > 3)
1735           throw anna::RuntimeException("Wrong body content format on HTTP Request. Check 'HELP.md' for more information.", ANNA_FILE_LOCATION);
1736         if(param3 == "") throw anna::RuntimeException("Missing milliseconds for 'timeout' command in test id operation", ANNA_FILE_LOCATION);
1737         anna::Millisecond timeout = checkTimeMeasure("Test case timeout", param3);
1738         testManager.getTestCase(id)->addTimeout(timeout); // creates / reuses
1739       }
1740       else if ((param2 == "sendxml2e")||(param2 == "sendxml2c")) {
1741         if (numParams > 4)
1742           throw anna::RuntimeException("Wrong body content format on HTTP Request. Check 'HELP.md' for more information.", ANNA_FILE_LOCATION);
1743         if(param3 == "") throw anna::RuntimeException(anna::functions::asString("Missing xml file for '%s' command in test id operation", param2.c_str()), ANNA_FILE_LOCATION);
1744         codecMsg.loadXMLFile(param3);
1745         LOGWARNING(
1746           if (!codecMsg.isRequest() && (param4 == ""))
1747             anna::Logger::warning("Step number has not been provided. Take into account that this answer message will be sent 'as is' and sequence information could be wrong at the remote peer", ANNA_FILE_LOCATION);
1748         );
1749
1750         updateOperatedOriginHostWithMessage(codecMsg);
1751         int stepNumber = ((param4 != "") ? atoi(param4.c_str()):-1);
1752
1753         if (param2 == "sendxml2e")
1754           testManager.getTestCase(id)->addSendDiameterXml2e(codecMsg.code(), getOperatedHost(), stepNumber); // creates / reuses
1755         else
1756           testManager.getTestCase(id)->addSendDiameterXml2c(codecMsg.code(), getOperatedHost(), stepNumber); // creates / reuses
1757       }
1758       else if (param2 == "delay") {
1759         if (numParams > 3)
1760           throw anna::RuntimeException("Wrong body content format on HTTP Request. Check 'HELP.md' for more information.", ANNA_FILE_LOCATION);
1761         if(param3 == "") throw anna::RuntimeException("Missing milliseconds for 'delay' command in test id operation", ANNA_FILE_LOCATION);
1762         anna::Millisecond delay = ((param3 == "0" /* special case */) ? (anna::Millisecond)0 : checkTimeMeasure("Test case delay step", param3));
1763         testManager.getTestCase(id)->addDelay(delay); // creates / reuses
1764       }
1765
1766 // TODO(***)
1767 //                                        The way to identify the test case, is through registered Session-Id values for
1768 //                                         programmed requests. But this depends on the type of node. Acting as clients,
1769 //                                         requests received have Session-Id values which are already registered with
1770 //                                         one test case, causing an error if not found. Acting as servers, requests are
1771 //                                         received over a diameter local server from a client which are generating that
1772 //                                         Session-Id values. Then we know nothing about such values. The procedure in
1773 //                                         this case is find out a test case not-started containing a condition which
1774 //                                         comply with the incoming message, and reactivates it.
1775 // The other solution: register Session-Id values for answers send to client from a local diameter server.
1776       else if ((param2 == "waitfe")||(param2 == "waitfc")) {
1777         if (numParams > 11)
1778           throw anna::RuntimeException("Wrong body content format on HTTP Request. Check 'HELP.md' for more information.", ANNA_FILE_LOCATION);
1779         if (param3 != "" || param4 != "" || param5 != "" || param6 != "" || param7 != "" || param8 != "" || param9 != "" || param10 != "" || param11 != "") {
1780           bool fromEntity = (param2.substr(4,2) == "fe");
1781           testManager.getTestCase(id)->addWaitDiameter(fromEntity, param3, param4, param5, param6, param7, param8, param9, param10, param11);
1782         }
1783         else {
1784           throw anna::RuntimeException(anna::functions::asString("Missing condition for '%s' command in test id operation", param2.c_str()), ANNA_FILE_LOCATION);
1785         }
1786       }
1787       else if ((param2 == "waitfe-hex")||(param2 == "waitfc-hex")) {
1788         if (numParams > 4)
1789           throw anna::RuntimeException("Wrong body content format on HTTP Request. Check 'HELP.md' for more information.", ANNA_FILE_LOCATION);
1790         if(param3 == "") throw anna::RuntimeException(anna::functions::asString("Missing hex file for '%s' command in test id operation", param2.c_str()), ANNA_FILE_LOCATION);
1791
1792         // Get DataBlock from file with hex content:
1793         if(!getDataBlockFromHexFile(param3, db_aux))
1794           throw anna::RuntimeException("Error reading hex content from file provided", ANNA_FILE_LOCATION);
1795
1796         // Hexadecimal representation read from file:
1797         std::string regexp = anna::functions::asHexString(db_aux);
1798
1799         // optional 'full':
1800         if(param4 != "strict") {
1801           //// If request, we will ignore sequence data:
1802           //if (anna::diameter::codec::functions::requestBit(db_aux))
1803             regexp.replace (24, 16, "[0-9A-Fa-f]{16}");
1804
1805           regexp.insert(0, "^");
1806           regexp += "$";
1807         }
1808
1809         bool fromEntity = (param2.substr(4,2) == "fe");
1810         testManager.getTestCase(id)->addWaitDiameterRegexpHex(fromEntity, regexp);
1811       }
1812       else if ((param2 == "waitfe-xml")||(param2 == "waitfc-xml")) {
1813         if (numParams > 4)
1814           throw anna::RuntimeException("Wrong body content format on HTTP Request. Check 'HELP.md' for more information.", ANNA_FILE_LOCATION);
1815         if(param3 == "") throw anna::RuntimeException(anna::functions::asString("Missing xml file for '%s' command in test id operation", param2.c_str()), ANNA_FILE_LOCATION);
1816
1817         codecMsg.loadXMLFile(param3);
1818         std::string regexp = codecMsg.asXMLString(true /* normalization */);
1819
1820         // Now we must insert regular expressions in hop-by-hop, end-to-end and Origin-State-Id:
1821
1822         // optional 'full':
1823         if(param4 != "strict") {
1824           std::string::size_type pos, pos_1, pos_2;
1825
1826           pos = regexp.find("end-to-end-id=", 0u);
1827           if (pos != std::string::npos) {
1828             pos = regexp.find("\"", pos);
1829             pos_1 = pos;
1830             pos = regexp.find("\"", pos+1);
1831             pos_2 = pos;
1832             regexp.replace(pos_1 + 1, pos_2 - pos_1 - 1, "[0-9]+");
1833           }
1834
1835           pos = regexp.find("hop-by-hop-id=", 0u);
1836           if (pos != std::string::npos) {
1837             pos = regexp.find("\"", pos);
1838             pos_1 = pos;
1839             pos = regexp.find("\"", pos+1);
1840             pos_2 = pos;
1841             regexp.replace(pos_1 + 1, pos_2 - pos_1 - 1, "[0-9]+");
1842           }
1843
1844           // For this representation: <avp name="Origin-State-Id" data="1428633668"/>
1845           //pos = regexp.find("Origin-State-Id", 0u);
1846           //pos = regexp.find("\"", pos);
1847           //pos = regexp.find("\"", pos+1);
1848           //pos_1 = pos;
1849           //pos = regexp.find("\"", pos+1);
1850           //pos_2 = pos;
1851           //regexp.replace(pos_1 + 1, pos_2 - pos_1 - 1, "[0-9]+");
1852           // But we have this one: <avp data="1428633668" name="Origin-State-Id"/>
1853           pos = regexp.find("Origin-State-Id", 0u);
1854           if (pos != std::string::npos) {
1855             pos = regexp.rfind("\"", pos);
1856             pos = regexp.rfind("\"", pos-1);
1857             pos = regexp.rfind("\"", pos-1);
1858             pos_1 = pos;
1859             pos = regexp.find("\"", pos+1);
1860             pos_2 = pos;
1861             regexp.replace(pos_1 + 1, pos_2 - pos_1 - 1, "[0-9]+");
1862           }
1863
1864           //regexp.insert(0, "^");
1865           //regexp += "$";
1866         }
1867
1868         bool fromEntity = (param2.substr(4,2) == "fe");
1869         testManager.getTestCase(id)->addWaitDiameterRegexpXml(fromEntity, regexp);
1870       }
1871       else if (param2 == "sh-command") {
1872         // Allow pipes in command:
1873         //if (numParams > 4)
1874         //  throw anna::RuntimeException("Wrong body content format on HTTP Request. Check 'HELP.md' for more information.", ANNA_FILE_LOCATION);
1875         if(param3 == "") throw anna::RuntimeException("Missing script/executable command-line for 'sh-command' in test id operation", ANNA_FILE_LOCATION);
1876         std::string token = "|sh-command|";
1877         std::string command = operation.substr(operation.find(token) + token.size());
1878         testManager.getTestCase(id)->addCommand(command); // creates / reuses
1879       }
1880       else {
1881         throw anna::RuntimeException("Wrong body content format on HTTP Request. Check 'HELP.md' for more information.", ANNA_FILE_LOCATION);
1882       }
1883     }
1884
1885   } else if(opType == "loadxml") {
1886     codecMsg.loadXMLFile(param1);
1887     response = codecMsg.asXMLString();
1888     return true; // OK
1889   } else if(opType == "diameterServerSessions") {
1890     int diameterServerSessions = atoi(param1.c_str());
1891     getOperatedServer()->setMaxConnections(diameterServerSessions);
1892
1893
1894
1895   } else {
1896     throw anna::RuntimeException("Wrong body content format on HTTP Request. Check 'HELP.md' for more information.", ANNA_FILE_LOCATION);
1897   }
1898
1899
1900
1901   // HTTP response
1902   response = "Operation correctly processed: "; response += operation;
1903   if (opt_response != "") {
1904     response += " => ";
1905     response += opt_response;
1906   }
1907
1908   return result;
1909 }
1910
1911 anna::xml::Node* Launcher::asXML(anna::xml::Node* parent) const
1912 {
1913   anna::xml::Node* result = parent->createChild("launcher");
1914   anna::comm::Application::asXML(result);
1915   // Timming:
1916   result->createAttribute("StartTime", a_start_time.asString());
1917   result->createAttribute("InitialWorkingDirectory", a_initialWorkingDirectory);
1918   result->createAttribute("SecondsLifeTime", anna::time::functions::lapsedMilliseconds() / 1000);
1919   // Diameter:
1920   anna::diameter::comm::OriginHostManager &ohm = anna::diameter::comm::OriginHostManager::instantiate();
1921   for (diameter::comm::origin_hosts_it it = ohm.begin(); it != ohm.end(); it++) {
1922     it->second->asXML(result);
1923   }
1924
1925   // Registered codec engines:
1926   anna::diameter::codec::EngineManager &em = anna::diameter::codec::EngineManager::instantiate();
1927   em.asXML(result);
1928
1929   // OAM & statistics:
1930   oamAsXML(result);
1931   statsAsXML(result);
1932
1933   // Testing: could be heavy if test case reports are enabled
1934   anna::testing::TestManager::instantiate().asXML(result);
1935
1936   return result;
1937 }
1938
1939 anna::xml::Node* Launcher::oamAsXML(anna::xml::Node* parent) const
1940 {
1941   anna::xml::Node* result = parent->createChild("Oam");
1942
1943   // OAM:
1944   anna::diameter::comm::OamModule::instantiate().asXML(result);
1945   anna::diameter::comm::ApplicationMessageOamModule::instantiate().asXML(result);
1946   anna::diameter::codec::OamModule::instantiate().asXML(result);
1947
1948   return result;
1949 }
1950
1951 anna::xml::Node* Launcher::statsAsXML(anna::xml::Node* parent) const
1952 {
1953   anna::xml::Node* result = parent->createChild("Statistics");
1954
1955   // Statistics:
1956   anna::statistics::Engine::instantiate().asXML(result);
1957
1958   return result;
1959 }