Fixes and improvs. Basic DRA feature.
authorEduardo Ramos Testillano <eduardo.ramos.testillano@ericsson.com>
Tue, 8 Sep 2015 00:19:21 +0000 (02:19 +0200)
committerEduardo Ramos Testillano <eduardo.ramos.testillano@ericsson.com>
Fri, 11 Sep 2015 20:19:15 +0000 (22:19 +0200)
23 files changed:
example/diameter/launcher/Launcher.cpp
example/diameter/launcher/Launcher.hpp
example/diameter/launcher/MyDiameterEngine.hpp
example/diameter/launcher/RealmNode.cpp
example/diameter/launcher/RealmNode.hpp
example/diameter/launcher/resources/scripts/operation_signal.sh
example/diameter/launcher/resources/services_examples/balancer.xml
example/diameter/launcher/resources/services_examples/client.xml
example/diameter/launcher/resources/services_examples/dummy.xml
example/diameter/launcher/resources/services_examples/multi-client.xml
example/diameter/launcher/resources/services_examples/server.xml
example/diameter/launcher/resources/services_examples/services.dtd
example/diameter/launcher/testing/TestCase.cpp
example/diameter/launcher/testing/TestManager.cpp
example/diameter/launcher/testing/TestManager.hpp
example/diameter/launcher/testing/TestStep.cpp
example/diameter/launcher/testing/TestStep.hpp
include/anna/diameter.comm/Engine.hpp
include/anna/diameter/codec/EngineImpl.hpp
include/anna/diameter/codec/Message.hpp
source/diameter.comm/Engine.cpp
source/diameter.comm/LocalServer.cpp
source/diameter.comm/ServerSession.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
index 21c3d25..204459a 100644 (file)
@@ -53,6 +53,7 @@ class Launcher : public anna::comm::Application {
   // Core engines:
   MyCommunicator *a_communicator;
   anna::diameter::codec::Engine *a_codecEngine;
+  anna::diameter::stack::Dictionary *a_baseProtocolDictionary;
   anna::timex::Engine* a_timeEngine;
   MyCounterRecorder *a_counterRecorder;
   anna::Millisecond a_admlMinResolution;
@@ -79,6 +80,7 @@ public:
   void startServices() throw(anna::RuntimeException);
 
   anna::diameter::codec::Engine *getCodecEngine() const throw() { return a_codecEngine; }
+  //anna::diameter::stack::Dictionary *getBaseProtocolDictionary() const throw() { return a_baseProtocolDictionary; }
   RealmNode *getWorkingNode() const throw(anna::RuntimeException); // management operations working node
   bool setWorkingNode(const std::string &name) throw(); // we could update ignoreFlagsOnValidation/integrationAndDebugging over the global codec engine
                                                         //  but finally, that configuration issues will be global to the process.
index 29b3c1b..d7c9b07 100644 (file)
 #include <MyLocalServer.hpp>
 
 
+namespace anna {
+  namespace diameter {
+    namespace stack {
+      class Dictionary;
+    }
+  }
+}
+
 class MyDiameterEngine : public anna::diameter::comm::Engine {
 public:
 
-  MyDiameterEngine(const char *className = "MyDiameterEngine") : Engine(className, NULL /* we will assign the base protocol codec engine later*/) {;}
+  MyDiameterEngine(const char *className, const anna::diameter::stack::Dictionary *baseProtocolDictionary) : Engine(className, baseProtocolDictionary) {;}
 
 // Default implementation is enough
 //   void readDPA(anna::DataBlock &dpa, const anna::DataBlock & dpr) throw() {;} // DPA is not replied
index 630ab8f..d3a2b89 100644 (file)
 #include <RealmNode.hpp>
 #include <MyDiameterEngine.hpp>
 
+namespace anna {
+  namespace diameter {
+    namespace stack {
+      class Dictionary;
+    }
+  }
+}
 
-
-RealmNode::RealmNode(const std::string &originRealm, unsigned int applicationId, anna::diameter::codec::Engine *codecEngine) :
-  a_originRealm(originRealm), a_applicationId(applicationId), a_codecEngine(codecEngine) {
+RealmNode::RealmNode(const std::string &originRealm, anna::diameter::codec::Engine *codecEngine, const anna::diameter::stack::Dictionary *baseProtocolDictionary) :
+  a_originRealm(originRealm), a_codecEngine(codecEngine) {
 
   std::string commEngineName = a_originRealm + "_DiameterCommEngine";
-  a_commEngine = new MyDiameterEngine(commEngineName.c_str());
+  a_commEngine = new MyDiameterEngine(commEngineName.c_str(), baseProtocolDictionary);
   a_commEngine->setAutoBind(false);  // allow to create client-sessions without binding them, in order to set timeouts.
-  a_commEngine->setBaseProtocolCodecEngine(getCodecEngine());
 
   a_logFile = "";
   a_burstLogFile = "";
@@ -56,7 +61,7 @@ RealmNode::RealmNode(const std::string &originRealm, unsigned int applicationId,
 }
 
 
-void RealmNode::createEntity(const std::string &entityRepresentation, const anna::Millisecond &bindTimeout, const anna::Millisecond &applicationTimeout) throw() {
+void RealmNode::createEntity(const std::string &entityRepresentation, const anna::Millisecond &bindTimeout, const anna::Millisecond &applicationTimeout) throw(anna::RuntimeException) {
 
   anna::socket_v servers = anna::functions::getSocketVectorFromString(entityRepresentation);
   std::string entityDescription = "Launcher diameter entity for "; entityDescription += a_originRealm;
@@ -402,7 +407,6 @@ throw() {
   anna::xml::Node* result = parent->createChild("RealmNode");
 
   result->createAttribute("OriginRealm", a_originRealm);
-  result->createAttribute("ApplicationId", a_applicationId);
   result->createAttribute("LogFile", a_logFile);
   result->createAttribute("SplitLog", a_splitLog ? "yes" : "no");
   result->createAttribute("DetailedLog", a_detailedLog ? "yes" : "no");
index a8a49fd..774e858 100644 (file)
@@ -25,6 +25,9 @@ namespace anna {
     namespace codec {
       class Engine;
     }
+    namespace stack {
+      class Dictionary;
+    }
     namespace comm {
       class Message;
     }
@@ -50,7 +53,6 @@ class RealmNode {
 
   // main
   std::string a_originRealm;
-  unsigned int a_applicationId;
 
   // Timming
   anna::Millisecond a_allowedInactivityTime;
@@ -74,13 +76,13 @@ class RealmNode {
   int a_burstPopCounter;
 
 public:
-  RealmNode(const std::string &originRealm, unsigned int applicationId, anna::diameter::codec::Engine *codecEngine);
+  RealmNode(const std::string &originRealm, anna::diameter::codec::Engine *codecEngine, const anna::diameter::stack::Dictionary *baseProtocolDictionary);
   ~RealmNode() {;}
 
   // Core resources:
   MyDiameterEngine* getMyDiameterEngine() const throw() { return a_commEngine; }
   anna::diameter::codec::Engine *getCodecEngine() const throw() { return a_codecEngine; }
-  void createEntity(const std::string &entityRepresentation, const anna::Millisecond &bindTimeout, const anna::Millisecond &applicationTimeout) throw();
+  void createEntity(const std::string &entityRepresentation, const anna::Millisecond &bindTimeout, const anna::Millisecond &applicationTimeout) throw(anna::RuntimeException);
   MyDiameterEntity *getEntity() const throw() { return a_entity; }
   void startDiameterServer(const std::string &serverRepresentation, int sessions, const anna::Millisecond &inactivityTimeout) throw(anna::RuntimeException);
   MyLocalServer* getDiameterServer() throw() { return a_diameterServer; }
index 8c9ce85..d312981 100755 (executable)
@@ -18,10 +18,10 @@ PID=`cat .pid`
 
 # Send operation:
 [ "$1" = "" ] && _exit "Usage: $0 <operation string>; i.e.: $0 help"
-echo $1 > sigusr2.tasks.input
+echo $1 > sigusr2.in
 kill -s SIGUSR2 $PID
 
-#echo "You could see results on '`pwd`/sigusr2.tasks.output' file."
-cat `pwd`/sigusr2.tasks.output 2>/dev/null
+#echo "You could see results on '`pwd`/sigusr2.out' file."
+cat `pwd`/sigusr2.out 2>/dev/null
 echo
 
index d7bfbc6..a7d3f30 100644 (file)
@@ -3,7 +3,7 @@
   <stack id="0" dictionary="dictionary.xml"/>
 
   <!-- Nodes -->
-  <node originRealm="ADML-balancer" applicationId="0" entity="192.168.12.11:3868,192.168.12.21:3868" diameterServer="localhost:3868" balance="yes"/>
-  <!-- <node originRealm="ADML-proxy" applicationId="0" entity="localhost:3868" diameterServer="localhost:3870"/> -->
+  <node originRealm="ADML-balancer" entity="192.168.12.11:3868,192.168.12.21:3868" diameterServer="localhost:3868" balance="yes"/>
+  <!-- <node originRealm="ADML-proxy" entity="localhost:3868" diameterServer="localhost:3870"/> -->
 </services>
 
index dfbb943..4c5e019 100644 (file)
@@ -3,6 +3,6 @@
   <stack id="0" dictionary="dictionary.xml"/>
 
   <!-- Nodes -->
-  <node originRealm="ADML-client" applicationId="0" entity="localhost:3868"/>
+  <node originRealm="ADML-client" entity="localhost:3868"/>
 </services>
 
index f1602be..c09c529 100644 (file)
@@ -3,6 +3,6 @@
   <stack id="0" dictionary="dictionary.xml"/>
 
   <!-- Nodes -->
-  <node originRealm="ADML-dummy" applicationId="0"/>
+  <node originRealm="ADML-dummy"/>
 </services>
 
index 795143f..a47db65 100644 (file)
@@ -3,11 +3,12 @@
   <stack id="16777236" dictionary="dictionaryRx.xml"/>
   <stack id="16777238" dictionary="dictionaryGx.xml"/>
   <stack id="16777302" dictionary="dictionarySy.xml"/>
+  <stack id="0" dictionary="dictionaryBase.xml"/>
 
   <!-- Nodes -->
-  <node originRealm="afNode" applicationId="16777236" entity="192.168.12.11:3868,192.168.12.21:3868"/>
-  <node originRealm="ggsnNode" applicationId="16777238" entity="192.168.12.11:3868,192.168.12.21:3868"/>
-  <node originRealm="ggsn2Node" applicationId="16777238" entity="192.168.12.11:3868,192.168.12.21:3868"/>
-  <node originRealm="ocsNode" applicationId="16777302" entity="192.168.12.11:3868,192.168.12.21:3868"/>
+  <node originRealm="afNode" entity="192.168.12.11:3868,192.168.12.21:3868"/>
+  <node originRealm="ggsnNode" entity="192.168.12.11:3868,192.168.12.21:3868"/>
+  <node originRealm="ggsn2Node" entity="192.168.12.11:3868,192.168.12.21:3868"/>
+  <node originRealm="ocsNode" entity="192.168.12.11:3868,192.168.12.21:3868"/>
 </services>
 
index c257afc..e615c4a 100644 (file)
@@ -3,6 +3,6 @@
   <stack id="0" dictionary="dictionary.xml"/>
 
   <!-- Nodes -->
-  <node originRealm="ADML-server" applicationId="0" diameterServer="localhost:3868"/>
+  <node originRealm="ADML-server" diameterServer="localhost:3868"/>
 </services>
 
index 05edb86..6dd31ed 100755 (executable)
@@ -8,19 +8,18 @@
 <!--
    Stack record
 
-   id:         Normally the id corresponds to the Application-Id for which the dictionary provided is designed.
+   id:         Normally the id corresponds to the Application-Id for which the dictionary provided is designed
                (in multistack applications, it shall be mandatory respect such association to know the stack used
                for processed messages).
    dictionary: Path to the dictionary file
 -->
 
 <!ELEMENT node EMPTY>
-<!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>
+<!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>
 <!--
    Node record
 
    originRealm:                             Node identifier (Origin-Realm name).
-   applicationId:                           The Application-Id provided must exists as a registered 'stack id'.
    originHost:                              Diameter application host name (system name). If missing, process sets o.s. hostname
                                             Note that if you have two or more realms, the names must be different.
    cer:                                     User defined CER path file to be encoded to establish diameter connections.
index ccb9b42..1c5beeb 100644 (file)
@@ -343,6 +343,7 @@ TestStepWait *TestCase::searchNextWaitConditionFulfilled(const anna::DataBlock &
   TestStepWait *result;
   for (std::vector<TestStep*>::const_iterator it = a_stepsIt /* current */; it != a_steps.end(); it++) {
     if ((*it)->getType() != TestStep::Type::Wait) continue;
+    if ((*it)->isCompleted()) continue;
     result = (TestStepWait*)(*it);
     if ((result->getCondition().receivedFromEntity() == waitFromEntity) && (result->fulfilled(message)))
       return result;
index ca45e9c..97a0d4f 100644 (file)
@@ -232,6 +232,7 @@ bool TestManager::tick() throw() {
     return false;
   }
 
+  // Synchronous sendings per tick:
   int count = a_synchronousAmount;
   while (count > 0) {
     if (!nextTestCase()) return false; // stop the clock
@@ -271,21 +272,29 @@ bool TestManager::nextTestCase() throw() {
       }
     }
 
-    // Hard reset, because normally a cycle takes more time that a single test case lifetime. We can consider that never
-    //  going to break a in-progress test case due to cycle repeat
+    // Soft reset to initialize already finished (in previous cycle) test cases:
     a_currentTestIt->second->reset(false);
 
     // Process test case:
     LOGDEBUG(anna::Logger::debug(anna::functions::asString("Processing test case id = %llu, currently '%s' state", a_currentTestIt->first, TestCase::asText(a_currentTestIt->second->getState())), ANNA_FILE_LOCATION));
     if (a_currentTestIt->second->getState() != TestCase::State::InProgress) {
       a_currentTestIt->second->process();
-      return true;
+      return true; // is not probably to reach still In-Progress test cases from previous cycles due to the whole
+                   //  time for complete the test cases pool regarding the single test case lifetime. You shouldn't
+                   //  forget to programm a test case timeout with a reasonable value
     }
   }
 }
 
-TestCase *TestManager::getTestCaseFromSessionId(const anna::DataBlock &message, std::string &sessionId) throw(anna::RuntimeException) {
-  sessionId = anna::diameter::helpers::base::functions::getSessionId(message);
+TestCase *TestManager::getTestCaseFromSessionId(const anna::DataBlock &message, std::string &sessionId) throw() {
+  try {
+    sessionId = anna::diameter::helpers::base::functions::getSessionId(message);
+  }
+  catch (anna::RuntimeException &ex) {
+    //ex.trace();
+    LOGWARNING(anna::Logger::warning("Cannot get the Session-Id from received DataBlock in order to identify the Test Case", ANNA_FILE_LOCATION));
+    return NULL;
+  }
   std::map<std::string /* session id's */, TestCase*>::const_iterator sessionIdIt = a_sessionIdTestCaseMap.find(sessionId);
   if (sessionIdIt != a_sessionIdTestCaseMap.end())
     return sessionIdIt->second;
index 313e15f..614cb1e 100644 (file)
@@ -117,7 +117,7 @@ class TestManager : public anna::timex::TimeEventObserver, public anna::Singleto
     TestCase *getTestCase(unsigned int id) throw(); // creates/reuses a test case
 
     // Main logic
-    TestCase *getTestCaseFromSessionId(const anna::DataBlock &message, std::string &sessionId) throw(anna::RuntimeException);
+    TestCase *getTestCaseFromSessionId(const anna::DataBlock &message, std::string &sessionId) throw();
     void receiveMessage(const anna::DataBlock &message, const anna::diameter::comm::ClientSession *clientSession) throw(anna::RuntimeException);
     void receiveMessage(const anna::DataBlock &message, const anna::diameter::comm::ServerSession *serverSession) throw(anna::RuntimeException);
 
index 076a227..c4ec2e5 100644 (file)
@@ -75,20 +75,20 @@ std::string TestStep::asXMLString() const throw() {
 }
 
 bool TestStep::execute() throw() {
-  LOGDEBUG(anna::Logger::debug(anna::functions::asString("EXECUTING %s for Test Case %llu (%p) (%p)", asText(a_type), a_testCase->getId(), (TestCaseStep*)this, this), ANNA_FILE_LOCATION));
+  LOGDEBUG(anna::Logger::debug(anna::functions::asString("EXECUTING %s (step number %d) for Test Case %llu (%p) (%p)", asText(a_type), a_number, a_testCase->getId(), (TestCaseStep*)this, this), ANNA_FILE_LOCATION));
   setBeginTimestamp(anna::functions::millisecond());
   return do_execute();
 }
 
 void TestStep::complete() throw() {
-  LOGDEBUG(anna::Logger::debug(anna::functions::asString("COMPLETE %s for Test Case %llu (%p) (%p)", asText(a_type), a_testCase->getId(), (TestCaseStep*)this, this), ANNA_FILE_LOCATION));
+  LOGDEBUG(anna::Logger::debug(anna::functions::asString("COMPLETE %s (step number %d) for Test Case %llu (%p) (%p)", asText(a_type), a_number, a_testCase->getId(), (TestCaseStep*)this, this), ANNA_FILE_LOCATION));
   a_completed = true;
   setEndTimestamp(anna::functions::millisecond());
   do_complete();
 }
 
 void TestStep::reset() throw() {
-  LOGDEBUG(anna::Logger::debug(anna::functions::asString("RESET %s for Test Case %llu (%p) (%p)", asText(a_type), a_testCase->getId(), (TestCaseStep*)this, this), ANNA_FILE_LOCATION));
+  LOGDEBUG(anna::Logger::debug(anna::functions::asString("RESET %s (step number %d) for Test Case %llu (%p) (%p)", asText(a_type), a_number, a_testCase->getId(), (TestCaseStep*)this, this), ANNA_FILE_LOCATION));
   // type and testCase kept
   a_completed = false;
   a_beginTimestamp = 0;
@@ -193,62 +193,56 @@ bool TestStepSendxml::do_execute() throw() {
   bool success = false;
   std::string failReason, s_warn;
 
-  // Update sequence for answers:
-  if (a_waitForRequestStepNumber != -1) { // is an answer: try to copy sequence information; alert about Session-Id discrepance
-    // Request which was received:
-    const TestStepWait *tsw = (const TestStepWait*)(a_testCase->getStep(a_waitForRequestStepNumber));
-    const anna::DataBlock &request = tsw->getMsgDataBlock();
-    anna::diameter::HopByHop hbh = anna::diameter::codec::functions::getHopByHop(request);
-    anna::diameter::EndToEnd ete = anna::diameter::codec::functions::getEndToEnd(request);
-    // Update sequence:
-    anna::diameter::codec::functions::setHopByHop(a_message, hbh);
-    anna::diameter::codec::functions::setEndToEnd(a_message, ete);
-
-    // Check Session-Id for warning ...
-    std::string sessionIdAnswer = anna::diameter::helpers::base::functions::getSessionId(a_message);
-    std::string sessionIdRequest = anna::diameter::helpers::base::functions::getSessionId(request);
-    if (sessionIdRequest != sessionIdAnswer) {
-      s_warn = anna::functions::asString("Sending an answer which Session-Id (%s) is different than supposed corresponding request (%s)", sessionIdAnswer.c_str(), sessionIdRequest.c_str());
-      LOGWARNING(anna::Logger::warning(s_warn, ANNA_FILE_LOCATION));
-      a_testCase->addDebugSummaryHint(s_warn);
+  try {
+    // Update sequence for answers:
+    if (a_waitForRequestStepNumber != -1) { // is an answer: try to copy sequence information; alert about Session-Id discrepance
+      // Request which was received:
+      const TestStepWait *tsw = (const TestStepWait*)(a_testCase->getStep(a_waitForRequestStepNumber));
+      const anna::DataBlock &request = tsw->getMsgDataBlock();
+      anna::diameter::HopByHop hbh = anna::diameter::codec::functions::getHopByHop(request);
+      anna::diameter::EndToEnd ete = anna::diameter::codec::functions::getEndToEnd(request);
+      // Update sequence:
+      anna::diameter::codec::functions::setHopByHop(a_message, hbh);
+      anna::diameter::codec::functions::setEndToEnd(a_message, ete);
+
+      // Check Session-Id for warning ...
+      std::string sessionIdAnswer = anna::diameter::helpers::base::functions::getSessionId(a_message);
+      std::string sessionIdRequest = anna::diameter::helpers::base::functions::getSessionId(request);
+      if (sessionIdRequest != sessionIdAnswer) {
+        s_warn = anna::functions::asString("Sending an answer which Session-Id (%s) is different than supposed corresponding request (%s)", sessionIdAnswer.c_str(), sessionIdRequest.c_str());
+        LOGWARNING(anna::Logger::warning(s_warn, ANNA_FILE_LOCATION));
+        a_testCase->addDebugSummaryHint(s_warn);
+      }
     }
-  }
 
-  if (getType() == Type::Sendxml2e) {
-    MyDiameterEntity *entity = a_realmNode->getEntity();
-    if (entity) {
-      try {
+    if (getType() == Type::Sendxml2e) {
+      MyDiameterEntity *entity = a_realmNode->getEntity();
+      if (entity) {
         //msg->clearBody();
         msg->setBody(a_message);
         /* response = NULL =*/entity->send(msg);
         success = true;
-      } catch(anna::RuntimeException &ex) {
-        ex.trace();
-        failReason = ex.asString();
+      }
+      else {
+        failReason = "There is no diameter entity currently configured. Unable to send the message";
+        LOGWARNING(anna::Logger::warning(failReason, ANNA_FILE_LOCATION));
       }
     }
-    else {
-      failReason = "There is no diameter entity currently configured. Unable to send the message";
-      LOGWARNING(anna::Logger::warning(failReason, ANNA_FILE_LOCATION));
-    }
-  }
-  else if (getType() == Type::Sendxml2c) {
-    MyLocalServer *localServer = a_realmNode->getDiameterServer();
-    if (localServer) {
-      try {
+    else if (getType() == Type::Sendxml2c) {
+      MyLocalServer *localServer = a_realmNode->getDiameterServer();
+      if (localServer) {
         //msg->clearBody();
         msg->setBody(a_message);
         /* response = NULL =*/localServer->send(msg);
         success = true;
-      } catch(anna::RuntimeException &ex) {
-        ex.trace();
-        failReason = ex.asString();
+      }
+      else {
+        failReason = "There is no diameter local server currently configured. Unable to send the message";
+        LOGWARNING(anna::Logger::warning(failReason, ANNA_FILE_LOCATION));
       }
     }
-    else {
-      failReason = "There is no diameter local server currently configured. Unable to send the message";
-      LOGWARNING(anna::Logger::warning(failReason, ANNA_FILE_LOCATION));
-    }
+  } catch(anna::RuntimeException &ex) {
+    failReason = ex.asString();
   }
 
   // release msg
@@ -265,10 +259,6 @@ bool TestStepSendxml::do_execute() throw() {
   return success; // go next if sent was OK
 }
 
-void TestStepSendxml::do_complete() throw() {
-  next();
-}
-
 void TestStepSendxml::do_reset() throw() {
   a_expired = false;
   //a_message.clear();
@@ -302,7 +292,7 @@ bool TestStepDelay::do_execute() throw() {
 
 void TestStepDelay::do_complete() throw() {
   a_timer = NULL;
-  next();
+  next(); // next() invoked here because execute() is always false for delay and never dvance the iterator
 }
 
 void TestStepDelay::do_reset() throw() {
@@ -371,7 +361,7 @@ bool TestStepWait::do_execute() throw() {
 }
 
 void TestStepWait::do_complete() throw() {
-  next();
+  next(); // next() invoked here because execute() never do this.
 }
 
 bool TestStepWait::fulfilled(const anna::DataBlock &db/*, bool matchSessionId*/) throw() {
index 90b64aa..3473c54 100644 (file)
@@ -132,7 +132,7 @@ class TestStepSendxml : public TestStep {
 
     // virtuals
     bool do_execute() throw();
-    void do_complete() throw();
+    void do_complete() throw() {;}
     void do_reset() throw();
     anna::xml::Node* asXML(anna::xml::Node* parent) const throw();
 };
index 8ec2757..742c0fc 100644 (file)
 
 // STL
 #include <map>
+#include <vector>
 #include <string>
 #include <algorithm>
 
 #include <anna/app/Component.hpp>
 #include <anna/core/util/Recycler.hpp>
 
+#include <anna/diameter/codec/Engine.hpp>
 #include <anna/diameter.comm/Server.hpp>
 #include <anna/diameter.comm/ServerSession.hpp>
 #include <anna/config/defines.hpp>
@@ -47,9 +49,13 @@ namespace codec {
 class Engine;
 }
 
-namespace comm {
+namespace stack {
+class Dictionary;
+}
 
+namespace comm {
 
+class Response;
 class Entity;
 class Server;
 class LocalServer;
@@ -94,27 +100,6 @@ class LocalServer;
 class Engine : public anna::app::Component {
 public:
 
-  /**
-   * Sets the base protocol codec engine used internally
-   *
-   * @param baseProtocolCodecEngine This will be used internally during invokation of @readCEA, @readDPA and @readDWA on servers,
-   * and also used during base protocol messages tracing (if debug traces are enabled). You could provide NULL but you must be
-   * sure that neither of the former situations are going to happen or an exception will be thrown. It is recommended to register
-   * a codec engine pointed to a base protocol stack (you can use the files 'avps_ietf.xml' and 'commands_baseProtocol.xml'
-   * located on ANNA suite project under 'source/diameter/stack/setups', or perhaps you can create your own dictionary from
-   * file or directly with the dictionay creation API. Even you can use a greater dictionary (application dictionary), the
-   * only condition is that must contain the resources to build base protocol messages. You could provide this in engine constructor,
-   * but don't forget it.
-  */
-  void setBaseProtocolCodecEngine(codec::Engine *baseProtocolCodecEngine) throw() { a_baseProtocolCodecEngine = baseProtocolCodecEngine; }
-
-  /**
-   * Gets the base protocol codec engine used internally
-   *
-   * @see setBaseProtocolCodecEngine
-   */
-  codec::Engine * getBaseProtocolCodecEngine() const throw() { return a_baseProtocolCodecEngine; }
-
   /**
      Diameter application node realm name (used to be the site domain name).
 
@@ -632,6 +617,19 @@ public:
   */
   virtual void readDWA(anna::DataBlock &dwa, const anna::DataBlock & dwr) throw();
 
+  /**
+   * DRA basics: CER information is gathered on every server session managed by the diameter comm engine. You could send the message to a
+   * specific realm, and optionally you could restrict a host inside it. This is common for requests (answers are normally sent through
+   * the same source server session where the request was received). Exception will be thrown if not found an available server session
+   * for the Destination-Realm and/or Destination-Host provided
+   *
+   * @param destinationRealm If empty, NULL is returned, because is nonsense to specify a host out of realm context
+   * @param destinationHost If empty, no restriction is applied within the target realm node. Random delivery is applied for the available server sessions
+   *
+   * @return transactional response reference, or NULL if answer is sent
+   */
+  const Response* sendRealmHost(const Message* message, const std::string &destinationRealm, const std::string &destinationHost = "") throw(anna::RuntimeException);
+
   /**
      Reset engine statistics.
      At the moment, only diameter servers processing time is observed.
@@ -653,17 +651,15 @@ protected:
      Constructor.
 
      @param className Component class name
-     @param baseProtocolCodecEngine This will be used internally during invokation of @readCEA, @readDPA and @readDWA on servers,
-     and also used during base protocol messages tracing (if debug traces are enabled). You could provide NULL but you must be
-     sure that neither of the former situations are going to happen or an exception will be thrown. It is recommended to register
-     a codec engine pointed to a base protocol stack (you can use the files 'avps_ietf.xml' and 'commands_baseProtocol.xml'
-     located on ANNA suite project under 'source/diameter/stack/setups', or perhaps you can create your own dictionary from
-     file or directly with the dictionay creation API. Even you can use a greater dictionary (application dictionary), the
-     only condition is that must contain the resources to build base protocol messages. You could use @setBaseProtocolCodecEngine
-     to set this reference later; don't forget it.
+     @param baseProtocolDictionary This will be used internally when calling \@readCEA, \@readDPA and \@readDWA on
+     servers, and also used during base protocol messages tracing (if debug traces are enabled). You could provide
+     NULL, but you must be sure that neither of the former situations are going to happen or an exception will be
+     thrown (using setClientCERandDWR with DataBlock arguments, expects externally encoded messages and could help).
+     It is recommended to set a base protocol dictionary loading 'source/diameter/stack/setups' dictionaries (for
+     example 'avps_ietf.xml' plus 'commands_baseProtocol.xml'), or using the dictionary creation API. The dictionary
+     could also be an application stack, the only condition is containing the resources to build base protocol messages.
   */
-  Engine(const char *className, codec::Engine *baseProtocolCodecEngine);
-
+  Engine(const char *className, const stack::Dictionary *baseProtocolDictionary);
 
   // INTERNAL CREATORS AND CLOSE METHODS
   Server *createServer(Entity*, const socket_t&) throw(anna::RuntimeException);
@@ -712,11 +708,10 @@ protected:
   virtual void releaseLocalServer(LocalServer*) throw() {;}
 
 
-
 private:
 
   // Internal use: tracing and readCEA/DPA/DWA
-  codec::Engine *a_baseProtocolCodecEngine;
+  codec::Engine a_baseProtocolCodecEngine;
 
   std::string a_realm;
   std::string a_host;
@@ -756,8 +751,17 @@ private:
 
   // Integrity:
   void checkEntityCollision(const socket_v &) throw(anna::RuntimeException);
+  void assertBaseProtocolHealth() throw(anna::RuntimeException); // checks the dictionary
 
 
+  //  Gets the base protocol codec engine used internally.
+  //  This engine is initializaed on constructor with the base protocol dictionary.
+  //  The reason to not reuse any other codec engine from the application is to have this one isolated with no interference
+  //  regarding configuration changes (validation depth/mode, fix mode, etc.).
+  //
+  //  @return Pointer to the internal base protocol codec engine
+  codec::Engine *getBaseProtocolCodecEngine() const throw() { return const_cast<codec::Engine *>(&a_baseProtocolCodecEngine); }
+
   //////////////////////////
   // CLIENT FUNCTIONALITY //
   //////////////////////////
@@ -830,10 +834,22 @@ private:
   const_localServer_iterator localServer_end() const throw() { return a_localServers.end(); }
   static const LocalServer* localServer(const_localServer_iterator ii) throw() { return ii->second; }
 
-  // Server sessions are managed within LocalServer (not at engine) due to dynamic cration nature
-
+  // Server sessions are managed within LocalServer (not at engine) due to dynamic creation nature
+  // Here we maintain the Destination-Realm / Destination-Host maps for DRA basics:
+  typedef std::vector<ServerSession*> server_sessions_vector_t;
+  typedef server_sessions_vector_t::const_iterator server_sessions_it_t;
+  typedef server_sessions_vector_t::iterator server_sessions_nc_it_t;
+  typedef std::map <std::string /* Destination-Host */, server_sessions_vector_t> dh_server_sessions_map_t;
+  typedef dh_server_sessions_map_t::const_iterator dh_server_sessions_it_t;
+  typedef dh_server_sessions_map_t::iterator dh_server_sessions_nc_it_t;
+  typedef std::map <std::string /* Destination-Realm */, dh_server_sessions_map_t> dr_dh_server_sessions_map_t;
+  typedef dr_dh_server_sessions_map_t::const_iterator dr_dh_server_sessions_it_t;
+  typedef dr_dh_server_sessions_map_t::iterator dr_dh_server_sessions_nc_it_t;
+  dr_dh_server_sessions_map_t a_dr_dh_server_sessions;
+  void manageDrDhServerSession(ServerSession *ss, bool register_or_desregister) throw();
 
   friend class Session;
+  friend class ClientSession;
   friend class ServerSession;
   friend class ServerSocket;
   friend class Server;
index 1884083..b973e8d 100644 (file)
@@ -238,8 +238,9 @@ public:
   * this to have the commonly recommended way to choose the stack: using the Application-Id value.
   *
   * @warning do not activate in case of multithreaded applications.
+  * @warning must register the base protocol stack (with id = 0 = application-id) to manage base protocol messages.
   * @param enable Activates/deactivates the stack selection from the Application-Id value within the message header.
-  *               False by default.
+  *               False by default on engine construction.
   */
   void selectStackWithApplicationId (bool enable) throw() { a_selectStackWithApplicationId = enable; }
 
index a3916f7..48d2b84 100644 (file)
@@ -263,7 +263,7 @@ public:
      Sets the message application id.
 
      The codec engine could be configured to force a stack selection based in this field value: see #selectStackWithApplicationId.
-     In multistack applications (which also shall be monothreaded), you only have to take care about how to apply this method: the thing
+     In multistack applications (in case of being monothread), you only have to take care about how to apply this method: the thing
      is that you must not interleave message builds which belongs to different stacks. For example, you could think about setting the
      message header for message A using stack A. Then, start to add the message header fields for a second message B using another stack B.
      Following you would add the message A avps, but then, the stack is not going to be automatically changed (this is only done through this
index 3a7f843..9bfaf60 100644 (file)
@@ -5,9 +5,11 @@
 // See project site at http://redmine.teslayout.com/projects/anna-suite                           //
 // See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE //
 
+// Standard
+#include <stdlib.h> // rand()
 
-#include <anna/diameter.comm/Engine.hpp>
 
+#include <anna/diameter.comm/Engine.hpp>
 #include <anna/core/tracing/Logger.hpp>
 #include <anna/core/tracing/TraceMethod.hpp>
 #include <anna/xml/Node.hpp>
 #include <anna/diameter/helpers/helpers.hpp>
 #include <anna/diameter/codec/Message.hpp>
 #include <anna/diameter/codec/Avp.hpp>
+#include <anna/diameter.comm/Response.hpp>
 
 // STD
 #include <map>
 
 using namespace std;
-using namespace anna::diameter::comm;
+using namespace anna::diameter;
+
 
+namespace anna {
+  namespace diameter {
+    namespace stack {
+      class Dictionary;
+    }
+  }
+}
 
-Engine::Engine(const char *className, codec::Engine *baseProtocolCodecEngine) :
+comm::Engine::Engine(const char *className, const stack::Dictionary *baseProtocolDictionary) :
   anna::app::Component(className),
-  a_baseProtocolCodecEngine(baseProtocolCodecEngine),
   a_autoBind(true),
   a_availableForEntities(false),
   a_availableForLocalServers(false),
@@ -49,19 +59,34 @@ Engine::Engine(const char *className, codec::Engine *baseProtocolCodecEngine) :
 //      a_dwa(true),
   a_watchdogPeriod(ClientSession::DefaultWatchdogPeriod),
   a_maxConnectionDelay(anna::comm::ClientSocket::DefaultMaxConnectionDelay /* 200 ms*/),
-  a_numberOfClientSessionsPerServer(1) {
+  a_numberOfClientSessionsPerServer(1),
+  a_baseProtocolCodecEngine((std::string("baseProtocolCodecEngine_for_") + std::string(className)).c_str())
+{
   anna::diameter::sccs::activate();
   a_realm = anna::functions::getDomainname();
   a_host = anna::functions::getHostname();
+
+  // Internal base protocol codec engine:
+  a_baseProtocolCodecEngine.setDictionary(baseProtocolDictionary);
+  a_baseProtocolCodecEngine.setValidationMode(anna::diameter::codec::Engine::ValidationMode::Always); // default was: after decoding
 }
 
-Server* Engine::allocateServer() throw() { return a_serversRecycler.create(); }
-void Engine::releaseServer(Server *server) throw() { a_serversRecycler.release(server); }
-ClientSession* Engine::allocateClientSession() throw() { return a_clientSessionsRecycler.create(); }
-void Engine::releaseClientSession(ClientSession *clientSession) throw() { a_clientSessionsRecycler.release(clientSession); }
+
+void comm::Engine::assertBaseProtocolHealth() throw(anna::RuntimeException) {
+  if (!getBaseProtocolCodecEngine()->getDictionary())
+    throw anna::RuntimeException("Invalid diameter::comm::Engine object: base protocol dictionary provided on constructor was NULL", ANNA_FILE_LOCATION);
+  // it would be interesting to check and identify certain base protocol elements in the dictionary ...
+  //  but these things will be checked in runtime and will fail if they should.
+}
+
+
+comm::Server* comm::Engine::allocateServer() throw() { return a_serversRecycler.create(); }
+void comm::Engine::releaseServer(Server *server) throw() { a_serversRecycler.release(server); }
+comm::ClientSession* comm::Engine::allocateClientSession() throw() { return a_clientSessionsRecycler.create(); }
+void comm::Engine::releaseClientSession(ClientSession *clientSession) throw() { a_clientSessionsRecycler.release(clientSession); }
 
 
-void Engine::setClientCERandDWR(const anna::DataBlock & cer, const anna::DataBlock & dwr) throw(anna::RuntimeException) {
+void comm::Engine::setClientCERandDWR(const anna::DataBlock & cer, const anna::DataBlock & dwr) throw(anna::RuntimeException) {
   if(codec::functions::getCommandId(cer) != helpers::base::COMMANDID__Capabilities_Exchange_Request) {
     throw anna::RuntimeException("The message provided as 'CER' is not a Capabilities-Exchange-Request", ANNA_FILE_LOCATION);
   }
@@ -74,11 +99,10 @@ void Engine::setClientCERandDWR(const anna::DataBlock & cer, const anna::DataBlo
   a_dwr = dwr;
 }
 
-void Engine::setClientCERandDWR(const std::string & cer, const std::string & dwr) throw(anna::RuntimeException) {
+void comm::Engine::setClientCERandDWR(const std::string & cer, const std::string & dwr) throw(anna::RuntimeException) {
 
-  // Check for base protocol codec engine:
-  if (!getBaseProtocolCodecEngine())
-    throw anna::RuntimeException("Use diameter::comm::Engine::setBaseProtocolCodecEngine() to allow base protocol messages encoding, or use setClientCERandDWR(const anna::DataBlock & cer, const anna::DataBlock & dwr) which expect externally encoded messages", ANNA_FILE_LOCATION);
+  // Check for base protocol codec engine health:
+  assertBaseProtocolHealth();
 
   // Build CER
   //   <CER> ::= < Diameter Header: 257, REQ >
@@ -155,7 +179,7 @@ void Engine::setClientCERandDWR(const std::string & cer, const std::string & dwr
   setClientCERandDWR(diameterCER.code(), diameterDWR.code());
 }
 
-void Engine::setWatchdogPeriod(const anna::Millisecond & wp) throw(anna::RuntimeException) {
+void comm::Engine::setWatchdogPeriod(const anna::Millisecond & wp) throw(anna::RuntimeException) {
   if(wp < ClientSession::DefaultWatchdogPeriod) {
     throw anna::RuntimeException(anna::functions::asString("Please set watchdog period over %s", ClientSession::DefaultWatchdogPeriod.asString().c_str()), ANNA_FILE_LOCATION);
   }
@@ -163,7 +187,7 @@ void Engine::setWatchdogPeriod(const anna::Millisecond & wp) throw(anna::Runtime
   a_watchdogPeriod = wp;
 }
 
-void Engine::checkEntityCollision(const socket_v &v) throw(anna::RuntimeException) {
+void comm::Engine::checkEntityCollision(const socket_v &v) throw(anna::RuntimeException) {
   socket_v::const_iterator it;
   socket_v::const_iterator it_min(v.begin());
   socket_v::const_iterator it_max(v.end());
@@ -184,7 +208,7 @@ void Engine::checkEntityCollision(const socket_v &v) throw(anna::RuntimeExceptio
     throw anna::RuntimeException("diameter::comm::Engine::checkEntityCollision: Provided addresses list (sockets) must have all items different", ANNA_FILE_LOCATION);
 }
 
-Entity* Engine::createEntity(const socket_v & socketList, const std::string &description)
+comm::Entity* comm::Engine::createEntity(const socket_v & socketList, const std::string &description)
 throw(anna::RuntimeException) {
   Entity* result(NULL);
   anna::Guard guard(this, "anna::diameter::comm::Engine::createEntity");
@@ -233,7 +257,7 @@ throw(anna::RuntimeException) {
 }
 
 
-LocalServer *Engine::createLocalServer(const std::string & addr, int port, int maxConnections, const anna::Millisecond & allowedInactivityTime, int category, const std::string & description)
+comm::LocalServer *comm::Engine::createLocalServer(const std::string & addr, int port, int maxConnections, const anna::Millisecond & allowedInactivityTime, int category, const std::string & description)
 throw(anna::RuntimeException) {
   LocalServer* result(NULL);
   anna::Guard guard(this, "anna::diameter::comm::Engine::createLocalServer");
@@ -268,7 +292,7 @@ throw(anna::RuntimeException) {
 }
 
 
-Entity* Engine::createEntity(const std::string & addr1, int port1, const std::string & addr2, int port2, const std::string &description)
+comm::Entity* comm::Engine::createEntity(const std::string & addr1, int port1, const std::string & addr2, int port2, const std::string &description)
 throw(anna::RuntimeException) {
   socket_v dualList;
   dualList.push_back(socket_t(addr1, port1));
@@ -277,7 +301,7 @@ throw(anna::RuntimeException) {
 }
 
 
-Server* Engine::createServer(Entity *entity, const socket_t & socket)
+comm::Server* comm::Engine::createServer(Entity *entity, const socket_t & socket)
 throw(anna::RuntimeException) {
   Server* result(NULL);
   anna::Guard guard(this, "anna::diameter::comm::Engine::createServer");
@@ -309,7 +333,7 @@ throw(anna::RuntimeException) {
 
 
 // Lohacemos privado
-ClientSession* Engine::createClientSession(Server *server, int socketId)
+comm::ClientSession* comm::Engine::createClientSession(Server *server, int socketId)
 throw(anna::RuntimeException) {
   ClientSession* result(NULL);
   anna::Guard guard(this, "anna::diameter::comm::Engine::createClientSession");
@@ -355,7 +379,7 @@ throw(anna::RuntimeException) {
 }
 
 
-bool Engine::broadcastEntities(const Message* message) throw(anna::RuntimeException) {
+bool comm::Engine::broadcastEntities(const Message* message) throw(anna::RuntimeException) {
   LOGMETHOD(anna::TraceMethod tttm("diameter::comm::Engine", "broadcastEntities", ANNA_FILE_LOCATION));
   bool allok = true;
   bool ok;
@@ -374,7 +398,7 @@ bool Engine::broadcastEntities(const Message* message) throw(anna::RuntimeExcept
   return allok;
 }
 
-bool Engine::broadcastLocalServers(const Message* message) throw(anna::RuntimeException) {
+bool comm::Engine::broadcastLocalServers(const Message* message) throw(anna::RuntimeException) {
   LOGMETHOD(anna::TraceMethod tttm("diameter::comm::Engine", "broadcastLocalServers", ANNA_FILE_LOCATION));
   bool allok = true;
   bool ok;
@@ -393,7 +417,7 @@ bool Engine::broadcastLocalServers(const Message* message) throw(anna::RuntimeEx
   return allok;
 }
 
-bool Engine::bind() throw(anna::RuntimeException) {
+bool comm::Engine::bind() throw(anna::RuntimeException) {
   LOGMETHOD(anna::TraceMethod tttm("diameter::comm::Engine", "bind", ANNA_FILE_LOCATION));
   bool result = true; // all OK return
 
@@ -409,12 +433,12 @@ bool Engine::bind() throw(anna::RuntimeException) {
   return result;
 }
 
-ClientSession* Engine::findClientSession(const std::string & addr, int port, int socketId, anna::Exception::Mode::_v emode)
+comm::ClientSession* comm::Engine::findClientSession(const std::string & addr, int port, int socketId, anna::Exception::Mode::_v emode)
 throw(anna::RuntimeException) {
   return findClientSession(ClientSession::getKey(addr, port, socketId), emode);
 }
 
-ClientSession* Engine::findClientSession(const std::string & key, anna::Exception::Mode::_v emode)
+comm::ClientSession* comm::Engine::findClientSession(const std::string & key, anna::Exception::Mode::_v emode)
 throw(anna::RuntimeException) {
   anna::Guard guard(this, "anna::diameter::comm::Engine::findClientSession");
   clientSession_iterator ii = clientSession_find(key);
@@ -438,7 +462,7 @@ throw(anna::RuntimeException) {
 }
 
 
-Server* Engine::findServer(const std::string & addr, int port, anna::Exception::Mode::_v emode)
+comm::Server* comm::Engine::findServer(const std::string & addr, int port, anna::Exception::Mode::_v emode)
 throw(anna::RuntimeException) {
   anna::Guard guard(this, "anna::diameter::comm::Engine::findServer");
   server_iterator ii = server_find(server_key(addr, port));
@@ -462,7 +486,7 @@ throw(anna::RuntimeException) {
   return NULL;
 }
 
-Entity* Engine::findEntity(const socket_v & socketList, anna::Exception::Mode::_v emode)
+comm::Entity* comm::Engine::findEntity(const socket_v & socketList, anna::Exception::Mode::_v emode)
 throw(anna::RuntimeException) {
   anna::Guard guard(this, "anna::diameter::comm::Engine::findEntity");
   entity_key key(getEntityKey(socketList));
@@ -486,7 +510,7 @@ throw(anna::RuntimeException) {
   return NULL;
 }
 
-Entity* Engine::findEntity(const std::string & addr1, int port1, const std::string & addr2, int port2, anna::Exception::Mode::_v emode)
+comm::Entity* comm::Engine::findEntity(const std::string & addr1, int port1, const std::string & addr2, int port2, anna::Exception::Mode::_v emode)
 throw(anna::RuntimeException) {
   socket_v dualList;
   dualList.push_back(socket_t(addr1, port1));
@@ -509,7 +533,7 @@ throw(anna::RuntimeException) {
 //}
 
 
-LocalServer* Engine::findLocalServer(const std::string & addr, int port, anna::Exception::Mode::_v emode)
+comm::LocalServer* comm::Engine::findLocalServer(const std::string & addr, int port, anna::Exception::Mode::_v emode)
 throw(anna::RuntimeException) {
   anna::Guard guard(this, "anna::diameter::comm::Engine::findLocalServer");
   socket_t key(addr, port);
@@ -535,7 +559,7 @@ throw(anna::RuntimeException) {
 }
 
 
-ServerSession* Engine::findServerSession(int socketId, anna::Exception::Mode::_v emode) throw(anna::RuntimeException) {
+comm::ServerSession* comm::Engine::findServerSession(int socketId, anna::Exception::Mode::_v emode) throw(anna::RuntimeException) {
   anna::Guard guard(this, "anna::diameter::comm::Engine::findServerSession");
   ServerSession *result;
 
@@ -562,7 +586,7 @@ ServerSession* Engine::findServerSession(int socketId, anna::Exception::Mode::_v
 }
 
 
-void Engine::closeClientSession(ClientSession* clientSession, bool destroy)
+void comm::Engine::closeClientSession(comm::ClientSession* clientSession, bool destroy)
 throw(anna::RuntimeException) {
   if(clientSession == NULL)
     return;
@@ -600,7 +624,7 @@ throw(anna::RuntimeException) {
 
 
 
-void Engine::closeServer(Server* server, bool destroy)
+void comm::Engine::closeServer(comm::Server* server, bool destroy)
 throw(anna::RuntimeException) {
   if(server == NULL)
     return;
@@ -632,7 +656,7 @@ throw(anna::RuntimeException) {
 }
 
 
-void Engine::closeEntity(Entity* entity, bool destroy)
+void comm::Engine::closeEntity(comm::Entity* entity, bool destroy)
 throw(anna::RuntimeException) {
   if(entity == NULL)
     return;
@@ -667,7 +691,7 @@ throw(anna::RuntimeException) {
 
 
 
-void Engine::closeLocalServer(LocalServer* localServer, bool destroy)
+void comm::Engine::closeLocalServer(comm::LocalServer* localServer, bool destroy)
 throw(anna::RuntimeException) {
   if(localServer == NULL)
     return;
@@ -700,7 +724,7 @@ throw(anna::RuntimeException) {
 
 
 
-void Engine::closeEntities(bool destroy) throw(anna::RuntimeException) {
+void comm::Engine::closeEntities(bool destroy) throw(anna::RuntimeException) {
   LOGMETHOD(anna::TraceMethod tttm("diameter::comm::Engine", "closeEntities", ANNA_FILE_LOCATION));
   anna::Guard guard(this, "anna::diameter::comm::Engine::closeEntities");
 
@@ -708,7 +732,7 @@ void Engine::closeEntities(bool destroy) throw(anna::RuntimeException) {
     closeEntity(entity(it), destroy);
 }
 
-void Engine::closeLocalServers(bool destroy) throw(anna::RuntimeException) {
+void comm::Engine::closeLocalServers(bool destroy) throw(anna::RuntimeException) {
   LOGMETHOD(anna::TraceMethod tttm("diameter::comm::Engine", "closeLocalServers", ANNA_FILE_LOCATION));
   anna::Guard guard(this, "anna::diameter::comm::Engine::closeLocalServers");
 
@@ -716,7 +740,7 @@ void Engine::closeLocalServers(bool destroy) throw(anna::RuntimeException) {
     closeLocalServer(localServer(it), destroy);
 }
 
-void Engine::eraseDeprecatedIdleEntities() throw() {
+void comm::Engine::eraseDeprecatedIdleEntities() throw() {
   LOGMETHOD(anna::TraceMethod tttm("diameter::comm::Engine", "eraseDeprecatedIdleEntities", ANNA_FILE_LOCATION));
   Entity *et;
 
@@ -727,7 +751,7 @@ void Engine::eraseDeprecatedIdleEntities() throw() {
   }
 }
 
-int Engine::getOTARequestsForEntities() const throw() {
+int comm::Engine::getOTARequestsForEntities() const throw() {
   int result = 0;
 
   for(const_entity_iterator it = entity_begin(), maxii = entity_end(); it != maxii; it ++)
@@ -736,7 +760,7 @@ int Engine::getOTARequestsForEntities() const throw() {
   return result;
 }
 
-int Engine::getOTARequestsForLocalServers() const throw() {
+int comm::Engine::getOTARequestsForLocalServers() const throw() {
   int result = 0;
 
   for(const_localServer_iterator it = localServer_begin(), maxii = localServer_end(); it != maxii; it ++)
@@ -746,31 +770,31 @@ int Engine::getOTARequestsForLocalServers() const throw() {
 }
 
 
-void Engine::setRealm(const std::string & name) throw() {
+void comm::Engine::setRealm(const std::string & name) throw() {
   a_realm = ((name != "") ? name : anna::functions::getDomainname());
 }
 
 
-void Engine::setHost(const std::string & name) throw() {
+void comm::Engine::setHost(const std::string & name) throw() {
   a_host = ((name != "") ? name : anna::functions::getHostname());
 }
 
 
 
-void Engine::raiseAutoRecovery(bool autoRecovery) throw(anna::RuntimeException) {
+void comm::Engine::raiseAutoRecovery(bool autoRecovery) throw(anna::RuntimeException) {
   LOGMETHOD(anna::TraceMethod tttm("diameter::comm::Engine", "raiseAutoRecovery", ANNA_FILE_LOCATION));
 
   for(entity_iterator it = entity_begin(), maxii = entity_end(); it != maxii; it ++)
     entity(it)->raiseAutoRecovery(autoRecovery);
 }
 
-void Engine::do_stop()
+void comm::Engine::do_stop()
 throw() {
   LOGMETHOD(anna::TraceMethod tttm("diameter::comm::Engine", "do_stop", ANNA_FILE_LOCATION));
   close(true /* destroy */);
 }
 
-std::string Engine::asString(void) const throw() {
+std::string comm::Engine::asString(void) const throw() {
   std::string trace;
   trace =  "\n================================";
   trace += "\nDiameter comm Engine information";
@@ -811,7 +835,7 @@ std::string Engine::asString(void) const throw() {
 }
 
 
-anna::xml::Node* Engine::asXML(anna::xml::Node* parent) const
+anna::xml::Node* comm::Engine::asXML(anna::xml::Node* parent) const
 throw() {
   parent = anna::app::Component::asXML(parent);
   anna::xml::Node* result = parent->createChild("diameter.comm.Engine");
@@ -834,33 +858,57 @@ throw() {
   for(const_localServer_iterator it = localServer_begin(); it != localServer_end(); it++)
     localServer(it)->asXML(localServers);
 
+  // DRA Basics
+  // Aspect:
+  //  <Engine.RemoteRealm Name="afNodeHostRealm.com">
+  //     <Engine.RemoteRealmHost Name="afNodeHostname.afNodeHostRealm.com" ServerSession="localhost:3868|ServerSessionId:4"/>
+  //  </Engine.RemoteRealm>
+  //  <Engine.RemoteRealm Name="ggsnNodeHostRealm.com">
+  //     <Engine.RemoteRealmHost Name="ggsnNodeHostname.ggsnNodeHostRealm.com" ServerSession="localhost:3868|ServerSessionId:6"/>
+  //  </Engine.RemoteRealm>
+  for (dr_dh_server_sessions_it_t drit = a_dr_dh_server_sessions.begin(); drit != a_dr_dh_server_sessions.end(); drit++) {
+    anna::xml::Node* remoteRealm = result->createChild("Engine.RemoteRealm");
+    remoteRealm->createAttribute("Name", drit->first);
+    dh_server_sessions_map_t *dhServerSessions = (dh_server_sessions_map_t *)&(drit->second);
+    for (dh_server_sessions_it_t dhit = dhServerSessions->begin(); dhit != dhServerSessions->end(); dhit++) {
+      anna::xml::Node* remoteRealmHost = remoteRealm->createChild("Engine.RemoteRealmHost");
+      remoteRealmHost->createAttribute("Name", dhit->first);
+      server_sessions_vector_t *serverSessions = (server_sessions_vector_t *)&(dhit->second);
+      for (server_sessions_it_t ssit = serverSessions->begin(); ssit != serverSessions->end(); ssit++) {
+        std::string socket = anna::functions::socketLiteralAsString((*ssit)->getAddress(), (*ssit)->getPort());
+        std::string ss_desc = socket + anna::functions::asString("|ServerSessionId:%d", (*ssit)->getSocketId());
+        remoteRealmHost->createAttribute("ServerSession", ss_desc);
+      }
+    }
+  }
+
   return result;
 }
 
-Engine::clientSession_iterator Engine::clientSession_find(const clientSession_key &key) throw() {
+comm::Engine::clientSession_iterator comm::Engine::clientSession_find(const clientSession_key &key) throw() {
   return a_clientSessions.find(key);
 }
 
-Engine::server_iterator Engine::server_find(const server_key &key) throw() {
+comm::Engine::server_iterator comm::Engine::server_find(const server_key &key) throw() {
   return a_servers.find(key);
 }
 
-Engine::entity_iterator Engine::entity_find(const entity_key &key) throw() {
+comm::Engine::entity_iterator comm::Engine::entity_find(const entity_key &key) throw() {
   return a_entities.find(key);
 }
 
-Engine::localServer_iterator Engine::localServer_find(const socket_t &key) throw() {
+comm::Engine::localServer_iterator comm::Engine::localServer_find(const socket_t &key) throw() {
   return a_localServers.find(key);
 }
 
-Engine::entity_key Engine::getEntityKey(const std::string & addr1, int port1, const std::string & addr2, int port2) const throw() {
+comm::Engine::entity_key comm::Engine::getEntityKey(const std::string & addr1, int port1, const std::string & addr2, int port2) const throw() {
   socket_v dualList;
   dualList.push_back(socket_t(addr1, port1));
   dualList.push_back(socket_t(addr2, port2));
   return (getEntityKey(dualList));
 }
 
-Engine::entity_key Engine::getEntityKey(const socket_v &v) const throw() {
+comm::Engine::entity_key comm::Engine::getEntityKey(const socket_v &v) const throw() {
   std::string result;
   socket_v::const_iterator it;
   socket_v::const_iterator it_min(v.begin());
@@ -877,7 +925,7 @@ Engine::entity_key Engine::getEntityKey(const socket_v &v) const throw() {
 }
 
 
-void Engine::availabilityLostForEntities() throw() {
+void comm::Engine::availabilityLostForEntities() throw() {
   a_availableForEntities = false;
   LOGDEBUG(
     std::string msg = "diameter::comm::Engine { Realm: ";
@@ -894,7 +942,7 @@ void Engine::availabilityLostForEntities() throw() {
 }
 
 
-void Engine::availabilityRecoveredForEntities() throw() {
+void comm::Engine::availabilityRecoveredForEntities() throw() {
   a_availableForEntities = true;
   LOGDEBUG(
     std::string msg = "diameter::comm::Engine { Realm: ";
@@ -911,7 +959,7 @@ void Engine::availabilityRecoveredForEntities() throw() {
 }
 
 
-void Engine::availabilityLostForLocalServers() throw() {
+void comm::Engine::availabilityLostForLocalServers() throw() {
   a_availableForLocalServers = false;
   LOGDEBUG(
     std::string msg = "diameter::comm::Engine { Realm: ";
@@ -928,7 +976,7 @@ void Engine::availabilityLostForLocalServers() throw() {
 }
 
 
-void Engine::availabilityRecoveredForLocalServers() throw() {
+void comm::Engine::availabilityRecoveredForLocalServers() throw() {
   a_availableForLocalServers = true;
   LOGDEBUG(
     std::string msg = "diameter::comm::Engine { Realm: ";
@@ -945,7 +993,7 @@ void Engine::availabilityRecoveredForLocalServers() throw() {
 }
 
 
-bool Engine::refreshAvailabilityForEntities() throw() {
+bool comm::Engine::refreshAvailabilityForEntities() throw() {
   // Here available
   if(a_availableForEntities) {  // check not-bound state for all client-sessions:
     bool isolate = true;
@@ -971,7 +1019,7 @@ bool Engine::refreshAvailabilityForEntities() throw() {
   return false;
 }
 
-bool Engine::refreshAvailabilityForLocalServers() throw() {
+bool comm::Engine::refreshAvailabilityForLocalServers() throw() {
   // Here available
   if(a_availableForLocalServers) {  // check not-bound state for all client-sessions:
     bool isolate = true;
@@ -998,7 +1046,11 @@ bool Engine::refreshAvailabilityForLocalServers() throw() {
 }
 
 
-void Engine::readDPA(anna::DataBlock &dpa, const anna::DataBlock & dpr) throw() {
+void comm::Engine::readDPA(anna::DataBlock &dpa, const anna::DataBlock & dpr) throw() {
+
+  // Check for base protocol codec engine health:
+  assertBaseProtocolHealth();
+
   // Default DPA implementation:
   //
   //   'Disconnect-Peer-Answer' (282,answer)
@@ -1044,7 +1096,11 @@ void Engine::readDPA(anna::DataBlock &dpa, const anna::DataBlock & dpr) throw()
 }
 
 
-void Engine::readCEA(anna::DataBlock &cea, const anna::DataBlock & cer) throw() {
+void comm::Engine::readCEA(anna::DataBlock &cea, const anna::DataBlock & cer) throw() {
+
+  // Check for base protocol codec engine health:
+  assertBaseProtocolHealth();
+
   // Default CEA implementation:
   //
   //   'Capabilities-Exchange-Answer' (257,answer)
@@ -1109,8 +1165,71 @@ void Engine::readCEA(anna::DataBlock &cea, const anna::DataBlock & cer) throw()
   }
 }
 
+void comm::Engine::manageDrDhServerSession(ServerSession *ss, bool register_or_desregister) throw() {
+
+  // Decode CER (TODO: use raw buffer helpers)
+  std::string destinationRealm, destinationHost;
+  codec::Message codecMsg(getBaseProtocolCodecEngine());
+  try {
+    codecMsg.decode(ss->a_cer);
+    destinationRealm = codecMsg.getAvp(anna::diameter::helpers::base::AVPID__Origin_Realm)->getDiameterIdentity()->getValue();
+    destinationHost = codecMsg.getAvp(anna::diameter::helpers::base::AVPID__Origin_Host)->getDiameterIdentity()->getValue();
+  }
+  catch(anna::RuntimeException &ex) {
+    ex.trace();
+    return;
+  }
+
+  dr_dh_server_sessions_nc_it_t drit = a_dr_dh_server_sessions.find(destinationRealm);
+  if (drit != a_dr_dh_server_sessions.end()) { // found
+    dh_server_sessions_map_t *dhServerSessions = (dh_server_sessions_map_t *)&(drit->second);
+    dh_server_sessions_nc_it_t dhit = dhServerSessions->find(destinationHost);
+    if (dhit != dhServerSessions->end()) { // found
+      server_sessions_vector_t *serverSessions = (server_sessions_vector_t *)&(dhit->second);
+      if (register_or_desregister) { // REGISTER
+        serverSessions->push_back(ss);
+      }
+      else { // DESREGISTER
+        // Sequential search the specific server session:
+        for (server_sessions_nc_it_t ssit = serverSessions->begin(); ssit != serverSessions->end(); ssit++) {
+          if ((*ssit)->getAddress() != ss->getAddress()) continue;
+          if ((*ssit)->getPort() != ss->getPort()) continue;
+          if ((*ssit)->getSocketId() != ss->getSocketId()) continue;
+          serverSessions->erase(ssit);  // if it is the last server session removed in DR-DH path, the XML will show this tree empty
+                                        // (it could be a hint for past registerings):
+                                        //          <Engine.RemoteRealm Name="afNodeHostRealm.com">
+                                        //             <Engine.RemoteRealmHost Name="afNodeHostname.afNodeHostRealm.com"/>
+                                        //          </Engine.RemoteRealm>
+                                        //          <Engine.RemoteRealm Name="ggsnNodeHostRealm.com">
+                                        //             <Engine.RemoteRealmHost Name="ggsnNodeHostname.ggsnNodeHostRealm.com"/>
+                                        //          </Engine.RemoteRealm>
+
+          break;
+        }
+      }
+    }
+    else {
+      if (!register_or_desregister) return; // strange (host not found)
+      server_sessions_vector_t ssVector;
+      ssVector.push_back(ss);
+      (*dhServerSessions)[destinationHost] = ssVector;
+    }
+  }
+  else {
+    if (!register_or_desregister) return; // strange (realm not found)
+    server_sessions_vector_t ssVector;
+    ssVector.push_back(ss);
+    dh_server_sessions_map_t dhServerSessions;
+    dhServerSessions[destinationHost] = ssVector;
+    a_dr_dh_server_sessions[destinationRealm] = dhServerSessions;
+  }
+}
+
+void comm::Engine::readDWA(anna::DataBlock &dwa, const anna::DataBlock & dwr) throw() {
+
+  // Check for base protocol codec engine health:
+  assertBaseProtocolHealth();
 
-void Engine::readDWA(anna::DataBlock &dwa, const anna::DataBlock & dwr) throw() {
   // Default DWA implementation:
   //
   //   'Device-Watchdog-Answer' (280,answer)
@@ -1156,7 +1275,7 @@ void Engine::readDWA(anna::DataBlock &dwa, const anna::DataBlock & dwr) throw()
   }
 }
 
-void Engine::resetStatistics() throw() {
+void comm::Engine::resetStatistics() throw() {
   for(server_iterator it = server_begin(), maxii = server_end(); it != maxii; it ++)
     server(it)->resetStatistics();
 
@@ -1164,12 +1283,58 @@ void Engine::resetStatistics() throw() {
     localServer(it)->resetStatistics();
 }
 
-void Engine::do_initialize() throw(RuntimeException) {
+void comm::Engine::do_initialize() throw(RuntimeException) {
   LOGMETHOD(anna::TraceMethod tttm("diameter::comm::Engine", "do_initialize", ANNA_FILE_LOCATION));
   LOGDEBUG(anna::Logger::debug("Nothing special done on component initialization", ANNA_FILE_LOCATION));
 }
 
-void Engine::lazyInitialize() throw(RuntimeException) {
+void comm::Engine::lazyInitialize() throw(RuntimeException) {
   LOGMETHOD(anna::TraceMethod tttm("diameter::comm::Engine", "lazyInitialize", ANNA_FILE_LOCATION));
   anna::app::Component::initialize(); // this will invoke do_initialize
 }
+
+// Not tested yet
+const comm::Response* comm::Engine::sendRealmHost(const Message* message, const std::string &destinationRealm, const std::string &destinationHost) throw(anna::RuntimeException) {
+
+  if (destinationRealm == "")
+    throw anna::RuntimeException("Unable to resolve the destination: empty provided Destination-Realm name", ANNA_FILE_LOCATION);
+
+  // Get the server sessions which fulfill the restrictions:
+  dr_dh_server_sessions_it_t drit = a_dr_dh_server_sessions.find(destinationRealm);
+  if (drit == a_dr_dh_server_sessions.end())
+    throw anna::RuntimeException(anna::functions::asString("Unable to resolve the destination: Destination-Realm name is not registered (no remote clients have been connected to '%s')", destinationRealm.c_str()), ANNA_FILE_LOCATION);
+
+  dh_server_sessions_map_t *dhServerSessions = (dh_server_sessions_map_t *)&(drit->second);
+  // randomize between all server sessions for all hosts:
+  dh_server_sessions_nc_it_t dhit;
+  int hostsN = dhServerSessions->size();
+  if (hostsN == 0) // avoids next division by cero (rand() % 0)
+    throw anna::RuntimeException(anna::functions::asString("Unable to resolve the destination: neither Destination-Host currently connected to Destination-Realm '%s'", destinationRealm.c_str()), ANNA_FILE_LOCATION);
+
+  if (destinationHost == "") {
+    // in this case, randomize the host:
+    dhit = dhServerSessions->begin();
+    int randomHostIndx = rand() % hostsN; // number between 0 and the number of hosts - 1
+    std::advance (dhit, randomHostIndx);
+  }
+  else {
+    dhit = dhServerSessions->find(destinationHost);
+    if (dhit == dhServerSessions->end())
+      throw anna::RuntimeException(anna::functions::asString("Unable to resolve the destination: Destination-Host '%s' is not registered for Destination-Realm '%s'", destinationHost.c_str(), destinationRealm.c_str()), ANNA_FILE_LOCATION);
+  }
+
+  // Now, randomize the available server sessions:
+  server_sessions_vector_t *serverSessions = (server_sessions_vector_t *)&(dhit->second);
+  int serverSessionN = serverSessions->size();
+  if (serverSessionN == 0) { // avoids next division by cero (rand() % 0)
+    std::string aux = "";
+    if (destinationHost != "") { aux = "to Destination-Host '"; aux += destinationHost; aux += "'"; }
+    std::string msg = anna::functions::asString("Unable to resolve the destination: neither server session currently connected%s within Destination-Realm '%s'", aux.c_str(), destinationRealm.c_str());
+    throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
+  }
+
+  server_sessions_nc_it_t ssit = serverSessions->begin();
+  int randomServerSessionIndx = rand() % serverSessionN; // number between 0 and the number of server sessions - 1
+  std::advance (ssit, randomServerSessionIndx);
+  return (*ssit)->send(message);
+}
index 985e4a8..a14b3c6 100644 (file)
@@ -305,6 +305,10 @@ throw(anna::RuntimeException) {
   if(ii == serverSession_end())
     return;
 
+  // Remove origin-realm / origin-host for server session in delivery map
+  // This is related to http://redmine.teslayout.com/issues/41
+  a_engine->manageDrDhServerSession(serverSession, false /* desregister */);
+
   try {
     //serverSession->setState(ServerSession::State::Closing); NOT MANAGED WITH SERVER SESSIONS
     serverSession->unbind(true /* always forceDisconnect on server sessions ... */);
index 1e2728c..ad0b15a 100644 (file)
@@ -55,7 +55,7 @@ const anna::Millisecond ServerSession::DefaultAllowedInactivityTime(90000); // I
 ServerSession::ServerSession() : Session("diameter::comm::ServerSession", "Diameter Inactivity Detection Timer"),
   a_receiverFactory(this),
   a_cer(ClassCode::Bind),
-  a_dwr(ClassCode::ApplicationMessage) // realmente no es necesario, los Message son por defecto de aplicacion
+  a_dwr(ClassCode::ApplicationMessage) // not actually needed; Message is application type by default
 { initialize(); }
 
 void ServerSession::initialize() throw() {
@@ -81,7 +81,6 @@ int ServerSession::getPort() const throw() {
   return a_parent->getKey().second;
 }
 
-
 const Response* ServerSession::send(const Message* message) throw(anna::RuntimeException) {
   LOGMETHOD(anna::TraceMethod traceMethod(a_className, "send", ANNA_FILE_LOCATION));
 
@@ -398,6 +397,9 @@ throw(anna::RuntimeException) {
       }
 
       a_cer.setBody(db);
+      // Basic DRA:
+      getParent()->getEngine()->manageDrDhServerSession(this, true /* register */);
+
       sendCEA();
       //activateTimer(); // Ya se invoca al inicio de este metodo ::receive
       //bool changes = a_parent->refreshAvailability();