Dynamic realm registration
[anna.git] / example / diameter / launcher / Launcher.cpp
index a1b1075..2cf882d 100644 (file)
@@ -120,7 +120,7 @@ Launcher::Launcher() : anna::comm::Application("launcher", "DiameterLauncher", "
 
 
 
-void Launcher::servicesFromXML(const anna::xml::Node* servicesNode) throw(anna::RuntimeException) {
+void Launcher::servicesFromXML(const anna::xml::Node* servicesNode, bool eventOperation) throw(anna::RuntimeException) {
   //<!ATTLIST stack id CDATA #REQUIRED dictionary CDATA #REQUIRED>
   const anna::xml::Attribute  *id, *dictionary;
 
@@ -135,6 +135,13 @@ void Launcher::servicesFromXML(const anna::xml::Node* servicesNode) throw(anna::
   // Stacks
   anna::diameter::stack::Engine &stackEngine = anna::diameter::stack::Engine::instantiate();
   anna::diameter::stack::Dictionary *d;
+  ///////////////////////////////////////////
+  // APPLICATION MESSAGE OAM MODULE SCOPES //
+  ///////////////////////////////////////////
+  // We will register a scope per stack id registered. The counters will be dynamically registered at count method.
+  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;
 
   for(anna::xml::Node::const_child_iterator it = servicesNode->child_begin(); it != servicesNode->child_end(); it++) {
     std::string nodeName = (*it)->getName();
@@ -147,6 +154,11 @@ void Launcher::servicesFromXML(const anna::xml::Node* servicesNode) throw(anna::
       try {
         d = stackEngine.createDictionary(id->getIntegerValue(), dictionary->getValue());
         getCodecEngine()->setDictionary(d);
+
+        // OAM module for counters:
+        appMsgOamModule.createStackCounterScope(scope_id, id->getIntegerValue() /* application-id */);
+        scope_id++;
+
       } catch(anna::RuntimeException &ex) {
         //_exit(ex.asString());
         throw ex;
@@ -154,6 +166,12 @@ void Launcher::servicesFromXML(const anna::xml::Node* servicesNode) throw(anna::
     }
   }
 
+  // Show loaded stacks:
+  std::cout << "Stacks currently loaded:" << std::endl;
+  std::cout << anna::functions::tab(stackEngine.asString(false /* light */));
+  std::cout << std::endl;
+
+
   // Codec engine adjustments:
   // Auto stack selection based on Application-ID:
   bool multistack = (stackEngine.stack_size() > 1);
@@ -256,6 +274,7 @@ void Launcher::servicesFromXML(const anna::xml::Node* servicesNode) throw(anna::
           a_workingNode->setRequestRetransmissions(retransmissions);
           a_workingNode->getEntity()->setSessionBasedModelsType(sessionBasedModelsType);
           a_workingNode->getEntity()->setBalance(balance ? (balance->getValue() == "yes") : false); // for sendings
+          if (eventOperation) a_workingNode->getEntity()->bind();
         }
       }
 
@@ -275,15 +294,27 @@ void Launcher::servicesFromXML(const anna::xml::Node* servicesNode) throw(anna::
       a_workingNode->setLogs(s_log, b_splitLog, b_detailedLog, b_dumpLog, s_burstLog);
 
 
+      // Lazy initialization for comm engine:
+      if (eventOperation) commEngine->lazyInitialize();
+
       // New Node assignment //////////////////////////////////////////////////////////////////////
       a_nodes[originRealm->getValue()] = a_workingNode;
       /////////////////////////////////////////////////////////////////////////////////////////////
     }
   }
+
+  // Diameter comm engines which are loaded after application start (via management operation 'services') are not really started,
+  //  but this don't care because application startComponents() -> initialize() -> do_initialize() -> do nothing.
+  // And when stopped, running state is not checked and it will be stopped anyway.
 }
 
 
-void Launcher::loadServices(const std::string & xmlPathFile) throw(anna::RuntimeException) {
+void Launcher::loadServices(const std::string & xmlPathFile, bool eventOperation) throw(anna::RuntimeException) {
+
+  if (xmlPathFile == "null" || xmlPathFile == "") {
+    LOGWARNING(anna::Logger::warning("Ignoring services configuration on start: empty or 'null' string provided as xml file. Use management interface (operation 'services') in order to add services", ANNA_FILE_LOCATION));
+    return;
+  }
 
   LOGDEBUG(
     std::string trace = "Loading ADML services file '";
@@ -315,7 +346,7 @@ void Launcher::loadServices(const std::string & xmlPathFile) throw(anna::Runtime
     trace += anna::xml::Compiler().apply(rootNode);
     anna::Logger::debug(trace, ANNA_FILE_LOCATION);
   );
-  servicesFromXML(rootNode);
+  servicesFromXML(rootNode, eventOperation);
 }
 
 
@@ -360,6 +391,14 @@ anna::Millisecond Launcher::checkTimeMeasure(const std::string &parameter, const
   throw RuntimeException(msg, ANNA_FILE_LOCATION);
 }
 
+RealmNode *Launcher::getWorkingNode() const throw(anna::RuntimeException) {
+
+  if (!a_workingNode)
+    throw RuntimeException("No services yet loaded. Try 'services' operation (via management interface), or restart process using command-line 'services' parameter", ANNA_FILE_LOCATION);
+
+  return a_workingNode;
+}
+
 bool Launcher::setWorkingNode(const std::string &name) throw() {
   bool result = false;
 
@@ -379,8 +418,8 @@ bool Launcher::setWorkingNode(const std::string &name) throw() {
 }
 
 RealmNode *Launcher::getRealmNode(const std::string &realmName) const throw() {
-  for (realm_nodes_it it = a_nodes.begin(); it != a_nodes.end(); it++)
-    if(it->second->getMyDiameterEngine()->getRealm() == realmName) return it->second;
+  realm_nodes_it it = a_nodes.find(realmName);
+  if (it != a_nodes.end()) return it->second;
 
   return NULL; // this never happens
 }
@@ -405,13 +444,12 @@ throw(anna::RuntimeException) {
     a_counterRecorder = new MyCounterRecorder(cntDir + anna::functions::asString("/Counters.Pid%d", (int)getPid()));
   }
 
+  // Tracing:
+  if(cl.exists("trace"))
+    anna::Logger::setLevel(anna::Logger::asLevel(cl.getValue("trace")));
+
   // Load launcher services:
   loadServices(cl.getValue("services")); // before run (have components to be created)
-
-  // Show loaded stacks:
-  std::cout << "Stacks provided:" << std::endl;
-  std::cout << anna::functions::tab(anna::diameter::stack::Engine::instantiate().asString(false /* light */));
-  std::cout << std::endl;
 }
 
 void Launcher::run()
@@ -425,10 +463,6 @@ throw(anna::RuntimeException) {
   // Statistics:
   anna::statistics::Engine::instantiate().enable();
 
-  // Tracing:
-  if(cl.exists("trace"))
-    anna::Logger::setLevel(anna::Logger::asLevel(cl.getValue("trace")));
-
   LOGINFORMATION(
     // Help on startup traces:
     anna::Logger::information(help(), ANNA_FILE_LOCATION);
@@ -557,18 +591,6 @@ throw(anna::RuntimeException) {
   oamDiameterCodec.registerCounter(anna::diameter::codec::OamModule::Counter::LevelValidation__FailedRuleForCardinalityMoreThanNeeded,                 "", 17 /*2017*/);
   oamDiameterCodec.registerCounter(anna::diameter::codec::OamModule::Counter::LevelValidation__FailedGenericAvpRuleForCardinalityFoundDisregardedItem, "", 18 /*2018*/);
   oamDiameterCodec.registerCounter(anna::diameter::codec::OamModule::Counter::LevelValidation__FoundDisregardedItemsAndGenericAVPWasNotSpecified,      "", 19 /*2019*/);
-  ///////////////////////////////////////////
-  // APPLICATION MESSAGE OAM MODULE SCOPES //
-  ///////////////////////////////////////////
-  // We will register a scope per stack id registered. The counters will be dynamically registered at count method.
-  anna::diameter::comm::ApplicationMessageOamModule & appMsgOamModule = anna::diameter::comm::ApplicationMessageOamModule::instantiate();
-  int scope_id = 3;
-  for (anna::diameter::stack::Engine::const_stack_iterator it = stackEngine.stack_begin(); it != stackEngine.stack_end(); it++) {
-    appMsgOamModule.createStackCounterScope(scope_id, it->first);
-    scope_id++;
-  }
-  appMsgOamModule.enableCounters(); // this special module is disabled by default (the only)
-
 
 
   /////////////////////////////////
@@ -577,7 +599,7 @@ throw(anna::RuntimeException) {
   if(a_counterRecorderClock) {
     oamDiameterComm.setCounterRecorder(a_counterRecorder);
     oamDiameterCodec.setCounterRecorder(a_counterRecorder);
-    appMsgOamModule.setCounterRecorder(a_counterRecorder);
+    anna::diameter::comm::ApplicationMessageOamModule::instantiate().setCounterRecorder(a_counterRecorder);
     a_timeEngine->activate(a_counterRecorderClock); // start clock
   }
 
@@ -640,7 +662,6 @@ throw(anna::RuntimeException) {
     if (entity) entity->bind();
   }
 
-
   // Go into communicator poll
   // Reconnection period (tcp reconnect retry time):
   const char *varname = "reconnectionPeriod";
@@ -817,6 +838,14 @@ std::string Launcher::help() const throw() {
   result += "\n";
   result += "\n------------------------------------------------------------------------------------------- Hot changes";
   result += "\n";
+  result += "\nservices[|source file]               Adds and starts the services specified in the xml file provided.";
+  result += "\n                                      (if missing, the file 'services.xml' will be used).";
+  result += "\n                                     The last loaded realm node will be automatically the new current";
+  result += "\n                                      working node. This is used to load new nodes once the ADML is";
+  result += "\n                                      started, regardless if '--services' command line parameter was";
+  result += "\n                                      used or not. Those services which are not correctly loaded, will";
+  result += "\n                                      be ignored, keeping the process alive.";
+  result += "\n";
   result += "\ndiameterServerSessions|<integer>     Updates the maximum number of accepted connections to diameter";
   result += "\n                                      server socket.";
   result += "\ncontext[|target file]                Application context could also be written by mean this operation,";
@@ -985,9 +1014,6 @@ void Launcher::eventOperation(const std::string &operation, std::string &respons
   std::string result = "";
   anna::DataBlock db_aux(true);
   anna::diameter::codec::Message codecMsg(getCodecEngine());
-  MyDiameterEntity *entity = getWorkingNode()->getEntity();
-  MyDiameterEngine *commEngine = getWorkingNode()->getMyDiameterEngine();
-  MyLocalServer *localServer = getWorkingNode()->getDiameterServer();
 
   ///////////////////////////////////////////////////////////////////
   // Simple operations without arguments:
@@ -1067,6 +1093,20 @@ void Launcher::eventOperation(const std::string &operation, std::string &respons
     return;
   }
 
+  if(opType == "services") {
+    std::string servicesFile = ((numParams == 1) ? param1 : "services.xml");
+    try {
+      loadServices(servicesFile, true /* bind entities */);
+    }
+    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());
+      return;
+    }
+    response_content = anna::functions::asString("Loaded services from file '%s'\n", servicesFile.c_str());
+    return;
+  }
+
   // Realm switch:
   if(opType == "node") {
     if (param1 != "") {
@@ -1078,6 +1118,12 @@ void Launcher::eventOperation(const std::string &operation, std::string &respons
     return;
   }
 
+  // Diameter endpoints:
+  MyDiameterEntity *entity = getWorkingNode()->getEntity();
+  MyDiameterEngine *commEngine = getWorkingNode()->getMyDiameterEngine();
+  MyLocalServer *localServer = getWorkingNode()->getDiameterServer();
+
+
   if(opType == "code") {
     codecMsg.loadXML(param1);
     std::string hexString = anna::functions::asHexString(codecMsg.code());