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