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