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