Stack selection by application-id (configurable), and minor fixes
authorEduardo Ramos Testillano <eduardo.ramos.testillano@ericsson.com>
Fri, 10 Apr 2015 00:33:52 +0000 (02:33 +0200)
committerEduardo Ramos Testillano <eduardo.ramos.testillano@ericsson.com>
Fri, 10 Apr 2015 00:33:52 +0000 (02:33 +0200)
example/diameter/launcher/main.cpp
include/anna/diameter/codec/EngineImpl.hpp
include/anna/diameter/codec/Message.hpp
source/diameter/codec/EngineImpl.cpp
source/diameter/codec/Message.cpp
source/diameter/stack/Dictionary.cpp

index 5a5db42..0b5ca7e 100644 (file)
@@ -1479,7 +1479,7 @@ throw(anna::RuntimeException) {
   anna::diameter::stack::Engine &stackEngine = anna::diameter::stack::Engine::instantiate();
 
   try {
-    anna::diameter::stack::Dictionary * d = stackEngine.createDictionary(0 /* stack id */);
+    anna::diameter::stack::Dictionary * d = stackEngine.createDictionary(0 /* stack id; its value don't mind, is not used (ADL is monostack) */);
     // Analyze comma-separated list:
     anna::Tokenizer lst;
     std::string dictionaryParameter = cl.getValue("dictionary");
index 049906b..776a5fb 100644 (file)
@@ -209,6 +209,8 @@ private:
   ValidationMode::_v a_validationMode;
   bool a_ignoreFlags;
   FixMode::_v a_fixMode;
+  bool a_selectStackWithApplicationId; // default behaviour: let the user switch the stack (false for this boolean)
+
 
   // Auxiliary
   const stack::Dictionary * a_dictionary;
@@ -231,6 +233,7 @@ public:
   virtual ~EngineImpl() {;}
 
 
+  // setters
   /**
      Sets diameter dictionary loaded at stack engine. It's recommended to configure a valid dictionary
      (if not, or NULL provided at #setDictionary, all avps will be managed as 'Unknown' format and all
@@ -238,20 +241,45 @@ public:
 
      @param dictionary Diameter dictionary. At single threaded processes, the same codec engine could be used with
      different diameter dictionaries (multi-stack applications). In that case the process must switch the stack for
-     the whole decoding or enconding operation over a Message depending on the context. But the smart way implies
-     inherit from this engine creating a component for each diameter stack managed in the application. Inheritance
-     is mandatory in multi-threaded processes: one engine, a unique stack.
+     the whole decoding or enconding operation over a Message depending on the context (normally the message header
+     Application-Id is used as stack identifier). But the smart way implies inherit from this engine creating a
+     component for each diameter stack managed in the application. Inheritance is mandatory in multi-threaded processes:
+     one engine, a unique stack.
   */
   void setDictionary(const stack::Dictionary * dictionary) throw() { a_dictionary = dictionary; }
 
-  // get
   /**
   * Sets diameter dictionary loaded at stack engine with the provided identifier.
   *
-  * @param stackId Stack identifier. When missing, default stack (stack::Engine::getDefaultStack()) will be used
+  * @param stackId Stack identifier. When missing, NULL will be returned
   * @return Returns configured dictionary (NULL if stack id was not found)
   */
-  const stack::Dictionary *setDictionary(int stackId = -1) throw();
+  const stack::Dictionary *setDictionary(unsigned int stackId) throw();
+
+
+  /**
+  * By default, the user will select the appropiate stack id depending on the context (see #setDictionary), but
+  * some applications could consider interesting automatic stack selection based on managed messages (incoming
+  * decoded ones, or built messages to be encoded). By default, no changes are done through the engine. Multithreaded
+  * processes should have a unique codec engine for each managed stack (this selection is disabled by default, then
+  * you don't have to worry about), but mono processes with multistack implementation over the same-unique engine,
+  * should activate 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.
+  * @param enable Activates/deactivates the stack selection from the Application-Id value within the message header.
+  */
+  void selectStackWithApplicationId (bool enable) throw() { a_selectStackWithApplicationId = enable; }
+
+  // getters
+
+  /**
+    Gets the currently configured behaviour regarding stack selection for multistack codec engines in mono thread
+    applications.
+
+    @return True if selection is done with the Application-Id. False if no selection is performed (user responsibility).
+  */
+  bool selectStackWithApplicationId (void) throw() { return a_selectStackWithApplicationId; }
+
 
   /**
      Gets currently configured dictionary. NULL if not configured (manual encode/decode operations).
index 2b65175..c8dab7c 100644 (file)
@@ -280,10 +280,21 @@ public:
   void setPotentiallyReTransmittedMessageBit(bool activate = true) throw() { if(activate) a_flags |= TBitMask; else a_flags &= (~TBitMask); }
 
   /**
-     Sets the message application id
+     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
+     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
+     method). The result could be unexpected when adding/encoding messages with a dictionary which does not correspond.
+
+     @warning do not interleave build/encode operations between different messages which uses different stacks over the same codec engine.
+     It seems common sense, but it is not bad to advice about this.
+
      @param aid Application-id.
   */
-  void setApplicationId(U32 aid) throw() { a_applicationId = aid; }
+  void setApplicationId(U32 aid) throw();
 
   /**
      Sets the message hop-by-hop
index c28a3ed..45a85ed 100644 (file)
@@ -145,6 +145,7 @@ EngineImpl::EngineImpl(const char* className) :
   a_validationDepth(ValidationDepth::FirstError),
   a_validationMode(ValidationMode::AfterDecoding),
   a_ignoreFlags(false),
+  a_selectStackWithApplicationId(false),
   a_fixMode(FixMode::BeforeCoding) {
   anna::diameter::sccs::activate();
   anna::xml::functions::initialize();
@@ -155,7 +156,7 @@ EngineImpl::EngineImpl(const char* className) :
 //------------------------------------------------------------------------------
 //-------------------------------------------------- EngineImpl::setDictionary()
 //------------------------------------------------------------------------------
-const anna::diameter::stack::Dictionary *EngineImpl::setDictionary(int stackId) throw() {
+const anna::diameter::stack::Dictionary *EngineImpl::setDictionary(unsigned int stackId) throw() {
   a_dictionary = (stack::Engine::instantiate()).getDictionary(stackId);
   return a_dictionary;
 }
index 6f906f8..4938a03 100644 (file)
@@ -227,6 +227,20 @@ void Message::setId(const char *name) throw(anna::RuntimeException) {
 }
 
 
+//------------------------------------------------------------------------------
+//-------------------------------------------------- Message::setApplicationId()
+//------------------------------------------------------------------------------
+void Message::setApplicationId(U32 aid) throw() {
+  a_applicationId = aid;
+
+  // Default behaviour:
+  if (!getEngine()->selectStackWithApplicationId()) return;
+
+  // Adapts for Application-ID stack identifier:
+  getEngine()->setDictionary(aid);
+}
+
+
 //------------------------------------------------------------------------------
 //------------------------------------------------------------ Message::addAvp()
 //------------------------------------------------------------------------------
@@ -294,7 +308,7 @@ void Message::decode(const anna::DataBlock &db, Message *ptrAnswer) throw(anna::
   //
   // VALIDATION PHASE
   // Launch exception on first validation error (validateAll == false), or log warning reporting all validation errors when
-  //  complete validation is desired (validateAll == true, engine default) launching a final exception like "decoded an invalid message".
+  //  complete validation is desired (validateAll == true, engine default) launching a final exception like "the decoded message is invalid".
   // OAM
   OamModule &oamModule = OamModule::instantiate();
 
@@ -331,9 +345,10 @@ void Message::decode(const anna::DataBlock &db, Message *ptrAnswer) throw(anna::
 
   U24 code = DECODE3BYTES_INDX_VALUETYPE(buffer, 5, U24);
 
-  setId(CommandId(code, requestBit() /* based on a_flags */));
+  // This is called before setId, and in general before any operation which needs to know about the stack elements.
+  setApplicationId(DECODE4BYTES_INDX_VALUETYPE(buffer, 8, U32)); // centralize set, because it could be used for stack selection.
 
-  a_applicationId = DECODE4BYTES_INDX_VALUETYPE(buffer, 8, U32);
+  setId(CommandId(code, requestBit() /* based on a_flags */));
 
   a_hopByHop = DECODE4BYTES_INDX_VALUETYPE(buffer, 12, U32);
 
@@ -409,7 +424,7 @@ void Message::decode(const anna::DataBlock &db, Message *ptrAnswer) throw(anna::
 
   if((vmode == Engine::ValidationMode::AfterDecoding) || (vmode == Engine::ValidationMode::Always))
     if(!valid(answer))
-      throw anna::RuntimeException("Decoded an invalid message. See previous report on warning-level traces", ANNA_FILE_LOCATION);
+      throw anna::RuntimeException("The decoded message is invalid. See previous report on warning-level traces", ANNA_FILE_LOCATION);
 }
 
 
@@ -768,6 +783,10 @@ void Message::fromXML(const anna::xml::Node* messageNode) throw(anna::RuntimeExc
     a_version = i_aux;
   }
 
+  // Application-id
+  // This is called before setId, and in general before any operation which needs to know about the stack elements.
+  setApplicationId(appid->getIntegerValue()); // this could set the dictionary...
+
   // Dictionary
   const stack::Dictionary * dictionary = getEngine()->getDictionary();
   const stack::Command *stackCommand = NULL;
@@ -853,19 +872,6 @@ void Message::fromXML(const anna::xml::Node* messageNode) throw(anna::RuntimeExc
     a_flags = flagsBCK;
   }
 
-  // Application-id
-  u_aux = appid->getIntegerValue();
-
-  /*
-  if(u_aux < 0) {
-    std::string msg = "Error processing command <application-id '"; msg += appid->getValue();
-    msg += "': negative values are not allowed";
-    throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
-  }
-  */
-
-  setApplicationId(u_aux);
-
   // Hob-by-hop-id
   if(hbh) {
     u_aux = hbh->getIntegerValue();
index 0d48b5d..821659c 100644 (file)
@@ -252,7 +252,7 @@ void Dictionary::addCommand(const Command & command) throw(anna::RuntimeExceptio
   const Command * found = getCommand(command.getId());
   if(found) {
     if(!a_allowUpdates) {
-      std::string s_ex = "Cannot add a command with an existing identifier (code,request):\n";
+      std::string s_ex = "Cannot add a command with an existing identifier:\n";
       s_ex += command.asString();
       throw anna::RuntimeException(s_ex, ANNA_FILE_LOCATION);
     }