Fixes and improvs. Basic DRA feature.
[anna.git] / example / diameter / launcher / Launcher.cpp
index eb4dfe7..a6c5bee 100644 (file)
@@ -31,8 +31,8 @@
 #include <TestCase.hpp>
 
 
-#define SIGUSR2_TASKS_INPUT_FILENAME "./sigusr2.tasks.input"
-#define SIGUSR2_TASKS_OUTPUT_FILENAME "./sigusr2.tasks.output"
+#define SIGUSR2_TASKS_INPUT_FILENAME "./sigusr2.in"
+#define SIGUSR2_TASKS_OUTPUT_FILENAME "./sigusr2.out"
 
 
 
@@ -47,19 +47,18 @@ const char *ServicesDTD = "\
 <!--\n\
    Stack record\n\
 \n\
-   id:         Normally the id corresponds to the Application-Id for which the dictionary provided is designed.\n\
+   id:         Normally the id corresponds to the Application-Id for which the dictionary provided is designed\n\
                (in multistack applications, it shall be mandatory respect such association to know the stack used\n\
                for processed messages).\n\
    dictionary: Path to the dictionary file\n\
 -->\n\
 \n\
 <!ELEMENT node EMPTY>\n\
-<!ATTLIST node originRealm CDATA #REQUIRED applicationId CDATA #REQUIRED originHost CDATA #IMPLIED 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>\n\
+<!ATTLIST node originRealm CDATA #REQUIRED originHost CDATA #IMPLIED 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>\n\
 <!--\n\
    Node record\n\
 \n\
    originRealm:                             Node identifier (Origin-Realm name).\n\
-   applicationId:                           The Application-Id provided must exists as a registered 'stack id'.\n\
    originHost:                              Diameter application host name (system name). If missing, process sets o.s. hostname\n\
                                             Note that if you have two or more realms, the names must be different.\n\
    cer:                                     User defined CER path file to be encoded to establish diameter connections.\n\
@@ -112,6 +111,7 @@ const char *ServicesDTD = "\
 
 Launcher::Launcher() : anna::comm::Application("launcher", "DiameterLauncher", "1.1"), a_communicator(NULL) {
   a_codecEngine = new anna::diameter::codec::Engine("MyCodecEngine");
+  a_baseProtocolDictionary = NULL;
   a_timeEngine = NULL;
   a_counterRecorder = NULL;
   a_admlMinResolution = 2 * anna::timex::Engine::minResolution; // 2*10 = 20 ms; 1000/20 = 50 ticks per second;
@@ -131,8 +131,8 @@ void Launcher::servicesFromXML(const anna::xml::Node* servicesNode, bool eventOp
   //<!ATTLIST stack id CDATA #REQUIRED dictionary CDATA #REQUIRED>
   const anna::xml::Attribute  *id, *dictionary;
 
-  // <!ATTLIST node originRealm CDATA #REQUIRED applicationId CDATA #REQUIRED originHost CDATA #IMPLIED 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>
-  const anna::xml::Attribute  *originRealm, *appId, *originHost, *cer, *dwr, *allowedInactivityTime, *tcpConnectDelay,
+  // <!ATTLIST node originRealm CDATA #REQUIRED originHost CDATA #IMPLIED 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>
+  const anna::xml::Attribute  *originRealm, *originHost, *cer, *dwr, *allowedInactivityTime, *tcpConnectDelay,
                               *answersTimeout, *ceaTimeout, *watchdogPeriod, *entity, *entityServerSessions,
                               *diameterServer, *diameterServerSessions, *balance, *sessionBasedModelsClientSocketSelection,
                               *retries, *log, *splitLog, *detailedLog, *dumpLog, *burstLog;
@@ -149,6 +149,8 @@ void Launcher::servicesFromXML(const anna::xml::Node* servicesNode, bool eventOp
   anna::diameter::comm::ApplicationMessageOamModule & appMsgOamModule = anna::diameter::comm::ApplicationMessageOamModule::instantiate();
   appMsgOamModule.enableCounters(); // this special module is disabled by default (the only)
   static int scope_id = 3;
+  bool id_0_registered = false;
+  unsigned int id_value;
 
   for(anna::xml::Node::const_child_iterator it = servicesNode->child_begin(); it != servicesNode->child_end(); it++) {
     std::string nodeName = (*it)->getName();
@@ -157,32 +159,44 @@ void Launcher::servicesFromXML(const anna::xml::Node* servicesNode, bool eventOp
       // Input data:
       id = (*it)->getAttribute("id");
       dictionary = (*it)->getAttribute("dictionary");
+      id_value = id->getIntegerValue();
 
       try {
-        d = stackEngine.createDictionary(id->getIntegerValue(), dictionary->getValue());
+        d = stackEngine.createDictionary(id_value, dictionary->getValue());
         getCodecEngine()->setDictionary(d);
 
         // OAM module for counters:
-        appMsgOamModule.createStackCounterScope(scope_id, id->getIntegerValue() /* application-id */);
+        appMsgOamModule.createStackCounterScope(scope_id, id_value /* application-id */);
         scope_id++;
 
       } catch(anna::RuntimeException &ex) {
         //_exit(ex.asString());
         throw ex;
       }
+
+      if (id_value == 0)
+        id_0_registered = true;
+        a_baseProtocolDictionary = d;
     }
   }
 
   // Show loaded stacks:
   std::cout << "Stacks currently loaded:" << std::endl;
-  std::cout << anna::functions::tab(stackEngine.asString(false /* light */));
-  std::cout << std::endl;
+  std::cout << anna::functions::tab(stackEngine.asString(false /* light */)) << std::endl;
 
 
   // Codec engine adjustments:
   // Auto stack selection based on Application-ID:
   bool multistack = (stackEngine.stack_size() > 1);
-  if (multistack) getCodecEngine()->selectStackWithApplicationId(true);
+  if (multistack) {
+    getCodecEngine()->selectStackWithApplicationId(true);
+    // In multistack, id = 0 MUST be registered:
+    if (!id_0_registered)
+      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);
+  }
+  else {
+    a_baseProtocolDictionary = d;
+  }
 
   for(anna::xml::Node::const_child_iterator it = servicesNode->child_begin(); it != servicesNode->child_end(); it++) {
     std::string nodeName = (*it)->getName();
@@ -190,7 +204,6 @@ void Launcher::servicesFromXML(const anna::xml::Node* servicesNode, bool eventOp
     if(nodeName == "node") {
       // Input data:
       originRealm = (*it)->getAttribute("originRealm");
-      appId = (*it)->getAttribute("applicationId");
       originHost = (*it)->getAttribute("originHost", false /* no exception */);
       cer = (*it)->getAttribute("cer", false /* no exception */);
       dwr = (*it)->getAttribute("dwr", false /* no exception */);
@@ -213,10 +226,6 @@ void Launcher::servicesFromXML(const anna::xml::Node* servicesNode, bool eventOp
       burstLog = (*it)->getAttribute("burstLog", false /* no exception */); // (yes | no)
 
       // Basic checkings:
-      if (stackEngine.getDictionary(appId->getIntegerValue()) == NULL) {
-        std::string msg = "Cannot found a registered stack id with the value of applicationId provided: "; msg += appId->getValue();
-        throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
-      }
       realm_nodes_it nodeIt = a_nodes.find(originRealm->getValue());
       if (nodeIt != a_nodes.end()) {
         std::string msg = "Already registered node name (Origin-Realm): "; msg += originRealm->getValue();
@@ -251,7 +260,7 @@ void Launcher::servicesFromXML(const anna::xml::Node* servicesNode, bool eventOp
       }
 
       // Create new Node instance /////////////////////////////////////////////////////////////////
-      a_workingNode = new RealmNode(originRealm->getValue(), appId->getIntegerValue(), a_codecEngine);
+      a_workingNode = new RealmNode(originRealm->getValue(), a_codecEngine, a_baseProtocolDictionary);
       MyDiameterEngine *commEngine = a_workingNode->getMyDiameterEngine();
       /////////////////////////////////////////////////////////////////////////////////////////////
 
@@ -730,6 +739,7 @@ void Launcher::signalUSR2() throw(anna::RuntimeException) {
     msg += "')";
     anna::Logger::notice(msg, ANNA_FILE_LOCATION);
   );
+
   // Operation:
   std::string line;
   std::string response_content;
@@ -753,7 +763,7 @@ void Launcher::signalUSR2() throw(anna::RuntimeException) {
       ex.trace();
     }
 
-    out_file << response_content;
+    out_file << response_content << "\n";
   }
 
   in_file.close();
@@ -806,27 +816,53 @@ std::string Launcher::help() const throw() {
   result += "\n";
   result += "\nStart the launcher process without arguments in order to see all the startup configuration";
   result += "\n posibilities, many of which could be modified on the air through the management interface";
-  result += "\n (we will talk later about this great feature). Some of the more common parameters are:";
+  result += "\n (we will talk later about this great feature). There is only one mandatory parameter which";
+  result += "\n is the services definition: --services <services xml file>. You must follow the dtd schema";
+  result += "\n to build a valid services xml file. Some basic examples are:";
+  result += "\n";
+  result += "\nClient configuration:";
+  result += "\n";
+  result += "\n<services>";
+  result += "\n  <!-- Stacks -->";
+  result += "\n  <stack id=\"0\" dictionary=\"dictionary.xml\"/>";
   result += "\n";
-  result += "\nAs mandatory, the stacks enabled given through the applicationId and the xml dictionary:";
-  result += "\n   --stacks <appid1,dictionary1#appid2,dictionary2#...#appidN,dictionaryN>";
+  result += "\n  <!-- Nodes -->";
+  result += "\n  <node originRealm=\"ADML-client\" entity=\"localhost:3868\"/>";
+  result += "\n</services>";
   result += "\n";
-  result += "\nActing as a diameter server (accepting i.e. 10 connections), you would have:";
-  result += "\n   --diameterServer localhost:3868 --diameterServerSessions 10 --entityServerSessions 0";
+  result += "\nServer configuration:";
   result += "\n";
-  result += "\nActing as a diameter client (launching i.e. 10 connections to each entity server), you would have:";
-  result += "\n   --entity 192.168.12.11:3868,192.168.12.21:3868 --entityServerSessions 10 --diameterServerSessions 0";
+  result += "\n<services>";
+  result += "\n  <!-- Stacks -->";
+  result += "\n  <stack id=\"0\" dictionary=\"dictionary.xml\"/>";
+  result += "\n";
+  result += "\n  <!-- Nodes -->";
+  result += "\n  <node originRealm=\"ADML-server\" diameterServer=\"localhost:3868\"/>";
+  result += "\n</services>";
   result += "\n";
   result += "\nIf you act as a proxy or a translation agent, you need to combine both former setups, and probably";
   result += "\n will need to program the answers to be replied through the operations interface. To balance the";
   result += "\n traffic at your client side you shall use '--balance' and '--sessionBasedModelsClientSocketSelection'";
-  result += "\n arguments in order to define the balancing behaviour.";
+  result += "\n arguments in order to define the balancing behaviour. To make hybrid setups you only must mix the realms:";
   result += "\n";
-  result += "\nThe process builds automatically CER and DWR messages as a client, but you could specify your own";
-  result += "\n customized ones using '--cer <xml message file>' and '--dwr <xml message file>'.";
-  result += "\nThe process builds automatically CEA and DWA messages as a server, but you could program your own";
-  result += "\n customized ones using operations interface.";
+  result += "\nClient and server configuration:";
+  result += "\n";
+  result += "\n<services>";
+  result += "\n  <!-- Stacks -->";
+  result += "\n  <stack id=\"16777236\" dictionary=\"dictionary_Rx.xml\"/>";
+  result += "\n  <stack id=\"16777238\" dictionary=\"dictionary_Gx.xml\"/>";
+  result += "\n  <stack id=\"0\" dictionary=\"dictionary_base.xml\"/>";
   result += "\n";
+  result += "\n  <!-- Nodes -->";
+  result += "\n  <node originRealm=\"ADML-Rx-client\" entity=\"localhost:3868\" cer=\"cer_Rx.xml\"/>";
+  result += "\n  <node originRealm=\"ADML-Gx-client\" entity=\"localhost:3868\" cer=\"cer_Gx.xml\"/>";
+  result += "\n</services>";
+  result += "\n";
+  result += "\n";
+  result += "\nThe process builds automatically CER and DWR messages as a client, but you could specify your own";
+  result += "\n as shown in the hybrid former example. Note that the base protocol stack must be registered because";
+  result += "\n the configuration corresponds to a multistack process which change the stack using the application-id";
+  result += "\n processed (0 in the case of base protocol messages: CER, CEA, DWR, DWA, DPR, DPA).";
   result += "\n";
   result += "\nDYNAMIC OPERATIONS";
   result += "\n------------------";
@@ -837,13 +873,13 @@ std::string Launcher::help() const throw() {
   result += "\n";
   result += "\n--------------------------------------------------------------------------------------- General purpose";
   result += "\n";
-  result += "\nhelp                                 This help. Startup information-level traces also dump this help.";
+  result += "\nhelp                                 This help.";
   result += "\n";
   result += "\n---------------------------------------------------------------------------------------- Node selection";
   result += "\n";
   result += "\nnode[|<name>]                         Select current working node by mean the registered name.";
   result += "\n                                      All the subsequent operations will be referred to this node.";
-  result += "\n                                      Without argument, the current node is dumped on stdout.";
+  result += "\n                                      Without argument, the current node information is retrieved.";
   result += "\n";
   result += "\n------------------------------------------------------------------------------------ Parsing operations";
   result += "\n";
@@ -1241,11 +1277,14 @@ std::string Launcher::help() const throw() {
   result += "\n interface.";
   result += "\n";
   result += "\n";
+
   return result;
 }
 
 void Launcher::eventOperation(const std::string &operation, std::string &response_content) throw(anna::RuntimeException) {
   LOGMETHOD(anna::TraceMethod tm("Launcher", "eventOperation", ANNA_FILE_LOCATION));
+  if (operation == "") return; // ignore
+
   CommandLine& cl(anna::CommandLine::instantiate());
   TestManager &testManager = TestManager::instantiate();
   LOGDEBUG(anna::Logger::debug(operation, ANNA_FILE_LOCATION));
@@ -1253,21 +1292,17 @@ void Launcher::eventOperation(const std::string &operation, std::string &respons
   // Default response:
   response_content = "Operation processed with exception (see traces): ";
   response_content += operation;
-  response_content += "\n";
 
-  std::string result_msg = "";
-  anna::DataBlock db_aux(true);
 
+  std::string opt_response_content = ""; // aditional response content
+  anna::DataBlock db_aux(true);
 
   ///////////////////////////////////////////////////////////////////
   // Simple operations without arguments:
 
   // Help:
   if(operation == "help") {
-    std::string s_help = help();
-    std::cout << s_help << std::endl;
-    LOGINFORMATION(anna::Logger::information(s_help, ANNA_FILE_LOCATION));
-    response_content = "Help dumped on stdout and information-level traces\n";
+    response_content = help();
     return;
   }
 
@@ -1275,14 +1310,14 @@ void Launcher::eventOperation(const std::string &operation, std::string &respons
   if(operation == "collect") {
     resetCounters();
     resetStatistics();
-    response_content = "All process counters & statistic information have been reset\n";
+    response_content = "All process counters & statistic information have been reset";
     return;
   }
 
   // Counters dump on demand:
   if(operation == "forceCountersRecord") {
     forceCountersRecord();
-    response_content = "Current counters have been dump to disk\n";
+    response_content = "Current counters have been dump to disk";
     return;
   }
 
@@ -1351,7 +1386,7 @@ void Launcher::eventOperation(const std::string &operation, std::string &respons
   if(opType == "context") {
     std::string contextFile = ((numParams == 1) ? param1 : anna::functions::asString("/var/tmp/anna.context.%05d", getPid()));
     writeContext(contextFile);
-    response_content = anna::functions::asString("Context dumped on file '%s'\n", contextFile.c_str());
+    response_content = anna::functions::asString("Context dumped on file '%s'", contextFile.c_str());
     return;
   }
 
@@ -1362,20 +1397,20 @@ void Launcher::eventOperation(const std::string &operation, std::string &respons
     }
     catch(anna::RuntimeException &ex) {
       ex.trace();
-      response_content = anna::functions::asString("Loaded services from file '%s' with some problems (ignored ones)\n", servicesFile.c_str());
+      response_content = anna::functions::asString("Loaded services from file '%s' with some problems (ignored ones)", servicesFile.c_str());
       return;
     }
-    response_content = anna::functions::asString("Loaded services from file '%s'\n", servicesFile.c_str());
+    response_content = anna::functions::asString("Loaded services from file '%s'", servicesFile.c_str());
     return;
   }
 
   // Realm switch:
   if(opType == "node") {
     if (param1 != "") {
-      if (setWorkingNode(param1)) response_content = anna::functions::asString("Current node is now '%s'\n", param1.c_str());
+      if (setWorkingNode(param1)) response_content = anna::functions::asString("Current node is now '%s'", param1.c_str());
     }
     else {
-      std::cout << getWorkingNode()->asXMLString() << std::endl;
+      response_content = getWorkingNode()->asXMLString();
     }
     return;
   }
@@ -1420,9 +1455,9 @@ void Launcher::eventOperation(const std::string &operation, std::string &respons
 
         if(opType == "show") commEngine->findClientSession(key)->show();
 
-        if(opType == "hidden") result_msg = commEngine->findClientSession(key)->hidden() ? "true" : "false";
+        if(opType == "hidden") opt_response_content = commEngine->findClientSession(key)->hidden() ? "true" : "false";
 
-        if(opType == "shown") result_msg = commEngine->findClientSession(key)->shown() ? "true" : "false";
+        if(opType == "shown") opt_response_content = commEngine->findClientSession(key)->shown() ? "true" : "false";
       } else {
         std::string address;
         int port;
@@ -1432,18 +1467,18 @@ void Launcher::eventOperation(const std::string &operation, std::string &respons
 
         if(opType == "show") commEngine->findServer(address, port)->show();
 
-        if(opType == "hidden") result_msg = commEngine->findServer(address, port)->hidden() ? "true" : "false";
+        if(opType == "hidden") opt_response_content = commEngine->findServer(address, port)->hidden() ? "true" : "false";
 
-        if(opType == "shown") result_msg = commEngine->findServer(address, port)->shown() ? "true" : "false";
+        if(opType == "shown") opt_response_content = commEngine->findServer(address, port)->shown() ? "true" : "false";
       }
     } else {
       if(opType == "hide") entity->hide();
 
       if(opType == "show") entity->show();
 
-      if(opType == "hidden") result_msg = entity->hidden() ? "true" : "false";
+      if(opType == "hidden") opt_response_content = entity->hidden() ? "true" : "false";
 
-      if(opType == "shown") result_msg = entity->shown() ? "true" : "false";
+      if(opType == "shown") opt_response_content = entity->shown() ? "true" : "false";
     }
   } else if((opType == "sendxml") || (opType == "sendxml2e") || (opType == "sendhex") || (opType == "sendhex2e")) {
     if(!entity) throw anna::RuntimeException("No entity configured to send the message", ANNA_FILE_LOCATION);
@@ -1490,9 +1525,9 @@ void Launcher::eventOperation(const std::string &operation, std::string &respons
     // burst|look|<order>              Show programmed burst message for order provided, current when missing.
 
     if(param1 == "clear") {
-      result_msg = "removed ";
-      result_msg += anna::functions::asString(getWorkingNode()->clearBurst());
-      result_msg += " elements";
+      opt_response_content = "removed ";
+      opt_response_content += anna::functions::asString(getWorkingNode()->clearBurst());
+      opt_response_content += " elements";
     } else if(param1 == "load") {
       if(param2 == "") throw anna::RuntimeException("Missing xml path file for burst load operation", ANNA_FILE_LOCATION);
 
@@ -1502,10 +1537,10 @@ void Launcher::eventOperation(const std::string &operation, std::string &respons
       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)
 
       int position = getWorkingNode()->loadBurstMessage(codecMsg.code());
-      result_msg = "loaded '";
-      result_msg += param2;
-      result_msg += "' file into burst list position ";
-      result_msg += anna::functions::asString(position);
+      opt_response_content = "loaded '";
+      opt_response_content += param2;
+      opt_response_content += "' file into burst list position ";
+      opt_response_content += anna::functions::asString(position);
     } else if(param1 == "start") {
       if(param2 == "") throw anna::RuntimeException("Missing initial load for burst start operation", ANNA_FILE_LOCATION);
 
@@ -1513,8 +1548,8 @@ void Launcher::eventOperation(const std::string &operation, std::string &respons
       int processed = getWorkingNode()->startBurst(initialLoad);
 
       if(processed > 0) {
-        result_msg = "initial load completed for ";
-        result_msg += anna::functions::entriesAsString(processed, "message");
+        opt_response_content = "initial load completed for ";
+        opt_response_content += anna::functions::entriesAsString(processed, "message");
       }
     } else if(param1 == "push") {
       if(param2 == "") throw anna::RuntimeException("Missing load amount for burst push operation", ANNA_FILE_LOCATION);
@@ -1522,8 +1557,8 @@ void Launcher::eventOperation(const std::string &operation, std::string &respons
       int pushed = getWorkingNode()->pushBurst(atoi(param2.c_str()));
 
       if(pushed > 0) {
-        result_msg = "pushed ";
-        result_msg += anna::functions::entriesAsString(pushed, "message");
+        opt_response_content = "pushed ";
+        opt_response_content += anna::functions::entriesAsString(pushed, "message");
       }
     } else if(param1 == "pop") {
       if(param2 == "") throw anna::RuntimeException("Missing amount for burst pop operation", ANNA_FILE_LOCATION);
@@ -1532,39 +1567,39 @@ void Launcher::eventOperation(const std::string &operation, std::string &respons
       int popped = getWorkingNode()->popBurst(releaseLoad);
 
       if(popped > 0) {
-        result_msg = "burst popped for ";
-        result_msg += anna::functions::entriesAsString(popped, "message");
+        opt_response_content = "burst popped for ";
+        opt_response_content += anna::functions::entriesAsString(popped, "message");
       }
     } else if(param1 == "stop") {
       int left = getWorkingNode()->stopBurst();
 
       if(left != -1) {
-        result_msg += anna::functions::entriesAsString(left, "message");
-        result_msg += " left to the end of the cycle";
+        opt_response_content += anna::functions::entriesAsString(left, "message");
+        opt_response_content += " left to the end of the cycle";
       }
     } else if(param1 == "repeat") {
       if(param2 == "") param2 = "yes";
 
       bool repeat = (param2 == "yes");
       getWorkingNode()->repeatBurst(repeat);
-      result_msg += (repeat ? "repeat enabled" : "repeat disabled");
+      opt_response_content += (repeat ? "repeat enabled" : "repeat disabled");
     } else if(param1 == "send") {
       if(param2 == "") throw anna::RuntimeException("Missing amount for burst send operation", ANNA_FILE_LOCATION);
 
       int sent = getWorkingNode()->sendBurst(atoi(param2.c_str()));
 
       if(sent > 0) {
-        result_msg = "sent ";
-        result_msg += anna::functions::entriesAsString(sent, "message");
+        opt_response_content = "sent ";
+        opt_response_content += anna::functions::entriesAsString(sent, "message");
       }
     } else if(param1 == "goto") {
       if(param2 == "") throw anna::RuntimeException("Missing order position for burst goto operation", ANNA_FILE_LOCATION);
 
-      result_msg = getWorkingNode()->gotoBurst(atoi(param2.c_str()));
+      opt_response_content = getWorkingNode()->gotoBurst(atoi(param2.c_str()));
     } else if(param1 == "look") {
       int order = ((param2 != "") ? atoi(param2.c_str()) : -1);
-      result_msg = "\n\n";
-      result_msg += getWorkingNode()->lookBurst(order);
+      opt_response_content = "\n\n";
+      opt_response_content += getWorkingNode()->lookBurst(order);
     } else {
       throw anna::RuntimeException("Wrong body content format on HTTP Request for 'burst' operation (unexpected action parameter). See help", ANNA_FILE_LOCATION);
     }
@@ -1586,12 +1621,12 @@ void Launcher::eventOperation(const std::string &operation, std::string &respons
 
       bool success = ((param2 != "") ? testManager.configureTTPS(atoi(param2.c_str())) : false);
       if (success) {
-        result_msg = "assigned new test launch rate to ";
-        result_msg += anna::functions::asString(atoi(param2.c_str()));
-        result_msg += " events per second";
+        opt_response_content = "assigned new test launch rate to ";
+        opt_response_content += anna::functions::asString(atoi(param2.c_str()));
+        opt_response_content += " events per second";
       }
       else {
-        result_msg += "unable to configure the test rate provided";
+        opt_response_content += "unable to configure the test rate provided";
       }
     }
     else if(param1 == "ip-limit") {
@@ -1602,16 +1637,16 @@ void Launcher::eventOperation(const std::string &operation, std::string &respons
       if (param2 != "") {
         limit = atoi(param2.c_str());
         testManager.setInProgressLimit(limit);
-        result_msg = "new in-progress limit: ";
-        result_msg += (limit != UINT_MAX) ? anna::functions::asString(limit) : "<no limit>";
+        opt_response_content = "new in-progress limit: ";
+        opt_response_content += (limit != UINT_MAX) ? anna::functions::asString(limit) : "<no limit>";
       }
       else {
-        result_msg = "in-progress limit amount: ";
+        opt_response_content = "in-progress limit amount: ";
         limit = testManager.getInProgressLimit();
-        result_msg += (limit != UINT_MAX) ? anna::functions::asString(limit) : "<no limit>";
-        result_msg += "; currently there are ";
-        result_msg += anna::functions::asString(testManager.getInProgressCount());
-        result_msg += " test cases running";
+        opt_response_content += (limit != UINT_MAX) ? anna::functions::asString(limit) : "<no limit>";
+        opt_response_content += "; currently there are ";
+        opt_response_content += anna::functions::asString(testManager.getInProgressCount());
+        opt_response_content += " test cases running";
       }
     }
     else if(param1 == "repeat") {
@@ -1620,7 +1655,7 @@ void Launcher::eventOperation(const std::string &operation, std::string &respons
 
       if(param2 == "") param2 = "yes";
       testManager.setPoolRepeat((param2 == "yes"));
-      result_msg += (testManager.getPoolRepeat() ? "repeat enabled" : "repeat disabled");
+      opt_response_content += (testManager.getPoolRepeat() ? "repeat enabled" : "repeat disabled");
     }
     else if(param1 == "report") {
       if (numParams > 2)
@@ -1628,7 +1663,7 @@ void Launcher::eventOperation(const std::string &operation, std::string &respons
 
       if(param2 == "") param2 = "yes";
       testManager.setDumpReports((param2 == "yes"));
-      result_msg += (testManager.getDumpReports() ? "report enabled" : "report disabled");
+      opt_response_content += (testManager.getDumpReports() ? "report enabled" : "report disabled");
     }
     else if(param1 == "goto") {
       if (numParams > 2)
@@ -1637,13 +1672,13 @@ void Launcher::eventOperation(const std::string &operation, std::string &respons
       if(param2 == "") throw anna::RuntimeException("Missing id for test goto operation", ANNA_FILE_LOCATION);
       int id = atoi(param2.c_str());
       if (testManager.gotoTestCase(id)) {
-        result_msg = "position updated for id provided (";
+        opt_response_content = "position updated for id provided (";
       }
       else {
-        result_msg = "cannot found test id (";
+        opt_response_content = "cannot found test id (";
       }
-      result_msg += anna::functions::asString(id);
-      result_msg += ")";
+      opt_response_content += anna::functions::asString(id);
+      opt_response_content += ")";
     }
     else if(param1 == "look") {
       if (numParams > 2)
@@ -1653,17 +1688,17 @@ void Launcher::eventOperation(const std::string &operation, std::string &respons
       TestCase *testCase = testManager.findTestCase(id);
 
       if (testCase) {
-        result_msg = "\n\n";
-        result_msg += testCase->asXMLString();
+        response_content = testCase->asXMLString();
+        return;
       }
       else {
         if (id == -1) {
-          result_msg = "no current test case detected (testing started ?)";
+          opt_response_content = "no current test case detected (testing started ?)";
         }
         else {
-          result_msg = "cannot found test id (";
-          result_msg += anna::functions::asString(id);
-          result_msg += ")";
+          opt_response_content = "cannot found test id (";
+          opt_response_content += anna::functions::asString(id);
+          opt_response_content += ")";
         }
       }
     }
@@ -1679,21 +1714,21 @@ void Launcher::eventOperation(const std::string &operation, std::string &respons
 
       if (testCase) {
         bool done = testCase->reset((param2 == "hard") ? true:false);
-        result_msg = "test ";
-        result_msg += param2;
-        result_msg += " reset for id ";
-        result_msg += anna::functions::asString(id);
-        result_msg += done ? ": done": ": not done";
+        opt_response_content = "test ";
+        opt_response_content += param2;
+        opt_response_content += " reset for id ";
+        opt_response_content += anna::functions::asString(id);
+        opt_response_content += done ? ": done": ": not done";
       }
       else {
         if (id == -1) {
           bool anyReset = testManager.resetPool((param2 == "hard") ? true:false);
-          result_msg = "reset have been sent to all programmed tests: "; result_msg += anyReset ? "some/all was actually reset" : "nothing was reset";
+          opt_response_content = "reset have been sent to all programmed tests: "; opt_response_content += anyReset ? "some/all was actually reset" : "nothing was reset";
         }
         else {
-          result_msg = "cannot found test id (";
-          result_msg += anna::functions::asString(id);
-          result_msg += ")";
+          opt_response_content = "cannot found test id (";
+          opt_response_content += anna::functions::asString(id);
+          opt_response_content += ")";
         }
       }
     }
@@ -1702,10 +1737,10 @@ void Launcher::eventOperation(const std::string &operation, std::string &respons
         throw anna::RuntimeException("Wrong body content format on HTTP Request. Use 'help' management command to see more information.", ANNA_FILE_LOCATION);
 
       if (testManager.clearPool()) {
-        result_msg = "all the programmed test cases have been dropped";
+        opt_response_content = "all the programmed test cases have been dropped";
       }
       else {
-        result_msg = "there are not programmed test cases to be removed";
+        opt_response_content = "there are not programmed test cases to be removed";
       }
     }
     else {
@@ -1787,9 +1822,6 @@ void Launcher::eventOperation(const std::string &operation, std::string &respons
       else {
         throw anna::RuntimeException("Wrong body content format on HTTP Request. Use 'help' management command to see more information.", ANNA_FILE_LOCATION);
       }
-
-      result_msg = "new step added to test id ";
-      result_msg += anna::functions::asString(id);
     }
 
   } else if((opType == "sendxml2c") || (opType == "sendhex2c")) {
@@ -1821,8 +1853,8 @@ void Launcher::eventOperation(const std::string &operation, std::string &respons
     }
   } else if(opType == "loadxml") {
     codecMsg.loadXML(param1);
-    std::string xmlString = codecMsg.asXMLString();
-    std::cout << xmlString << std::endl;
+    response_content = codecMsg.asXMLString();
+    return;
   } else if(opType == "diameterServerSessions") {
     int diameterServerSessions = atoi(param1.c_str());
 
@@ -1836,8 +1868,7 @@ void Launcher::eventOperation(const std::string &operation, std::string &respons
       throw anna::RuntimeException("Operation not applicable (no own diameter server has been configured)", ANNA_FILE_LOCATION);
 
     if(param1 == "") { // programmed answers FIFO's to stdout
-      std::cout << localServer->getReactingAnswers()->asString("ANSWERS TO CLIENT") << std::endl;
-      response_content = "Programmed answers dumped on stdout\n";
+      response_content = localServer->getReactingAnswers()->asString("ANSWERS TO CLIENT");
       return;
     } else if (param1 == "rotate") {
       localServer->getReactingAnswers()->rotate(true);
@@ -1866,8 +1897,7 @@ void Launcher::eventOperation(const std::string &operation, std::string &respons
       throw anna::RuntimeException("Operation not applicable (no diameter entity has been configured)", ANNA_FILE_LOCATION);
 
     if(param1 == "") { // programmed answers FIFO's to stdout
-      std::cout << entity->getReactingAnswers()->asString("ANSWERS TO ENTITY") << std::endl;
-      response_content = "Programmed answers dumped on stdout\n";
+      response_content = entity->getReactingAnswers()->asString("ANSWERS TO ENTITY");
       return;
     } else if (param1 == "rotate") {
       entity->getReactingAnswers()->rotate(true);
@@ -1896,8 +1926,11 @@ void Launcher::eventOperation(const std::string &operation, std::string &respons
   }
 
   // HTTP response
-  response_content = "Operation correctly processed: "; response_content += operation; response_content += " => ";
-  response_content += result_msg;
+  response_content = "Operation correctly processed: "; response_content += operation;
+  if (opt_response_content != "") {
+    response_content += " => ";
+    response_content += opt_response_content;
+  }
 }
 
 anna::xml::Node* Launcher::asXML(anna::xml::Node* parent) const