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